c_objects := $(c_sources:src/%.c=bin-$(platform)/%.c.o)
# tools
-gcc_path := $(shell pwd)/gcc
+gcc_path := $(shell pwd)/gcc/install
#gcc_optimizations := -O9 -ffast-math -fomit-frame-pointer -foptimize-sibling-calls -finline-functions
#gcc_optimzations += -funroll-loops -ffunction-sections -fdata-sections
gcc_optimizations := -O0
## GCJ-Derived Platforms #######################################################################
# if the user doesn't change gcc_path, and it's not built, then we'll try to build it
-$(shell pwd)/gcc/bin/$(target)-gcj:
+$(shell pwd)/gcc/install/bin/$(target)-gcj:
make -C gcc
# java_classes is here to force compilation of the .class files (they get used via -Ibin/) without
public Object _call(JS.Array args) { return _call(args, JS.getCurrentFunction()); }
public Object _call(JS.Array args, Function currentFunction) {
Trap currentTrap = TrapContext.get().currentTrap;
- /*
- if (currentTrap == null || (currentFunction != currentTrap.f)) {
- if (Log.on) Log.log(this, "attempt to cascade() by a function that was not invoked as a trap at " +
- currentFunction.getSourceName());
- if (Log.on) Log.log(this, "currentfunction == " + currentFunction);
- if (Log.on) Log.log(this, "currentTrap.f == " + currentTrap.f);
- return null;
- }
- */
if (args.length() != 0) TrapContext.get().putCascadeHappened = true;
Trap t = currentTrap.next;
// if we've hit the end of the trap stack, just do a put(,,,true)
-// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
package org.xwt.js;
import org.xwt.util.*;
import java.io.*;
-/** a block of Forth bytecode */
-class ForthBlock implements OpCodes, Tokens {
+/**
+ * A block of JavaScript bytecode.
+ *
+ * The JavaScript ByteCode runs on a very simple stack machine.
+ */
+class ByteCodeBlock implements ByteCodes, Tokens {
int line;
String sourceName;
Object[] arg = new Object[10];
int size = 0;
- public ForthBlock(int line, String sourceName) { this.line = line; this.sourceName = sourceName; }
- public ForthBlock(int line, String sourceName, int op_, Object arg_) { this(line, sourceName); add(op_, arg_); }
+ public ByteCodeBlock(int line, String sourceName) { this.line = line; this.sourceName = sourceName; }
+ public ByteCodeBlock(int line, String sourceName, int op_, Object arg_) { this(line, sourceName); add(op_, arg_); }
public int size() { return size; }
public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; }
- public void paste(ForthBlock other) { for(int i=0; i<other.size; i++) add(other.op[i], other.arg[i]); }
- public ForthBlock add(int op_) { return add(op_, null); }
- public ForthBlock add(int op_, Object arg_) {
+ public void paste(ByteCodeBlock other) { for(int i=0; i<other.size; i++) add(other.op[i], other.arg[i]); }
+ public ByteCodeBlock add(int op_) { return add(op_, null); }
+ public ByteCodeBlock add(int op_, Object arg_) {
if (size == op.length - 1) {
int[] op2 = new int[op.length * 2]; System.arraycopy(op, 0, op2, 0, op.length); op = op2;
Object[] arg2 = new Object[op.length * 2]; System.arraycopy(arg, 0, arg2, 0, arg.length); arg = arg2;
return this;
}
- public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn { return eval(s, new Stack()); }
- public Object eval(final JS.Scope s, Stack t) throws ControlTransferException {
- for(int i=0; i<size; i++)
- switch(op[i]) {
- case LABEL: break; // FIXME
+ public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn { return eval(s, new Vec()); }
+ public Object eval(final JS.Scope s, Vec t) throws ControlTransferException {
+ for(int i=0; i<size; i++) switch(op[i]) {
+ case LABEL: break;
case LITERAL: t.push(arg[i]); break;
case OBJECT: t.push(new JS.Obj()); break;
case ARRAY: t.push(new JS.Array(JS.toNumber(arg[i]).intValue())); break;
case DECLARE: s.declare((String)t.pop()); break;
- case THIS: t.push(s); break; // FIXME: transparents
+ case TOPSCOPE: t.push(s); break;
case JT: if (JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
case JF: if (!JS.toBoolean(t.pop())) i += JS.toNumber(arg[i]).intValue() - 1; break;
case JMP: i += JS.toNumber(arg[i]).intValue() - 1; break;
case POP: t.pop(); break;
- case SWAP: t.swap(); break;
+ case SWAP: { Object o1 = t.pop(); Object o2 = t.pop(); t.push(o1); t.push(o2); break; }
case DUP: t.push(t.peek()); break;
- case NOP: break;
- case EXPR: t.push(((ForthBlock)arg[i]).eval(s)); break;
- case SCOPE: t.push(((ForthBlock)arg[i]).eval(new JS.Scope(s))); break;
-
+ case SCOPE: t.push(((ByteCodeBlock)arg[i]).eval(new JS.Scope(s))); break;
case ASSERT: if (!JS.toBoolean(t.pop())) throw new EvaluatorException(line, sourceName, "assertion failed"); break;
case RETURN: throw new ReturnException(t.pop());
case THROW: throw new JS.Exn(t.pop());
-
case TRY: break;
case INSTANCEOF: break;
case TYPEOF: break;
+ case BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
+ case BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
+ case BREAK: return Boolean.FALSE;
+ case CONTINUE: return Boolean.TRUE;
+
case PUSHKEYS: {
Object o = t.peek();
Object[] keys = ((JS)o).keys();
break;
}
- case Lexer.BITNOT: t.push(new Long(~JS.toLong(t.pop()))); break;
- case Lexer.BANG: t.push(new Boolean(!JS.toBoolean(t.pop()))); break;
-
- case Lexer.BREAK: {
- // FIXME: make sure this can only appear in proper places
- return Boolean.FALSE;
- }
- case Lexer.CONTINUE: {
- // FIXME: make sure this can only appear in proper places
- return Boolean.TRUE;
- }
case LOOP: {
- ForthBlock loop = (ForthBlock)arg[i];
- Stack t2 = new Stack();
- t2.push(Boolean.TRUE);
+ ByteCodeBlock loop = (ByteCodeBlock)arg[i];
+ Vec stack2 = new Vec();
+ stack2.push(Boolean.TRUE);
while (true) {
Boolean result;
- try {
- result = (Boolean)loop.eval(new JS.Scope(s), t2);
- } catch (ContinueException c) {
- result = Boolean.TRUE;
- } catch (BreakException b) {
- result = Boolean.FALSE;
- }
+ try { result = (Boolean)loop.eval(new JS.Scope(s), stack2);
+ } catch (ContinueException c) { result = Boolean.TRUE;
+ } catch (BreakException b) { result = Boolean.FALSE; }
if (result == Boolean.FALSE) break;
- t2 = new Stack();
- t2.push(Boolean.FALSE);
+ stack2 = new Vec();
+ stack2.push(Boolean.FALSE);
}
break;
}
break;
}
- case OpCodes.FUNCTION: {
- final ForthBlock myBytes = (ForthBlock)arg[i];
+ case NEWFUNCTION: {
+ final ByteCodeBlock myBytes = (ByteCodeBlock)arg[i];
t.push(new JS.Function() {
- public String toString() { return sourceName + ":" + line; }
public String getSourceName() throws JS.Exn { return sourceName; }
public int getLine() throws JS.Exn { return line; }
- public Object _call(final JS.Array args) throws JS.Exn {
- Function save = JS.getCurrentFunction();
- JS.currentFunction.put(java.lang.Thread.currentThread(), this);
+ public Object _call(final JS.Array args) throws JS.Exn, ControlTransferException {
JS.Scope scope = new JS.Scope(s) {
- // FIXME
public String getSourceName() { return sourceName; }
public Object get(Object key) throws JS.Exn {
if (key.equals("trapee")) return org.xwt.Trap.currentTrapee();
return super.get(key);
}
};
- Stack t0 = new Stack();
- t0.push(args);
- try {
- return myBytes.eval(scope, t0);
- } catch (ReturnException r) {
- return r.retval;
- } catch (ControlTransferException c) {
- throw new EvaluatorException(line, sourceName, "error, ControlTransferException tried to leave a function: " + c);
- } finally {
- if (save == null) JS.currentFunction.remove(java.lang.Thread.currentThread());
- else JS.currentFunction.put(java.lang.Thread.currentThread(), save);
- }
+ Vec stack = new Vec();
+ stack.push(args);
+ return myBytes.eval(scope, stack);
}
});
break;
}
- case Lexer.INC: case Lexer.DEC: {
+ case INC: case DEC: {
boolean isPrefix = JS.toBoolean(arg[i]);
Object key = t.pop();
JS obj = (JS)t.pop();
Number num = JS.toNumber(obj.get(key));
- Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
+ Number val = new Double(op[i] == INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0);
obj.put(key, val);
t.push(isPrefix ? val : num);
break;
Object left = t.pop();
switch(op[i]) {
- case Lexer.BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
- case Lexer.BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
- case Lexer.BITAND: t.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
+ case BITOR: t.push(new Long(JS.toLong(left) | JS.toLong(right))); break;
+ case BITXOR: t.push(new Long(JS.toLong(left) ^ JS.toLong(right))); break;
+ case BITAND: t.push(new Long(JS.toLong(left) & JS.toLong(right))); break;
- case Lexer.ADD: {
+ case ADD: {
Object l = left;
Object r = right;
if (l instanceof String || r instanceof String) {
t.push(new Double(JS.toDouble(l) + JS.toDouble(r))); break;
}
- case Lexer.SUB: t.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
- case Lexer.MUL: t.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
- case Lexer.DIV: t.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
- case Lexer.MOD: t.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
+ case SUB: t.push(new Double(JS.toDouble(left) - JS.toDouble(right))); break;
+ case MUL: t.push(new Double(JS.toDouble(left) * JS.toDouble(right))); break;
+ case DIV: t.push(new Double(JS.toDouble(left) / JS.toDouble(right))); break;
+ case MOD: t.push(new Double(JS.toDouble(left) % JS.toDouble(right))); break;
- case Lexer.LSH: t.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
- case Lexer.RSH: t.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
- case Lexer.URSH: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
+ case LSH: t.push(new Long(JS.toLong(left) << JS.toLong(right))); break;
+ case RSH: t.push(new Long(JS.toLong(left) >> JS.toLong(right))); break;
+ case URSH: t.push(new Long(JS.toLong(left) >>> JS.toLong(right))); break;
- // FIXME: these need to work on strings
- case Lexer.LT: t.push(JS.toDouble(left) < JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.LE: t.push(JS.toDouble(left) <= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.GT: t.push(JS.toDouble(left) > JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.GE: t.push(JS.toDouble(left) >= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case LT: t.push(JS.toDouble(left) < JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case LE: t.push(JS.toDouble(left) <= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case GT: t.push(JS.toDouble(left) > JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
+ case GE: t.push(JS.toDouble(left) >= JS.toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break;
- case Lexer.EQ:
- case Lexer.NE: {
- // FIXME: should use Javascript coercion-equality rules
+ case EQ:
+ case NE: {
Object l = left;
Object r = right;
boolean ret;
else if (l instanceof Number) ret = JS.toNumber(r).doubleValue() == JS.toNumber(l).doubleValue();
else if (l instanceof String) ret = r != null && l.equals(r.toString());
else ret = l.equals(r);
- t.push(new Boolean(op[i] == Lexer.EQ ? ret : !ret)); break;
+ t.push(new Boolean(op[i] == EQ ? ret : !ret)); break;
}
default: throw new Error("unknown opcode " + op[i]);
} }
}
- if (t.size() != 1) {
- for(int i=0; i<size; i++) {
- System.out.println((op[i] >= 0 ? codeToString[op[i]] : "" + op[i]) + " [" + arg[i] + "]");
- }
- throw new EvaluatorException(line, sourceName, "eval() terminated with " + t.size() + " elements on the stack; one expected");
- }
+ if (t.size() != 1)
+ throw new EvaluatorException(line, sourceName, "eval() terminated with " + t.size() +
+ " elements on the stack; one expected");
return t.pop();
}
public Object doGet(final Object o, final Object v) {
- if (o == null) {
+ if (o == null)
throw new EvaluatorException(line, sourceName, "tried to get property \"" + v + "\" from the null value");
- }
if (o instanceof String) {
if (v.equals("length")) return new Integer(((String)o).length());
else if (v.equals("substring")) return new JS.Function() {
return null;
}
- static class Stack {
- public Object[] os = new Object[256];
- private int size = 0;
- public void push(Object o) { os[size++] = o; }
- public Object pop() { return os[--size]; }
- public Object peek() { return os[size - 1]; }
- public void swap() { Object temp = os[size - 1]; os[size - 1] = os[size - 2]; os[size - 2] = temp; }
- public int size() { return size; }
- }
-
static class EvaluatorException extends RuntimeException {
- public EvaluatorException(int line, String sourceName, String s) { super(sourceName + ":" + line + " " + s); }
+ public EvaluatorException(int line, String sourceName, String s) {
+ super(sourceName + ":" + line + " " + s);
+ }
}
- static abstract class ControlTransferException extends Exception { }
+ static abstract class ControlTransferException extends RuntimeException { }
static class BreakException extends ControlTransferException {
public String label;
BreakException(String label) { this.label = label; }
-// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
+// Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
package org.xwt.js;
-/** each instruction is an opcode and an optional literal literal. */
-public interface OpCodes {
+/**
+ * Constants for the various JavaScript ByteCode operations.
+ *
+ * Each instruction is an opcode and an optional literal literal; the
+ * arithmetic Tokens are also valid. They are: BITOR, BITXOR, BITAND,
+ * ADD, INC, DEC, SUB, MUL, DIV, MOD, LSH, RSH, URSH, LT, LE, GT, GE,
+ * EQ, NE
+ */
+interface ByteCodes {
/** push the literal onto the stack */
public static final byte LITERAL = -2;
public static final byte OBJECT = -4;
/** create a new instance; literal is a reference to the corresponding ForthBlock */
- public static final byte FUNCTION = -5;
+ public static final byte NEWFUNCTION = -5;
/** pop a string off the stack and declare it in the current scope */
public static final byte DECLARE = -6;
/** push a reference to the current scope onto the stack */
- /*
- public static final byte THIS = -7;
- */
+ public static final byte TOPSCOPE = -7;
/** pop two elements off the stack; push stack[-1].get(stack[top]) */
public static final byte GET = -8;
/** pop an element; push a JS.Array containing the keys of the popped element */
public static final byte PUSHKEYS = -19;
- /** FIXME: execute the ForthBlock pointed to by the literal */
- public static final byte EXPR = -20;
-
/** swap the top two elements on the stack */
public static final byte SWAP = -23;
import java.io.*;
import java.util.*;
-/** The public API for the JS engine */
-// FEATURE: try using mutable, recycled 'Num' objects
+/**
+ * The public API for the JS engine; JS itself is actually a class
+ * implementing the minimal amount of functionality for an Object
+ * which can be manipulated by JavaScript code.
+ */
public abstract class JS {
+
// Static Methods //////////////////////////////////////////////////////////////////////
- public static Function getCurrentFunction() {
- return (Function)currentFunction.get(Thread.currentThread());
- }
- public static String getCurrentFunctionSourceName() {
- return getCurrentFunctionSourceName(Thread.currentThread());
- }
+ private static Hashtable currentFunction = new Hashtable();
+ public static Function getCurrentFunction() { return (Function)currentFunction.get(Thread.currentThread()); }
+ public static String getCurrentFunctionSourceName() { return getCurrentFunctionSourceName(Thread.currentThread()); }
+ public static String getFileAndLine() { return getCurrentFunctionSourceName() + ":" + getCurrentFunction().getLine(); }
public static String getCurrentFunctionSourceName(Thread t) {
Function f = (Function)currentFunction.get(t);
if (f == null) return "null";
return f.getSourceName();
}
- public static String getFileAndLine() {
- return "unknown:??";
- }
public static boolean toBoolean(Object o) {
if (o == null) return false;
if (o instanceof Number) return o.equals(new Integer(0));
return true;
}
+
public static long toLong(Object o) { return toNumber(o).longValue(); }
public static double toDouble(Object o) { return toNumber(o).doubleValue(); }
public static Number toNumber(Object o) {
if (o instanceof String) try { return new Double((String)o); } catch (NumberFormatException e) { return new Double(0); }
if (o instanceof Boolean) return ((Boolean)o).booleanValue() ? new Long(1) : new Long(0);
if (o instanceof JS) return ((JS)o).coerceToNumber();
- // FIXME
- throw new Error("toNumber() got object of type " + o.getClass().getName());
+ throw new Error("toNumber() got object of type " + o.getClass().getName() + " which we don't know how to handle");
}
+
// Instance Methods ////////////////////////////////////////////////////////////////////
public abstract Object get(Object key) throws JS.Exn;
// Subclasses /////////////////////////////////////////////////////////////////////////
+ /** A slightly more featureful version of JS */
public static class Obj extends JS {
private Hash entries = new Hash();
private boolean sealed = false;
public Object[] keys() { return(entries.keys()); }
}
+ /** An exception which can be thrown and caught by JavaScripts */
public static class Exn extends RuntimeException {
private Object js = null;
public Exn(Object js) { this.js = js; }
public Object getObject() { return js; }
}
+ /** A JavaScript Array */
public static class Array extends Obj {
private Vec vec = new Vec();
public Array() { }
public void setElementAt(Object o, int i) { vec.setElementAt(o, i); }
}
- public static Hashtable currentFunction = new Hashtable();
+ /** Anything that is callable */
public static abstract class Function extends Obj {
- public abstract Object _call(JS.Array args) throws JS.Exn;
public String getSourceName() throws JS.Exn { return "unknown"; }
public int getLine() throws JS.Exn { return -1; }
- public final Object call(JS.Array args) throws JS.Exn { return _call(args); }
- }
-
- public static class Script extends Function {
- Vector e = null;
- private Script(Vector e) { this.e = e; }
- public String getSourceName() throws JS.Exn { return ((ForthBlock)e.elementAt(0)).sourceName; }
- public Object _call(JS.Array args) throws JS.Exn {
- Scope rootScope = (Scope)args.elementAt(0);
+ public abstract Object _call(JS.Array args) throws JS.Exn, ByteCodeBlock.ControlTransferException;
+ public final Object call(JS.Array args) throws JS.Exn {
Function saved = (Function)currentFunction.get(Thread.currentThread());
currentFunction.put(Thread.currentThread(), this);
try {
- for(int i=0; i<e.size(); i++)
- ((ForthBlock)e.elementAt(i)).eval(rootScope);
- } catch (ForthBlock.ReturnException e) {
- // ignore
- } catch (ForthBlock.ControlTransferException e) {
- throw new JS.Exn("block threw a ControlTransferException: " + e);
+ return _call(args);
+ } catch (ByteCodeBlock.ReturnException e) { // ignore
+ return e.retval;
+ } catch (ByteCodeBlock.ControlTransferException e) {
+ throw new RuntimeException(getSourceName() + ":" + getLine() +
+ " error, ControlTransferException tried to leave a function");
} finally {
if (saved == null) currentFunction.remove(Thread.currentThread());
else currentFunction.put(Thread.currentThread(), saved);
}
+ }
+ }
+
+ public static class Script extends Function {
+ Vector e = null;
+ private Script(Vector e) { this.e = e; }
+ public String getSourceName() throws JS.Exn { return ((ByteCodeBlock)e.elementAt(0)).sourceName; }
+ public Object _call(JS.Array args) throws JS.Exn, ByteCodeBlock.ControlTransferException {
+ Scope rootScope = (Scope)args.elementAt(0);
+ for(int i=0; i<e.size(); i++) ((ByteCodeBlock)e.elementAt(i)).eval(rootScope);
return null;
}
public static Script parse(Reader r, String sourceName, int line) throws IOException {
try {
Vector exprs = new Vector();
while(true) {
- ForthBlock ret = p.parseStatement();
+ ByteCodeBlock ret = p.parseStatement();
if (ret == null) break;
exprs.addElement(ret);
}
case 3: switch (s.charAt(0)) {
case 'a': if (s.charAt(2)=='d' && s.charAt(1)=='n') return AND; break;
case 'f': if (s.charAt(2)=='r' && s.charAt(1)=='o') return FOR; break;
- case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') return RESERVED; break;
- case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') throw new IOException("the new keyword is not permitted in XWT scripts");
+ case 'i': if (s.charAt(2)=='t' && s.charAt(1)=='n') return RESERVED;
+ case 'n': if (s.charAt(2)=='w' && s.charAt(1)=='e') return RESERVED;
case 't': if (s.charAt(2)=='y' && s.charAt(1)=='r') return TRY; break;
case 'v': if (s.charAt(2)=='r' && s.charAt(1)=='a') return VAR; break;
} break;
if (c=='e') { if (s.charAt(2)=='u' && s.charAt(1)=='r') return TRUE; }
else if (c=='s') { if (s.charAt(2)=='i' && s.charAt(1)=='h') return THIS; }
return -1;
- case 'w': if (s.equals("with")) throw new IOException("the WITH keyword is not permitted in XWT scripts"); else return -1;
- case 'v': if (s.equals("void")) throw new IOException("the VOID keyword is not permitted in XWT scripts"); else return -1;
+ case 'w': if (s.equals("with")) return RESERVED; else return -1;
+ case 'v': if (s.equals("void")) return RESERVED; else return -1;
} break;
case 5: switch (s.charAt(2)) {
case 'a': return s.equals("class") ? RESERVED : -1;
case 'i': return s.equals("while") ? WHILE : -1;
case 'l': return s.equals("false") ? FALSE : -1;
case 'n': c=s.charAt(0);
- if (s.equals("const")) throw new IOException("the const keyword is not permitted in XWT");
+ if (s.equals("const")) return RESERVED;
else if (s.equals("final")) return RESERVED;
return -1;
case 'o': c=s.charAt(0);
case 6: switch (s.charAt(1)) {
case 'a': return s.equals("class") ? RESERVED : -1;
case 'e': c=s.charAt(0);
- if (s.equals("delete")) throw new IOException("the delete keyword is not permitted in XWT scripts");
+ if (s.equals("delete")) return RESERVED;
else if (c=='r') return s.equals("return") ? RETURN : -1;
break;
case 'h': return s.equals("throws") ? RESERVED : -1;
break;
case 10: c=s.charAt(1);
if (c=='m') return s.equals("implements") ? RESERVED : -1;
- else if (c=='n' && s.equals("instanceof")) throw new IOException("the instanceof keyword is not permitted in XWT scripts");
+ else if (c=='n' && s.equals("instanceof")) return RESERVED;
break;
case 12: return s.equals("synchronized") ? RESERVED : -1;
}
case '&': return in.match('&') ? AND : in.match('=') ? ASSIGN_BITAND : BITAND;
case '=': return !in.match('=') ? ASSIGN : in.match('=') ? SHEQ : EQ;
case '!': return !in.match('=') ? BANG : in.match('=') ? SHNE : NE;
- case '%': return in.match('=') ? MOD_ASSIGN : MOD;
+ case '%': return in.match('=') ? ASSIGN_MOD : MOD;
case '~': return BITNOT;
case '+': return in.match('=') ? ASSIGN_ADD : in.match('+') ? INC : ADD;
case '-': return in.match('=') ? ASSIGN_SUB: in.match('-') ? DEC : SUB;
import org.xwt.util.*;
import java.io.*;
-/** parses a stream of lexed tokens into ForthBlock's */
-public class Parser extends Lexer implements OpCodes {
+/** Parses a stream of lexed tokens into a tree of ByteCodeBlock's */
+class Parser extends Lexer implements ByteCodes {
+
// Constructors //////////////////////////////////////////////////////
public static void main(String[] s) throws Exception {
Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0);
while(true) {
- ForthBlock block = new ForthBlock(0, "stdin");
+ ByteCodeBlock block = new ByteCodeBlock(0, "stdin");
p.parseStatement(false, block);
if (block == null) return;
System.out.println(block);
}
/** append the largest expression beginning with prefix containing no operators of precedence below <tt>minPrecedence</tt> */
- public ForthBlock startExpr() throws IOException { return startExpr(-1); }
- public void startExpr(ForthBlock block) throws IOException { startExpr(-1, block); }
- public ForthBlock startExpr(int minPrecedence) throws IOException {
- ForthBlock ret = new ForthBlock(line, sourceName);
+ public ByteCodeBlock startExpr() throws IOException { return startExpr(-1); }
+ public void startExpr(ByteCodeBlock block) throws IOException { startExpr(-1, block); }
+ public ByteCodeBlock startExpr(int minPrecedence) throws IOException {
+ ByteCodeBlock ret = new ByteCodeBlock(line, sourceName);
startExpr(minPrecedence, ret);
return ret.size() == 0 ? null : ret;
}
- public void startExpr(int minPrecedence, ForthBlock appendTo) throws IOException {
+ public void startExpr(int minPrecedence, ByteCodeBlock appendTo) throws IOException {
int tok = getToken();
int curLine = line;
if (tok == -1) return;
if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok]))
{ pushBackToken(); return; }
- ForthBlock b = appendTo;
+ ByteCodeBlock b = appendTo;
switch (tok) {
- case NUMBER: continueExpr(b.add(ForthBlock.LITERAL, number), minPrecedence); return;
- case STRING: continueExpr(b.add(ForthBlock.LITERAL, string), minPrecedence); return;
- case THIS: continueExpr(b.add(THIS, null), minPrecedence); return;
- case NULL: continueExpr(b.add(ForthBlock.LITERAL, null), minPrecedence); return;
- case TRUE: case FALSE: continueExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
+ case NUMBER: continueExpr(b.add(ByteCodeBlock.LITERAL, number), minPrecedence); return;
+ case STRING: continueExpr(b.add(ByteCodeBlock.LITERAL, string), minPrecedence); return;
+ case THIS: continueExpr(b.add(TOPSCOPE, null), minPrecedence); return;
+ case NULL: continueExpr(b.add(ByteCodeBlock.LITERAL, null), minPrecedence); return;
+ case TRUE: case FALSE: continueExpr(b.add(ByteCodeBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); return;
case LB: {
- b.add(b.ARRAY, new Integer(0));
+ b.add(ARRAY, new Integer(0));
int i = 0;
while(true) {
int size = b.size();
startExpr(b);
if (size == b.size())
if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
- b.add(b.LITERAL, new Integer(i++));
- if (size == b.size()) b.add(b.LITERAL, null);
- b.add(b.PUT);
- b.add(b.POP);
+ b.add(LITERAL, new Integer(i++));
+ if (size == b.size()) b.add(LITERAL, null);
+ b.add(PUT);
+ b.add(POP);
if (peekToken() == RB) { consume(RB); continueExpr(b, minPrecedence); return; }
consume(COMMA);
}
}
case SUB: {
consume(NUMBER);
- continueExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
+ continueExpr(b.add(ByteCodeBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence);
return;
}
case LP: {
return;
}
case LC: {
- b.add(b.OBJECT, null);
+ b.add(OBJECT, null);
if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
while(true) {
if (peekToken() != NAME && peekToken() != STRING) throw new Error("expected NAME or STRING");
getToken();
- b.add(b.LITERAL, string);
+ b.add(LITERAL, string);
consume(COLON);
startExpr(b);
- b.add(b.PUT);
- b.add(b.POP);
+ b.add(PUT);
+ b.add(POP);
if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
consume(COMMA);
if (peekToken() == RC) { consume(RC); continueExpr(b, minPrecedence); return; }
String name = string;
if (peekToken() == ASSIGN) {
consume(ASSIGN);
- b.add(THIS);
- b.add(ForthBlock.LITERAL, name);
+ b.add(TOPSCOPE);
+ b.add(ByteCodeBlock.LITERAL, name);
startExpr(minPrecedence, b);
- b.add(ForthBlock.PUT);
- b.add(ForthBlock.SWAP);
- b.add(ForthBlock.POP);
+ b.add(ByteCodeBlock.PUT);
+ b.add(ByteCodeBlock.SWAP);
+ b.add(ByteCodeBlock.POP);
} else {
- b.add(THIS);
- b.add(ForthBlock.LITERAL, name);
- b.add(ForthBlock.GET);
+ b.add(TOPSCOPE);
+ b.add(ByteCodeBlock.LITERAL, name);
+ b.add(ByteCodeBlock.GET);
}
continueExpr(b, minPrecedence);
return;
}
- case Tokens.FUNCTION: {
+ case FUNCTION: {
consume(LP);
int numArgs = 0;
- ForthBlock b2 = new ForthBlock(curLine, sourceName);
- b2.add(THIS);
- b2.add(b.SWAP);
- b2.add(b.LITERAL, "arguments");
- b2.add(b.LITERAL, "arguments");
- b2.add(b.DECLARE);
- b2.add(b.SWAP);
- b2.add(b.PUT);
- b2.add(b.SWAP);
- b2.add(b.POP);
+ ByteCodeBlock b2 = new ByteCodeBlock(curLine, sourceName);
+ b2.add(TOPSCOPE);
+ b2.add(SWAP);
+ b2.add(LITERAL, "arguments");
+ b2.add(LITERAL, "arguments");
+ b2.add(DECLARE);
+ b2.add(SWAP);
+ b2.add(PUT);
+ b2.add(SWAP);
+ b2.add(POP);
if (peekToken() == RP) consume(RP);
else while(true) {
if (peekToken() == COMMA) {
- // FIXME: pop an item off the stack here?
consume(COMMA);
+
} else {
consume(NAME);
// declare the name
- b2.add(b.LITERAL, string);
- b2.add(b.DECLARE);
+ b2.add(LITERAL, string);
+ b2.add(DECLARE);
// retrieve it from the arguments array
- b2.add(b.LITERAL, new Integer(numArgs));
- b2.add(b.GET_PRESERVE);
- b2.add(b.SWAP);
- b2.add(b.POP);
+ b2.add(LITERAL, new Integer(numArgs));
+ b2.add(GET_PRESERVE);
+ b2.add(SWAP);
+ b2.add(POP);
// put it to the current scope
- b2.add(THIS);
- b2.add(b.SWAP);
- b2.add(b.LITERAL, string);
- b2.add(b.SWAP);
- b2.add(b.PUT);
+ b2.add(TOPSCOPE);
+ b2.add(SWAP);
+ b2.add(LITERAL, string);
+ b2.add(SWAP);
+ b2.add(PUT);
// clean the stack
- b2.add(b.POP);
- b2.add(b.POP);
+ b2.add(POP);
+ b2.add(POP);
if (peekToken() == RP) { consume(RP); break; }
consume(COMMA);
numArgs++;
}
// pop off the arguments array
- b2.add(b.POP);
+ b2.add(POP);
parseStatement(true, b2);
- b2.add(b.LITERAL, null);
+ b2.add(LITERAL, null);
b2.add(RETURN);
- continueExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence);
+ continueExpr(b.add(NEWFUNCTION, b2), minPrecedence);
return;
}
default: pushBackToken(); return;
}
}
- public void continueExpr(ForthBlock prefix, int minPrecedence) throws IOException {
+ public void continueExpr(ByteCodeBlock prefix, int minPrecedence) throws IOException {
int tok = getToken();
int curLine = line;
if (tok == -1) return;
{ pushBackToken(); return; }
if (prefix == null) throw new Error("got null prefix");
- ForthBlock b = prefix;
+ ByteCodeBlock b = prefix;
switch (tok) {
prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true));
startExpr(precedence[tok - 1], b);
prefix.add(tok - 1);
- prefix.add(b.PUT);
- prefix.add(b.SWAP);
- prefix.add(b.POP);
+ prefix.add(PUT);
+ prefix.add(SWAP);
+ prefix.add(POP);
continueExpr(b, minPrecedence);
return;
}
consume(COMMA);
}
consume(RP);
- b.add(b.CALL, new Integer(i));
+ b.add(CALL, new Integer(i));
continueExpr(b, minPrecedence);
return;
}
int size = b.size();
startExpr(precedence[tok], b);
b.arg[size - 1] = new Integer(b.size() - size + 2);
- b.add(b.JMP, new Integer(2));
- b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
+ b.add(JMP, new Integer(2));
+ b.add(LITERAL, tok == AND ? new Boolean(false) : new Boolean(true));
continueExpr(b, minPrecedence);
return;
}
String target = string;
if (peekToken() == ASSIGN) {
consume(ASSIGN);
- b.add(b.LITERAL, target);
+ b.add(LITERAL, target);
startExpr(b);
- b.add(b.PUT);
- b.add(b.SWAP);
- b.add(b.POP);
+ b.add(PUT);
+ b.add(SWAP);
+ b.add(POP);
} else {
- b.add(b.LITERAL, target);
- b.add(b.GET);
+ b.add(LITERAL, target);
+ b.add(GET);
}
continueExpr(b, minPrecedence);
return;
if (peekToken() == ASSIGN) {
consume(ASSIGN);
startExpr(b);
- b.add(b.PUT);
- b.add(b.SWAP);
- b.add(b.POP);
+ b.add(PUT);
+ b.add(SWAP);
+ b.add(POP);
} else {
- b.add(b.GET);
+ b.add(GET);
}
continueExpr(b, minPrecedence);
return;
}
case HOOK: {
- b.add(b.JF, new Integer(0));
+ b.add(JF, new Integer(0));
int size = b.size();
startExpr(b);
b.arg[size - 1] = new Integer(b.size() - size + 2);
- b.add(b.JMP, new Integer(0));
+ b.add(JMP, new Integer(0));
consume(COLON);
size = b.size();
startExpr(b);
}
/** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */
- public ForthBlock parseStatement() throws IOException {
- ForthBlock ret = new ForthBlock(line, sourceName);
+ public ByteCodeBlock parseStatement() throws IOException {
+ ByteCodeBlock ret = new ByteCodeBlock(line, sourceName);
ret.add(ret.LITERAL, null);
parseStatement(false, ret);
if (ret.size() == 1) return null;
}
public void parseStatement(boolean requireBraces) throws IOException {
- parseStatement(requireBraces, new ForthBlock(line, sourceName));
+ parseStatement(requireBraces, new ByteCodeBlock(line, sourceName));
}
- public void parseStatement(boolean requireBraces, ForthBlock b) throws IOException {
+ public void parseStatement(boolean requireBraces, ByteCodeBlock b) throws IOException {
int tok = peekToken();
if (tok == -1) return;
boolean braced = tok == LC;
case THROW: case RETURN: case ASSERT: {
getToken();
- if (tok == RETURN && peekToken() == SEMI) b.add(b.LITERAL, null);
+ if (tok == RETURN && peekToken() == SEMI) b.add(LITERAL, null);
else startExpr(b);
consume(SEMI);
b.add(tok);
case VAR: {
consume(VAR);
- b.add(THIS); // push the current scope
+ b.add(TOPSCOPE); // push the current scope
while(true) {
consume(NAME);
String name = string;
- b.add(b.LITERAL, name); // push the name to be declared
- b.add(b.DECLARE); // declare it
+ b.add(LITERAL, name); // push the name to be declared
+ b.add(DECLARE); // declare it
if (peekToken() == ASSIGN) { // if there is an '=' after the variable name
- b.add(b.LITERAL, name); // put the var name back on the stack
+ b.add(LITERAL, name); // put the var name back on the stack
consume(ASSIGN);
startExpr(b);
- b.add(b.PUT);
- b.add(b.POP);
+ b.add(PUT);
+ b.add(POP);
}
if (peekToken() != COMMA) break;
consume(COMMA);
}
- b.add(b.POP);
+ b.add(POP);
if (peekToken() == SEMI) consume(SEMI);
break;
}
startExpr(b);
consume(RP);
- b.add(b.JF, new Integer(0));
+ b.add(JF, new Integer(0));
int size = b.size();
parseStatement(false, b);
if (peekToken() == ELSE) {
consume(ELSE);
b.arg[size - 1] = new Integer(2 + b.size() - size);
- b.add(b.JMP, new Integer(3));
- b.add(b.EXPR, parseStatement());
- b.add(b.POP);
- } else {
- b.arg[size - 1] = new Integer(1 + b.size() - size);
+ b.add(JMP, new Integer(0));
+ size = b.size();
+ parseStatement(false, b);
}
+ b.arg[size - 1] = new Integer(1 + b.size() - size);
break;
}
case WHILE: {
consume(WHILE);
consume(LP);
- ForthBlock loop = new ForthBlock(curLine, sourceName);
- b.add(loop.LOOP, loop);
+ ByteCodeBlock loop = new ByteCodeBlock(curLine, sourceName);
+ b.add(LOOP, loop);
- loop.add(loop.POP);
+ loop.add(POP);
startExpr(loop);
- loop.add(loop.JT, new Integer(2));
- loop.add(Lexer.BREAK);
+ loop.add(JT, new Integer(2));
+ loop.add(BREAK);
consume(RP);
parseStatement(false, loop);
case SWITCH: {
consume(SWITCH);
consume(LP);
- ForthBlock loop = new ForthBlock(curLine, sourceName);
- b.add(loop.LOOP, loop);
+ ByteCodeBlock loop = new ByteCodeBlock(curLine, sourceName);
+ b.add(LOOP, loop);
startExpr(loop);
consume(RP);
consume(LC);
while(true)
if (peekToken() == CASE) {
consume(CASE);
- loop.add(loop.DUP);
+ loop.add(DUP);
startExpr(loop);
consume(COLON);
loop.add(EQ);
- loop.add(loop.JF, new Integer(0));
+ loop.add(JF, new Integer(0));
int size = loop.size();
while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) {
int size2 = loop.size();
case DO: {
consume(DO);
- ForthBlock loop = new ForthBlock(curLine, sourceName);
- b.add(loop.LOOP, loop);
+ ByteCodeBlock loop = new ByteCodeBlock(curLine, sourceName);
+ b.add(LOOP, loop);
parseStatement(false, loop);
consume(WHILE);
consume(LP);
startExpr(loop);
- loop.add(loop.JT, new Integer(2));
- loop.add(Lexer.BREAK);
- loop.add(Lexer.CONTINUE);
+ loop.add(JT, new Integer(2));
+ loop.add(BREAK);
+ loop.add(CONTINUE);
consume(RP);
consume(SEMI);
break;
}
case TRY: {
- // FIXME: don't just ignore this!
// We deliberately allow you to omit braces in catch{}/finally{} if they are single statements...
consume(TRY);
parseStatement(true, b);
consume(NAME);
consume(IN);
startExpr(b);
- b.add(b.PUSHKEYS);
- b.add(b.LITERAL, "length");
- b.add(b.GET);
+ b.add(PUSHKEYS);
+ b.add(LITERAL, "length");
+ b.add(GET);
consume(RP);
- ForthBlock b2 = new ForthBlock(curLine, sourceName);
- b.add(b.SCOPE, b2);
- b2.add(b.LITERAL, new Integer(1));
+ ByteCodeBlock b2 = new ByteCodeBlock(curLine, sourceName);
+ b.add(SCOPE, b2);
+ b2.add(LITERAL, new Integer(1));
b2.add(SUB);
- b2.add(b.DUP);
- b2.add(b.LITERAL, new Integer(0));
+ b2.add(DUP);
+ b2.add(LITERAL, new Integer(0));
b2.add(LT);
- b2.add(b.JT, new Integer(7));
- b2.add(b.GET_PRESERVE);
- b2.add(b.LITERAL, varName);
- b2.add(b.LITERAL, varName);
- b2.add(b.DECLARE);
- b2.add(b.PUT);
- b2.add(b.EXPR, parseStatement());
+ b2.add(JT, new Integer(7));
+ b2.add(GET_PRESERVE);
+ b2.add(LITERAL, varName);
+ b2.add(LITERAL, varName);
+ b2.add(DECLARE);
+ b2.add(PUT);
+ parseStatement(false, b2);
break;
} else {
- ForthBlock b2 = new ForthBlock(curLine, sourceName);
- b.add(b.SCOPE, b2);
- b.add(b.POP);
+ ByteCodeBlock b2 = new ByteCodeBlock(curLine, sourceName);
+ b.add(SCOPE, b2);
+ b.add(POP);
int size = b2.size();
startExpr(b2);
- if (b2.size() - size > 0) b2.add(b.POP);
+ if (b2.size() - size > 0) b2.add(POP);
consume(SEMI);
- ForthBlock e2 = startExpr();
+ ByteCodeBlock e2 = startExpr();
consume(SEMI);
+ if (e2 == null) e2 = new ByteCodeBlock(curLine, sourceName, b.LITERAL, null);
- if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null);
-
- ForthBlock b3 = new ForthBlock(curLine, sourceName);
- b2.add(b.LOOP, b3);
- b2.add(b.LITERAL, null);
+ ByteCodeBlock b3 = new ByteCodeBlock(curLine, sourceName);
+ b2.add(LOOP, b3);
+ b2.add(LITERAL, null);
- b3.add(b.JT, new Integer(0));
+ b3.add(JT, new Integer(0));
size = b3.size();
startExpr(b3);
consume(RP);
- if (b3.size() - size > 0) b3.add(b.POP);
+ if (b3.size() - size > 0) b3.add(POP);
b3.arg[size - 1] = new Integer(b3.size() - size + 1);
- b3.add(b.EXPR, e2);
- b3.add(b.JT, new Integer(2));
+ b3.paste(e2);
+ b3.add(JT, new Integer(2));
b3.add(BREAK);
parseStatement(false, b3);
b3.add(BREAK);
String name = string;
if (peekToken() == COLON) {
consume(COLON);
- b.add(ForthBlock.LABEL, string);
+ b.add(ByteCodeBlock.LABEL, string);
break;
} else {
pushBackToken(NAME, name);
int size = b.size();
startExpr(b);
if (size == b.size()) return;
- b.add(b.POP);
+ b.add(POP);
if (peekToken() == SEMI) consume(SEMI);
break;
}
// Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
package org.xwt.js;
-public interface Tokens {
+interface Tokens {
// Token Constants //////////////////////////////////////////////////////////
public final static int
DIV = 28, // /
ASSIGN_DIV = 29, // /=
MOD = 30, // %
- ASSIGN_MOD = 31, // %=
+ ASSIGN_MOD = 31, // %=
BITNOT = 32, // ~
- ASSIGN_BITNOT= 33, // ~=
+ ASSIGN_BITNOT= 33, // ~=
DELPROP = 34, // delete
TYPEOF = 35, // typeof
NAME = 36, // *** identifiers ***
ASSIGN = 57, // =
HOOK = 58, // ?
COLON = 59, // :
- OR = 60, // ||
- AND = 61, // &&
- INC = 62, // ++
- DEC = 63, // --
- DOT = 64, // .
- FUNCTION = 65, // function
+ OR = 60, // ||
+ AND = 61, // &&
+ INC = 62, // ++
+ DEC = 63, // --
+ DOT = 64, // .
+ FUNCTION = 65, // function
IF = 66, // if keyword
- ELSE = 67, // else keyword
- SWITCH = 68, // switch keyword
- CASE = 69, // case keyword
- DEFAULT = 70, // default keyword
- WHILE = 71, // while keyword
- DO = 72, // do keyword
- FOR = 73, // for keyword
- BREAK = 74, // break keyword
- CONTINUE = 75, // continue keyword
- VAR = 76, // var keyword
- WITH = 77, // with keyword
- CATCH = 78, // catch keyword
- FINALLY = 79, // finally keyword
- RESERVED = 80, // reserved keywords
- NOP = 81, // NOP
- VOID = 82, // void keyword
- MOD_ASSIGN = 83, // %=
- BANG = 84, // %=
- ASSERT = 85; // assert keyword
+ ELSE = 67, // else keyword
+ SWITCH = 68, // switch keyword
+ CASE = 69, // case keyword
+ DEFAULT = 70, // default keyword
+ WHILE = 71, // while keyword
+ DO = 72, // do keyword
+ FOR = 73, // for keyword
+ BREAK = 74, // break keyword
+ CONTINUE = 75, // continue keyword
+ VAR = 76, // var keyword
+ WITH = 77, // with keyword
+ CATCH = 78, // catch keyword
+ FINALLY = 79, // finally keyword
+ RESERVED = 80, // reserved keywords
+ BANG = 84, // !
+ ASSERT = 85; // assert keyword
public static final int MAX_TOKEN = ASSERT;
"HOOK", "COLON", "OR", "AND", "INC", "DEC", "DOT", "FUNCTION",
"IF", "ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO",
"FOR", "BREAK", "CONTINUE", "VAR", "WITH", "CATCH", "FINALLY",
- "RESERVED", "NOP", "VOID", "MOD_ASSIGN", "BANG", "ASSERT" };
+ "RESERVED", "BANG", "ASSERT" };
}
store[size++] = o;
}
+ public Object peek() {
+ return lastElement();
+ }
+
public Object elementAt(int i) {
return store[i];
}