You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			3495 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			3495 lines
		
	
	
		
			134 KiB
		
	
	
	
		
			Python
		
	
# -----------------------------------------------------------------------------
 | 
						|
# ply: yacc.py
 | 
						|
#
 | 
						|
# Copyright (C) 2001-2017
 | 
						|
# David M. Beazley (Dabeaz LLC)
 | 
						|
# All rights reserved.
 | 
						|
#
 | 
						|
# Redistribution and use in source and binary forms, with or without
 | 
						|
# modification, are permitted provided that the following conditions are
 | 
						|
# met:
 | 
						|
#
 | 
						|
# * Redistributions of source code must retain the above copyright notice,
 | 
						|
#   this list of conditions and the following disclaimer.
 | 
						|
# * Redistributions in binary form must reproduce the above copyright notice,
 | 
						|
#   this list of conditions and the following disclaimer in the documentation
 | 
						|
#   and/or other materials provided with the distribution.
 | 
						|
# * Neither the name of the David Beazley or Dabeaz LLC may be used to
 | 
						|
#   endorse or promote products derived from this software without
 | 
						|
#  specific prior written permission.
 | 
						|
#
 | 
						|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
						|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | 
						|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | 
						|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
						|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
						|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | 
						|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
						|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
						|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
						|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | 
						|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# This implements an LR parser that is constructed from grammar rules defined
 | 
						|
# as Python functions. The grammer is specified by supplying the BNF inside
 | 
						|
# Python documentation strings.  The inspiration for this technique was borrowed
 | 
						|
# from John Aycock's Spark parsing system.  PLY might be viewed as cross between
 | 
						|
# Spark and the GNU bison utility.
 | 
						|
#
 | 
						|
# The current implementation is only somewhat object-oriented. The
 | 
						|
# LR parser itself is defined in terms of an object (which allows multiple
 | 
						|
# parsers to co-exist).  However, most of the variables used during table
 | 
						|
# construction are defined in terms of global variables.  Users shouldn't
 | 
						|
# notice unless they are trying to define multiple parsers at the same
 | 
						|
# time using threads (in which case they should have their head examined).
 | 
						|
#
 | 
						|
# This implementation supports both SLR and LALR(1) parsing.  LALR(1)
 | 
						|
# support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu),
 | 
						|
# using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles,
 | 
						|
# Techniques, and Tools" (The Dragon Book).  LALR(1) has since been replaced
 | 
						|
# by the more efficient DeRemer and Pennello algorithm.
 | 
						|
#
 | 
						|
# :::::::: WARNING :::::::
 | 
						|
#
 | 
						|
# Construction of LR parsing tables is fairly complicated and expensive.
 | 
						|
# To make this module run fast, a *LOT* of work has been put into
 | 
						|
# optimization---often at the expensive of readability and what might
 | 
						|
# consider to be good Python "coding style."   Modify the code at your
 | 
						|
# own risk!
 | 
						|
# ----------------------------------------------------------------------------
 | 
						|
 | 
						|
import re
 | 
						|
import types
 | 
						|
import sys
 | 
						|
import os.path
 | 
						|
import inspect
 | 
						|
import base64
 | 
						|
import warnings
 | 
						|
 | 
						|
__version__    = '3.10'
 | 
						|
__tabversion__ = '3.10'
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#                     === User configurable parameters ===
 | 
						|
#
 | 
						|
# Change these to modify the default behavior of yacc (if you wish)
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
yaccdebug   = True             # Debugging mode.  If set, yacc generates a
 | 
						|
                               # a 'parser.out' file in the current directory
 | 
						|
 | 
						|
debug_file  = 'parser.out'     # Default name of the debugging file
 | 
						|
tab_module  = 'parsetab'       # Default name of the table module
 | 
						|
default_lr  = 'LALR'           # Default LR table generation method
 | 
						|
 | 
						|
error_count = 3                # Number of symbols that must be shifted to leave recovery mode
 | 
						|
 | 
						|
yaccdevel   = False            # Set to True if developing yacc.  This turns off optimized
 | 
						|
                               # implementations of certain functions.
 | 
						|
 | 
						|
resultlimit = 40               # Size limit of results when running in debug mode.
 | 
						|
 | 
						|
pickle_protocol = 0            # Protocol to use when writing pickle files
 | 
						|
 | 
						|
# String type-checking compatibility
 | 
						|
if sys.version_info[0] < 3:
 | 
						|
    string_types = basestring
 | 
						|
else:
 | 
						|
    string_types = str
 | 
						|
 | 
						|
MAXINT = sys.maxsize
 | 
						|
 | 
						|
# This object is a stand-in for a logging object created by the
 | 
						|
# logging module.   PLY will use this by default to create things
 | 
						|
# such as the parser.out file.  If a user wants more detailed
 | 
						|
# information, they can create their own logging object and pass
 | 
						|
# it into PLY.
 | 
						|
 | 
						|
class PlyLogger(object):
 | 
						|
    def __init__(self, f):
 | 
						|
        self.f = f
 | 
						|
 | 
						|
    def debug(self, msg, *args, **kwargs):
 | 
						|
        self.f.write((msg % args) + '\n')
 | 
						|
 | 
						|
    info = debug
 | 
						|
 | 
						|
    def warning(self, msg, *args, **kwargs):
 | 
						|
        self.f.write('WARNING: ' + (msg % args) + '\n')
 | 
						|
 | 
						|
    def error(self, msg, *args, **kwargs):
 | 
						|
        self.f.write('ERROR: ' + (msg % args) + '\n')
 | 
						|
 | 
						|
    critical = debug
 | 
						|
 | 
						|
# Null logger is used when no output is generated. Does nothing.
 | 
						|
class NullLogger(object):
 | 
						|
    def __getattribute__(self, name):
 | 
						|
        return self
 | 
						|
 | 
						|
    def __call__(self, *args, **kwargs):
 | 
						|
        return self
 | 
						|
 | 
						|
# Exception raised for yacc-related errors
 | 
						|
class YaccError(Exception):
 | 
						|
    pass
 | 
						|
 | 
						|
# Format the result message that the parser produces when running in debug mode.
 | 
						|
def format_result(r):
 | 
						|
    repr_str = repr(r)
 | 
						|
    if '\n' in repr_str:
 | 
						|
        repr_str = repr(repr_str)
 | 
						|
    if len(repr_str) > resultlimit:
 | 
						|
        repr_str = repr_str[:resultlimit] + ' ...'
 | 
						|
    result = '<%s @ 0x%x> (%s)' % (type(r).__name__, id(r), repr_str)
 | 
						|
    return result
 | 
						|
 | 
						|
# Format stack entries when the parser is running in debug mode
 | 
						|
def format_stack_entry(r):
 | 
						|
    repr_str = repr(r)
 | 
						|
    if '\n' in repr_str:
 | 
						|
        repr_str = repr(repr_str)
 | 
						|
    if len(repr_str) < 16:
 | 
						|
        return repr_str
 | 
						|
    else:
 | 
						|
        return '<%s @ 0x%x>' % (type(r).__name__, id(r))
 | 
						|
 | 
						|
# Panic mode error recovery support.   This feature is being reworked--much of the
 | 
						|
# code here is to offer a deprecation/backwards compatible transition
 | 
						|
 | 
						|
_errok = None
 | 
						|
_token = None
 | 
						|
_restart = None
 | 
						|
_warnmsg = '''PLY: Don't use global functions errok(), token(), and restart() in p_error().
 | 
						|
Instead, invoke the methods on the associated parser instance:
 | 
						|
 | 
						|
    def p_error(p):
 | 
						|
        ...
 | 
						|
        # Use parser.errok(), parser.token(), parser.restart()
 | 
						|
        ...
 | 
						|
 | 
						|
    parser = yacc.yacc()
 | 
						|
'''
 | 
						|
 | 
						|
def errok():
 | 
						|
    warnings.warn(_warnmsg)
 | 
						|
    return _errok()
 | 
						|
 | 
						|
def restart():
 | 
						|
    warnings.warn(_warnmsg)
 | 
						|
    return _restart()
 | 
						|
 | 
						|
def token():
 | 
						|
    warnings.warn(_warnmsg)
 | 
						|
    return _token()
 | 
						|
 | 
						|
# Utility function to call the p_error() function with some deprecation hacks
 | 
						|
def call_errorfunc(errorfunc, token, parser):
 | 
						|
    global _errok, _token, _restart
 | 
						|
    _errok = parser.errok
 | 
						|
    _token = parser.token
 | 
						|
    _restart = parser.restart
 | 
						|
    r = errorfunc(token)
 | 
						|
    try:
 | 
						|
        del _errok, _token, _restart
 | 
						|
    except NameError:
 | 
						|
        pass
 | 
						|
    return r
 | 
						|
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
#                        ===  LR Parsing Engine ===
 | 
						|
#
 | 
						|
# The following classes are used for the LR parser itself.  These are not
 | 
						|
# used during table construction and are independent of the actual LR
 | 
						|
# table generation algorithm
 | 
						|
#-----------------------------------------------------------------------------
 | 
						|
 | 
						|
# This class is used to hold non-terminal grammar symbols during parsing.
 | 
						|
# It normally has the following attributes set:
 | 
						|
#        .type       = Grammar symbol type
 | 
						|
#        .value      = Symbol value
 | 
						|
#        .lineno     = Starting line number
 | 
						|
#        .endlineno  = Ending line number (optional, set automatically)
 | 
						|
#        .lexpos     = Starting lex position
 | 
						|
#        .endlexpos  = Ending lex position (optional, set automatically)
 | 
						|
 | 
						|
class YaccSymbol:
 | 
						|
    def __str__(self):
 | 
						|
        return self.type
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return str(self)
 | 
						|
 | 
						|
# This class is a wrapper around the objects actually passed to each
 | 
						|
# grammar rule.   Index lookup and assignment actually assign the
 | 
						|
# .value attribute of the underlying YaccSymbol object.
 | 
						|
# The lineno() method returns the line number of a given
 | 
						|
# item (or 0 if not defined).   The linespan() method returns
 | 
						|
# a tuple of (startline,endline) representing the range of lines
 | 
						|
# for a symbol.  The lexspan() method returns a tuple (lexpos,endlexpos)
 | 
						|
# representing the range of positional information for a symbol.
 | 
						|
 | 
						|
class YaccProduction:
 | 
						|
    def __init__(self, s, stack=None):
 | 
						|
        self.slice = s
 | 
						|
        self.stack = stack
 | 
						|
        self.lexer = None
 | 
						|
        self.parser = None
 | 
						|
 | 
						|
    def __getitem__(self, n):
 | 
						|
        if isinstance(n, slice):
 | 
						|
            return [s.value for s in self.slice[n]]
 | 
						|
        elif n >= 0:
 | 
						|
            return self.slice[n].value
 | 
						|
        else:
 | 
						|
            return self.stack[n].value
 | 
						|
 | 
						|
    def __setitem__(self, n, v):
 | 
						|
        self.slice[n].value = v
 | 
						|
 | 
						|
    def __getslice__(self, i, j):
 | 
						|
        return [s.value for s in self.slice[i:j]]
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
        return len(self.slice)
 | 
						|
 | 
						|
    def lineno(self, n):
 | 
						|
        return getattr(self.slice[n], 'lineno', 0)
 | 
						|
 | 
						|
    def set_lineno(self, n, lineno):
 | 
						|
        self.slice[n].lineno = lineno
 | 
						|
 | 
						|
    def linespan(self, n):
 | 
						|
        startline = getattr(self.slice[n], 'lineno', 0)
 | 
						|
        endline = getattr(self.slice[n], 'endlineno', startline)
 | 
						|
        return startline, endline
 | 
						|
 | 
						|
    def lexpos(self, n):
 | 
						|
        return getattr(self.slice[n], 'lexpos', 0)
 | 
						|
 | 
						|
    def lexspan(self, n):
 | 
						|
        startpos = getattr(self.slice[n], 'lexpos', 0)
 | 
						|
        endpos = getattr(self.slice[n], 'endlexpos', startpos)
 | 
						|
        return startpos, endpos
 | 
						|
 | 
						|
    def error(self):
 | 
						|
        raise SyntaxError
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                               == LRParser ==
 | 
						|
#
 | 
						|
# The LR Parsing engine.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
class LRParser:
 | 
						|
    def __init__(self, lrtab, errorf):
 | 
						|
        self.productions = lrtab.lr_productions
 | 
						|
        self.action = lrtab.lr_action
 | 
						|
        self.goto = lrtab.lr_goto
 | 
						|
        self.errorfunc = errorf
 | 
						|
        self.set_defaulted_states()
 | 
						|
        self.errorok = True
 | 
						|
 | 
						|
    def errok(self):
 | 
						|
        self.errorok = True
 | 
						|
 | 
						|
    def restart(self):
 | 
						|
        del self.statestack[:]
 | 
						|
        del self.symstack[:]
 | 
						|
        sym = YaccSymbol()
 | 
						|
        sym.type = '$end'
 | 
						|
        self.symstack.append(sym)
 | 
						|
        self.statestack.append(0)
 | 
						|
 | 
						|
    # Defaulted state support.
 | 
						|
    # This method identifies parser states where there is only one possible reduction action.
 | 
						|
    # For such states, the parser can make a choose to make a rule reduction without consuming
 | 
						|
    # the next look-ahead token.  This delayed invocation of the tokenizer can be useful in
 | 
						|
    # certain kinds of advanced parsing situations where the lexer and parser interact with
 | 
						|
    # each other or change states (i.e., manipulation of scope, lexer states, etc.).
 | 
						|
    #
 | 
						|
    # See:  https://www.gnu.org/software/bison/manual/html_node/Default-Reductions.html#Default-Reductions
 | 
						|
    def set_defaulted_states(self):
 | 
						|
        self.defaulted_states = {}
 | 
						|
        for state, actions in self.action.items():
 | 
						|
            rules = list(actions.values())
 | 
						|
            if len(rules) == 1 and rules[0] < 0:
 | 
						|
                self.defaulted_states[state] = rules[0]
 | 
						|
 | 
						|
    def disable_defaulted_states(self):
 | 
						|
        self.defaulted_states = {}
 | 
						|
 | 
						|
    def parse(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
 | 
						|
        if debug or yaccdevel:
 | 
						|
            if isinstance(debug, int):
 | 
						|
                debug = PlyLogger(sys.stderr)
 | 
						|
            return self.parsedebug(input, lexer, debug, tracking, tokenfunc)
 | 
						|
        elif tracking:
 | 
						|
            return self.parseopt(input, lexer, debug, tracking, tokenfunc)
 | 
						|
        else:
 | 
						|
            return self.parseopt_notrack(input, lexer, debug, tracking, tokenfunc)
 | 
						|
 | 
						|
 | 
						|
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
    # parsedebug().
 | 
						|
    #
 | 
						|
    # This is the debugging enabled version of parse().  All changes made to the
 | 
						|
    # parsing engine should be made here.   Optimized versions of this function
 | 
						|
    # are automatically created by the ply/ygen.py script.  This script cuts out
 | 
						|
    # sections enclosed in markers such as this:
 | 
						|
    #
 | 
						|
    #      #--! DEBUG
 | 
						|
    #      statements
 | 
						|
    #      #--! DEBUG
 | 
						|
    #
 | 
						|
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
    def parsedebug(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
 | 
						|
        #--! parsedebug-start
 | 
						|
        lookahead = None                         # Current lookahead symbol
 | 
						|
        lookaheadstack = []                      # Stack of lookahead symbols
 | 
						|
        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
 | 
						|
        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
 | 
						|
        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
 | 
						|
        defaulted_states = self.defaulted_states # Local reference to defaulted states
 | 
						|
        pslice  = YaccProduction(None)           # Production object passed to grammar rules
 | 
						|
        errorcount = 0                           # Used during error recovery
 | 
						|
 | 
						|
        #--! DEBUG
 | 
						|
        debug.info('PLY: PARSE DEBUG START')
 | 
						|
        #--! DEBUG
 | 
						|
 | 
						|
        # If no lexer was given, we will try to use the lex module
 | 
						|
        if not lexer:
 | 
						|
            from . import lex
 | 
						|
            lexer = lex.lexer
 | 
						|
 | 
						|
        # Set up the lexer and parser objects on pslice
 | 
						|
        pslice.lexer = lexer
 | 
						|
        pslice.parser = self
 | 
						|
 | 
						|
        # If input was supplied, pass to lexer
 | 
						|
        if input is not None:
 | 
						|
            lexer.input(input)
 | 
						|
 | 
						|
        if tokenfunc is None:
 | 
						|
            # Tokenize function
 | 
						|
            get_token = lexer.token
 | 
						|
        else:
 | 
						|
            get_token = tokenfunc
 | 
						|
 | 
						|
        # Set the parser() token method (sometimes used in error recovery)
 | 
						|
        self.token = get_token
 | 
						|
 | 
						|
        # Set up the state and symbol stacks
 | 
						|
 | 
						|
        statestack = []                # Stack of parsing states
 | 
						|
        self.statestack = statestack
 | 
						|
        symstack   = []                # Stack of grammar symbols
 | 
						|
        self.symstack = symstack
 | 
						|
 | 
						|
        pslice.stack = symstack         # Put in the production
 | 
						|
        errtoken   = None               # Err token
 | 
						|
 | 
						|
        # The start state is assumed to be (0,$end)
 | 
						|
 | 
						|
        statestack.append(0)
 | 
						|
        sym = YaccSymbol()
 | 
						|
        sym.type = '$end'
 | 
						|
        symstack.append(sym)
 | 
						|
        state = 0
 | 
						|
        while True:
 | 
						|
            # Get the next symbol on the input.  If a lookahead symbol
 | 
						|
            # is already set, we just use that. Otherwise, we'll pull
 | 
						|
            # the next token off of the lookaheadstack or from the lexer
 | 
						|
 | 
						|
            #--! DEBUG
 | 
						|
            debug.debug('')
 | 
						|
            debug.debug('State  : %s', state)
 | 
						|
            #--! DEBUG
 | 
						|
 | 
						|
            if state not in defaulted_states:
 | 
						|
                if not lookahead:
 | 
						|
                    if not lookaheadstack:
 | 
						|
                        lookahead = get_token()     # Get the next token
 | 
						|
                    else:
 | 
						|
                        lookahead = lookaheadstack.pop()
 | 
						|
                    if not lookahead:
 | 
						|
                        lookahead = YaccSymbol()
 | 
						|
                        lookahead.type = '$end'
 | 
						|
 | 
						|
                # Check the action table
 | 
						|
                ltype = lookahead.type
 | 
						|
                t = actions[state].get(ltype)
 | 
						|
            else:
 | 
						|
                t = defaulted_states[state]
 | 
						|
                #--! DEBUG
 | 
						|
                debug.debug('Defaulted state %s: Reduce using %d', state, -t)
 | 
						|
                #--! DEBUG
 | 
						|
 | 
						|
            #--! DEBUG
 | 
						|
            debug.debug('Stack  : %s',
 | 
						|
                        ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
 | 
						|
            #--! DEBUG
 | 
						|
 | 
						|
            if t is not None:
 | 
						|
                if t > 0:
 | 
						|
                    # shift a symbol on the stack
 | 
						|
                    statestack.append(t)
 | 
						|
                    state = t
 | 
						|
 | 
						|
                    #--! DEBUG
 | 
						|
                    debug.debug('Action : Shift and goto state %s', t)
 | 
						|
                    #--! DEBUG
 | 
						|
 | 
						|
                    symstack.append(lookahead)
 | 
						|
                    lookahead = None
 | 
						|
 | 
						|
                    # Decrease error count on successful shift
 | 
						|
                    if errorcount:
 | 
						|
                        errorcount -= 1
 | 
						|
                    continue
 | 
						|
 | 
						|
                if t < 0:
 | 
						|
                    # reduce a symbol on the stack, emit a production
 | 
						|
                    p = prod[-t]
 | 
						|
                    pname = p.name
 | 
						|
                    plen  = p.len
 | 
						|
 | 
						|
                    # Get production function
 | 
						|
                    sym = YaccSymbol()
 | 
						|
                    sym.type = pname       # Production name
 | 
						|
                    sym.value = None
 | 
						|
 | 
						|
                    #--! DEBUG
 | 
						|
                    if plen:
 | 
						|
                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str,
 | 
						|
                                   '['+','.join([format_stack_entry(_v.value) for _v in symstack[-plen:]])+']',
 | 
						|
                                   goto[statestack[-1-plen]][pname])
 | 
						|
                    else:
 | 
						|
                        debug.info('Action : Reduce rule [%s] with %s and goto state %d', p.str, [],
 | 
						|
                                   goto[statestack[-1]][pname])
 | 
						|
 | 
						|
                    #--! DEBUG
 | 
						|
 | 
						|
                    if plen:
 | 
						|
                        targ = symstack[-plen-1:]
 | 
						|
                        targ[0] = sym
 | 
						|
 | 
						|
                        #--! TRACKING
 | 
						|
                        if tracking:
 | 
						|
                            t1 = targ[1]
 | 
						|
                            sym.lineno = t1.lineno
 | 
						|
                            sym.lexpos = t1.lexpos
 | 
						|
                            t1 = targ[-1]
 | 
						|
                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
 | 
						|
                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
 | 
						|
                        #--! TRACKING
 | 
						|
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
                        # The code enclosed in this section is duplicated
 | 
						|
                        # below as a performance optimization.  Make sure
 | 
						|
                        # changes get made in both locations.
 | 
						|
 | 
						|
                        pslice.slice = targ
 | 
						|
 | 
						|
                        try:
 | 
						|
                            # Call the grammar rule with our special slice object
 | 
						|
                            del symstack[-plen:]
 | 
						|
                            self.state = state
 | 
						|
                            p.callable(pslice)
 | 
						|
                            del statestack[-plen:]
 | 
						|
                            #--! DEBUG
 | 
						|
                            debug.info('Result : %s', format_result(pslice[0]))
 | 
						|
                            #--! DEBUG
 | 
						|
                            symstack.append(sym)
 | 
						|
                            state = goto[statestack[-1]][pname]
 | 
						|
                            statestack.append(state)
 | 
						|
                        except SyntaxError:
 | 
						|
                            # If an error was set. Enter error recovery state
 | 
						|
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
 | 
						|
                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
 | 
						|
                            statestack.pop()                    # Pop back one state (before the reduce)
 | 
						|
                            state = statestack[-1]
 | 
						|
                            sym.type = 'error'
 | 
						|
                            sym.value = 'error'
 | 
						|
                            lookahead = sym
 | 
						|
                            errorcount = error_count
 | 
						|
                            self.errorok = False
 | 
						|
 | 
						|
                        continue
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
                    else:
 | 
						|
 | 
						|
                        #--! TRACKING
 | 
						|
                        if tracking:
 | 
						|
                            sym.lineno = lexer.lineno
 | 
						|
                            sym.lexpos = lexer.lexpos
 | 
						|
                        #--! TRACKING
 | 
						|
 | 
						|
                        targ = [sym]
 | 
						|
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
                        # The code enclosed in this section is duplicated
 | 
						|
                        # above as a performance optimization.  Make sure
 | 
						|
                        # changes get made in both locations.
 | 
						|
 | 
						|
                        pslice.slice = targ
 | 
						|
 | 
						|
                        try:
 | 
						|
                            # Call the grammar rule with our special slice object
 | 
						|
                            self.state = state
 | 
						|
                            p.callable(pslice)
 | 
						|
                            #--! DEBUG
 | 
						|
                            debug.info('Result : %s', format_result(pslice[0]))
 | 
						|
                            #--! DEBUG
 | 
						|
                            symstack.append(sym)
 | 
						|
                            state = goto[statestack[-1]][pname]
 | 
						|
                            statestack.append(state)
 | 
						|
                        except SyntaxError:
 | 
						|
                            # If an error was set. Enter error recovery state
 | 
						|
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
 | 
						|
                            statestack.pop()                    # Pop back one state (before the reduce)
 | 
						|
                            state = statestack[-1]
 | 
						|
                            sym.type = 'error'
 | 
						|
                            sym.value = 'error'
 | 
						|
                            lookahead = sym
 | 
						|
                            errorcount = error_count
 | 
						|
                            self.errorok = False
 | 
						|
 | 
						|
                        continue
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
                if t == 0:
 | 
						|
                    n = symstack[-1]
 | 
						|
                    result = getattr(n, 'value', None)
 | 
						|
                    #--! DEBUG
 | 
						|
                    debug.info('Done   : Returning %s', format_result(result))
 | 
						|
                    debug.info('PLY: PARSE DEBUG END')
 | 
						|
                    #--! DEBUG
 | 
						|
                    return result
 | 
						|
 | 
						|
            if t is None:
 | 
						|
 | 
						|
                #--! DEBUG
 | 
						|
                debug.error('Error  : %s',
 | 
						|
                            ('%s . %s' % (' '.join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip())
 | 
						|
                #--! DEBUG
 | 
						|
 | 
						|
                # We have some kind of parsing error here.  To handle
 | 
						|
                # this, we are going to push the current token onto
 | 
						|
                # the tokenstack and replace it with an 'error' token.
 | 
						|
                # If there are any synchronization rules, they may
 | 
						|
                # catch it.
 | 
						|
                #
 | 
						|
                # In addition to pushing the error token, we call call
 | 
						|
                # the user defined p_error() function if this is the
 | 
						|
                # first syntax error.  This function is only called if
 | 
						|
                # errorcount == 0.
 | 
						|
                if errorcount == 0 or self.errorok:
 | 
						|
                    errorcount = error_count
 | 
						|
                    self.errorok = False
 | 
						|
                    errtoken = lookahead
 | 
						|
                    if errtoken.type == '$end':
 | 
						|
                        errtoken = None               # End of file!
 | 
						|
                    if self.errorfunc:
 | 
						|
                        if errtoken and not hasattr(errtoken, 'lexer'):
 | 
						|
                            errtoken.lexer = lexer
 | 
						|
                        self.state = state
 | 
						|
                        tok = call_errorfunc(self.errorfunc, errtoken, self)
 | 
						|
                        if self.errorok:
 | 
						|
                            # User must have done some kind of panic
 | 
						|
                            # mode recovery on their own.  The
 | 
						|
                            # returned token is the next lookahead
 | 
						|
                            lookahead = tok
 | 
						|
                            errtoken = None
 | 
						|
                            continue
 | 
						|
                    else:
 | 
						|
                        if errtoken:
 | 
						|
                            if hasattr(errtoken, 'lineno'):
 | 
						|
                                lineno = lookahead.lineno
 | 
						|
                            else:
 | 
						|
                                lineno = 0
 | 
						|
                            if lineno:
 | 
						|
                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
 | 
						|
                            else:
 | 
						|
                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
 | 
						|
                        else:
 | 
						|
                            sys.stderr.write('yacc: Parse error in input. EOF\n')
 | 
						|
                            return
 | 
						|
 | 
						|
                else:
 | 
						|
                    errorcount = error_count
 | 
						|
 | 
						|
                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
 | 
						|
                # entire parse has been rolled back and we're completely hosed.   The token is
 | 
						|
                # discarded and we just keep going.
 | 
						|
 | 
						|
                if len(statestack) <= 1 and lookahead.type != '$end':
 | 
						|
                    lookahead = None
 | 
						|
                    errtoken = None
 | 
						|
                    state = 0
 | 
						|
                    # Nuke the pushback stack
 | 
						|
                    del lookaheadstack[:]
 | 
						|
                    continue
 | 
						|
 | 
						|
                # case 2: the statestack has a couple of entries on it, but we're
 | 
						|
                # at the end of the file. nuke the top entry and generate an error token
 | 
						|
 | 
						|
                # Start nuking entries on the stack
 | 
						|
                if lookahead.type == '$end':
 | 
						|
                    # Whoa. We're really hosed here. Bail out
 | 
						|
                    return
 | 
						|
 | 
						|
                if lookahead.type != 'error':
 | 
						|
                    sym = symstack[-1]
 | 
						|
                    if sym.type == 'error':
 | 
						|
                        # Hmmm. Error is on top of stack, we'll just nuke input
 | 
						|
                        # symbol and continue
 | 
						|
                        #--! TRACKING
 | 
						|
                        if tracking:
 | 
						|
                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
 | 
						|
                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
 | 
						|
                        #--! TRACKING
 | 
						|
                        lookahead = None
 | 
						|
                        continue
 | 
						|
 | 
						|
                    # Create the error symbol for the first time and make it the new lookahead symbol
 | 
						|
                    t = YaccSymbol()
 | 
						|
                    t.type = 'error'
 | 
						|
 | 
						|
                    if hasattr(lookahead, 'lineno'):
 | 
						|
                        t.lineno = t.endlineno = lookahead.lineno
 | 
						|
                    if hasattr(lookahead, 'lexpos'):
 | 
						|
                        t.lexpos = t.endlexpos = lookahead.lexpos
 | 
						|
                    t.value = lookahead
 | 
						|
                    lookaheadstack.append(lookahead)
 | 
						|
                    lookahead = t
 | 
						|
                else:
 | 
						|
                    sym = symstack.pop()
 | 
						|
                    #--! TRACKING
 | 
						|
                    if tracking:
 | 
						|
                        lookahead.lineno = sym.lineno
 | 
						|
                        lookahead.lexpos = sym.lexpos
 | 
						|
                    #--! TRACKING
 | 
						|
                    statestack.pop()
 | 
						|
                    state = statestack[-1]
 | 
						|
 | 
						|
                continue
 | 
						|
 | 
						|
            # Call an error function here
 | 
						|
            raise RuntimeError('yacc: internal parser error!!!\n')
 | 
						|
 | 
						|
        #--! parsedebug-end
 | 
						|
 | 
						|
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
    # parseopt().
 | 
						|
    #
 | 
						|
    # Optimized version of parse() method.  DO NOT EDIT THIS CODE DIRECTLY!
 | 
						|
    # This code is automatically generated by the ply/ygen.py script. Make
 | 
						|
    # changes to the parsedebug() method instead.
 | 
						|
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
    def parseopt(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
 | 
						|
        #--! parseopt-start
 | 
						|
        lookahead = None                         # Current lookahead symbol
 | 
						|
        lookaheadstack = []                      # Stack of lookahead symbols
 | 
						|
        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
 | 
						|
        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
 | 
						|
        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
 | 
						|
        defaulted_states = self.defaulted_states # Local reference to defaulted states
 | 
						|
        pslice  = YaccProduction(None)           # Production object passed to grammar rules
 | 
						|
        errorcount = 0                           # Used during error recovery
 | 
						|
 | 
						|
 | 
						|
        # If no lexer was given, we will try to use the lex module
 | 
						|
        if not lexer:
 | 
						|
            from . import lex
 | 
						|
            lexer = lex.lexer
 | 
						|
 | 
						|
        # Set up the lexer and parser objects on pslice
 | 
						|
        pslice.lexer = lexer
 | 
						|
        pslice.parser = self
 | 
						|
 | 
						|
        # If input was supplied, pass to lexer
 | 
						|
        if input is not None:
 | 
						|
            lexer.input(input)
 | 
						|
 | 
						|
        if tokenfunc is None:
 | 
						|
            # Tokenize function
 | 
						|
            get_token = lexer.token
 | 
						|
        else:
 | 
						|
            get_token = tokenfunc
 | 
						|
 | 
						|
        # Set the parser() token method (sometimes used in error recovery)
 | 
						|
        self.token = get_token
 | 
						|
 | 
						|
        # Set up the state and symbol stacks
 | 
						|
 | 
						|
        statestack = []                # Stack of parsing states
 | 
						|
        self.statestack = statestack
 | 
						|
        symstack   = []                # Stack of grammar symbols
 | 
						|
        self.symstack = symstack
 | 
						|
 | 
						|
        pslice.stack = symstack         # Put in the production
 | 
						|
        errtoken   = None               # Err token
 | 
						|
 | 
						|
        # The start state is assumed to be (0,$end)
 | 
						|
 | 
						|
        statestack.append(0)
 | 
						|
        sym = YaccSymbol()
 | 
						|
        sym.type = '$end'
 | 
						|
        symstack.append(sym)
 | 
						|
        state = 0
 | 
						|
        while True:
 | 
						|
            # Get the next symbol on the input.  If a lookahead symbol
 | 
						|
            # is already set, we just use that. Otherwise, we'll pull
 | 
						|
            # the next token off of the lookaheadstack or from the lexer
 | 
						|
 | 
						|
 | 
						|
            if state not in defaulted_states:
 | 
						|
                if not lookahead:
 | 
						|
                    if not lookaheadstack:
 | 
						|
                        lookahead = get_token()     # Get the next token
 | 
						|
                    else:
 | 
						|
                        lookahead = lookaheadstack.pop()
 | 
						|
                    if not lookahead:
 | 
						|
                        lookahead = YaccSymbol()
 | 
						|
                        lookahead.type = '$end'
 | 
						|
 | 
						|
                # Check the action table
 | 
						|
                ltype = lookahead.type
 | 
						|
                t = actions[state].get(ltype)
 | 
						|
            else:
 | 
						|
                t = defaulted_states[state]
 | 
						|
 | 
						|
 | 
						|
            if t is not None:
 | 
						|
                if t > 0:
 | 
						|
                    # shift a symbol on the stack
 | 
						|
                    statestack.append(t)
 | 
						|
                    state = t
 | 
						|
 | 
						|
 | 
						|
                    symstack.append(lookahead)
 | 
						|
                    lookahead = None
 | 
						|
 | 
						|
                    # Decrease error count on successful shift
 | 
						|
                    if errorcount:
 | 
						|
                        errorcount -= 1
 | 
						|
                    continue
 | 
						|
 | 
						|
                if t < 0:
 | 
						|
                    # reduce a symbol on the stack, emit a production
 | 
						|
                    p = prod[-t]
 | 
						|
                    pname = p.name
 | 
						|
                    plen  = p.len
 | 
						|
 | 
						|
                    # Get production function
 | 
						|
                    sym = YaccSymbol()
 | 
						|
                    sym.type = pname       # Production name
 | 
						|
                    sym.value = None
 | 
						|
 | 
						|
 | 
						|
                    if plen:
 | 
						|
                        targ = symstack[-plen-1:]
 | 
						|
                        targ[0] = sym
 | 
						|
 | 
						|
                        #--! TRACKING
 | 
						|
                        if tracking:
 | 
						|
                            t1 = targ[1]
 | 
						|
                            sym.lineno = t1.lineno
 | 
						|
                            sym.lexpos = t1.lexpos
 | 
						|
                            t1 = targ[-1]
 | 
						|
                            sym.endlineno = getattr(t1, 'endlineno', t1.lineno)
 | 
						|
                            sym.endlexpos = getattr(t1, 'endlexpos', t1.lexpos)
 | 
						|
                        #--! TRACKING
 | 
						|
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
                        # The code enclosed in this section is duplicated
 | 
						|
                        # below as a performance optimization.  Make sure
 | 
						|
                        # changes get made in both locations.
 | 
						|
 | 
						|
                        pslice.slice = targ
 | 
						|
 | 
						|
                        try:
 | 
						|
                            # Call the grammar rule with our special slice object
 | 
						|
                            del symstack[-plen:]
 | 
						|
                            self.state = state
 | 
						|
                            p.callable(pslice)
 | 
						|
                            del statestack[-plen:]
 | 
						|
                            symstack.append(sym)
 | 
						|
                            state = goto[statestack[-1]][pname]
 | 
						|
                            statestack.append(state)
 | 
						|
                        except SyntaxError:
 | 
						|
                            # If an error was set. Enter error recovery state
 | 
						|
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
 | 
						|
                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
 | 
						|
                            statestack.pop()                    # Pop back one state (before the reduce)
 | 
						|
                            state = statestack[-1]
 | 
						|
                            sym.type = 'error'
 | 
						|
                            sym.value = 'error'
 | 
						|
                            lookahead = sym
 | 
						|
                            errorcount = error_count
 | 
						|
                            self.errorok = False
 | 
						|
 | 
						|
                        continue
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
                    else:
 | 
						|
 | 
						|
                        #--! TRACKING
 | 
						|
                        if tracking:
 | 
						|
                            sym.lineno = lexer.lineno
 | 
						|
                            sym.lexpos = lexer.lexpos
 | 
						|
                        #--! TRACKING
 | 
						|
 | 
						|
                        targ = [sym]
 | 
						|
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
                        # The code enclosed in this section is duplicated
 | 
						|
                        # above as a performance optimization.  Make sure
 | 
						|
                        # changes get made in both locations.
 | 
						|
 | 
						|
                        pslice.slice = targ
 | 
						|
 | 
						|
                        try:
 | 
						|
                            # Call the grammar rule with our special slice object
 | 
						|
                            self.state = state
 | 
						|
                            p.callable(pslice)
 | 
						|
                            symstack.append(sym)
 | 
						|
                            state = goto[statestack[-1]][pname]
 | 
						|
                            statestack.append(state)
 | 
						|
                        except SyntaxError:
 | 
						|
                            # If an error was set. Enter error recovery state
 | 
						|
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
 | 
						|
                            statestack.pop()                    # Pop back one state (before the reduce)
 | 
						|
                            state = statestack[-1]
 | 
						|
                            sym.type = 'error'
 | 
						|
                            sym.value = 'error'
 | 
						|
                            lookahead = sym
 | 
						|
                            errorcount = error_count
 | 
						|
                            self.errorok = False
 | 
						|
 | 
						|
                        continue
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
                if t == 0:
 | 
						|
                    n = symstack[-1]
 | 
						|
                    result = getattr(n, 'value', None)
 | 
						|
                    return result
 | 
						|
 | 
						|
            if t is None:
 | 
						|
 | 
						|
 | 
						|
                # We have some kind of parsing error here.  To handle
 | 
						|
                # this, we are going to push the current token onto
 | 
						|
                # the tokenstack and replace it with an 'error' token.
 | 
						|
                # If there are any synchronization rules, they may
 | 
						|
                # catch it.
 | 
						|
                #
 | 
						|
                # In addition to pushing the error token, we call call
 | 
						|
                # the user defined p_error() function if this is the
 | 
						|
                # first syntax error.  This function is only called if
 | 
						|
                # errorcount == 0.
 | 
						|
                if errorcount == 0 or self.errorok:
 | 
						|
                    errorcount = error_count
 | 
						|
                    self.errorok = False
 | 
						|
                    errtoken = lookahead
 | 
						|
                    if errtoken.type == '$end':
 | 
						|
                        errtoken = None               # End of file!
 | 
						|
                    if self.errorfunc:
 | 
						|
                        if errtoken and not hasattr(errtoken, 'lexer'):
 | 
						|
                            errtoken.lexer = lexer
 | 
						|
                        self.state = state
 | 
						|
                        tok = call_errorfunc(self.errorfunc, errtoken, self)
 | 
						|
                        if self.errorok:
 | 
						|
                            # User must have done some kind of panic
 | 
						|
                            # mode recovery on their own.  The
 | 
						|
                            # returned token is the next lookahead
 | 
						|
                            lookahead = tok
 | 
						|
                            errtoken = None
 | 
						|
                            continue
 | 
						|
                    else:
 | 
						|
                        if errtoken:
 | 
						|
                            if hasattr(errtoken, 'lineno'):
 | 
						|
                                lineno = lookahead.lineno
 | 
						|
                            else:
 | 
						|
                                lineno = 0
 | 
						|
                            if lineno:
 | 
						|
                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
 | 
						|
                            else:
 | 
						|
                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
 | 
						|
                        else:
 | 
						|
                            sys.stderr.write('yacc: Parse error in input. EOF\n')
 | 
						|
                            return
 | 
						|
 | 
						|
                else:
 | 
						|
                    errorcount = error_count
 | 
						|
 | 
						|
                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
 | 
						|
                # entire parse has been rolled back and we're completely hosed.   The token is
 | 
						|
                # discarded and we just keep going.
 | 
						|
 | 
						|
                if len(statestack) <= 1 and lookahead.type != '$end':
 | 
						|
                    lookahead = None
 | 
						|
                    errtoken = None
 | 
						|
                    state = 0
 | 
						|
                    # Nuke the pushback stack
 | 
						|
                    del lookaheadstack[:]
 | 
						|
                    continue
 | 
						|
 | 
						|
                # case 2: the statestack has a couple of entries on it, but we're
 | 
						|
                # at the end of the file. nuke the top entry and generate an error token
 | 
						|
 | 
						|
                # Start nuking entries on the stack
 | 
						|
                if lookahead.type == '$end':
 | 
						|
                    # Whoa. We're really hosed here. Bail out
 | 
						|
                    return
 | 
						|
 | 
						|
                if lookahead.type != 'error':
 | 
						|
                    sym = symstack[-1]
 | 
						|
                    if sym.type == 'error':
 | 
						|
                        # Hmmm. Error is on top of stack, we'll just nuke input
 | 
						|
                        # symbol and continue
 | 
						|
                        #--! TRACKING
 | 
						|
                        if tracking:
 | 
						|
                            sym.endlineno = getattr(lookahead, 'lineno', sym.lineno)
 | 
						|
                            sym.endlexpos = getattr(lookahead, 'lexpos', sym.lexpos)
 | 
						|
                        #--! TRACKING
 | 
						|
                        lookahead = None
 | 
						|
                        continue
 | 
						|
 | 
						|
                    # Create the error symbol for the first time and make it the new lookahead symbol
 | 
						|
                    t = YaccSymbol()
 | 
						|
                    t.type = 'error'
 | 
						|
 | 
						|
                    if hasattr(lookahead, 'lineno'):
 | 
						|
                        t.lineno = t.endlineno = lookahead.lineno
 | 
						|
                    if hasattr(lookahead, 'lexpos'):
 | 
						|
                        t.lexpos = t.endlexpos = lookahead.lexpos
 | 
						|
                    t.value = lookahead
 | 
						|
                    lookaheadstack.append(lookahead)
 | 
						|
                    lookahead = t
 | 
						|
                else:
 | 
						|
                    sym = symstack.pop()
 | 
						|
                    #--! TRACKING
 | 
						|
                    if tracking:
 | 
						|
                        lookahead.lineno = sym.lineno
 | 
						|
                        lookahead.lexpos = sym.lexpos
 | 
						|
                    #--! TRACKING
 | 
						|
                    statestack.pop()
 | 
						|
                    state = statestack[-1]
 | 
						|
 | 
						|
                continue
 | 
						|
 | 
						|
            # Call an error function here
 | 
						|
            raise RuntimeError('yacc: internal parser error!!!\n')
 | 
						|
 | 
						|
        #--! parseopt-end
 | 
						|
 | 
						|
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
    # parseopt_notrack().
 | 
						|
    #
 | 
						|
    # Optimized version of parseopt() with line number tracking removed.
 | 
						|
    # DO NOT EDIT THIS CODE DIRECTLY. This code is automatically generated
 | 
						|
    # by the ply/ygen.py script. Make changes to the parsedebug() method instead.
 | 
						|
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
    def parseopt_notrack(self, input=None, lexer=None, debug=False, tracking=False, tokenfunc=None):
 | 
						|
        #--! parseopt-notrack-start
 | 
						|
        lookahead = None                         # Current lookahead symbol
 | 
						|
        lookaheadstack = []                      # Stack of lookahead symbols
 | 
						|
        actions = self.action                    # Local reference to action table (to avoid lookup on self.)
 | 
						|
        goto    = self.goto                      # Local reference to goto table (to avoid lookup on self.)
 | 
						|
        prod    = self.productions               # Local reference to production list (to avoid lookup on self.)
 | 
						|
        defaulted_states = self.defaulted_states # Local reference to defaulted states
 | 
						|
        pslice  = YaccProduction(None)           # Production object passed to grammar rules
 | 
						|
        errorcount = 0                           # Used during error recovery
 | 
						|
 | 
						|
 | 
						|
        # If no lexer was given, we will try to use the lex module
 | 
						|
        if not lexer:
 | 
						|
            from . import lex
 | 
						|
            lexer = lex.lexer
 | 
						|
 | 
						|
        # Set up the lexer and parser objects on pslice
 | 
						|
        pslice.lexer = lexer
 | 
						|
        pslice.parser = self
 | 
						|
 | 
						|
        # If input was supplied, pass to lexer
 | 
						|
        if input is not None:
 | 
						|
            lexer.input(input)
 | 
						|
 | 
						|
        if tokenfunc is None:
 | 
						|
            # Tokenize function
 | 
						|
            get_token = lexer.token
 | 
						|
        else:
 | 
						|
            get_token = tokenfunc
 | 
						|
 | 
						|
        # Set the parser() token method (sometimes used in error recovery)
 | 
						|
        self.token = get_token
 | 
						|
 | 
						|
        # Set up the state and symbol stacks
 | 
						|
 | 
						|
        statestack = []                # Stack of parsing states
 | 
						|
        self.statestack = statestack
 | 
						|
        symstack   = []                # Stack of grammar symbols
 | 
						|
        self.symstack = symstack
 | 
						|
 | 
						|
        pslice.stack = symstack         # Put in the production
 | 
						|
        errtoken   = None               # Err token
 | 
						|
 | 
						|
        # The start state is assumed to be (0,$end)
 | 
						|
 | 
						|
        statestack.append(0)
 | 
						|
        sym = YaccSymbol()
 | 
						|
        sym.type = '$end'
 | 
						|
        symstack.append(sym)
 | 
						|
        state = 0
 | 
						|
        while True:
 | 
						|
            # Get the next symbol on the input.  If a lookahead symbol
 | 
						|
            # is already set, we just use that. Otherwise, we'll pull
 | 
						|
            # the next token off of the lookaheadstack or from the lexer
 | 
						|
 | 
						|
 | 
						|
            if state not in defaulted_states:
 | 
						|
                if not lookahead:
 | 
						|
                    if not lookaheadstack:
 | 
						|
                        lookahead = get_token()     # Get the next token
 | 
						|
                    else:
 | 
						|
                        lookahead = lookaheadstack.pop()
 | 
						|
                    if not lookahead:
 | 
						|
                        lookahead = YaccSymbol()
 | 
						|
                        lookahead.type = '$end'
 | 
						|
 | 
						|
                # Check the action table
 | 
						|
                ltype = lookahead.type
 | 
						|
                t = actions[state].get(ltype)
 | 
						|
            else:
 | 
						|
                t = defaulted_states[state]
 | 
						|
 | 
						|
 | 
						|
            if t is not None:
 | 
						|
                if t > 0:
 | 
						|
                    # shift a symbol on the stack
 | 
						|
                    statestack.append(t)
 | 
						|
                    state = t
 | 
						|
 | 
						|
 | 
						|
                    symstack.append(lookahead)
 | 
						|
                    lookahead = None
 | 
						|
 | 
						|
                    # Decrease error count on successful shift
 | 
						|
                    if errorcount:
 | 
						|
                        errorcount -= 1
 | 
						|
                    continue
 | 
						|
 | 
						|
                if t < 0:
 | 
						|
                    # reduce a symbol on the stack, emit a production
 | 
						|
                    p = prod[-t]
 | 
						|
                    pname = p.name
 | 
						|
                    plen  = p.len
 | 
						|
 | 
						|
                    # Get production function
 | 
						|
                    sym = YaccSymbol()
 | 
						|
                    sym.type = pname       # Production name
 | 
						|
                    sym.value = None
 | 
						|
 | 
						|
 | 
						|
                    if plen:
 | 
						|
                        targ = symstack[-plen-1:]
 | 
						|
                        targ[0] = sym
 | 
						|
 | 
						|
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
                        # The code enclosed in this section is duplicated
 | 
						|
                        # below as a performance optimization.  Make sure
 | 
						|
                        # changes get made in both locations.
 | 
						|
 | 
						|
                        pslice.slice = targ
 | 
						|
 | 
						|
                        try:
 | 
						|
                            # Call the grammar rule with our special slice object
 | 
						|
                            del symstack[-plen:]
 | 
						|
                            self.state = state
 | 
						|
                            p.callable(pslice)
 | 
						|
                            del statestack[-plen:]
 | 
						|
                            symstack.append(sym)
 | 
						|
                            state = goto[statestack[-1]][pname]
 | 
						|
                            statestack.append(state)
 | 
						|
                        except SyntaxError:
 | 
						|
                            # If an error was set. Enter error recovery state
 | 
						|
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
 | 
						|
                            symstack.extend(targ[1:-1])         # Put the production slice back on the stack
 | 
						|
                            statestack.pop()                    # Pop back one state (before the reduce)
 | 
						|
                            state = statestack[-1]
 | 
						|
                            sym.type = 'error'
 | 
						|
                            sym.value = 'error'
 | 
						|
                            lookahead = sym
 | 
						|
                            errorcount = error_count
 | 
						|
                            self.errorok = False
 | 
						|
 | 
						|
                        continue
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
                    else:
 | 
						|
 | 
						|
 | 
						|
                        targ = [sym]
 | 
						|
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
                        # The code enclosed in this section is duplicated
 | 
						|
                        # above as a performance optimization.  Make sure
 | 
						|
                        # changes get made in both locations.
 | 
						|
 | 
						|
                        pslice.slice = targ
 | 
						|
 | 
						|
                        try:
 | 
						|
                            # Call the grammar rule with our special slice object
 | 
						|
                            self.state = state
 | 
						|
                            p.callable(pslice)
 | 
						|
                            symstack.append(sym)
 | 
						|
                            state = goto[statestack[-1]][pname]
 | 
						|
                            statestack.append(state)
 | 
						|
                        except SyntaxError:
 | 
						|
                            # If an error was set. Enter error recovery state
 | 
						|
                            lookaheadstack.append(lookahead)    # Save the current lookahead token
 | 
						|
                            statestack.pop()                    # Pop back one state (before the reduce)
 | 
						|
                            state = statestack[-1]
 | 
						|
                            sym.type = 'error'
 | 
						|
                            sym.value = 'error'
 | 
						|
                            lookahead = sym
 | 
						|
                            errorcount = error_count
 | 
						|
                            self.errorok = False
 | 
						|
 | 
						|
                        continue
 | 
						|
                        # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 | 
						|
 | 
						|
                if t == 0:
 | 
						|
                    n = symstack[-1]
 | 
						|
                    result = getattr(n, 'value', None)
 | 
						|
                    return result
 | 
						|
 | 
						|
            if t is None:
 | 
						|
 | 
						|
 | 
						|
                # We have some kind of parsing error here.  To handle
 | 
						|
                # this, we are going to push the current token onto
 | 
						|
                # the tokenstack and replace it with an 'error' token.
 | 
						|
                # If there are any synchronization rules, they may
 | 
						|
                # catch it.
 | 
						|
                #
 | 
						|
                # In addition to pushing the error token, we call call
 | 
						|
                # the user defined p_error() function if this is the
 | 
						|
                # first syntax error.  This function is only called if
 | 
						|
                # errorcount == 0.
 | 
						|
                if errorcount == 0 or self.errorok:
 | 
						|
                    errorcount = error_count
 | 
						|
                    self.errorok = False
 | 
						|
                    errtoken = lookahead
 | 
						|
                    if errtoken.type == '$end':
 | 
						|
                        errtoken = None               # End of file!
 | 
						|
                    if self.errorfunc:
 | 
						|
                        if errtoken and not hasattr(errtoken, 'lexer'):
 | 
						|
                            errtoken.lexer = lexer
 | 
						|
                        self.state = state
 | 
						|
                        tok = call_errorfunc(self.errorfunc, errtoken, self)
 | 
						|
                        if self.errorok:
 | 
						|
                            # User must have done some kind of panic
 | 
						|
                            # mode recovery on their own.  The
 | 
						|
                            # returned token is the next lookahead
 | 
						|
                            lookahead = tok
 | 
						|
                            errtoken = None
 | 
						|
                            continue
 | 
						|
                    else:
 | 
						|
                        if errtoken:
 | 
						|
                            if hasattr(errtoken, 'lineno'):
 | 
						|
                                lineno = lookahead.lineno
 | 
						|
                            else:
 | 
						|
                                lineno = 0
 | 
						|
                            if lineno:
 | 
						|
                                sys.stderr.write('yacc: Syntax error at line %d, token=%s\n' % (lineno, errtoken.type))
 | 
						|
                            else:
 | 
						|
                                sys.stderr.write('yacc: Syntax error, token=%s' % errtoken.type)
 | 
						|
                        else:
 | 
						|
                            sys.stderr.write('yacc: Parse error in input. EOF\n')
 | 
						|
                            return
 | 
						|
 | 
						|
                else:
 | 
						|
                    errorcount = error_count
 | 
						|
 | 
						|
                # case 1:  the statestack only has 1 entry on it.  If we're in this state, the
 | 
						|
                # entire parse has been rolled back and we're completely hosed.   The token is
 | 
						|
                # discarded and we just keep going.
 | 
						|
 | 
						|
                if len(statestack) <= 1 and lookahead.type != '$end':
 | 
						|
                    lookahead = None
 | 
						|
                    errtoken = None
 | 
						|
                    state = 0
 | 
						|
                    # Nuke the pushback stack
 | 
						|
                    del lookaheadstack[:]
 | 
						|
                    continue
 | 
						|
 | 
						|
                # case 2: the statestack has a couple of entries on it, but we're
 | 
						|
                # at the end of the file. nuke the top entry and generate an error token
 | 
						|
 | 
						|
                # Start nuking entries on the stack
 | 
						|
                if lookahead.type == '$end':
 | 
						|
                    # Whoa. We're really hosed here. Bail out
 | 
						|
                    return
 | 
						|
 | 
						|
                if lookahead.type != 'error':
 | 
						|
                    sym = symstack[-1]
 | 
						|
                    if sym.type == 'error':
 | 
						|
                        # Hmmm. Error is on top of stack, we'll just nuke input
 | 
						|
                        # symbol and continue
 | 
						|
                        lookahead = None
 | 
						|
                        continue
 | 
						|
 | 
						|
                    # Create the error symbol for the first time and make it the new lookahead symbol
 | 
						|
                    t = YaccSymbol()
 | 
						|
                    t.type = 'error'
 | 
						|
 | 
						|
                    if hasattr(lookahead, 'lineno'):
 | 
						|
                        t.lineno = t.endlineno = lookahead.lineno
 | 
						|
                    if hasattr(lookahead, 'lexpos'):
 | 
						|
                        t.lexpos = t.endlexpos = lookahead.lexpos
 | 
						|
                    t.value = lookahead
 | 
						|
                    lookaheadstack.append(lookahead)
 | 
						|
                    lookahead = t
 | 
						|
                else:
 | 
						|
                    sym = symstack.pop()
 | 
						|
                    statestack.pop()
 | 
						|
                    state = statestack[-1]
 | 
						|
 | 
						|
                continue
 | 
						|
 | 
						|
            # Call an error function here
 | 
						|
            raise RuntimeError('yacc: internal parser error!!!\n')
 | 
						|
 | 
						|
        #--! parseopt-notrack-end
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                          === Grammar Representation ===
 | 
						|
#
 | 
						|
# The following functions, classes, and variables are used to represent and
 | 
						|
# manipulate the rules that make up a grammar.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
# regex matching identifiers
 | 
						|
_is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$')
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# class Production:
 | 
						|
#
 | 
						|
# This class stores the raw information about a single production or grammar rule.
 | 
						|
# A grammar rule refers to a specification such as this:
 | 
						|
#
 | 
						|
#       expr : expr PLUS term
 | 
						|
#
 | 
						|
# Here are the basic attributes defined on all productions
 | 
						|
#
 | 
						|
#       name     - Name of the production.  For example 'expr'
 | 
						|
#       prod     - A list of symbols on the right side ['expr','PLUS','term']
 | 
						|
#       prec     - Production precedence level
 | 
						|
#       number   - Production number.
 | 
						|
#       func     - Function that executes on reduce
 | 
						|
#       file     - File where production function is defined
 | 
						|
#       lineno   - Line number where production function is defined
 | 
						|
#
 | 
						|
# The following attributes are defined or optional.
 | 
						|
#
 | 
						|
#       len       - Length of the production (number of symbols on right hand side)
 | 
						|
#       usyms     - Set of unique symbols found in the production
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
class Production(object):
 | 
						|
    reduced = 0
 | 
						|
    def __init__(self, number, name, prod, precedence=('right', 0), func=None, file='', line=0):
 | 
						|
        self.name     = name
 | 
						|
        self.prod     = tuple(prod)
 | 
						|
        self.number   = number
 | 
						|
        self.func     = func
 | 
						|
        self.callable = None
 | 
						|
        self.file     = file
 | 
						|
        self.line     = line
 | 
						|
        self.prec     = precedence
 | 
						|
 | 
						|
        # Internal settings used during table construction
 | 
						|
 | 
						|
        self.len  = len(self.prod)   # Length of the production
 | 
						|
 | 
						|
        # Create a list of unique production symbols used in the production
 | 
						|
        self.usyms = []
 | 
						|
        for s in self.prod:
 | 
						|
            if s not in self.usyms:
 | 
						|
                self.usyms.append(s)
 | 
						|
 | 
						|
        # List of all LR items for the production
 | 
						|
        self.lr_items = []
 | 
						|
        self.lr_next = None
 | 
						|
 | 
						|
        # Create a string representation
 | 
						|
        if self.prod:
 | 
						|
            self.str = '%s -> %s' % (self.name, ' '.join(self.prod))
 | 
						|
        else:
 | 
						|
            self.str = '%s -> <empty>' % self.name
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        return self.str
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return 'Production(' + str(self) + ')'
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
        return len(self.prod)
 | 
						|
 | 
						|
    def __nonzero__(self):
 | 
						|
        return 1
 | 
						|
 | 
						|
    def __getitem__(self, index):
 | 
						|
        return self.prod[index]
 | 
						|
 | 
						|
    # Return the nth lr_item from the production (or None if at the end)
 | 
						|
    def lr_item(self, n):
 | 
						|
        if n > len(self.prod):
 | 
						|
            return None
 | 
						|
        p = LRItem(self, n)
 | 
						|
        # Precompute the list of productions immediately following.
 | 
						|
        try:
 | 
						|
            p.lr_after = Prodnames[p.prod[n+1]]
 | 
						|
        except (IndexError, KeyError):
 | 
						|
            p.lr_after = []
 | 
						|
        try:
 | 
						|
            p.lr_before = p.prod[n-1]
 | 
						|
        except IndexError:
 | 
						|
            p.lr_before = None
 | 
						|
        return p
 | 
						|
 | 
						|
    # Bind the production function name to a callable
 | 
						|
    def bind(self, pdict):
 | 
						|
        if self.func:
 | 
						|
            self.callable = pdict[self.func]
 | 
						|
 | 
						|
# This class serves as a minimal standin for Production objects when
 | 
						|
# reading table data from files.   It only contains information
 | 
						|
# actually used by the LR parsing engine, plus some additional
 | 
						|
# debugging information.
 | 
						|
class MiniProduction(object):
 | 
						|
    def __init__(self, str, name, len, func, file, line):
 | 
						|
        self.name     = name
 | 
						|
        self.len      = len
 | 
						|
        self.func     = func
 | 
						|
        self.callable = None
 | 
						|
        self.file     = file
 | 
						|
        self.line     = line
 | 
						|
        self.str      = str
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        return self.str
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return 'MiniProduction(%s)' % self.str
 | 
						|
 | 
						|
    # Bind the production function name to a callable
 | 
						|
    def bind(self, pdict):
 | 
						|
        if self.func:
 | 
						|
            self.callable = pdict[self.func]
 | 
						|
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# class LRItem
 | 
						|
#
 | 
						|
# This class represents a specific stage of parsing a production rule.  For
 | 
						|
# example:
 | 
						|
#
 | 
						|
#       expr : expr . PLUS term
 | 
						|
#
 | 
						|
# In the above, the "." represents the current location of the parse.  Here
 | 
						|
# basic attributes:
 | 
						|
#
 | 
						|
#       name       - Name of the production.  For example 'expr'
 | 
						|
#       prod       - A list of symbols on the right side ['expr','.', 'PLUS','term']
 | 
						|
#       number     - Production number.
 | 
						|
#
 | 
						|
#       lr_next      Next LR item. Example, if we are ' expr -> expr . PLUS term'
 | 
						|
#                    then lr_next refers to 'expr -> expr PLUS . term'
 | 
						|
#       lr_index   - LR item index (location of the ".") in the prod list.
 | 
						|
#       lookaheads - LALR lookahead symbols for this item
 | 
						|
#       len        - Length of the production (number of symbols on right hand side)
 | 
						|
#       lr_after    - List of all productions that immediately follow
 | 
						|
#       lr_before   - Grammar symbol immediately before
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
class LRItem(object):
 | 
						|
    def __init__(self, p, n):
 | 
						|
        self.name       = p.name
 | 
						|
        self.prod       = list(p.prod)
 | 
						|
        self.number     = p.number
 | 
						|
        self.lr_index   = n
 | 
						|
        self.lookaheads = {}
 | 
						|
        self.prod.insert(n, '.')
 | 
						|
        self.prod       = tuple(self.prod)
 | 
						|
        self.len        = len(self.prod)
 | 
						|
        self.usyms      = p.usyms
 | 
						|
 | 
						|
    def __str__(self):
 | 
						|
        if self.prod:
 | 
						|
            s = '%s -> %s' % (self.name, ' '.join(self.prod))
 | 
						|
        else:
 | 
						|
            s = '%s -> <empty>' % self.name
 | 
						|
        return s
 | 
						|
 | 
						|
    def __repr__(self):
 | 
						|
        return 'LRItem(' + str(self) + ')'
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# rightmost_terminal()
 | 
						|
#
 | 
						|
# Return the rightmost terminal from a list of symbols.  Used in add_production()
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
def rightmost_terminal(symbols, terminals):
 | 
						|
    i = len(symbols) - 1
 | 
						|
    while i >= 0:
 | 
						|
        if symbols[i] in terminals:
 | 
						|
            return symbols[i]
 | 
						|
        i -= 1
 | 
						|
    return None
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                           === GRAMMAR CLASS ===
 | 
						|
#
 | 
						|
# The following class represents the contents of the specified grammar along
 | 
						|
# with various computed properties such as first sets, follow sets, LR items, etc.
 | 
						|
# This data is used for critical parts of the table generation process later.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
class GrammarError(YaccError):
 | 
						|
    pass
 | 
						|
 | 
						|
class Grammar(object):
 | 
						|
    def __init__(self, terminals):
 | 
						|
        self.Productions  = [None]  # A list of all of the productions.  The first
 | 
						|
                                    # entry is always reserved for the purpose of
 | 
						|
                                    # building an augmented grammar
 | 
						|
 | 
						|
        self.Prodnames    = {}      # A dictionary mapping the names of nonterminals to a list of all
 | 
						|
                                    # productions of that nonterminal.
 | 
						|
 | 
						|
        self.Prodmap      = {}      # A dictionary that is only used to detect duplicate
 | 
						|
                                    # productions.
 | 
						|
 | 
						|
        self.Terminals    = {}      # A dictionary mapping the names of terminal symbols to a
 | 
						|
                                    # list of the rules where they are used.
 | 
						|
 | 
						|
        for term in terminals:
 | 
						|
            self.Terminals[term] = []
 | 
						|
 | 
						|
        self.Terminals['error'] = []
 | 
						|
 | 
						|
        self.Nonterminals = {}      # A dictionary mapping names of nonterminals to a list
 | 
						|
                                    # of rule numbers where they are used.
 | 
						|
 | 
						|
        self.First        = {}      # A dictionary of precomputed FIRST(x) symbols
 | 
						|
 | 
						|
        self.Follow       = {}      # A dictionary of precomputed FOLLOW(x) symbols
 | 
						|
 | 
						|
        self.Precedence   = {}      # Precedence rules for each terminal. Contains tuples of the
 | 
						|
                                    # form ('right',level) or ('nonassoc', level) or ('left',level)
 | 
						|
 | 
						|
        self.UsedPrecedence = set() # Precedence rules that were actually used by the grammer.
 | 
						|
                                    # This is only used to provide error checking and to generate
 | 
						|
                                    # a warning about unused precedence rules.
 | 
						|
 | 
						|
        self.Start = None           # Starting symbol for the grammar
 | 
						|
 | 
						|
 | 
						|
    def __len__(self):
 | 
						|
        return len(self.Productions)
 | 
						|
 | 
						|
    def __getitem__(self, index):
 | 
						|
        return self.Productions[index]
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # set_precedence()
 | 
						|
    #
 | 
						|
    # Sets the precedence for a given terminal. assoc is the associativity such as
 | 
						|
    # 'left','right', or 'nonassoc'.  level is a numeric level.
 | 
						|
    #
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def set_precedence(self, term, assoc, level):
 | 
						|
        assert self.Productions == [None], 'Must call set_precedence() before add_production()'
 | 
						|
        if term in self.Precedence:
 | 
						|
            raise GrammarError('Precedence already specified for terminal %r' % term)
 | 
						|
        if assoc not in ['left', 'right', 'nonassoc']:
 | 
						|
            raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'")
 | 
						|
        self.Precedence[term] = (assoc, level)
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # add_production()
 | 
						|
    #
 | 
						|
    # Given an action function, this function assembles a production rule and
 | 
						|
    # computes its precedence level.
 | 
						|
    #
 | 
						|
    # The production rule is supplied as a list of symbols.   For example,
 | 
						|
    # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and
 | 
						|
    # symbols ['expr','PLUS','term'].
 | 
						|
    #
 | 
						|
    # Precedence is determined by the precedence of the right-most non-terminal
 | 
						|
    # or the precedence of a terminal specified by %prec.
 | 
						|
    #
 | 
						|
    # A variety of error checks are performed to make sure production symbols
 | 
						|
    # are valid and that %prec is used correctly.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def add_production(self, prodname, syms, func=None, file='', line=0):
 | 
						|
 | 
						|
        if prodname in self.Terminals:
 | 
						|
            raise GrammarError('%s:%d: Illegal rule name %r. Already defined as a token' % (file, line, prodname))
 | 
						|
        if prodname == 'error':
 | 
						|
            raise GrammarError('%s:%d: Illegal rule name %r. error is a reserved word' % (file, line, prodname))
 | 
						|
        if not _is_identifier.match(prodname):
 | 
						|
            raise GrammarError('%s:%d: Illegal rule name %r' % (file, line, prodname))
 | 
						|
 | 
						|
        # Look for literal tokens
 | 
						|
        for n, s in enumerate(syms):
 | 
						|
            if s[0] in "'\"":
 | 
						|
                try:
 | 
						|
                    c = eval(s)
 | 
						|
                    if (len(c) > 1):
 | 
						|
                        raise GrammarError('%s:%d: Literal token %s in rule %r may only be a single character' %
 | 
						|
                                           (file, line, s, prodname))
 | 
						|
                    if c not in self.Terminals:
 | 
						|
                        self.Terminals[c] = []
 | 
						|
                    syms[n] = c
 | 
						|
                    continue
 | 
						|
                except SyntaxError:
 | 
						|
                    pass
 | 
						|
            if not _is_identifier.match(s) and s != '%prec':
 | 
						|
                raise GrammarError('%s:%d: Illegal name %r in rule %r' % (file, line, s, prodname))
 | 
						|
 | 
						|
        # Determine the precedence level
 | 
						|
        if '%prec' in syms:
 | 
						|
            if syms[-1] == '%prec':
 | 
						|
                raise GrammarError('%s:%d: Syntax error. Nothing follows %%prec' % (file, line))
 | 
						|
            if syms[-2] != '%prec':
 | 
						|
                raise GrammarError('%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule' %
 | 
						|
                                   (file, line))
 | 
						|
            precname = syms[-1]
 | 
						|
            prodprec = self.Precedence.get(precname)
 | 
						|
            if not prodprec:
 | 
						|
                raise GrammarError('%s:%d: Nothing known about the precedence of %r' % (file, line, precname))
 | 
						|
            else:
 | 
						|
                self.UsedPrecedence.add(precname)
 | 
						|
            del syms[-2:]     # Drop %prec from the rule
 | 
						|
        else:
 | 
						|
            # If no %prec, precedence is determined by the rightmost terminal symbol
 | 
						|
            precname = rightmost_terminal(syms, self.Terminals)
 | 
						|
            prodprec = self.Precedence.get(precname, ('right', 0))
 | 
						|
 | 
						|
        # See if the rule is already in the rulemap
 | 
						|
        map = '%s -> %s' % (prodname, syms)
 | 
						|
        if map in self.Prodmap:
 | 
						|
            m = self.Prodmap[map]
 | 
						|
            raise GrammarError('%s:%d: Duplicate rule %s. ' % (file, line, m) +
 | 
						|
                               'Previous definition at %s:%d' % (m.file, m.line))
 | 
						|
 | 
						|
        # From this point on, everything is valid.  Create a new Production instance
 | 
						|
        pnumber  = len(self.Productions)
 | 
						|
        if prodname not in self.Nonterminals:
 | 
						|
            self.Nonterminals[prodname] = []
 | 
						|
 | 
						|
        # Add the production number to Terminals and Nonterminals
 | 
						|
        for t in syms:
 | 
						|
            if t in self.Terminals:
 | 
						|
                self.Terminals[t].append(pnumber)
 | 
						|
            else:
 | 
						|
                if t not in self.Nonterminals:
 | 
						|
                    self.Nonterminals[t] = []
 | 
						|
                self.Nonterminals[t].append(pnumber)
 | 
						|
 | 
						|
        # Create a production and add it to the list of productions
 | 
						|
        p = Production(pnumber, prodname, syms, prodprec, func, file, line)
 | 
						|
        self.Productions.append(p)
 | 
						|
        self.Prodmap[map] = p
 | 
						|
 | 
						|
        # Add to the global productions list
 | 
						|
        try:
 | 
						|
            self.Prodnames[prodname].append(p)
 | 
						|
        except KeyError:
 | 
						|
            self.Prodnames[prodname] = [p]
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # set_start()
 | 
						|
    #
 | 
						|
    # Sets the starting symbol and creates the augmented grammar.  Production
 | 
						|
    # rule 0 is S' -> start where start is the start symbol.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def set_start(self, start=None):
 | 
						|
        if not start:
 | 
						|
            start = self.Productions[1].name
 | 
						|
        if start not in self.Nonterminals:
 | 
						|
            raise GrammarError('start symbol %s undefined' % start)
 | 
						|
        self.Productions[0] = Production(0, "S'", [start])
 | 
						|
        self.Nonterminals[start].append(0)
 | 
						|
        self.Start = start
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # find_unreachable()
 | 
						|
    #
 | 
						|
    # Find all of the nonterminal symbols that can't be reached from the starting
 | 
						|
    # symbol.  Returns a list of nonterminals that can't be reached.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def find_unreachable(self):
 | 
						|
 | 
						|
        # Mark all symbols that are reachable from a symbol s
 | 
						|
        def mark_reachable_from(s):
 | 
						|
            if s in reachable:
 | 
						|
                return
 | 
						|
            reachable.add(s)
 | 
						|
            for p in self.Prodnames.get(s, []):
 | 
						|
                for r in p.prod:
 | 
						|
                    mark_reachable_from(r)
 | 
						|
 | 
						|
        reachable = set()
 | 
						|
        mark_reachable_from(self.Productions[0].prod[0])
 | 
						|
        return [s for s in self.Nonterminals if s not in reachable]
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # infinite_cycles()
 | 
						|
    #
 | 
						|
    # This function looks at the various parsing rules and tries to detect
 | 
						|
    # infinite recursion cycles (grammar rules where there is no possible way
 | 
						|
    # to derive a string of only terminals).
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def infinite_cycles(self):
 | 
						|
        terminates = {}
 | 
						|
 | 
						|
        # Terminals:
 | 
						|
        for t in self.Terminals:
 | 
						|
            terminates[t] = True
 | 
						|
 | 
						|
        terminates['$end'] = True
 | 
						|
 | 
						|
        # Nonterminals:
 | 
						|
 | 
						|
        # Initialize to false:
 | 
						|
        for n in self.Nonterminals:
 | 
						|
            terminates[n] = False
 | 
						|
 | 
						|
        # Then propagate termination until no change:
 | 
						|
        while True:
 | 
						|
            some_change = False
 | 
						|
            for (n, pl) in self.Prodnames.items():
 | 
						|
                # Nonterminal n terminates iff any of its productions terminates.
 | 
						|
                for p in pl:
 | 
						|
                    # Production p terminates iff all of its rhs symbols terminate.
 | 
						|
                    for s in p.prod:
 | 
						|
                        if not terminates[s]:
 | 
						|
                            # The symbol s does not terminate,
 | 
						|
                            # so production p does not terminate.
 | 
						|
                            p_terminates = False
 | 
						|
                            break
 | 
						|
                    else:
 | 
						|
                        # didn't break from the loop,
 | 
						|
                        # so every symbol s terminates
 | 
						|
                        # so production p terminates.
 | 
						|
                        p_terminates = True
 | 
						|
 | 
						|
                    if p_terminates:
 | 
						|
                        # symbol n terminates!
 | 
						|
                        if not terminates[n]:
 | 
						|
                            terminates[n] = True
 | 
						|
                            some_change = True
 | 
						|
                        # Don't need to consider any more productions for this n.
 | 
						|
                        break
 | 
						|
 | 
						|
            if not some_change:
 | 
						|
                break
 | 
						|
 | 
						|
        infinite = []
 | 
						|
        for (s, term) in terminates.items():
 | 
						|
            if not term:
 | 
						|
                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
 | 
						|
                    # s is used-but-not-defined, and we've already warned of that,
 | 
						|
                    # so it would be overkill to say that it's also non-terminating.
 | 
						|
                    pass
 | 
						|
                else:
 | 
						|
                    infinite.append(s)
 | 
						|
 | 
						|
        return infinite
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # undefined_symbols()
 | 
						|
    #
 | 
						|
    # Find all symbols that were used the grammar, but not defined as tokens or
 | 
						|
    # grammar rules.  Returns a list of tuples (sym, prod) where sym in the symbol
 | 
						|
    # and prod is the production where the symbol was used.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    def undefined_symbols(self):
 | 
						|
        result = []
 | 
						|
        for p in self.Productions:
 | 
						|
            if not p:
 | 
						|
                continue
 | 
						|
 | 
						|
            for s in p.prod:
 | 
						|
                if s not in self.Prodnames and s not in self.Terminals and s != 'error':
 | 
						|
                    result.append((s, p))
 | 
						|
        return result
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # unused_terminals()
 | 
						|
    #
 | 
						|
    # Find all terminals that were defined, but not used by the grammar.  Returns
 | 
						|
    # a list of all symbols.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    def unused_terminals(self):
 | 
						|
        unused_tok = []
 | 
						|
        for s, v in self.Terminals.items():
 | 
						|
            if s != 'error' and not v:
 | 
						|
                unused_tok.append(s)
 | 
						|
 | 
						|
        return unused_tok
 | 
						|
 | 
						|
    # ------------------------------------------------------------------------------
 | 
						|
    # unused_rules()
 | 
						|
    #
 | 
						|
    # Find all grammar rules that were defined,  but not used (maybe not reachable)
 | 
						|
    # Returns a list of productions.
 | 
						|
    # ------------------------------------------------------------------------------
 | 
						|
 | 
						|
    def unused_rules(self):
 | 
						|
        unused_prod = []
 | 
						|
        for s, v in self.Nonterminals.items():
 | 
						|
            if not v:
 | 
						|
                p = self.Prodnames[s][0]
 | 
						|
                unused_prod.append(p)
 | 
						|
        return unused_prod
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # unused_precedence()
 | 
						|
    #
 | 
						|
    # Returns a list of tuples (term,precedence) corresponding to precedence
 | 
						|
    # rules that were never used by the grammar.  term is the name of the terminal
 | 
						|
    # on which precedence was applied and precedence is a string such as 'left' or
 | 
						|
    # 'right' corresponding to the type of precedence.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def unused_precedence(self):
 | 
						|
        unused = []
 | 
						|
        for termname in self.Precedence:
 | 
						|
            if not (termname in self.Terminals or termname in self.UsedPrecedence):
 | 
						|
                unused.append((termname, self.Precedence[termname][0]))
 | 
						|
 | 
						|
        return unused
 | 
						|
 | 
						|
    # -------------------------------------------------------------------------
 | 
						|
    # _first()
 | 
						|
    #
 | 
						|
    # Compute the value of FIRST1(beta) where beta is a tuple of symbols.
 | 
						|
    #
 | 
						|
    # During execution of compute_first1, the result may be incomplete.
 | 
						|
    # Afterward (e.g., when called from compute_follow()), it will be complete.
 | 
						|
    # -------------------------------------------------------------------------
 | 
						|
    def _first(self, beta):
 | 
						|
 | 
						|
        # We are computing First(x1,x2,x3,...,xn)
 | 
						|
        result = []
 | 
						|
        for x in beta:
 | 
						|
            x_produces_empty = False
 | 
						|
 | 
						|
            # Add all the non-<empty> symbols of First[x] to the result.
 | 
						|
            for f in self.First[x]:
 | 
						|
                if f == '<empty>':
 | 
						|
                    x_produces_empty = True
 | 
						|
                else:
 | 
						|
                    if f not in result:
 | 
						|
                        result.append(f)
 | 
						|
 | 
						|
            if x_produces_empty:
 | 
						|
                # We have to consider the next x in beta,
 | 
						|
                # i.e. stay in the loop.
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                # We don't have to consider any further symbols in beta.
 | 
						|
                break
 | 
						|
        else:
 | 
						|
            # There was no 'break' from the loop,
 | 
						|
            # so x_produces_empty was true for all x in beta,
 | 
						|
            # so beta produces empty as well.
 | 
						|
            result.append('<empty>')
 | 
						|
 | 
						|
        return result
 | 
						|
 | 
						|
    # -------------------------------------------------------------------------
 | 
						|
    # compute_first()
 | 
						|
    #
 | 
						|
    # Compute the value of FIRST1(X) for all symbols
 | 
						|
    # -------------------------------------------------------------------------
 | 
						|
    def compute_first(self):
 | 
						|
        if self.First:
 | 
						|
            return self.First
 | 
						|
 | 
						|
        # Terminals:
 | 
						|
        for t in self.Terminals:
 | 
						|
            self.First[t] = [t]
 | 
						|
 | 
						|
        self.First['$end'] = ['$end']
 | 
						|
 | 
						|
        # Nonterminals:
 | 
						|
 | 
						|
        # Initialize to the empty set:
 | 
						|
        for n in self.Nonterminals:
 | 
						|
            self.First[n] = []
 | 
						|
 | 
						|
        # Then propagate symbols until no change:
 | 
						|
        while True:
 | 
						|
            some_change = False
 | 
						|
            for n in self.Nonterminals:
 | 
						|
                for p in self.Prodnames[n]:
 | 
						|
                    for f in self._first(p.prod):
 | 
						|
                        if f not in self.First[n]:
 | 
						|
                            self.First[n].append(f)
 | 
						|
                            some_change = True
 | 
						|
            if not some_change:
 | 
						|
                break
 | 
						|
 | 
						|
        return self.First
 | 
						|
 | 
						|
    # ---------------------------------------------------------------------
 | 
						|
    # compute_follow()
 | 
						|
    #
 | 
						|
    # Computes all of the follow sets for every non-terminal symbol.  The
 | 
						|
    # follow set is the set of all symbols that might follow a given
 | 
						|
    # non-terminal.  See the Dragon book, 2nd Ed. p. 189.
 | 
						|
    # ---------------------------------------------------------------------
 | 
						|
    def compute_follow(self, start=None):
 | 
						|
        # If already computed, return the result
 | 
						|
        if self.Follow:
 | 
						|
            return self.Follow
 | 
						|
 | 
						|
        # If first sets not computed yet, do that first.
 | 
						|
        if not self.First:
 | 
						|
            self.compute_first()
 | 
						|
 | 
						|
        # Add '$end' to the follow list of the start symbol
 | 
						|
        for k in self.Nonterminals:
 | 
						|
            self.Follow[k] = []
 | 
						|
 | 
						|
        if not start:
 | 
						|
            start = self.Productions[1].name
 | 
						|
 | 
						|
        self.Follow[start] = ['$end']
 | 
						|
 | 
						|
        while True:
 | 
						|
            didadd = False
 | 
						|
            for p in self.Productions[1:]:
 | 
						|
                # Here is the production set
 | 
						|
                for i, B in enumerate(p.prod):
 | 
						|
                    if B in self.Nonterminals:
 | 
						|
                        # Okay. We got a non-terminal in a production
 | 
						|
                        fst = self._first(p.prod[i+1:])
 | 
						|
                        hasempty = False
 | 
						|
                        for f in fst:
 | 
						|
                            if f != '<empty>' and f not in self.Follow[B]:
 | 
						|
                                self.Follow[B].append(f)
 | 
						|
                                didadd = True
 | 
						|
                            if f == '<empty>':
 | 
						|
                                hasempty = True
 | 
						|
                        if hasempty or i == (len(p.prod)-1):
 | 
						|
                            # Add elements of follow(a) to follow(b)
 | 
						|
                            for f in self.Follow[p.name]:
 | 
						|
                                if f not in self.Follow[B]:
 | 
						|
                                    self.Follow[B].append(f)
 | 
						|
                                    didadd = True
 | 
						|
            if not didadd:
 | 
						|
                break
 | 
						|
        return self.Follow
 | 
						|
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # build_lritems()
 | 
						|
    #
 | 
						|
    # This function walks the list of productions and builds a complete set of the
 | 
						|
    # LR items.  The LR items are stored in two ways:  First, they are uniquely
 | 
						|
    # numbered and placed in the list _lritems.  Second, a linked list of LR items
 | 
						|
    # is built for each production.  For example:
 | 
						|
    #
 | 
						|
    #   E -> E PLUS E
 | 
						|
    #
 | 
						|
    # Creates the list
 | 
						|
    #
 | 
						|
    #  [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ]
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def build_lritems(self):
 | 
						|
        for p in self.Productions:
 | 
						|
            lastlri = p
 | 
						|
            i = 0
 | 
						|
            lr_items = []
 | 
						|
            while True:
 | 
						|
                if i > len(p):
 | 
						|
                    lri = None
 | 
						|
                else:
 | 
						|
                    lri = LRItem(p, i)
 | 
						|
                    # Precompute the list of productions immediately following
 | 
						|
                    try:
 | 
						|
                        lri.lr_after = self.Prodnames[lri.prod[i+1]]
 | 
						|
                    except (IndexError, KeyError):
 | 
						|
                        lri.lr_after = []
 | 
						|
                    try:
 | 
						|
                        lri.lr_before = lri.prod[i-1]
 | 
						|
                    except IndexError:
 | 
						|
                        lri.lr_before = None
 | 
						|
 | 
						|
                lastlri.lr_next = lri
 | 
						|
                if not lri:
 | 
						|
                    break
 | 
						|
                lr_items.append(lri)
 | 
						|
                lastlri = lri
 | 
						|
                i += 1
 | 
						|
            p.lr_items = lr_items
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                            == Class LRTable ==
 | 
						|
#
 | 
						|
# This basic class represents a basic table of LR parsing information.
 | 
						|
# Methods for generating the tables are not defined here.  They are defined
 | 
						|
# in the derived class LRGeneratedTable.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
class VersionError(YaccError):
 | 
						|
    pass
 | 
						|
 | 
						|
class LRTable(object):
 | 
						|
    def __init__(self):
 | 
						|
        self.lr_action = None
 | 
						|
        self.lr_goto = None
 | 
						|
        self.lr_productions = None
 | 
						|
        self.lr_method = None
 | 
						|
 | 
						|
    def read_table(self, module):
 | 
						|
        if isinstance(module, types.ModuleType):
 | 
						|
            parsetab = module
 | 
						|
        else:
 | 
						|
            exec('import %s' % module)
 | 
						|
            parsetab = sys.modules[module]
 | 
						|
 | 
						|
        if parsetab._tabversion != __tabversion__:
 | 
						|
            raise VersionError('yacc table file version is out of date')
 | 
						|
 | 
						|
        self.lr_action = parsetab._lr_action
 | 
						|
        self.lr_goto = parsetab._lr_goto
 | 
						|
 | 
						|
        self.lr_productions = []
 | 
						|
        for p in parsetab._lr_productions:
 | 
						|
            self.lr_productions.append(MiniProduction(*p))
 | 
						|
 | 
						|
        self.lr_method = parsetab._lr_method
 | 
						|
        return parsetab._lr_signature
 | 
						|
 | 
						|
    def read_pickle(self, filename):
 | 
						|
        try:
 | 
						|
            import cPickle as pickle
 | 
						|
        except ImportError:
 | 
						|
            import pickle
 | 
						|
 | 
						|
        if not os.path.exists(filename):
 | 
						|
          raise ImportError
 | 
						|
 | 
						|
        in_f = open(filename, 'rb')
 | 
						|
 | 
						|
        tabversion = pickle.load(in_f)
 | 
						|
        if tabversion != __tabversion__:
 | 
						|
            raise VersionError('yacc table file version is out of date')
 | 
						|
        self.lr_method = pickle.load(in_f)
 | 
						|
        signature      = pickle.load(in_f)
 | 
						|
        self.lr_action = pickle.load(in_f)
 | 
						|
        self.lr_goto   = pickle.load(in_f)
 | 
						|
        productions    = pickle.load(in_f)
 | 
						|
 | 
						|
        self.lr_productions = []
 | 
						|
        for p in productions:
 | 
						|
            self.lr_productions.append(MiniProduction(*p))
 | 
						|
 | 
						|
        in_f.close()
 | 
						|
        return signature
 | 
						|
 | 
						|
    # Bind all production function names to callable objects in pdict
 | 
						|
    def bind_callables(self, pdict):
 | 
						|
        for p in self.lr_productions:
 | 
						|
            p.bind(pdict)
 | 
						|
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                           === LR Generator ===
 | 
						|
#
 | 
						|
# The following classes and functions are used to generate LR parsing tables on
 | 
						|
# a grammar.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# digraph()
 | 
						|
# traverse()
 | 
						|
#
 | 
						|
# The following two functions are used to compute set valued functions
 | 
						|
# of the form:
 | 
						|
#
 | 
						|
#     F(x) = F'(x) U U{F(y) | x R y}
 | 
						|
#
 | 
						|
# This is used to compute the values of Read() sets as well as FOLLOW sets
 | 
						|
# in LALR(1) generation.
 | 
						|
#
 | 
						|
# Inputs:  X    - An input set
 | 
						|
#          R    - A relation
 | 
						|
#          FP   - Set-valued function
 | 
						|
# ------------------------------------------------------------------------------
 | 
						|
 | 
						|
def digraph(X, R, FP):
 | 
						|
    N = {}
 | 
						|
    for x in X:
 | 
						|
        N[x] = 0
 | 
						|
    stack = []
 | 
						|
    F = {}
 | 
						|
    for x in X:
 | 
						|
        if N[x] == 0:
 | 
						|
            traverse(x, N, stack, F, X, R, FP)
 | 
						|
    return F
 | 
						|
 | 
						|
def traverse(x, N, stack, F, X, R, FP):
 | 
						|
    stack.append(x)
 | 
						|
    d = len(stack)
 | 
						|
    N[x] = d
 | 
						|
    F[x] = FP(x)             # F(X) <- F'(x)
 | 
						|
 | 
						|
    rel = R(x)               # Get y's related to x
 | 
						|
    for y in rel:
 | 
						|
        if N[y] == 0:
 | 
						|
            traverse(y, N, stack, F, X, R, FP)
 | 
						|
        N[x] = min(N[x], N[y])
 | 
						|
        for a in F.get(y, []):
 | 
						|
            if a not in F[x]:
 | 
						|
                F[x].append(a)
 | 
						|
    if N[x] == d:
 | 
						|
        N[stack[-1]] = MAXINT
 | 
						|
        F[stack[-1]] = F[x]
 | 
						|
        element = stack.pop()
 | 
						|
        while element != x:
 | 
						|
            N[stack[-1]] = MAXINT
 | 
						|
            F[stack[-1]] = F[x]
 | 
						|
            element = stack.pop()
 | 
						|
 | 
						|
class LALRError(YaccError):
 | 
						|
    pass
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                             == LRGeneratedTable ==
 | 
						|
#
 | 
						|
# This class implements the LR table generation algorithm.  There are no
 | 
						|
# public methods except for write()
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
class LRGeneratedTable(LRTable):
 | 
						|
    def __init__(self, grammar, method='LALR', log=None):
 | 
						|
        if method not in ['SLR', 'LALR']:
 | 
						|
            raise LALRError('Unsupported method %s' % method)
 | 
						|
 | 
						|
        self.grammar = grammar
 | 
						|
        self.lr_method = method
 | 
						|
 | 
						|
        # Set up the logger
 | 
						|
        if not log:
 | 
						|
            log = NullLogger()
 | 
						|
        self.log = log
 | 
						|
 | 
						|
        # Internal attributes
 | 
						|
        self.lr_action     = {}        # Action table
 | 
						|
        self.lr_goto       = {}        # Goto table
 | 
						|
        self.lr_productions  = grammar.Productions    # Copy of grammar Production array
 | 
						|
        self.lr_goto_cache = {}        # Cache of computed gotos
 | 
						|
        self.lr0_cidhash   = {}        # Cache of closures
 | 
						|
 | 
						|
        self._add_count    = 0         # Internal counter used to detect cycles
 | 
						|
 | 
						|
        # Diagonistic information filled in by the table generator
 | 
						|
        self.sr_conflict   = 0
 | 
						|
        self.rr_conflict   = 0
 | 
						|
        self.conflicts     = []        # List of conflicts
 | 
						|
 | 
						|
        self.sr_conflicts  = []
 | 
						|
        self.rr_conflicts  = []
 | 
						|
 | 
						|
        # Build the tables
 | 
						|
        self.grammar.build_lritems()
 | 
						|
        self.grammar.compute_first()
 | 
						|
        self.grammar.compute_follow()
 | 
						|
        self.lr_parse_table()
 | 
						|
 | 
						|
    # Compute the LR(0) closure operation on I, where I is a set of LR(0) items.
 | 
						|
 | 
						|
    def lr0_closure(self, I):
 | 
						|
        self._add_count += 1
 | 
						|
 | 
						|
        # Add everything in I to J
 | 
						|
        J = I[:]
 | 
						|
        didadd = True
 | 
						|
        while didadd:
 | 
						|
            didadd = False
 | 
						|
            for j in J:
 | 
						|
                for x in j.lr_after:
 | 
						|
                    if getattr(x, 'lr0_added', 0) == self._add_count:
 | 
						|
                        continue
 | 
						|
                    # Add B --> .G to J
 | 
						|
                    J.append(x.lr_next)
 | 
						|
                    x.lr0_added = self._add_count
 | 
						|
                    didadd = True
 | 
						|
 | 
						|
        return J
 | 
						|
 | 
						|
    # Compute the LR(0) goto function goto(I,X) where I is a set
 | 
						|
    # of LR(0) items and X is a grammar symbol.   This function is written
 | 
						|
    # in a way that guarantees uniqueness of the generated goto sets
 | 
						|
    # (i.e. the same goto set will never be returned as two different Python
 | 
						|
    # objects).  With uniqueness, we can later do fast set comparisons using
 | 
						|
    # id(obj) instead of element-wise comparison.
 | 
						|
 | 
						|
    def lr0_goto(self, I, x):
 | 
						|
        # First we look for a previously cached entry
 | 
						|
        g = self.lr_goto_cache.get((id(I), x))
 | 
						|
        if g:
 | 
						|
            return g
 | 
						|
 | 
						|
        # Now we generate the goto set in a way that guarantees uniqueness
 | 
						|
        # of the result
 | 
						|
 | 
						|
        s = self.lr_goto_cache.get(x)
 | 
						|
        if not s:
 | 
						|
            s = {}
 | 
						|
            self.lr_goto_cache[x] = s
 | 
						|
 | 
						|
        gs = []
 | 
						|
        for p in I:
 | 
						|
            n = p.lr_next
 | 
						|
            if n and n.lr_before == x:
 | 
						|
                s1 = s.get(id(n))
 | 
						|
                if not s1:
 | 
						|
                    s1 = {}
 | 
						|
                    s[id(n)] = s1
 | 
						|
                gs.append(n)
 | 
						|
                s = s1
 | 
						|
        g = s.get('$end')
 | 
						|
        if not g:
 | 
						|
            if gs:
 | 
						|
                g = self.lr0_closure(gs)
 | 
						|
                s['$end'] = g
 | 
						|
            else:
 | 
						|
                s['$end'] = gs
 | 
						|
        self.lr_goto_cache[(id(I), x)] = g
 | 
						|
        return g
 | 
						|
 | 
						|
    # Compute the LR(0) sets of item function
 | 
						|
    def lr0_items(self):
 | 
						|
        C = [self.lr0_closure([self.grammar.Productions[0].lr_next])]
 | 
						|
        i = 0
 | 
						|
        for I in C:
 | 
						|
            self.lr0_cidhash[id(I)] = i
 | 
						|
            i += 1
 | 
						|
 | 
						|
        # Loop over the items in C and each grammar symbols
 | 
						|
        i = 0
 | 
						|
        while i < len(C):
 | 
						|
            I = C[i]
 | 
						|
            i += 1
 | 
						|
 | 
						|
            # Collect all of the symbols that could possibly be in the goto(I,X) sets
 | 
						|
            asyms = {}
 | 
						|
            for ii in I:
 | 
						|
                for s in ii.usyms:
 | 
						|
                    asyms[s] = None
 | 
						|
 | 
						|
            for x in asyms:
 | 
						|
                g = self.lr0_goto(I, x)
 | 
						|
                if not g or id(g) in self.lr0_cidhash:
 | 
						|
                    continue
 | 
						|
                self.lr0_cidhash[id(g)] = len(C)
 | 
						|
                C.append(g)
 | 
						|
 | 
						|
        return C
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    #                       ==== LALR(1) Parsing ====
 | 
						|
    #
 | 
						|
    # LALR(1) parsing is almost exactly the same as SLR except that instead of
 | 
						|
    # relying upon Follow() sets when performing reductions, a more selective
 | 
						|
    # lookahead set that incorporates the state of the LR(0) machine is utilized.
 | 
						|
    # Thus, we mainly just have to focus on calculating the lookahead sets.
 | 
						|
    #
 | 
						|
    # The method used here is due to DeRemer and Pennelo (1982).
 | 
						|
    #
 | 
						|
    # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1)
 | 
						|
    #     Lookahead Sets", ACM Transactions on Programming Languages and Systems,
 | 
						|
    #     Vol. 4, No. 4, Oct. 1982, pp. 615-649
 | 
						|
    #
 | 
						|
    # Further details can also be found in:
 | 
						|
    #
 | 
						|
    #  J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing",
 | 
						|
    #      McGraw-Hill Book Company, (1985).
 | 
						|
    #
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # compute_nullable_nonterminals()
 | 
						|
    #
 | 
						|
    # Creates a dictionary containing all of the non-terminals that might produce
 | 
						|
    # an empty production.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def compute_nullable_nonterminals(self):
 | 
						|
        nullable = set()
 | 
						|
        num_nullable = 0
 | 
						|
        while True:
 | 
						|
            for p in self.grammar.Productions[1:]:
 | 
						|
                if p.len == 0:
 | 
						|
                    nullable.add(p.name)
 | 
						|
                    continue
 | 
						|
                for t in p.prod:
 | 
						|
                    if t not in nullable:
 | 
						|
                        break
 | 
						|
                else:
 | 
						|
                    nullable.add(p.name)
 | 
						|
            if len(nullable) == num_nullable:
 | 
						|
                break
 | 
						|
            num_nullable = len(nullable)
 | 
						|
        return nullable
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # find_nonterminal_trans(C)
 | 
						|
    #
 | 
						|
    # Given a set of LR(0) items, this functions finds all of the non-terminal
 | 
						|
    # transitions.    These are transitions in which a dot appears immediately before
 | 
						|
    # a non-terminal.   Returns a list of tuples of the form (state,N) where state
 | 
						|
    # is the state number and N is the nonterminal symbol.
 | 
						|
    #
 | 
						|
    # The input C is the set of LR(0) items.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def find_nonterminal_transitions(self, C):
 | 
						|
        trans = []
 | 
						|
        for stateno, state in enumerate(C):
 | 
						|
            for p in state:
 | 
						|
                if p.lr_index < p.len - 1:
 | 
						|
                    t = (stateno, p.prod[p.lr_index+1])
 | 
						|
                    if t[1] in self.grammar.Nonterminals:
 | 
						|
                        if t not in trans:
 | 
						|
                            trans.append(t)
 | 
						|
        return trans
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # dr_relation()
 | 
						|
    #
 | 
						|
    # Computes the DR(p,A) relationships for non-terminal transitions.  The input
 | 
						|
    # is a tuple (state,N) where state is a number and N is a nonterminal symbol.
 | 
						|
    #
 | 
						|
    # Returns a list of terminals.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def dr_relation(self, C, trans, nullable):
 | 
						|
        dr_set = {}
 | 
						|
        state, N = trans
 | 
						|
        terms = []
 | 
						|
 | 
						|
        g = self.lr0_goto(C[state], N)
 | 
						|
        for p in g:
 | 
						|
            if p.lr_index < p.len - 1:
 | 
						|
                a = p.prod[p.lr_index+1]
 | 
						|
                if a in self.grammar.Terminals:
 | 
						|
                    if a not in terms:
 | 
						|
                        terms.append(a)
 | 
						|
 | 
						|
        # This extra bit is to handle the start state
 | 
						|
        if state == 0 and N == self.grammar.Productions[0].prod[0]:
 | 
						|
            terms.append('$end')
 | 
						|
 | 
						|
        return terms
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # reads_relation()
 | 
						|
    #
 | 
						|
    # Computes the READS() relation (p,A) READS (t,C).
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def reads_relation(self, C, trans, empty):
 | 
						|
        # Look for empty transitions
 | 
						|
        rel = []
 | 
						|
        state, N = trans
 | 
						|
 | 
						|
        g = self.lr0_goto(C[state], N)
 | 
						|
        j = self.lr0_cidhash.get(id(g), -1)
 | 
						|
        for p in g:
 | 
						|
            if p.lr_index < p.len - 1:
 | 
						|
                a = p.prod[p.lr_index + 1]
 | 
						|
                if a in empty:
 | 
						|
                    rel.append((j, a))
 | 
						|
 | 
						|
        return rel
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # compute_lookback_includes()
 | 
						|
    #
 | 
						|
    # Determines the lookback and includes relations
 | 
						|
    #
 | 
						|
    # LOOKBACK:
 | 
						|
    #
 | 
						|
    # This relation is determined by running the LR(0) state machine forward.
 | 
						|
    # For example, starting with a production "N : . A B C", we run it forward
 | 
						|
    # to obtain "N : A B C ."   We then build a relationship between this final
 | 
						|
    # state and the starting state.   These relationships are stored in a dictionary
 | 
						|
    # lookdict.
 | 
						|
    #
 | 
						|
    # INCLUDES:
 | 
						|
    #
 | 
						|
    # Computes the INCLUDE() relation (p,A) INCLUDES (p',B).
 | 
						|
    #
 | 
						|
    # This relation is used to determine non-terminal transitions that occur
 | 
						|
    # inside of other non-terminal transition states.   (p,A) INCLUDES (p', B)
 | 
						|
    # if the following holds:
 | 
						|
    #
 | 
						|
    #       B -> LAT, where T -> epsilon and p' -L-> p
 | 
						|
    #
 | 
						|
    # L is essentially a prefix (which may be empty), T is a suffix that must be
 | 
						|
    # able to derive an empty string.  State p' must lead to state p with the string L.
 | 
						|
    #
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def compute_lookback_includes(self, C, trans, nullable):
 | 
						|
        lookdict = {}          # Dictionary of lookback relations
 | 
						|
        includedict = {}       # Dictionary of include relations
 | 
						|
 | 
						|
        # Make a dictionary of non-terminal transitions
 | 
						|
        dtrans = {}
 | 
						|
        for t in trans:
 | 
						|
            dtrans[t] = 1
 | 
						|
 | 
						|
        # Loop over all transitions and compute lookbacks and includes
 | 
						|
        for state, N in trans:
 | 
						|
            lookb = []
 | 
						|
            includes = []
 | 
						|
            for p in C[state]:
 | 
						|
                if p.name != N:
 | 
						|
                    continue
 | 
						|
 | 
						|
                # Okay, we have a name match.  We now follow the production all the way
 | 
						|
                # through the state machine until we get the . on the right hand side
 | 
						|
 | 
						|
                lr_index = p.lr_index
 | 
						|
                j = state
 | 
						|
                while lr_index < p.len - 1:
 | 
						|
                    lr_index = lr_index + 1
 | 
						|
                    t = p.prod[lr_index]
 | 
						|
 | 
						|
                    # Check to see if this symbol and state are a non-terminal transition
 | 
						|
                    if (j, t) in dtrans:
 | 
						|
                        # Yes.  Okay, there is some chance that this is an includes relation
 | 
						|
                        # the only way to know for certain is whether the rest of the
 | 
						|
                        # production derives empty
 | 
						|
 | 
						|
                        li = lr_index + 1
 | 
						|
                        while li < p.len:
 | 
						|
                            if p.prod[li] in self.grammar.Terminals:
 | 
						|
                                break      # No forget it
 | 
						|
                            if p.prod[li] not in nullable:
 | 
						|
                                break
 | 
						|
                            li = li + 1
 | 
						|
                        else:
 | 
						|
                            # Appears to be a relation between (j,t) and (state,N)
 | 
						|
                            includes.append((j, t))
 | 
						|
 | 
						|
                    g = self.lr0_goto(C[j], t)               # Go to next set
 | 
						|
                    j = self.lr0_cidhash.get(id(g), -1)      # Go to next state
 | 
						|
 | 
						|
                # When we get here, j is the final state, now we have to locate the production
 | 
						|
                for r in C[j]:
 | 
						|
                    if r.name != p.name:
 | 
						|
                        continue
 | 
						|
                    if r.len != p.len:
 | 
						|
                        continue
 | 
						|
                    i = 0
 | 
						|
                    # This look is comparing a production ". A B C" with "A B C ."
 | 
						|
                    while i < r.lr_index:
 | 
						|
                        if r.prod[i] != p.prod[i+1]:
 | 
						|
                            break
 | 
						|
                        i = i + 1
 | 
						|
                    else:
 | 
						|
                        lookb.append((j, r))
 | 
						|
            for i in includes:
 | 
						|
                if i not in includedict:
 | 
						|
                    includedict[i] = []
 | 
						|
                includedict[i].append((state, N))
 | 
						|
            lookdict[(state, N)] = lookb
 | 
						|
 | 
						|
        return lookdict, includedict
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # compute_read_sets()
 | 
						|
    #
 | 
						|
    # Given a set of LR(0) items, this function computes the read sets.
 | 
						|
    #
 | 
						|
    # Inputs:  C        =  Set of LR(0) items
 | 
						|
    #          ntrans   = Set of nonterminal transitions
 | 
						|
    #          nullable = Set of empty transitions
 | 
						|
    #
 | 
						|
    # Returns a set containing the read sets
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def compute_read_sets(self, C, ntrans, nullable):
 | 
						|
        FP = lambda x: self.dr_relation(C, x, nullable)
 | 
						|
        R =  lambda x: self.reads_relation(C, x, nullable)
 | 
						|
        F = digraph(ntrans, R, FP)
 | 
						|
        return F
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # compute_follow_sets()
 | 
						|
    #
 | 
						|
    # Given a set of LR(0) items, a set of non-terminal transitions, a readset,
 | 
						|
    # and an include set, this function computes the follow sets
 | 
						|
    #
 | 
						|
    # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)}
 | 
						|
    #
 | 
						|
    # Inputs:
 | 
						|
    #            ntrans     = Set of nonterminal transitions
 | 
						|
    #            readsets   = Readset (previously computed)
 | 
						|
    #            inclsets   = Include sets (previously computed)
 | 
						|
    #
 | 
						|
    # Returns a set containing the follow sets
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def compute_follow_sets(self, ntrans, readsets, inclsets):
 | 
						|
        FP = lambda x: readsets[x]
 | 
						|
        R  = lambda x: inclsets.get(x, [])
 | 
						|
        F = digraph(ntrans, R, FP)
 | 
						|
        return F
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # add_lookaheads()
 | 
						|
    #
 | 
						|
    # Attaches the lookahead symbols to grammar rules.
 | 
						|
    #
 | 
						|
    # Inputs:    lookbacks         -  Set of lookback relations
 | 
						|
    #            followset         -  Computed follow set
 | 
						|
    #
 | 
						|
    # This function directly attaches the lookaheads to productions contained
 | 
						|
    # in the lookbacks set
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def add_lookaheads(self, lookbacks, followset):
 | 
						|
        for trans, lb in lookbacks.items():
 | 
						|
            # Loop over productions in lookback
 | 
						|
            for state, p in lb:
 | 
						|
                if state not in p.lookaheads:
 | 
						|
                    p.lookaheads[state] = []
 | 
						|
                f = followset.get(trans, [])
 | 
						|
                for a in f:
 | 
						|
                    if a not in p.lookaheads[state]:
 | 
						|
                        p.lookaheads[state].append(a)
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # add_lalr_lookaheads()
 | 
						|
    #
 | 
						|
    # This function does all of the work of adding lookahead information for use
 | 
						|
    # with LALR parsing
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def add_lalr_lookaheads(self, C):
 | 
						|
        # Determine all of the nullable nonterminals
 | 
						|
        nullable = self.compute_nullable_nonterminals()
 | 
						|
 | 
						|
        # Find all non-terminal transitions
 | 
						|
        trans = self.find_nonterminal_transitions(C)
 | 
						|
 | 
						|
        # Compute read sets
 | 
						|
        readsets = self.compute_read_sets(C, trans, nullable)
 | 
						|
 | 
						|
        # Compute lookback/includes relations
 | 
						|
        lookd, included = self.compute_lookback_includes(C, trans, nullable)
 | 
						|
 | 
						|
        # Compute LALR FOLLOW sets
 | 
						|
        followsets = self.compute_follow_sets(trans, readsets, included)
 | 
						|
 | 
						|
        # Add all of the lookaheads
 | 
						|
        self.add_lookaheads(lookd, followsets)
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # lr_parse_table()
 | 
						|
    #
 | 
						|
    # This function constructs the parse tables for SLR or LALR
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    def lr_parse_table(self):
 | 
						|
        Productions = self.grammar.Productions
 | 
						|
        Precedence  = self.grammar.Precedence
 | 
						|
        goto   = self.lr_goto         # Goto array
 | 
						|
        action = self.lr_action       # Action array
 | 
						|
        log    = self.log             # Logger for output
 | 
						|
 | 
						|
        actionp = {}                  # Action production array (temporary)
 | 
						|
 | 
						|
        log.info('Parsing method: %s', self.lr_method)
 | 
						|
 | 
						|
        # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items
 | 
						|
        # This determines the number of states
 | 
						|
 | 
						|
        C = self.lr0_items()
 | 
						|
 | 
						|
        if self.lr_method == 'LALR':
 | 
						|
            self.add_lalr_lookaheads(C)
 | 
						|
 | 
						|
        # Build the parser table, state by state
 | 
						|
        st = 0
 | 
						|
        for I in C:
 | 
						|
            # Loop over each production in I
 | 
						|
            actlist = []              # List of actions
 | 
						|
            st_action  = {}
 | 
						|
            st_actionp = {}
 | 
						|
            st_goto    = {}
 | 
						|
            log.info('')
 | 
						|
            log.info('state %d', st)
 | 
						|
            log.info('')
 | 
						|
            for p in I:
 | 
						|
                log.info('    (%d) %s', p.number, p)
 | 
						|
            log.info('')
 | 
						|
 | 
						|
            for p in I:
 | 
						|
                    if p.len == p.lr_index + 1:
 | 
						|
                        if p.name == "S'":
 | 
						|
                            # Start symbol. Accept!
 | 
						|
                            st_action['$end'] = 0
 | 
						|
                            st_actionp['$end'] = p
 | 
						|
                        else:
 | 
						|
                            # We are at the end of a production.  Reduce!
 | 
						|
                            if self.lr_method == 'LALR':
 | 
						|
                                laheads = p.lookaheads[st]
 | 
						|
                            else:
 | 
						|
                                laheads = self.grammar.Follow[p.name]
 | 
						|
                            for a in laheads:
 | 
						|
                                actlist.append((a, p, 'reduce using rule %d (%s)' % (p.number, p)))
 | 
						|
                                r = st_action.get(a)
 | 
						|
                                if r is not None:
 | 
						|
                                    # Whoa. Have a shift/reduce or reduce/reduce conflict
 | 
						|
                                    if r > 0:
 | 
						|
                                        # Need to decide on shift or reduce here
 | 
						|
                                        # By default we favor shifting. Need to add
 | 
						|
                                        # some precedence rules here.
 | 
						|
 | 
						|
                                        # Shift precedence comes from the token
 | 
						|
                                        sprec, slevel = Precedence.get(a, ('right', 0))
 | 
						|
 | 
						|
                                        # Reduce precedence comes from rule being reduced (p)
 | 
						|
                                        rprec, rlevel = Productions[p.number].prec
 | 
						|
 | 
						|
                                        if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')):
 | 
						|
                                            # We really need to reduce here.
 | 
						|
                                            st_action[a] = -p.number
 | 
						|
                                            st_actionp[a] = p
 | 
						|
                                            if not slevel and not rlevel:
 | 
						|
                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
 | 
						|
                                                self.sr_conflicts.append((st, a, 'reduce'))
 | 
						|
                                            Productions[p.number].reduced += 1
 | 
						|
                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
 | 
						|
                                            st_action[a] = None
 | 
						|
                                        else:
 | 
						|
                                            # Hmmm. Guess we'll keep the shift
 | 
						|
                                            if not rlevel:
 | 
						|
                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
 | 
						|
                                                self.sr_conflicts.append((st, a, 'shift'))
 | 
						|
                                    elif r < 0:
 | 
						|
                                        # Reduce/reduce conflict.   In this case, we favor the rule
 | 
						|
                                        # that was defined first in the grammar file
 | 
						|
                                        oldp = Productions[-r]
 | 
						|
                                        pp = Productions[p.number]
 | 
						|
                                        if oldp.line > pp.line:
 | 
						|
                                            st_action[a] = -p.number
 | 
						|
                                            st_actionp[a] = p
 | 
						|
                                            chosenp, rejectp = pp, oldp
 | 
						|
                                            Productions[p.number].reduced += 1
 | 
						|
                                            Productions[oldp.number].reduced -= 1
 | 
						|
                                        else:
 | 
						|
                                            chosenp, rejectp = oldp, pp
 | 
						|
                                        self.rr_conflicts.append((st, chosenp, rejectp))
 | 
						|
                                        log.info('  ! reduce/reduce conflict for %s resolved using rule %d (%s)',
 | 
						|
                                                 a, st_actionp[a].number, st_actionp[a])
 | 
						|
                                    else:
 | 
						|
                                        raise LALRError('Unknown conflict in state %d' % st)
 | 
						|
                                else:
 | 
						|
                                    st_action[a] = -p.number
 | 
						|
                                    st_actionp[a] = p
 | 
						|
                                    Productions[p.number].reduced += 1
 | 
						|
                    else:
 | 
						|
                        i = p.lr_index
 | 
						|
                        a = p.prod[i+1]       # Get symbol right after the "."
 | 
						|
                        if a in self.grammar.Terminals:
 | 
						|
                            g = self.lr0_goto(I, a)
 | 
						|
                            j = self.lr0_cidhash.get(id(g), -1)
 | 
						|
                            if j >= 0:
 | 
						|
                                # We are in a shift state
 | 
						|
                                actlist.append((a, p, 'shift and go to state %d' % j))
 | 
						|
                                r = st_action.get(a)
 | 
						|
                                if r is not None:
 | 
						|
                                    # Whoa have a shift/reduce or shift/shift conflict
 | 
						|
                                    if r > 0:
 | 
						|
                                        if r != j:
 | 
						|
                                            raise LALRError('Shift/shift conflict in state %d' % st)
 | 
						|
                                    elif r < 0:
 | 
						|
                                        # Do a precedence check.
 | 
						|
                                        #   -  if precedence of reduce rule is higher, we reduce.
 | 
						|
                                        #   -  if precedence of reduce is same and left assoc, we reduce.
 | 
						|
                                        #   -  otherwise we shift
 | 
						|
 | 
						|
                                        # Shift precedence comes from the token
 | 
						|
                                        sprec, slevel = Precedence.get(a, ('right', 0))
 | 
						|
 | 
						|
                                        # Reduce precedence comes from the rule that could have been reduced
 | 
						|
                                        rprec, rlevel = Productions[st_actionp[a].number].prec
 | 
						|
 | 
						|
                                        if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')):
 | 
						|
                                            # We decide to shift here... highest precedence to shift
 | 
						|
                                            Productions[st_actionp[a].number].reduced -= 1
 | 
						|
                                            st_action[a] = j
 | 
						|
                                            st_actionp[a] = p
 | 
						|
                                            if not rlevel:
 | 
						|
                                                log.info('  ! shift/reduce conflict for %s resolved as shift', a)
 | 
						|
                                                self.sr_conflicts.append((st, a, 'shift'))
 | 
						|
                                        elif (slevel == rlevel) and (rprec == 'nonassoc'):
 | 
						|
                                            st_action[a] = None
 | 
						|
                                        else:
 | 
						|
                                            # Hmmm. Guess we'll keep the reduce
 | 
						|
                                            if not slevel and not rlevel:
 | 
						|
                                                log.info('  ! shift/reduce conflict for %s resolved as reduce', a)
 | 
						|
                                                self.sr_conflicts.append((st, a, 'reduce'))
 | 
						|
 | 
						|
                                    else:
 | 
						|
                                        raise LALRError('Unknown conflict in state %d' % st)
 | 
						|
                                else:
 | 
						|
                                    st_action[a] = j
 | 
						|
                                    st_actionp[a] = p
 | 
						|
 | 
						|
            # Print the actions associated with each terminal
 | 
						|
            _actprint = {}
 | 
						|
            for a, p, m in actlist:
 | 
						|
                if a in st_action:
 | 
						|
                    if p is st_actionp[a]:
 | 
						|
                        log.info('    %-15s %s', a, m)
 | 
						|
                        _actprint[(a, m)] = 1
 | 
						|
            log.info('')
 | 
						|
            # Print the actions that were not used. (debugging)
 | 
						|
            not_used = 0
 | 
						|
            for a, p, m in actlist:
 | 
						|
                if a in st_action:
 | 
						|
                    if p is not st_actionp[a]:
 | 
						|
                        if not (a, m) in _actprint:
 | 
						|
                            log.debug('  ! %-15s [ %s ]', a, m)
 | 
						|
                            not_used = 1
 | 
						|
                            _actprint[(a, m)] = 1
 | 
						|
            if not_used:
 | 
						|
                log.debug('')
 | 
						|
 | 
						|
            # Construct the goto table for this state
 | 
						|
 | 
						|
            nkeys = {}
 | 
						|
            for ii in I:
 | 
						|
                for s in ii.usyms:
 | 
						|
                    if s in self.grammar.Nonterminals:
 | 
						|
                        nkeys[s] = None
 | 
						|
            for n in nkeys:
 | 
						|
                g = self.lr0_goto(I, n)
 | 
						|
                j = self.lr0_cidhash.get(id(g), -1)
 | 
						|
                if j >= 0:
 | 
						|
                    st_goto[n] = j
 | 
						|
                    log.info('    %-30s shift and go to state %d', n, j)
 | 
						|
 | 
						|
            action[st] = st_action
 | 
						|
            actionp[st] = st_actionp
 | 
						|
            goto[st] = st_goto
 | 
						|
            st += 1
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # write()
 | 
						|
    #
 | 
						|
    # This function writes the LR parsing tables to a file
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def write_table(self, tabmodule, outputdir='', signature=''):
 | 
						|
        if isinstance(tabmodule, types.ModuleType):
 | 
						|
            raise IOError("Won't overwrite existing tabmodule")
 | 
						|
 | 
						|
        basemodulename = tabmodule.split('.')[-1]
 | 
						|
        filename = os.path.join(outputdir, basemodulename) + '.py'
 | 
						|
        try:
 | 
						|
            f = open(filename, 'w')
 | 
						|
 | 
						|
            f.write('''
 | 
						|
# %s
 | 
						|
# This file is automatically generated. Do not edit.
 | 
						|
_tabversion = %r
 | 
						|
 | 
						|
_lr_method = %r
 | 
						|
 | 
						|
_lr_signature = %r
 | 
						|
    ''' % (os.path.basename(filename), __tabversion__, self.lr_method, signature))
 | 
						|
 | 
						|
            # Change smaller to 0 to go back to original tables
 | 
						|
            smaller = 1
 | 
						|
 | 
						|
            # Factor out names to try and make smaller
 | 
						|
            if smaller:
 | 
						|
                items = {}
 | 
						|
 | 
						|
                for s, nd in self.lr_action.items():
 | 
						|
                    for name, v in nd.items():
 | 
						|
                        i = items.get(name)
 | 
						|
                        if not i:
 | 
						|
                            i = ([], [])
 | 
						|
                            items[name] = i
 | 
						|
                        i[0].append(s)
 | 
						|
                        i[1].append(v)
 | 
						|
 | 
						|
                f.write('\n_lr_action_items = {')
 | 
						|
                for k, v in items.items():
 | 
						|
                    f.write('%r:([' % k)
 | 
						|
                    for i in v[0]:
 | 
						|
                        f.write('%r,' % i)
 | 
						|
                    f.write('],[')
 | 
						|
                    for i in v[1]:
 | 
						|
                        f.write('%r,' % i)
 | 
						|
 | 
						|
                    f.write(']),')
 | 
						|
                f.write('}\n')
 | 
						|
 | 
						|
                f.write('''
 | 
						|
_lr_action = {}
 | 
						|
for _k, _v in _lr_action_items.items():
 | 
						|
   for _x,_y in zip(_v[0],_v[1]):
 | 
						|
      if not _x in _lr_action:  _lr_action[_x] = {}
 | 
						|
      _lr_action[_x][_k] = _y
 | 
						|
del _lr_action_items
 | 
						|
''')
 | 
						|
 | 
						|
            else:
 | 
						|
                f.write('\n_lr_action = { ')
 | 
						|
                for k, v in self.lr_action.items():
 | 
						|
                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
 | 
						|
                f.write('}\n')
 | 
						|
 | 
						|
            if smaller:
 | 
						|
                # Factor out names to try and make smaller
 | 
						|
                items = {}
 | 
						|
 | 
						|
                for s, nd in self.lr_goto.items():
 | 
						|
                    for name, v in nd.items():
 | 
						|
                        i = items.get(name)
 | 
						|
                        if not i:
 | 
						|
                            i = ([], [])
 | 
						|
                            items[name] = i
 | 
						|
                        i[0].append(s)
 | 
						|
                        i[1].append(v)
 | 
						|
 | 
						|
                f.write('\n_lr_goto_items = {')
 | 
						|
                for k, v in items.items():
 | 
						|
                    f.write('%r:([' % k)
 | 
						|
                    for i in v[0]:
 | 
						|
                        f.write('%r,' % i)
 | 
						|
                    f.write('],[')
 | 
						|
                    for i in v[1]:
 | 
						|
                        f.write('%r,' % i)
 | 
						|
 | 
						|
                    f.write(']),')
 | 
						|
                f.write('}\n')
 | 
						|
 | 
						|
                f.write('''
 | 
						|
_lr_goto = {}
 | 
						|
for _k, _v in _lr_goto_items.items():
 | 
						|
   for _x, _y in zip(_v[0], _v[1]):
 | 
						|
       if not _x in _lr_goto: _lr_goto[_x] = {}
 | 
						|
       _lr_goto[_x][_k] = _y
 | 
						|
del _lr_goto_items
 | 
						|
''')
 | 
						|
            else:
 | 
						|
                f.write('\n_lr_goto = { ')
 | 
						|
                for k, v in self.lr_goto.items():
 | 
						|
                    f.write('(%r,%r):%r,' % (k[0], k[1], v))
 | 
						|
                f.write('}\n')
 | 
						|
 | 
						|
            # Write production table
 | 
						|
            f.write('_lr_productions = [\n')
 | 
						|
            for p in self.lr_productions:
 | 
						|
                if p.func:
 | 
						|
                    f.write('  (%r,%r,%d,%r,%r,%d),\n' % (p.str, p.name, p.len,
 | 
						|
                                                          p.func, os.path.basename(p.file), p.line))
 | 
						|
                else:
 | 
						|
                    f.write('  (%r,%r,%d,None,None,None),\n' % (str(p), p.name, p.len))
 | 
						|
            f.write(']\n')
 | 
						|
            f.close()
 | 
						|
 | 
						|
        except IOError as e:
 | 
						|
            raise
 | 
						|
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # pickle_table()
 | 
						|
    #
 | 
						|
    # This function pickles the LR parsing tables to a supplied file object
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def pickle_table(self, filename, signature=''):
 | 
						|
        try:
 | 
						|
            import cPickle as pickle
 | 
						|
        except ImportError:
 | 
						|
            import pickle
 | 
						|
        with open(filename, 'wb') as outf:
 | 
						|
            pickle.dump(__tabversion__, outf, pickle_protocol)
 | 
						|
            pickle.dump(self.lr_method, outf, pickle_protocol)
 | 
						|
            pickle.dump(signature, outf, pickle_protocol)
 | 
						|
            pickle.dump(self.lr_action, outf, pickle_protocol)
 | 
						|
            pickle.dump(self.lr_goto, outf, pickle_protocol)
 | 
						|
 | 
						|
            outp = []
 | 
						|
            for p in self.lr_productions:
 | 
						|
                if p.func:
 | 
						|
                    outp.append((p.str, p.name, p.len, p.func, os.path.basename(p.file), p.line))
 | 
						|
                else:
 | 
						|
                    outp.append((str(p), p.name, p.len, None, None, None))
 | 
						|
            pickle.dump(outp, outf, pickle_protocol)
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
#                            === INTROSPECTION ===
 | 
						|
#
 | 
						|
# The following functions and classes are used to implement the PLY
 | 
						|
# introspection features followed by the yacc() function itself.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# get_caller_module_dict()
 | 
						|
#
 | 
						|
# This function returns a dictionary containing all of the symbols defined within
 | 
						|
# a caller further down the call stack.  This is used to get the environment
 | 
						|
# associated with the yacc() call if none was provided.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
def get_caller_module_dict(levels):
 | 
						|
    f = sys._getframe(levels)
 | 
						|
    ldict = f.f_globals.copy()
 | 
						|
    if f.f_globals != f.f_locals:
 | 
						|
        ldict.update(f.f_locals)
 | 
						|
    return ldict
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# parse_grammar()
 | 
						|
#
 | 
						|
# This takes a raw grammar rule string and parses it into production data
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
def parse_grammar(doc, file, line):
 | 
						|
    grammar = []
 | 
						|
    # Split the doc string into lines
 | 
						|
    pstrings = doc.splitlines()
 | 
						|
    lastp = None
 | 
						|
    dline = line
 | 
						|
    for ps in pstrings:
 | 
						|
        dline += 1
 | 
						|
        p = ps.split()
 | 
						|
        if not p:
 | 
						|
            continue
 | 
						|
        try:
 | 
						|
            if p[0] == '|':
 | 
						|
                # This is a continuation of a previous rule
 | 
						|
                if not lastp:
 | 
						|
                    raise SyntaxError("%s:%d: Misplaced '|'" % (file, dline))
 | 
						|
                prodname = lastp
 | 
						|
                syms = p[1:]
 | 
						|
            else:
 | 
						|
                prodname = p[0]
 | 
						|
                lastp = prodname
 | 
						|
                syms   = p[2:]
 | 
						|
                assign = p[1]
 | 
						|
                if assign != ':' and assign != '::=':
 | 
						|
                    raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file, dline))
 | 
						|
 | 
						|
            grammar.append((file, dline, prodname, syms))
 | 
						|
        except SyntaxError:
 | 
						|
            raise
 | 
						|
        except Exception:
 | 
						|
            raise SyntaxError('%s:%d: Syntax error in rule %r' % (file, dline, ps.strip()))
 | 
						|
 | 
						|
    return grammar
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# ParserReflect()
 | 
						|
#
 | 
						|
# This class represents information extracted for building a parser including
 | 
						|
# start symbol, error function, tokens, precedence list, action functions,
 | 
						|
# etc.
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
class ParserReflect(object):
 | 
						|
    def __init__(self, pdict, log=None):
 | 
						|
        self.pdict      = pdict
 | 
						|
        self.start      = None
 | 
						|
        self.error_func = None
 | 
						|
        self.tokens     = None
 | 
						|
        self.modules    = set()
 | 
						|
        self.grammar    = []
 | 
						|
        self.error      = False
 | 
						|
 | 
						|
        if log is None:
 | 
						|
            self.log = PlyLogger(sys.stderr)
 | 
						|
        else:
 | 
						|
            self.log = log
 | 
						|
 | 
						|
    # Get all of the basic information
 | 
						|
    def get_all(self):
 | 
						|
        self.get_start()
 | 
						|
        self.get_error_func()
 | 
						|
        self.get_tokens()
 | 
						|
        self.get_precedence()
 | 
						|
        self.get_pfunctions()
 | 
						|
 | 
						|
    # Validate all of the information
 | 
						|
    def validate_all(self):
 | 
						|
        self.validate_start()
 | 
						|
        self.validate_error_func()
 | 
						|
        self.validate_tokens()
 | 
						|
        self.validate_precedence()
 | 
						|
        self.validate_pfunctions()
 | 
						|
        self.validate_modules()
 | 
						|
        return self.error
 | 
						|
 | 
						|
    # Compute a signature over the grammar
 | 
						|
    def signature(self):
 | 
						|
        parts = []
 | 
						|
        try:
 | 
						|
            if self.start:
 | 
						|
                parts.append(self.start)
 | 
						|
            if self.prec:
 | 
						|
                parts.append(''.join([''.join(p) for p in self.prec]))
 | 
						|
            if self.tokens:
 | 
						|
                parts.append(' '.join(self.tokens))
 | 
						|
            for f in self.pfuncs:
 | 
						|
                if f[3]:
 | 
						|
                    parts.append(f[3])
 | 
						|
        except (TypeError, ValueError):
 | 
						|
            pass
 | 
						|
        return ''.join(parts)
 | 
						|
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
    # validate_modules()
 | 
						|
    #
 | 
						|
    # This method checks to see if there are duplicated p_rulename() functions
 | 
						|
    # in the parser module file.  Without this function, it is really easy for
 | 
						|
    # users to make mistakes by cutting and pasting code fragments (and it's a real
 | 
						|
    # bugger to try and figure out why the resulting parser doesn't work).  Therefore,
 | 
						|
    # we just do a little regular expression pattern matching of def statements
 | 
						|
    # to try and detect duplicates.
 | 
						|
    # -----------------------------------------------------------------------------
 | 
						|
 | 
						|
    def validate_modules(self):
 | 
						|
        # Match def p_funcname(
 | 
						|
        fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(')
 | 
						|
 | 
						|
        for module in self.modules:
 | 
						|
            try:
 | 
						|
                lines, linen = inspect.getsourcelines(module)
 | 
						|
            except IOError:
 | 
						|
                continue
 | 
						|
 | 
						|
            counthash = {}
 | 
						|
            for linen, line in enumerate(lines):
 | 
						|
                linen += 1
 | 
						|
                m = fre.match(line)
 | 
						|
                if m:
 | 
						|
                    name = m.group(1)
 | 
						|
                    prev = counthash.get(name)
 | 
						|
                    if not prev:
 | 
						|
                        counthash[name] = linen
 | 
						|
                    else:
 | 
						|
                        filename = inspect.getsourcefile(module)
 | 
						|
                        self.log.warning('%s:%d: Function %s redefined. Previously defined on line %d',
 | 
						|
                                         filename, linen, name, prev)
 | 
						|
 | 
						|
    # Get the start symbol
 | 
						|
    def get_start(self):
 | 
						|
        self.start = self.pdict.get('start')
 | 
						|
 | 
						|
    # Validate the start symbol
 | 
						|
    def validate_start(self):
 | 
						|
        if self.start is not None:
 | 
						|
            if not isinstance(self.start, string_types):
 | 
						|
                self.log.error("'start' must be a string")
 | 
						|
 | 
						|
    # Look for error handler
 | 
						|
    def get_error_func(self):
 | 
						|
        self.error_func = self.pdict.get('p_error')
 | 
						|
 | 
						|
    # Validate the error function
 | 
						|
    def validate_error_func(self):
 | 
						|
        if self.error_func:
 | 
						|
            if isinstance(self.error_func, types.FunctionType):
 | 
						|
                ismethod = 0
 | 
						|
            elif isinstance(self.error_func, types.MethodType):
 | 
						|
                ismethod = 1
 | 
						|
            else:
 | 
						|
                self.log.error("'p_error' defined, but is not a function or method")
 | 
						|
                self.error = True
 | 
						|
                return
 | 
						|
 | 
						|
            eline = self.error_func.__code__.co_firstlineno
 | 
						|
            efile = self.error_func.__code__.co_filename
 | 
						|
            module = inspect.getmodule(self.error_func)
 | 
						|
            self.modules.add(module)
 | 
						|
 | 
						|
            argcount = self.error_func.__code__.co_argcount - ismethod
 | 
						|
            if argcount != 1:
 | 
						|
                self.log.error('%s:%d: p_error() requires 1 argument', efile, eline)
 | 
						|
                self.error = True
 | 
						|
 | 
						|
    # Get the tokens map
 | 
						|
    def get_tokens(self):
 | 
						|
        tokens = self.pdict.get('tokens')
 | 
						|
        if not tokens:
 | 
						|
            self.log.error('No token list is defined')
 | 
						|
            self.error = True
 | 
						|
            return
 | 
						|
 | 
						|
        if not isinstance(tokens, (list, tuple)):
 | 
						|
            self.log.error('tokens must be a list or tuple')
 | 
						|
            self.error = True
 | 
						|
            return
 | 
						|
 | 
						|
        if not tokens:
 | 
						|
            self.log.error('tokens is empty')
 | 
						|
            self.error = True
 | 
						|
            return
 | 
						|
 | 
						|
        self.tokens = tokens
 | 
						|
 | 
						|
    # Validate the tokens
 | 
						|
    def validate_tokens(self):
 | 
						|
        # Validate the tokens.
 | 
						|
        if 'error' in self.tokens:
 | 
						|
            self.log.error("Illegal token name 'error'. Is a reserved word")
 | 
						|
            self.error = True
 | 
						|
            return
 | 
						|
 | 
						|
        terminals = set()
 | 
						|
        for n in self.tokens:
 | 
						|
            if n in terminals:
 | 
						|
                self.log.warning('Token %r multiply defined', n)
 | 
						|
            terminals.add(n)
 | 
						|
 | 
						|
    # Get the precedence map (if any)
 | 
						|
    def get_precedence(self):
 | 
						|
        self.prec = self.pdict.get('precedence')
 | 
						|
 | 
						|
    # Validate and parse the precedence map
 | 
						|
    def validate_precedence(self):
 | 
						|
        preclist = []
 | 
						|
        if self.prec:
 | 
						|
            if not isinstance(self.prec, (list, tuple)):
 | 
						|
                self.log.error('precedence must be a list or tuple')
 | 
						|
                self.error = True
 | 
						|
                return
 | 
						|
            for level, p in enumerate(self.prec):
 | 
						|
                if not isinstance(p, (list, tuple)):
 | 
						|
                    self.log.error('Bad precedence table')
 | 
						|
                    self.error = True
 | 
						|
                    return
 | 
						|
 | 
						|
                if len(p) < 2:
 | 
						|
                    self.log.error('Malformed precedence entry %s. Must be (assoc, term, ..., term)', p)
 | 
						|
                    self.error = True
 | 
						|
                    return
 | 
						|
                assoc = p[0]
 | 
						|
                if not isinstance(assoc, string_types):
 | 
						|
                    self.log.error('precedence associativity must be a string')
 | 
						|
                    self.error = True
 | 
						|
                    return
 | 
						|
                for term in p[1:]:
 | 
						|
                    if not isinstance(term, string_types):
 | 
						|
                        self.log.error('precedence items must be strings')
 | 
						|
                        self.error = True
 | 
						|
                        return
 | 
						|
                    preclist.append((term, assoc, level+1))
 | 
						|
        self.preclist = preclist
 | 
						|
 | 
						|
    # Get all p_functions from the grammar
 | 
						|
    def get_pfunctions(self):
 | 
						|
        p_functions = []
 | 
						|
        for name, item in self.pdict.items():
 | 
						|
            if not name.startswith('p_') or name == 'p_error':
 | 
						|
                continue
 | 
						|
            if isinstance(item, (types.FunctionType, types.MethodType)):
 | 
						|
                line = getattr(item, 'co_firstlineno', item.__code__.co_firstlineno)
 | 
						|
                module = inspect.getmodule(item)
 | 
						|
                p_functions.append((line, module, name, item.__doc__))
 | 
						|
 | 
						|
        # Sort all of the actions by line number; make sure to stringify
 | 
						|
        # modules to make them sortable, since `line` may not uniquely sort all
 | 
						|
        # p functions
 | 
						|
        p_functions.sort(key=lambda p_function: (
 | 
						|
            p_function[0],
 | 
						|
            str(p_function[1]),
 | 
						|
            p_function[2],
 | 
						|
            p_function[3]))
 | 
						|
        self.pfuncs = p_functions
 | 
						|
 | 
						|
    # Validate all of the p_functions
 | 
						|
    def validate_pfunctions(self):
 | 
						|
        grammar = []
 | 
						|
        # Check for non-empty symbols
 | 
						|
        if len(self.pfuncs) == 0:
 | 
						|
            self.log.error('no rules of the form p_rulename are defined')
 | 
						|
            self.error = True
 | 
						|
            return
 | 
						|
 | 
						|
        for line, module, name, doc in self.pfuncs:
 | 
						|
            file = inspect.getsourcefile(module)
 | 
						|
            func = self.pdict[name]
 | 
						|
            if isinstance(func, types.MethodType):
 | 
						|
                reqargs = 2
 | 
						|
            else:
 | 
						|
                reqargs = 1
 | 
						|
            if func.__code__.co_argcount > reqargs:
 | 
						|
                self.log.error('%s:%d: Rule %r has too many arguments', file, line, func.__name__)
 | 
						|
                self.error = True
 | 
						|
            elif func.__code__.co_argcount < reqargs:
 | 
						|
                self.log.error('%s:%d: Rule %r requires an argument', file, line, func.__name__)
 | 
						|
                self.error = True
 | 
						|
            elif not func.__doc__:
 | 
						|
                self.log.warning('%s:%d: No documentation string specified in function %r (ignored)',
 | 
						|
                                 file, line, func.__name__)
 | 
						|
            else:
 | 
						|
                try:
 | 
						|
                    parsed_g = parse_grammar(doc, file, line)
 | 
						|
                    for g in parsed_g:
 | 
						|
                        grammar.append((name, g))
 | 
						|
                except SyntaxError as e:
 | 
						|
                    self.log.error(str(e))
 | 
						|
                    self.error = True
 | 
						|
 | 
						|
                # Looks like a valid grammar rule
 | 
						|
                # Mark the file in which defined.
 | 
						|
                self.modules.add(module)
 | 
						|
 | 
						|
        # Secondary validation step that looks for p_ definitions that are not functions
 | 
						|
        # or functions that look like they might be grammar rules.
 | 
						|
 | 
						|
        for n, v in self.pdict.items():
 | 
						|
            if n.startswith('p_') and isinstance(v, (types.FunctionType, types.MethodType)):
 | 
						|
                continue
 | 
						|
            if n.startswith('t_'):
 | 
						|
                continue
 | 
						|
            if n.startswith('p_') and n != 'p_error':
 | 
						|
                self.log.warning('%r not defined as a function', n)
 | 
						|
            if ((isinstance(v, types.FunctionType) and v.__code__.co_argcount == 1) or
 | 
						|
                   (isinstance(v, types.MethodType) and v.__func__.__code__.co_argcount == 2)):
 | 
						|
                if v.__doc__:
 | 
						|
                    try:
 | 
						|
                        doc = v.__doc__.split(' ')
 | 
						|
                        if doc[1] == ':':
 | 
						|
                            self.log.warning('%s:%d: Possible grammar rule %r defined without p_ prefix',
 | 
						|
                                             v.__code__.co_filename, v.__code__.co_firstlineno, n)
 | 
						|
                    except IndexError:
 | 
						|
                        pass
 | 
						|
 | 
						|
        self.grammar = grammar
 | 
						|
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
# yacc(module)
 | 
						|
#
 | 
						|
# Build a parser
 | 
						|
# -----------------------------------------------------------------------------
 | 
						|
 | 
						|
def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None,
 | 
						|
         check_recursion=True, optimize=False, write_tables=True, debugfile=debug_file,
 | 
						|
         outputdir=None, debuglog=None, errorlog=None, picklefile=None):
 | 
						|
 | 
						|
    if tabmodule is None:
 | 
						|
        tabmodule = tab_module
 | 
						|
 | 
						|
    # Reference to the parsing method of the last built parser
 | 
						|
    global parse
 | 
						|
 | 
						|
    # If pickling is enabled, table files are not created
 | 
						|
    if picklefile:
 | 
						|
        write_tables = 0
 | 
						|
 | 
						|
    if errorlog is None:
 | 
						|
        errorlog = PlyLogger(sys.stderr)
 | 
						|
 | 
						|
    # Get the module dictionary used for the parser
 | 
						|
    if module:
 | 
						|
        _items = [(k, getattr(module, k)) for k in dir(module)]
 | 
						|
        pdict = dict(_items)
 | 
						|
        # If no __file__ attribute is available, try to obtain it from the __module__ instead
 | 
						|
        if '__file__' not in pdict:
 | 
						|
            pdict['__file__'] = sys.modules[pdict['__module__']].__file__
 | 
						|
    else:
 | 
						|
        pdict = get_caller_module_dict(2)
 | 
						|
 | 
						|
    if outputdir is None:
 | 
						|
        # If no output directory is set, the location of the output files
 | 
						|
        # is determined according to the following rules:
 | 
						|
        #     - If tabmodule specifies a package, files go into that package directory
 | 
						|
        #     - Otherwise, files go in the same directory as the specifying module
 | 
						|
        if isinstance(tabmodule, types.ModuleType):
 | 
						|
            srcfile = tabmodule.__file__
 | 
						|
        else:
 | 
						|
            if '.' not in tabmodule:
 | 
						|
                srcfile = pdict['__file__']
 | 
						|
            else:
 | 
						|
                parts = tabmodule.split('.')
 | 
						|
                pkgname = '.'.join(parts[:-1])
 | 
						|
                exec('import %s' % pkgname)
 | 
						|
                srcfile = getattr(sys.modules[pkgname], '__file__', '')
 | 
						|
        outputdir = os.path.dirname(srcfile)
 | 
						|
 | 
						|
    # Determine if the module is package of a package or not.
 | 
						|
    # If so, fix the tabmodule setting so that tables load correctly
 | 
						|
    pkg = pdict.get('__package__')
 | 
						|
    if pkg and isinstance(tabmodule, str):
 | 
						|
        if '.' not in tabmodule:
 | 
						|
            tabmodule = pkg + '.' + tabmodule
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    # Set start symbol if it's specified directly using an argument
 | 
						|
    if start is not None:
 | 
						|
        pdict['start'] = start
 | 
						|
 | 
						|
    # Collect parser information from the dictionary
 | 
						|
    pinfo = ParserReflect(pdict, log=errorlog)
 | 
						|
    pinfo.get_all()
 | 
						|
 | 
						|
    if pinfo.error:
 | 
						|
        raise YaccError('Unable to build parser')
 | 
						|
 | 
						|
    # Check signature against table files (if any)
 | 
						|
    signature = pinfo.signature()
 | 
						|
 | 
						|
    # Read the tables
 | 
						|
    try:
 | 
						|
        lr = LRTable()
 | 
						|
        if picklefile:
 | 
						|
            read_signature = lr.read_pickle(picklefile)
 | 
						|
        else:
 | 
						|
            read_signature = lr.read_table(tabmodule)
 | 
						|
        if optimize or (read_signature == signature):
 | 
						|
            try:
 | 
						|
                lr.bind_callables(pinfo.pdict)
 | 
						|
                parser = LRParser(lr, pinfo.error_func)
 | 
						|
                parse = parser.parse
 | 
						|
                return parser
 | 
						|
            except Exception as e:
 | 
						|
                errorlog.warning('There was a problem loading the table file: %r', e)
 | 
						|
    except VersionError as e:
 | 
						|
        errorlog.warning(str(e))
 | 
						|
    except ImportError:
 | 
						|
        pass
 | 
						|
 | 
						|
    if debuglog is None:
 | 
						|
        if debug:
 | 
						|
            try:
 | 
						|
                debuglog = PlyLogger(open(os.path.join(outputdir, debugfile), 'w'))
 | 
						|
            except IOError as e:
 | 
						|
                errorlog.warning("Couldn't open %r. %s" % (debugfile, e))
 | 
						|
                debuglog = NullLogger()
 | 
						|
        else:
 | 
						|
            debuglog = NullLogger()
 | 
						|
 | 
						|
    debuglog.info('Created by PLY version %s (http://www.dabeaz.com/ply)', __version__)
 | 
						|
 | 
						|
    errors = False
 | 
						|
 | 
						|
    # Validate the parser information
 | 
						|
    if pinfo.validate_all():
 | 
						|
        raise YaccError('Unable to build parser')
 | 
						|
 | 
						|
    if not pinfo.error_func:
 | 
						|
        errorlog.warning('no p_error() function is defined')
 | 
						|
 | 
						|
    # Create a grammar object
 | 
						|
    grammar = Grammar(pinfo.tokens)
 | 
						|
 | 
						|
    # Set precedence level for terminals
 | 
						|
    for term, assoc, level in pinfo.preclist:
 | 
						|
        try:
 | 
						|
            grammar.set_precedence(term, assoc, level)
 | 
						|
        except GrammarError as e:
 | 
						|
            errorlog.warning('%s', e)
 | 
						|
 | 
						|
    # Add productions to the grammar
 | 
						|
    for funcname, gram in pinfo.grammar:
 | 
						|
        file, line, prodname, syms = gram
 | 
						|
        try:
 | 
						|
            grammar.add_production(prodname, syms, funcname, file, line)
 | 
						|
        except GrammarError as e:
 | 
						|
            errorlog.error('%s', e)
 | 
						|
            errors = True
 | 
						|
 | 
						|
    # Set the grammar start symbols
 | 
						|
    try:
 | 
						|
        if start is None:
 | 
						|
            grammar.set_start(pinfo.start)
 | 
						|
        else:
 | 
						|
            grammar.set_start(start)
 | 
						|
    except GrammarError as e:
 | 
						|
        errorlog.error(str(e))
 | 
						|
        errors = True
 | 
						|
 | 
						|
    if errors:
 | 
						|
        raise YaccError('Unable to build parser')
 | 
						|
 | 
						|
    # Verify the grammar structure
 | 
						|
    undefined_symbols = grammar.undefined_symbols()
 | 
						|
    for sym, prod in undefined_symbols:
 | 
						|
        errorlog.error('%s:%d: Symbol %r used, but not defined as a token or a rule', prod.file, prod.line, sym)
 | 
						|
        errors = True
 | 
						|
 | 
						|
    unused_terminals = grammar.unused_terminals()
 | 
						|
    if unused_terminals:
 | 
						|
        debuglog.info('')
 | 
						|
        debuglog.info('Unused terminals:')
 | 
						|
        debuglog.info('')
 | 
						|
        for term in unused_terminals:
 | 
						|
            errorlog.warning('Token %r defined, but not used', term)
 | 
						|
            debuglog.info('    %s', term)
 | 
						|
 | 
						|
    # Print out all productions to the debug log
 | 
						|
    if debug:
 | 
						|
        debuglog.info('')
 | 
						|
        debuglog.info('Grammar')
 | 
						|
        debuglog.info('')
 | 
						|
        for n, p in enumerate(grammar.Productions):
 | 
						|
            debuglog.info('Rule %-5d %s', n, p)
 | 
						|
 | 
						|
    # Find unused non-terminals
 | 
						|
    unused_rules = grammar.unused_rules()
 | 
						|
    for prod in unused_rules:
 | 
						|
        errorlog.warning('%s:%d: Rule %r defined, but not used', prod.file, prod.line, prod.name)
 | 
						|
 | 
						|
    if len(unused_terminals) == 1:
 | 
						|
        errorlog.warning('There is 1 unused token')
 | 
						|
    if len(unused_terminals) > 1:
 | 
						|
        errorlog.warning('There are %d unused tokens', len(unused_terminals))
 | 
						|
 | 
						|
    if len(unused_rules) == 1:
 | 
						|
        errorlog.warning('There is 1 unused rule')
 | 
						|
    if len(unused_rules) > 1:
 | 
						|
        errorlog.warning('There are %d unused rules', len(unused_rules))
 | 
						|
 | 
						|
    if debug:
 | 
						|
        debuglog.info('')
 | 
						|
        debuglog.info('Terminals, with rules where they appear')
 | 
						|
        debuglog.info('')
 | 
						|
        terms = list(grammar.Terminals)
 | 
						|
        terms.sort()
 | 
						|
        for term in terms:
 | 
						|
            debuglog.info('%-20s : %s', term, ' '.join([str(s) for s in grammar.Terminals[term]]))
 | 
						|
 | 
						|
        debuglog.info('')
 | 
						|
        debuglog.info('Nonterminals, with rules where they appear')
 | 
						|
        debuglog.info('')
 | 
						|
        nonterms = list(grammar.Nonterminals)
 | 
						|
        nonterms.sort()
 | 
						|
        for nonterm in nonterms:
 | 
						|
            debuglog.info('%-20s : %s', nonterm, ' '.join([str(s) for s in grammar.Nonterminals[nonterm]]))
 | 
						|
        debuglog.info('')
 | 
						|
 | 
						|
    if check_recursion:
 | 
						|
        unreachable = grammar.find_unreachable()
 | 
						|
        for u in unreachable:
 | 
						|
            errorlog.warning('Symbol %r is unreachable', u)
 | 
						|
 | 
						|
        infinite = grammar.infinite_cycles()
 | 
						|
        for inf in infinite:
 | 
						|
            errorlog.error('Infinite recursion detected for symbol %r', inf)
 | 
						|
            errors = True
 | 
						|
 | 
						|
    unused_prec = grammar.unused_precedence()
 | 
						|
    for term, assoc in unused_prec:
 | 
						|
        errorlog.error('Precedence rule %r defined for unknown symbol %r', assoc, term)
 | 
						|
        errors = True
 | 
						|
 | 
						|
    if errors:
 | 
						|
        raise YaccError('Unable to build parser')
 | 
						|
 | 
						|
    # Run the LRGeneratedTable on the grammar
 | 
						|
    if debug:
 | 
						|
        errorlog.debug('Generating %s tables', method)
 | 
						|
 | 
						|
    lr = LRGeneratedTable(grammar, method, debuglog)
 | 
						|
 | 
						|
    if debug:
 | 
						|
        num_sr = len(lr.sr_conflicts)
 | 
						|
 | 
						|
        # Report shift/reduce and reduce/reduce conflicts
 | 
						|
        if num_sr == 1:
 | 
						|
            errorlog.warning('1 shift/reduce conflict')
 | 
						|
        elif num_sr > 1:
 | 
						|
            errorlog.warning('%d shift/reduce conflicts', num_sr)
 | 
						|
 | 
						|
        num_rr = len(lr.rr_conflicts)
 | 
						|
        if num_rr == 1:
 | 
						|
            errorlog.warning('1 reduce/reduce conflict')
 | 
						|
        elif num_rr > 1:
 | 
						|
            errorlog.warning('%d reduce/reduce conflicts', num_rr)
 | 
						|
 | 
						|
    # Write out conflicts to the output file
 | 
						|
    if debug and (lr.sr_conflicts or lr.rr_conflicts):
 | 
						|
        debuglog.warning('')
 | 
						|
        debuglog.warning('Conflicts:')
 | 
						|
        debuglog.warning('')
 | 
						|
 | 
						|
        for state, tok, resolution in lr.sr_conflicts:
 | 
						|
            debuglog.warning('shift/reduce conflict for %s in state %d resolved as %s',  tok, state, resolution)
 | 
						|
 | 
						|
        already_reported = set()
 | 
						|
        for state, rule, rejected in lr.rr_conflicts:
 | 
						|
            if (state, id(rule), id(rejected)) in already_reported:
 | 
						|
                continue
 | 
						|
            debuglog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
 | 
						|
            debuglog.warning('rejected rule (%s) in state %d', rejected, state)
 | 
						|
            errorlog.warning('reduce/reduce conflict in state %d resolved using rule (%s)', state, rule)
 | 
						|
            errorlog.warning('rejected rule (%s) in state %d', rejected, state)
 | 
						|
            already_reported.add((state, id(rule), id(rejected)))
 | 
						|
 | 
						|
        warned_never = []
 | 
						|
        for state, rule, rejected in lr.rr_conflicts:
 | 
						|
            if not rejected.reduced and (rejected not in warned_never):
 | 
						|
                debuglog.warning('Rule (%s) is never reduced', rejected)
 | 
						|
                errorlog.warning('Rule (%s) is never reduced', rejected)
 | 
						|
                warned_never.append(rejected)
 | 
						|
 | 
						|
    # Write the table file if requested
 | 
						|
    if write_tables:
 | 
						|
        try:
 | 
						|
            lr.write_table(tabmodule, outputdir, signature)
 | 
						|
        except IOError as e:
 | 
						|
            errorlog.warning("Couldn't create %r. %s" % (tabmodule, e))
 | 
						|
 | 
						|
    # Write a pickled version of the tables
 | 
						|
    if picklefile:
 | 
						|
        try:
 | 
						|
            lr.pickle_table(picklefile, signature)
 | 
						|
        except IOError as e:
 | 
						|
            errorlog.warning("Couldn't create %r. %s" % (picklefile, e))
 | 
						|
 | 
						|
    # Build the parser
 | 
						|
    lr.bind_callables(pinfo.pdict)
 | 
						|
    parser = LRParser(lr, pinfo.error_func)
 | 
						|
 | 
						|
    parse = parser.parse
 | 
						|
    return parser
 |