// Primitive runtime code generation of expressions. This is a jas // implementation of the example in Aho Sethi Ullman. // // You pass to it statements of the form // a = 1*2 + 3*5; // Only integer operations are allowed, and variables // are magically created by using them in the LHS of a statement. // // You can print out the value of expressions with the println keyword: // println(a + 10); // // It compiles this into a bytearray, and then loads it as // a new class. // // Unfortunately, this trick cannot be used in an applet --- security // restrictions prevent ClassLoaders from being placed on the // stack. So much for writing regexp compilers that can do codegen instead of // state tables. // // The grammar is simple, so code generation is part of the parsing // step. Operator precedence is directly handled by the grammar. // // Grammar + production rules: // // start -> list EOF // list -> id = expr ; { emit(istore, id.index); } list // | println expr ; { emit(<println sequence>); } list // | lambda // expr -> term moreterms // moreterms -> + term { emit(iadd) } moreterms // | - term { emit(isub) } moreterms // | lambda // term -> factor morefactors // morefactors -> * factor { emit(imul) } morefactors // | / factor { emit(idiv) } morefactors // | lambda // factor -> ( expr ) // | number { emit(iconst, number.value) } // | id { emit(iload, id.index); } import java.util.*; import java.io.*; import jas.*; import sun.tools.java.RuntimeConstants; public class exprcomp implements RuntimeConstants { StreamTokenizer inp; CodeAttr myCode; short cur_stack_height; short max_stack_height; short max_vars; Hashtable vars; public exprcomp(StreamTokenizer inp) throws jasError { inp.eolIsSignificant(false); this.inp = inp; myCode = new CodeAttr(); // Add initializations myCode.addInsn(new Insn(opc_aload_0)); myCode.addInsn(new Insn(opc_invokenonvirtual, new MethodCP("java/lang/Object", "<init>", "()V"))); cur_stack_height = max_stack_height = 1; vars = new Hashtable(); max_vars = 1; } public void write(DataOutputStream out) throws IOException, jasError { ClassEnv clazz = new ClassEnv(); myCode.setStackSize(max_stack_height); myCode.setVarSize(max_vars); // add initializer to class clazz.addMethod ((short) ACC_PUBLIC, "<init>", "()V", myCode, null); clazz.setClassAccess((short) ACC_PUBLIC); clazz.setClass(new ClassCP("results")); clazz.setSuperClass(new ClassCP("java/lang/Object")); clazz.write(out); } public void parse() throws IOException, parseError, jasError { inp.nextToken(); while(inp.ttype != inp.TT_EOF) { if (inp.ttype != inp.TT_WORD) { throw new parseError("Expected an id at line " + inp.lineno()); } if (inp.sval.equals("println")) { match(inp.TT_WORD); expr(); match(';'); emit(new Insn(opc_getstatic, new FieldCP("java/lang/System", "out", "Ljava/io/PrintStream;"))); emit(new Insn(opc_swap)); emit(new Insn(opc_invokevirtual, new MethodCP("java/io/PrintStream", "println", "(I)V"))); } else { // search, maybe add into var list Integer idx; if ((idx = (Integer) vars.get(inp.sval)) == null) { idx = new Integer(max_vars++); vars.put(inp.sval.intern(), idx); } match(inp.TT_WORD); match('='); expr(); match(';'); emit(new Insn(opc_istore, idx.intValue())); } } emit(new Insn(opc_return)); } void expr() throws IOException, parseError, jasError { term(); while (true) { switch(inp.ttype) { case '+': match('+'); term(); emit(new Insn(opc_iadd)); break; case '-': match('-'); term(); emit(new Insn(opc_isub)); break; default: return; } cur_stack_height--; } } void term() throws IOException, parseError, jasError { factor(); while (true) { switch(inp.ttype) { case '*': match('*'); factor(); emit(new Insn(opc_imul)); break; case '/': match('/'); factor(); emit(new Insn(opc_idiv)); break; default: return; } cur_stack_height --; } } void factor() throws IOException, parseError, jasError { switch(inp.ttype) { case '(': match('('); expr(); match(')'); break; case inp.TT_NUMBER: int val = (int)(inp.nval); emit(new Insn(opc_bipush, (short)val)); match(inp.TT_NUMBER); break; case inp.TT_WORD: Integer idx; if ((idx = (Integer) vars.get(inp.sval)) == null) { throw new parseError ("Unknown variable " + inp.sval + " at line " + inp.lineno()); } emit(new Insn(opc_iload, idx.intValue())); match(inp.TT_WORD); break; default: throw new parseError("Syntax error at line " + inp.lineno()); } cur_stack_height++; if (max_stack_height < cur_stack_height) max_stack_height = cur_stack_height; } void match(int val) throws IOException, parseError { if (inp.ttype != val) { throw new parseError ("line " + inp.lineno() + ": expected " + val + " but got " + inp); } inp.nextToken(); } void emit(Insn insn) { myCode.addInsn(insn); } public static void main(String argv[]) throws Exception { exprcomp compiler = new exprcomp(new StreamTokenizer(new FileInputStream(argv[0]))); compiler.parse(); ByteArrayOutputStream data = new ByteArrayOutputStream(); compiler.write(new DataOutputStream(data)); dynaloader dl = new dynaloader(data.toByteArray()); dl.exec(); } } class dynaloader extends ClassLoader { Hashtable cache; Class ex; dynaloader(byte[] data) throws ClassFormatError { cache = new Hashtable(); ex = defineClass(data, 0, data.length); cache.put("results", ex); resolveClass(ex); } void exec() throws Exception { ex.newInstance(); } public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { Class c = (Class) cache.get(name); if (c == null) { c = findSystemClass(name); cache.put(name, c); } if (resolve) resolveClass(c); return c; } } class parseError extends Exception { parseError(String s) { super(s); } }