240 likes | 419 Vues
LANGUAGE TRANSLATORS: WEEK 21. LECTURE: Using JavaCup to create simple interpreters http://helios.hud.ac.uk/scomtlm/cis2380 Tutorial / Practical 1. Run JavaCup on Linux / Ouranos following the “self study” file in the website 2. Continue to do paper based exercises creating SR parsers.
E N D
LANGUAGE TRANSLATORS: WEEK 21 LECTURE: Using JavaCup to create simple interpreters http://helios.hud.ac.uk/scomtlm/cis2380 Tutorial / Practical 1. Run JavaCup on Linux / Ouranos following the “self study” file in the website 2. Continue to do paper based exercises creating SR parsers
JavaCup Grammar in “parser.cup” PLUS action code JavaCup Scanner in “scanner.java” Parsing Table in “parser.java” and “sym.java” PLUS action code Library Classes for Interpreting Parsing Table javac Interpreter
An Interpreter for an expression evaluator stm ::= ID:id ASSIGNS expr:e {: put(id,e.intValue()); :} SEMI | PRINT expr:e {: System.out.println("= " + e); :} SEMI ; expr ::= expr:e1 SQUARED {: RESULT = new Integer(e1.intValue() * e1.intValue());:} | ID:id {:RESULT = new Integer(get(id));:} | expr:e1 PLUS expr:e2 {: RESULT = new Integer(e1.intValue() + e2.intValue());:} The left hand side of each production is always implicitly labelled as RESULT.
Problem with evaluation during parsing: Imagine trying to implement Java’s “DO <stm> WHILE <expr>” “IF (<exp>) <stm>” using option 2 (evaluating code while parsing). These are examples of compound statements The problem is that repetition or branching cannot be easily dealt with when interpreting the program at the same time as parsing it. We can execute an assignment statement after we parse it … but not so easy with parts of compound statements.
A better option.. RECALL Option 3 from previous week: - Use JavaCup to build up a Parse Tree; Attach ACTION CODE to production rules to do this. - Once parsing has finished, pass this Parse Tree to the next part of the interpreter/ compiler/ translator eg to a Java interpreter or Compiler.
LPL (See code in Q3parser directory for definition). Using Javacup + interpreter code we can easily create an interpreter for code such as the following example x = 23; repeat x = x-1; y = x*x; print(x,y) until x; print(x)
Part of LPL’s parser.cup file stm::= ID:id ASSIGNS exp:e {: RESULT = new AssignStm(id,e); :} | REPEAT stm:s1 UNTIL exp:e {: RESULT = new RepeatStm(s1,e); :} | exp ::= INT:n {: RESULT = new NumExp(n.intValue()); :} | ID:id {:RESULT = new IdExp(id);:} | exp:e1 PLUS exp:e2 {: RESULT = new OpExp(e1,OpExp.Plus,e2); :}
LPL – build up of syntax tree Prog(interpStm(<SYNTAX TREE>, new NumExp(9 Table("",0,null)); REST OF TREE stm(new AssignStm(new IdExp(“x”), new NumExp(9))) PARSING expr(new IdExp(“x”)) expr(new NumExp(9)) ID,"x" ASSIGNS INT,9 SEMI SCANNING x = 9; TOKEN STREAM
Example from lecture 3... Eg <SYNTAX TREE> might be something like this … new CompoundStm( new AssignStm("b",new NumExp(5)), new CompoundStm( new AssignStm("a", new OpExp(new NumExp(5), OpExp.Times, new IdExp("b"))), new PrintStm(new ExpList(new OpExp(new IdExp("b"), OpExp.Times, new IdExp("a")))) ) );
LPL – how the interpreter works Once the input program is parsed successfully, the program’s parse tree is passed to the interpreter to be interpreted! The interpreter code is situated at the beginning of parser.cup and is copied to parser.java verbatim. Sequence: • When executed with an input program P, the Parser creates a Syntax (or ‘parse’) Tree representing P. • The Syntax Tree together with an empty Table is passed to the interpreter in the action code. • The entries in the Table correspond to variable’s current values as P is interpreted (the Table is called the ‘Store’) • Commands in P change the Store
LPL - the Store Table represents the STORE of the program class Table { String id; int value; Table tail; Table(String i, int v, Table t) {id =i; value=v; tail=t;} etc - it is a RECURSIVE type
LPL - INTERPRETER interpStm applies a statement s to a store t (t is represented as a Table) interpStm is called initially with an empty store … prog ::= stm: s {: RESULT = interpStm(s,new Table("",0,null)); :} ; interpStm’sinput is the parse tree in Java Constructor form. This could be processed in many ways e.g. a SYMBOL TABLE can be extracted from it and used to track variable DECLARATIONS.
interpStm - compound If s is a compound statement then we want to apply the first statement to the current store t getting store t’, then apply the second statement to t’. That is what this piece of code does: else if (s instanceof CompoundStm) return interpStm(s.get2(), interpStm(s.get1(),t)); Here t’ = interpStm(s.get1(),t)
interpStm - assignment ASSIGNMENT: else if (s instanceof AssignStm) return update(t,s.getLHS(),interpExp(s.getRHS(), t)); If s is an assignment then it has a LHS (the variable) and a RHS (the expression). update takes the current store t, the LHS, the VALUE of the RHS in the current store, and returns a NEW store. Table update(Table t,String str, int i) { return (new Table(str, i, t));}
interpStm - repeat Apply the inside of the loop to obtain a new store. If the repeat expression is 0 in this new store then end, otherwise recursively call InterpStm with the new store. else if (s instanceof RepeatStm) { Table table_temp = interpStm(s.getRepStm(),t); int int_temp = interpExp(s.getRepExp(),table_temp); if (int_temp == 0) return table_temp; else return interpStm(s, table_temp); }
InterExp InterExp is a recursive method for evaluating Expressions in the context of a certain store. InterpExp takes expression e and store t and returns a VALUE. Note a VALUE in LPL is always ‘int’ - we don’t allow booleans, strings etc
Conclusions... The definition of LPL in parser.cup contains the syntax specification and the Java Code implementing LPL’s interpreter
APPENDIX:Steps to Updating/improving the JavaCup-generated Interpreter Each time you make an addition to the language go through the following steps. • As an example, we will use the WHILE COMMAND
STEP 1 • Work out what concrete syntax you want for your new command or other extension. Write down its semantics. WHILE <exp> DO <stm> Evaluate <exp>. If it is zero skip and goto the next statement after WHILE in the program. Otherwise execute <stm>, then execute WHILE <exp> DO <stm> again.
STEP 2 • Put the new syntax in scanner.java Eg add the following to scanner.java: case ’W': advance(); advance(); advance(); advance(); advance(); return new Symbol(sym.WHILE); case ’D': advance(); advance(); return new Symbol(sym.DO);
Step 3: • Add abstract syntax to parser.cup - the non-terminals/terminals, the production rule, and the attached action code terminal REPEAT, UNTIL, SEMI, PLUS, WHILE, DO ... Stm ::= WHILE exp:e DO stm:s {: RESULT = new WhileStm(s,e); :}
Step 4: • Add the new Syntax Class for the While Statement to the action code class WhileStm extends Stm { Stm s; Exp exp; WhileStm(Stm a, Exp e) {s=a; exp=e;} public Stm getWhileStm() {return s;} public Exp getWhileExp() {return exp;} }
Step 5: • Add to the action code statements that interpret the WHILE Stm Table interpStm(Stm s, Table t) { ... else if (s instanceof WhileStm) int temp = interpExp(s.getWhileExp(),t); if (temp == 0) return t; else return interpStm(s, interpStm(s.getWhileStm(),t)) ...
Step 6: Follow the steps to create the interpreter in the READ - ME file as usual.