242 lines
7 KiB
Java
242 lines
7 KiB
Java
// 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); } }
|