elisp: Operator Precedence Grammars
22.7.1.2 Operator Precedence Grammars
.....................................
SMIE’s precedence grammars simply give to each token a pair of
precedences: the left-precedence and the right-precedence. We say ‘T1 <
T2’ if the right-precedence of token ‘T1’ is less than the
left-precedence of token ‘T2’. A good way to read this ‘<’ is as a kind
of parenthesis: if we find ‘... T1 something T2 ...’ then that should be
parsed as ‘... T1 (something T2 ...’ rather than as ‘... T1 something)
T2 ...’. The latter interpretation would be the case if we had ‘T1 >
T2’. If we have ‘T1 = T2’, it means that token T2 follows token T1 in
the same syntactic construction, so typically we have ‘"begin" = "end"’.
Such pairs of precedences are sufficient to express left-associativity
or right-associativity of infix operators, nesting of tokens like
parentheses and many other cases.
-- Function: smie-prec2->grammar table
This function takes a _prec2_ grammar TABLE and returns an alist
suitable for use in ‘smie-setup’. The _prec2_ TABLE is itself
meant to be built by one of the functions below.
-- Function: smie-merge-prec2s &rest tables
This function takes several _prec2_ TABLES and merges them into a
new _prec2_ table.
-- Function: smie-precs->prec2 precs
This function builds a _prec2_ table from a table of precedences
PRECS. PRECS should be a list, sorted by precedence (for example
‘"+"’ will come before ‘"*"’), of elements of the form ‘(ASSOC OP
...)’, where each OP is a token that acts as an operator; ASSOC is
their associativity, which can be either ‘left’, ‘right’, ‘assoc’,
or ‘nonassoc’. All operators in a given element share the same
precedence level and associativity.
-- Function: smie-bnf->prec2 bnf &rest resolvers
This function lets you specify the grammar using a BNF notation.
It accepts a BNF description of the grammar along with a set of
conflict resolution rules RESOLVERS, and returns a _prec2_ table.
BNF is a list of nonterminal definitions of the form ‘(NONTERM RHS1
RHS2 ...)’ where each RHS is a (non-empty) list of terminals (aka
tokens) or non-terminals.
Not all grammars are accepted:
• An RHS cannot be an empty list (an empty list is never needed,
since SMIE allows all non-terminals to match the empty string
anyway).
• An RHS cannot have 2 consecutive non-terminals: each pair of
non-terminals needs to be separated by a terminal (aka token).
This is a fundamental limitation of operator precedence
grammars.
Additionally, conflicts can occur:
• The returned _prec2_ table holds constraints between pairs of
tokens, and for any given pair only one constraint can be
present: T1 < T2, T1 = T2, or T1 > T2.
• A token can be an ‘opener’ (something similar to an
open-paren), a ‘closer’ (like a close-paren), or ‘neither’ of
the two (e.g., an infix operator, or an inner token like
‘"else"’).
Precedence conflicts can be resolved via RESOLVERS, which is a list
of _precs_ tables (see ‘smie-precs->prec2’): for each precedence
conflict, if those ‘precs’ tables specify a particular constraint,
then the conflict is resolved by using this constraint instead,
else a conflict is reported and one of the conflicting constraints
is picked arbitrarily and the others are simply ignored.