Expressions

The simulation language contains a variety of expressions, each of which are well typed and evaluation of which will not cause side-effects, so can be considered to be functional.

Data Structures

Data structures are atomic expressions and are described fully on the data structures page.

Function Calls

Function calls in the simulation language are provided as a “get out clause” from the necessity of providing a richer set of operations on data structures. Instead of providing a full and complex set of operators for strings and arrays (for example, string concatenation or array indexing), it was decided to implement such operations as built-in functions in the simulator/language runtime. This keeps the core simulation language simple, while providing enough flexibility in data structure manipulation where necessary.

The language provides no way for users to define their own functions — all function calls from expressions are to built-in functions in the simulator itself. If you need a new built-in function to be added to the language, please file a bug report.

Functions in the simulation language have the following characteristics:

  • No side-effects and no dependence on global state. All functions are completely pure.

  • Strong polymorphic typing. The return type of a function is itself a pure function of the input types so that, for example, the structHead function has return type s when given input of type (sas), and return type u when given input of type (ua{sv}a{sv}).

  • Static typing. All function types are checked at parse time rather than run time.

  • Call-by-reference or call-by-value. Most functions use call-by-value semantics, but the pairKeys function is defined to use call-by-reference for its second parameter to better allow fuzzing.

A function call is made using the function's name, followed by a tuple of its parameters. A full listing of the built-in functions can be found on the functions page.

Function Call

arrayRemove (object->ArrayOfThings, 0u)

Numerical Operators

A standard selection of numerical operators are defined in the language, all binary operators. The type of such a numerical expression is the type of its left-hand child expression. The types of the two child expressions do not have to be equal, though both have to be numeric types.

Integer arithmetic saturates on over- or under-flow in the language, so for example the value of 2 - 6 is 0 for unsigned integer types, rather than under-flowing and wrapping round to a high positive number. As with other programming languages, the value zero is unsigned for signed integer types — there is no distinction between 0 and -0.

x * y

Multiply one numeric value with another.

x / y

Divide one numeric value by another. Integer division truncates fractional parts, rounding towards zero.

Division by zero results in the maximum value for the type, with the same sign as the dividend (x). Dividing zero by zero gives zero. This is so that simulation descriptions don't have to handle division by zero exceptions.

x % y

Take the modulus of one numeric value with respect to another. Floating point values are converted to 64-bit signed integers before the modulus is taken.

The modulus of two signed numbers takes the sign of the dividend (x). Taking the modulus of a number with zero gives zero as the result. This preserves the invariant that y * (x / y) + (x % y) ≡ y for all x and y (except y == 0, which gives zero as the result).

x + y

Add one numeric value to another.

x - y

Subtract one numeric value from another.

Boolean Operators

A standard selection of boolean operators are defined in the language, with two binary and one unary operator. The type of a boolean expression is boolean, and all of its children must have boolean type.

x && y

Multiply one numeric value with another (potentially of a different type).

x || y

Multiply one numeric value with another (potentially of a different type).

!x

Multiply one numeric value with another (potentially of a different type).

Comparison Operators

A standard selection of comparison operators are defined in the language, all binary operators. The four numerical comparison operators only accept numerical values (of any numerical type — integeric or floating point), though the left- and right-hand values may be of different types. The two equality comparison operators accept any type (basic or container) of values, but the left- and right-hand values must be of the same type. The type of any comparison expression is boolean.

x <~ y

Test whether the first number is less than the second.

x <= y

Test whether the first number is less than or equal to the second.

x ~> y

Test whether the first number is greater than the second.

x >= y

Test whether the first number is greater than or equal to the second.

x == y

Test whether the two values are equal.

x != y

Test whether the two values are not equal.