/* * Copyright (c) 2003, 2005, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.java_cup.internal.runtime; import java.util.Stack; /** This class implements a skeleton table driven LR parser. In general, * LR parsers are a form of bottom up shift-reduce parsers. Shift-reduce * parsers act by shifting input onto a parse stack until the Symbols * matching the right hand side of a production appear on the top of the * stack. Once this occurs, a reduce is performed. This involves removing * the Symbols corresponding to the right hand side of the production * (the so called "handle") and replacing them with the non-terminal from * the left hand side of the production. <p> * * To control the decision of whether to shift or reduce at any given point, * the parser uses a state machine (the "viable prefix recognition machine" * built by the parser generator). The current state of the machine is placed * on top of the parse stack (stored as part of a Symbol object representing * a terminal or non terminal). The parse action table is consulted * (using the current state and the current lookahead Symbol as indexes) to * determine whether to shift or to reduce. When the parser shifts, it * changes to a new state by pushing a new Symbol (containing a new state) * onto the stack. When the parser reduces, it pops the handle (right hand * side of a production) off the stack. This leaves the parser in the state * it was in before any of those Symbols were matched. Next the reduce-goto * table is consulted (using the new state and current lookahead Symbol as * indexes) to determine a new state to go to. The parser then shifts to * this goto state by pushing the left hand side Symbol of the production * (also containing the new state) onto the stack.<p> * * This class actually provides four LR parsers. The methods parse() and * debug_parse() provide two versions of the main parser (the only difference * being that debug_parse() emits debugging trace messages as it parses). * In addition to these main parsers, the error recovery mechanism uses two * more. One of these is used to simulate "parsing ahead" in the input * without carrying out actions (to verify that a potential error recovery * has worked), and the other is used to parse through buffered "parse ahead" * input in order to execute all actions and re-synchronize the actual parser * configuration.<p> * * This is an abstract class which is normally filled out by a subclass * generated by the JavaCup parser generator. In addition to supplying * the actual parse tables, generated code also supplies methods which * invoke various pieces of user supplied code, provide access to certain * special Symbols (e.g., EOF and error), etc. Specifically, the following * abstract methods are normally supplied by generated code: * <dl compact> * <dt> short[][] production_table() * <dd> Provides a reference to the production table (indicating the index of * the left hand side non terminal and the length of the right hand side * for each production in the grammar). * <dt> short[][] action_table() * <dd> Provides a reference to the parse action table. * <dt> short[][] reduce_table() * <dd> Provides a reference to the reduce-goto table. * <dt> int start_state() * <dd> Indicates the index of the start state. * <dt> int start_production() * <dd> Indicates the index of the starting production. * <dt> int EOF_sym() * <dd> Indicates the index of the EOF Symbol. * <dt> int error_sym() * <dd> Indicates the index of the error Symbol. * <dt> Symbol do_action() * <dd> Executes a piece of user supplied action code. This always comes at * the point of a reduce in the parse, so this code also allocates and * fills in the left hand side non terminal Symbol object that is to be * pushed onto the stack for the reduce. * <dt> void init_actions() * <dd> Code to initialize a special object that encapsulates user supplied * actions (this object is used by do_action() to actually carry out the * actions). * </dl> * * In addition to these routines that <i>must</i> be supplied by the * generated subclass there are also a series of routines that <i>may</i> * be supplied. These include: * <dl> * <dt> Symbol scan() * <dd> Used to get the next input Symbol from the scanner. * <dt> Scanner getScanner() * <dd> Used to provide a scanner for the default implementation of * scan(). * <dt> int error_sync_size() * <dd> This determines how many Symbols past the point of an error * must be parsed without error in order to consider a recovery to * be valid. This defaults to 3. Values less than 2 are not * recommended. * <dt> void report_error(String message, Object info) * <dd> This method is called to report an error. The default implementation * simply prints a message to System.err and where the error occurred. * This method is often replaced in order to provide a more sophisticated * error reporting mechanism. * <dt> void report_fatal_error(String message, Object info) * <dd> This method is called when a fatal error that cannot be recovered from * is encountered. In the default implementation, it calls * report_error() to emit a message, then throws an exception. * <dt> void syntax_error(Symbol cur_token) * <dd> This method is called as soon as syntax error is detected (but * before recovery is attempted). In the default implementation it * invokes: report_error("Syntax error", null); * <dt> void unrecovered_syntax_error(Symbol cur_token) * <dd> This method is called if syntax error recovery fails. In the default * implementation it invokes:<br> * report_fatal_error("Couldn't repair and continue parse", null); * </dl> * * @see com.sun.java_cup.internal.runtime.Symbol * @see com.sun.java_cup.internal.runtime.Symbol * @see com.sun.java_cup.internal.runtime.virtual_parse_stack * @author Frank Flannery */ public abstract class lr_parser { /*-----------------------------------------------------------*/ /*--- Constructor(s) ----------------------------------------*/ /*-----------------------------------------------------------*/ /** Simple constructor. */ public lr_parser() { /* nothing to do here */ } /** Constructor that sets the default scanner. [CSA/davidm] */ public lr_parser(Scanner s) { this(); /* in case default constructor someday does something */ setScanner(s); } /*-----------------------------------------------------------*/ /*--- (Access to) Static (Class) Variables ------------------*/ /*-----------------------------------------------------------*/ /** The default number of Symbols after an error we much match to consider * it recovered from. */ protected final static int _error_sync_size = 3; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The number of Symbols after an error we much match to consider it * recovered from. */ protected int error_sync_size() {return _error_sync_size; } /*-----------------------------------------------------------*/ /*--- (Access to) Instance Variables ------------------------*/ /*-----------------------------------------------------------*/ /** Table of production information (supplied by generated subclass). * This table contains one entry per production and is indexed by * the negative-encoded values (reduce actions) in the action_table. * Each entry has two parts, the index of the non-terminal on the * left hand side of the production, and the number of Symbols * on the right hand side. */ public abstract short[][] production_table(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The action table (supplied by generated subclass). This table is * indexed by state and terminal number indicating what action is to * be taken when the parser is in the given state (i.e., the given state * is on top of the stack) and the given terminal is next on the input. * States are indexed using the first dimension, however, the entries for * a given state are compacted and stored in adjacent index, value pairs * which are searched for rather than accessed directly (see get_action()). * The actions stored in the table will be either shifts, reduces, or * errors. Shifts are encoded as positive values (one greater than the * state shifted to). Reduces are encoded as negative values (one less * than the production reduced by). Error entries are denoted by zero. * * @see com.sun.java_cup.internal.runtime.lr_parser#get_action */ public abstract short[][] action_table(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The reduce-goto table (supplied by generated subclass). This * table is indexed by state and non-terminal number and contains * state numbers. States are indexed using the first dimension, however, * the entries for a given state are compacted and stored in adjacent * index, value pairs which are searched for rather than accessed * directly (see get_reduce()). When a reduce occurs, the handle * (corresponding to the RHS of the matched production) is popped off * the stack. The new top of stack indicates a state. This table is * then indexed by that state and the LHS of the reducing production to * indicate where to "shift" to. * * @see com.sun.java_cup.internal.runtime.lr_parser#get_reduce */ public abstract short[][] reduce_table(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The index of the start state (supplied by generated subclass). */ public abstract int start_state(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The index of the start production (supplied by generated subclass). */ public abstract int start_production(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The index of the end of file terminal Symbol (supplied by generated * subclass). */ public abstract int EOF_sym(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The index of the special error Symbol (supplied by generated subclass). */ public abstract int error_sym(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Internal flag to indicate when parser should quit. */ protected boolean _done_parsing = false; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** This method is called to indicate that the parser should quit. This is * normally called by an accept action, but can be used to cancel parsing * early in other circumstances if desired. */ public void done_parsing() { _done_parsing = true; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /* Global parse state shared by parse(), error recovery, and * debugging routines */ /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Indication of the index for top of stack (for use by actions). */ protected int tos; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The current lookahead Symbol. */ protected Symbol cur_token; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** The parse stack itself. */ protected Stack stack = new Stack(); /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Direct reference to the production table. */ protected short[][] production_tab; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Direct reference to the action table. */ protected short[][] action_tab; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Direct reference to the reduce-goto table. */ protected short[][] reduce_tab; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** This is the scanner object used by the default implementation * of scan() to get Symbols. To avoid name conflicts with existing * code, this field is private. [CSA/davidm] */ private Scanner _scanner; /** * Simple accessor method to set the default scanner. */ public void setScanner(Scanner s) { _scanner = s; } /** * Simple accessor method to get the default scanner. */ public Scanner getScanner() { return _scanner; } /*-----------------------------------------------------------*/ /*--- General Methods ---------------------------------------*/ /*-----------------------------------------------------------*/ /** Perform a bit of user supplied action code (supplied by generated * subclass). Actions are indexed by an internal action number assigned * at parser generation time. * * @param act_num the internal index of the action to be performed. * @param parser the parser object we are acting for. * @param stack the parse stack of that object. * @param top the index of the top element of the parse stack. */ public abstract Symbol do_action( int act_num, lr_parser parser, Stack stack, int top) throws java.lang.Exception; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** User code for initialization inside the parser. Typically this * initializes the scanner. This is called before the parser requests * the first Symbol. Here this is just a placeholder for subclasses that * might need this and we perform no action. This method is normally * overridden by the generated code using this contents of the "init with" * clause as its body. */ public void user_init() throws java.lang.Exception { } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Initialize the action object. This is called before the parser does * any parse actions. This is filled in by generated code to create * an object that encapsulates all action code. */ protected abstract void init_actions() throws java.lang.Exception; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Get the next Symbol from the input (supplied by generated subclass). * Once end of file has been reached, all subsequent calls to scan * should return an EOF Symbol (which is Symbol number 0). By default * this method returns getScanner().next_token(); this implementation * can be overriden by the generated parser using the code declared in * the "scan with" clause. Do not recycle objects; every call to * scan() should return a fresh object. */ public Symbol scan() throws java.lang.Exception { return getScanner().next_token(); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Report a fatal error. This method takes a message string and an * additional object (to be used by specializations implemented in * subclasses). Here in the base class a very simple implementation * is provided which reports the error then throws an exception. * * @param message an error message. * @param info an extra object reserved for use by specialized subclasses. */ public void report_fatal_error( String message, Object info) throws java.lang.Exception { /* stop parsing (not really necessary since we throw an exception, but) */ done_parsing(); /* use the normal error message reporting to put out the message */ report_error(message, info); /* throw an exception */ throw new Exception("Can't recover from previous error(s)"); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Report a non fatal error (or warning). This method takes a message * string and an additional object (to be used by specializations * implemented in subclasses). Here in the base class a very simple * implementation is provided which simply prints the message to * System.err. * * @param message an error message. * @param info an extra object reserved for use by specialized subclasses. */ public void report_error(String message, Object info) { System.err.print(message); if (info instanceof Symbol) if (((Symbol)info).left != -1) System.err.println(" at character " + ((Symbol)info).left + " of input"); else System.err.println(""); else System.err.println(""); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** This method is called when a syntax error has been detected and recovery * is about to be invoked. Here in the base class we just emit a * "Syntax error" error message. * * @param cur_token the current lookahead Symbol. */ public void syntax_error(Symbol cur_token) { report_error("Syntax error", cur_token); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** This method is called if it is determined that syntax error recovery * has been unsuccessful. Here in the base class we report a fatal error. * * @param cur_token the current lookahead Symbol. */ public void unrecovered_syntax_error(Symbol cur_token) throws java.lang.Exception { report_fatal_error("Couldn't repair and continue parse", cur_token); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Fetch an action from the action table. The table is broken up into * rows, one per state (rows are indexed directly by state number). * Within each row, a list of index, value pairs are given (as sequential * entries in the table), and the list is terminated by a default entry * (denoted with a Symbol index of -1). To find the proper entry in a row * we do a linear or binary search (depending on the size of the row). * * @param state the state index of the action being accessed. * @param sym the Symbol index of the action being accessed. */ protected final short get_action(int state, int sym) { short tag; int first, last, probe; short[] row = action_tab[state]; /* linear search if we are < 10 entries */ if (row.length < 20) for (probe = 0; probe < row.length; probe++) { /* is this entry labeled with our Symbol or the default? */ tag = row[probe++]; if (tag == sym || tag == -1) { /* return the next entry */ return row[probe]; } } /* otherwise binary search */ else { first = 0; last = (row.length-1)/2 - 1; /* leave out trailing default entry */ while (first <= last) { probe = (first+last)/2; if (sym == row[probe*2]) return row[probe*2+1]; else if (sym > row[probe*2]) first = probe+1; else last = probe-1; } /* not found, use the default at the end */ return row[row.length-1]; } /* shouldn't happened, but if we run off the end we return the default (error == 0) */ return 0; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Fetch a state from the reduce-goto table. The table is broken up into * rows, one per state (rows are indexed directly by state number). * Within each row, a list of index, value pairs are given (as sequential * entries in the table), and the list is terminated by a default entry * (denoted with a Symbol index of -1). To find the proper entry in a row * we do a linear search. * * @param state the state index of the entry being accessed. * @param sym the Symbol index of the entry being accessed. */ protected final short get_reduce(int state, int sym) { short tag; short[] row = reduce_tab[state]; /* if we have a null row we go with the default */ if (row == null) return -1; for (int probe = 0; probe < row.length; probe++) { /* is this entry labeled with our Symbol or the default? */ tag = row[probe++]; if (tag == sym || tag == -1) { /* return the next entry */ return row[probe]; } } /* if we run off the end we return the default (error == -1) */ return -1; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** This method provides the main parsing routine. It returns only when * done_parsing() has been called (typically because the parser has * accepted, or a fatal error has been reported). See the header * documentation for the class regarding how shift/reduce parsers operate * and how the various tables are used. */ public Symbol parse() throws java.lang.Exception { /* the current action code */ int act; /* the Symbol/stack element returned by a reduce */ Symbol lhs_sym = null; /* information about production being reduced with */ short handle_size, lhs_sym_num; /* set up direct reference to tables to drive the parser */ production_tab = production_table(); action_tab = action_table(); reduce_tab = reduce_table(); /* initialize the action encapsulation object */ init_actions(); /* do user initialization */ user_init(); /* get the first token */ cur_token = scan(); /* push dummy Symbol with start state to get us underway */ stack.removeAllElements(); stack.push(new Symbol(0, start_state())); tos = 0; /* continue until we are told to stop */ for (_done_parsing = false; !_done_parsing; ) { /* Check current token for freshness. */ if (cur_token.used_by_parser) throw new Error("Symbol recycling detected (fix your scanner)."); /* current state is always on the top of the stack */ /* look up action out of the current state with the current input */ act = get_action(((Symbol)stack.peek()).parse_state, cur_token.sym); /* decode the action -- > 0 encodes shift */ if (act > 0) { /* shift to the encoded state by pushing it on the stack */ cur_token.parse_state = act-1; cur_token.used_by_parser = true; stack.push(cur_token); tos++; /* advance to the next Symbol */ cur_token = scan(); } /* if its less than zero, then it encodes a reduce action */ else if (act < 0) { /* perform the action for the reduce */ lhs_sym = do_action((-act)-1, this, stack, tos); /* look up information about the production */ lhs_sym_num = production_tab[(-act)-1][0]; handle_size = production_tab[(-act)-1][1]; /* pop the handle off the stack */ for (int i = 0; i < handle_size; i++) { stack.pop(); tos--; } /* look up the state to go to from the one popped back to */ act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num); /* shift to that state */ lhs_sym.parse_state = act; lhs_sym.used_by_parser = true; stack.push(lhs_sym); tos++; } /* finally if the entry is zero, we have an error */ else if (act == 0) { /* call user syntax error reporting routine */ syntax_error(cur_token); /* try to error recover */ if (!error_recovery(false)) { /* if that fails give up with a fatal syntax error */ unrecovered_syntax_error(cur_token); /* just in case that wasn't fatal enough, end parse */ done_parsing(); } else { lhs_sym = (Symbol)stack.peek(); } } } return lhs_sym; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Write a debugging message to System.err for the debugging version * of the parser. * * @param mess the text of the debugging message. */ public void debug_message(String mess) { System.err.println(mess); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Dump the parse stack for debugging purposes. */ public void dump_stack() { if (stack == null) { debug_message("# Stack dump requested, but stack is null"); return; } debug_message("============ Parse Stack Dump ============"); /* dump the stack */ for (int i=0; i<stack.size(); i++) { debug_message("Symbol: " + ((Symbol)stack.elementAt(i)).sym + " State: " + ((Symbol)stack.elementAt(i)).parse_state); } debug_message("=========================================="); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Do debug output for a reduce. * * @param prod_num the production we are reducing with. * @param nt_num the index of the LHS non terminal. * @param rhs_size the size of the RHS. */ public void debug_reduce(int prod_num, int nt_num, int rhs_size) { debug_message("# Reduce with prod #" + prod_num + " [NT=" + nt_num + ", " + "SZ=" + rhs_size + "]"); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Do debug output for shift. * * @param shift_tkn the Symbol being shifted onto the stack. */ public void debug_shift(Symbol shift_tkn) { debug_message("# Shift under term #" + shift_tkn.sym + " to state #" + shift_tkn.parse_state); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Do debug output for stack state. [CSA] */ public void debug_stack() { StringBuffer sb=new StringBuffer("## STACK:"); for (int i=0; i<stack.size(); i++) { Symbol s = (Symbol) stack.elementAt(i); sb.append(" <state "+s.parse_state+", sym "+s.sym+">"); if ((i%3)==2 || (i==(stack.size()-1))) { debug_message(sb.toString()); sb = new StringBuffer(" "); } } } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Perform a parse with debugging output. This does exactly the * same things as parse(), except that it calls debug_shift() and * debug_reduce() when shift and reduce moves are taken by the parser * and produces various other debugging messages. */ public Symbol debug_parse() throws java.lang.Exception { /* the current action code */ int act; /* the Symbol/stack element returned by a reduce */ Symbol lhs_sym = null; /* information about production being reduced with */ short handle_size, lhs_sym_num; /* set up direct reference to tables to drive the parser */ production_tab = production_table(); action_tab = action_table(); reduce_tab = reduce_table(); debug_message("# Initializing parser"); /* initialize the action encapsulation object */ init_actions(); /* do user initialization */ user_init(); /* the current Symbol */ cur_token = scan(); debug_message("# Current Symbol is #" + cur_token.sym); /* push dummy Symbol with start state to get us underway */ stack.removeAllElements(); stack.push(new Symbol(0, start_state())); tos = 0; /* continue until we are told to stop */ for (_done_parsing = false; !_done_parsing; ) { /* Check current token for freshness. */ if (cur_token.used_by_parser) throw new Error("Symbol recycling detected (fix your scanner)."); /* current state is always on the top of the stack */ //debug_stack(); /* look up action out of the current state with the current input */ act = get_action(((Symbol)stack.peek()).parse_state, cur_token.sym); /* decode the action -- > 0 encodes shift */ if (act > 0) { /* shift to the encoded state by pushing it on the stack */ cur_token.parse_state = act-1; cur_token.used_by_parser = true; debug_shift(cur_token); stack.push(cur_token); tos++; /* advance to the next Symbol */ cur_token = scan(); debug_message("# Current token is " + cur_token); } /* if its less than zero, then it encodes a reduce action */ else if (act < 0) { /* perform the action for the reduce */ lhs_sym = do_action((-act)-1, this, stack, tos); /* look up information about the production */ lhs_sym_num = production_tab[(-act)-1][0]; handle_size = production_tab[(-act)-1][1]; debug_reduce((-act)-1, lhs_sym_num, handle_size); /* pop the handle off the stack */ for (int i = 0; i < handle_size; i++) { stack.pop(); tos--; } /* look up the state to go to from the one popped back to */ act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num); debug_message("# Reduce rule: top state " + ((Symbol)stack.peek()).parse_state + ", lhs sym " + lhs_sym_num + " -> state " + act); /* shift to that state */ lhs_sym.parse_state = act; lhs_sym.used_by_parser = true; stack.push(lhs_sym); tos++; debug_message("# Goto state #" + act); } /* finally if the entry is zero, we have an error */ else if (act == 0) { /* call user syntax error reporting routine */ syntax_error(cur_token); /* try to error recover */ if (!error_recovery(true)) { /* if that fails give up with a fatal syntax error */ unrecovered_syntax_error(cur_token); /* just in case that wasn't fatal enough, end parse */ done_parsing(); } else { lhs_sym = (Symbol)stack.peek(); } } } return lhs_sym; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /* Error recovery code */ /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Attempt to recover from a syntax error. This returns false if recovery * fails, true if it succeeds. Recovery happens in 4 steps. First we * pop the parse stack down to a point at which we have a shift out * of the top-most state on the error Symbol. This represents the * initial error recovery configuration. If no such configuration is * found, then we fail. Next a small number of "lookahead" or "parse * ahead" Symbols are read into a buffer. The size of this buffer is * determined by error_sync_size() and determines how many Symbols beyond * the error must be matched to consider the recovery a success. Next, * we begin to discard Symbols in attempt to get past the point of error * to a point where we can continue parsing. After each Symbol, we attempt * to "parse ahead" though the buffered lookahead Symbols. The "parse ahead" * process simulates that actual parse, but does not modify the real * parser's configuration, nor execute any actions. If we can parse all * the stored Symbols without error, then the recovery is considered a * success. Once a successful recovery point is determined, we do an * actual parse over the stored input -- modifying the real parse * configuration and executing all actions. Finally, we return the the * normal parser to continue with the overall parse. * * @param debug should we produce debugging messages as we parse. */ protected boolean error_recovery(boolean debug) throws java.lang.Exception { if (debug) debug_message("# Attempting error recovery"); /* first pop the stack back into a state that can shift on error and do that shift (if that fails, we fail) */ if (!find_recovery_config(debug)) { if (debug) debug_message("# Error recovery fails"); return false; } /* read ahead to create lookahead we can parse multiple times */ read_lookahead(); /* repeatedly try to parse forward until we make it the required dist */ for (;;) { /* try to parse forward, if it makes it, bail out of loop */ if (debug) debug_message("# Trying to parse ahead"); if (try_parse_ahead(debug)) { break; } /* if we are now at EOF, we have failed */ if (lookahead[0].sym == EOF_sym()) { if (debug) debug_message("# Error recovery fails at EOF"); return false; } /* otherwise, we consume another Symbol and try again */ if (debug) debug_message("# Consuming Symbol #" + cur_err_token().sym); restart_lookahead(); } /* we have consumed to a point where we can parse forward */ if (debug) debug_message("# Parse-ahead ok, going back to normal parse"); /* do the real parse (including actions) across the lookahead */ parse_lookahead(debug); /* we have success */ return true; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Determine if we can shift under the special error Symbol out of the * state currently on the top of the (real) parse stack. */ protected boolean shift_under_error() { /* is there a shift under error Symbol */ return get_action(((Symbol)stack.peek()).parse_state, error_sym()) > 0; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Put the (real) parse stack into error recovery configuration by * popping the stack down to a state that can shift on the special * error Symbol, then doing the shift. If no suitable state exists on * the stack we return false * * @param debug should we produce debugging messages as we parse. */ protected boolean find_recovery_config(boolean debug) { Symbol error_token; int act; if (debug) debug_message("# Finding recovery state on stack"); /* Remember the right-position of the top symbol on the stack */ int right_pos = ((Symbol)stack.peek()).right; int left_pos = ((Symbol)stack.peek()).left; /* pop down until we can shift under error Symbol */ while (!shift_under_error()) { /* pop the stack */ if (debug) debug_message("# Pop stack by one, state was # " + ((Symbol)stack.peek()).parse_state); left_pos = ((Symbol)stack.pop()).left; tos--; /* if we have hit bottom, we fail */ if (stack.empty()) { if (debug) debug_message("# No recovery state found on stack"); return false; } } /* state on top of the stack can shift under error, find the shift */ act = get_action(((Symbol)stack.peek()).parse_state, error_sym()); if (debug) { debug_message("# Recover state found (#" + ((Symbol)stack.peek()).parse_state + ")"); debug_message("# Shifting on error to state #" + (act-1)); } /* build and shift a special error Symbol */ error_token = new Symbol(error_sym(), left_pos, right_pos); error_token.parse_state = act-1; error_token.used_by_parser = true; stack.push(error_token); tos++; return true; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Lookahead Symbols used for attempting error recovery "parse aheads". */ protected Symbol lookahead[]; /** Position in lookahead input buffer used for "parse ahead". */ protected int lookahead_pos; /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Read from input to establish our buffer of "parse ahead" lookahead * Symbols. */ protected void read_lookahead() throws java.lang.Exception { /* create the lookahead array */ lookahead = new Symbol[error_sync_size()]; /* fill in the array */ for (int i = 0; i < error_sync_size(); i++) { lookahead[i] = cur_token; cur_token = scan(); } /* start at the beginning */ lookahead_pos = 0; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Return the current lookahead in our error "parse ahead" buffer. */ protected Symbol cur_err_token() { return lookahead[lookahead_pos]; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Advance to next "parse ahead" input Symbol. Return true if we have * input to advance to, false otherwise. */ protected boolean advance_lookahead() { /* advance the input location */ lookahead_pos++; /* return true if we didn't go off the end */ return lookahead_pos < error_sync_size(); } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Reset the parse ahead input to one Symbol past where we started error * recovery (this consumes one new Symbol from the real input). */ protected void restart_lookahead() throws java.lang.Exception { /* move all the existing input over */ for (int i = 1; i < error_sync_size(); i++) lookahead[i-1] = lookahead[i]; /* read a new Symbol into the last spot */ cur_token = scan(); lookahead[error_sync_size()-1] = cur_token; /* reset our internal position marker */ lookahead_pos = 0; } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Do a simulated parse forward (a "parse ahead") from the current * stack configuration using stored lookahead input and a virtual parse * stack. Return true if we make it all the way through the stored * lookahead input without error. This basically simulates the action of * parse() using only our saved "parse ahead" input, and not executing any * actions. * * @param debug should we produce debugging messages as we parse. */ protected boolean try_parse_ahead(boolean debug) throws java.lang.Exception { int act; short lhs, rhs_size; /* create a virtual stack from the real parse stack */ virtual_parse_stack vstack = new virtual_parse_stack(stack); /* parse until we fail or get past the lookahead input */ for (;;) { /* look up the action from the current state (on top of stack) */ act = get_action(vstack.top(), cur_err_token().sym); /* if its an error, we fail */ if (act == 0) return false; /* > 0 encodes a shift */ if (act > 0) { /* push the new state on the stack */ vstack.push(act-1); if (debug) debug_message("# Parse-ahead shifts Symbol #" + cur_err_token().sym + " into state #" + (act-1)); /* advance simulated input, if we run off the end, we are done */ if (!advance_lookahead()) return true; } /* < 0 encodes a reduce */ else { /* if this is a reduce with the start production we are done */ if ((-act)-1 == start_production()) { if (debug) debug_message("# Parse-ahead accepts"); return true; } /* get the lhs Symbol and the rhs size */ lhs = production_tab[(-act)-1][0]; rhs_size = production_tab[(-act)-1][1]; /* pop handle off the stack */ for (int i = 0; i < rhs_size; i++) vstack.pop(); if (debug) debug_message("# Parse-ahead reduces: handle size = " + rhs_size + " lhs = #" + lhs + " from state #" + vstack.top()); /* look up goto and push it onto the stack */ vstack.push(get_reduce(vstack.top(), lhs)); if (debug) debug_message("# Goto state #" + vstack.top()); } } } /*. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .*/ /** Parse forward using stored lookahead Symbols. In this case we have * already verified that parsing will make it through the stored lookahead * Symbols and we are now getting back to the point at which we can hand * control back to the normal parser. Consequently, this version of the * parser performs all actions and modifies the real parse configuration. * This returns once we have consumed all the stored input or we accept. * * @param debug should we produce debugging messages as we parse. */ protected void parse_lookahead(boolean debug) throws java.lang.Exception { /* the current action code */ int act; /* the Symbol/stack element returned by a reduce */ Symbol lhs_sym = null; /* information about production being reduced with */ short handle_size, lhs_sym_num; /* restart the saved input at the beginning */ lookahead_pos = 0; if (debug) { debug_message("# Reparsing saved input with actions"); debug_message("# Current Symbol is #" + cur_err_token().sym); debug_message("# Current state is #" + ((Symbol)stack.peek()).parse_state); } /* continue until we accept or have read all lookahead input */ while(!_done_parsing) { /* current state is always on the top of the stack */ /* look up action out of the current state with the current input */ act = get_action(((Symbol)stack.peek()).parse_state, cur_err_token().sym); /* decode the action -- > 0 encodes shift */ if (act > 0) { /* shift to the encoded state by pushing it on the stack */ cur_err_token().parse_state = act-1; cur_err_token().used_by_parser = true; if (debug) debug_shift(cur_err_token()); stack.push(cur_err_token()); tos++; /* advance to the next Symbol, if there is none, we are done */ if (!advance_lookahead()) { if (debug) debug_message("# Completed reparse"); /* scan next Symbol so we can continue parse */ // BUGFIX by Chris Harris <ckharris@ucsd.edu>: // correct a one-off error by commenting out // this next line. /*cur_token = scan();*/ /* go back to normal parser */ return; } if (debug) debug_message("# Current Symbol is #" + cur_err_token().sym); } /* if its less than zero, then it encodes a reduce action */ else if (act < 0) { /* perform the action for the reduce */ lhs_sym = do_action((-act)-1, this, stack, tos); /* look up information about the production */ lhs_sym_num = production_tab[(-act)-1][0]; handle_size = production_tab[(-act)-1][1]; if (debug) debug_reduce((-act)-1, lhs_sym_num, handle_size); /* pop the handle off the stack */ for (int i = 0; i < handle_size; i++) { stack.pop(); tos--; } /* look up the state to go to from the one popped back to */ act = get_reduce(((Symbol)stack.peek()).parse_state, lhs_sym_num); /* shift to that state */ lhs_sym.parse_state = act; lhs_sym.used_by_parser = true; stack.push(lhs_sym); tos++; if (debug) debug_message("# Goto state #" + act); } /* finally if the entry is zero, we have an error (shouldn't happen here, but...)*/ else if (act == 0) { report_fatal_error("Syntax error", lhs_sym); return; } } } /*-----------------------------------------------------------*/ /** Utility function: unpacks parse tables from strings */ protected static short[][] unpackFromStrings(String[] sa) { // Concatanate initialization strings. StringBuffer sb = new StringBuffer(sa[0]); for (int i=1; i<sa.length; i++) sb.append(sa[i]); int n=0; // location in initialization string int size1 = (((int)sb.charAt(n))<<16) | ((int)sb.charAt(n+1)); n+=2; short[][] result = new short[size1][]; for (int i=0; i<size1; i++) { int size2 = (((int)sb.charAt(n))<<16) | ((int)sb.charAt(n+1)); n+=2; result[i] = new short[size2]; for (int j=0; j<size2; j++) result[i][j] = (short) (sb.charAt(n++)-2); } return result; } }