Expressions

Arithmetic expressions

The operators +, -, *, /, % represent the addition, substraction, product, division and modulo operations. They have the conventional precedence level.

arithm_expression
    : expression '+' expression
    | expression '-' expression
    | expression '*' expression
    | expression '/' expression
    | expression '%' expression
    | '+' expression
    | '-' expression
    ;

Applied on numbers (integers or floats)

The operators +, -, * convert their operands to floats and return a float as soon as one of the two operands is a float. On the contrary they return an integer when both operands are integers.

The operator / converts its operands to floats and returns a float, even if the two operands are integers.

The operator modulo % can be applied on integers only.

Warning

In previous versions of LocalSolver the division followed the C convention: if the two operands were integers, the result was also an integer. The behavior has changed since the 4.0 release.

Applied on strings

Only the + operator is defined for strings. It corresponds to the concatenation operator. When one of the two operands is a string, then the other is converted to a string and a new string concatenating the two operands is returned. For instance "abc"+12 returns abc12.

Applied on LSExpressions

When one of the two operands is an LSExpression, then the operators return an LSExpression representing the arithmetic operation of these two terms in the mathematical model. If the mathematical model cannot handle one of the operand (e.g if one of them is a string), an exception is thrown.

Other cases, other types

By default, throws an exception. See the different modules for additionnal information on the available arithmetic operators for each type.

Examples:

a = 10;
b = a + 5.0;      // a is equals to 15
c <- a * 3.0;     // a is converted to an LSExpression
                  // and a new LSExpression of type PRODUCT is added to the model.
d = "foo" + 42    // 42 is converted to a string, then the concatenation is performed.

// This arithmetic operation will throw an exception:
// the modulo operation is not overloaded for strings.
e = "foo" % 2;

Relational expressions

The relational operators are ==, !=, <, >, >= and <=.

relational_expression
    : expression '<' expression
    | expression '>' expression
    | expression '==' expression
    | expression '!=' expression
    | expression '>=' expression
    | expression '<=' expression
    ;

Applied on numbers (integers or floats)

The operators return a boolean value equal to 1 when the comparison is true, 0 otherwise. If one of the operand is a float, the operators convert both operands to floats and then, compare them.

Applied on strings

When one of the two operands is a string then the other is converted to a string. Then, the comparison is done lexicographically.

Applied on LSExpressions

When one of the two operands is an LSExpression then the operators return an LSExpression representing the comparison of these two terms in the mathematical model. If the mathematical model cannot handle one of the operand (e.g if one of them is a string), an exception is thrown.

Applied on nil

The operators ==, != can be used to compare a value with nil. In that case, the operators return 1 (respectively 0) if the compared operand is equals (respectively not equals) to nil. The other relational operators (<, >, <=, >=) cannot be used with nil.

Other cases, other types

By default, throws an exception. See the different modules for additionnal information on the available relational operators for each type.

Examples:

a = {1,8};
b = {1,8};

println(8 < 9.2);           // print 1
println(a == b);            // throw an exception since "==" is not defined on maps
println("abc" <= "abcde");  // print 1
println("abc" == "aBc");    // print 0 since comparison is case-sensitive

Logical expressions

The operators !, &&, and || represent the boolean negation, logical and, and logical or.

logical_expression
    : expression '&&' expression
    | expression '||' expression
    | '!' expression
    ;

Applied on booleans

When applied on booleans (0 or 1), the logical operators use short-circuit evaluation: the second argument is interpreted only if the value of the first argument is not sufficient to determine the whole value of the expression.

  • The unary operator ! returns 1 if its argument is 0, 0 otherwise.
  • The operation x && y first evaluates x; if x is 0 (false), its value is returned; otherwise, y is evaluated and the resulting value is returned.
  • The operation x || y first evaluates x; if x is 1 (true), its value is returned; otherwise, y is evaluated and the resulting value is returned.

Applied on LSExpressions

As soon as one of the operands is an LSExpression the logical operation returns an LSExpression representing the operation in the mathematical model. In that case, both operands are evaluated. If the mathematical model cannot handle one of the operand (e.g if one of them is a string), an exception is thrown.

Other cases, other types

By default, throws an exception. See the different modules for additionnal information on the available logical operators for each type.

Conditional (ternary) expressions

logical_expression
    : expression '?' expression ':' expression
    ;

First argument is boolean

The ternary operation cond ? trueExpr : falseExpr first evaluates cond expression. If cond is 1, then trueExpr is evaluated and returned, otherwise falseExpr is evaluated and returned. Note that only two of the three operands are evaluated. trueExpr and falseExpr can be of any type.

First argument is an LSExpression

The operation returns an LSExpression representing this conditional operation in the mathematical model (operator IF). In that case, the three operands are evaluated. If the mathematical model cannot handle one of the operand (e.g if one of them is a string), an exception is thrown.

Other cases, other types

By default, throws an exception. See the different modules for additionnal information on the availability of the ternary operator for each type.

Indexed expressions

The binary operator [] is the index selector. It is appliable to maps or any other types that contain subvalues.

index_expression
    : expression '[' expression ']'
    ;

Applied on maps

The main usage of the index selector is for maps. a[b] returns the value in a for the key b. The key can be of any type except nil that has a special meaning. If the key does not exist in the map, no error is thrown but nil is returned.

Applied on LSExpressions

As soon as one of the operands (value or index) is an LSExpression the index selector returns an LSExpression representing the index operation in the mathematical model (operator AT). If the mathematical model cannot handle one of the operand (e.g if one of them is a string), an exception is thrown.

Applied on modules

The key must be a string. In that case, the expression a[b] returns the global variable with the name represented in b for the module a. If the global variable was not declared in the module, no error is thrown but nil is returned.

Other cases, other types

By default, throws an exception. See the different modules for additionnal information on the availability of the index operator for each type.

Member expressions

The binary operator . is the member selector. Most of the time, its semantic is similar to the index selector, but it throws an error if the key is not present in the underlying container. Furthermore, the type of the index is restricted to string literals only (formely, an identifier).

In conjunction with maps, it is used for Object-oriented-programming.

member_expression
    :  expression '.' identifier
    ;

Applied on maps

The expression a.b returns the value in a for the key b, where b is a string literal. Contrary to the index operator, if the key does not exist in the map, an exception is thrown.

Applied on modules

The expression a.b returns the global variable with the name b for the module a. Contrary to the index operator, if the global variable was not declared in the module, an exception is thrown.

Other cases, other types

As the member selector is the base of Object-oriented-programming, it is extensively used in all modules and types. Please refer to the documentation of each module for additionnal information.

Operator precedence & associativity

  • Expressions inside parentheses are evaluated first
  • Nested parentheses are evaluated from the innermost to the outermost parentheses.
  • Operators in the following tables are ranked by decreasing precedences: operators with higher precedence are evaluated before operators with relatively lower precedence.
  • Operators on the same line have equal precedence. Their associativity determines the execution order.
Description Operators Associativity
Parentheses, index and member selector operators ( ) [ ] left to right
Boolean negation, opposite and typeof operators ! - + typeof left to right
Multiplication, division, modulo * / % left to right
Addition, substraction + - left to right
Relational and type compatibility operators < > <= >= is left to right
Equality operators == != left to right
Logical and && left to right
Logical or || left to right
Ternary conditional ? : right to left
Assignment = <- right to left

Map declaration

LSP language offers a shortcut to declare maps or arrays quickly with the { } brace syntax.

map_expression
    : '{' '}'
    | '{' map_list '}'
    ;

map_list
    : expression
    | map_key '=' expression
    | map_key ':' expression
    | map_list ',' expression
    | map_list ',' map_key '=' expression
    | map_list ',' map_key ':' expression
    ;

map_key
    : string
    | identifier
    | integer
    | '-' integer
    ;

The declaration of a map starts with the { character and ends with }. Between these two delimiters, you can define a list of values or a list of <key, value> pairs. A map is both an associative table and an array. Thus, if you only set a value, without a key, a default integer key is automatically attributed to the value. This automatic key is equals to the biggest integer key presents in the map plus one or zero if the map does not contains integer keys.

If the same key is attributed more than once in the declaration, only the last value is retained.

Examples:

// Affects values -5, 4 and "foo" to the keys 0, 1 and 2
a = {-5, 4, "foo"};

// Affects values -3, -5, 8, -78 and 22 to the keys 0, "key1", 10, 11 and 12
b = {
    -3,
    "key1" : -5,
    10 : 8,
    -78,
    22
};


// Bigger example with nested maps
c = {
    "key1": 42,
    "key2": 31,
    "foo",
    -78.4e+6,
    "key3": {
        "nested map",
        4,
        -8
    }
};

Note

Even if maps accept keys of any types, only string keys and int keys are accepted in the short map declaration.