Expressions
This chapter describes the expression grammar fic actually parses.
Expressions appear as:
- the right-hand side of assignments inside event bodies;
- the payload of a channel send (
c!e); - the body of a guard (
[b]); - arguments to calls, sub-process calls, and computation-event carriers.
One grammar (Expr) covers literals, variables, calls, sizeof, unary
and binary operators, and field access. There’s no separate arithmetic
vs. logical vs. boolean sub-grammar — a boolean-shaped subset of
Expr is what’s required in guard position (see below), not a
different grammar.
Expr ::= Lit | ident | CExpr | 'sizeof' '(' Type ')'
| UnOp Expr | Expr BinOp Expr
| FieldBase '.' ident -- field access
FieldBase ::= ident | CExpr | FieldBase '.' ident -- var, call, or field chain
Lit ::= int_lit | hex_lit | bool_lit
UnOp ::= '-' | '!' | '~'
BinOp ::= '+' | '-' | '*' | '/' | '%'
| '==' | '!=' | '<' | '<=' | '>' | '>='
| '&&' | '||'
| '&' | '|' | '^' | '<<' | '>>'
Operator precedence
Loosest to tightest:
| Level | Operators |
|---|---|
| 1 | || |
| 2 | && |
| 3 | | |
| 4 | ^ |
| 5 | & |
| 6 | ==, != |
| 7 | <, <=, >, >= |
| 8 | <<, >> |
| 9 | +, - |
| 10 | *, /, % |
| 11 | unary -, !, ~ |
| 12 | primary atoms |
Call expressions (CExpr)
CExpr ::= ident '.' ident '(' (Expr (',' Expr)*)? ')'
A call expression invokes a function defined in an imported .c
file, prefixed with the import alias chosen at the
import declaration. The alias
is required — there are no local callables in Fiducia, only
externally-implemented functions reached through an alias.
The first ident is the alias; the second is the function name. No
whitespace is permitted around the . or before the (:
eth.send_frame() // OK
eth.send_frame(pkt, len) // OK — arguments are full Expr, comma-separated
drv .start() // rejected — whitespace before '.'
drv. start() // rejected — whitespace after '.'
drv.start () // rejected — whitespace before '('
Guard expressions
A guard ([b] P) requires a
boolean-shaped Expr — arithmetic like [a + b] is rejected at parse
time, since a guard must denote a yes/no decision, not a value. Boolean-shaped means:
a boolean literal, a variable, a call, a field access, a !-negated
boolean-shaped expression, or a comparison/&&/|| combination of
boolean-shaped expressions.
[a + b == c] // OK — comparison of arithmetic is boolean-shaped
[eth.is_forward()] // OK — call
[!flag && ready] // OK — negation + conjunction
[true] // OK
[a + b] // rejected — arithmetic isn't boolean-shaped