From 56387d062db4aca0510daa34579aa139570bac87 Mon Sep 17 00:00:00 2001 From: megacz Date: Fri, 30 Jan 2004 07:00:44 +0000 Subject: [PATCH] 2003/06/04 23:35:36 darcs-hash:20040130070044-2ba56-0ab3382d5fd4ae849b5735d0e3ea20c46fa6c48f.gz --- src/org/xwt/js/ForthBlock.java | 349 ++++++++++++++++ src/org/xwt/js/JS.java | 29 +- src/org/xwt/js/Lexer.java | 109 +---- src/org/xwt/js/Parser.java | 893 ++++++++-------------------------------- src/org/xwt/js/Tokens.java | 111 +++++ 5 files changed, 659 insertions(+), 832 deletions(-) create mode 100644 src/org/xwt/js/ForthBlock.java create mode 100644 src/org/xwt/js/Tokens.java diff --git a/src/org/xwt/js/ForthBlock.java b/src/org/xwt/js/ForthBlock.java new file mode 100644 index 0000000..6946258 --- /dev/null +++ b/src/org/xwt/js/ForthBlock.java @@ -0,0 +1,349 @@ +// Copyright 2002 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 Tokens { + + /* + * Each instruction is an opcode and an optional literal literal. + */ + + // opcodes: + public static final byte arithmetic = -1; // -- arithmetic operators from parser + public static final byte LITERAL = -2; // < String | Number | null > -- push a literal onto the stack + public static final byte ARRAY = -3; // < size > -- create a new array of size + public static final byte OBJECT = -4; // -- push an empty object onto the stack + public static final byte FUNCTION = -5; // < bytecode_block > -- push a new instance of a function with the given bytecode + public static final byte DECLARE = -6; // < name > -- declare in the current scope + public static final byte THIS = -7; // -- push the topmost non-transparent scope onto the stack + public static final byte GET = -8; // -- get stack[0] from stack[1] + public static final byte GET_PRESERVE = -80; // -- get stack[0] from stack[1] + public static final byte PUT = -9; // -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack + public static final byte JT = -13; // < relative_address > -- pop the stack; if true, jump to + public static final byte JF = -21; // < relative_address > -- pop the stack; if false, jump to + public static final byte JMP = -22; // < relative_address > -- jump to + static public final byte POP = -14; // -- discard the top element on the stack + + public static final byte CALL = -15; // < numargs > -- call stack[0] with the topmost values as arguments + + public static final byte PUSHKEYS = -19; // -- ?? + public static final byte EXPR = -20; // -- transitional + public static final byte SWAP = -23; // -- transitional + public static final byte SCOPE = -30; // -- transitional + public static final byte LOOP = -40; // -- transitional + public static final byte DUP = -50; // -- transitional + public static final byte LABEL = -60; // -- transitional + + int line; + String sourceName; + int[] op = new int[10]; + 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 int size() { return size; } + public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; } + public ForthBlock add(int op_) { return add(op_, null); } + public ForthBlock 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; + } + op[size] = op_; + arg[size] = arg_; + size++; + 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= 0; j--) arguments.setElementAt(t.pop(), j); + JS.Function f = (JS.Function)t.pop(); + if (f == null) throw new JS.Exn(new EvaluatorException(line, sourceName, "attempted to call null")); + t.push(f.call(arguments)); + break; + } + + case FUNCTION: { + final ForthBlock myBytes = (ForthBlock)arg[i]; + t.push(new JS.Function() { + public String toString() { return sourceName + ":" + line; } + public String getSourceName() throws JS.Exn { return sourceName; } + public Object _call(final JS.Array args) throws JS.Exn { + Function save = JS.getCurrentFunction(); + JS.currentFunction.put(java.lang.Thread.currentThread(), this); + 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(); + else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction; + 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); + } + } + }); + break; + } + + case Lexer.INC: case Lexer.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); + obj.put(key, val); + t.push(isPrefix ? val : num); + break; + } + + default: { + Object right = t.pop(); + 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 Lexer.ADD: { + Object l = left; + Object r = right; + if (l instanceof String || r instanceof String) { + if (l == null) l = "null"; + if (r == null) r = "null"; + if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue()) + l = new Long(((Number)l).longValue()); + if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue()) + r = new Long(((Number)r).longValue()); + t.push(l.toString() + r.toString()); break; + } + 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 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; + + // 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 Lexer.EQ: + case Lexer.NE: { + // FIXME: should use Javascript coercion-equality rules + Object l = left; + Object r = right; + boolean ret; + if (l == null) { Object tmp = r; r = l; l = tmp; } + if (l == null && r == null) ret = true; + else if (l instanceof Boolean) ret = new Boolean(JS.toBoolean(r)).equals(l); + 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; + } + + default: throw new Error("unknown opcode " + op[i]); + } } + } + 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) 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() { + public Object _call(JS.Array args) { + if (args.length() == 1) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue()); + else if (args.length() == 2) return ((String)o).substring(JS.toNumber(args.elementAt(0)).intValue(), + JS.toNumber(args.elementAt(1)).intValue()); + else throw new Error("String.substring() can only take one or two arguments"); + } + }; + else if (v.equals("toLowerCase")) return new JS.Function() { + public Object _call(JS.Array args) { + return ((String)o).toLowerCase(); + } }; + else if (v.equals("toUpperCase")) return new JS.Function() { + public Object _call(JS.Array args) { + return ((String)o).toString().toUpperCase(); + } }; + else if (v.equals("charAt")) return new JS.Function() { + public Object _call(JS.Array args) { + return ((String)o).charAt(JS.toNumber(args.elementAt(0)).intValue()) + ""; + } }; + else if (v.equals("lastIndexOf")) return new JS.Function() { + public Object _call(JS.Array args) { + if (args.length() != 1) return null; + return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString())); + } }; + else if (v.equals("indexOf")) return new JS.Function() { + public Object _call(JS.Array args) { + if (args.length() != 1) return null; + return new Integer(((String)o).indexOf(args.elementAt(0).toString())); + } }; + throw new Error("Not Implemented: propery " + v + " on String objects"); + } else if (o instanceof Boolean) { + throw new Error("Not Implemented: properties on Boolean objects"); + } else if (o instanceof Number) { + Log.log(this, "Not Implemented: properties on Number objects"); + return null; + //throw new Error("Not Implemented: properties on Number objects"); + } else if (o instanceof JS) { + return ((JS)o).get(v); + } + 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); } + } + + static abstract class ControlTransferException extends Exception { } + static class BreakException extends ControlTransferException { + public String label; + BreakException(String label) { this.label = label; } + } + static class ContinueException extends ControlTransferException { + public String label; + ContinueException(String label) { this.label = label; } + } + static class ReturnException extends ControlTransferException { + public Object retval; + ReturnException(Object retval) { this.retval = retval; } + } + +} diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index 0628891..c053881 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -26,6 +26,23 @@ public abstract class JS { return "unknown:??"; } + public static boolean toBoolean(Object o) { + if (o == null) return false; + if (o instanceof Boolean) return ((Boolean)o).booleanValue(); + 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 == null) return new Long(0); + if (o instanceof Number) return ((Number)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()); + } // Instance Methods //////////////////////////////////////////////////////////////////// @@ -88,7 +105,7 @@ public abstract class JS { } } public void put(Object key, Object val) { - if (key.equals("length")) vec.setSize(Parser.toNumber(val).intValue()); + if (key.equals("length")) vec.setSize(toNumber(val).intValue()); int i = intVal(key); if (i == Integer.MIN_VALUE) super.put(key, val); else { @@ -121,17 +138,17 @@ public abstract class JS { public static class Script extends Function { Vector e = null; private Script(Vector e) { this.e = e; } - public String getSourceName() throws JS.Exn { return ((Parser.Expr)e.elementAt(0)).sourceName; } + 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); Function saved = (Function)currentFunction.get(Thread.currentThread()); currentFunction.put(Thread.currentThread(), this); try { for(int i=0; i - GE = 15, // >= - LSH = 16, // << - ASSIGN_LSH = 17, // <<= - RSH = 18, // >> - ASSIGN_RSH = 19, // >>= - URSH = 20, // >>> - ASSIGN_URSH = 21, // >>>= - ADD = 22, // + - ASSIGN_ADD = 23, // += - SUB = 24, // - - ASSIGN_SUB = 25, // -= - MUL = 26, // * - ASSIGN_MUL = 27, // *= - DIV = 28, // / - ASSIGN_DIV = 29, // /= - MOD = 30, // % - ASSIGN_MOD = 31, // %= - BITNOT = 32, // ~ - ASSIGN_BITNOT= 33, // ~= - DELPROP = 34, // delete - TYPEOF = 35, // typeof - NAME = 36, // *** identifiers *** - NUMBER = 37, // *** numeric literals *** - STRING = 38, // *** string literals *** - NULL = 39, // null - THIS = 40, // this - FALSE = 41, // false - TRUE = 42, // true - SHEQ = 43, // === - SHNE = 44, // !== - THROW = 45, // throw - IN = 46, // in - INSTANCEOF = 47, // instanceof - TRY = 48, // try - SEMI = 49, // ; - LB = 50, // [ - RB = 51, // ] - LC = 52, // { - RC = 53, // } - LP = 54, // ( - RP = 55, // ) - COMMA = 56, // , - ASSIGN = 57, // = - HOOK = 58, // ? - COLON = 59, // : - 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 - - public static final int MAX_TOKEN = ASSERT; - - public final static String[] codeToString = new String[] { - "0", "EOL", "RETURN", "GOTO", "BITOR", "ASSIGN_BITOR", - "BITXOR", "ASSIGN_BITXOR", "BITAND", "ASSIGN_BITAND", "EQ", - "NE", "LT", "LE", "GT", "GE", "LSH", "ASSIGN_LSH", "RSH", - "ASSIGN_RSH", "URSH", "ASSIGN_URSH", "ADD", "ASSIGN_ADD", - "SUB", "ASSIGN_SUB", "MUL", "ASSIGN_MUL", "DIV", "ASSIGN_DIV", - "MOD", "ASSIGN_MOD", "BITNOT", "ASSIGN_BITNOT=", "DELPROP", - "TYPEOF", "NAME", "NUMBER", "STRING", "NULL", "THIS", "FALSE", - "TRUE", "SHEQ", "SHNE", "THROW", "IN", "INSTANCEOF", "TRY", - "SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN", - "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" }; - - // Predicates /////////////////////////////////////////////////////////////////////// protected static boolean isJSIdentifier(String s) { diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index 6f55b50..0a8fa22 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -4,8 +4,7 @@ package org.xwt.js; import org.xwt.util.*; import java.io.*; - -/** parses a stream of lexed tokens into a tree of Expr's */ +/** parses a stream of lexed tokens into ForthBlock's */ public class Parser extends Lexer { // Constructors ////////////////////////////////////////////////////// @@ -20,7 +19,7 @@ public class Parser extends Lexer { public static void main(String[] s) throws Exception { Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0); while(true) { - Expr block = p.parseBlock(false); + ForthBlock block = p.parseStatement(false); if (block == null) return; System.out.println(block); if (p.peekToken() == -1) return; @@ -64,50 +63,46 @@ public class Parser extends Lexer { } /** parses the largest possible expression */ - public Expr parseMaximalExpr() throws IOException { return parseMaximalExpr(null, -1); } - public Expr parseMaximalExpr(Expr prefix, int minPrecedence) throws IOException { + public ForthBlock parseMaximalForthBlock() throws IOException { return parseMaximalForthBlock(null, -1); } + public ForthBlock parseMaximalForthBlock(ForthBlock prefix, int minPrecedence) throws IOException { while(true) { if (peekToken() == -1) break; - Expr save = prefix; - prefix = parseSingleExpr(prefix, minPrecedence); + ForthBlock save = prefix; + prefix = parseSingleForthBlock(prefix, minPrecedence); if (save == prefix) break; - if (prefix == null) throw new ParserException("parseSingleExpr() returned null"); + if (prefix == null) throw new ParserException("parseSingleForthBlock() returned null"); } return prefix; } - public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException { - Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null; + public ForthBlock parseSingleForthBlock(ForthBlock prefix, int minPrecedence) throws IOException { + ForthBlock e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null; int tok = peekToken(); - if (minPrecedence > 0 && - tok < precedence.length && - precedence[tok] != 0 && - (isRightAssociative[tok] ? - (precedence[tok] < minPrecedence) : - (precedence[tok] <= minPrecedence))) { + if (minPrecedence > 0 && tok < precedence.length && precedence[tok] != 0 && + (isRightAssociative[tok] ? (precedence[tok] < minPrecedence) : (precedence[tok] <= minPrecedence))) return prefix; - } + getToken(); int curLine = line; - // these case arms match the precedence of operators; each arm is a precedence level. switch (tok) { case VAR: { if (prefix != null) { pushBackToken(); return prefix; } - ByteCode b = new ByteCode(curLine); - b.add(b.THIS, NO_ARG); + ForthBlock b = new ForthBlock(curLine, sourceName); + b.add(b.THIS); while(true) { consume(NAME); String name = string; - b.add(b.DECLARE, name); + b.add(b.LITERAL, name); + b.add(b.DECLARE); if (peekToken() == ASSIGN) { b.add(b.LITERAL, name); consume(ASSIGN); - b.add(b.EXPR, parseMaximalExpr()); - b.add(b.PUT, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.EXPR, parseMaximalForthBlock()); + b.add(b.PUT); + b.add(b.POP); } if (peekToken() != COMMA) break; consume(COMMA); @@ -119,16 +114,16 @@ public class Parser extends Lexer { case IF: { if (prefix != null) { pushBackToken(); return prefix; } - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); consume(LP); - b.add(b.EXPR, parseMaximalExpr()); + b.add(b.EXPR, parseMaximalForthBlock()); consume(RP); b.add(b.JF, new Integer(3)); - b.add(b.EXPR, parseBlock(false)); + b.add(b.EXPR, parseStatement(false)); b.add(b.JMP, new Integer(2)); if (peekToken() != ELSE) return b.add(b.LITERAL, null); consume(ELSE); - b.add(b.EXPR, parseBlock(false)); + b.add(b.EXPR, parseStatement(false)); return b; } @@ -145,41 +140,41 @@ public class Parser extends Lexer { case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV; case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD; { - ByteCode b = (ByteCode)prefix; + ForthBlock b = (ForthBlock)prefix; b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true)); - b.add(b.EXPR, parseMaximalExpr(null, precedence[tok])); - b.add(tok, NO_ARG); - b.add(b.PUT, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok])); + b.add(tok); + b.add(b.PUT); + b.add(b.SWAP); + b.add(b.POP); return b; } case INC: case DEC: if (prefix == null) { // prefix - ByteCode b = (ByteCode)parseMaximalExpr(null, precedence[tok]); + ForthBlock b = (ForthBlock)parseMaximalForthBlock(null, precedence[tok]); b.set(b.size() - 1, tok, new Boolean(true)); return b; } else { // postfix - ByteCode b = (ByteCode)prefix; + ForthBlock b = (ForthBlock)prefix; b.set(b.size() - 1, tok, new Boolean(false)); return b; } case LP: if (prefix == null) { // grouping - ByteCode b = new ByteCode(curLine, ByteCode.EXPR, parseMaximalExpr()); + ForthBlock b = new ForthBlock(curLine, sourceName, ForthBlock.EXPR, parseMaximalForthBlock()); consume(RP); return b; } else { // invocation - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); int i = 0; b.add(b.EXPR, prefix); while(peekToken() != RP) { - b.add(b.EXPR, parseMaximalExpr()); + b.add(b.EXPR, parseMaximalForthBlock()); i++; if (peekToken() == RP) break; consume(COMMA); @@ -191,49 +186,37 @@ public class Parser extends Lexer { case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: { if (prefix != null) { pushBackToken(); return prefix; } - ByteCode b = new ByteCode(curLine); - b.add(b.EXPR, parseMaximalExpr(null, precedence[tok])); - b.add(tok, NO_ARG); + ForthBlock b = new ForthBlock(curLine, sourceName); + b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok])); + b.add(tok); return b; } case SUB: if (prefix == null && peekToken() == NUMBER) { getToken(); - return new ByteCode(curLine, ByteCode.LITERAL, new Double(number.doubleValue() * -1)); + return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, new Double(number.doubleValue() * -1)); } // else fall through case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH: case RSH: case URSH: case ADD: case MUL: case DIV: case MOD: case GT: case GE: case EQ: case NE: case LT: case LE: { if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression"); - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, prefix); - b.add(b.EXPR, parseMaximalExpr(null, precedence[tok])); - b.add(tok, NO_ARG); + b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok])); + b.add(tok); return b; } // includes short-circuit logic case OR: case AND: { if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression"); - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.LITERAL, tok == AND ? new Boolean(false) : new Boolean(true)); b.add(b.EXPR, prefix); b.add(tok == AND ? b.JF : b.JT, new Integer(3)); - b.add(b.POP, NO_ARG); - b.add(b.EXPR, parseMaximalExpr(null, precedence[tok])); - return b; - } - - case ASSIGN: { - if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression"); - ByteCode b = new ByteCode(curLine); - b.add(b.THIS, NO_ARG); - b.add(b.LITERAL, prefix.string); // FIXME, this is ass-ugly - b.add(b.EXPR, parseMaximalExpr(null, precedence[ASSIGN])); - b.add(b.PUT, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.POP); + b.add(b.EXPR, parseMaximalForthBlock(null, precedence[tok])); return b; } @@ -242,93 +225,91 @@ public class Parser extends Lexer { case NUMBER: if (prefix != null) { pushBackToken(); return prefix; } - return new ByteCode(curLine, ByteCode.LITERAL, number); + return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, number); case STRING: if (prefix != null) { pushBackToken(); return prefix; } - return new ByteCode(curLine, ByteCode.LITERAL, string); + return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, string); case NULL: case TRUE: case FALSE: case NOP: if (prefix != null) { pushBackToken(); return prefix; } - return new ByteCode(curLine, ByteCode.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE)); + return new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE)); case COMMA: pushBackToken(); return prefix; case THIS: if (prefix != null) { pushBackToken(); return prefix; } - return new ByteCode(curLine, ByteCode.THIS, NO_ARG); - - // potential lvalues + return new ForthBlock(curLine, sourceName, ForthBlock.THIS, null); case NAME: { if (prefix != null) { pushBackToken(); return prefix; } String name = string; - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); if (peekToken() == ASSIGN) { consume(ASSIGN); - b.add(ByteCode.THIS, NO_ARG); - b.add(ByteCode.LITERAL, name); - b.add(ByteCode.EXPR, parseMaximalExpr(null, minPrecedence)); - b.add(ByteCode.PUT, NO_ARG); - b.add(ByteCode.SWAP, NO_ARG); - b.add(ByteCode.POP, NO_ARG); + b.add(ForthBlock.THIS); + b.add(ForthBlock.LITERAL, name); + b.add(ForthBlock.EXPR, parseMaximalForthBlock(null, minPrecedence)); + b.add(ForthBlock.PUT); + b.add(ForthBlock.SWAP); + b.add(ForthBlock.POP); return b; } else { - b.add(ByteCode.THIS, NO_ARG); - b.add(ByteCode.LITERAL, name); - b.add(ByteCode.GET, NO_ARG); - return parseMaximalExpr(b, minPrecedence); + b.add(ForthBlock.THIS); + b.add(ForthBlock.LITERAL, name); + b.add(ForthBlock.GET); + return parseMaximalForthBlock(b, minPrecedence); } } case DOT: { consume(NAME); String target = string; - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, prefix); if (peekToken() == ASSIGN) { consume(ASSIGN); - Expr val = parseMaximalExpr(); + ForthBlock val = parseMaximalForthBlock(); b.add(b.LITERAL, target); b.add(b.EXPR, val); - b.add(b.PUT, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.PUT); + b.add(b.SWAP); + b.add(b.POP); } else { b.add(b.LITERAL, target); - b.add(b.GET, NO_ARG); + b.add(b.GET); } return b; } case LB: { - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); if (prefix == null) { b.add(b.ARRAY, new Integer(0)); int i = 0; while(true) { - Expr e = parseMaximalExpr(); + ForthBlock e = parseMaximalForthBlock(); if (e == null && peekToken() == RB) { consume(RB); return b; } b.add(b.LITERAL, new Integer(i++)); if (e == null) b.add(b.LITERAL, null); else b.add(b.EXPR, e); - b.add(b.PUT, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.PUT); + b.add(b.POP); if (peekToken() == RB) { consume(RB); return b; } consume(COMMA); } } else { b.add(b.EXPR, prefix); - b.add(b.EXPR, parseMaximalExpr()); + b.add(b.EXPR, parseMaximalForthBlock()); consume(RB); if (peekToken() == ASSIGN) { consume(ASSIGN); - b.add(b.EXPR, parseMaximalExpr()); - b.add(b.PUT, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.EXPR, parseMaximalForthBlock()); + b.add(b.PUT); + b.add(b.SWAP); + b.add(b.POP); } else { - b.add(b.GET, NO_ARG); + b.add(b.GET); } return b; } @@ -336,7 +317,7 @@ public class Parser extends Lexer { case LC: { if (prefix != null) { pushBackToken(); return prefix; } - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.OBJECT, null); if (peekToken() == RC) { consume(RC); return b; } while(true) { @@ -344,9 +325,9 @@ public class Parser extends Lexer { getToken(); b.add(b.LITERAL, string); consume(COLON); - b.add(b.EXPR, parseMaximalExpr()); - b.add(b.PUT, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.EXPR, parseMaximalForthBlock()); + b.add(b.PUT); + b.add(b.POP); if (peekToken() == RC) { consume(RC); return b; } consume(COMMA); if (peekToken() == RC) { consume(RC); return b; } @@ -354,30 +335,30 @@ public class Parser extends Lexer { } case HOOK: { - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, prefix); b.add(b.JF, new Integer(3)); - b.add(b.EXPR, parseMaximalExpr()); + b.add(b.EXPR, parseMaximalForthBlock()); b.add(b.JMP, new Integer(2)); consume(COLON); - b.add(b.EXPR, parseMaximalExpr()); + b.add(b.EXPR, parseMaximalForthBlock()); return b; } case FUNCTION: { if (prefix != null) { pushBackToken(); return prefix; } consume(LP); - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); int numArgs = 0; - b.add(b.THIS, NO_ARG); - b.add(b.SWAP, NO_ARG); + b.add(b.THIS); + b.add(b.SWAP); b.add(b.LITERAL, "arguments"); b.add(b.LITERAL, "arguments"); - b.add(b.DECLARE, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.PUT, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.DECLARE); + b.add(b.SWAP); + b.add(b.PUT); + b.add(b.SWAP); + b.add(b.POP); if (peekToken() == RP) consume(RP); else while(true) { if (peekToken() == COMMA) { @@ -387,24 +368,24 @@ public class Parser extends Lexer { // declare the name b.add(b.LITERAL, string); - b.add(b.DECLARE, NO_ARG); + b.add(b.DECLARE); // retrieve it from the arguments array b.add(b.LITERAL, new Integer(numArgs)); - b.add(b.GET_PRESERVE, NO_ARG); - b.add(b.SWAP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.GET_PRESERVE); + b.add(b.SWAP); + b.add(b.POP); // put it to the current scope - b.add(b.THIS, NO_ARG); - b.add(b.SWAP, NO_ARG); + b.add(b.THIS); + b.add(b.SWAP); b.add(b.LITERAL, string); - b.add(b.SWAP, NO_ARG); - b.add(b.PUT, NO_ARG); + b.add(b.SWAP); + b.add(b.PUT); // clean the stack - b.add(b.POP, NO_ARG); - b.add(b.POP, NO_ARG); + b.add(b.POP); + b.add(b.POP); if (peekToken() == RP) { consume(RP); break; } consume(COMMA); @@ -412,59 +393,59 @@ public class Parser extends Lexer { numArgs++; } // pop off the arguments array - b.add(b.POP, NO_ARG); - parseBlock(true, b); - return new ByteCode(curLine, b.FUNCTION, b); + b.add(b.POP); + parseStatement(true, b); + return new ForthBlock(curLine, sourceName, b.FUNCTION, b); } case WHILE: { if (prefix != null) { pushBackToken(); return prefix; } consume(LP); - ByteCode r = new ByteCode(curLine); - ByteCode loop = new ByteCode(curLine); + ForthBlock r = new ForthBlock(curLine, sourceName); + ForthBlock loop = new ForthBlock(curLine, sourceName); r.add(loop.LOOP, loop); r.add(r.LITERAL, null); - loop.add(loop.EXPR, parseMaximalExpr()); + loop.add(loop.EXPR, parseMaximalForthBlock()); loop.add(loop.JT, new Integer(2)); - loop.add(Lexer.BREAK, NO_ARG); + loop.add(Lexer.BREAK); consume(RP); - parseBlock(false, loop); + parseStatement(false, loop); // if we fall out of the end, definately continue - loop.add(CONTINUE, NO_ARG); + loop.add(CONTINUE); return r; } case SWITCH: { if (prefix != null) { pushBackToken(); return prefix; } consume(LP); - ByteCode r = new ByteCode(curLine); - ByteCode loop = new ByteCode(curLine); + ForthBlock r = new ForthBlock(curLine, sourceName); + ForthBlock loop = new ForthBlock(curLine, sourceName); r.add(loop.LOOP, loop); r.add(r.LITERAL, null); - loop.add(loop.EXPR, parseMaximalExpr()); + loop.add(loop.EXPR, parseMaximalForthBlock()); consume(RP); consume(LC); while(true) { - Expr caseExpr; + ForthBlock caseForthBlock; tok = getToken(); if (tok == CASE) { - loop.add(loop.DUP, NO_ARG); - loop.add(loop.EXPR, parseMaximalExpr()); - loop.add(EQ, NO_ARG); + loop.add(loop.DUP); + loop.add(loop.EXPR, parseMaximalForthBlock()); + loop.add(EQ); loop.add(loop.JF, new Integer(2)); } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT"); consume(COLON); - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) { - if ((e1 = parseBlock(false)) == null) break; + if ((e1 = parseStatement(false)) == null) break; b.add(b.EXPR, e1); } loop.add(loop.EXPR, b); if (peekToken() == RC) { consume(RC); - r.add(BREAK, NO_ARG); + r.add(BREAK); return r; } } @@ -472,18 +453,18 @@ public class Parser extends Lexer { case DO: { if (prefix != null) { pushBackToken(); return prefix; } - ByteCode r = new ByteCode(curLine); - ByteCode loop = new ByteCode(curLine); + ForthBlock r = new ForthBlock(curLine, sourceName); + ForthBlock loop = new ForthBlock(curLine, sourceName); r.add(loop.LOOP, loop); r.add(r.LITERAL, null); - parseBlock(false, loop); + parseStatement(false, loop); consume(WHILE); consume(LP); - loop.add(loop.EXPR, parseMaximalExpr()); + loop.add(loop.EXPR, parseMaximalForthBlock()); loop.add(loop.JT, new Integer(2)); - loop.add(Lexer.BREAK, NO_ARG); - loop.add(Lexer.CONTINUE, NO_ARG); + loop.add(Lexer.BREAK); + loop.add(Lexer.CONTINUE); consume(RP); consume(SEMI); return r; @@ -493,7 +474,7 @@ public class Parser extends Lexer { // FIXME: don't just ignore this! // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements... if (prefix != null) { pushBackToken(); return prefix; } - Expr tryBlock = parseBlock(true); + ForthBlock tryBlock = parseStatement(true); tok = peekToken(); if (tok == CATCH) { @@ -518,49 +499,60 @@ public class Parser extends Lexer { boolean forIn = peekToken() == IN; pushBackToken(tok, varName); - ByteCode b = new ByteCode(curLine); + ForthBlock b = new ForthBlock(curLine, sourceName); if (forIn) { consume(NAME); consume(IN); - b.add(b.EXPR, parseMaximalExpr()); - b.add(b.PUSHKEYS, NO_ARG); + b.add(b.EXPR, parseMaximalForthBlock()); + b.add(b.PUSHKEYS); b.add(b.LITERAL, "length"); - b.add(b.GET, NO_ARG); + b.add(b.GET); consume(RP); - ByteCode b2 = new ByteCode(curLine); + ForthBlock b2 = new ForthBlock(curLine, sourceName); b.add(b.SCOPE, b2); b2.add(b.LITERAL, new Integer(1)); - b2.add(SUB, NO_ARG); - b2.add(b.DUP, NO_ARG); + b2.add(SUB); + b2.add(b.DUP); b2.add(b.LITERAL, new Integer(0)); - b2.add(LT, NO_ARG); + b2.add(LT); b2.add(b.JT, new Integer(7)); - b2.add(b.GET_PRESERVE, NO_ARG); + b2.add(b.GET_PRESERVE); b2.add(b.LITERAL, varName); b2.add(b.LITERAL, varName); - b2.add(b.DECLARE, NO_ARG); - b2.add(b.PUT, NO_ARG); - b2.add(b.EXPR, parseBlock(false)); + b2.add(b.DECLARE); + b2.add(b.PUT); + b2.add(b.EXPR, parseStatement(false)); b2.add(b.LITERAL, null); return b; } else { - ByteCode b2 = new ByteCode(curLine); + ForthBlock b2 = new ForthBlock(curLine, sourceName); b.add(b.SCOPE, b2); - b2.add(b.EXPR, parseMaximalExpr()); - b2.add(b.POP, NO_ARG); + + e1 = parseMaximalForthBlock(); + if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null); + + b2.add(b.EXPR, e1); + b2.add(b.POP); consume(SEMI); - ByteCode b3 = new ByteCode(curLine); + e2 = parseMaximalForthBlock(); + consume(SEMI); + e3 = parseMaximalForthBlock(); + consume(RP); + + if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null); + if (e3 == null) e3 = new ForthBlock(curLine, sourceName, b.LITERAL, null); + + ForthBlock b3 = new ForthBlock(curLine, sourceName); b2.add(b.LOOP, b3); b2.add(b.LITERAL, null); - b3.add(b.EXPR, parseMaximalExpr()); - consume(SEMI); + b3.add(b.JT, new Integer(3)); + b3.add(b.EXPR, e3); + b3.add(b.POP); + b3.add(b.EXPR, e2); b3.add(b.JT, new Integer(2)); - b3.add(BREAK, NO_ARG); - b3.add(b.JT, new Integer(2)); - b3.add(b.EXPR, parseMaximalExpr()); - consume(RP); - parseBlock(false, b3); + b3.add(BREAK); + parseStatement(false, b3); return b; } } @@ -571,433 +563,33 @@ public class Parser extends Lexer { } } - // Expr ////////////////////////////////////////////////////////////////////// - - public static final Object NO_ARG = new Object(); - - class ByteCode extends Expr { - - // This class interprets a small forthlike bytecode language - // we use to represent JavaScript. It's a stack machine. - - // Each instruction is an opcode and a literal. Any operation - // that accesses the top of the stack and does not have a - // mandatory literal can be performed on a literal instead of - // the top of the stack. - - // opcodes: - public static final byte arithmetic = -1; // -- arithmetic operators from parser - public static final byte LITERAL = -2; // < String | Number | null > -- push a literal onto the stack - public static final byte ARRAY = -3; // < size > -- create a new array of size - public static final byte OBJECT = -4; // -- push an empty object onto the stack - public static final byte FUNCTION = -5; // < bytecode_block > -- push a new instance of a function with the given bytecode - public static final byte DECLARE = -6; // < name > -- declare in the current scope - public static final byte THIS = -7; // -- push the topmost non-transparent scope onto the stack - public static final byte GET = -8; // -- get stack[0] from stack[1] - public static final byte GET_PRESERVE = -80; // -- get stack[0] from stack[1] - public static final byte PUT = -9; // -- put stack[1] to key stack[0] on stack[2]; leaves object on the stack - public static final byte JT = -13; // < relative_address > -- pop the stack; if true, jump to - public static final byte JF = -21; // < relative_address > -- pop the stack; if false, jump to - public static final byte JMP = -22; // < relative_address > -- jump to - static public final byte POP = -14; // -- discard the top element on the stack - - public static final byte CALL = -15; // < numargs > -- call stack[0] with the topmost values as arguments - - public static final byte PUSHKEYS = -19; // -- ?? - public static final byte EXPR = -20; // -- transitional - public static final byte SWAP = -23; // -- transitional - public static final byte SCOPE = -30; // -- transitional - public static final byte LOOP = -40; // -- transitional - public static final byte DUP = -50; // -- transitional - public static final byte LABEL = -60; // -- transitional - - int[] op = new int[10]; - Object[] arg = new Object[10]; - int size = 0; - - public ByteCode(int line) { super(line, "foo"); } - public ByteCode(int line, int op_, Object arg_) { this(line); add(op_, arg_); } - - public int size() { return size; } - public void set(int pos, int op_, Object arg_) { op[pos] = op_; arg[pos] = arg_; } - public ByteCode 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; - } - op[size] = op_; - arg[size] = arg_; - size++; - return this; - } - - public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn { - return eval(s, new Parser.Thread()); - } - public Object eval(final JS.Scope s, Parser.Thread t) throws ControlTransferException { - for(int i=0; i= 0; j--) arguments.setElementAt(t.pop(), j); - JS.Function f = (JS.Function)t.pop(); - if (f == null) throw new JS.Exn(new EvaluatorException("attempted to call null")); - t.push(f.call(arguments)); - break; - } - - case FUNCTION: { - final ByteCode myBytes = (ByteCode)arg[i]; - t.push(new JS.Function() { - public String toString() { return sourceName + ":" + line; } - public String getSourceName() throws JS.Exn { return sourceName; } - public Object _call(final JS.Array args) throws JS.Exn { - Function save = JS.getCurrentFunction(); - JS.currentFunction.put(java.lang.Thread.currentThread(), this); - 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(); - else if (key.equals("cascade")) return org.xwt.Trap.cascadeFunction; - return super.get(key); - } - }; - Parser.Thread t0 = new Parser.Thread(); - t0.push(args); - try { - return myBytes.eval(scope, t0); - } catch (ReturnException r) { - return r.retval; - } catch (ControlTransferException c) { - throw new EvaluatorException("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); - } - } - }); - break; - } - - case Lexer.INC: case Lexer.DEC: { - boolean isPrefix = toBoolean(arg[i]); - Object key = t.pop(); - JS obj = (JS)t.pop(); - Number num = toNumber(obj.get(key)); - Number val = new Double(op[i] == Lexer.INC ? num.doubleValue() + 1.0 : num.doubleValue() - 1.0); - obj.put(key, val); - t.push(isPrefix ? val : num); - break; - } - - default: { - Object right = t.pop(); - Object left = t.pop(); - switch(op[i]) { - - case Lexer.BITOR: t.push(new Long(toLong(left) | toLong(right))); break; - case Lexer.BITXOR: t.push(new Long(toLong(left) ^ toLong(right))); break; - case Lexer.BITAND: t.push(new Long(toLong(left) & toLong(right))); break; - - case Lexer.ADD: { - Object l = left; - Object r = right; - if (l instanceof String || r instanceof String) { - if (l == null) l = "null"; - if (r == null) r = "null"; - if (l instanceof Number && ((Number)l).doubleValue() == ((Number)l).longValue()) - l = new Long(((Number)l).longValue()); - if (r instanceof Number && ((Number)r).doubleValue() == ((Number)r).longValue()) - r = new Long(((Number)r).longValue()); - t.push(l.toString() + r.toString()); break; - } - t.push(new Double(toDouble(l) + toDouble(r))); break; - } - - case Lexer.SUB: t.push(new Double(toDouble(left) - toDouble(right))); break; - case Lexer.MUL: t.push(new Double(toDouble(left) * toDouble(right))); break; - case Lexer.DIV: t.push(new Double(toDouble(left) / toDouble(right))); break; - case Lexer.MOD: t.push(new Double(toDouble(left) % toDouble(right))); break; - - case Lexer.LSH: t.push(new Long(toLong(left) << toLong(right))); break; - case Lexer.RSH: t.push(new Long(toLong(left) >> toLong(right))); break; - case Lexer.URSH: t.push(new Long(toLong(left) >>> toLong(right))); break; - - // FIXME: these need to work on strings - case Lexer.LT: t.push(toDouble(left) < toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break; - case Lexer.LE: t.push(toDouble(left) <= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break; - case Lexer.GT: t.push(toDouble(left) > toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break; - case Lexer.GE: t.push(toDouble(left) >= toDouble(right) ? Boolean.TRUE : Boolean.FALSE); break; - - case Lexer.EQ: - case Lexer.NE: { - // FIXME: should use Javascript coercion-equality rules - Object l = left; - Object r = right; - boolean ret; - if (l == null) { Object tmp = r; r = l; l = tmp; } - if (l == null && r == null) ret = true; - else if (l instanceof Boolean) ret = new Boolean(toBoolean(r)).equals(l); - else if (l instanceof Number) ret = toNumber(r).doubleValue() == 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; - } - - default: throw new Error("unknown opcode " + op[i]); - } } - } - if (t.size() != 1) { - throw new EvaluatorException("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) throw new EvaluatorException("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() { - public Object _call(JS.Array args) { - if (args.length() == 1) return ((String)o).substring(toNumber(args.elementAt(0)).intValue()); - else if (args.length() == 2) return ((String)o).substring(toNumber(args.elementAt(0)).intValue(), - toNumber(args.elementAt(1)).intValue()); - else throw new Error("String.substring() can only take one or two arguments"); - } - }; - else if (v.equals("toLowerCase")) return new JS.Function() { - public Object _call(JS.Array args) { - return ((String)o).toLowerCase(); - } }; - else if (v.equals("toUpperCase")) return new JS.Function() { - public Object _call(JS.Array args) { - return ((String)o).toString().toUpperCase(); - } }; - else if (v.equals("charAt")) return new JS.Function() { - public Object _call(JS.Array args) { - return ((String)o).charAt(toNumber(args.elementAt(0)).intValue()) + ""; - } }; - else if (v.equals("lastIndexOf")) return new JS.Function() { - public Object _call(JS.Array args) { - if (args.length() != 1) return null; - return new Integer(((String)o).lastIndexOf(args.elementAt(0).toString())); - } }; - else if (v.equals("indexOf")) return new JS.Function() { - public Object _call(JS.Array args) { - if (args.length() != 1) return null; - return new Integer(((String)o).indexOf(args.elementAt(0).toString())); - } }; - throw new Error("Not Implemented: propery " + v + " on String objects"); - } else if (o instanceof Boolean) { - throw new Error("Not Implemented: properties on Boolean objects"); - } else if (o instanceof Number) { - Log.log(this, "Not Implemented: properties on Number objects"); - return null; - //throw new Error("Not Implemented: properties on Number objects"); - } else if (o instanceof JS) { - return ((JS)o).get(v); - } - return null; - } - } - - class Thread { - 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; } - } - - class ExprList extends Expr { - Vec v = new Vec(); - public ExprList(int curLine, int code) { super(curLine, code); } - public void add(Expr e) { v.addElement(e); } - public int numExprs() { return v.size(); } - public int size() { return v.size(); } - public Expr elementAt(int i) { return (Expr)v.elementAt(i); } - public Object eval(final JS.Scope s) throws ControlTransferException, JS.Exn { - switch(code) { - case LC: { - // Block - JS.Scope scope = new JS.Scope(s); - for(int i=0; i + GE = 15, // >= + LSH = 16, // << + ASSIGN_LSH = 17, // <<= + RSH = 18, // >> + ASSIGN_RSH = 19, // >>= + URSH = 20, // >>> + ASSIGN_URSH = 21, // >>>= + ADD = 22, // + + ASSIGN_ADD = 23, // += + SUB = 24, // - + ASSIGN_SUB = 25, // -= + MUL = 26, // * + ASSIGN_MUL = 27, // *= + DIV = 28, // / + ASSIGN_DIV = 29, // /= + MOD = 30, // % + ASSIGN_MOD = 31, // %= + BITNOT = 32, // ~ + ASSIGN_BITNOT= 33, // ~= + DELPROP = 34, // delete + TYPEOF = 35, // typeof + NAME = 36, // *** identifiers *** + NUMBER = 37, // *** numeric literals *** + STRING = 38, // *** string literals *** + NULL = 39, // null + THIS = 40, // this + FALSE = 41, // false + TRUE = 42, // true + SHEQ = 43, // === + SHNE = 44, // !== + THROW = 45, // throw + IN = 46, // in + INSTANCEOF = 47, // instanceof + TRY = 48, // try + SEMI = 49, // ; + LB = 50, // [ + RB = 51, // ] + LC = 52, // { + RC = 53, // } + LP = 54, // ( + RP = 55, // ) + COMMA = 56, // , + ASSIGN = 57, // = + HOOK = 58, // ? + COLON = 59, // : + 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 + + public static final int MAX_TOKEN = ASSERT; + + public final static String[] codeToString = new String[] { + "0", "EOL", "RETURN", "GOTO", "BITOR", "ASSIGN_BITOR", + "BITXOR", "ASSIGN_BITXOR", "BITAND", "ASSIGN_BITAND", "EQ", + "NE", "LT", "LE", "GT", "GE", "LSH", "ASSIGN_LSH", "RSH", + "ASSIGN_RSH", "URSH", "ASSIGN_URSH", "ADD", "ASSIGN_ADD", + "SUB", "ASSIGN_SUB", "MUL", "ASSIGN_MUL", "DIV", "ASSIGN_DIV", + "MOD", "ASSIGN_MOD", "BITNOT", "ASSIGN_BITNOT=", "DELPROP", + "TYPEOF", "NAME", "NUMBER", "STRING", "NULL", "THIS", "FALSE", + "TRUE", "SHEQ", "SHNE", "THROW", "IN", "INSTANCEOF", "TRY", + "SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN", + "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" }; + +} -- 1.7.10.4