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 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.
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; ... 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.)
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.
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.
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.
! 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.
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.)
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_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!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_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] |
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 |
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. |