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 
Parsemodule 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