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