Skip to main content

Manual

rascal-0.40.17

Synopsis

An Exp evaluator that uses a manually written conversion from parse tree to abstract syntax tree.

Examples

Starting from the grammar for Exp:

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

lexical IntegerLiteral = [0-9]+;

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

First we define a parse function for Exp:

import ParseTree;

Exp parseExp(str txt) = parse(#Exp, txt);

and test it:

rascal>example = parseExp("2+3");
Exp: (Exp) `2+3`

Now we need an abstract definition of the Exp language, where the non-terminals of the grammar lign up with the Algebraic Data Type names:

data Exp 
= con(int n)
| mul(Exp e1, Exp e2)
| add(Exp e1, Exp e2)
;

NB: it is important to separate the concrete syntax definition for Exp from the abstract data definition in separate modules to avoid name clashes.

Next, we define a load function:

import String;

Exp load((Exp)`<IntegerLiteral l>`) = con(toInt("<l>"));
Exp load((Exp)`<Exp e1> * <Exp e2>`) = mul(load(e1), load(e2));
Exp load((Exp)`<Exp e1> + <Exp e2>`) = add(load(e1), load(e2));
Exp load((Exp)`( <Exp e> )`) = load(e);

Some comments:

  • ❶ We reuse the previously defined concrete syntax with layout.
  • ❷ We also reuse the previously defined abstract syntax.
  • ❸ Import the Parse module defined above.
  • ❹ The conversion from parse tree to abstract syntax tree starts here.

Let's try it:

rascal>load(example);
Exp: add(
con(2),
con(3))

What remains is to write the interpreter using the above components:

int eval(con(int n)) = n;                            
int eval(mul(Exp e1, Exp e2)) = eval(e1) * eval(e2);
int eval(add(Exp e1, Exp e2)) = eval(e1) + eval(e2);

Here is how it works:

rascal>eval(load(example));
int: 5