# Expressions Expressions are the parts that compute values (and optionally assign them). **note:** We could do recursive descent formal specification. But I think it is rather ugly. We could also do the generic case, and then simply state what the precedence rules are? ## Assignment Expression An assignment expression is a right-to-left associative binary operator that evaluates the right hand side expression and assigns it to the left hand side. It is defined as: ``` AssignOp = "=" | "@=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" ExprAssign = ExprConditional AssignOp ExprConditional ``` Where the assignment operators are: - `=`: Set assignment: assign right hand side to left hand side. - `@=`: Concatenate assignment: concatenates right hand side to left hand side. - `*=`: Multiply: multiplies the left hand side with the value on the right hand side. - `/=`: Divide: divides the left hand side by the number on the right hand side. - `%=`: Remainder: Applies modulo operator directly to left hand side using the divisor on the right hand side. - `+=`: Addition: adds right hand side to left hand side. - `-=`: Subtraction: subtracts right hand side from left hand side. - `<<=`: Shift left: Bitshifts the number on the left hand side, towards the left, by the number of bits on the right hand side. - `>>=`: Shift right: Bitshifts the number on the left hand side, towards the right, by the number of bits on the right hand side. - `&=`: Bitwise and: Performs a bitwise `and` on the left hand side using the integer on the right hand side. - `^=`: Bitwise xor: Performs a bitwise `xor` on the left hand side using the integer on the right hand side. - `|=`: Bitwise or: Performs a bitwise `or` on the left hand side using the integer on the right hand side. ## Conditional Expression A conditional expression is an expression that contains a test expression and two nested subexpressions. If the test expression evaluates to true, then the first nested subexpression is evaluated. Otherwise the second nester subexpression is evaluated. It is defined as: ``` ExprConditional = ExprConcat "?" Expr ":" Expr ``` Due to its formulation one can see that nested conditional expressions are eagerly evaluated in the subexpressions, but not in the test expression. ## Binary Operator Expressions Binary operators apply an operation to its two subexpressions. We have the following possible binary operators: ``` BinaryOp = "@" | "||" | "&&" | "|" | "^" | "&" | "==" | "!=" | "<" | ">" | "<=" | ">=" | "<<" | ">>" | "+" | "-" | "*" | "/" | "%" ``` Where we have: - `@`: Concatenation - `||`: Logical or - `&&`: Logical and - `|`: Bitwise or - `^`: Bitwise xor - `&`: Bitwise and - `==`: Equality test - `!=`: Inequality test - `<`: Less-than comparison - `>`: Greater-than comparison - `<=`: Less-than-or-equal-to comparison - `>=`: Greater-than-or-equal-to comparison - `<<`: Bitshift left - `>>`: Bitshift right - `+`: Addition - `-`: Subtraction - `*`: Multiplication - `/`: Division - `%`: Remainder/modulo We'll define the particular kinds of expressions that use these operators (and remaining kinds of operators in expressions) in such an order that the precedence rules are satisfied. The first in the chain has the lowest precedence. ``` ExprBinConcat = ExprBinLogicalOr ("@" ExprBinLogicalOr)* ExprBinLogicalOr = ExprBinLogicalAnd ("||" ExprBinLogicalAnd)* ExprBinLogicalAnd = ExprBinBitwiseOr ("&&" ExprBinBitwiseOr)* ExprBinBitwiseOr = ExprBinBitwiseXor ("|" ExprBinBitwiseXor)* ExprBinBitwiseXor = ExprBinBitwiseAnd ("^" ExprBinBitwiseAnd)* ExprBinBitwiseAnd = ExprBinEquality ("&" ExprBinEquality)* ExprBinEquality = ExprBinRelational (["==" | "!="] ExprBinRelational)* ExprBinRelational = ExprBinShift (["<", ">", "<=", ">="] ExprBinShift)* ExprBinShift = ExprBinAddSub (["<<", ">>"] ExprBinAddSub)* ExprBinAddSub = ExprBinMulDivMod (["+" | "-"] ExprBinMulDivMod)* ExprBinMulDivMod = ExprPrefix (["*" | "/" | "%"] ExprPrefix)* ``` ## Prefix Operator Expression Prefix expressions apply their operators to the suffixed expression. Prefix expressions are evaluated right-to-left. The following prefix operators are supported: ``` PrefixOp = "+" | "-" | "~" | "!" ``` Where we have: - `+`: Positive. Essentially meaningless in this rather strictly typed language, but supported for completeness (and sometimes it looks visually appealing in e.g. `sign = (x > 0) ? +1 : -1`). - `-`: Negation. - `~`: Bitwise not. - `!`: Logical not. Which are used to define the prefix expression itself as: ``` ExprPrefix = (PrefixOp ExprPrefix)? | ExprPostfix ``` ## Postfix Operator Expression Postfix operations are slightly more involves, because the postfixes take arguments following a particular pattern. We have the following possible postfix expressions: ``` PostfixOp = ("[" Expr "]") | ("[" Expr ".." Expr "]") | ("." Ident) ``` Where we have: - `[a_number]`: Indexing expression, can be applied to arrays and strings. - `[lower_idx..higher_idx]`: Slicing expressions, takes a range of values from an array or string. - `.a_field`: Select expression, can only be applied to struct values and returns the selected field's value. Which are used in: ``` ExprPostfix = ExprPrimary PostfixOp* ``` ## Primary Expressions Primary expressions are the ones with the highest precedence. We have the variants: ``` ExprParens = "(" Expr ")" ExprLitArray = "{" (Expr ",")* Expr? "}" ExprLitInt = TkInt ExprLitString = TkStr ExprLitChar = TkChar ExprLitBool = TkBool ExprLitStructField = Ident ":" Expr ExprLitStruct = TypeRef "{" (ExprLitStructField ",")* ExprLitStructField? "}" ExprLitEnum = TypeRef "::" Ident ExprLitUnion = TypeRef "::" Ident ("(" (Expr ",")* Expr? ")")? ExprBinding = KwLet ExprPrefix "=" ExprPrefix ExprCall = TypeRef "(" (Expr ",")* Expr? ")" ExprCast = "cast" PolyArgs? "(" Expr ")" ExprVar = Ident ``` Here we find, consecutively: - The parenthesized expression, indicating its contents should be evaluated at a higher precedence. - The various constructable literals. - The binding expression, where the values on the right hand side of the equality are bound to the binding variables on the left hand side. - Calling of functions (and components). - Casting of values. - Reference to a variable. Naturally the validity of each of these expressions depends strongly on the context in which they're used and their arguments. Likewise for deciding which expression should be used (e.g. an enum variant and a union variant without embedded values differ only in the fact that their type has been defined using a different keyword). We'll come back to all of these rules in a later section of this document. For now, we can group all of these possibilities together as: ``` ExprPrimary = ExprParens | ExprLitArray | ExprLitInt | ExprLitString | ExprLitChar | ExprLitBool | ExprLitStruct | ExprLitEnum | ExprLitUnion | ExprBinding | ExprCall | ExprCast | ExprVar ``` ## Putting Everything Together Now that we have a chain of expressions defined, each depending on the next one in the chain, we can finish by simply defining the basic `Expr` as: ``` Expr = ExprAssign ```