Expressions and Operators

Variable References

identifier

An identifier by itself is an expression referring to the value of a variable.

x = 1;
y = 2;

z = x + y;    // z == 1 + 2 == 3

The notable exception to this is identifiers used as names in binding literals.

b = [ foo = 1 ];    // "foo" does not refer to a variable

if Expressions

if test_expr then expr_1 else expr_2

The test expression must evaluate to a boolean.  If it evaluates to TRUE, then the result of the if expression is the result of expr_1.  Otherwise, the result of the expression is the result of expr_2.

b = if x >= 0 then [ x_positive = TRUE ] else [];

l += if _is_list(v) then v then v else <v>;

Note that if is only a kind of expression, not a kind of statement.

Function Calls

expr_1([expr_2, expr_3, ...])

expr_1 must evaluate to a function.  There can be zero or more expressions as arguments, separated by commas.  A trailing comma is permitted.

The function call mechanism provides special treatment for the identifier consisting of a single period, called the current environment and pronounced "dot". Dot is typically assigned a binding that contains the tools, switches, and file system required for the rest of the build. The initial environment of a model does not bind dot.

When a function is called, the value of "." which is used while executing its body is determined as follows:

Thus, the value of ".", if any, is passed through the dynamic call chain until it is altered either explicitly by an assignment statement or implicitly by calling a function with an extra actual parameter.

foo(y = 1)
{
  return ./x + y;
};
. = [x=1];

// Passing "." implicitly:
a = foo();                 // a = 1+1 == 2
b = foo(2);                // b = 1+2 == 3
. = [x=3];
c = foo();                 // c = 3+1 == 4

// Passing "." explicitly:
d = foo(1, [x=4]);         // d = 4+1 == 5
e = foo(6, [x=0]);         // e = 0+6 == 6

Note that function calls do not have side effects.  Vesta SDL is a functional language, so the only effect a function can have is the result value it produces.

foo(a, b)
{
  . += [ x = a + b ];
  return ./x;
};

. = [x=1];
c = foo(1, 2);            // c == 1+2 == 3, but ./x == 1
d = foo("foo", "bar");    // d == "foobar", but ./x == 1

bar(x)
{
  c += x;
  return x;
}

e = bar(3);               // e == 3+3 == 6, but c == 3;

Statement Blocks

{ statement; ... return expr; }
{ statement; ... value expr; }

Blocks of statements are a type of expression. Every block must end with a return statement, which may not appear anywhere else within the block. (The value and return keywords are interchangeable.)

x = {
       // Modify the value of dot before calling foo
       . ++= [ foo/options/bar = 3 ];
       r = foo(i);
       // Extract part of the return value of foo
       value r/state;
    };

Blocks are often used in conjunction with if:

x = if kind == "foo" then {
       r = foo(i);
       value r/state;
    } else {
       r = bar(i, j);
       value r+1;
    };

The final semicolon in a block is optional.

Note that like functions statement block expressions do not have side effects.  The only effect a block can have is the result value it produces.  (Remember, Vesta SDL is a functional language.)

foo = 1;

bar = {
  foo = 2;    // assignment local to statement block!
  value foo;
};

// foo == 1
// bar == 2

(Don't confuse this with statement blocks used in iteration statements, which do have side effects.)

_self Special Variable

The variable named "_self" is predefined as a function which refers to the model in which it is used.  In other words, it is as though every model imports itself under this name.

This feature is most often used with the _model_name primitive function to insert a version identifier into the result of a build.

Like any other variable, _self can be assigned a new value.

Operators

Equality Operators

expr == expr

The binary equality operator is written "==". Its result is TRUE if the left and right operands are equal and FALSE otherwise.

1 == 2                     // FALSE
4 == (2+2)                 // TRUE

"foo"    == "bar"          // FALSE
"foobar" == ("foo"+"bar")  // TRUE

ERR == ERR                 // TRUE

FALSE == FALSE             // TRUE
FALSE == TRUE              // FALSE

For lists and bindings, the equality test recursively compares each element of the aggregate object.

b = _append(_bind1("x", 1), _bind1("y", 2));

b == [x=1, y=2]            // TRUE
b == [x=2, y=2]            // FALSE
b == [x=1, y=2, z=3]       // FALSE

l = _append(_list1("foo"), _list1("bar"));

l == <"foo", "bar">        // TRUE
l == <"foo", 2>            // FALSE
l == <"foo">               // FALSE

Note that the name/value pairs in a binding are ordered.  (See the primitive functions _head, _tail, _elem, and _sub, all of which can be used on bindings.)  For two bindings to be considered equal, their name/value pairs must be in the same order.  (For this reason, using the equality operator with bindings may not produce the results you would expect.)

b = _append(_bind1("x", 1), _bind1("y", 2));

b == [x=1, y=2]            // TRUE
b == [y=2, x=1]            // FALSE !

The equality operator cannot be used to compare functions (user-defined, imported models, or primitives).

foo()
{
  return 2+2;
};
bar = foo;

foo == bar   // run-time error

If the operands have different types, they are always considered in-equal.

1 == "foo"                 // FALSE
TRUE == <1,2,3>           // FALSE
_append == [ x=1, y=2 ]   // FALSE

expr != expr

The binary inequality operator is written "!=". Its result is exactly the opposite of the equality operator: TRUE if the left and right operands are different and FALSE if they are equal.

1 != 2                     // TRUE
4 != (2+2)                 // FALSE

"foo"    != "bar"          // TRUE
"foobar" != ("foo"+"bar")  // FALSE

l = _list1("foo");
l != <"foo", 2>            // TRUE
l != <"foo">               // FALSE

b = _bind1("x", 1);
b != [x=1, y=2]            // TRUE
b != [x=1]                 // FALSE

The ordering constraint on bindings also applies to the inequality operator, as does the inability to compare functions.

Relational Operators

int_expr < int_expr
int_expr > int_expr
int_expr <= int_expr
int_expr >= int_expr

There are four binary relational operators, which only operate on integers:

Note that the relational operators do not work on text values. (In fact, Vesta SDL provides no way to compare text strings for lexical ordering.) Operands of non-integer types will cause a run-time error.

Logical Operators

! bool_expr

The unary "!" operator performs logical negation: it returns TRUE if its operand is FALSE and FALSE if its operand is TRUE. (Don't confuse this with the binary "!" operator used for binding name-existence tests.)

bool_expr && bool_expr

The binary "&&" operator performs a logical AND with short-circuit evaluation. That is, if the left operand evaluates to FALSE, the result is FALSE and the right operand is never evaluated. If both operands evaluate to TRUE, the result will be TRUE.

bool_expr || bool_expr

The binary "||" operator performs a logical OR with short-circuit evaluation. If the left operand evaluates to TRUE, the result is TRUE and the right operand is never evaluated. If either operand evaluates to TRUE, the result will be TRUE.

bool_expr => bool_expr

The binary "=>" operator performs logical implication with short-circuit evaluation. ("A => B" is equivalent to "!A || B".) If the left operand evaluates to FALSE, the result is TRUE and the right operand is never evaluated. If both operands evaluate to TRUE, the result will be TRUE. If the left operand evaluates to TRUE and the right operand evaluates to FALSE, the result will be FALSE.

Arithmetic Operators

Integers are the only numeric type, so arithmetic operators are obviously limited to integer operands. However, "+" and "-" also server other purposes (see below).

int_expr + int_expr

The binary "+" operator performs integer addition. Its result is the sum of the left and right operands.

int_expr - int_expr

The binary "-" operator performs integer addition. Its result is the difference of the left and right operands.

- int_expr

The unary "-" operator performs integer negation. ("-i" is equivalent to "0 - i".)

int_expr * int_expr

The binary "*" operator performs integer multiplication. Its result is the product of the left and right operands.

Note that there are no operators for division and modulus. To perform those functions, use the _div and _mod primitive functions.

If the result of any of the arithmetic operators lies outside the implementation-defined range, the evaluation halts with a run-time error. (As of this writing, the implementation is limited to integers which can be represented as two's-complement in 32-bits.)

Concatenation Operators

list_expr + list_expr
text_expr + text_expr

When used on list or text values, the binary "+" operator appends the left and right operands together.

"foo"+"bar" == "foobar"

<1,2>+<3,4> == <1,2,3,4>

For lists, the "+" operator is equivalent to the _append primitive function.

Note that when using "+", both operands must be the same type.

<1,2,3>+4                          // run-time error
<1,2,3>+_list1(4) == <1,2,3,4>

"foo"+4                            // run-time error

Binding Lookup Operator

binding_expr/identifier

This is the simplest way to look up the value associated with a name in a binding.

b = [ x = 1, y = 2 ];

u = b/x;          // u == 1
v = b/y;          // v == 2

w = b/x + b/y;    // w == 3

binding_expr/"string"

Names which are not legal identifiers or are reserved words can still be used for binding lookups, as long as they are enclosed in double quotes.

b = [ "bad-ident" = 1,
      "foreach" = 2,
      "4321" = 1234 ];

u = b/"bad-ident";        // u == 1
v = b/"foreach";          // v == 2
w = b/"4321";             // w == 1234

binding_expr/$text_var

text_var must hold a text value. This will look up the value associated with the name of text_var's value.

b = [ x = 1, y = 2 ];

name1 = "x";
name2 = "y";

u = b/$name1;     // u == 1
v = b/$name2;     // v == 2

If text_var's value is not a text, the evaluation will stop with a run-time error.

binding_expr/$(text_expr)
binding_expr/%text_expr%

text_expr must evaluate to a text value. This will look up the value associated with the name of text_expr's result.

b = [ foobar = 1, barfoo = 2 ];

name1 = "foo";
name2 = "bar";

u = b/$(name1 + name2);    // u == 1
v = b/%name2 + name1%;     // v == 2

If text_expr doesn't evaluate to a text, the evaluation will stop with a run-time error.

The binding lookup operator is left-associative which facilitates nested lookups.

b = [ foo = [ bar = [ x = 1, y = 2 ] ] ];

name1 = "foo";
name2 = "ba";

u = b/foo/bar/x;              // u == 1
v = b/$name1/$(name2+"r");    // v == 2

Note that binding lookups can also be performed with the _lookup primitive function, which is essentially equivalent to the forms described above.

Binding Name-Existence Test Operator

binding!identifier
binding!"string"
binding!$var
binding!$(expr)
binding!%expr%

Tests whether a name is assigned a value within a binding.  Has the the same forms as binding lookups.

b = [ foo = 1, foobar = 2 ];

test1 = b!foo;               // test1 == TRUE

test2 = b!"import";          // test2 == FALSE

name1 = "bar";
test3 = b!$name1;            // test3 == FALSE

test4 = b!$("foo"+name1);    // test4 == TRUE

Equivalent to the _defined primitive function in functionality.

Binding Manipulation Operators

binding_expr + binding_expr

The binary "+" operator, called the binding overlay operator, produces a new binding with name/value pairs for all the names in its left and right operands. For any names which have values in both operands, the value from the right operand is used in the result.

[foo="bar"] + [bar="foo"] == [foo="bar",bar="foo"]
[x=1,y=2]   + [y=3,z=4]   == [x=1,y=3,z=4]
[foo=7]     + [foo=FALSE] == [foo=FALSE]

Contrast this with the _append primitive function, which causes a run-time error if its two arguments have any names in common.

Note that with "+", sub-bindings are not merged. The sub-binding from the right operand is used.

[foo=[x=1]] + [foo=[y=2]] == [foo=[y=2]]
[foo=[x=1]] + [foo=[]]    == [foo=[]]

binding_expr ++ binding_expr

The binary "++"operator, called the recursive binding overlay operator, is similar to binding overlay operator, except that it recursively merges values that are bindings in both operands.

[foo=[x=1]] ++ [foo=[y=2]] == [foo=[x=1,y=2]]

[foo/bar/a=TRUE] ++ [foo/baz/b=FALSE] == [foo=[bar=[a=TRUE],baz[b=FALSE]]]

[foo=[x=1,y=2]] ++ [foo=[y=3,z=4], bar=TRUE] == [foo=[x=1,y=3,z=4], bar=TRUE]

Note that if a name that is bound to a binding in the left operand is bound to a non-binding in the right operand, the value from the right operand will be chosen instead of the sub-binding.

[foo/bar/a=TRUE,blah=TRUE] ++ [foo=FALSE] == [foo=FALSE,blah=TRUE]

binding_expr - binding_expr

The binary "-" is the binding difference operator. Its result is a binding formed by removing from the left operand all name/value pairs whose names are defined in the right operand. (The values associated in the right operand are ignored.)

[x=1,y=2,z=3] - [y="foo"] == [x=1,z=3]

[x=1,y=2,z=3] - [a=1,b=2,c=2] == [x=1,y=2,z=3]

Assignment Operators

int_var += int_expr
int_var -= int_expr
int_var *= int_expr

list_var += list_expr

text_var += text_expr

binding_var += binding_expr
binding_var ++= binding_expr
binding_var -= binding_expr

The additive ("+", "++", "-") and multiplicative ("*") operators also have assignment versions ("+=", "++=", "-=", "*=").

i = 1;
i += 2;             // i == 3
i *= 3;             // i == 9
i -= 4;             // i == 5

l = <1,2>;
l += <3,4>;         // l == <1,2,3,4>

t = "foo";
t += "bar";         // t == "foobar"

b = [foo/x=1];
b += [bar=TRUE];    // b == [foo=[x=1],bar=TRUE]
b ++= [foo/y=2];    // b == [foo=[x=1,y=2],bar=TRUE]
b -= [foo=0];       // b == [bar=TRUE]

Note that using an assignment operator is not an expression, but an assignment statement.

i = (j *= 2) + 1;                  // syntax error

j *= 2; i = j + 1;                 // correct

b -= (c ++= [foo/bar/baz=<2>]);    // syntax error

c ++= [foo/bar/baz=<2>]; b -= c;   // correct

Summary

Operator/Expression and Operand Types Result Type Description
identifier any Variable reference.
if bool then expr_1 else expr_2 any Conditional expression.
function([arg_1, arg_2, ...]) any Function call.
{ statement; ... return expr; }
{ statement; ... value expr; }
any Statement block.
bool == bool  bool Boolean equality test.
bool != bool  bool Boolean inequality test.
int == int  bool Integer equality test.
int != int  bool Integer inequality test.
text == text  bool Text equality test.
text != text  bool Text inequality test.
list == list  bool List equality test. Recursively compares all list elements.
list != list  bool List inequality test. Recursively compares all list elements.
binding == binding  bool Binding equality test. Recursively compares all name/value pairs. The name/value pairs in a binding are ordered, and bindings must have the same order to be considered equal.
binding != binding  bool Binding inequality test. Recursively compares all name/value pairs. The name/value pairs in a binding are ordered, and bindings are considered in-equal if they do not have the same order.
int < int  bool Integer less-than comparison.
int > int  bool Integer greater-than comparison.
int <= int  bool Integer less-than-or-equal-to comparison.
int >= int  bool Integer greater-than-or-equal-to comparison.
!bool  bool Boolean inversion.
bool || bool  bool Conditional OR, with the same short-circuit semantics as C/C++.
bool && bool  bool Conditional AND, with the same short-circuit semantics as C/C++.
bool => bool  bool Conditional implication, with short-circuit semantics.  A => B is equivalent to !A || B.
int + int int Integer addition.  Returns the sum of the two integer operands. is returned. 
int - int int Integer subtraction.  Returns the integer difference (the first operand minus the second).
- int  int Unary integer negation.  -I is equivalent to 0 - I.
int * int int Integer multiplication.  Returns the integer product of the two operands.
text + text  text Text concatenation operator.
list + list  list List concatenation operator. Equivalent to _append.
binding / identifier
binding / $text_var
binding / $(text_expr)
binding / %text_expr%
any Binding lookup operator.
binding ! identifier
binding ! $text_var
binding ! $(text_expr)
binding ! %text_expr%
bool Binding name-existence test operator.
binding + binding  binding Binding overlay operator.
binding ++ binding  binding Binding recursive overlay operator.
binding - binding  binding Binding subtraction operator.

Assignment Operator and Operand Type Variable Type Description
var += int; int Integer addition.
var -= int; int Integer subtraction.
var *= int; int Integer multiplication.
var += text; text Text concatenation.
var += list; list List concatenation.
var += binding; binding Binding overlay.
var ++= binding; binding Binding recursive overlay.
var -= binding; binding Binding subtraction.


Kenneth C. Schalk <ken@xorian.net>   / Vesta SDL Programmer's Reference