Skip to main content

With Layout

rascal-0.40.17

Synopsis

Defines a concrete syntax for Exp with layout.

Description

In Rascal, the major difference between lexical syntax and non-lexical syntax is that:

  • Strings that are parsed according to the lexical syntax do not contain additional layout characters such as spaces, new lines, and source code comments.
  • Strings that are parsed according to the normal (non-lexical) syntax can contain layout characters between each element.
  • Which 'layout' (whitespace and/or source code comments) will be accepted has to be defined explicitly by the grammar author.

The following example extends the grammar for Exp in No Layout with a layout definition:

layout Whitespace = [\t-\n\r\ ]*;        

lexical IntegerLiteral = [0-9]+;

start syntax Exp
= IntegerLiteral
| bracket "(" Exp ")"
> left Exp "*" Exp
> left Exp "+" Exp
;
  • ❶ Using the layout definition, we define that the Whitespace non-terminal is used in between every symbol of the syntax productions in the current module.

And now we can use spaces in our definition of the eval function as well:

import String;
import ParseTree;

int eval(str txt) = eval(parse(#start[Exp], txt).top);

int eval((Exp)`<IntegerLiteral l>`) = toInt("<l>");
int eval((Exp)`<Exp e1> * <Exp e2>`) = eval(e1) * eval(e2);
int eval((Exp)`<Exp e1> + <Exp e2>`) = eval(e1) + eval(e2);
int eval((Exp)`( <Exp e> )`) = eval(e);

value main() {
return eval(" 2+3");
}

test bool tstEval1() = eval(" 7") == 7;
test bool tstEval2() = eval("7 * 3") == 21;
test bool tstEval3() = eval("7 + 3") == 10;
test bool tstEval4() = eval(" 3 + 4*5 ") == 23;

Note that Pattern Matching will ignore all trees in layout positions, such that the parse tree of "1 + \n1" will match against <Exp e1> + <Exp e2>. The same goes for equality on parse trees.

For the above example Rascal will insert the Whitespace non-terminal between every element of the syntax rules for Exp. Moreover, for the start production (See No Layout) Whitespace will be added before and after the Exp.

Examples

The effect of the layout definition is that before parser generation the following grammar is derived for Exp:

syntax Exp 
= IntegerLiteral
| bracket "(" Whitespace Exp Whitespace ")"
> left Exp Whitespace "*" Whitespace Exp
> left Exp Whitespace "+" Whitespace Exp
;

syntax start[Exp] = Whitespace Exp top Whitespace;

To put this all to the test:

rascal>eval("2 +  3");
int: 5
rascal>eval("2 + 3*4");
int: 14
rascal>eval("( 2+3 )* 4");
int: 20

Pitfalls

  • If the grammar for Exp would contain an optional symbol, as in syntax Exp = Exp "+"? Exp, then it would be ambiguous. Does a space in "1 1", belong to the Whitespace before or after the missing +? To disambiguate the layout definition should be changed to layout Whitespace = [\ \t\n\r]* !>> [\ \t\n\r]. That will make sure the space goes with the first Whitespace, because even an empty Whitespace list must never be followed immediately by a space.