From: megacz Date: Fri, 30 Jan 2004 06:59:52 +0000 (+0000) Subject: 2003/05/04 04:00:49 X-Git-Tag: RC3~999 X-Git-Url: http://git.megacz.com/?a=commitdiff_plain;h=395d61dea4f5f7b22120e8b0b3fd9b75351c0a98;p=org.ibex.core.git 2003/05/04 04:00:49 darcs-hash:20040130065952-2ba56-0062ee1cb26e0e2ba4d9216029310771ef588513.gz --- diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index 51929af..0a949e3 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -2,54 +2,94 @@ package org.xwt.js; import org.xwt.util.*; - + /** The public API for the JS engine */ +// FEATURE: try using mutable, recycled 'Num' objects public interface JS { - public Object get(Object key) throws JS.Exn; - public Object put(Object key, Object val) throws JS.Exn; - public Object[] enumerateProperties(); - public String coerceToString() throws JS.Exn; - /* - public Num coerceToNumber() throws JS.Exn; - public boolean coerceToBoolean() throws JS.Exn; - public Object call(Object[] args) throws JS.Exn; - */ + public abstract Object get(Object key) throws JS.Exn; + public abstract void put(Object key, Object val) throws JS.Exn; + public abstract Object[] enumerateProperties(); + + public abstract Number coerceToNumber() throws JS.Exn; + public abstract boolean coerceToBoolean() throws JS.Exn; + public abstract String coerceToString() throws JS.Exn; + + public static class Obj implements JS { + private Hash entries; + public Obj() { } + public Object get(Object key) { return entries.get(key); } + public void put(Object key, Object val) { entries.put(key, val); } + public Object[] enumerateProperties() { return(entries.keys()); } + public Number coerceToNumber() { throw new Error("tried to turn a Object into a Number"); } + public String coerceToString() { throw new Error("tried to turn a Object into a String"); } + public boolean coerceToBoolean() { throw new Error("tried to turn a Object into a Boolean"); } + } + + public static class Array extends Obj { + private Vec vec = new Vec(); + private static int intVal(Object o) { + if (o instanceof Number) { + int intVal = ((Number)o).intValue(); + if (intVal == ((Number)o).doubleValue()) return intVal; + return Integer.MIN_VALUE; + } + if (!(o instanceof String)) return Integer.MIN_VALUE; + String s = (String)o; + for(int i=0; i '9') return Integer.MIN_VALUE; + return Integer.parseInt(s); + } + public Object get(Object key) { + if (key.equals("length")) return new Long(vec.size()); + int i = intVal(key); + if (i == Integer.MIN_VALUE) return super.get(key); + return vec.elementAt(i); + } + public void put(Object key, Object val) { + if (key.equals("length")) vec.setSize(Expr.toNumber(val).intValue()); + int i = intVal(key); + if (i == Integer.MIN_VALUE) super.put(key, val); + else vec.setElementAt(val, i); + } + public Object[] enumerateProperties() { return null; } // FIXME + void addElement(Object o) { vec.addElement(o); } + } + + public static interface Function extends JS { + public abstract Object call(JS.Array args) throws JS.Exn; // FEATURE: try to recycle these arrays? + public abstract Scope getParentScopeOfDeclaration(); + } + + public static abstract class ObjFunction extends Obj implements Function { } + /** if JS calls a Java method, and the Java method throws an exception, it can only be caught by JS if it is a subclass of Exn. */ public static class Exn extends RuntimeException { private Object js = null; public Exn(Object js) { this.js = js; } public Object getObject() { return js; } } - + /** Any object which becomes part of the scope chain must support this interface */ - public static interface Scope extends JS { - public boolean has(Object key); - public void declare(String s); - public JS getParentScope(); + public static class Scope extends Obj { + private Scope parentScope; + private static Object NULL = new Object(); + public Scope(Scope parentScope) { this.parentScope = parentScope; } + public JS getParentScope() { return parentScope; } + public boolean has(Object key) { return super.get(key) != null; } + public Object get(Object key) { + if (!has(key)) return getParentScope().get(key); + Object ret = super.get(key); return ret == NULL ? null : ret; + } + public void put(Object key, Object val) { + if (!has(key)) getParentScope().put(key, val); + else super.put(key, val == null ? NULL : val); + } + public Object[] enumerateProperties() { throw new Error("you can't enumerate the properties of a Scope"); } + public void declare(String s) { super.put(s, NULL); } } - /** A mutable, boxed numeric value. These are recycled -- never duplicate references -- use duplicate() instead. */ - /* - public static class Num implements Cloneable, JS { - - private Num() { } - public boolean isDouble = false; - public long longVal = -1; - public double doubleVal = -1; - - private static Vec pool = new Vec(); - public static synchronized void recycle(Num n) { pool.push(n); } - public static synchronized Num getOne() { return (pool.size() > 0) ? (Num)pool.pop() : new Num(); } - - public Num duplicate() { try { return (Num)clone(); } catch (CloneNotSupportedException c) { throw new Error(c); } } - - public Object get(Object key) throws JS.Exn { return null; } - public Object put(Object key, Object val) throws JS.Exn { throw new JS.Exn("attempt to set a property on a Number"); } - public Object[] enumerateProperties() { return new Object[] { }; } - public String coerceToString() throws JS.Exn { return isDouble ? String.valueOf(doubleVal) : String.valueOf(longVal); } - public Num coerceToNumber() throws JS.Exn { return duplicate(); } - public Object call(Object[] args) throws JS.Exn { throw new JS.Exn("attempt to apply the () operator to a Number"); } - } - */ } + + + + diff --git a/src/org/xwt/js/Lexer.java b/src/org/xwt/js/Lexer.java index 92cb4a6..dfc83e0 100644 --- a/src/org/xwt/js/Lexer.java +++ b/src/org/xwt/js/Lexer.java @@ -1,5 +1,4 @@ -// This file was derived from org.mozilla.javascript.TokenStream; it -// is covered by the NPL 1.1. +// Derived from org.mozilla.javascript.TokenStream [NPL] /** * The contents of this file are subject to the Netscape Public @@ -379,7 +378,6 @@ class Lexer { if (op != EOL) twoBack = op; op = _getToken(); } while (op == EOL); // FIXME - //if (op == SEMI) throw new Error(); return op; } diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index 82da503..b09e38c 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -4,6 +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 */ public class Parser extends Lexer { @@ -59,6 +60,7 @@ public class Parser extends Lexer { Expr tail = null; OUTER: while(true) { Expr smt; + switch(tok = peekToken()) { case -1: break OUTER; case LC: smt = parseBlock(true); break; @@ -67,6 +69,7 @@ public class Parser extends Lexer { smt = new Expr(tok, parseMaximalExpr()); if (getToken() != SEMI) throw new Error("expected ;"); break; + case GOTO: case BREAK: case CONTINUE: getToken(); if (getToken() == NAME) @@ -110,21 +113,16 @@ 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 { - Expr save = null; while(true) { - save = prefix; if (peekToken() == -1) break; + Expr save = prefix; prefix = parseSingleExpr(prefix, minPrecedence); if (save == prefix) break; - if (prefix == null) throw new Error("parseSingleExpr_() returned null"); + if (prefix == null) throw new Error("parseSingleExpr() returned null"); } return prefix; } - /** parses the smallest possible complete expression */ - public Expr parseSingleExpr() throws IOException { return parseSingleExpr(null, 0); } - - /** parses the smallest possible complete expression beginning with prefix and only using operators with at least minPrecedence */ public Expr parseSingleExpr(Expr prefix, int minPrecedence) throws IOException { Expr e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null; @@ -149,9 +147,15 @@ public class Parser extends Lexer { case BITOR: case BITXOR: case BITAND: case SHEQ: case SHNE: case LSH: case RSH: case URSH: case ADD: case SUB: case MUL: case DIV: case MOD: case COMMA: case ASSIGN: case GT: case GE: case OR: case AND: - case EQ: case NE: case LT: case LE: case DOT: + case EQ: case NE: case LT: case LE: return new Expr(tok, prefix, parseMaximalExpr(null, precedence[tok])); + case DOT: + e1 = parseMaximalExpr(null, precedence[tok]); + if (e1.code == NAME) e1.code = STRING; + else throw new Error("argument to DOT must be a NAME"); + return new Expr(DOT, prefix, e1); + case BITNOT: case INSTANCEOF: if (prefix != null) throw new Error("didn't expect non-null prefix!"); return new Expr(tok, parseMaximalExpr(null, precedence[tok])); @@ -159,10 +163,11 @@ public class Parser extends Lexer { case INC: case DEC: if (prefix == null) { // prefix - return new Expr(tok, parseMaximalExpr(null, precedence[tok])); + e1 = parseMaximalExpr(null, precedence[tok]); + return new Expr(ASSIGN, e1, new Expr(tok == INC ? ADD : SUB, e1, new Expr(new Integer(1)))); } else { // postfix - return new Expr(tok, null, prefix); + return new Expr(tok, prefix); } case LP: @@ -173,7 +178,7 @@ public class Parser extends Lexer { } else { // invocation while(peekToken() != RP) { - Expr e = parseMaximalExpr(null, precedence[COMMA]); + Expr e = parseMaximalExpr(); if (head == null) head = tail = e; else tail = tail.next = e; tok = getToken(); if (tok == RP) { pushBackToken(); break; } @@ -188,14 +193,14 @@ public class Parser extends Lexer { // subscripting e1 = parseMaximalExpr(); if (getToken() != RB) throw new Error("expected a right brace"); - return new Expr(LB, prefix, e1); + return new Expr(DOT, prefix, e1); } else { // array ctor tok = getToken(); while(true) { if (tok == RB) return new Expr(LB, prefix, head); - if (head == null) head = tail = parseMaximalExpr(null, precedence[COMMA]); - else tail = tail.next = parseMaximalExpr(null, precedence[COMMA]); + if (head == null) head = tail = parseMaximalExpr(); + else tail = tail.next = parseMaximalExpr(); tok = getToken(); if (tok != COMMA && tok != RP) throw new Error("expected right bracket or comma"); } @@ -205,12 +210,12 @@ public class Parser extends Lexer { if (prefix != null) throw new Error("didn't expect non-null prefix"); tok = getToken(); while(true) { - if (tok == RC) return new Expr(LC, head); + if (tok == RC) return new Expr(RC, head); if (tok != NAME) throw new Error("expecting name"); expect(NAME); getToken(); Expr name = new Expr(NAME, string); if (tok != COLON) throw new Error("expecting colon"); - e1 = new Expr(COLON, name, parseMaximalExpr(null, precedence[COMMA])); + e1 = new Expr(COLON, name, parseMaximalExpr()); if (head == null) head = tail = e1; else tail = tail.next = e1; tok = getToken(); if (tok != COMMA && tok != RP) throw new Error("expected right curly or comma"); @@ -231,13 +236,20 @@ public class Parser extends Lexer { Expr firstExpr = null; Expr lastExpr = null; while(true) { - if (getToken() != CASE) throw new Error("expected CASE"); - Expr caseExpr = parseMaximalExpr(); - if (getToken() != COLON) throw new Error("expected COLON"); - Expr e = new Expr(CASE, caseExpr, parseBlock(false)); - if (lastExpr == null) firstExpr = e; - else lastExpr.next = e; - lastExpr = e; + tok = getToken(); + Expr caseExpr; + if (tok == DEFAULT) { + caseExpr = null; + } else if (tok == CASE) { + caseExpr = parseMaximalExpr(); + } else { + throw new Error("expected CASE"); + } + expect(COLON); getToken(); + e1 = new Expr(tok, caseExpr, parseBlock(false)); + if (lastExpr == null) firstExpr = e1; + else lastExpr.next = e1; + lastExpr = e1; if (getToken() == RC) return new Expr(SWITCH, switchExpr, firstExpr); } } @@ -269,7 +281,7 @@ public class Parser extends Lexer { Expr e = null; if (tok == ASSIGN) { getToken(); - initVal = parseMaximalExpr(null, precedence[COMMA]); + initVal = parseMaximalExpr(); tok = peekToken(); e = new Expr(ASSIGN, name, initVal); } else { @@ -285,15 +297,23 @@ public class Parser extends Lexer { // We deliberately allow you to omit braces in catch{}/finally{} if they are single statements... if (prefix != null) throw new Error("didn't expect non-null prefix"); Expr tryBlock = parseBlock(true); - while ((tok = peekToken()) == CATCH || tok == FINALLY) { + + tok = peekToken(); + if (tok == CATCH) { getToken(); if (getToken() != LP) throw new Error("expected ("); if (getToken() != NAME) throw new Error("expected name"); Expr name = new Expr(NAME, string); if (getToken() != RP) throw new Error("expected )"); - e1 = new Expr(tok, name, parseBlock(false)); + head = tail = new Expr(CATCH, name, parseBlock(false)); + tok = peekToken(); + } + if (tok == FINALLY) { + getToken(); + e1 = new Expr(FINALLY, parseBlock(false)); if (head == null) head = tail = e1; else tail = tail.next = e1; } + if (head == null) throw new Error("try without catch or finally"); return new Expr(TRY, tryBlock, head); }