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.