$ template make_parser() $ $ // generate the parser/ParserException.java $ output $basedir + '/parser/ParserException.java' /* This file was generated by SableCC (http://www.sablecc.org/). */ package @package.parser; import @package.node.*; public class ParserException extends Exception { Token token; public ParserException(Token token, String message) { super(message); this.token = token; } public Token getToken() { return token; } } $ end output $ // generate the parser/State.java $ output $basedir + '/parser/State.java' /* This file was generated by SableCC (http://www.sablecc.org/). */ package @package.parser; import java.util.ArrayList; final class State { int state; ArrayList nodes; State(int state, ArrayList nodes) { this.state = state; this.nodes = nodes; } } $ end output $ // generate the parser/TokenIndex.java $ output $basedir + '/parser/TokenIndex.java' /* This file was generated by SableCC (http://www.sablecc.org/). */ package @package.parser; import @package.node.*; import @package.analysis.*; class TokenIndex extends AnalysisAdapter { int index; $ foreach {//token[@parser_index]} public void case@ename(@ename node) { index = @parser_index; } $ end public void caseEOF(EOF node) { index = ${tokens/eof/@parser_index}; } } $ end output $ // generate the parser/Parser.java $ output $basedir + '/parser/Parser.java' /* This file was generated by SableCC (http://www.sablecc.org/). */ package @package.parser; import @package.lexer.*; import @package.node.*; import @package.analysis.*; import java.util.*; import java.io.*; public class Parser { public final Analysis ignoredTokens = new AnalysisAdapter(); protected ArrayList nodeList; private final Lexer lexer; private final ListIterator stack = new LinkedList().listIterator(); private int last_shift; private int last_pos; private int last_line; private Token last_token; private final TokenIndex converter = new TokenIndex(); private final int[] action = new int[2]; private final static int SHIFT = 0; private final static int REDUCE = 1; private final static int ACCEPT = 2; private final static int ERROR = 3; public Parser(Lexer lexer) { this.lexer = lexer; if(actionTable == null) { try { BufferedReader r = new BufferedReader (new InputStreamReader ( Parser.class.getResourceAsStream("parser.dat"))); // read actionTable int length = Integer.parseInt(r.readLine()); actionTable = new int[length][][]; for(int i = 0; i < actionTable.length; i++) { length = Integer.parseInt(r.readLine()); actionTable[i] = new int[length][3]; for(int j = 0; j < actionTable[i].length; j++) { for(int k = 0; k < 3; k++) { actionTable[i][j][k] = Integer.parseInt(r.readLine()); } } } // read gotoTable length = Integer.parseInt(r.readLine()); gotoTable = new int[length][][]; for(int i = 0; i < gotoTable.length; i++) { length = Integer.parseInt(r.readLine()); gotoTable[i] = new int[length][2]; for(int j = 0; j < gotoTable[i].length; j++) { for(int k = 0; k < 2; k++) { gotoTable[i][j][k] = Integer.parseInt(r.readLine()); } } } // read errorMessages length = Integer.parseInt(r.readLine()); errorMessages = new String[length]; for(int i = 0; i < errorMessages.length; i++) { errorMessages[i] = r.readLine(); } // read errors length = Integer.parseInt(r.readLine()); errors = new int[length]; for(int i = 0; i < errors.length; i++) { errors[i] = Integer.parseInt(r.readLine()); } r.close(); } catch(Exception e) { throw new RuntimeException("The file \"parser.dat\" is either missing or corrupted."); } } } private int goTo(int index) { int state = state(); int low = 1; int high = gotoTable[index].length - 1; int value = gotoTable[index][0][1]; while(low <= high) { int middle = (low + high) / 2; if(state < gotoTable[index][middle][0]) { high = middle - 1; } else if(state > gotoTable[index][middle][0]) { low = middle + 1; } else { value = gotoTable[index][middle][1]; break; } } return value; } private void push(int numstate, ArrayList listNode) throws ParserException, LexerException, IOException { this.nodeList = listNode; if(!stack.hasNext()) { stack.add(new State(numstate, this.nodeList)); return; } State s = (State) stack.next(); s.state = numstate; s.nodes = this.nodeList; } private int state() { State s = (State) stack.previous(); stack.next(); return s.state; } private ArrayList pop() { return (ArrayList) ((State) stack.previous()).nodes; } private int index(Switchable token) { converter.index = -1; token.apply(converter); return converter.index; } public Start parse() throws ParserException, LexerException, IOException { push(0, null); List ign = null; while(true) { while(index(lexer.peek()) == -1) { if(ign == null) { ign = new TypedList(NodeCast.instance); } ign.add(lexer.next()); } if(ign != null) { ignoredTokens.setIn(lexer.peek(), ign); ign = null; } last_pos = lexer.peek().getPos(); last_line = lexer.peek().getLine(); last_token = lexer.peek(); int index = index(lexer.peek()); action[0] = actionTable[state()][0][1]; action[1] = actionTable[state()][0][2]; int low = 1; int high = actionTable[state()].length - 1; while(low <= high) { int middle = (low + high) / 2; if(index < actionTable[state()][middle][0]) { high = middle - 1; } else if(index > actionTable[state()][middle][0]) { low = middle + 1; } else { action[0] = actionTable[state()][middle][1]; action[1] = actionTable[state()][middle][2]; break; } } switch(action[0]) { case SHIFT: { ArrayList list = new ArrayList(); list.add(lexer.next()); push(action[1], list); last_shift = action[1]; } break; case REDUCE: switch(action[1]) { $ foreach {rules/rule} case @index: { ArrayList list = new@index(); push(goTo(@leftside), list); } break; $ end foreach } break; case ACCEPT: { EOF node2 = (EOF) lexer.next(); ${/parser/productions/production/@ename} node1 = (${/parser/productions/production/@ename}) ((ArrayList)pop()).get(0); Start node = new Start(node1, node2); return node; } case ERROR: throw new ParserException(last_token, "[" + last_line + "," + last_pos + "] " + errorMessages[errors[action[1]]]); } } } $ foreach {rules/rule} ArrayList new@index() { ArrayList nodeList = new ArrayList(); $ foreach {action} $ choose $ when {@cmd='POP'} ArrayList @result = (ArrayList) pop(); $ end $ when {@cmd='FETCHLIST'} TypedList @result = (TypedList)@from.get(@index); $ end $ when {@cmd='FETCHNODE'} @etype @result = (@etype)@from.get(@index); $ end $ when {@cmd='ADDNODE'} if ( @node != null ) { @tolist.add(@node); } $ end $ when {@cmd='ADDLIST'} if ( @fromlist != null ) { @tolist.addAll(@fromlist); } $ end $ when {@cmd='MAKELIST'} TypedList @result = new TypedList(); $ end $ when {@cmd='MAKENODE'} @etype @result = new @etype ( $ foreach {arg} $ if @null null[-sep ','-] $ else ${.}[-sep ','-] $ end $ end foreach ); $ end $ when {@cmd='RETURNNODE'} $ if @null nodeList.add(null); $ else nodeList.add(@node); $ end $ end $ when {@cmd='RETURNLIST'} nodeList.add(@list); $ end $ end choose $ end foreach return nodeList; } $ end foreach private static int[][][] actionTable; /* = { $ foreach {parser_data/action_table/row} { $ foreach {action} {@from, @action, @to}, $ end foreach }, $ end foreach }; */ private static int[][][] gotoTable; /* = { $ foreach {parser_data/goto_table/row} { $ foreach {goto} {@from, @to}, $ end foreach }, $ end foreach }; */ private static String[] errorMessages; /* = { $ foreach {parser_data/error_messages/msg} "${sablecc:escape_string_literal(string(.))}", $ end }; */ private static int[] errors; /* = { [-foreach {parser_data/errors/i}-]${.}, [-end-] }; */ } $ end output $ // writing our own format of parser.dat $ output $basedir + '/parser/parser.dat' $ // number of actionTable rows ${count(parser_data/action_table/row)} $ foreach {parser_data/action_table/row} $ // number of actions ${count(action)} $ foreach {action} @from @action @to $ end $ end foreach $ // number of gotoTable rows ${count(parser_data/goto_table/row)} $ foreach {parser_data/goto_table/row} $ // number of goto_table ${count(goto)} $ foreach {goto} @from @to $ end $ end foreach $ // number of error messages ${count(parser_data/error_messages/msg)} $ foreach {parser_data/error_messages/msg} ${sablecc:escape_string_literal(string(.))} $ end foreach $ // number of errors ${count(parser_data/errors/i)} $ foreach {parser_data/errors/i} ${.} $ end foreach $ end output $ end template