/** finish a finally block and carry out whatever instruction initiated the finally block */
public static final byte FINALLY_DONE = -24;
+ /** finish a finally block and carry out whatever instruction initiated the finally block */
+ public static final byte MAKE_GRAMMAR = -25;
+
public static final String[] bytecodeToString = new String[] {
"", "", "LITERAL", "ARRAY", "OBJECT", "NEWFUNCTION", "DECLARE", "TOPSCOPE",
"GET", "GET_PRESERVE", "PUT", "JT", "JF", "JMP", "POP", "CALL", "PUSHKEYS",
"SWAP", "NEWSCOPE", "OLDSCOPE", "DUP", "LABEL", "LOOP", "CALLMETHOD",
- "FINALLY_DONE"
+ "FINALLY_DONE", "MAKE_GRAMMAR"
};
}
case THROW:
throw new JSExn(stack.pop());
+ case MAKE_GRAMMAR: {
+ final Grammar r = (Grammar)arg;
+ final JSScope final_scope = scope;
+ Grammar r2 = new Grammar() {
+ public int match(String s, int start, Hash v, JSScope scope) throws JSExn {
+ return r.match(s, start, v, final_scope);
+ }
+ public int matchAndWrite(String s, int start, Hash v, JSScope scope, String key) throws JSExn {
+ return r.matchAndWrite(s, start, v, final_scope, key);
+ }
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ Hash v = new Hash();
+ r.matchAndWrite((String)a0, 0, v, final_scope, "foo");
+ return v.get("foo");
+ }
+ };
+ Object obj = stack.pop();
+ if (obj != null && obj instanceof Grammar) r2 = new Grammar.Alternative((Grammar)obj, r2);
+ stack.push(r2);
+ break;
+ }
+
case ASSIGN_SUB: case ASSIGN_ADD: {
Object val = stack.pop();
Object key = stack.pop();
return cx.resume();
}
+ public JSScope getParentScope() { return parentScope; }
// Adding and Altering Bytecodes ///////////////////////////////////////////////////
case ')': return RP;
case ',': return COMMA;
case '?': return HOOK;
- case ':': return COLON;
+ case ':': return !in.match(':') ? COLON : in.match('=') ? GRAMMAR : le(":: is not a valid token");
case '.': return DOT;
case '|': return in.match('|') ? OR : (in.match('=') ? ASSIGN_BITOR : BITOR);
case '^': return in.match('=') ? ASSIGN_BITXOR : BITXOR;
}
}
+ private int le(String s) throws LexerException { if (true) throw new LexerException(s); return 0; }
// SmartReader ////////////////////////////////////////////////////////////////
continueExpr(b, minPrecedence);
}
+ private Grammar parseGrammar(Grammar g) throws IOException {
+ int tok = getToken();
+ if (g != null)
+ switch(tok) {
+ case BITOR: return new Grammar.Alternative(g, parseGrammar(null));
+ case ADD: return new Grammar.Repetition(g, 1, Integer.MAX_VALUE);
+ case MUL: return new Grammar.Repetition(g, 0, Integer.MAX_VALUE);
+ case HOOK: return new Grammar.Repetition(g, 0, 1);
+ }
+ Grammar g0 = null;
+ switch(tok) {
+ //case NUMBER: g0 = new Grammar.Literal(number); break;
+ case NAME: g0 = new Grammar.Reference(string); break;
+ case STRING:
+ g0 = new Grammar.Literal(string);
+ if (peekToken() == DOT) {
+ String old = string;
+ consume(DOT);
+ consume(DOT);
+ consume(STRING);
+ if (old.length() != 1 || string.length() != 1) throw pe("literal ranges must be single-char strings");
+ g0 = new Grammar.Range(old.charAt(0), string.charAt(0));
+ }
+ break;
+ case LP: g0 = parseGrammar(null); consume(RP); break;
+ default: pushBackToken(); return g;
+ }
+ if (g == null) return parseGrammar(g0);
+ return new Grammar.Juxtaposition(g, g0);
+ }
/**
* Assuming that a complete assignable (lvalue) has just been
// force the default case
tok = -1;
switch(tok) {
+
+ case GRAMMAR: {
+ b.add(parserLine, GET_PRESERVE);
+ Grammar g = parseGrammar(null);
+ if (peekToken() == LC) {
+ g.action = new JSFunction(sourceName, parserLine, null);
+ parseBlock(g.action);
+ g.action.add(parserLine, LITERAL, null); // in case we "fall out the bottom", return NULL
+ g.action.add(parserLine, RETURN);
+ }
+ b.add(parserLine, MAKE_GRAMMAR, g);
+ b.add(parserLine, PUT);
+ break;
+ }
+
case ASSIGN_BITOR: case ASSIGN_BITXOR: case ASSIGN_BITAND: case ASSIGN_LSH: case ASSIGN_RSH: case ASSIGN_URSH:
case ASSIGN_MUL: case ASSIGN_DIV: case ASSIGN_MOD: case ASSIGN_ADD: case ASSIGN_SUB: {
if (tok != ASSIGN_ADD && tok != ASSIGN_SUB) b.add(parserLine, GET_PRESERVE);
public static final int CATCH = 75; // catch keyword
public static final int FINALLY = 76; // finally keyword
public static final int RESERVED = 77; // reserved keyword
+ public static final int GRAMMAR = 78; // the grammar-definition operator (::=)
- public static final int MAX_TOKEN = RESERVED;
-
+ public static final int MAX_TOKEN = GRAMMAR;
+
public final static String[] codeToString = new String[] {
"BITOR", "ASSIGN_BITOR", "BITXOR", "ASSIGN_BITXOR", "BITAND",
"ASSIGN_BITAND", "LSH", "ASSIGN_LSH", "RSH", "ASSIGN_RSH",
"SEMI", "LB", "RB", "LC", "RC", "LP", "RP", "COMMA", "ASSIGN",
"HOOK", "COLON", "INC", "DEC", "DOT", "FUNCTION", "IF",
"ELSE", "SWITCH", "CASE", "DEFAULT", "WHILE", "DO", "FOR",
- "VAR", "WITH", "CATCH", "FINALLY", "RESERVED"
+ "VAR", "WITH", "CATCH", "FINALLY", "RESERVED", "GRAMMAR"
};
}
--- /dev/null
+package org.xwt.util;
+
+import java.util.*;
+import java.io.*;
+import org.xwt.js.*;
+
+public abstract class Grammar extends JS {
+
+ public JSFunction action = null;
+ public Grammar() { }
+
+ // means we call()ed a Grammar that hasn't been bound to a scope yet
+ public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+ throw new Error("this should never happen");
+ }
+
+ public abstract int match(String s, int start, Hash v, JSScope scope) throws JSExn;
+ public int matchAndWrite(final String s, final int start, Hash v, JSScope scope, String key) throws JSExn {
+ final Hash v2 = new Hash();
+ final int ret = match(s, start, v2, scope);
+ if (ret == -1) return -1;
+ Object result = action == null ?
+ s.substring(start, ret) :
+ action.cloneWithNewParentScope(new JSScope(scope) {
+ public Object get(Object key) throws JSExn {
+ if (v2.get(key) != null) return v2.get(key);
+ if (key.equals("whole")) return s.substring(start, ret);
+ return super.get(key);
+ }
+ }).call(null, null, null, null, 0);
+ if (key != null) {
+ Object old = v.get(key);
+ if (old == null) { }
+ else if (old instanceof JSArray) { ((JSArray)old).addElement(result); result = old; }
+ else { JSArray j = new JSArray(); j.addElement(old); j.addElement(result); result = j; }
+ v.put(key, result);
+ }
+ return ret;
+ }
+
+ public static class Alternative extends Grammar {
+ private Grammar r1, r2;
+ public Alternative(Grammar r1, Grammar r2) { this.r1 = r1; this.r2 = r2; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ int s1 = r1.match(s, start, v, r);
+ if (s1 != -1) return s1;
+ int s2 = r2.match(s, start, v, r);
+ if (s2 != -1) return s2;
+ return -1;
+ }
+ }
+
+ public static class Juxtaposition extends Grammar {
+ private Grammar r1, r2;
+ public Juxtaposition(Grammar r1, Grammar r2) { this.r1 = r1; this.r2 = r2; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ int s1 = r1.match(s, start, v, r);
+ if (s1 == -1) return -1;
+ int s2 = r2.match(s, s1, v, r);
+ if (s2 == -1) return -1;
+ return s2;
+ }
+ }
+
+ public static class Repetition extends Grammar {
+ private Grammar r1;
+ private int min, max;
+ public Repetition(Grammar r1, int min, int max) { this.r1 = r1; this.min = min; this.max = max; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ int i;
+ for(i=0; i<max; i++) {
+ start = r1.match(s, start, v, r);
+ if (start == -1) return -1;
+ }
+ if (i < min) return -1;
+ return start;
+ }
+ }
+
+ public static class Literal extends Grammar {
+ String str;
+ public Literal(String str) { this.str = str; }
+ public int match(String s, int start, Hash v, JSScope r) {
+ if (!s.regionMatches(start, str, 0, str.length())) return -1;
+ return start + str.length();
+ }
+ }
+
+ public static class Range extends Grammar {
+ char min, max;
+ public Range(char min, char max) { this.min = min; this.max = max; }
+ public int match(String s, int start, Hash v, JSScope r) throws JSExn {
+ if (!(s.charAt(start) >= min && s.charAt(start) <= max)) return -1;
+ return start + 1;
+ }
+ }
+
+ public static class Reference extends Grammar {
+ String key;
+ public Reference(String key) { this.key = key; }
+ public int match(String s, int start, Hash v, JSScope scope) throws JSExn {
+ return ((Grammar)scope.get(key)).matchAndWrite(s, start, v, scope, key);
+ }
+ }
+}