Large example
Here's an interpreter for a language with boolean, integer and
string variables, plus various expressions and statements. It's realized
by a grammar file, a mapper file and various Java files.
Compile the interpreter and run a sample script as follows:
cd /foo/examples/interpreter
javac *.java
mork Mapper.map
java interpreter.Main tests/fak.script
script.grm
[PARSER]
Script ::= "script" Declarations Block;
Declarations ::= ( Variable ";" )* ;
Variable ::= Type Identifier ;
Type ::= "bool" | "int" ;
Type ::= "string" ;
Stmt ::= Block | Print | Input | If | While | Assign ;
Block ::= "{" Stmt* "}";
Print ::= "print" Expr ";";
Input ::= "input" Reference ";";
If ::= "if" "(" Expr ")" Block ("else" Block )? ;
While ::= "while" "(" Expr ")" Stmt ;
Assign ::= Reference "=" Expr ";";
Expr ::= Or | And ;
Or ::= Expr "||" And ;
And ::= And2 | Equation ;
And2 ::= And "&&" Equation ;
Equation ::= Equation2 | Relation ;
Equation2 ::= Equation ("==" | "!=") Relation ;
Relation ::= Relation2 | Sum ;
Relation2 ::= Relation ("<" | ">" | "<=" | ">=") Sum ;
Sum ::= Sum2 | Prod ;
Sum2 ::= Sum ("+"|"-") Prod ;
Prod ::= Prod2 | Unary ;
Prod2 ::= Prod ("*"|"/"|"%") Unary ;
Unary ::= Unary1 | Atom ;
Unary1 ::= ("+"|"-"|"!") Unary;
Atom ::= Const | Reference | "(" Expr ")";
Reference ::= Identifier ;
Const ::= String | Integer | Boolean ;
Boolean ::= "true" | "false" ;
[SCANNER]
white = Space, Comment;
String ::= '"' '"'!* '"' ;
Integer ::= Digit+ ;
Identifier ::= Letter (Letter | Digit | '_')* ;
Letter ::= 'A'..'Z' | 'a'..'z' ;
Digit ::= '0'..'9';
Space ::= (' ' | '\b' | '\t' | '\n' | '\f' | '\r' )+ ;
Comment ::= '#' ('\n'|'\r')!* ;
Mapper.map
mapper interpreter.Mapper;
grm = "script.grm";
import interpreter:
Assign, Binary, Block, Const, Declarations, Expression, If, Input,
Print, Reference, Script, Unary, Variable, While;
import de.mlhartme.mork.semantics:
BuiltIn;
import java.lang:
Boolean, Integer, String;
Script => Script;
Declarations => Declarations : >\\Script > \\Script//Reference;
Variable => Variable;
"int" => Expression.INT;
"bool" => Expression.BOOL;
"string" => Expression.STR;
# Statements
Block => Block;
While => While;
If => If;
Input => Input;
Print => Print;
Assign => Assign;
# Expressions
Const => Const;
Reference => Reference;
Or => Binary;
And2 => Binary;
Equation2 => Binary;
Relation2 => Binary;
Sum2 => Binary;
Prod2 => Binary;
Unary1 => Unary;
# Operators
"+" => Expression.ADD;
"-" => Expression.SUB;
"*" => Expression.MUL;
"/" => Expression.DIV;
"%" => Expression.REM;
"&&" => Expression.AND;
"||" => Expression.OR;
"!" => Expression.NOT;
"==" => Expression.EQ;
"!=" => Expression.NE;
"<" => Expression.LT;
">" => Expression.GT;
"<=" => Expression.LE;
">=" => Expression.GE;
# misc
Boolean => Boolean;
"true" => Boolean.TRUE;
"false" => Boolean.FALSE;
Identifier => String;
=> [text]: > Identifier;
String => BuiltIn.parseString;
=> [text]: > String;
Integer => Integer.parseInt;
=> [text]: > Integer;
Java files
The Java files are listed here.
The kick-off code is in Main.java, the other classes resemble a kind
of abstract syntax. Running the interpreter instantiates a script object
and invokes the run method on it.
Compare this example with interpreter examples based on JavaCC/jjTree
or SableCC: Mork maps a script file to user-defined classes instead of generated
classes. These classes implement the necessary computations by using the appropriate
OO features. There is no need for tree-walks, visitor classes or similar stuff that
is necessary to add computations to generated classes. The mapper file separates
syntax and computations (i.e. classes), making both of them reusable.
|