package org.simantics.scl.compiler.parser.grammar.input; import java.io.DataInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; public abstract class GrammarParser { public static final boolean TRACE = false; private static final int INITIAL_CAPACITY = 16; private static final int STATE_COUNT = 19; private static final int TERMINAL_COUNT = 16; private static final int NONTERMINAL_COUNT = 6; private static final int PRODUCT_COUNT = 8; private static final int[] ACTION_ROW_ID = new int[STATE_COUNT]; private static final int[] ACTION_COLUMN_ID = new int[TERMINAL_COUNT]; private static final short[] ACTION_TABLE = new short[56]; private static final int[] ERROR_TABLE = new int[10]; private static final int[] GOTO_ROW_ID = new int[STATE_COUNT]; private static final int[] GOTO_COLUMN_ID = new int[NONTERMINAL_COUNT]; private static final short[] GOTO_TABLE = new short[12]; private static final int[] PRODUCT_LHS = new int[PRODUCT_COUNT]; private static final short STATE_MASK = (short)0x0fff; private static final short REDUCE_MASK = (short)0x8000; private static final short POP_MASK = (short)0x4000; private static final short PUSH_MASK = (short)0x2000; private static final short ERROR_ACTION = (short)0xffff; private static final short ACCEPT_ACTION = (short)0xfffe; public static final String[] TERMINAL_NAMES = new String[] { "NONTERMINAL", "EQUALS", "BAR", "SEMICOLON", "INITIAL", "HASH", "TERMINAL", "COMMA", "SHIFT", "REDUCE", "STAR", "PLUS", "OPTIONAL", "LPAREN", "RPAREN", "EOF" }; public static final String[] NONTERMINAL_NAMES = new String[] { "file", "declaration", "prod", "regexps", "regexp", "init$1" }; static { try { DataInputStream input = new DataInputStream(GrammarParser.class.getResourceAsStream("GrammarParser.dat")); for(int i=0;i>5] >> (id&31))&1) != 0 ) return ERROR_ACTION; return ACTION_TABLE[ACTION_ROW_ID[state] + ACTION_COLUMN_ID[symbol]]; } private static short getGoto(int state, int symbol) { return GOTO_TABLE[GOTO_ROW_ID[state] + GOTO_COLUMN_ID[symbol]]; } protected abstract Token nextToken(); private Object[] symbolStack = new Object[INITIAL_CAPACITY]; private int symbolStackLength = 0; private int[] stateStack = new int[INITIAL_CAPACITY]; private int[] symbolStackPositionStack = new int[INITIAL_CAPACITY]; private int stateStackLength = 0; // For reduce private int reductionLength; protected int length() { return reductionLength; } protected Object get(int i) { if(i < 0 || i >= reductionLength) throw new IndexOutOfBoundsException(); return symbolStack[symbolStackLength+i]; } private String parseErrorDescription(int state, Token token, int tokenId) { StringBuilder b = new StringBuilder(); b.append("Unexpected token '").append(token) .append("' (").append(TERMINAL_NAMES[tokenId]) .append("). Expected one of "); ArrayList possibleTerminals = new ArrayList(); for(int i=0;i 0) b.append(", "); b.append(possibleTerminals.get(i)); } b.append('.'); return b.toString(); } protected abstract RuntimeException syntaxError(Token token, String description); private static String describeAction(boolean isGoto, int action) { if(action == ERROR_ACTION) return "ERROR"; if(action == ACCEPT_ACTION) return "ACCEPT"; StringBuilder b = new StringBuilder(); if(isGoto) b.append("GOTO "); else { if((action & REDUCE_MASK) != 0) { action ^= REDUCE_MASK; b.append("REDUCE"); } else b.append("SHIFT"); } if((action & POP_MASK) != 0) { action ^= POP_MASK; b.append(" POP"); } if((action & PUSH_MASK) != 0) { action ^= PUSH_MASK; b.append(" PUSH"); } b.append(' ').append(action); return b.toString(); } private void printState(int state) { System.out.print("state=" + state + ":"); for(int i=symbolStackLength-1,j=stateStackLength-1;i>=0;--i) { Object s = symbolStack[i]; if(s instanceof Token) System.out.print(" " + TERMINAL_NAMES[((Token)s).id]); else if(s == null) System.out.print(" null"); else System.out.print(" " + s.getClass().getSimpleName()); while(j>=0 && symbolStackPositionStack[j]==i) System.out.print(" (" + stateStack[j--] + ")"); } System.out.println(); } private Object parse(int state) { while(true) { Token token = nextToken(); int tokenId = token.id; if(TRACE) System.out.println("---> token " + TERMINAL_NAMES[tokenId] + " \"" + token.text + "\" <---"); while(true) { if(TRACE) printState(state); short action = getAction(state, tokenId); if(TRACE) System.out.println(" -> action=" + describeAction(false, action)); //System.out.println(STATE_DESCRIPTIONS[state]); if((action & REDUCE_MASK) != 0) { if(action == ACCEPT_ACTION) return symbolStack[symbolStackLength-1]; if(action == ERROR_ACTION) throw syntaxError(token, parseErrorDescription(state, token, tokenId)); int popAmount = (action >>> 13)&3; if(TRACE) { if(popAmount > 0) System.out.println(" POP " + popAmount); } stateStackLength -= popAmount; action &= STATE_MASK; int reductionBegin = symbolStackPositionStack[--stateStackLength]; reductionLength = symbolStackLength-reductionBegin; symbolStackLength = reductionBegin; if(symbolStackLength == symbolStack.length) symbolStack = Arrays.copyOf(symbolStack, symbolStackLength*2); Object symbol = reduce(action); postReduce(symbol); symbolStack[symbolStackLength] = symbol; state = stateStack[stateStackLength]; if(TRACE) { ++symbolStackLength; printState(state); --symbolStackLength; System.out.println(" nonterminal=" + NONTERMINAL_NAMES[PRODUCT_LHS[action]]); } action = getGoto(state, PRODUCT_LHS[action]); if(TRACE) System.out.println(" -> action=" + describeAction(true, action)); // Pop state if((action & POP_MASK) != 0) { --stateStackLength; } // Push state if((action & PUSH_MASK) != 0) { if(stateStackLength == stateStack.length) { stateStack = Arrays.copyOf(stateStack, stateStackLength*2); symbolStackPositionStack = Arrays.copyOf(symbolStackPositionStack, stateStackLength*2); } symbolStackPositionStack[stateStackLength] = symbolStackLength; stateStack[stateStackLength++] = state; } state = action & STATE_MASK; ++symbolStackLength; } else { // Pop state if((action & POP_MASK) != 0) { --stateStackLength; } // Push state if((action & PUSH_MASK) != 0) { if(stateStackLength == stateStack.length) { stateStack = Arrays.copyOf(stateStack, stateStackLength*2); symbolStackPositionStack = Arrays.copyOf(symbolStackPositionStack, stateStackLength*2); } symbolStackPositionStack[stateStackLength] = symbolStackLength; stateStack[stateStackLength++] = state; } // New state state = action & STATE_MASK; // Push symbol if(symbolStackLength == symbolStack.length) symbolStack = Arrays.copyOf(symbolStack, symbolStackLength*2); symbolStack[symbolStackLength++] = token; break; } } } } public Object parseFile() { return parse(0); } protected Object reduce(int productionId) { try { switch(productionId) { case 0: return reduceFile(); case 1: return reduceProduction(); case 2: return reduceInitial(); case 3: return reduceProductionRhs(); case 4: return reduceConcatenation(); case 5: return reduceTerminal(); case 6: return reduceUnion(); default: throw new RuntimeException("Internal parser error."); } } catch(RuntimeException e) { StringBuilder b = new StringBuilder(); b.append("Failed to reduce"); for(int i=0;i