135 Expression Grammar
(************** Expressions **************)
Expression =
EquivalenceExpression;
EquivalenceExpression =
ImplicationExpression,
{ "↔", ImplicationExpression };
ImplicationExpression =
OrExpression,
[ "→", ImplicationExpression
| "←", ImplicationExpression ];
OrExpression =
AndExpression,
{ "∨", AndExpression };
AndExpression =
NotExpression,
{ "∧", NotExpression };
NotExpression =
"¬", NotExpression
| RenameExpression;
RenameExpression =
DifferenceExpression,
{ "as", Identifier };
DifferenceExpression =
UnionExpression,
{ ( SetDifferenceOperator | PathDifferenceOperator ), UnionExpression };
SetDifferenceOperator =
"∖" (* Unicode set minus U+2216; canonical spelling *)
| "\"; (* reverse solidus U+005C; keyboard input alias *)
PathDifferenceOperator =
"--"; (* path operator difference *)
UnionExpression =
IntersectionExpression,
{ ( "⊔" | "∪" | "|" ), IntersectionExpression };
IntersectionExpression =
ComparisonExpression,
{ ( "⊓" | "∩" | "⨝" ), ComparisonExpression };
ComparisonExpression =
( RangeExpression | TypeExpression ), "isa", TypeExpression
| RangeExpression, { ComparisonOperator, RangeExpression };
RangeExpression =
AdditiveExpression,
[ "..", AdditiveExpression ];
ComparisonOperator =
"=" | "≠"
| "<" | ">" | "≤" | "≥"
| "⊂" | "⊃" | "⊆" | "⊇" | "⊄" | "⊅"
| "∈" | "∉";
AdditiveExpression =
MultiplicativeExpression,
{ ( "+" | "-" ), MultiplicativeExpression };
MultiplicativeExpression =
PowerExpression,
{ ( "*" | "/" ), PowerExpression };
PowerExpression =
CardinalityExpression,
[ "^", PowerExpression ];
CardinalityExpression =
CompositionExpression,
[ CardinalitySuffix ];
CompositionExpression =
PostfixExpression,
{
".", PathSegment
| "⇒", PostfixExpression
};
PathSegment =
FoldSuffix
| FilterExpression
| ProjectionExpression
| ReceiverCall
| PostfixExpression;
ProjectionExpression =
RoundExpression;
ReceiverCall =
Identifier, [ GenericApplication ], "(", [ CallArgumentList ], ")";
PostfixExpression =
PrimaryExpression,
{ PostfixSuffix };
PostfixSuffix =
GenericApplication
| PostfixRound
| PostfixSquare
| PostfixCurly
| "!"
| "~";
PostfixRound =
"(", [ CallArgumentList ], ")";
PostfixSquare =
"[", [ Slice | ExpressionList ], "]";
Slice =
[ SliceBound ], ":", [ SliceBound ];
SliceBound =
AdditiveExpression;
PostfixCurly =
"{", [ ExpressionList ], "}";
CardinalitySuffix =
":",
(
"?"
| "+"
| ExpressionCardinalityBound,
[ "+" | "..", ( "*" | "∞" | ExpressionCardinalityBound ) ]
);
ExpressionCardinalityBound =
IntegerLiteral
| Identifier
| "(", Expression, ")";
PrimaryExpression =
Literal
| LetExpression
| IfExpression
| InverseExpression
| Identifier
| "this"
| "none"
| "*"
| "∅"
| "@", Identifier
| "Σ", [ FoldTail ]
| FilterExpression
| RoundExpression
| CurlyExpression
| UnaryExpression;
UnaryExpression =
( "+" | "-" ), PrimaryExpression;
InverseExpression =
"inverse", CompositionExpression;
GenericApplication =
"<", [ GenericArgumentList ], ">";
LetExpression =
"let", LetBinding, { ",", LetBinding }, "in", Expression;
LetBinding =
Identifier, "=", Expression;
IfExpression =
"if", Expression, "then", Expression, "else", Expression;
FilterExpression =
"[", Expression, "]";
RoundExpression =
"(", [ ExpressionList ], ")";
CurlyExpression =
"{", [ ExpressionList ], "}";
ExpressionList =
Expression, { ",", Expression };
CallArgumentList =
CallArgument, { ",", CallArgument };
CallArgument =
Expression
| "&", Identifier
| "^", Identifier;
FoldSuffix =
"Σ", [ FoldTail ];
FoldTail =
NamedFoldApplication
| FoldBlock;
NamedFoldApplication =
QualifiedFoldName, [ GenericApplication ], [ "(", [ ExpressionList ], ")" ];
QualifiedFoldName =
FoldName, { ".", FoldName };
FoldName =
Identifier;
FoldBlock =
UnseededFoldBlock
| SeededFoldBlock;
UnseededFoldBlock =
"{", Expression, ";", Identifier, Identifier, "↦", Expression, "}";
SeededFoldBlock =
"{",
"first", Identifier, "↦", Expression,
";",
Identifier, Identifier, "↦", Expression,
"}";
Literal =
BooleanLiteral
| IntegerLiteral
| RealLiteral
| StringLiteral;
Operator precedence table
The expression grammar is written in precedence levels. The following table is a summary of those productions. Operators appearing earlier in the table bind more tightly than those appearing later. Where there is any doubt, the grammar productions are normative.
| Precedence (high → low) | Operators | Associativity | Notes |
|---|---|---|---|
| 1 (highest) | literals, identifiers, grouping, primary delimiter forms | n/a | Includes this, none, *, ∅, @name, Σ..., [], (), and {}. |
| 2 | +, - (prefix) |
right-associative | Unary arithmetic prefix operators. |
| 3 | <...>, (...), [...], {...}, !, ~ |
left-associative | Postfix generic, call, index, slice, constructor, path negation, and path complement forms. |
| 4 | ., ⇒, .[E], .f(...), .Σ, .Σname, .Σname<...>(...), .Σqualified.name, .Σ{...} |
left-associative | Path composition, universal path composition, explicit filter segments, receiver calls, and folds. |
| 5 | :?, :+, :k, :k+, :k..m |
n/a | Cardinality qualifiers on complete path expressions. |
| 6 | ^ |
right-associative | Exponentiation. |
| 7 | *, / |
left-associative | Multiplicative arithmetic operators. |
| 8 | +, - (binary) |
left-associative | Additive arithmetic operators. |
| 9 | .. |
non-associative | Inclusive integer range expression. |
| 10 |
=, ≠,
<, >, ≤, ≥,
⊂, ⊃, ⊆, ⊇,
⊄, ⊅, ∈, ∉,
isa
|
left-associative syntax | Comparison, set comparison, membership, and value-or-type-expression type tests. |
| 11 | ⊓, ∩, ⨝ |
left-associative | Path conjunction, set intersection, and join. |
| 12 | ⊔, ∪, | |
left-associative | Path disjunction, set union, and the alternative operator. |
| 13 | --; set difference |
left-associative | -- is path difference. Set difference uses the canonical set-minus character described in the lexical grammar, with keyboard backslash accepted as an input alias. |
| 14 | as |
left-associative | Rename operator. |
| 15 | ¬ |
right-associative | Logical negation. |
| 16 | ∧ |
left-associative | Logical conjunction. |
| 17 | ∨ |
left-associative | Logical disjunction. |
| 18 | →, ->, ← |
right-associative | Logical implication. |
| 19 | ↔ |
left-associative | Logical equivalence. |
| 20 (lowest) | if ... then ... else ... |
special form | Conditional expression; the condition and branches are full expressions. |
Index and slice expressions
A postfix square expression containing an expression list is an index operation, such as items[0]
or matrix[i, j]. A postfix square expression containing a colon is a one-dimensional slice:
name[0:3]
name[:3]
name[3:]
name[:]
name[-4:]
The lower bound is inclusive and the upper bound is exclusive. An omitted lower bound denotes the
start of the value, and an omitted upper bound denotes its end. Negative bounds count backwards
from the end. Slice bounds are additive expressions, so bounds such as start + 1 and end - 1
are permitted.
A slice is an ordinary postfix expression when read and may also denote a location when used as an
update target. For example, name[:3] reads the selected prefix, while name[:3] := "Dr "
replaces that prefix. Assigning to an empty slice inserts text, and assigning the empty string to a
non-empty slice deletes the selected text, as described in 103 Variable Updates.
The current syntax supports one-dimensional slices only. A slice cannot be combined with other comma-separated square items; multidimensional slicing is not defined.
Notes on ambiguity and associativity
Repeated comparison operators are parsed as ordinary left-nested binary expressions, not as
mathematical comparison chains. For example, a = b = c is parsed like (a = b) = c, not like
a = b ∧ b = c. Use explicit conjunction when a chain of comparisons is intended.
The type-test operator isa is parsed at comparison precedence. Its left operand may be either a
value expression or a type expression, and its right operand is a type expression. Thus
value isa T tests membership of a value in T, while S isa T tests whether S is a subtype of T.
The same keyword is used in declaration and generic constraint syntax, for example
where T isa Real.
The operator ⇒ is universal path composition, not logical implication. Logical implication
is written →.
Parentheses may always be used to override or clarify the default precedence and associativity rules.