From: megacz Date: Fri, 30 Jan 2004 07:00:47 +0000 (+0000) Subject: 2003/06/07 08:21:30 X-Git-Tag: RC3~947 X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=3734845964723dd29be220bb562c02af2158899d 2003/06/07 08:21:30 darcs-hash:20040130070047-2ba56-73d7a36adf259fbafb7a4292e7ea4e8a65166234.gz --- diff --git a/src/org/xwt/js/ForthBlock.java b/src/org/xwt/js/ForthBlock.java index 43607be..f1f1e0a 100644 --- a/src/org/xwt/js/ForthBlock.java +++ b/src/org/xwt/js/ForthBlock.java @@ -38,8 +38,15 @@ class ForthBlock implements OpCodes, Tokens { 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 OpCodes.THIS: t.push(s); break; // FIXME: transparents + case DECLARE: { + String st = (String)t.pop(); + if (st.equals("sel")) { + System.out.println("**** SEL!"); + for(int j=0; j<4; j++) System.out.println(op[i+j] + " " + arg[i+j]); + } + s.declare(st); break; + } + case THIS: t.push(s); break; // FIXME: transparents 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; @@ -48,7 +55,7 @@ class ForthBlock implements OpCodes, Tokens { 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), t)); break; + case SCOPE: t.push(((ForthBlock)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()); @@ -137,6 +144,7 @@ class ForthBlock implements OpCodes, Tokens { 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); @@ -235,13 +243,18 @@ class ForthBlock implements OpCodes, Tokens { } } } if (t.size() != 1) { + for(int i=0; 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"); } 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 == 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() { diff --git a/src/org/xwt/js/JS.java b/src/org/xwt/js/JS.java index c053881..65bb465 100644 --- a/src/org/xwt/js/JS.java +++ b/src/org/xwt/js/JS.java @@ -1,4 +1,4 @@ -// 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.*; @@ -132,6 +132,7 @@ public abstract class JS { 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); } } @@ -161,7 +162,7 @@ public abstract class JS { try { Vector exprs = new Vector(); while(true) { - ForthBlock ret = p.parseStatement(false); + ForthBlock ret = p.parseStatement(); if (ret == null) break; exprs.addElement(ret); } @@ -194,7 +195,10 @@ public abstract class JS { else super.put(key, val == null ? NULL : val); } public Object[] keys() { throw new Error("you can't enumerate the properties of a Scope"); } - public void declare(String s) { if (isTransparent()) getParentScope().declare(s); else super.put(s, NULL);} + public void declare(String s) { + if (isTransparent()) getParentScope().declare(s); + else super.put(s, NULL); + } } diff --git a/src/org/xwt/js/Lexer.java b/src/org/xwt/js/Lexer.java index 2a28338..47d46f7 100644 --- a/src/org/xwt/js/Lexer.java +++ b/src/org/xwt/js/Lexer.java @@ -100,15 +100,15 @@ class Lexer implements Tokens { if (c=='e') { if (s.charAt(2)=='s' && s.charAt(1)=='l') return ELSE; } else if (c=='m') { if (s.charAt(2)=='u' && s.charAt(1)=='n') return RESERVED; } return -1; - case 'g': return s.equals("goto") ? GOTO : -1; + case 'g': return s.equals("goto") ? RESERVED : -1; case 'l': return s.equals("long") ? RESERVED : -1; case 'n': return s.equals("null") ? NULL : -1; case 't': c=s.charAt(3); 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 'v': return s.equals("void") ? RESERVED : -1; - case 'w': return s.equals("with") ? WITH : -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; } break; case 5: switch (s.charAt(2)) { case 'a': return s.equals("class") ? RESERVED : -1; @@ -175,6 +175,7 @@ class Lexer implements Tokens { in.unread(); String str = in.getString(); int result = getKeyword(str); + if (result == RESERVED) throw new IOException("the reserved word \"" + str + "\" is not permitted in XWT scripts"); if (result != -1) return result; this.string = str; return NAME; diff --git a/src/org/xwt/js/OpCodes.java b/src/org/xwt/js/OpCodes.java index a2a24e7..428d06f 100644 --- a/src/org/xwt/js/OpCodes.java +++ b/src/org/xwt/js/OpCodes.java @@ -20,7 +20,9 @@ public interface OpCodes { public static final byte DECLARE = -6; /** push a reference to the current scope onto the stack */ + /* public static final byte THIS = -7; + */ /** pop two elements off the stack; push stack[-1].get(stack[top]) */ public static final byte GET = -8; diff --git a/src/org/xwt/js/Parser.java b/src/org/xwt/js/Parser.java index ced2039..5a170ba 100644 --- a/src/org/xwt/js/Parser.java +++ b/src/org/xwt/js/Parser.java @@ -1,4 +1,4 @@ -// 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.*; @@ -19,7 +19,8 @@ public class Parser extends Lexer implements OpCodes { public static void main(String[] s) throws Exception { Parser p = new Parser(new InputStreamReader(System.in), "stdin", 0); while(true) { - ForthBlock block = p.parseStatement(false); + ForthBlock block = new ForthBlock(0, "stdin"); + p.parseStatement(false, block); if (block == null) return; System.out.println(block); if (p.peekToken() == -1) return; @@ -31,9 +32,63 @@ public class Parser extends Lexer implements OpCodes { static byte[] precedence = new byte[MAX_TOKEN + 1]; static boolean[] isRightAssociative = new boolean[MAX_TOKEN + 1]; + static boolean[] mustStartExpression = new boolean[MAX_TOKEN + 1]; + static boolean[] cannotStartExpression = new boolean[MAX_TOKEN + 1]; static { - precedence[ASSIGN] = 1; + + mustStartExpression[VAR] = true; + mustStartExpression[IF] = true; + mustStartExpression[BANG] = true; + mustStartExpression[BITNOT] = true; + mustStartExpression[INSTANCEOF] = true; + mustStartExpression[TYPEOF] = true; + mustStartExpression[NUMBER] = true; + mustStartExpression[STRING] = true; + mustStartExpression[NULL] = true; + mustStartExpression[TRUE] = true; + mustStartExpression[FALSE] = true; + mustStartExpression[Tokens.FUNCTION] = true; + mustStartExpression[NAME] = true; + mustStartExpression[LC] = true; + mustStartExpression[THIS] = true; + + cannotStartExpression[OR] = true; + cannotStartExpression[AND] = true; + cannotStartExpression[BITOR] = true; + cannotStartExpression[BITXOR] = true; + cannotStartExpression[BITAND] = true; + cannotStartExpression[SHEQ] = true; + cannotStartExpression[SHNE] = true; + cannotStartExpression[LSH] = true; + cannotStartExpression[RSH] = true; + cannotStartExpression[URSH] = true; + cannotStartExpression[ADD] = true; + cannotStartExpression[MUL] = true; + cannotStartExpression[DIV] = true; + cannotStartExpression[MOD] = true; + cannotStartExpression[GT] = true; + cannotStartExpression[GE] = true; + cannotStartExpression[EQ] = true; + cannotStartExpression[NE] = true; + cannotStartExpression[LT] = true; + cannotStartExpression[LE] = true; + cannotStartExpression[DOT] = true; + cannotStartExpression[HOOK] = true; + cannotStartExpression[ASSIGN_BITOR] = true; + cannotStartExpression[ASSIGN_BITXOR] = true; + cannotStartExpression[ASSIGN_BITAND] = true; + cannotStartExpression[ASSIGN_LSH] = true; + cannotStartExpression[ASSIGN_RSH] = true; + cannotStartExpression[ASSIGN_URSH] = true; + cannotStartExpression[ASSIGN_ADD] = true; + cannotStartExpression[ASSIGN_SUB] = true; + cannotStartExpression[ASSIGN_MUL] = true; + cannotStartExpression[ASSIGN_DIV] = true; + cannotStartExpression[ASSIGN_MOD] = true; + isRightAssociative[ASSIGN] = true; + + precedence[ASSIGN] = 1; precedence[HOOK] = 2; precedence[COMMA] = 3; precedence[OR] = precedence[AND] = 4; @@ -57,6 +112,7 @@ public class Parser extends Lexer implements OpCodes { // Parsing Logic ///////////////////////////////////////////////////////// + /** gets a token and throws an exception if it is not code */ public void consume(int code) throws IOException { if (getToken() != code) throw new ParserException("expected " + codeToString[code] + ", got " + (op == -1 ? "EOL" : codeToString[op])); @@ -64,102 +120,68 @@ public class Parser extends Lexer implements OpCodes { /** parses the largest possible expression */ public ForthBlock parseExpr() throws IOException { return parseExpr(null, -1); } + + /** return the largest expression beginning with prefix containing no operators of precedence below minPrecedence */ public ForthBlock parseExpr(ForthBlock prefix, int minPrecedence) throws IOException { - ForthBlock e1 = null, e2 = null, e3 = null, head = null, tail = null, ret = null; + return parseExpr(prefix, minPrecedence, new ForthBlock(line, sourceName)); + } - int tok = peekToken(); - if (minPrecedence > 0 && tok < precedence.length && precedence[tok] != 0 && - (isRightAssociative[tok] ? (precedence[tok] < minPrecedence) : (precedence[tok] <= minPrecedence))) - return prefix; + /** append the largest expression beginning with prefix containing no operators of precedence below minPrecedence */ + public ForthBlock parseExpr(ForthBlock prefix, int minPrecedence, ForthBlock appendTo) throws IOException { - getToken(); + int tok = getToken(); int curLine = line; + if (tok == -1) return prefix; + if (minPrecedence > 0 && precedence[tok] != 0) + if (precedence[tok] < minPrecedence || (precedence[tok] == minPrecedence && !isRightAssociative[tok])) + { pushBackToken(); return prefix; } - switch (tok) { + if (prefix != null && mustStartExpression[tok]) { pushBackToken(); return prefix; } + if (prefix == null && cannotStartExpression[tok]) { pushBackToken(); return prefix; } - case VAR: { - if (prefix != null) { pushBackToken(); return prefix; } - ForthBlock b = new ForthBlock(curLine, sourceName); - b.add(OpCodes.THIS); - while(true) { - consume(NAME); - String name = string; - b.add(b.LITERAL, name); - b.add(b.DECLARE); - if (peekToken() == ASSIGN) { - b.add(b.LITERAL, name); - consume(ASSIGN); - b.add(b.EXPR, parseExpr()); - b.add(b.PUT); - b.add(b.POP); - } - if (peekToken() != COMMA) break; - consume(COMMA); - } - return parseExpr(b, minPrecedence); - } + ForthBlock b = appendTo; + + switch (tok) { - case IN: pushBackToken(); return prefix; + case NUMBER: return parseExpr(b.add(ForthBlock.LITERAL, number), minPrecedence); + case STRING: return parseExpr(b.add(ForthBlock.LITERAL, string), minPrecedence); + case THIS: return parseExpr(b.add(THIS, null), minPrecedence); + case NULL: return parseExpr(b.add(ForthBlock.LITERAL, null), minPrecedence); + case TRUE: case FALSE: return parseExpr(b.add(ForthBlock.LITERAL, new Boolean(tok == TRUE)), minPrecedence); - case IF: { - if (prefix != null) { pushBackToken(); return prefix; } - ForthBlock b = new ForthBlock(curLine, sourceName); - consume(LP); - b.add(b.EXPR, parseExpr()); - consume(RP); - b.add(b.JF, new Integer(3)); - b.add(b.EXPR, parseStatement(false)); - b.add(b.JMP, new Integer(2)); - if (peekToken() != ELSE) return parseExpr(b.add(b.LITERAL, null), minPrecedence); - consume(ELSE); - b.add(b.EXPR, parseStatement(false)); + case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH: + case ASSIGN_ADD: case ASSIGN_SUB: case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: { + b.add(b.EXPR, prefix); + prefix.set(prefix.size() - 1, b.GET_PRESERVE, new Boolean(true)); + prefix.add(prefix.EXPR, parseExpr(null, precedence[tok - 1])); + prefix.add(tok - 1); + prefix.add(b.PUT); + prefix.add(b.SWAP); + prefix.add(b.POP); return parseExpr(b, minPrecedence); } - // FIXME: ugly hack!! - case ASSIGN_BITOR: if (tok == ASSIGN_BITOR) tok = BITOR; - case ASSIGN_BITXOR: if (tok == ASSIGN_BITXOR) tok = BITXOR; - case ASSIGN_BITAND: if (tok == ASSIGN_BITAND) tok = BITAND; - case ASSIGN_LSH: if (tok == ASSIGN_LSH) tok = LSH; - case ASSIGN_RSH: if (tok == ASSIGN_RSH) tok = RSH; - case ASSIGN_URSH: if (tok == ASSIGN_URSH) tok = URSH; - case ASSIGN_ADD: if (tok == ASSIGN_ADD) tok = ADD; - case ASSIGN_SUB: if (tok == ASSIGN_SUB) tok = SUB; - case ASSIGN_MUL: if (tok == ASSIGN_MUL) tok = MUL; - case ASSIGN_DIV: if (tok == ASSIGN_DIV) tok = DIV; - case ASSIGN_MOD: if (tok == ASSIGN_MOD) tok = MOD; - { - ForthBlock b = (ForthBlock)prefix; - b.set(b.size() - 1, b.GET_PRESERVE, new Boolean(true)); - b.add(b.EXPR, parseExpr(null, precedence[tok])); - b.add(tok); - b.add(b.PUT); - b.add(b.SWAP); - b.add(b.POP); - return parseExpr(b, minPrecedence); - } - case INC: case DEC: if (prefix == null) { // prefix - ForthBlock b = (ForthBlock)parseExpr(null, precedence[tok]); - b.set(b.size() - 1, tok, new Boolean(true)); + ForthBlock subexp = parseExpr(null, precedence[tok]); + subexp.set(subexp.size() - 1, tok, new Boolean(true)); + b.add(b.EXPR, subexp); return parseExpr(b, minPrecedence); } else { // postfix - ForthBlock b = (ForthBlock)prefix; - b.set(b.size() - 1, tok, new Boolean(false)); + prefix.set(prefix.size() - 1, tok, new Boolean(false)); + b.add(b.EXPR, prefix); return parseExpr(b, minPrecedence); } case LP: if (prefix == null) { // grouping - ForthBlock b = new ForthBlock(curLine, sourceName, ForthBlock.EXPR, parseExpr()); + b.add(EXPR, parseExpr()); consume(RP); return parseExpr(b, minPrecedence); } else { // invocation - ForthBlock b = new ForthBlock(curLine, sourceName); int i = 0; b.add(b.EXPR, prefix); while(peekToken() != RP) { @@ -174,33 +196,27 @@ public class Parser extends Lexer implements OpCodes { } case BANG: case BITNOT: case INSTANCEOF: case TYPEOF: { - if (prefix != null) { pushBackToken(); return prefix; } - ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, parseExpr(null, precedence[tok])); b.add(tok); return parseExpr(b, minPrecedence); } case SUB: - if (prefix == null && peekToken() == NUMBER) { - getToken(); - return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence); - } // else fall through + if (prefix == null) { + consume(NUMBER); + return parseExpr(b.add(ForthBlock.LITERAL, new Double(number.doubleValue() * -1)), minPrecedence); + } + // 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"); - ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, prefix); b.add(b.EXPR, parseExpr(null, precedence[tok])); b.add(tok); return parseExpr(b, minPrecedence); } - // includes short-circuit logic case OR: case AND: { - if (prefix == null) throw new ParserException("the " + codeToString[tok] + " token cannot start an expression"); - 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)); @@ -209,52 +225,27 @@ public class Parser extends Lexer implements OpCodes { return parseExpr(b, minPrecedence); } - case WITH: throw new ParserException("XWT does not allow the WITH keyword"); - case VOID: case RESERVED: throw new ParserException("reserved word that you shouldn't be using"); - - case NUMBER: - if (prefix != null) { pushBackToken(); return prefix; } - return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, number), minPrecedence); - - case STRING: - if (prefix != null) { pushBackToken(); return prefix; } - return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, string), minPrecedence); - - case NULL: case TRUE: case FALSE: case NOP: - if (prefix != null) { pushBackToken(); return prefix; } - return parseExpr(new ForthBlock(curLine, sourceName, ForthBlock.LITERAL, (tok == NULL || tok == NOP) ? null : new Boolean(tok == TRUE)), minPrecedence); - - case COMMA: pushBackToken(); return prefix; - - case Tokens.THIS: - if (prefix != null) { pushBackToken(); return prefix; } - return parseExpr(new ForthBlock(curLine, sourceName, OpCodes.THIS, null), minPrecedence); - case NAME: { - if (prefix != null) { pushBackToken(); return prefix; } String name = string; - ForthBlock b = new ForthBlock(curLine, sourceName); if (peekToken() == ASSIGN) { consume(ASSIGN); - b.add(OpCodes.THIS); + b.add(THIS); b.add(ForthBlock.LITERAL, name); b.add(ForthBlock.EXPR, parseExpr(null, minPrecedence)); b.add(ForthBlock.PUT); b.add(ForthBlock.SWAP); b.add(ForthBlock.POP); - return parseExpr(b, minPrecedence); } else { - b.add(OpCodes.THIS); + b.add(THIS); b.add(ForthBlock.LITERAL, name); b.add(ForthBlock.GET); - return parseExpr(parseExpr(b, minPrecedence), minPrecedence); } + return parseExpr(b, minPrecedence); } case DOT: { consume(NAME); String target = string; - ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, prefix); if (peekToken() == ASSIGN) { consume(ASSIGN); @@ -272,7 +263,6 @@ public class Parser extends Lexer implements OpCodes { } case LB: { - ForthBlock b = new ForthBlock(curLine, sourceName); if (prefix == null) { b.add(b.ARRAY, new Integer(0)); int i = 0; @@ -305,8 +295,6 @@ public class Parser extends Lexer implements OpCodes { } case LC: { - if (prefix != null) { pushBackToken(); return prefix; } - ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.OBJECT, null); if (peekToken() == RC) { consume(RC); return parseExpr(b, minPrecedence); } while(true) { @@ -324,7 +312,6 @@ public class Parser extends Lexer implements OpCodes { } case HOOK: { - ForthBlock b = new ForthBlock(curLine, sourceName); b.add(b.EXPR, prefix); b.add(b.JF, new Integer(3)); b.add(b.EXPR, parseExpr()); @@ -335,46 +322,46 @@ public class Parser extends Lexer implements OpCodes { } case Tokens.FUNCTION: { - if (prefix != null) { pushBackToken(); return prefix; } consume(LP); - ForthBlock b = new ForthBlock(curLine, sourceName); int numArgs = 0; - b.add(OpCodes.THIS); - b.add(b.SWAP); - b.add(b.LITERAL, "arguments"); - b.add(b.LITERAL, "arguments"); - b.add(b.DECLARE); - b.add(b.SWAP); - b.add(b.PUT); - b.add(b.SWAP); - b.add(b.POP); + 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); 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 - b.add(b.LITERAL, string); - b.add(b.DECLARE); + b2.add(b.LITERAL, string); + b2.add(b.DECLARE); // retrieve it from the arguments array - b.add(b.LITERAL, new Integer(numArgs)); - b.add(b.GET_PRESERVE); - b.add(b.SWAP); - b.add(b.POP); + b2.add(b.LITERAL, new Integer(numArgs)); + b2.add(b.GET_PRESERVE); + b2.add(b.SWAP); + b2.add(b.POP); // put it to the current scope - b.add(OpCodes.THIS); - b.add(b.SWAP); - b.add(b.LITERAL, string); - b.add(b.SWAP); - b.add(b.PUT); + b2.add(THIS); + b2.add(b.SWAP); + b2.add(b.LITERAL, string); + b2.add(b.SWAP); + b2.add(b.PUT); // clean the stack - b.add(b.POP); - b.add(b.POP); + b2.add(b.POP); + b2.add(b.POP); if (peekToken() == RP) { consume(RP); break; } consume(COMMA); @@ -382,105 +369,198 @@ public class Parser extends Lexer implements OpCodes { numArgs++; } // pop off the arguments array - b.add(b.POP); - parseStatement(true, b); - return parseExpr(new ForthBlock(curLine, sourceName, OpCodes.FUNCTION, b), minPrecedence); + b2.add(b.POP); + parseStatement(true, b2); + b2.add(b.LITERAL, null); + b2.add(RETURN); + return parseExpr(b.add(OpCodes.FUNCTION, b2), minPrecedence); } - case WHILE: { - if (prefix != null) { pushBackToken(); return prefix; } - consume(LP); - 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, parseExpr()); - loop.add(loop.JT, new Integer(2)); - loop.add(Lexer.BREAK); - consume(RP); - parseStatement(false, loop); - - // if we fall out of the end, definately continue - loop.add(CONTINUE); - return parseExpr(r, minPrecedence); + default: pushBackToken(); return prefix; } + } + + /** 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); + ret.add(ret.LITERAL, null); + parseStatement(false, ret); + if (ret.size() == 1) return null; + return ret; + } - case SWITCH: { - if (prefix != null) { pushBackToken(); return prefix; } - consume(LP); - 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, parseExpr()); - consume(RP); - consume(LC); - while(true) { - ForthBlock caseForthBlock; - tok = getToken(); - if (tok == CASE) { - loop.add(loop.DUP); - loop.add(loop.EXPR, parseExpr()); - loop.add(EQ); - loop.add(loop.JF, new Integer(2)); - } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT"); - consume(COLON); - ForthBlock b = new ForthBlock(curLine, sourceName); - while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) { - if ((e1 = parseStatement(false)) == null) break; - b.add(b.EXPR, e1); + public void parseStatement(boolean requireBraces) throws IOException { + parseStatement(requireBraces, new ForthBlock(line, sourceName)); + } + public void parseStatement(boolean requireBraces, ForthBlock b) throws IOException { + int tok = peekToken(); + if (tok == -1) return; + boolean braced = tok == LC; + if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]); + if (braced) consume(LC); + int curLine = line; + while(true) { + switch(tok = peekToken()) { + + case THROW: case RETURN: case ASSERT: { + getToken(); + if (tok == RETURN && peekToken() == SEMI) b.add(b.LITERAL, null); + else b.add(b.EXPR, parseExpr()); + consume(SEMI); + b.add(tok); + break; + } + + case BREAK: case CONTINUE: { + getToken(); + if (peekToken() == NAME) consume(NAME); + b.add(tok, string); + consume(SEMI); + break; + } + + case SEMI: + consume(SEMI); + if (!braced) return; + break; + + case VAR: { + consume(VAR); + b.add(THIS); // 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 + if (peekToken() == ASSIGN) { // if there is an '=' after the variable name + b.add(b.LITERAL, name); // put the var name back on the stack + consume(ASSIGN); + b.add(b.EXPR, parseExpr()); + b.add(b.PUT); + b.add(b.POP); + } + if (peekToken() != COMMA) break; + consume(COMMA); } - loop.add(loop.EXPR, b); - if (peekToken() == RC) { - consume(RC); - r.add(BREAK); - return parseExpr(r, minPrecedence); + b.add(b.POP); + if (peekToken() == SEMI) consume(SEMI); + break; + } + + case IF: { + consume(IF); + consume(LP); + b.add(b.EXPR, parseExpr()); + consume(RP); + b.add(b.JF, new Integer(4)); + b.add(b.EXPR, parseStatement()); + b.add(b.POP); + b.add(b.JMP, new Integer(3)); + if (peekToken() == ELSE) { + consume(ELSE); + b.add(b.EXPR, parseStatement()); + b.add(b.POP); + } else { + b.add(b.JMP, new Integer(1)); // nop + b.add(b.JMP, new Integer(1)); // nop } + break; } - } - - case DO: { - if (prefix != null) { pushBackToken(); return prefix; } - ForthBlock r = new ForthBlock(curLine, sourceName); - ForthBlock loop = new ForthBlock(curLine, sourceName); - r.add(loop.LOOP, loop); - r.add(r.LITERAL, null); - - parseStatement(false, loop); - consume(WHILE); - consume(LP); - loop.add(loop.EXPR, parseExpr()); - loop.add(loop.JT, new Integer(2)); - loop.add(Lexer.BREAK); - loop.add(Lexer.CONTINUE); - consume(RP); - consume(SEMI); - return parseExpr(r, minPrecedence); - } - case TRY: { - // 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; } - ForthBlock tryBlock = parseStatement(true); - - tok = peekToken(); - if (tok == CATCH) { - getToken(); - if (getToken() != LP) throw new ParserException("expected ("); - if (getToken() != NAME) throw new ParserException("expected name"); - if (getToken() != RP) throw new ParserException("expected )"); - tok = peekToken(); + case WHILE: { + consume(WHILE); + consume(LP); + ForthBlock loop = new ForthBlock(curLine, sourceName); + b.add(loop.LOOP, loop); + + loop.add(loop.POP); + loop.add(loop.EXPR, parseExpr()); + loop.add(loop.JT, new Integer(2)); + loop.add(Lexer.BREAK); + consume(RP); + parseStatement(false, loop); + + // if we fall out of the end, definately continue + loop.add(CONTINUE); + break; } - if (tok == FINALLY) getToken(); - return parseExpr(tryBlock, minPrecedence); - } + case SWITCH: { + consume(SWITCH); + consume(LP); + ForthBlock loop = new ForthBlock(curLine, sourceName); + b.add(loop.LOOP, loop); + loop.add(loop.EXPR, parseExpr()); + consume(RP); + consume(LC); + while(true) { + ForthBlock caseForthBlock; + tok = getToken(); + if (tok == CASE) { + loop.add(loop.DUP); + loop.add(loop.EXPR, parseExpr()); + loop.add(EQ); + loop.add(loop.JF, new Integer(2)); + } else if (tok != DEFAULT) throw new ParserException("expected CASE or DEFAULT"); + consume(COLON); + ForthBlock b2 = new ForthBlock(curLine, sourceName); + ForthBlock e1 = null; + while(peekToken() != CASE && peekToken() != DEFAULT && peekToken() != RC) { + if ((e1 = parseStatement()) == null) break; + b2.add(b.EXPR, e1); + b2.add(b.POP); + } + loop.add(loop.EXPR, b2); + if (peekToken() == RC) { + consume(RC); + loop.add(BREAK); + break; + } + } + break; + } + + case DO: { + consume(DO); + ForthBlock loop = new ForthBlock(curLine, sourceName); + b.add(loop.LOOP, loop); + + parseStatement(false, loop); + consume(WHILE); + consume(LP); + loop.add(loop.EXPR, parseExpr()); + loop.add(loop.JT, new Integer(2)); + loop.add(Lexer.BREAK); + loop.add(Lexer.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); + + if (peekToken() == CATCH) { + getToken(); + consume(LP); + consume(NAME); + consume(RP); + parseStatement(); // just discard the catchblock + } + + if (peekToken() == FINALLY) { + consume(FINALLY); + parseStatement(false, b); + } + break; + } case FOR: { - if (prefix != null) { pushBackToken(); return prefix; } - if (getToken() != LP) throw new ParserException("expected left paren"); + consume(FOR); + consume(LP); tok = getToken(); if (tok == VAR) tok = getToken(); @@ -488,7 +568,6 @@ public class Parser extends Lexer implements OpCodes { boolean forIn = peekToken() == IN; pushBackToken(tok, varName); - ForthBlock b = new ForthBlock(curLine, sourceName); if (forIn) { consume(NAME); consume(IN); @@ -510,23 +589,24 @@ public class Parser extends Lexer implements OpCodes { b2.add(b.LITERAL, varName); b2.add(b.DECLARE); b2.add(b.PUT); - b2.add(b.EXPR, parseStatement(false)); - b2.add(b.LITERAL, null); - return parseExpr(b, minPrecedence); + b2.add(b.EXPR, parseStatement()); + //b2.add(b.LITERAL, null); + break; } else { ForthBlock b2 = new ForthBlock(curLine, sourceName); b.add(b.SCOPE, b2); + b.add(b.POP); - e1 = parseExpr(); + ForthBlock e1 = parseExpr(); if (e1 == null) e1 = new ForthBlock(curLine, sourceName, b.LITERAL, null); b2.add(b.EXPR, e1); b2.add(b.POP); consume(SEMI); - e2 = parseExpr(); + ForthBlock e2 = parseExpr(); consume(SEMI); - e3 = parseExpr(); + ForthBlock e3 = parseExpr(); consume(RP); if (e2 == null) e2 = new ForthBlock(curLine, sourceName, b.LITERAL, null); @@ -542,88 +622,38 @@ public class Parser extends Lexer implements OpCodes { b3.add(b.JT, new Integer(2)); b3.add(BREAK); parseStatement(false, b3); - return parseExpr(b, minPrecedence); + b3.add(BREAK); + break; } } - default: - pushBackToken(); - return prefix; - } - } - - /** a block is either a single statement or a list of statements surrounded by curly braces; all expressions are also statements */ - public ForthBlock parseStatement(boolean requireBraces) throws IOException { return parseStatement(requireBraces, null); } - public ForthBlock parseStatement(boolean requireBraces, ForthBlock b) throws IOException { - ForthBlock smt = null; - int tok = peekToken(); - if (tok == -1) return null; - boolean braced = tok == LC; - if (requireBraces && !braced) throw new ParserException("expected {, got " + codeToString[tok]); - if (braced) consume(LC); - int curLine = line; - ForthBlock ret = new ForthBlock(curLine, sourceName); - ForthBlock block = b == null ? new ForthBlock(curLine, sourceName) : b; - block.add(ret.LITERAL, Boolean.TRUE); - ret.add(block.SCOPE, block); - while(true) { - switch(tok = peekToken()) { - - case LC: smt = parseStatement(true); break; - case GOTO: throw new ParserException("goto not supported"); - - case THROW: case RETURN: case ASSERT: { - getToken(); - ForthBlock r = new ForthBlock(curLine, sourceName); - if (tok == RETURN && peekToken() == SEMI) r.add(b.LITERAL, null); - else r.add(b.EXPR, parseExpr()); - consume(SEMI); - r.add(tok); - smt = r; - break; - } - - case BREAK: case CONTINUE: { - getToken(); - if (peekToken() == NAME) consume(NAME); - smt = new ForthBlock(curLine, sourceName, tok, string); - consume(SEMI); - break; - } - - case RC: - if (braced) consume(RC); - return block.size() == 0 ? null : ret; - - case SEMI: - consume(SEMI); - if (!braced) return block.size() == 0 ? null : ret; - continue; - case NAME: { - String name = string; consume(NAME); + String name = string; if (peekToken() == COLON) { consume(COLON); - smt = new ForthBlock(curLine, sourceName, ForthBlock.LABEL, string); + b.add(ForthBlock.LABEL, string); break; } else { pushBackToken(NAME, name); // fall through to default case } } - - case -1: - default: - smt = parseExpr(); - if (smt == null) return block.size() == 0 ? null : ret; - if (peekToken() == SEMI) getToken(); + // fall through + case RC: + if (tok == RC && braced) { consume(RC); return; } + // fall through + default: { + ForthBlock ret = parseExpr(); + if (ret == null) return; + b.add(b.EXPR, ret); + b.add(b.POP); + if (peekToken() == SEMI) consume(SEMI); break; } - - if (!braced) return smt; - block.add(block.EXPR, smt); - block.add(block.POP); + } + + if (!braced) return; } }