Navigation
|
Syntax -
syntax Exp = Assoc Label Symbol1 Symbol2 ...
-
syntax Exp = Assoc ( Alt1 | Alt2 | ... )
-
syntax Exp = Assoc Symbol1 Symbol2 ...
Here Assoc is one of: left , right , assoc or non-assoc . See SyntaxDefinitions on how to define alternatives and Symbols.
Description Using Associativity declarations we may disambiguate binary recursive operators.
The semantics are that an associativity modifier will instruct the parser to disallow certain productions to nest at particular argument positions:
-
left and assoc will disallow productions to directly nest in their right-most position.
-
right will disallow productions to directly nest in their left-most position.
-
non-assoc will disallow productions to directly nest in either their left-most or their right-most position.
When associativity is declared for a group of productions, e.g. left ( Alt1 | $Alt _2$ | Alt3) , then each alternative will be mutually associative to each other alternative and itself. If an alternative of a group defines its own local associativity, as in left ( right Alt1 | Alt2 | Alt3) , then Alt1 is right associative with respect to itself and left associative with respect to all others in the group.
A finer point is that associativity has no effect on any other position than the left-most and right-most position (see also Priority). This is to guarantee that associativity does not introduce parse errors. The following tables explain when an assocativity declaration filters given two productions father and child that share an associativity group.
If left (Parent | Child) | Parent None: E = "[" E "]" | Parent Left-most: E = E "*" | Parent Right-most: E = "*" E | Parent Both: E = E "*" E |
---|
Child None: E = "{" E "}" | No filter | No filter | No filter | No filter | Child Left-most: E = E "+" | No filter | No filter | Filter under right | Filter under right | Child Right-most: E = "+" E | No filter | No filter | No filter | No filter | Child Both: E = E "+" E | No filter | No filter | Filter under right | Filter under right | If right (Parent | Child) | Parent None: E = "[" E "]" | Parent Left-most: E = E "*" | Parent Right-most: E = "*" E | Parent Both: E = E "*" E |
---|
Child None: E = "{" E "}" | No filter | No filter | No filter | No filter | Child Left-most: E = E "+" | No filter | No filter | No filter | No filter | Child Right-most: E = "+" E | No filter | Filter under left | No filter | Filter under left | Child Both: E = E "+" E | No filter | Filter under left | No filter | Filter under left |
Benefits - short notation for common constructs in programming languages
- removes ambiguity but can not introduce parse errors
- Allows the use of less non-terminals for the same expression grammar (typically only one), which makes parse trees simpler as well as the mapping to an abstract syntax tree more direct.
Pitfalls - Please do not assume that Rascal's associativity declarations have the same semantics as SDF's associativity declarations.
- Use of productions that are not both left and right recursive in an associativity group, although safe, is not very meaningful. We would advise to use the Priority relation such a case. For example:
Original associativity | Better written as priority |
---|
E = left ( "+" E | E "+" E ); | E = E "+" E > "+" E; | E = right ( "+" E | E "+" E ); | E = "+" E > E "+" E; | E = left ( E "+" | E "+" E); | E = E "+" > E "+" E; | E = right ( E "+" | E "+" E); | E = E "+" E > E "+" ; |
|