# Binding Power Write an expression grammar the way you would say it, and the parse comes out wrong: `2 * 3 + 5` groups by how the recursion happened to fall rather than by what arithmetic means. **Binding power** is how you tell APM what the operators are worth. A rule bound to a table is parsed by a precedence loop instead of a greedy tail. Left recursion in the rule is fine — write it directly. ## The table Each operator gets two numbers: a **left binding power** and a **right binding power**. Entries end with `;`. ```parser bindpow bp { "or" : (10, 11) ; "and" : (20, 21) ; "==" : (30, 31) ; "<" : (40, 41) ; "+" : (50, 51) ; "-" : (50, 51) ; "*" : (60, 61) ; "/" : (60, 61) ; "not" : (0, 70) ; }; ``` Higher numbers bind tighter, so `*` takes its operands before `+` gets a look. ## Associativity is the gap, not a keyword There is no `left` or `right` to write. The relationship between an operator's two numbers **is** its associativity. In APM, an operator grabs what is to its left when the left number is high enough to beat what is already holding it, and it keeps going to the right while the right number stays above what follows. So: | Written | Means | Because | | :--- | :--- | :--- | | `r = l + 1` | **left**-associative — `a - b - c` is `(a - b) - c` | the right side binds one step tighter, so the second `-` cannot reach back and take `b` away from the first | | `r = l - 1` | **right**-associative — `a ^ b ^ c` is `a ^ (b ^ c)` | the right side binds one step looser, so the second `^` wins `b` | | `r = l` | left-associative, the same as `l + 1` in practice | a tie goes to whoever got there first | The gap is always one step. Bigger gaps between the two numbers of the **same** operator buy nothing; it is the gap between *different* operators that sets precedence. ## Prefix and postfix are a zero A side that binds nothing is `0`, and that is also how the kind is written: | | | | :--- | :--- | | `(0, r)` | prefix — `not a` | | `(l, 0)` | postfix — `a++` | | `(l, r)` | infix — `a + b` | The slot is genuinely dead, so a `0` can never be mistaken for a real power. A prefix arm is only ever matched where an operand is expected, where nothing reads its left; a postfix arm folds immediately, so nothing reads its right. What decides which kind an occurrence *is* is the **grammar** — where the alternative sits — not what the table calls it. The table only supplies the numbers. ## Binding a rule to a table `feat {"bind": }` in front of the definition -- `feat` always goes in front of what it configures, an action or a [semvar](variable.md): ```parser feat {"bind": bp} expr := atom | expr . "or" . expr | expr . "and" . expr | expr . "==" . expr | expr . "<" . expr | expr . "+" . expr | expr . "-" . expr | expr . "*" . expr | expr . "/" . expr | "not" . expr ; atom := number | string | ident | group; group := "(" . expr . ")"; ``` The first alternative that is not an operator arm is the **operand** — here `atom`. The rest are the arms, and each must name an operator the table declares. A `feat` binding applies to the action itself, so **every call site** parses it that way. It is the right choice when the rule *is* an expression rule and there is no other way you would ever want it read. ## The tree a bound rule makes Without a map, a bound rule wraps each fold in a node of its own and the operator sits beside its operands: ``` expr "2 + 3 * 4" ├── atom "2" ├── text "+" └── expr "3 * 4" ``` A map on the rule puts the **operator on top**, which is the shape every hand-written expression parser produces: ```parser feat {"bind": bp} expr := ( atom | 'l'expr . "+" . 'r'expr | 'l'expr . "*" . 'r'expr ) -> ( atom | "+": ('l' 'r') | "*": ('l' 'r') ); ``` ``` text "+" ├── atom "2" └── text "*" ├── atom "3" └── atom "4" ``` The two labels name the operands. They are not units of the arm -- the loop supplies them -- which is why they are the one place a label means something the body does not contain. ### An arm may carry more than its operator A ternary matches its middle operand inside the arm, and a subscript matches a whole expression between brackets. Both are ordinary units, and both keep what the map says about them: ```parser bindpow post { "?" : (4, 3) ; "[" : (50, 0) ; "+" : (20, 21) ; }; feat {"bind": post} e := ( num | 'l'e . "+" . 'r'e | 'c'e . "?" . 'q'e . ":" . 'x'e | 'h'e . "[" . 'i'e . "]" ) -> ( num | "+": ('l' 'r') | "?": ('c' 'q' 'x') | "[": ('h' 'i') ); ``` ``` 1+2?3:4 1+2[3] text "?" text "+" ├── text "+" ├── num "1" ├── e "3" └── text "[" └── num "4" ├── num "2" └── e "3" ``` A unit the map does not name makes nothing, here as everywhere -- which is what drops the `:` and the `]`. :::{note} `['l']` on an operand is wrong, and quietly so. An operand is either a plain unit or an earlier fold; ascend cannot tell them apart, so it would lift an inner fold's children out and flatten the nesting the powers just built. ::: ## Binding at the call site The other way round is to leave the rule unbound and pick the table where you call it, with `table::-rule`: ```parser number = <0:9>+; bindpow bp { "+" : (50, 51) ; "*" : (60, 61) ; }; expr := number | expr . "+" . expr | expr . "*" . expr; top := bp::-expr; # parse expr under bp, here ``` Over `2 + 3 * 4` that gives `2 + (3 * 4)`. Use this when the same rule should be read with different precedences in different places, or when you want the grammar to stay readable as a plain recursive rule and the precedence to be a decision made elsewhere. :::{important} Every operator the rule writes must appear in the table, and every operator in the table must appear in the rule. An operator that is not declared simply ends the expression, which would silently truncate a parse rather than fail it — so it is refused when the grammar is validated. ::: :::{seealso} `bindpow` and `feat` are [reserved words](keywords.md), and a table's name shares the name space actions live in. :::