Expressions

ll.ul4c supports many of the operators supported by Python. The following subchapters describe all expressions/operators that UL4 supports and are ordered from highest precedence to lowest.

Generator expressions

UL4 supports generator expressions with look like list comprehensions without the square brackets. Generator expression do not create lists in memory but instead return an iterable that can be iterated once. Function and methods that require an iterable argument can directly consume such iterables:

<?print ", ".join("(" + c + ")" for c in "gurk")?>

will output

(g), (u), (r), (k)

Outside of function/method arguments (or when more that one argument is passed) parentheses are required around generator expressions:

<?code ge = ("(" + c + ")" for c in "gurk")?>
<?print ", ".join(ge)?>

Hint

Generator expressions are implemented by ll.ul4c.GeneratorExpressionAST.

Index/slice access

Index and slice access is available for all container types, i.e. in the expression a[b] the following type combinations are supported:

  • string, integer: Returns the bth character from the string a. Note that negative b values are supported and are relative to the end, so a[-1] is the last character.

  • list, integer: Returns the bth list entry of the list a. Negative b values are supported too.

  • dict, string: Return the value from the dictionary a corresponding to the key b. Note that some implementations might support keys other than strings too. (The Python and Java implementations do for example. The Javascript implementation does too, if Map is supported.)

If the specified key doesn’t exist or the index is out of range for the string or list, a special “undefined” object is returned.

Slices are also supported (for list and string objects). As in Python one or both of the indexes may be missing to start at the first or end after the last character/item. Negative indexes are relative to the end. Indexes that are out of bounds are simply clipped, so

<?print "Hello, World!"[7:-1]?>

prints World and

<?print "Hello, World!"[:-8]?>

prints Hello.

Hint

Index/slice access is implemented by ll.ul4c.ItemAST.

Attribute access

For string keys it’s also possible to access dictionary entries via the attribute access operator ., i.e. foo.key is the same as foo["key"] if foo is a dictionary.

Hint

Attribute access is implemented by ll.ul4c.AttrAST.

Function calls

A function call in UL4 looks like this: date(2014, 10, 9, 17, 29). (this returns the date object @(2014-10-09T17:29)). Some of the trailing arguments in a function call might be optional and have default values. For example the first three arguments for the date function (year, month and day) are required, the remaining four arguments (hour, minute, second and microsecond) are optional and default to 0.

Parameter values can also be passed via keyword arguments, i.e. date(2014, 10, 9) could also be written as date(day=9, month=10, year=2014).

Furthermore Python’s * and ** syntax is supported for passing additional positional or keyword arguments. For example:

<?code args = [2014, 10, 9, 17, 29]?>
<?code d = date(*args)?>

is the same as:

<?code d = date(2014, 10, 9, 17, 29)?>

The same can also be done with a keyword dictionary and the ** syntax:

<?code kwargs = {"day": 9, "month": 10, "year": 2014, "hour": 17: "minute": 29}?>
<?code d = date(**kwargs)?>

Of course it’s also possible to mix argument passing mechanics:

<?code d = date(2014, *[10, 9], **{"hour": 17, "minute": 29})?>

or

<?code d = date(2014, month=10, day=9, **{'hour': 17, 'minute': 29})?>

However the * and ** arguments can only be use at the end of the argument list and positional arguments must always be before keyword arguments.

A list of builtin functions can be found in Functions.

Hint

This documentation uses Python’s / and * notation to specify positional-only and keyword-only arguments. So

<?ul4 f(x, /, y, *, z)?>

means that the function f accepts the parameter x only when passed by position, y can be passed either by position or by keyword and z will only be accepted when passed by keyword.

Hint

Function calls are implemented by ll.ul4c.CallAST.

Unary operators

Arithmetic negation

The unary operator - inverts the sign of its operand, which must be an integer, float of boolean value:

<?code x = 42?><?print -x?>

prints -42. For - boolean values are treated as the numbers 0 and 1, i.e.:

<?code x = True?><?print -x?>

prints -1.

Hint

Arithmetic negation is implemented by ll.ul4c.NegAST.

Binary negation

The unary operator ~ inverts the bits of an integer or boolean value. Non-negative numbers are interpreted as having an unlimited number of leading 0 bits and negative numbers are interpreted as having an unlimited number of leading 1 bits. The means that ~x will be negative if x is non-negative and vice versa.

Hint

Arithmetic negation is implemented by ll.ul4c.BitNotAST.

Multiplicative binary operators

Multiplication

The multiplication operator * returns the arithmetic product of its operands (which must be integer, float or boolean values). Furthermore it’s possible to multiply a sequence (i.e. a string or list) with a non-negative integer to get a new sequences that repeats the items of the original sequence a number of times, e.g. "foo" * 2 returns "foofoo" and [1, 2, 3] * 3 return [1, 2, 3, 1, 2, 3, 1, 2, 3]. Multiplying with 0 returns an empty string or list.

Hint

Multiplication is implemented by ll.ul4c.MulAST.

True division

The true division operator / returns the quotient of its operands (which must be integer, float or boolean values). The result is always a float value. 1/2 returns 0.5.

Hint

True division is implemented by ll.ul4c.TrueDivAST.

Floor division

The float division operator // returns the quotient of its operands (which must be integer, float or boolean values) rounded down to an integer (rounding is always done towards -infinity, i.e. (-25)/10 returns -3). If any of the operands is a float the result is a float too, otherwise it’s an integer.

Hint

Floor division is implemented by ll.ul4c.FloorDivAST.

Modulo

The modulo operator % returns the remainder from the division of the first operand by the second, e.g. 15 % 7 returns 1.

Hint

The modulo operator is implemented by ll.ul4c.ModAST.

Additive binary operators

Addition

The addition operator + returns the sum of its operands (which must be integer, float or boolean values). Furthermore sequences of the same type can be added, so "foo" + "bar" returns "foobar" and [1, 2] + [3, 4] returns [1, 2, 3, 4].

Hint

Addition is implemented by ll.ul4c.AddAST.

Subtraction

The subtraction operator - returns the difference of its operands (which must be integer, float or boolean values).

Hint

Subtraction is implemented by ll.ul4c.SubAST.

Bit shift operators

Binary left shift operator

The binary left shift operator << shifts the bits of its first operand (an integer or boolean) to the left by the number of positions given by the second operand (which must also be an integer or boolean).

Hint

The binary left shift operator is implemented by ll.ul4c.ShiftLeftAST.

Binary right shift operator

The binary right shift operator >> shifts the bits of its first operand (an integer or boolean) to the right by the number of positions given by the second operand (which must also be an integer or boolean).

Hint

The binary right shift operator is implemented by ll.ul4c.ShiftRightAST.

Binary bitwise “and” operator

The bitwise and operator & returns the bitwise “and” combination of its operands (which must be integer or boolean values). E.g. 6 & 3 returns 2.

As with the unary operator ~, negative numbers are interpreted as having an unlimited number of leading 1 bits.

Hint

The binary bitwise “and” operator is implemented by ll.ul4c.BitAndAST.

Binary bitwise “exclusive or” operator

The bitwise exclusive or operator ^ returns the bitwise exclusive “or” combination of its operands (which must be integer or boolean values). E.g. 6 ^ 3 returns 5.

Negative numbers are again interpreted as having an unlimited number of leading 1 bits.

Hint

The binary bitwise “exclusive or” operator is implemented by ll.ul4c.BitXOrAST.

Binary bitwise “inclusive or” operator

The bitwise inclusive or operator | returns the bitwise inclusive “or” combination of its operands (which must be integer or boolean values). E.g. 6 | 3 returns 7.

Negative numbers are again interpreted as having an unlimited number of leading 1 bits.

Hint

The binary bitwise “inclusive or” operator is implemented by ll.ul4c.BitOrAST.

Binary comparison operators

The comparison operators ==, !=, <, <=, > and >= compare the value of the two operands. == and != support comparison of all types of object. All others support comparison of “compatible” objects, which means all “number” objects (integer, float and boolean) can be compared with each other, all other objects can only be compared to objects of the same type.

Hint

These operators are implemented by ll.ul4c.EQAST, ll.ul4c.NEAST, ll.ul4c.LTAST, ll.ul4c.LEAST, ll.ul4c.GTAST and ll.ul4c.GEAST.

Identity comparison operators

The comparison operators is and is not test whether both operands refer to the same object or not.

Note that the behaviour of these operators for “atomic” immutable objects (like integers, floats and strings) is implementation defined.

Hint

These operators are implemented by ll.ul4c.IsAST and ll.ul4c.IsNotAST.

Containment tests

The in operator

The in operator tests whether the first operand is contained in the second operand. In the expression a in b the following type combinations are supported:

  • string, string: Checks whether a is a substring of b.

  • any object, list: Checks whether the object a is in the list b (comparison is done by value not by identity)

  • string, dict: Checks whether the key a is in the dictionary b. (Note that some implementations might support keys other than strings too. E.g. Python and Java do, Javascript does only for Map objects.)

Hint

The in operator is implemented by ll.ul4c.ContainsAST.

The not in operator

The not in operator returns the inverted result of the in operator, i.e. it tests whether the first operand is not contained in the second operand.

Hint

The not in operator is implemented by ll.ul4c.NotContainsAST.

Boolean negation

The unary operator not inverts the truth value of its operand. I.e. not x is True for None, False, the undefined value, 0, 0.0, empty lists, strings, dictionaries and other empty containers and False for everything else.

Hint

The boolean negation operator is implemented by ll.ul4c.NotAST.

Boolean “and” operator

The binary operator and returns whether both of its operands are true. It works like Python and operator by short-circuiting operand evaluation, i.e. if the result is clear from the first operand the seconds won’t be evaluated.

Furthermore and always return one of the operands.

So a and b first evaluates a; if a is false, its value is returned; otherwise, b is evaluated and the resulting value is returned.

Hint

The boolean “and” operator is implemented by ll.ul4c.AndAST.

Boolean “or” operator

The binary operator or returns whether any of its operands is true. Like and evaluation is short-circuited and one of the operands is returned.

For example, the following code will output the data.title object if it’s true, else data.id will be output:

<?printx data.title or data.id?>

Hint

The boolean “or” operator is implemented by ll.ul4c.OrAST.

Conditional expression

The conditional expression (also called an “inline if”) a if c else b first evaluates the condition c. If it is true a is evaluated and returned else b is evaluated and returned.

Hint

The “inline if” operator is implemented by ll.ul4c.IfAST.