package test.antlr.pie; /*** * Excerpted from "Language Implementation Patterns", * published by The Pragmatic Bookshelf. * Copyrights apply to this code. It may not be used to create training material, * courses, books, articles, and the like. Contact us if you are in doubt. * We make no guarantees that this code is fit for any purpose. * Visit http://www.pragmaticprogrammer.com/titles/tpdsl for more book information. ***/ import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Stack; import org.antlr.runtime.ANTLRInputStream; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.Token; import org.antlr.runtime.TokenRewriteStream; public class Interpreter { public InterpreterListener listener = // default response to messages new InterpreterListener() { public void info(String msg) { System.out.println(msg); } public void error(String msg) { System.err.println(msg); } public void error(String msg, Exception e) { error(msg); e.printStackTrace(System.err); } public void error(String msg, Token t) { error("line "+t.getLine()+": "+msg); } }; public static final ReturnValue sharedReturnValue = new ReturnValue(); GlobalScope globalScope; // global scope is filled by the parser MemorySpace globals = new MemorySpace("globals"); // global memory MemorySpace currentSpace = globals; Stack<FunctionSpace> stack = new Stack<FunctionSpace>();// call stack PieAST root; // the AST represents our code memory TokenRewriteStream tokens; PieLexer lex; // lexer/parser are part of the processor PieParser parser; public void interp(InputStream input) throws RecognitionException, IOException { globalScope = new GlobalScope(); lex = new PieLexer(new ANTLRInputStream(input)); tokens = new TokenRewriteStream(lex); parser = new PieParser(tokens, this); parser.setTreeAdaptor(InterPie.pieAdaptor); PieParser.program_return r = parser.program(); if ( parser.getNumberOfSyntaxErrors()==0 ) { root = (PieAST)r.getTree(); //System.out.println("tree: "+root.toStringTree()); block(root); } } /** visitor dispatch according to node token type */ public Object exec(PieAST t) { try { switch ( t.getType() ) { case PieParser.BLOCK : block(t); break; case PieParser.ASSIGN : assign(t); break; case PieParser.RETURN : ret(t); break; case PieParser.PRINT : print(t); break; case PieParser.IF : ifstat(t); break; case PieParser.WHILE : whileloop(t); break; case PieParser.CALL : return call(t); case PieParser.NEW : return instance(t); case PieParser.ADD : return add(t); case PieParser.SUB : return op(t); case PieParser.MUL : return op(t); case PieParser.EQ : return eq(t); case PieParser.LT : return lt(t); case PieParser.INT : return Integer.parseInt(t.getText()); case PieParser.CHAR : return new Character(t.getText().charAt(1)); case PieParser.FLOAT : return Float.parseFloat(t.getText()); case PieParser.STRING : String s = t.getText(); return s.substring(1,s.length()-1); case PieParser.DOT : case PieParser.ID : return load(t); default : // catch unhandled node types throw new UnsupportedOperationException("Node "+ t.getText()+"<"+t.getType()+"> not handled"); } } catch (Exception e) { listener.error("problem executing "+t.toStringTree(), e); } return null; } @SuppressWarnings("unchecked") public void block(PieAST t) { if ( t.getType()!=PieParser.BLOCK ) { listener.error("not a block: "+t.toStringTree()); } List<PieAST> stats = t.getChildren(); for (PieAST x : stats) exec(x); } public Object call(PieAST t) { // Resolve function's name String fname = t.getChild(0).getText(); FunctionSymbol fs = (FunctionSymbol)t.scope.resolve(fname); if ( fs==null ) { listener.error("no such function "+fname, t.token); return null; } FunctionSpace fspace = new FunctionSpace(fs); MemorySpace saveSpace = currentSpace; currentSpace = fspace; int argCount = t.getChildCount()-1; // check for argument compatibility if ( fs.formalArgs==null && argCount>0 || // args compatible? fs.formalArgs!=null && fs.formalArgs.size()!=argCount ) { listener.error("function "+fs.name+" argument list mismatch"); return null; } int i = 0; // define args according to order in formalArgs for (Symbol argS : fs.formalArgs.values()) { VariableSymbol arg = (VariableSymbol)argS; PieAST ithArg = (PieAST)t.getChild(i+1); Object argValue = exec(ithArg); fspace.put(arg.name, argValue); i++; } Object result = null; stack.push(fspace); // PUSH new arg, local scope try { exec(fs.blockAST); } // do the call catch (ReturnValue rv) { result = rv.value; } // trap return value stack.pop(); // POP arg, locals currentSpace = saveSpace; return result; } public void ret(PieAST t) { sharedReturnValue.value = exec((PieAST)t.getChild(0)); throw sharedReturnValue; } public void print(PieAST t) { PieAST expr = (PieAST)t.getChild(0); System.out.println( exec(expr) ); } public void assign(PieAST t) { PieAST lhs = (PieAST)t.getChild(0); // get operands PieAST expr = (PieAST)t.getChild(1); Object value = exec(expr); // walk/evaluate expr if ( lhs.getType()==PieParser.DOT ) { fieldassign(lhs, value); // field ^('=' ^('.' a x) expr) return; } // var assign ^('=' a expr) MemorySpace space = getSpaceWithSymbol(lhs.getText()); if ( space==null ) space = currentSpace; // create in current space space.put(lhs.getText(), value); // store } public void fieldassign(PieAST lhs, Object value) { PieAST o = (PieAST) lhs.getChild(0); PieAST f = (PieAST) lhs.getChild(1); String fieldname = f.getText(); Object a = load(o); if ( !(a instanceof StructInstance) ) { // make a good error message: String leftpart = parser.input.toString(lhs.getTokenStartIndex(), lhs.getTokenStopIndex()-2); String all = parser.input.toString(lhs.getTokenStartIndex(), lhs.getTokenStopIndex()); listener.error(leftpart+" is not a struct in "+all, o.token); return; } StructInstance struct = (StructInstance)a; if ( struct.def.resolveMember(fieldname) == null ) { listener.error("can't assign; "+struct.name+" has no "+fieldname+ " field", f.token); return; } struct.put(fieldname, value); } public void whileloop(PieAST t) { PieAST condStart = (PieAST)t.getChild(0); PieAST codeStart = (PieAST)t.getChild(1); Boolean c = (Boolean)exec(condStart); while ( c ) { exec(codeStart); c = (Boolean)exec(condStart); } } public void ifstat(PieAST t) { PieAST condStart = (PieAST)t.getChild(0); PieAST codeStart = (PieAST)t.getChild(1); PieAST elseCodeStart = null; if ( t.getChildCount()==3 ) elseCodeStart = (PieAST)t.getChild(2); Boolean c = (Boolean)exec(condStart); if ( ((Boolean)c).booleanValue() ) exec(codeStart); else if ( elseCodeStart!=null ) exec(elseCodeStart); } public boolean eq(PieAST t) { Object a = exec( (PieAST)t.getChild(0) ); Object b = exec( (PieAST)t.getChild(1) ); return a.equals(b); } public boolean lt(PieAST t) { Object a = exec( (PieAST)t.getChild(0) ); Object b = exec( (PieAST)t.getChild(1) ); if ( a instanceof Number && b instanceof Number ) { Number x = (Number)a; Number y = (Number)b; return x.floatValue() < y.floatValue(); } return false; } public Object op(PieAST t) { Object a = exec( (PieAST)t.getChild(0) ); Object b = exec( (PieAST)t.getChild(1) ); if ( a instanceof Float || b instanceof Float ) { float x = ((Number)a).floatValue(); float y = ((Number)b).floatValue(); switch (t.getType()) { case PieParser.ADD : return x + y; case PieParser.SUB : return x - y; case PieParser.MUL : return x * y; } } if ( a instanceof Integer || b instanceof Integer ) { int x = ((Number)a).intValue(); int y = ((Number)b).intValue(); switch (t.getType()) { case PieParser.ADD : return x + y; case PieParser.SUB : return x - y; case PieParser.MUL : return x * y; } } return 0; } public Object add(PieAST t) { Object a = exec( (PieAST)t.getChild(0) ); Object b = exec( (PieAST)t.getChild(1) ); if ( a instanceof String || b instanceof String ) { return a.toString() + b.toString(); } return op(t); } public Object load(PieAST t) { if ( t.getType()==PieParser.DOT ) return fieldload(t); MemorySpace s = getSpaceWithSymbol(t.getText()); // just a not a.b if ( s!=null ) return s.get(t.getText()); listener.error("no such variable "+t.getText(), t.token); return null; } public Object fieldload(PieAST t) { // E.g., a.b in tree ^('.' a b) PieAST expr = (PieAST)t.getChild(0); // get left node or subtree PieAST b = (PieAST)t.getChild(1); // must be an ID node String id = b.getText(); StructInstance struct = (StructInstance)load(expr); // find expr if ( struct.def.resolveMember(id)==null ) { // is it a struct? listener.error(struct.name+" has no "+id+" field", b.token); return null; } return struct.get(id); } /** Return scope holding id's value; current func space or global. */ public MemorySpace getSpaceWithSymbol(String id) { if (stack.size()>0 && stack.peek().get(id)!=null) { // in top stack? return stack.peek(); } if ( globals.get(id)!=null ) return globals; // in globals? return null; // nowhere } public StructInstance instance(PieAST t) { PieAST structNameNode = (PieAST)t.getChild(0); StructSymbol s = (StructSymbol) structNameNode.scope.resolve(structNameNode.getText()); return new StructInstance(s); } }