2004/01/08 05:02:19
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:44:07 +0000 (07:44 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:44:07 +0000 (07:44 +0000)
darcs-hash:20040130074407-2ba56-d3d9b7423f033c8daa6329ed6265d79dc3cbb6e6.gz

src/org/xwt/js/ByteCodes.java
src/org/xwt/js/Interpreter.java
src/org/xwt/js/JSFunction.java
src/org/xwt/js/Lexer.java
src/org/xwt/js/Parser.java
src/org/xwt/js/Tokens.java
src/org/xwt/util/Grammar.java [new file with mode: 0644]

index e270885..4a603a7 100644 (file)
@@ -82,10 +82,13 @@ interface ByteCodes {
     /** 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"
     };
 }
index ae8dc1f..32bf03e 100644 (file)
@@ -356,6 +356,28 @@ class Interpreter implements ByteCodes, Tokens {
             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();
index 2f24e9a..d200909 100644 (file)
@@ -72,6 +72,7 @@ public class JSFunction extends JS implements ByteCodes, Tokens, org.xwt.Schedul
         return cx.resume();
     }
 
+    public JSScope getParentScope() { return parentScope; }
 
     // Adding and Altering Bytecodes ///////////////////////////////////////////////////
 
index 6e541c1..b8ce343 100644 (file)
@@ -265,7 +265,7 @@ class Lexer implements Tokens {
         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;
@@ -295,6 +295,7 @@ class Lexer implements Tokens {
         }
     }
 
+    private int le(String s) throws LexerException { if (true) throw new LexerException(s); return 0; }
 
     // SmartReader ////////////////////////////////////////////////////////////////
 
index 4bf273d..66084e0 100644 (file)
@@ -314,6 +314,36 @@ class Parser extends Lexer implements ByteCodes {
         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
@@ -334,6 +364,21 @@ class Parser extends Lexer implements ByteCodes {
             // 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);
index fc70577..d61b17e 100644 (file)
@@ -93,9 +93,10 @@ interface Tokens {
     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",
@@ -108,7 +109,7 @@ interface Tokens {
         "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"
     };
 
 }
diff --git a/src/org/xwt/util/Grammar.java b/src/org/xwt/util/Grammar.java
new file mode 100644 (file)
index 0000000..678a63a
--- /dev/null
@@ -0,0 +1,105 @@
+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);
+        }
+    }
+}