diff options
| author | Chris Larson <chris_larson@mentor.com> | 2010-08-02 13:42:23 -0700 |
|---|---|---|
| committer | Richard Purdie <rpurdie@linux.intel.com> | 2010-08-03 14:06:07 +0100 |
| commit | 191a2883492841f30bbc21ab7bf4e4a0810d0760 (patch) | |
| tree | f91e66f57380a47ea31e95bac28a93dd1e9e6370 /bitbake/lib/ply | |
| parent | e6b6767369f6d0caa7a9efe32ccd4ed514bb3148 (diff) | |
| download | poky-191a2883492841f30bbc21ab7bf4e4a0810d0760.tar.gz | |
Add pysh, ply, and codegen to lib/ to prepare for future work
(Bitbake rev: d0a6e9c5c1887a885e0e73eba264ca66801f5ed0)
Signed-off-by: Chris Larson <chris_larson@mentor.com>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'bitbake/lib/ply')
| -rw-r--r-- | bitbake/lib/ply/__init__.py | 4 | ||||
| -rw-r--r-- | bitbake/lib/ply/lex.py | 1058 | ||||
| -rw-r--r-- | bitbake/lib/ply/yacc.py | 3276 |
3 files changed, 4338 insertions, 0 deletions
diff --git a/bitbake/lib/ply/__init__.py b/bitbake/lib/ply/__init__.py new file mode 100644 index 0000000000..853a985542 --- /dev/null +++ b/bitbake/lib/ply/__init__.py | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | # PLY package | ||
| 2 | # Author: David Beazley (dave@dabeaz.com) | ||
| 3 | |||
| 4 | __all__ = ['lex','yacc'] | ||
diff --git a/bitbake/lib/ply/lex.py b/bitbake/lib/ply/lex.py new file mode 100644 index 0000000000..267ec100fc --- /dev/null +++ b/bitbake/lib/ply/lex.py | |||
| @@ -0,0 +1,1058 @@ | |||
| 1 | # ----------------------------------------------------------------------------- | ||
| 2 | # ply: lex.py | ||
| 3 | # | ||
| 4 | # Copyright (C) 2001-2009, | ||
| 5 | # David M. Beazley (Dabeaz LLC) | ||
| 6 | # All rights reserved. | ||
| 7 | # | ||
| 8 | # Redistribution and use in source and binary forms, with or without | ||
| 9 | # modification, are permitted provided that the following conditions are | ||
| 10 | # met: | ||
| 11 | # | ||
| 12 | # * Redistributions of source code must retain the above copyright notice, | ||
| 13 | # this list of conditions and the following disclaimer. | ||
| 14 | # * Redistributions in binary form must reproduce the above copyright notice, | ||
| 15 | # this list of conditions and the following disclaimer in the documentation | ||
| 16 | # and/or other materials provided with the distribution. | ||
| 17 | # * Neither the name of the David Beazley or Dabeaz LLC may be used to | ||
| 18 | # endorse or promote products derived from this software without | ||
| 19 | # specific prior written permission. | ||
| 20 | # | ||
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 24 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 25 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 26 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 27 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 28 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 29 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 30 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 31 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 32 | # ----------------------------------------------------------------------------- | ||
| 33 | |||
| 34 | __version__ = "3.3" | ||
| 35 | __tabversion__ = "3.2" # Version of table file used | ||
| 36 | |||
| 37 | import re, sys, types, copy, os | ||
| 38 | |||
| 39 | # This tuple contains known string types | ||
| 40 | try: | ||
| 41 | # Python 2.6 | ||
| 42 | StringTypes = (types.StringType, types.UnicodeType) | ||
| 43 | except AttributeError: | ||
| 44 | # Python 3.0 | ||
| 45 | StringTypes = (str, bytes) | ||
| 46 | |||
| 47 | # Extract the code attribute of a function. Different implementations | ||
| 48 | # are for Python 2/3 compatibility. | ||
| 49 | |||
| 50 | if sys.version_info[0] < 3: | ||
| 51 | def func_code(f): | ||
| 52 | return f.func_code | ||
| 53 | else: | ||
| 54 | def func_code(f): | ||
| 55 | return f.__code__ | ||
| 56 | |||
| 57 | # This regular expression is used to match valid token names | ||
| 58 | _is_identifier = re.compile(r'^[a-zA-Z0-9_]+$') | ||
| 59 | |||
| 60 | # Exception thrown when invalid token encountered and no default error | ||
| 61 | # handler is defined. | ||
| 62 | |||
| 63 | class LexError(Exception): | ||
| 64 | def __init__(self,message,s): | ||
| 65 | self.args = (message,) | ||
| 66 | self.text = s | ||
| 67 | |||
| 68 | # Token class. This class is used to represent the tokens produced. | ||
| 69 | class LexToken(object): | ||
| 70 | def __str__(self): | ||
| 71 | return "LexToken(%s,%r,%d,%d)" % (self.type,self.value,self.lineno,self.lexpos) | ||
| 72 | def __repr__(self): | ||
| 73 | return str(self) | ||
| 74 | |||
| 75 | # This object is a stand-in for a logging object created by the | ||
| 76 | # logging module. | ||
| 77 | |||
| 78 | class PlyLogger(object): | ||
| 79 | def __init__(self,f): | ||
| 80 | self.f = f | ||
| 81 | def critical(self,msg,*args,**kwargs): | ||
| 82 | self.f.write((msg % args) + "\n") | ||
| 83 | |||
| 84 | def warning(self,msg,*args,**kwargs): | ||
| 85 | self.f.write("WARNING: "+ (msg % args) + "\n") | ||
| 86 | |||
| 87 | def error(self,msg,*args,**kwargs): | ||
| 88 | self.f.write("ERROR: " + (msg % args) + "\n") | ||
| 89 | |||
| 90 | info = critical | ||
| 91 | debug = critical | ||
| 92 | |||
| 93 | # Null logger is used when no output is generated. Does nothing. | ||
| 94 | class NullLogger(object): | ||
| 95 | def __getattribute__(self,name): | ||
| 96 | return self | ||
| 97 | def __call__(self,*args,**kwargs): | ||
| 98 | return self | ||
| 99 | |||
| 100 | # ----------------------------------------------------------------------------- | ||
| 101 | # === Lexing Engine === | ||
| 102 | # | ||
| 103 | # The following Lexer class implements the lexer runtime. There are only | ||
| 104 | # a few public methods and attributes: | ||
| 105 | # | ||
| 106 | # input() - Store a new string in the lexer | ||
| 107 | # token() - Get the next token | ||
| 108 | # clone() - Clone the lexer | ||
| 109 | # | ||
| 110 | # lineno - Current line number | ||
| 111 | # lexpos - Current position in the input string | ||
| 112 | # ----------------------------------------------------------------------------- | ||
| 113 | |||
| 114 | class Lexer: | ||
| 115 | def __init__(self): | ||
| 116 | self.lexre = None # Master regular expression. This is a list of | ||
| 117 | # tuples (re,findex) where re is a compiled | ||
| 118 | # regular expression and findex is a list | ||
| 119 | # mapping regex group numbers to rules | ||
| 120 | self.lexretext = None # Current regular expression strings | ||
| 121 | self.lexstatere = {} # Dictionary mapping lexer states to master regexs | ||
| 122 | self.lexstateretext = {} # Dictionary mapping lexer states to regex strings | ||
| 123 | self.lexstaterenames = {} # Dictionary mapping lexer states to symbol names | ||
| 124 | self.lexstate = "INITIAL" # Current lexer state | ||
| 125 | self.lexstatestack = [] # Stack of lexer states | ||
| 126 | self.lexstateinfo = None # State information | ||
| 127 | self.lexstateignore = {} # Dictionary of ignored characters for each state | ||
| 128 | self.lexstateerrorf = {} # Dictionary of error functions for each state | ||
| 129 | self.lexreflags = 0 # Optional re compile flags | ||
| 130 | self.lexdata = None # Actual input data (as a string) | ||
| 131 | self.lexpos = 0 # Current position in input text | ||
| 132 | self.lexlen = 0 # Length of the input text | ||
| 133 | self.lexerrorf = None # Error rule (if any) | ||
| 134 | self.lextokens = None # List of valid tokens | ||
| 135 | self.lexignore = "" # Ignored characters | ||
| 136 | self.lexliterals = "" # Literal characters that can be passed through | ||
| 137 | self.lexmodule = None # Module | ||
| 138 | self.lineno = 1 # Current line number | ||
| 139 | self.lexoptimize = 0 # Optimized mode | ||
| 140 | |||
| 141 | def clone(self,object=None): | ||
| 142 | c = copy.copy(self) | ||
| 143 | |||
| 144 | # If the object parameter has been supplied, it means we are attaching the | ||
| 145 | # lexer to a new object. In this case, we have to rebind all methods in | ||
| 146 | # the lexstatere and lexstateerrorf tables. | ||
| 147 | |||
| 148 | if object: | ||
| 149 | newtab = { } | ||
| 150 | for key, ritem in self.lexstatere.items(): | ||
| 151 | newre = [] | ||
| 152 | for cre, findex in ritem: | ||
| 153 | newfindex = [] | ||
| 154 | for f in findex: | ||
| 155 | if not f or not f[0]: | ||
| 156 | newfindex.append(f) | ||
| 157 | continue | ||
| 158 | newfindex.append((getattr(object,f[0].__name__),f[1])) | ||
| 159 | newre.append((cre,newfindex)) | ||
| 160 | newtab[key] = newre | ||
| 161 | c.lexstatere = newtab | ||
| 162 | c.lexstateerrorf = { } | ||
| 163 | for key, ef in self.lexstateerrorf.items(): | ||
| 164 | c.lexstateerrorf[key] = getattr(object,ef.__name__) | ||
| 165 | c.lexmodule = object | ||
| 166 | return c | ||
| 167 | |||
| 168 | # ------------------------------------------------------------ | ||
| 169 | # writetab() - Write lexer information to a table file | ||
| 170 | # ------------------------------------------------------------ | ||
| 171 | def writetab(self,tabfile,outputdir=""): | ||
| 172 | if isinstance(tabfile,types.ModuleType): | ||
| 173 | return | ||
| 174 | basetabfilename = tabfile.split(".")[-1] | ||
| 175 | filename = os.path.join(outputdir,basetabfilename)+".py" | ||
| 176 | tf = open(filename,"w") | ||
| 177 | tf.write("# %s.py. This file automatically created by PLY (version %s). Don't edit!\n" % (tabfile,__version__)) | ||
| 178 | tf.write("_tabversion = %s\n" % repr(__version__)) | ||
| 179 | tf.write("_lextokens = %s\n" % repr(self.lextokens)) | ||
| 180 | tf.write("_lexreflags = %s\n" % repr(self.lexreflags)) | ||
| 181 | tf.write("_lexliterals = %s\n" % repr(self.lexliterals)) | ||
| 182 | tf.write("_lexstateinfo = %s\n" % repr(self.lexstateinfo)) | ||
| 183 | |||
| 184 | tabre = { } | ||
| 185 | # Collect all functions in the initial state | ||
| 186 | initial = self.lexstatere["INITIAL"] | ||
| 187 | initialfuncs = [] | ||
| 188 | for part in initial: | ||
| 189 | for f in part[1]: | ||
| 190 | if f and f[0]: | ||
| 191 | initialfuncs.append(f) | ||
| 192 | |||
| 193 | for key, lre in self.lexstatere.items(): | ||
| 194 | titem = [] | ||
| 195 | for i in range(len(lre)): | ||
| 196 | titem.append((self.lexstateretext[key][i],_funcs_to_names(lre[i][1],self.lexstaterenames[key][i]))) | ||
| 197 | tabre[key] = titem | ||
| 198 | |||
| 199 | tf.write("_lexstatere = %s\n" % repr(tabre)) | ||
| 200 | tf.write("_lexstateignore = %s\n" % repr(self.lexstateignore)) | ||
| 201 | |||
| 202 | taberr = { } | ||
| 203 | for key, ef in self.lexstateerrorf.items(): | ||
| 204 | if ef: | ||
| 205 | taberr[key] = ef.__name__ | ||
| 206 | else: | ||
| 207 | taberr[key] = None | ||
| 208 | tf.write("_lexstateerrorf = %s\n" % repr(taberr)) | ||
| 209 | tf.close() | ||
| 210 | |||
| 211 | # ------------------------------------------------------------ | ||
| 212 | # readtab() - Read lexer information from a tab file | ||
| 213 | # ------------------------------------------------------------ | ||
| 214 | def readtab(self,tabfile,fdict): | ||
| 215 | if isinstance(tabfile,types.ModuleType): | ||
| 216 | lextab = tabfile | ||
| 217 | else: | ||
| 218 | if sys.version_info[0] < 3: | ||
| 219 | exec("import %s as lextab" % tabfile) | ||
| 220 | else: | ||
| 221 | env = { } | ||
| 222 | exec("import %s as lextab" % tabfile, env,env) | ||
| 223 | lextab = env['lextab'] | ||
| 224 | |||
| 225 | if getattr(lextab,"_tabversion","0.0") != __version__: | ||
| 226 | raise ImportError("Inconsistent PLY version") | ||
| 227 | |||
| 228 | self.lextokens = lextab._lextokens | ||
| 229 | self.lexreflags = lextab._lexreflags | ||
| 230 | self.lexliterals = lextab._lexliterals | ||
| 231 | self.lexstateinfo = lextab._lexstateinfo | ||
| 232 | self.lexstateignore = lextab._lexstateignore | ||
| 233 | self.lexstatere = { } | ||
| 234 | self.lexstateretext = { } | ||
| 235 | for key,lre in lextab._lexstatere.items(): | ||
| 236 | titem = [] | ||
| 237 | txtitem = [] | ||
| 238 | for i in range(len(lre)): | ||
| 239 | titem.append((re.compile(lre[i][0],lextab._lexreflags | re.VERBOSE),_names_to_funcs(lre[i][1],fdict))) | ||
| 240 | txtitem.append(lre[i][0]) | ||
| 241 | self.lexstatere[key] = titem | ||
| 242 | self.lexstateretext[key] = txtitem | ||
| 243 | self.lexstateerrorf = { } | ||
| 244 | for key,ef in lextab._lexstateerrorf.items(): | ||
| 245 | self.lexstateerrorf[key] = fdict[ef] | ||
| 246 | self.begin('INITIAL') | ||
| 247 | |||
| 248 | # ------------------------------------------------------------ | ||
| 249 | # input() - Push a new string into the lexer | ||
| 250 | # ------------------------------------------------------------ | ||
| 251 | def input(self,s): | ||
| 252 | # Pull off the first character to see if s looks like a string | ||
| 253 | c = s[:1] | ||
| 254 | if not isinstance(c,StringTypes): | ||
| 255 | raise ValueError("Expected a string") | ||
| 256 | self.lexdata = s | ||
| 257 | self.lexpos = 0 | ||
| 258 | self.lexlen = len(s) | ||
| 259 | |||
| 260 | # ------------------------------------------------------------ | ||
| 261 | # begin() - Changes the lexing state | ||
| 262 | # ------------------------------------------------------------ | ||
| 263 | def begin(self,state): | ||
| 264 | if not state in self.lexstatere: | ||
| 265 | raise ValueError("Undefined state") | ||
| 266 | self.lexre = self.lexstatere[state] | ||
| 267 | self.lexretext = self.lexstateretext[state] | ||
| 268 | self.lexignore = self.lexstateignore.get(state,"") | ||
| 269 | self.lexerrorf = self.lexstateerrorf.get(state,None) | ||
| 270 | self.lexstate = state | ||
| 271 | |||
| 272 | # ------------------------------------------------------------ | ||
| 273 | # push_state() - Changes the lexing state and saves old on stack | ||
| 274 | # ------------------------------------------------------------ | ||
| 275 | def push_state(self,state): | ||
| 276 | self.lexstatestack.append(self.lexstate) | ||
| 277 | self.begin(state) | ||
| 278 | |||
| 279 | # ------------------------------------------------------------ | ||
| 280 | # pop_state() - Restores the previous state | ||
| 281 | # ------------------------------------------------------------ | ||
| 282 | def pop_state(self): | ||
| 283 | self.begin(self.lexstatestack.pop()) | ||
| 284 | |||
| 285 | # ------------------------------------------------------------ | ||
| 286 | # current_state() - Returns the current lexing state | ||
| 287 | # ------------------------------------------------------------ | ||
| 288 | def current_state(self): | ||
| 289 | return self.lexstate | ||
| 290 | |||
| 291 | # ------------------------------------------------------------ | ||
| 292 | # skip() - Skip ahead n characters | ||
| 293 | # ------------------------------------------------------------ | ||
| 294 | def skip(self,n): | ||
| 295 | self.lexpos += n | ||
| 296 | |||
| 297 | # ------------------------------------------------------------ | ||
| 298 | # opttoken() - Return the next token from the Lexer | ||
| 299 | # | ||
| 300 | # Note: This function has been carefully implemented to be as fast | ||
| 301 | # as possible. Don't make changes unless you really know what | ||
| 302 | # you are doing | ||
| 303 | # ------------------------------------------------------------ | ||
| 304 | def token(self): | ||
| 305 | # Make local copies of frequently referenced attributes | ||
| 306 | lexpos = self.lexpos | ||
| 307 | lexlen = self.lexlen | ||
| 308 | lexignore = self.lexignore | ||
| 309 | lexdata = self.lexdata | ||
| 310 | |||
| 311 | while lexpos < lexlen: | ||
| 312 | # This code provides some short-circuit code for whitespace, tabs, and other ignored characters | ||
| 313 | if lexdata[lexpos] in lexignore: | ||
| 314 | lexpos += 1 | ||
| 315 | continue | ||
| 316 | |||
| 317 | # Look for a regular expression match | ||
| 318 | for lexre,lexindexfunc in self.lexre: | ||
| 319 | m = lexre.match(lexdata,lexpos) | ||
| 320 | if not m: continue | ||
| 321 | |||
| 322 | # Create a token for return | ||
| 323 | tok = LexToken() | ||
| 324 | tok.value = m.group() | ||
| 325 | tok.lineno = self.lineno | ||
| 326 | tok.lexpos = lexpos | ||
| 327 | |||
| 328 | i = m.lastindex | ||
| 329 | func,tok.type = lexindexfunc[i] | ||
| 330 | |||
| 331 | if not func: | ||
| 332 | # If no token type was set, it's an ignored token | ||
| 333 | if tok.type: | ||
| 334 | self.lexpos = m.end() | ||
| 335 | return tok | ||
| 336 | else: | ||
| 337 | lexpos = m.end() | ||
| 338 | break | ||
| 339 | |||
| 340 | lexpos = m.end() | ||
| 341 | |||
| 342 | # If token is processed by a function, call it | ||
| 343 | |||
| 344 | tok.lexer = self # Set additional attributes useful in token rules | ||
| 345 | self.lexmatch = m | ||
| 346 | self.lexpos = lexpos | ||
| 347 | |||
| 348 | newtok = func(tok) | ||
| 349 | |||
| 350 | # Every function must return a token, if nothing, we just move to next token | ||
| 351 | if not newtok: | ||
| 352 | lexpos = self.lexpos # This is here in case user has updated lexpos. | ||
| 353 | lexignore = self.lexignore # This is here in case there was a state change | ||
| 354 | break | ||
| 355 | |||
| 356 | # Verify type of the token. If not in the token map, raise an error | ||
| 357 | if not self.lexoptimize: | ||
| 358 | if not newtok.type in self.lextokens: | ||
| 359 | raise LexError("%s:%d: Rule '%s' returned an unknown token type '%s'" % ( | ||
| 360 | func_code(func).co_filename, func_code(func).co_firstlineno, | ||
| 361 | func.__name__, newtok.type),lexdata[lexpos:]) | ||
| 362 | |||
| 363 | return newtok | ||
| 364 | else: | ||
| 365 | # No match, see if in literals | ||
| 366 | if lexdata[lexpos] in self.lexliterals: | ||
| 367 | tok = LexToken() | ||
| 368 | tok.value = lexdata[lexpos] | ||
| 369 | tok.lineno = self.lineno | ||
| 370 | tok.type = tok.value | ||
| 371 | tok.lexpos = lexpos | ||
| 372 | self.lexpos = lexpos + 1 | ||
| 373 | return tok | ||
| 374 | |||
| 375 | # No match. Call t_error() if defined. | ||
| 376 | if self.lexerrorf: | ||
| 377 | tok = LexToken() | ||
| 378 | tok.value = self.lexdata[lexpos:] | ||
| 379 | tok.lineno = self.lineno | ||
| 380 | tok.type = "error" | ||
| 381 | tok.lexer = self | ||
| 382 | tok.lexpos = lexpos | ||
| 383 | self.lexpos = lexpos | ||
| 384 | newtok = self.lexerrorf(tok) | ||
| 385 | if lexpos == self.lexpos: | ||
| 386 | # Error method didn't change text position at all. This is an error. | ||
| 387 | raise LexError("Scanning error. Illegal character '%s'" % (lexdata[lexpos]), lexdata[lexpos:]) | ||
| 388 | lexpos = self.lexpos | ||
| 389 | if not newtok: continue | ||
| 390 | return newtok | ||
| 391 | |||
| 392 | self.lexpos = lexpos | ||
| 393 | raise LexError("Illegal character '%s' at index %d" % (lexdata[lexpos],lexpos), lexdata[lexpos:]) | ||
| 394 | |||
| 395 | self.lexpos = lexpos + 1 | ||
| 396 | if self.lexdata is None: | ||
| 397 | raise RuntimeError("No input string given with input()") | ||
| 398 | return None | ||
| 399 | |||
| 400 | # Iterator interface | ||
| 401 | def __iter__(self): | ||
| 402 | return self | ||
| 403 | |||
| 404 | def next(self): | ||
| 405 | t = self.token() | ||
| 406 | if t is None: | ||
| 407 | raise StopIteration | ||
| 408 | return t | ||
| 409 | |||
| 410 | __next__ = next | ||
| 411 | |||
| 412 | # ----------------------------------------------------------------------------- | ||
| 413 | # ==== Lex Builder === | ||
| 414 | # | ||
| 415 | # The functions and classes below are used to collect lexing information | ||
| 416 | # and build a Lexer object from it. | ||
| 417 | # ----------------------------------------------------------------------------- | ||
| 418 | |||
| 419 | # ----------------------------------------------------------------------------- | ||
| 420 | # get_caller_module_dict() | ||
| 421 | # | ||
| 422 | # This function returns a dictionary containing all of the symbols defined within | ||
| 423 | # a caller further down the call stack. This is used to get the environment | ||
| 424 | # associated with the yacc() call if none was provided. | ||
| 425 | # ----------------------------------------------------------------------------- | ||
| 426 | |||
| 427 | def get_caller_module_dict(levels): | ||
| 428 | try: | ||
| 429 | raise RuntimeError | ||
| 430 | except RuntimeError: | ||
| 431 | e,b,t = sys.exc_info() | ||
| 432 | f = t.tb_frame | ||
| 433 | while levels > 0: | ||
| 434 | f = f.f_back | ||
| 435 | levels -= 1 | ||
| 436 | ldict = f.f_globals.copy() | ||
| 437 | if f.f_globals != f.f_locals: | ||
| 438 | ldict.update(f.f_locals) | ||
| 439 | |||
| 440 | return ldict | ||
| 441 | |||
| 442 | # ----------------------------------------------------------------------------- | ||
| 443 | # _funcs_to_names() | ||
| 444 | # | ||
| 445 | # Given a list of regular expression functions, this converts it to a list | ||
| 446 | # suitable for output to a table file | ||
| 447 | # ----------------------------------------------------------------------------- | ||
| 448 | |||
| 449 | def _funcs_to_names(funclist,namelist): | ||
| 450 | result = [] | ||
| 451 | for f,name in zip(funclist,namelist): | ||
| 452 | if f and f[0]: | ||
| 453 | result.append((name, f[1])) | ||
| 454 | else: | ||
| 455 | result.append(f) | ||
| 456 | return result | ||
| 457 | |||
| 458 | # ----------------------------------------------------------------------------- | ||
| 459 | # _names_to_funcs() | ||
| 460 | # | ||
| 461 | # Given a list of regular expression function names, this converts it back to | ||
| 462 | # functions. | ||
| 463 | # ----------------------------------------------------------------------------- | ||
| 464 | |||
| 465 | def _names_to_funcs(namelist,fdict): | ||
| 466 | result = [] | ||
| 467 | for n in namelist: | ||
| 468 | if n and n[0]: | ||
| 469 | result.append((fdict[n[0]],n[1])) | ||
| 470 | else: | ||
| 471 | result.append(n) | ||
| 472 | return result | ||
| 473 | |||
| 474 | # ----------------------------------------------------------------------------- | ||
| 475 | # _form_master_re() | ||
| 476 | # | ||
| 477 | # This function takes a list of all of the regex components and attempts to | ||
| 478 | # form the master regular expression. Given limitations in the Python re | ||
| 479 | # module, it may be necessary to break the master regex into separate expressions. | ||
| 480 | # ----------------------------------------------------------------------------- | ||
| 481 | |||
| 482 | def _form_master_re(relist,reflags,ldict,toknames): | ||
| 483 | if not relist: return [] | ||
| 484 | regex = "|".join(relist) | ||
| 485 | try: | ||
| 486 | lexre = re.compile(regex,re.VERBOSE | reflags) | ||
| 487 | |||
| 488 | # Build the index to function map for the matching engine | ||
| 489 | lexindexfunc = [ None ] * (max(lexre.groupindex.values())+1) | ||
| 490 | lexindexnames = lexindexfunc[:] | ||
| 491 | |||
| 492 | for f,i in lexre.groupindex.items(): | ||
| 493 | handle = ldict.get(f,None) | ||
| 494 | if type(handle) in (types.FunctionType, types.MethodType): | ||
| 495 | lexindexfunc[i] = (handle,toknames[f]) | ||
| 496 | lexindexnames[i] = f | ||
| 497 | elif handle is not None: | ||
| 498 | lexindexnames[i] = f | ||
| 499 | if f.find("ignore_") > 0: | ||
| 500 | lexindexfunc[i] = (None,None) | ||
| 501 | else: | ||
| 502 | lexindexfunc[i] = (None, toknames[f]) | ||
| 503 | |||
| 504 | return [(lexre,lexindexfunc)],[regex],[lexindexnames] | ||
| 505 | except Exception: | ||
| 506 | m = int(len(relist)/2) | ||
| 507 | if m == 0: m = 1 | ||
| 508 | llist, lre, lnames = _form_master_re(relist[:m],reflags,ldict,toknames) | ||
| 509 | rlist, rre, rnames = _form_master_re(relist[m:],reflags,ldict,toknames) | ||
| 510 | return llist+rlist, lre+rre, lnames+rnames | ||
| 511 | |||
| 512 | # ----------------------------------------------------------------------------- | ||
| 513 | # def _statetoken(s,names) | ||
| 514 | # | ||
| 515 | # Given a declaration name s of the form "t_" and a dictionary whose keys are | ||
| 516 | # state names, this function returns a tuple (states,tokenname) where states | ||
| 517 | # is a tuple of state names and tokenname is the name of the token. For example, | ||
| 518 | # calling this with s = "t_foo_bar_SPAM" might return (('foo','bar'),'SPAM') | ||
| 519 | # ----------------------------------------------------------------------------- | ||
| 520 | |||
| 521 | def _statetoken(s,names): | ||
| 522 | nonstate = 1 | ||
| 523 | parts = s.split("_") | ||
| 524 | for i in range(1,len(parts)): | ||
| 525 | if not parts[i] in names and parts[i] != 'ANY': break | ||
| 526 | if i > 1: | ||
| 527 | states = tuple(parts[1:i]) | ||
| 528 | else: | ||
| 529 | states = ('INITIAL',) | ||
| 530 | |||
| 531 | if 'ANY' in states: | ||
| 532 | states = tuple(names) | ||
| 533 | |||
| 534 | tokenname = "_".join(parts[i:]) | ||
| 535 | return (states,tokenname) | ||
| 536 | |||
| 537 | |||
| 538 | # ----------------------------------------------------------------------------- | ||
| 539 | # LexerReflect() | ||
| 540 | # | ||
| 541 | # This class represents information needed to build a lexer as extracted from a | ||
| 542 | # user's input file. | ||
| 543 | # ----------------------------------------------------------------------------- | ||
| 544 | class LexerReflect(object): | ||
| 545 | def __init__(self,ldict,log=None,reflags=0): | ||
| 546 | self.ldict = ldict | ||
| 547 | self.error_func = None | ||
| 548 | self.tokens = [] | ||
| 549 | self.reflags = reflags | ||
| 550 | self.stateinfo = { 'INITIAL' : 'inclusive'} | ||
| 551 | self.files = {} | ||
| 552 | self.error = 0 | ||
| 553 | |||
| 554 | if log is None: | ||
| 555 | self.log = PlyLogger(sys.stderr) | ||
| 556 | else: | ||
| 557 | self.log = log | ||
| 558 | |||
| 559 | # Get all of the basic information | ||
| 560 | def get_all(self): | ||
| 561 | self.get_tokens() | ||
| 562 | self.get_literals() | ||
| 563 | self.get_states() | ||
| 564 | self.get_rules() | ||
| 565 | |||
| 566 | # Validate all of the information | ||
| 567 | def validate_all(self): | ||
| 568 | self.validate_tokens() | ||
| 569 | self.validate_literals() | ||
| 570 | self.validate_rules() | ||
| 571 | return self.error | ||
| 572 | |||
| 573 | # Get the tokens map | ||
| 574 | def get_tokens(self): | ||
| 575 | tokens = self.ldict.get("tokens",None) | ||
| 576 | if not tokens: | ||
| 577 | self.log.error("No token list is defined") | ||
| 578 | self.error = 1 | ||
| 579 | return | ||
| 580 | |||
| 581 | if not isinstance(tokens,(list, tuple)): | ||
| 582 | self.log.error("tokens must be a list or tuple") | ||
| 583 | self.error = 1 | ||
| 584 | return | ||
| 585 | |||
| 586 | if not tokens: | ||
| 587 | self.log.error("tokens is empty") | ||
| 588 | self.error = 1 | ||
| 589 | return | ||
| 590 | |||
| 591 | self.tokens = tokens | ||
| 592 | |||
| 593 | # Validate the tokens | ||
| 594 | def validate_tokens(self): | ||
| 595 | terminals = {} | ||
| 596 | for n in self.tokens: | ||
| 597 | if not _is_identifier.match(n): | ||
| 598 | self.log.error("Bad token name '%s'",n) | ||
| 599 | self.error = 1 | ||
| 600 | if n in terminals: | ||
| 601 | self.log.warning("Token '%s' multiply defined", n) | ||
| 602 | terminals[n] = 1 | ||
| 603 | |||
| 604 | # Get the literals specifier | ||
| 605 | def get_literals(self): | ||
| 606 | self.literals = self.ldict.get("literals","") | ||
| 607 | |||
| 608 | # Validate literals | ||
| 609 | def validate_literals(self): | ||
| 610 | try: | ||
| 611 | for c in self.literals: | ||
| 612 | if not isinstance(c,StringTypes) or len(c) > 1: | ||
| 613 | self.log.error("Invalid literal %s. Must be a single character", repr(c)) | ||
| 614 | self.error = 1 | ||
| 615 | continue | ||
| 616 | |||
| 617 | except TypeError: | ||
| 618 | self.log.error("Invalid literals specification. literals must be a sequence of characters") | ||
| 619 | self.error = 1 | ||
| 620 | |||
| 621 | def get_states(self): | ||
| 622 | self.states = self.ldict.get("states",None) | ||
| 623 | # Build statemap | ||
| 624 | if self.states: | ||
| 625 | if not isinstance(self.states,(tuple,list)): | ||
| 626 | self.log.error("states must be defined as a tuple or list") | ||
| 627 | self.error = 1 | ||
| 628 | else: | ||
| 629 | for s in self.states: | ||
| 630 | if not isinstance(s,tuple) or len(s) != 2: | ||
| 631 | self.log.error("Invalid state specifier %s. Must be a tuple (statename,'exclusive|inclusive')",repr(s)) | ||
| 632 | self.error = 1 | ||
| 633 | continue | ||
| 634 | name, statetype = s | ||
| 635 | if not isinstance(name,StringTypes): | ||
| 636 | self.log.error("State name %s must be a string", repr(name)) | ||
| 637 | self.error = 1 | ||
| 638 | continue | ||
| 639 | if not (statetype == 'inclusive' or statetype == 'exclusive'): | ||
| 640 | self.log.error("State type for state %s must be 'inclusive' or 'exclusive'",name) | ||
| 641 | self.error = 1 | ||
| 642 | continue | ||
| 643 | if name in self.stateinfo: | ||
| 644 | self.log.error("State '%s' already defined",name) | ||
| 645 | self.error = 1 | ||
| 646 | continue | ||
| 647 | self.stateinfo[name] = statetype | ||
| 648 | |||
| 649 | # Get all of the symbols with a t_ prefix and sort them into various | ||
| 650 | # categories (functions, strings, error functions, and ignore characters) | ||
| 651 | |||
| 652 | def get_rules(self): | ||
| 653 | tsymbols = [f for f in self.ldict if f[:2] == 't_' ] | ||
| 654 | |||
| 655 | # Now build up a list of functions and a list of strings | ||
| 656 | |||
| 657 | self.toknames = { } # Mapping of symbols to token names | ||
| 658 | self.funcsym = { } # Symbols defined as functions | ||
| 659 | self.strsym = { } # Symbols defined as strings | ||
| 660 | self.ignore = { } # Ignore strings by state | ||
| 661 | self.errorf = { } # Error functions by state | ||
| 662 | |||
| 663 | for s in self.stateinfo: | ||
| 664 | self.funcsym[s] = [] | ||
| 665 | self.strsym[s] = [] | ||
| 666 | |||
| 667 | if len(tsymbols) == 0: | ||
| 668 | self.log.error("No rules of the form t_rulename are defined") | ||
| 669 | self.error = 1 | ||
| 670 | return | ||
| 671 | |||
| 672 | for f in tsymbols: | ||
| 673 | t = self.ldict[f] | ||
| 674 | states, tokname = _statetoken(f,self.stateinfo) | ||
| 675 | self.toknames[f] = tokname | ||
| 676 | |||
| 677 | if hasattr(t,"__call__"): | ||
| 678 | if tokname == 'error': | ||
| 679 | for s in states: | ||
| 680 | self.errorf[s] = t | ||
| 681 | elif tokname == 'ignore': | ||
| 682 | line = func_code(t).co_firstlineno | ||
| 683 | file = func_code(t).co_filename | ||
| 684 | self.log.error("%s:%d: Rule '%s' must be defined as a string",file,line,t.__name__) | ||
| 685 | self.error = 1 | ||
| 686 | else: | ||
| 687 | for s in states: | ||
| 688 | self.funcsym[s].append((f,t)) | ||
| 689 | elif isinstance(t, StringTypes): | ||
| 690 | if tokname == 'ignore': | ||
| 691 | for s in states: | ||
| 692 | self.ignore[s] = t | ||
| 693 | if "\\" in t: | ||
| 694 | self.log.warning("%s contains a literal backslash '\\'",f) | ||
| 695 | |||
| 696 | elif tokname == 'error': | ||
| 697 | self.log.error("Rule '%s' must be defined as a function", f) | ||
| 698 | self.error = 1 | ||
| 699 | else: | ||
| 700 | for s in states: | ||
| 701 | self.strsym[s].append((f,t)) | ||
| 702 | else: | ||
| 703 | self.log.error("%s not defined as a function or string", f) | ||
| 704 | self.error = 1 | ||
| 705 | |||
| 706 | # Sort the functions by line number | ||
| 707 | for f in self.funcsym.values(): | ||
| 708 | if sys.version_info[0] < 3: | ||
| 709 | f.sort(lambda x,y: cmp(func_code(x[1]).co_firstlineno,func_code(y[1]).co_firstlineno)) | ||
| 710 | else: | ||
| 711 | # Python 3.0 | ||
| 712 | f.sort(key=lambda x: func_code(x[1]).co_firstlineno) | ||
| 713 | |||
| 714 | # Sort the strings by regular expression length | ||
| 715 | for s in self.strsym.values(): | ||
| 716 | if sys.version_info[0] < 3: | ||
| 717 | s.sort(lambda x,y: (len(x[1]) < len(y[1])) - (len(x[1]) > len(y[1]))) | ||
| 718 | else: | ||
| 719 | # Python 3.0 | ||
| 720 | s.sort(key=lambda x: len(x[1]),reverse=True) | ||
| 721 | |||
| 722 | # Validate all of the t_rules collected | ||
| 723 | def validate_rules(self): | ||
| 724 | for state in self.stateinfo: | ||
| 725 | # Validate all rules defined by functions | ||
| 726 | |||
| 727 | |||
| 728 | |||
| 729 | for fname, f in self.funcsym[state]: | ||
| 730 | line = func_code(f).co_firstlineno | ||
| 731 | file = func_code(f).co_filename | ||
| 732 | self.files[file] = 1 | ||
| 733 | |||
| 734 | tokname = self.toknames[fname] | ||
| 735 | if isinstance(f, types.MethodType): | ||
| 736 | reqargs = 2 | ||
| 737 | else: | ||
| 738 | reqargs = 1 | ||
| 739 | nargs = func_code(f).co_argcount | ||
| 740 | if nargs > reqargs: | ||
| 741 | self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) | ||
| 742 | self.error = 1 | ||
| 743 | continue | ||
| 744 | |||
| 745 | if nargs < reqargs: | ||
| 746 | self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) | ||
| 747 | self.error = 1 | ||
| 748 | continue | ||
| 749 | |||
| 750 | if not f.__doc__: | ||
| 751 | self.log.error("%s:%d: No regular expression defined for rule '%s'",file,line,f.__name__) | ||
| 752 | self.error = 1 | ||
| 753 | continue | ||
| 754 | |||
| 755 | try: | ||
| 756 | c = re.compile("(?P<%s>%s)" % (fname,f.__doc__), re.VERBOSE | self.reflags) | ||
| 757 | if c.match(""): | ||
| 758 | self.log.error("%s:%d: Regular expression for rule '%s' matches empty string", file,line,f.__name__) | ||
| 759 | self.error = 1 | ||
| 760 | except re.error: | ||
| 761 | _etype, e, _etrace = sys.exc_info() | ||
| 762 | self.log.error("%s:%d: Invalid regular expression for rule '%s'. %s", file,line,f.__name__,e) | ||
| 763 | if '#' in f.__doc__: | ||
| 764 | self.log.error("%s:%d. Make sure '#' in rule '%s' is escaped with '\\#'",file,line, f.__name__) | ||
| 765 | self.error = 1 | ||
| 766 | |||
| 767 | # Validate all rules defined by strings | ||
| 768 | for name,r in self.strsym[state]: | ||
| 769 | tokname = self.toknames[name] | ||
| 770 | if tokname == 'error': | ||
| 771 | self.log.error("Rule '%s' must be defined as a function", name) | ||
| 772 | self.error = 1 | ||
| 773 | continue | ||
| 774 | |||
| 775 | if not tokname in self.tokens and tokname.find("ignore_") < 0: | ||
| 776 | self.log.error("Rule '%s' defined for an unspecified token %s",name,tokname) | ||
| 777 | self.error = 1 | ||
| 778 | continue | ||
| 779 | |||
| 780 | try: | ||
| 781 | c = re.compile("(?P<%s>%s)" % (name,r),re.VERBOSE | self.reflags) | ||
| 782 | if (c.match("")): | ||
| 783 | self.log.error("Regular expression for rule '%s' matches empty string",name) | ||
| 784 | self.error = 1 | ||
| 785 | except re.error: | ||
| 786 | _etype, e, _etrace = sys.exc_info() | ||
| 787 | self.log.error("Invalid regular expression for rule '%s'. %s",name,e) | ||
| 788 | if '#' in r: | ||
| 789 | self.log.error("Make sure '#' in rule '%s' is escaped with '\\#'",name) | ||
| 790 | self.error = 1 | ||
| 791 | |||
| 792 | if not self.funcsym[state] and not self.strsym[state]: | ||
| 793 | self.log.error("No rules defined for state '%s'",state) | ||
| 794 | self.error = 1 | ||
| 795 | |||
| 796 | # Validate the error function | ||
| 797 | efunc = self.errorf.get(state,None) | ||
| 798 | if efunc: | ||
| 799 | f = efunc | ||
| 800 | line = func_code(f).co_firstlineno | ||
| 801 | file = func_code(f).co_filename | ||
| 802 | self.files[file] = 1 | ||
| 803 | |||
| 804 | if isinstance(f, types.MethodType): | ||
| 805 | reqargs = 2 | ||
| 806 | else: | ||
| 807 | reqargs = 1 | ||
| 808 | nargs = func_code(f).co_argcount | ||
| 809 | if nargs > reqargs: | ||
| 810 | self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,f.__name__) | ||
| 811 | self.error = 1 | ||
| 812 | |||
| 813 | if nargs < reqargs: | ||
| 814 | self.log.error("%s:%d: Rule '%s' requires an argument", file,line,f.__name__) | ||
| 815 | self.error = 1 | ||
| 816 | |||
| 817 | for f in self.files: | ||
| 818 | self.validate_file(f) | ||
| 819 | |||
| 820 | |||
| 821 | # ----------------------------------------------------------------------------- | ||
| 822 | # validate_file() | ||
| 823 | # | ||
| 824 | # This checks to see if there are duplicated t_rulename() functions or strings | ||
| 825 | # in the parser input file. This is done using a simple regular expression | ||
| 826 | # match on each line in the given file. | ||
| 827 | # ----------------------------------------------------------------------------- | ||
| 828 | |||
| 829 | def validate_file(self,filename): | ||
| 830 | import os.path | ||
| 831 | base,ext = os.path.splitext(filename) | ||
| 832 | if ext != '.py': return # No idea what the file is. Return OK | ||
| 833 | |||
| 834 | try: | ||
| 835 | f = open(filename) | ||
| 836 | lines = f.readlines() | ||
| 837 | f.close() | ||
| 838 | except IOError: | ||
| 839 | return # Couldn't find the file. Don't worry about it | ||
| 840 | |||
| 841 | fre = re.compile(r'\s*def\s+(t_[a-zA-Z_0-9]*)\(') | ||
| 842 | sre = re.compile(r'\s*(t_[a-zA-Z_0-9]*)\s*=') | ||
| 843 | |||
| 844 | counthash = { } | ||
| 845 | linen = 1 | ||
| 846 | for l in lines: | ||
| 847 | m = fre.match(l) | ||
| 848 | if not m: | ||
| 849 | m = sre.match(l) | ||
| 850 | if m: | ||
| 851 | name = m.group(1) | ||
| 852 | prev = counthash.get(name) | ||
| 853 | if not prev: | ||
| 854 | counthash[name] = linen | ||
| 855 | else: | ||
| 856 | self.log.error("%s:%d: Rule %s redefined. Previously defined on line %d",filename,linen,name,prev) | ||
| 857 | self.error = 1 | ||
| 858 | linen += 1 | ||
| 859 | |||
| 860 | # ----------------------------------------------------------------------------- | ||
| 861 | # lex(module) | ||
| 862 | # | ||
| 863 | # Build all of the regular expression rules from definitions in the supplied module | ||
| 864 | # ----------------------------------------------------------------------------- | ||
| 865 | def lex(module=None,object=None,debug=0,optimize=0,lextab="lextab",reflags=0,nowarn=0,outputdir="", debuglog=None, errorlog=None): | ||
| 866 | global lexer | ||
| 867 | ldict = None | ||
| 868 | stateinfo = { 'INITIAL' : 'inclusive'} | ||
| 869 | lexobj = Lexer() | ||
| 870 | lexobj.lexoptimize = optimize | ||
| 871 | global token,input | ||
| 872 | |||
| 873 | if errorlog is None: | ||
| 874 | errorlog = PlyLogger(sys.stderr) | ||
| 875 | |||
| 876 | if debug: | ||
| 877 | if debuglog is None: | ||
| 878 | debuglog = PlyLogger(sys.stderr) | ||
| 879 | |||
| 880 | # Get the module dictionary used for the lexer | ||
| 881 | if object: module = object | ||
| 882 | |||
| 883 | if module: | ||
| 884 | _items = [(k,getattr(module,k)) for k in dir(module)] | ||
| 885 | ldict = dict(_items) | ||
| 886 | else: | ||
| 887 | ldict = get_caller_module_dict(2) | ||
| 888 | |||
| 889 | # Collect parser information from the dictionary | ||
| 890 | linfo = LexerReflect(ldict,log=errorlog,reflags=reflags) | ||
| 891 | linfo.get_all() | ||
| 892 | if not optimize: | ||
| 893 | if linfo.validate_all(): | ||
| 894 | raise SyntaxError("Can't build lexer") | ||
| 895 | |||
| 896 | if optimize and lextab: | ||
| 897 | try: | ||
| 898 | lexobj.readtab(lextab,ldict) | ||
| 899 | token = lexobj.token | ||
| 900 | input = lexobj.input | ||
| 901 | lexer = lexobj | ||
| 902 | return lexobj | ||
| 903 | |||
| 904 | except ImportError: | ||
| 905 | pass | ||
| 906 | |||
| 907 | # Dump some basic debugging information | ||
| 908 | if debug: | ||
| 909 | debuglog.info("lex: tokens = %r", linfo.tokens) | ||
| 910 | debuglog.info("lex: literals = %r", linfo.literals) | ||
| 911 | debuglog.info("lex: states = %r", linfo.stateinfo) | ||
| 912 | |||
| 913 | # Build a dictionary of valid token names | ||
| 914 | lexobj.lextokens = { } | ||
| 915 | for n in linfo.tokens: | ||
| 916 | lexobj.lextokens[n] = 1 | ||
| 917 | |||
| 918 | # Get literals specification | ||
| 919 | if isinstance(linfo.literals,(list,tuple)): | ||
| 920 | lexobj.lexliterals = type(linfo.literals[0])().join(linfo.literals) | ||
| 921 | else: | ||
| 922 | lexobj.lexliterals = linfo.literals | ||
| 923 | |||
| 924 | # Get the stateinfo dictionary | ||
| 925 | stateinfo = linfo.stateinfo | ||
| 926 | |||
| 927 | regexs = { } | ||
| 928 | # Build the master regular expressions | ||
| 929 | for state in stateinfo: | ||
| 930 | regex_list = [] | ||
| 931 | |||
| 932 | # Add rules defined by functions first | ||
| 933 | for fname, f in linfo.funcsym[state]: | ||
| 934 | line = func_code(f).co_firstlineno | ||
| 935 | file = func_code(f).co_filename | ||
| 936 | regex_list.append("(?P<%s>%s)" % (fname,f.__doc__)) | ||
| 937 | if debug: | ||
| 938 | debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",fname,f.__doc__, state) | ||
| 939 | |||
| 940 | # Now add all of the simple rules | ||
| 941 | for name,r in linfo.strsym[state]: | ||
| 942 | regex_list.append("(?P<%s>%s)" % (name,r)) | ||
| 943 | if debug: | ||
| 944 | debuglog.info("lex: Adding rule %s -> '%s' (state '%s')",name,r, state) | ||
| 945 | |||
| 946 | regexs[state] = regex_list | ||
| 947 | |||
| 948 | # Build the master regular expressions | ||
| 949 | |||
| 950 | if debug: | ||
| 951 | debuglog.info("lex: ==== MASTER REGEXS FOLLOW ====") | ||
| 952 | |||
| 953 | for state in regexs: | ||
| 954 | lexre, re_text, re_names = _form_master_re(regexs[state],reflags,ldict,linfo.toknames) | ||
| 955 | lexobj.lexstatere[state] = lexre | ||
| 956 | lexobj.lexstateretext[state] = re_text | ||
| 957 | lexobj.lexstaterenames[state] = re_names | ||
| 958 | if debug: | ||
| 959 | for i in range(len(re_text)): | ||
| 960 | debuglog.info("lex: state '%s' : regex[%d] = '%s'",state, i, re_text[i]) | ||
| 961 | |||
| 962 | # For inclusive states, we need to add the regular expressions from the INITIAL state | ||
| 963 | for state,stype in stateinfo.items(): | ||
| 964 | if state != "INITIAL" and stype == 'inclusive': | ||
| 965 | lexobj.lexstatere[state].extend(lexobj.lexstatere['INITIAL']) | ||
| 966 | lexobj.lexstateretext[state].extend(lexobj.lexstateretext['INITIAL']) | ||
| 967 | lexobj.lexstaterenames[state].extend(lexobj.lexstaterenames['INITIAL']) | ||
| 968 | |||
| 969 | lexobj.lexstateinfo = stateinfo | ||
| 970 | lexobj.lexre = lexobj.lexstatere["INITIAL"] | ||
| 971 | lexobj.lexretext = lexobj.lexstateretext["INITIAL"] | ||
| 972 | lexobj.lexreflags = reflags | ||
| 973 | |||
| 974 | # Set up ignore variables | ||
| 975 | lexobj.lexstateignore = linfo.ignore | ||
| 976 | lexobj.lexignore = lexobj.lexstateignore.get("INITIAL","") | ||
| 977 | |||
| 978 | # Set up error functions | ||
| 979 | lexobj.lexstateerrorf = linfo.errorf | ||
| 980 | lexobj.lexerrorf = linfo.errorf.get("INITIAL",None) | ||
| 981 | if not lexobj.lexerrorf: | ||
| 982 | errorlog.warning("No t_error rule is defined") | ||
| 983 | |||
| 984 | # Check state information for ignore and error rules | ||
| 985 | for s,stype in stateinfo.items(): | ||
| 986 | if stype == 'exclusive': | ||
| 987 | if not s in linfo.errorf: | ||
| 988 | errorlog.warning("No error rule is defined for exclusive state '%s'", s) | ||
| 989 | if not s in linfo.ignore and lexobj.lexignore: | ||
| 990 | errorlog.warning("No ignore rule is defined for exclusive state '%s'", s) | ||
| 991 | elif stype == 'inclusive': | ||
| 992 | if not s in linfo.errorf: | ||
| 993 | linfo.errorf[s] = linfo.errorf.get("INITIAL",None) | ||
| 994 | if not s in linfo.ignore: | ||
| 995 | linfo.ignore[s] = linfo.ignore.get("INITIAL","") | ||
| 996 | |||
| 997 | # Create global versions of the token() and input() functions | ||
| 998 | token = lexobj.token | ||
| 999 | input = lexobj.input | ||
| 1000 | lexer = lexobj | ||
| 1001 | |||
| 1002 | # If in optimize mode, we write the lextab | ||
| 1003 | if lextab and optimize: | ||
| 1004 | lexobj.writetab(lextab,outputdir) | ||
| 1005 | |||
| 1006 | return lexobj | ||
| 1007 | |||
| 1008 | # ----------------------------------------------------------------------------- | ||
| 1009 | # runmain() | ||
| 1010 | # | ||
| 1011 | # This runs the lexer as a main program | ||
| 1012 | # ----------------------------------------------------------------------------- | ||
| 1013 | |||
| 1014 | def runmain(lexer=None,data=None): | ||
| 1015 | if not data: | ||
| 1016 | try: | ||
| 1017 | filename = sys.argv[1] | ||
| 1018 | f = open(filename) | ||
| 1019 | data = f.read() | ||
| 1020 | f.close() | ||
| 1021 | except IndexError: | ||
| 1022 | sys.stdout.write("Reading from standard input (type EOF to end):\n") | ||
| 1023 | data = sys.stdin.read() | ||
| 1024 | |||
| 1025 | if lexer: | ||
| 1026 | _input = lexer.input | ||
| 1027 | else: | ||
| 1028 | _input = input | ||
| 1029 | _input(data) | ||
| 1030 | if lexer: | ||
| 1031 | _token = lexer.token | ||
| 1032 | else: | ||
| 1033 | _token = token | ||
| 1034 | |||
| 1035 | while 1: | ||
| 1036 | tok = _token() | ||
| 1037 | if not tok: break | ||
| 1038 | sys.stdout.write("(%s,%r,%d,%d)\n" % (tok.type, tok.value, tok.lineno,tok.lexpos)) | ||
| 1039 | |||
| 1040 | # ----------------------------------------------------------------------------- | ||
| 1041 | # @TOKEN(regex) | ||
| 1042 | # | ||
| 1043 | # This decorator function can be used to set the regex expression on a function | ||
| 1044 | # when its docstring might need to be set in an alternative way | ||
| 1045 | # ----------------------------------------------------------------------------- | ||
| 1046 | |||
| 1047 | def TOKEN(r): | ||
| 1048 | def set_doc(f): | ||
| 1049 | if hasattr(r,"__call__"): | ||
| 1050 | f.__doc__ = r.__doc__ | ||
| 1051 | else: | ||
| 1052 | f.__doc__ = r | ||
| 1053 | return f | ||
| 1054 | return set_doc | ||
| 1055 | |||
| 1056 | # Alternative spelling of the TOKEN decorator | ||
| 1057 | Token = TOKEN | ||
| 1058 | |||
diff --git a/bitbake/lib/ply/yacc.py b/bitbake/lib/ply/yacc.py new file mode 100644 index 0000000000..6168fd9a03 --- /dev/null +++ b/bitbake/lib/ply/yacc.py | |||
| @@ -0,0 +1,3276 @@ | |||
| 1 | # ----------------------------------------------------------------------------- | ||
| 2 | # ply: yacc.py | ||
| 3 | # | ||
| 4 | # Copyright (C) 2001-2009, | ||
| 5 | # David M. Beazley (Dabeaz LLC) | ||
| 6 | # All rights reserved. | ||
| 7 | # | ||
| 8 | # Redistribution and use in source and binary forms, with or without | ||
| 9 | # modification, are permitted provided that the following conditions are | ||
| 10 | # met: | ||
| 11 | # | ||
| 12 | # * Redistributions of source code must retain the above copyright notice, | ||
| 13 | # this list of conditions and the following disclaimer. | ||
| 14 | # * Redistributions in binary form must reproduce the above copyright notice, | ||
| 15 | # this list of conditions and the following disclaimer in the documentation | ||
| 16 | # and/or other materials provided with the distribution. | ||
| 17 | # * Neither the name of the David Beazley or Dabeaz LLC may be used to | ||
| 18 | # endorse or promote products derived from this software without | ||
| 19 | # specific prior written permission. | ||
| 20 | # | ||
| 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 22 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 23 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 24 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 25 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 26 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 27 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 28 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 29 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 30 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 31 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 32 | # ----------------------------------------------------------------------------- | ||
| 33 | # | ||
| 34 | # This implements an LR parser that is constructed from grammar rules defined | ||
| 35 | # as Python functions. The grammer is specified by supplying the BNF inside | ||
| 36 | # Python documentation strings. The inspiration for this technique was borrowed | ||
| 37 | # from John Aycock's Spark parsing system. PLY might be viewed as cross between | ||
| 38 | # Spark and the GNU bison utility. | ||
| 39 | # | ||
| 40 | # The current implementation is only somewhat object-oriented. The | ||
| 41 | # LR parser itself is defined in terms of an object (which allows multiple | ||
| 42 | # parsers to co-exist). However, most of the variables used during table | ||
| 43 | # construction are defined in terms of global variables. Users shouldn't | ||
| 44 | # notice unless they are trying to define multiple parsers at the same | ||
| 45 | # time using threads (in which case they should have their head examined). | ||
| 46 | # | ||
| 47 | # This implementation supports both SLR and LALR(1) parsing. LALR(1) | ||
| 48 | # support was originally implemented by Elias Ioup (ezioup@alumni.uchicago.edu), | ||
| 49 | # using the algorithm found in Aho, Sethi, and Ullman "Compilers: Principles, | ||
| 50 | # Techniques, and Tools" (The Dragon Book). LALR(1) has since been replaced | ||
| 51 | # by the more efficient DeRemer and Pennello algorithm. | ||
| 52 | # | ||
| 53 | # :::::::: WARNING ::::::: | ||
| 54 | # | ||
| 55 | # Construction of LR parsing tables is fairly complicated and expensive. | ||
| 56 | # To make this module run fast, a *LOT* of work has been put into | ||
| 57 | # optimization---often at the expensive of readability and what might | ||
| 58 | # consider to be good Python "coding style." Modify the code at your | ||
| 59 | # own risk! | ||
| 60 | # ---------------------------------------------------------------------------- | ||
| 61 | |||
| 62 | __version__ = "3.3" | ||
| 63 | __tabversion__ = "3.2" # Table version | ||
| 64 | |||
| 65 | #----------------------------------------------------------------------------- | ||
| 66 | # === User configurable parameters === | ||
| 67 | # | ||
| 68 | # Change these to modify the default behavior of yacc (if you wish) | ||
| 69 | #----------------------------------------------------------------------------- | ||
| 70 | |||
| 71 | yaccdebug = 0 # Debugging mode. If set, yacc generates a | ||
| 72 | # a 'parser.out' file in the current directory | ||
| 73 | |||
| 74 | debug_file = 'parser.out' # Default name of the debugging file | ||
| 75 | tab_module = 'parsetab' # Default name of the table module | ||
| 76 | default_lr = 'LALR' # Default LR table generation method | ||
| 77 | |||
| 78 | error_count = 3 # Number of symbols that must be shifted to leave recovery mode | ||
| 79 | |||
| 80 | yaccdevel = 0 # Set to True if developing yacc. This turns off optimized | ||
| 81 | # implementations of certain functions. | ||
| 82 | |||
| 83 | resultlimit = 40 # Size limit of results when running in debug mode. | ||
| 84 | |||
| 85 | pickle_protocol = 0 # Protocol to use when writing pickle files | ||
| 86 | |||
| 87 | import re, types, sys, os.path | ||
| 88 | |||
| 89 | # Compatibility function for python 2.6/3.0 | ||
| 90 | if sys.version_info[0] < 3: | ||
| 91 | def func_code(f): | ||
| 92 | return f.func_code | ||
| 93 | else: | ||
| 94 | def func_code(f): | ||
| 95 | return f.__code__ | ||
| 96 | |||
| 97 | # Compatibility | ||
| 98 | try: | ||
| 99 | MAXINT = sys.maxint | ||
| 100 | except AttributeError: | ||
| 101 | MAXINT = sys.maxsize | ||
| 102 | |||
| 103 | # Python 2.x/3.0 compatibility. | ||
| 104 | def load_ply_lex(): | ||
| 105 | if sys.version_info[0] < 3: | ||
| 106 | import lex | ||
| 107 | else: | ||
| 108 | import ply.lex as lex | ||
| 109 | return lex | ||
| 110 | |||
| 111 | # This object is a stand-in for a logging object created by the | ||
| 112 | # logging module. PLY will use this by default to create things | ||
| 113 | # such as the parser.out file. If a user wants more detailed | ||
| 114 | # information, they can create their own logging object and pass | ||
| 115 | # it into PLY. | ||
| 116 | |||
| 117 | class PlyLogger(object): | ||
| 118 | def __init__(self,f): | ||
| 119 | self.f = f | ||
| 120 | def debug(self,msg,*args,**kwargs): | ||
| 121 | self.f.write((msg % args) + "\n") | ||
| 122 | info = debug | ||
| 123 | |||
| 124 | def warning(self,msg,*args,**kwargs): | ||
| 125 | self.f.write("WARNING: "+ (msg % args) + "\n") | ||
| 126 | |||
| 127 | def error(self,msg,*args,**kwargs): | ||
| 128 | self.f.write("ERROR: " + (msg % args) + "\n") | ||
| 129 | |||
| 130 | critical = debug | ||
| 131 | |||
| 132 | # Null logger is used when no output is generated. Does nothing. | ||
| 133 | class NullLogger(object): | ||
| 134 | def __getattribute__(self,name): | ||
| 135 | return self | ||
| 136 | def __call__(self,*args,**kwargs): | ||
| 137 | return self | ||
| 138 | |||
| 139 | # Exception raised for yacc-related errors | ||
| 140 | class YaccError(Exception): pass | ||
| 141 | |||
| 142 | # Format the result message that the parser produces when running in debug mode. | ||
| 143 | def format_result(r): | ||
| 144 | repr_str = repr(r) | ||
| 145 | if '\n' in repr_str: repr_str = repr(repr_str) | ||
| 146 | if len(repr_str) > resultlimit: | ||
| 147 | repr_str = repr_str[:resultlimit]+" ..." | ||
| 148 | result = "<%s @ 0x%x> (%s)" % (type(r).__name__,id(r),repr_str) | ||
| 149 | return result | ||
| 150 | |||
| 151 | |||
| 152 | # Format stack entries when the parser is running in debug mode | ||
| 153 | def format_stack_entry(r): | ||
| 154 | repr_str = repr(r) | ||
| 155 | if '\n' in repr_str: repr_str = repr(repr_str) | ||
| 156 | if len(repr_str) < 16: | ||
| 157 | return repr_str | ||
| 158 | else: | ||
| 159 | return "<%s @ 0x%x>" % (type(r).__name__,id(r)) | ||
| 160 | |||
| 161 | #----------------------------------------------------------------------------- | ||
| 162 | # === LR Parsing Engine === | ||
| 163 | # | ||
| 164 | # The following classes are used for the LR parser itself. These are not | ||
| 165 | # used during table construction and are independent of the actual LR | ||
| 166 | # table generation algorithm | ||
| 167 | #----------------------------------------------------------------------------- | ||
| 168 | |||
| 169 | # This class is used to hold non-terminal grammar symbols during parsing. | ||
| 170 | # It normally has the following attributes set: | ||
| 171 | # .type = Grammar symbol type | ||
| 172 | # .value = Symbol value | ||
| 173 | # .lineno = Starting line number | ||
| 174 | # .endlineno = Ending line number (optional, set automatically) | ||
| 175 | # .lexpos = Starting lex position | ||
| 176 | # .endlexpos = Ending lex position (optional, set automatically) | ||
| 177 | |||
| 178 | class YaccSymbol: | ||
| 179 | def __str__(self): return self.type | ||
| 180 | def __repr__(self): return str(self) | ||
| 181 | |||
| 182 | # This class is a wrapper around the objects actually passed to each | ||
| 183 | # grammar rule. Index lookup and assignment actually assign the | ||
| 184 | # .value attribute of the underlying YaccSymbol object. | ||
| 185 | # The lineno() method returns the line number of a given | ||
| 186 | # item (or 0 if not defined). The linespan() method returns | ||
| 187 | # a tuple of (startline,endline) representing the range of lines | ||
| 188 | # for a symbol. The lexspan() method returns a tuple (lexpos,endlexpos) | ||
| 189 | # representing the range of positional information for a symbol. | ||
| 190 | |||
| 191 | class YaccProduction: | ||
| 192 | def __init__(self,s,stack=None): | ||
| 193 | self.slice = s | ||
| 194 | self.stack = stack | ||
| 195 | self.lexer = None | ||
| 196 | self.parser= None | ||
| 197 | def __getitem__(self,n): | ||
| 198 | if n >= 0: return self.slice[n].value | ||
| 199 | else: return self.stack[n].value | ||
| 200 | |||
| 201 | def __setitem__(self,n,v): | ||
| 202 | self.slice[n].value = v | ||
| 203 | |||
| 204 | def __getslice__(self,i,j): | ||
| 205 | return [s.value for s in self.slice[i:j]] | ||
| 206 | |||
| 207 | def __len__(self): | ||
| 208 | return len(self.slice) | ||
| 209 | |||
| 210 | def lineno(self,n): | ||
| 211 | return getattr(self.slice[n],"lineno",0) | ||
| 212 | |||
| 213 | def set_lineno(self,n,lineno): | ||
| 214 | self.slice[n].lineno = lineno | ||
| 215 | |||
| 216 | def linespan(self,n): | ||
| 217 | startline = getattr(self.slice[n],"lineno",0) | ||
| 218 | endline = getattr(self.slice[n],"endlineno",startline) | ||
| 219 | return startline,endline | ||
| 220 | |||
| 221 | def lexpos(self,n): | ||
| 222 | return getattr(self.slice[n],"lexpos",0) | ||
| 223 | |||
| 224 | def lexspan(self,n): | ||
| 225 | startpos = getattr(self.slice[n],"lexpos",0) | ||
| 226 | endpos = getattr(self.slice[n],"endlexpos",startpos) | ||
| 227 | return startpos,endpos | ||
| 228 | |||
| 229 | def error(self): | ||
| 230 | raise SyntaxError | ||
| 231 | |||
| 232 | |||
| 233 | # ----------------------------------------------------------------------------- | ||
| 234 | # == LRParser == | ||
| 235 | # | ||
| 236 | # The LR Parsing engine. | ||
| 237 | # ----------------------------------------------------------------------------- | ||
| 238 | |||
| 239 | class LRParser: | ||
| 240 | def __init__(self,lrtab,errorf): | ||
| 241 | self.productions = lrtab.lr_productions | ||
| 242 | self.action = lrtab.lr_action | ||
| 243 | self.goto = lrtab.lr_goto | ||
| 244 | self.errorfunc = errorf | ||
| 245 | |||
| 246 | def errok(self): | ||
| 247 | self.errorok = 1 | ||
| 248 | |||
| 249 | def restart(self): | ||
| 250 | del self.statestack[:] | ||
| 251 | del self.symstack[:] | ||
| 252 | sym = YaccSymbol() | ||
| 253 | sym.type = '$end' | ||
| 254 | self.symstack.append(sym) | ||
| 255 | self.statestack.append(0) | ||
| 256 | |||
| 257 | def parse(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): | ||
| 258 | if debug or yaccdevel: | ||
| 259 | if isinstance(debug,int): | ||
| 260 | debug = PlyLogger(sys.stderr) | ||
| 261 | return self.parsedebug(input,lexer,debug,tracking,tokenfunc) | ||
| 262 | elif tracking: | ||
| 263 | return self.parseopt(input,lexer,debug,tracking,tokenfunc) | ||
| 264 | else: | ||
| 265 | return self.parseopt_notrack(input,lexer,debug,tracking,tokenfunc) | ||
| 266 | |||
| 267 | |||
| 268 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 269 | # parsedebug(). | ||
| 270 | # | ||
| 271 | # This is the debugging enabled version of parse(). All changes made to the | ||
| 272 | # parsing engine should be made here. For the non-debugging version, | ||
| 273 | # copy this code to a method parseopt() and delete all of the sections | ||
| 274 | # enclosed in: | ||
| 275 | # | ||
| 276 | # #--! DEBUG | ||
| 277 | # statements | ||
| 278 | # #--! DEBUG | ||
| 279 | # | ||
| 280 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 281 | |||
| 282 | def parsedebug(self,input=None,lexer=None,debug=None,tracking=0,tokenfunc=None): | ||
| 283 | lookahead = None # Current lookahead symbol | ||
| 284 | lookaheadstack = [ ] # Stack of lookahead symbols | ||
| 285 | actions = self.action # Local reference to action table (to avoid lookup on self.) | ||
| 286 | goto = self.goto # Local reference to goto table (to avoid lookup on self.) | ||
| 287 | prod = self.productions # Local reference to production list (to avoid lookup on self.) | ||
| 288 | pslice = YaccProduction(None) # Production object passed to grammar rules | ||
| 289 | errorcount = 0 # Used during error recovery | ||
| 290 | |||
| 291 | # --! DEBUG | ||
| 292 | debug.info("PLY: PARSE DEBUG START") | ||
| 293 | # --! DEBUG | ||
| 294 | |||
| 295 | # If no lexer was given, we will try to use the lex module | ||
| 296 | if not lexer: | ||
| 297 | lex = load_ply_lex() | ||
| 298 | lexer = lex.lexer | ||
| 299 | |||
| 300 | # Set up the lexer and parser objects on pslice | ||
| 301 | pslice.lexer = lexer | ||
| 302 | pslice.parser = self | ||
| 303 | |||
| 304 | # If input was supplied, pass to lexer | ||
| 305 | if input is not None: | ||
| 306 | lexer.input(input) | ||
| 307 | |||
| 308 | if tokenfunc is None: | ||
| 309 | # Tokenize function | ||
| 310 | get_token = lexer.token | ||
| 311 | else: | ||
| 312 | get_token = tokenfunc | ||
| 313 | |||
| 314 | # Set up the state and symbol stacks | ||
| 315 | |||
| 316 | statestack = [ ] # Stack of parsing states | ||
| 317 | self.statestack = statestack | ||
| 318 | symstack = [ ] # Stack of grammar symbols | ||
| 319 | self.symstack = symstack | ||
| 320 | |||
| 321 | pslice.stack = symstack # Put in the production | ||
| 322 | errtoken = None # Err token | ||
| 323 | |||
| 324 | # The start state is assumed to be (0,$end) | ||
| 325 | |||
| 326 | statestack.append(0) | ||
| 327 | sym = YaccSymbol() | ||
| 328 | sym.type = "$end" | ||
| 329 | symstack.append(sym) | ||
| 330 | state = 0 | ||
| 331 | while 1: | ||
| 332 | # Get the next symbol on the input. If a lookahead symbol | ||
| 333 | # is already set, we just use that. Otherwise, we'll pull | ||
| 334 | # the next token off of the lookaheadstack or from the lexer | ||
| 335 | |||
| 336 | # --! DEBUG | ||
| 337 | debug.debug('') | ||
| 338 | debug.debug('State : %s', state) | ||
| 339 | # --! DEBUG | ||
| 340 | |||
| 341 | if not lookahead: | ||
| 342 | if not lookaheadstack: | ||
| 343 | lookahead = get_token() # Get the next token | ||
| 344 | else: | ||
| 345 | lookahead = lookaheadstack.pop() | ||
| 346 | if not lookahead: | ||
| 347 | lookahead = YaccSymbol() | ||
| 348 | lookahead.type = "$end" | ||
| 349 | |||
| 350 | # --! DEBUG | ||
| 351 | debug.debug('Stack : %s', | ||
| 352 | ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) | ||
| 353 | # --! DEBUG | ||
| 354 | |||
| 355 | # Check the action table | ||
| 356 | ltype = lookahead.type | ||
| 357 | t = actions[state].get(ltype) | ||
| 358 | |||
| 359 | if t is not None: | ||
| 360 | if t > 0: | ||
| 361 | # shift a symbol on the stack | ||
| 362 | statestack.append(t) | ||
| 363 | state = t | ||
| 364 | |||
| 365 | # --! DEBUG | ||
| 366 | debug.debug("Action : Shift and goto state %s", t) | ||
| 367 | # --! DEBUG | ||
| 368 | |||
| 369 | symstack.append(lookahead) | ||
| 370 | lookahead = None | ||
| 371 | |||
| 372 | # Decrease error count on successful shift | ||
| 373 | if errorcount: errorcount -=1 | ||
| 374 | continue | ||
| 375 | |||
| 376 | if t < 0: | ||
| 377 | # reduce a symbol on the stack, emit a production | ||
| 378 | p = prod[-t] | ||
| 379 | pname = p.name | ||
| 380 | plen = p.len | ||
| 381 | |||
| 382 | # Get production function | ||
| 383 | sym = YaccSymbol() | ||
| 384 | sym.type = pname # Production name | ||
| 385 | sym.value = None | ||
| 386 | |||
| 387 | # --! DEBUG | ||
| 388 | if plen: | ||
| 389 | 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:]])+"]",-t) | ||
| 390 | else: | ||
| 391 | debug.info("Action : Reduce rule [%s] with %s and goto state %d", p.str, [],-t) | ||
| 392 | |||
| 393 | # --! DEBUG | ||
| 394 | |||
| 395 | if plen: | ||
| 396 | targ = symstack[-plen-1:] | ||
| 397 | targ[0] = sym | ||
| 398 | |||
| 399 | # --! TRACKING | ||
| 400 | if tracking: | ||
| 401 | t1 = targ[1] | ||
| 402 | sym.lineno = t1.lineno | ||
| 403 | sym.lexpos = t1.lexpos | ||
| 404 | t1 = targ[-1] | ||
| 405 | sym.endlineno = getattr(t1,"endlineno",t1.lineno) | ||
| 406 | sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) | ||
| 407 | |||
| 408 | # --! TRACKING | ||
| 409 | |||
| 410 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 411 | # The code enclosed in this section is duplicated | ||
| 412 | # below as a performance optimization. Make sure | ||
| 413 | # changes get made in both locations. | ||
| 414 | |||
| 415 | pslice.slice = targ | ||
| 416 | |||
| 417 | try: | ||
| 418 | # Call the grammar rule with our special slice object | ||
| 419 | del symstack[-plen:] | ||
| 420 | del statestack[-plen:] | ||
| 421 | p.callable(pslice) | ||
| 422 | # --! DEBUG | ||
| 423 | debug.info("Result : %s", format_result(pslice[0])) | ||
| 424 | # --! DEBUG | ||
| 425 | symstack.append(sym) | ||
| 426 | state = goto[statestack[-1]][pname] | ||
| 427 | statestack.append(state) | ||
| 428 | except SyntaxError: | ||
| 429 | # If an error was set. Enter error recovery state | ||
| 430 | lookaheadstack.append(lookahead) | ||
| 431 | symstack.pop() | ||
| 432 | statestack.pop() | ||
| 433 | state = statestack[-1] | ||
| 434 | sym.type = 'error' | ||
| 435 | lookahead = sym | ||
| 436 | errorcount = error_count | ||
| 437 | self.errorok = 0 | ||
| 438 | continue | ||
| 439 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 440 | |||
| 441 | else: | ||
| 442 | |||
| 443 | # --! TRACKING | ||
| 444 | if tracking: | ||
| 445 | sym.lineno = lexer.lineno | ||
| 446 | sym.lexpos = lexer.lexpos | ||
| 447 | # --! TRACKING | ||
| 448 | |||
| 449 | targ = [ sym ] | ||
| 450 | |||
| 451 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 452 | # The code enclosed in this section is duplicated | ||
| 453 | # above as a performance optimization. Make sure | ||
| 454 | # changes get made in both locations. | ||
| 455 | |||
| 456 | pslice.slice = targ | ||
| 457 | |||
| 458 | try: | ||
| 459 | # Call the grammar rule with our special slice object | ||
| 460 | p.callable(pslice) | ||
| 461 | # --! DEBUG | ||
| 462 | debug.info("Result : %s", format_result(pslice[0])) | ||
| 463 | # --! DEBUG | ||
| 464 | symstack.append(sym) | ||
| 465 | state = goto[statestack[-1]][pname] | ||
| 466 | statestack.append(state) | ||
| 467 | except SyntaxError: | ||
| 468 | # If an error was set. Enter error recovery state | ||
| 469 | lookaheadstack.append(lookahead) | ||
| 470 | symstack.pop() | ||
| 471 | statestack.pop() | ||
| 472 | state = statestack[-1] | ||
| 473 | sym.type = 'error' | ||
| 474 | lookahead = sym | ||
| 475 | errorcount = error_count | ||
| 476 | self.errorok = 0 | ||
| 477 | continue | ||
| 478 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 479 | |||
| 480 | if t == 0: | ||
| 481 | n = symstack[-1] | ||
| 482 | result = getattr(n,"value",None) | ||
| 483 | # --! DEBUG | ||
| 484 | debug.info("Done : Returning %s", format_result(result)) | ||
| 485 | debug.info("PLY: PARSE DEBUG END") | ||
| 486 | # --! DEBUG | ||
| 487 | return result | ||
| 488 | |||
| 489 | if t == None: | ||
| 490 | |||
| 491 | # --! DEBUG | ||
| 492 | debug.error('Error : %s', | ||
| 493 | ("%s . %s" % (" ".join([xx.type for xx in symstack][1:]), str(lookahead))).lstrip()) | ||
| 494 | # --! DEBUG | ||
| 495 | |||
| 496 | # We have some kind of parsing error here. To handle | ||
| 497 | # this, we are going to push the current token onto | ||
| 498 | # the tokenstack and replace it with an 'error' token. | ||
| 499 | # If there are any synchronization rules, they may | ||
| 500 | # catch it. | ||
| 501 | # | ||
| 502 | # In addition to pushing the error token, we call call | ||
| 503 | # the user defined p_error() function if this is the | ||
| 504 | # first syntax error. This function is only called if | ||
| 505 | # errorcount == 0. | ||
| 506 | if errorcount == 0 or self.errorok: | ||
| 507 | errorcount = error_count | ||
| 508 | self.errorok = 0 | ||
| 509 | errtoken = lookahead | ||
| 510 | if errtoken.type == "$end": | ||
| 511 | errtoken = None # End of file! | ||
| 512 | if self.errorfunc: | ||
| 513 | global errok,token,restart | ||
| 514 | errok = self.errok # Set some special functions available in error recovery | ||
| 515 | token = get_token | ||
| 516 | restart = self.restart | ||
| 517 | if errtoken and not hasattr(errtoken,'lexer'): | ||
| 518 | errtoken.lexer = lexer | ||
| 519 | tok = self.errorfunc(errtoken) | ||
| 520 | del errok, token, restart # Delete special functions | ||
| 521 | |||
| 522 | if self.errorok: | ||
| 523 | # User must have done some kind of panic | ||
| 524 | # mode recovery on their own. The | ||
| 525 | # returned token is the next lookahead | ||
| 526 | lookahead = tok | ||
| 527 | errtoken = None | ||
| 528 | continue | ||
| 529 | else: | ||
| 530 | if errtoken: | ||
| 531 | if hasattr(errtoken,"lineno"): lineno = lookahead.lineno | ||
| 532 | else: lineno = 0 | ||
| 533 | if lineno: | ||
| 534 | sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) | ||
| 535 | else: | ||
| 536 | sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) | ||
| 537 | else: | ||
| 538 | sys.stderr.write("yacc: Parse error in input. EOF\n") | ||
| 539 | return | ||
| 540 | |||
| 541 | else: | ||
| 542 | errorcount = error_count | ||
| 543 | |||
| 544 | # case 1: the statestack only has 1 entry on it. If we're in this state, the | ||
| 545 | # entire parse has been rolled back and we're completely hosed. The token is | ||
| 546 | # discarded and we just keep going. | ||
| 547 | |||
| 548 | if len(statestack) <= 1 and lookahead.type != "$end": | ||
| 549 | lookahead = None | ||
| 550 | errtoken = None | ||
| 551 | state = 0 | ||
| 552 | # Nuke the pushback stack | ||
| 553 | del lookaheadstack[:] | ||
| 554 | continue | ||
| 555 | |||
| 556 | # case 2: the statestack has a couple of entries on it, but we're | ||
| 557 | # at the end of the file. nuke the top entry and generate an error token | ||
| 558 | |||
| 559 | # Start nuking entries on the stack | ||
| 560 | if lookahead.type == "$end": | ||
| 561 | # Whoa. We're really hosed here. Bail out | ||
| 562 | return | ||
| 563 | |||
| 564 | if lookahead.type != 'error': | ||
| 565 | sym = symstack[-1] | ||
| 566 | if sym.type == 'error': | ||
| 567 | # Hmmm. Error is on top of stack, we'll just nuke input | ||
| 568 | # symbol and continue | ||
| 569 | lookahead = None | ||
| 570 | continue | ||
| 571 | t = YaccSymbol() | ||
| 572 | t.type = 'error' | ||
| 573 | if hasattr(lookahead,"lineno"): | ||
| 574 | t.lineno = lookahead.lineno | ||
| 575 | t.value = lookahead | ||
| 576 | lookaheadstack.append(lookahead) | ||
| 577 | lookahead = t | ||
| 578 | else: | ||
| 579 | symstack.pop() | ||
| 580 | statestack.pop() | ||
| 581 | state = statestack[-1] # Potential bug fix | ||
| 582 | |||
| 583 | continue | ||
| 584 | |||
| 585 | # Call an error function here | ||
| 586 | raise RuntimeError("yacc: internal parser error!!!\n") | ||
| 587 | |||
| 588 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 589 | # parseopt(). | ||
| 590 | # | ||
| 591 | # Optimized version of parse() method. DO NOT EDIT THIS CODE DIRECTLY. | ||
| 592 | # Edit the debug version above, then copy any modifications to the method | ||
| 593 | # below while removing #--! DEBUG sections. | ||
| 594 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 595 | |||
| 596 | |||
| 597 | def parseopt(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): | ||
| 598 | lookahead = None # Current lookahead symbol | ||
| 599 | lookaheadstack = [ ] # Stack of lookahead symbols | ||
| 600 | actions = self.action # Local reference to action table (to avoid lookup on self.) | ||
| 601 | goto = self.goto # Local reference to goto table (to avoid lookup on self.) | ||
| 602 | prod = self.productions # Local reference to production list (to avoid lookup on self.) | ||
| 603 | pslice = YaccProduction(None) # Production object passed to grammar rules | ||
| 604 | errorcount = 0 # Used during error recovery | ||
| 605 | |||
| 606 | # If no lexer was given, we will try to use the lex module | ||
| 607 | if not lexer: | ||
| 608 | lex = load_ply_lex() | ||
| 609 | lexer = lex.lexer | ||
| 610 | |||
| 611 | # Set up the lexer and parser objects on pslice | ||
| 612 | pslice.lexer = lexer | ||
| 613 | pslice.parser = self | ||
| 614 | |||
| 615 | # If input was supplied, pass to lexer | ||
| 616 | if input is not None: | ||
| 617 | lexer.input(input) | ||
| 618 | |||
| 619 | if tokenfunc is None: | ||
| 620 | # Tokenize function | ||
| 621 | get_token = lexer.token | ||
| 622 | else: | ||
| 623 | get_token = tokenfunc | ||
| 624 | |||
| 625 | # Set up the state and symbol stacks | ||
| 626 | |||
| 627 | statestack = [ ] # Stack of parsing states | ||
| 628 | self.statestack = statestack | ||
| 629 | symstack = [ ] # Stack of grammar symbols | ||
| 630 | self.symstack = symstack | ||
| 631 | |||
| 632 | pslice.stack = symstack # Put in the production | ||
| 633 | errtoken = None # Err token | ||
| 634 | |||
| 635 | # The start state is assumed to be (0,$end) | ||
| 636 | |||
| 637 | statestack.append(0) | ||
| 638 | sym = YaccSymbol() | ||
| 639 | sym.type = '$end' | ||
| 640 | symstack.append(sym) | ||
| 641 | state = 0 | ||
| 642 | while 1: | ||
| 643 | # Get the next symbol on the input. If a lookahead symbol | ||
| 644 | # is already set, we just use that. Otherwise, we'll pull | ||
| 645 | # the next token off of the lookaheadstack or from the lexer | ||
| 646 | |||
| 647 | if not lookahead: | ||
| 648 | if not lookaheadstack: | ||
| 649 | lookahead = get_token() # Get the next token | ||
| 650 | else: | ||
| 651 | lookahead = lookaheadstack.pop() | ||
| 652 | if not lookahead: | ||
| 653 | lookahead = YaccSymbol() | ||
| 654 | lookahead.type = '$end' | ||
| 655 | |||
| 656 | # Check the action table | ||
| 657 | ltype = lookahead.type | ||
| 658 | t = actions[state].get(ltype) | ||
| 659 | |||
| 660 | if t is not None: | ||
| 661 | if t > 0: | ||
| 662 | # shift a symbol on the stack | ||
| 663 | statestack.append(t) | ||
| 664 | state = t | ||
| 665 | |||
| 666 | symstack.append(lookahead) | ||
| 667 | lookahead = None | ||
| 668 | |||
| 669 | # Decrease error count on successful shift | ||
| 670 | if errorcount: errorcount -=1 | ||
| 671 | continue | ||
| 672 | |||
| 673 | if t < 0: | ||
| 674 | # reduce a symbol on the stack, emit a production | ||
| 675 | p = prod[-t] | ||
| 676 | pname = p.name | ||
| 677 | plen = p.len | ||
| 678 | |||
| 679 | # Get production function | ||
| 680 | sym = YaccSymbol() | ||
| 681 | sym.type = pname # Production name | ||
| 682 | sym.value = None | ||
| 683 | |||
| 684 | if plen: | ||
| 685 | targ = symstack[-plen-1:] | ||
| 686 | targ[0] = sym | ||
| 687 | |||
| 688 | # --! TRACKING | ||
| 689 | if tracking: | ||
| 690 | t1 = targ[1] | ||
| 691 | sym.lineno = t1.lineno | ||
| 692 | sym.lexpos = t1.lexpos | ||
| 693 | t1 = targ[-1] | ||
| 694 | sym.endlineno = getattr(t1,"endlineno",t1.lineno) | ||
| 695 | sym.endlexpos = getattr(t1,"endlexpos",t1.lexpos) | ||
| 696 | |||
| 697 | # --! TRACKING | ||
| 698 | |||
| 699 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 700 | # The code enclosed in this section is duplicated | ||
| 701 | # below as a performance optimization. Make sure | ||
| 702 | # changes get made in both locations. | ||
| 703 | |||
| 704 | pslice.slice = targ | ||
| 705 | |||
| 706 | try: | ||
| 707 | # Call the grammar rule with our special slice object | ||
| 708 | del symstack[-plen:] | ||
| 709 | del statestack[-plen:] | ||
| 710 | p.callable(pslice) | ||
| 711 | symstack.append(sym) | ||
| 712 | state = goto[statestack[-1]][pname] | ||
| 713 | statestack.append(state) | ||
| 714 | except SyntaxError: | ||
| 715 | # If an error was set. Enter error recovery state | ||
| 716 | lookaheadstack.append(lookahead) | ||
| 717 | symstack.pop() | ||
| 718 | statestack.pop() | ||
| 719 | state = statestack[-1] | ||
| 720 | sym.type = 'error' | ||
| 721 | lookahead = sym | ||
| 722 | errorcount = error_count | ||
| 723 | self.errorok = 0 | ||
| 724 | continue | ||
| 725 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 726 | |||
| 727 | else: | ||
| 728 | |||
| 729 | # --! TRACKING | ||
| 730 | if tracking: | ||
| 731 | sym.lineno = lexer.lineno | ||
| 732 | sym.lexpos = lexer.lexpos | ||
| 733 | # --! TRACKING | ||
| 734 | |||
| 735 | targ = [ sym ] | ||
| 736 | |||
| 737 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 738 | # The code enclosed in this section is duplicated | ||
| 739 | # above as a performance optimization. Make sure | ||
| 740 | # changes get made in both locations. | ||
| 741 | |||
| 742 | pslice.slice = targ | ||
| 743 | |||
| 744 | try: | ||
| 745 | # Call the grammar rule with our special slice object | ||
| 746 | p.callable(pslice) | ||
| 747 | symstack.append(sym) | ||
| 748 | state = goto[statestack[-1]][pname] | ||
| 749 | statestack.append(state) | ||
| 750 | except SyntaxError: | ||
| 751 | # If an error was set. Enter error recovery state | ||
| 752 | lookaheadstack.append(lookahead) | ||
| 753 | symstack.pop() | ||
| 754 | statestack.pop() | ||
| 755 | state = statestack[-1] | ||
| 756 | sym.type = 'error' | ||
| 757 | lookahead = sym | ||
| 758 | errorcount = error_count | ||
| 759 | self.errorok = 0 | ||
| 760 | continue | ||
| 761 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 762 | |||
| 763 | if t == 0: | ||
| 764 | n = symstack[-1] | ||
| 765 | return getattr(n,"value",None) | ||
| 766 | |||
| 767 | if t == None: | ||
| 768 | |||
| 769 | # We have some kind of parsing error here. To handle | ||
| 770 | # this, we are going to push the current token onto | ||
| 771 | # the tokenstack and replace it with an 'error' token. | ||
| 772 | # If there are any synchronization rules, they may | ||
| 773 | # catch it. | ||
| 774 | # | ||
| 775 | # In addition to pushing the error token, we call call | ||
| 776 | # the user defined p_error() function if this is the | ||
| 777 | # first syntax error. This function is only called if | ||
| 778 | # errorcount == 0. | ||
| 779 | if errorcount == 0 or self.errorok: | ||
| 780 | errorcount = error_count | ||
| 781 | self.errorok = 0 | ||
| 782 | errtoken = lookahead | ||
| 783 | if errtoken.type == '$end': | ||
| 784 | errtoken = None # End of file! | ||
| 785 | if self.errorfunc: | ||
| 786 | global errok,token,restart | ||
| 787 | errok = self.errok # Set some special functions available in error recovery | ||
| 788 | token = get_token | ||
| 789 | restart = self.restart | ||
| 790 | if errtoken and not hasattr(errtoken,'lexer'): | ||
| 791 | errtoken.lexer = lexer | ||
| 792 | tok = self.errorfunc(errtoken) | ||
| 793 | del errok, token, restart # Delete special functions | ||
| 794 | |||
| 795 | if self.errorok: | ||
| 796 | # User must have done some kind of panic | ||
| 797 | # mode recovery on their own. The | ||
| 798 | # returned token is the next lookahead | ||
| 799 | lookahead = tok | ||
| 800 | errtoken = None | ||
| 801 | continue | ||
| 802 | else: | ||
| 803 | if errtoken: | ||
| 804 | if hasattr(errtoken,"lineno"): lineno = lookahead.lineno | ||
| 805 | else: lineno = 0 | ||
| 806 | if lineno: | ||
| 807 | sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) | ||
| 808 | else: | ||
| 809 | sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) | ||
| 810 | else: | ||
| 811 | sys.stderr.write("yacc: Parse error in input. EOF\n") | ||
| 812 | return | ||
| 813 | |||
| 814 | else: | ||
| 815 | errorcount = error_count | ||
| 816 | |||
| 817 | # case 1: the statestack only has 1 entry on it. If we're in this state, the | ||
| 818 | # entire parse has been rolled back and we're completely hosed. The token is | ||
| 819 | # discarded and we just keep going. | ||
| 820 | |||
| 821 | if len(statestack) <= 1 and lookahead.type != '$end': | ||
| 822 | lookahead = None | ||
| 823 | errtoken = None | ||
| 824 | state = 0 | ||
| 825 | # Nuke the pushback stack | ||
| 826 | del lookaheadstack[:] | ||
| 827 | continue | ||
| 828 | |||
| 829 | # case 2: the statestack has a couple of entries on it, but we're | ||
| 830 | # at the end of the file. nuke the top entry and generate an error token | ||
| 831 | |||
| 832 | # Start nuking entries on the stack | ||
| 833 | if lookahead.type == '$end': | ||
| 834 | # Whoa. We're really hosed here. Bail out | ||
| 835 | return | ||
| 836 | |||
| 837 | if lookahead.type != 'error': | ||
| 838 | sym = symstack[-1] | ||
| 839 | if sym.type == 'error': | ||
| 840 | # Hmmm. Error is on top of stack, we'll just nuke input | ||
| 841 | # symbol and continue | ||
| 842 | lookahead = None | ||
| 843 | continue | ||
| 844 | t = YaccSymbol() | ||
| 845 | t.type = 'error' | ||
| 846 | if hasattr(lookahead,"lineno"): | ||
| 847 | t.lineno = lookahead.lineno | ||
| 848 | t.value = lookahead | ||
| 849 | lookaheadstack.append(lookahead) | ||
| 850 | lookahead = t | ||
| 851 | else: | ||
| 852 | symstack.pop() | ||
| 853 | statestack.pop() | ||
| 854 | state = statestack[-1] # Potential bug fix | ||
| 855 | |||
| 856 | continue | ||
| 857 | |||
| 858 | # Call an error function here | ||
| 859 | raise RuntimeError("yacc: internal parser error!!!\n") | ||
| 860 | |||
| 861 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 862 | # parseopt_notrack(). | ||
| 863 | # | ||
| 864 | # Optimized version of parseopt() with line number tracking removed. | ||
| 865 | # DO NOT EDIT THIS CODE DIRECTLY. Copy the optimized version and remove | ||
| 866 | # code in the #--! TRACKING sections | ||
| 867 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 868 | |||
| 869 | def parseopt_notrack(self,input=None,lexer=None,debug=0,tracking=0,tokenfunc=None): | ||
| 870 | lookahead = None # Current lookahead symbol | ||
| 871 | lookaheadstack = [ ] # Stack of lookahead symbols | ||
| 872 | actions = self.action # Local reference to action table (to avoid lookup on self.) | ||
| 873 | goto = self.goto # Local reference to goto table (to avoid lookup on self.) | ||
| 874 | prod = self.productions # Local reference to production list (to avoid lookup on self.) | ||
| 875 | pslice = YaccProduction(None) # Production object passed to grammar rules | ||
| 876 | errorcount = 0 # Used during error recovery | ||
| 877 | |||
| 878 | # If no lexer was given, we will try to use the lex module | ||
| 879 | if not lexer: | ||
| 880 | lex = load_ply_lex() | ||
| 881 | lexer = lex.lexer | ||
| 882 | |||
| 883 | # Set up the lexer and parser objects on pslice | ||
| 884 | pslice.lexer = lexer | ||
| 885 | pslice.parser = self | ||
| 886 | |||
| 887 | # If input was supplied, pass to lexer | ||
| 888 | if input is not None: | ||
| 889 | lexer.input(input) | ||
| 890 | |||
| 891 | if tokenfunc is None: | ||
| 892 | # Tokenize function | ||
| 893 | get_token = lexer.token | ||
| 894 | else: | ||
| 895 | get_token = tokenfunc | ||
| 896 | |||
| 897 | # Set up the state and symbol stacks | ||
| 898 | |||
| 899 | statestack = [ ] # Stack of parsing states | ||
| 900 | self.statestack = statestack | ||
| 901 | symstack = [ ] # Stack of grammar symbols | ||
| 902 | self.symstack = symstack | ||
| 903 | |||
| 904 | pslice.stack = symstack # Put in the production | ||
| 905 | errtoken = None # Err token | ||
| 906 | |||
| 907 | # The start state is assumed to be (0,$end) | ||
| 908 | |||
| 909 | statestack.append(0) | ||
| 910 | sym = YaccSymbol() | ||
| 911 | sym.type = '$end' | ||
| 912 | symstack.append(sym) | ||
| 913 | state = 0 | ||
| 914 | while 1: | ||
| 915 | # Get the next symbol on the input. If a lookahead symbol | ||
| 916 | # is already set, we just use that. Otherwise, we'll pull | ||
| 917 | # the next token off of the lookaheadstack or from the lexer | ||
| 918 | |||
| 919 | if not lookahead: | ||
| 920 | if not lookaheadstack: | ||
| 921 | lookahead = get_token() # Get the next token | ||
| 922 | else: | ||
| 923 | lookahead = lookaheadstack.pop() | ||
| 924 | if not lookahead: | ||
| 925 | lookahead = YaccSymbol() | ||
| 926 | lookahead.type = '$end' | ||
| 927 | |||
| 928 | # Check the action table | ||
| 929 | ltype = lookahead.type | ||
| 930 | t = actions[state].get(ltype) | ||
| 931 | |||
| 932 | if t is not None: | ||
| 933 | if t > 0: | ||
| 934 | # shift a symbol on the stack | ||
| 935 | statestack.append(t) | ||
| 936 | state = t | ||
| 937 | |||
| 938 | symstack.append(lookahead) | ||
| 939 | lookahead = None | ||
| 940 | |||
| 941 | # Decrease error count on successful shift | ||
| 942 | if errorcount: errorcount -=1 | ||
| 943 | continue | ||
| 944 | |||
| 945 | if t < 0: | ||
| 946 | # reduce a symbol on the stack, emit a production | ||
| 947 | p = prod[-t] | ||
| 948 | pname = p.name | ||
| 949 | plen = p.len | ||
| 950 | |||
| 951 | # Get production function | ||
| 952 | sym = YaccSymbol() | ||
| 953 | sym.type = pname # Production name | ||
| 954 | sym.value = None | ||
| 955 | |||
| 956 | if plen: | ||
| 957 | targ = symstack[-plen-1:] | ||
| 958 | targ[0] = sym | ||
| 959 | |||
| 960 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 961 | # The code enclosed in this section is duplicated | ||
| 962 | # below as a performance optimization. Make sure | ||
| 963 | # changes get made in both locations. | ||
| 964 | |||
| 965 | pslice.slice = targ | ||
| 966 | |||
| 967 | try: | ||
| 968 | # Call the grammar rule with our special slice object | ||
| 969 | del symstack[-plen:] | ||
| 970 | del statestack[-plen:] | ||
| 971 | p.callable(pslice) | ||
| 972 | symstack.append(sym) | ||
| 973 | state = goto[statestack[-1]][pname] | ||
| 974 | statestack.append(state) | ||
| 975 | except SyntaxError: | ||
| 976 | # If an error was set. Enter error recovery state | ||
| 977 | lookaheadstack.append(lookahead) | ||
| 978 | symstack.pop() | ||
| 979 | statestack.pop() | ||
| 980 | state = statestack[-1] | ||
| 981 | sym.type = 'error' | ||
| 982 | lookahead = sym | ||
| 983 | errorcount = error_count | ||
| 984 | self.errorok = 0 | ||
| 985 | continue | ||
| 986 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 987 | |||
| 988 | else: | ||
| 989 | |||
| 990 | targ = [ sym ] | ||
| 991 | |||
| 992 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 993 | # The code enclosed in this section is duplicated | ||
| 994 | # above as a performance optimization. Make sure | ||
| 995 | # changes get made in both locations. | ||
| 996 | |||
| 997 | pslice.slice = targ | ||
| 998 | |||
| 999 | try: | ||
| 1000 | # Call the grammar rule with our special slice object | ||
| 1001 | p.callable(pslice) | ||
| 1002 | symstack.append(sym) | ||
| 1003 | state = goto[statestack[-1]][pname] | ||
| 1004 | statestack.append(state) | ||
| 1005 | except SyntaxError: | ||
| 1006 | # If an error was set. Enter error recovery state | ||
| 1007 | lookaheadstack.append(lookahead) | ||
| 1008 | symstack.pop() | ||
| 1009 | statestack.pop() | ||
| 1010 | state = statestack[-1] | ||
| 1011 | sym.type = 'error' | ||
| 1012 | lookahead = sym | ||
| 1013 | errorcount = error_count | ||
| 1014 | self.errorok = 0 | ||
| 1015 | continue | ||
| 1016 | # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
| 1017 | |||
| 1018 | if t == 0: | ||
| 1019 | n = symstack[-1] | ||
| 1020 | return getattr(n,"value",None) | ||
| 1021 | |||
| 1022 | if t == None: | ||
| 1023 | |||
| 1024 | # We have some kind of parsing error here. To handle | ||
| 1025 | # this, we are going to push the current token onto | ||
| 1026 | # the tokenstack and replace it with an 'error' token. | ||
| 1027 | # If there are any synchronization rules, they may | ||
| 1028 | # catch it. | ||
| 1029 | # | ||
| 1030 | # In addition to pushing the error token, we call call | ||
| 1031 | # the user defined p_error() function if this is the | ||
| 1032 | # first syntax error. This function is only called if | ||
| 1033 | # errorcount == 0. | ||
| 1034 | if errorcount == 0 or self.errorok: | ||
| 1035 | errorcount = error_count | ||
| 1036 | self.errorok = 0 | ||
| 1037 | errtoken = lookahead | ||
| 1038 | if errtoken.type == '$end': | ||
| 1039 | errtoken = None # End of file! | ||
| 1040 | if self.errorfunc: | ||
| 1041 | global errok,token,restart | ||
| 1042 | errok = self.errok # Set some special functions available in error recovery | ||
| 1043 | token = get_token | ||
| 1044 | restart = self.restart | ||
| 1045 | if errtoken and not hasattr(errtoken,'lexer'): | ||
| 1046 | errtoken.lexer = lexer | ||
| 1047 | tok = self.errorfunc(errtoken) | ||
| 1048 | del errok, token, restart # Delete special functions | ||
| 1049 | |||
| 1050 | if self.errorok: | ||
| 1051 | # User must have done some kind of panic | ||
| 1052 | # mode recovery on their own. The | ||
| 1053 | # returned token is the next lookahead | ||
| 1054 | lookahead = tok | ||
| 1055 | errtoken = None | ||
| 1056 | continue | ||
| 1057 | else: | ||
| 1058 | if errtoken: | ||
| 1059 | if hasattr(errtoken,"lineno"): lineno = lookahead.lineno | ||
| 1060 | else: lineno = 0 | ||
| 1061 | if lineno: | ||
| 1062 | sys.stderr.write("yacc: Syntax error at line %d, token=%s\n" % (lineno, errtoken.type)) | ||
| 1063 | else: | ||
| 1064 | sys.stderr.write("yacc: Syntax error, token=%s" % errtoken.type) | ||
| 1065 | else: | ||
| 1066 | sys.stderr.write("yacc: Parse error in input. EOF\n") | ||
| 1067 | return | ||
| 1068 | |||
| 1069 | else: | ||
| 1070 | errorcount = error_count | ||
| 1071 | |||
| 1072 | # case 1: the statestack only has 1 entry on it. If we're in this state, the | ||
| 1073 | # entire parse has been rolled back and we're completely hosed. The token is | ||
| 1074 | # discarded and we just keep going. | ||
| 1075 | |||
| 1076 | if len(statestack) <= 1 and lookahead.type != '$end': | ||
| 1077 | lookahead = None | ||
| 1078 | errtoken = None | ||
| 1079 | state = 0 | ||
| 1080 | # Nuke the pushback stack | ||
| 1081 | del lookaheadstack[:] | ||
| 1082 | continue | ||
| 1083 | |||
| 1084 | # case 2: the statestack has a couple of entries on it, but we're | ||
| 1085 | # at the end of the file. nuke the top entry and generate an error token | ||
| 1086 | |||
| 1087 | # Start nuking entries on the stack | ||
| 1088 | if lookahead.type == '$end': | ||
| 1089 | # Whoa. We're really hosed here. Bail out | ||
| 1090 | return | ||
| 1091 | |||
| 1092 | if lookahead.type != 'error': | ||
| 1093 | sym = symstack[-1] | ||
| 1094 | if sym.type == 'error': | ||
| 1095 | # Hmmm. Error is on top of stack, we'll just nuke input | ||
| 1096 | # symbol and continue | ||
| 1097 | lookahead = None | ||
| 1098 | continue | ||
| 1099 | t = YaccSymbol() | ||
| 1100 | t.type = 'error' | ||
| 1101 | if hasattr(lookahead,"lineno"): | ||
| 1102 | t.lineno = lookahead.lineno | ||
| 1103 | t.value = lookahead | ||
| 1104 | lookaheadstack.append(lookahead) | ||
| 1105 | lookahead = t | ||
| 1106 | else: | ||
| 1107 | symstack.pop() | ||
| 1108 | statestack.pop() | ||
| 1109 | state = statestack[-1] # Potential bug fix | ||
| 1110 | |||
| 1111 | continue | ||
| 1112 | |||
| 1113 | # Call an error function here | ||
| 1114 | raise RuntimeError("yacc: internal parser error!!!\n") | ||
| 1115 | |||
| 1116 | # ----------------------------------------------------------------------------- | ||
| 1117 | # === Grammar Representation === | ||
| 1118 | # | ||
| 1119 | # The following functions, classes, and variables are used to represent and | ||
| 1120 | # manipulate the rules that make up a grammar. | ||
| 1121 | # ----------------------------------------------------------------------------- | ||
| 1122 | |||
| 1123 | import re | ||
| 1124 | |||
| 1125 | # regex matching identifiers | ||
| 1126 | _is_identifier = re.compile(r'^[a-zA-Z0-9_-]+$') | ||
| 1127 | |||
| 1128 | # ----------------------------------------------------------------------------- | ||
| 1129 | # class Production: | ||
| 1130 | # | ||
| 1131 | # This class stores the raw information about a single production or grammar rule. | ||
| 1132 | # A grammar rule refers to a specification such as this: | ||
| 1133 | # | ||
| 1134 | # expr : expr PLUS term | ||
| 1135 | # | ||
| 1136 | # Here are the basic attributes defined on all productions | ||
| 1137 | # | ||
| 1138 | # name - Name of the production. For example 'expr' | ||
| 1139 | # prod - A list of symbols on the right side ['expr','PLUS','term'] | ||
| 1140 | # prec - Production precedence level | ||
| 1141 | # number - Production number. | ||
| 1142 | # func - Function that executes on reduce | ||
| 1143 | # file - File where production function is defined | ||
| 1144 | # lineno - Line number where production function is defined | ||
| 1145 | # | ||
| 1146 | # The following attributes are defined or optional. | ||
| 1147 | # | ||
| 1148 | # len - Length of the production (number of symbols on right hand side) | ||
| 1149 | # usyms - Set of unique symbols found in the production | ||
| 1150 | # ----------------------------------------------------------------------------- | ||
| 1151 | |||
| 1152 | class Production(object): | ||
| 1153 | reduced = 0 | ||
| 1154 | def __init__(self,number,name,prod,precedence=('right',0),func=None,file='',line=0): | ||
| 1155 | self.name = name | ||
| 1156 | self.prod = tuple(prod) | ||
| 1157 | self.number = number | ||
| 1158 | self.func = func | ||
| 1159 | self.callable = None | ||
| 1160 | self.file = file | ||
| 1161 | self.line = line | ||
| 1162 | self.prec = precedence | ||
| 1163 | |||
| 1164 | # Internal settings used during table construction | ||
| 1165 | |||
| 1166 | self.len = len(self.prod) # Length of the production | ||
| 1167 | |||
| 1168 | # Create a list of unique production symbols used in the production | ||
| 1169 | self.usyms = [ ] | ||
| 1170 | for s in self.prod: | ||
| 1171 | if s not in self.usyms: | ||
| 1172 | self.usyms.append(s) | ||
| 1173 | |||
| 1174 | # List of all LR items for the production | ||
| 1175 | self.lr_items = [] | ||
| 1176 | self.lr_next = None | ||
| 1177 | |||
| 1178 | # Create a string representation | ||
| 1179 | if self.prod: | ||
| 1180 | self.str = "%s -> %s" % (self.name," ".join(self.prod)) | ||
| 1181 | else: | ||
| 1182 | self.str = "%s -> <empty>" % self.name | ||
| 1183 | |||
| 1184 | def __str__(self): | ||
| 1185 | return self.str | ||
| 1186 | |||
| 1187 | def __repr__(self): | ||
| 1188 | return "Production("+str(self)+")" | ||
| 1189 | |||
| 1190 | def __len__(self): | ||
| 1191 | return len(self.prod) | ||
| 1192 | |||
| 1193 | def __nonzero__(self): | ||
| 1194 | return 1 | ||
| 1195 | |||
| 1196 | def __getitem__(self,index): | ||
| 1197 | return self.prod[index] | ||
| 1198 | |||
| 1199 | # Return the nth lr_item from the production (or None if at the end) | ||
| 1200 | def lr_item(self,n): | ||
| 1201 | if n > len(self.prod): return None | ||
| 1202 | p = LRItem(self,n) | ||
| 1203 | |||
| 1204 | # Precompute the list of productions immediately following. Hack. Remove later | ||
| 1205 | try: | ||
| 1206 | p.lr_after = Prodnames[p.prod[n+1]] | ||
| 1207 | except (IndexError,KeyError): | ||
| 1208 | p.lr_after = [] | ||
| 1209 | try: | ||
| 1210 | p.lr_before = p.prod[n-1] | ||
| 1211 | except IndexError: | ||
| 1212 | p.lr_before = None | ||
| 1213 | |||
| 1214 | return p | ||
| 1215 | |||
| 1216 | # Bind the production function name to a callable | ||
| 1217 | def bind(self,pdict): | ||
| 1218 | if self.func: | ||
| 1219 | self.callable = pdict[self.func] | ||
| 1220 | |||
| 1221 | # This class serves as a minimal standin for Production objects when | ||
| 1222 | # reading table data from files. It only contains information | ||
| 1223 | # actually used by the LR parsing engine, plus some additional | ||
| 1224 | # debugging information. | ||
| 1225 | class MiniProduction(object): | ||
| 1226 | def __init__(self,str,name,len,func,file,line): | ||
| 1227 | self.name = name | ||
| 1228 | self.len = len | ||
| 1229 | self.func = func | ||
| 1230 | self.callable = None | ||
| 1231 | self.file = file | ||
| 1232 | self.line = line | ||
| 1233 | self.str = str | ||
| 1234 | def __str__(self): | ||
| 1235 | return self.str | ||
| 1236 | def __repr__(self): | ||
| 1237 | return "MiniProduction(%s)" % self.str | ||
| 1238 | |||
| 1239 | # Bind the production function name to a callable | ||
| 1240 | def bind(self,pdict): | ||
| 1241 | if self.func: | ||
| 1242 | self.callable = pdict[self.func] | ||
| 1243 | |||
| 1244 | |||
| 1245 | # ----------------------------------------------------------------------------- | ||
| 1246 | # class LRItem | ||
| 1247 | # | ||
| 1248 | # This class represents a specific stage of parsing a production rule. For | ||
| 1249 | # example: | ||
| 1250 | # | ||
| 1251 | # expr : expr . PLUS term | ||
| 1252 | # | ||
| 1253 | # In the above, the "." represents the current location of the parse. Here | ||
| 1254 | # basic attributes: | ||
| 1255 | # | ||
| 1256 | # name - Name of the production. For example 'expr' | ||
| 1257 | # prod - A list of symbols on the right side ['expr','.', 'PLUS','term'] | ||
| 1258 | # number - Production number. | ||
| 1259 | # | ||
| 1260 | # lr_next Next LR item. Example, if we are ' expr -> expr . PLUS term' | ||
| 1261 | # then lr_next refers to 'expr -> expr PLUS . term' | ||
| 1262 | # lr_index - LR item index (location of the ".") in the prod list. | ||
| 1263 | # lookaheads - LALR lookahead symbols for this item | ||
| 1264 | # len - Length of the production (number of symbols on right hand side) | ||
| 1265 | # lr_after - List of all productions that immediately follow | ||
| 1266 | # lr_before - Grammar symbol immediately before | ||
| 1267 | # ----------------------------------------------------------------------------- | ||
| 1268 | |||
| 1269 | class LRItem(object): | ||
| 1270 | def __init__(self,p,n): | ||
| 1271 | self.name = p.name | ||
| 1272 | self.prod = list(p.prod) | ||
| 1273 | self.number = p.number | ||
| 1274 | self.lr_index = n | ||
| 1275 | self.lookaheads = { } | ||
| 1276 | self.prod.insert(n,".") | ||
| 1277 | self.prod = tuple(self.prod) | ||
| 1278 | self.len = len(self.prod) | ||
| 1279 | self.usyms = p.usyms | ||
| 1280 | |||
| 1281 | def __str__(self): | ||
| 1282 | if self.prod: | ||
| 1283 | s = "%s -> %s" % (self.name," ".join(self.prod)) | ||
| 1284 | else: | ||
| 1285 | s = "%s -> <empty>" % self.name | ||
| 1286 | return s | ||
| 1287 | |||
| 1288 | def __repr__(self): | ||
| 1289 | return "LRItem("+str(self)+")" | ||
| 1290 | |||
| 1291 | # ----------------------------------------------------------------------------- | ||
| 1292 | # rightmost_terminal() | ||
| 1293 | # | ||
| 1294 | # Return the rightmost terminal from a list of symbols. Used in add_production() | ||
| 1295 | # ----------------------------------------------------------------------------- | ||
| 1296 | def rightmost_terminal(symbols, terminals): | ||
| 1297 | i = len(symbols) - 1 | ||
| 1298 | while i >= 0: | ||
| 1299 | if symbols[i] in terminals: | ||
| 1300 | return symbols[i] | ||
| 1301 | i -= 1 | ||
| 1302 | return None | ||
| 1303 | |||
| 1304 | # ----------------------------------------------------------------------------- | ||
| 1305 | # === GRAMMAR CLASS === | ||
| 1306 | # | ||
| 1307 | # The following class represents the contents of the specified grammar along | ||
| 1308 | # with various computed properties such as first sets, follow sets, LR items, etc. | ||
| 1309 | # This data is used for critical parts of the table generation process later. | ||
| 1310 | # ----------------------------------------------------------------------------- | ||
| 1311 | |||
| 1312 | class GrammarError(YaccError): pass | ||
| 1313 | |||
| 1314 | class Grammar(object): | ||
| 1315 | def __init__(self,terminals): | ||
| 1316 | self.Productions = [None] # A list of all of the productions. The first | ||
| 1317 | # entry is always reserved for the purpose of | ||
| 1318 | # building an augmented grammar | ||
| 1319 | |||
| 1320 | self.Prodnames = { } # A dictionary mapping the names of nonterminals to a list of all | ||
| 1321 | # productions of that nonterminal. | ||
| 1322 | |||
| 1323 | self.Prodmap = { } # A dictionary that is only used to detect duplicate | ||
| 1324 | # productions. | ||
| 1325 | |||
| 1326 | self.Terminals = { } # A dictionary mapping the names of terminal symbols to a | ||
| 1327 | # list of the rules where they are used. | ||
| 1328 | |||
| 1329 | for term in terminals: | ||
| 1330 | self.Terminals[term] = [] | ||
| 1331 | |||
| 1332 | self.Terminals['error'] = [] | ||
| 1333 | |||
| 1334 | self.Nonterminals = { } # A dictionary mapping names of nonterminals to a list | ||
| 1335 | # of rule numbers where they are used. | ||
| 1336 | |||
| 1337 | self.First = { } # A dictionary of precomputed FIRST(x) symbols | ||
| 1338 | |||
| 1339 | self.Follow = { } # A dictionary of precomputed FOLLOW(x) symbols | ||
| 1340 | |||
| 1341 | self.Precedence = { } # Precedence rules for each terminal. Contains tuples of the | ||
| 1342 | # form ('right',level) or ('nonassoc', level) or ('left',level) | ||
| 1343 | |||
| 1344 | self.UsedPrecedence = { } # Precedence rules that were actually used by the grammer. | ||
| 1345 | # This is only used to provide error checking and to generate | ||
| 1346 | # a warning about unused precedence rules. | ||
| 1347 | |||
| 1348 | self.Start = None # Starting symbol for the grammar | ||
| 1349 | |||
| 1350 | |||
| 1351 | def __len__(self): | ||
| 1352 | return len(self.Productions) | ||
| 1353 | |||
| 1354 | def __getitem__(self,index): | ||
| 1355 | return self.Productions[index] | ||
| 1356 | |||
| 1357 | # ----------------------------------------------------------------------------- | ||
| 1358 | # set_precedence() | ||
| 1359 | # | ||
| 1360 | # Sets the precedence for a given terminal. assoc is the associativity such as | ||
| 1361 | # 'left','right', or 'nonassoc'. level is a numeric level. | ||
| 1362 | # | ||
| 1363 | # ----------------------------------------------------------------------------- | ||
| 1364 | |||
| 1365 | def set_precedence(self,term,assoc,level): | ||
| 1366 | assert self.Productions == [None],"Must call set_precedence() before add_production()" | ||
| 1367 | if term in self.Precedence: | ||
| 1368 | raise GrammarError("Precedence already specified for terminal '%s'" % term) | ||
| 1369 | if assoc not in ['left','right','nonassoc']: | ||
| 1370 | raise GrammarError("Associativity must be one of 'left','right', or 'nonassoc'") | ||
| 1371 | self.Precedence[term] = (assoc,level) | ||
| 1372 | |||
| 1373 | # ----------------------------------------------------------------------------- | ||
| 1374 | # add_production() | ||
| 1375 | # | ||
| 1376 | # Given an action function, this function assembles a production rule and | ||
| 1377 | # computes its precedence level. | ||
| 1378 | # | ||
| 1379 | # The production rule is supplied as a list of symbols. For example, | ||
| 1380 | # a rule such as 'expr : expr PLUS term' has a production name of 'expr' and | ||
| 1381 | # symbols ['expr','PLUS','term']. | ||
| 1382 | # | ||
| 1383 | # Precedence is determined by the precedence of the right-most non-terminal | ||
| 1384 | # or the precedence of a terminal specified by %prec. | ||
| 1385 | # | ||
| 1386 | # A variety of error checks are performed to make sure production symbols | ||
| 1387 | # are valid and that %prec is used correctly. | ||
| 1388 | # ----------------------------------------------------------------------------- | ||
| 1389 | |||
| 1390 | def add_production(self,prodname,syms,func=None,file='',line=0): | ||
| 1391 | |||
| 1392 | if prodname in self.Terminals: | ||
| 1393 | raise GrammarError("%s:%d: Illegal rule name '%s'. Already defined as a token" % (file,line,prodname)) | ||
| 1394 | if prodname == 'error': | ||
| 1395 | raise GrammarError("%s:%d: Illegal rule name '%s'. error is a reserved word" % (file,line,prodname)) | ||
| 1396 | if not _is_identifier.match(prodname): | ||
| 1397 | raise GrammarError("%s:%d: Illegal rule name '%s'" % (file,line,prodname)) | ||
| 1398 | |||
| 1399 | # Look for literal tokens | ||
| 1400 | for n,s in enumerate(syms): | ||
| 1401 | if s[0] in "'\"": | ||
| 1402 | try: | ||
| 1403 | c = eval(s) | ||
| 1404 | if (len(c) > 1): | ||
| 1405 | raise GrammarError("%s:%d: Literal token %s in rule '%s' may only be a single character" % (file,line,s, prodname)) | ||
| 1406 | if not c in self.Terminals: | ||
| 1407 | self.Terminals[c] = [] | ||
| 1408 | syms[n] = c | ||
| 1409 | continue | ||
| 1410 | except SyntaxError: | ||
| 1411 | pass | ||
| 1412 | if not _is_identifier.match(s) and s != '%prec': | ||
| 1413 | raise GrammarError("%s:%d: Illegal name '%s' in rule '%s'" % (file,line,s, prodname)) | ||
| 1414 | |||
| 1415 | # Determine the precedence level | ||
| 1416 | if '%prec' in syms: | ||
| 1417 | if syms[-1] == '%prec': | ||
| 1418 | raise GrammarError("%s:%d: Syntax error. Nothing follows %%prec" % (file,line)) | ||
| 1419 | if syms[-2] != '%prec': | ||
| 1420 | raise GrammarError("%s:%d: Syntax error. %%prec can only appear at the end of a grammar rule" % (file,line)) | ||
| 1421 | precname = syms[-1] | ||
| 1422 | prodprec = self.Precedence.get(precname,None) | ||
| 1423 | if not prodprec: | ||
| 1424 | raise GrammarError("%s:%d: Nothing known about the precedence of '%s'" % (file,line,precname)) | ||
| 1425 | else: | ||
| 1426 | self.UsedPrecedence[precname] = 1 | ||
| 1427 | del syms[-2:] # Drop %prec from the rule | ||
| 1428 | else: | ||
| 1429 | # If no %prec, precedence is determined by the rightmost terminal symbol | ||
| 1430 | precname = rightmost_terminal(syms,self.Terminals) | ||
| 1431 | prodprec = self.Precedence.get(precname,('right',0)) | ||
| 1432 | |||
| 1433 | # See if the rule is already in the rulemap | ||
| 1434 | map = "%s -> %s" % (prodname,syms) | ||
| 1435 | if map in self.Prodmap: | ||
| 1436 | m = self.Prodmap[map] | ||
| 1437 | raise GrammarError("%s:%d: Duplicate rule %s. " % (file,line, m) + | ||
| 1438 | "Previous definition at %s:%d" % (m.file, m.line)) | ||
| 1439 | |||
| 1440 | # From this point on, everything is valid. Create a new Production instance | ||
| 1441 | pnumber = len(self.Productions) | ||
| 1442 | if not prodname in self.Nonterminals: | ||
| 1443 | self.Nonterminals[prodname] = [ ] | ||
| 1444 | |||
| 1445 | # Add the production number to Terminals and Nonterminals | ||
| 1446 | for t in syms: | ||
| 1447 | if t in self.Terminals: | ||
| 1448 | self.Terminals[t].append(pnumber) | ||
| 1449 | else: | ||
| 1450 | if not t in self.Nonterminals: | ||
| 1451 | self.Nonterminals[t] = [ ] | ||
| 1452 | self.Nonterminals[t].append(pnumber) | ||
| 1453 | |||
| 1454 | # Create a production and add it to the list of productions | ||
| 1455 | p = Production(pnumber,prodname,syms,prodprec,func,file,line) | ||
| 1456 | self.Productions.append(p) | ||
| 1457 | self.Prodmap[map] = p | ||
| 1458 | |||
| 1459 | # Add to the global productions list | ||
| 1460 | try: | ||
| 1461 | self.Prodnames[prodname].append(p) | ||
| 1462 | except KeyError: | ||
| 1463 | self.Prodnames[prodname] = [ p ] | ||
| 1464 | return 0 | ||
| 1465 | |||
| 1466 | # ----------------------------------------------------------------------------- | ||
| 1467 | # set_start() | ||
| 1468 | # | ||
| 1469 | # Sets the starting symbol and creates the augmented grammar. Production | ||
| 1470 | # rule 0 is S' -> start where start is the start symbol. | ||
| 1471 | # ----------------------------------------------------------------------------- | ||
| 1472 | |||
| 1473 | def set_start(self,start=None): | ||
| 1474 | if not start: | ||
| 1475 | start = self.Productions[1].name | ||
| 1476 | if start not in self.Nonterminals: | ||
| 1477 | raise GrammarError("start symbol %s undefined" % start) | ||
| 1478 | self.Productions[0] = Production(0,"S'",[start]) | ||
| 1479 | self.Nonterminals[start].append(0) | ||
| 1480 | self.Start = start | ||
| 1481 | |||
| 1482 | # ----------------------------------------------------------------------------- | ||
| 1483 | # find_unreachable() | ||
| 1484 | # | ||
| 1485 | # Find all of the nonterminal symbols that can't be reached from the starting | ||
| 1486 | # symbol. Returns a list of nonterminals that can't be reached. | ||
| 1487 | # ----------------------------------------------------------------------------- | ||
| 1488 | |||
| 1489 | def find_unreachable(self): | ||
| 1490 | |||
| 1491 | # Mark all symbols that are reachable from a symbol s | ||
| 1492 | def mark_reachable_from(s): | ||
| 1493 | if reachable[s]: | ||
| 1494 | # We've already reached symbol s. | ||
| 1495 | return | ||
| 1496 | reachable[s] = 1 | ||
| 1497 | for p in self.Prodnames.get(s,[]): | ||
| 1498 | for r in p.prod: | ||
| 1499 | mark_reachable_from(r) | ||
| 1500 | |||
| 1501 | reachable = { } | ||
| 1502 | for s in list(self.Terminals) + list(self.Nonterminals): | ||
| 1503 | reachable[s] = 0 | ||
| 1504 | |||
| 1505 | mark_reachable_from( self.Productions[0].prod[0] ) | ||
| 1506 | |||
| 1507 | return [s for s in list(self.Nonterminals) | ||
| 1508 | if not reachable[s]] | ||
| 1509 | |||
| 1510 | # ----------------------------------------------------------------------------- | ||
| 1511 | # infinite_cycles() | ||
| 1512 | # | ||
| 1513 | # This function looks at the various parsing rules and tries to detect | ||
| 1514 | # infinite recursion cycles (grammar rules where there is no possible way | ||
| 1515 | # to derive a string of only terminals). | ||
| 1516 | # ----------------------------------------------------------------------------- | ||
| 1517 | |||
| 1518 | def infinite_cycles(self): | ||
| 1519 | terminates = {} | ||
| 1520 | |||
| 1521 | # Terminals: | ||
| 1522 | for t in self.Terminals: | ||
| 1523 | terminates[t] = 1 | ||
| 1524 | |||
| 1525 | terminates['$end'] = 1 | ||
| 1526 | |||
| 1527 | # Nonterminals: | ||
| 1528 | |||
| 1529 | # Initialize to false: | ||
| 1530 | for n in self.Nonterminals: | ||
| 1531 | terminates[n] = 0 | ||
| 1532 | |||
| 1533 | # Then propagate termination until no change: | ||
| 1534 | while 1: | ||
| 1535 | some_change = 0 | ||
| 1536 | for (n,pl) in self.Prodnames.items(): | ||
| 1537 | # Nonterminal n terminates iff any of its productions terminates. | ||
| 1538 | for p in pl: | ||
| 1539 | # Production p terminates iff all of its rhs symbols terminate. | ||
| 1540 | for s in p.prod: | ||
| 1541 | if not terminates[s]: | ||
| 1542 | # The symbol s does not terminate, | ||
| 1543 | # so production p does not terminate. | ||
| 1544 | p_terminates = 0 | ||
| 1545 | break | ||
| 1546 | else: | ||
| 1547 | # didn't break from the loop, | ||
| 1548 | # so every symbol s terminates | ||
| 1549 | # so production p terminates. | ||
| 1550 | p_terminates = 1 | ||
| 1551 | |||
| 1552 | if p_terminates: | ||
| 1553 | # symbol n terminates! | ||
| 1554 | if not terminates[n]: | ||
| 1555 | terminates[n] = 1 | ||
| 1556 | some_change = 1 | ||
| 1557 | # Don't need to consider any more productions for this n. | ||
| 1558 | break | ||
| 1559 | |||
| 1560 | if not some_change: | ||
| 1561 | break | ||
| 1562 | |||
| 1563 | infinite = [] | ||
| 1564 | for (s,term) in terminates.items(): | ||
| 1565 | if not term: | ||
| 1566 | if not s in self.Prodnames and not s in self.Terminals and s != 'error': | ||
| 1567 | # s is used-but-not-defined, and we've already warned of that, | ||
| 1568 | # so it would be overkill to say that it's also non-terminating. | ||
| 1569 | pass | ||
| 1570 | else: | ||
| 1571 | infinite.append(s) | ||
| 1572 | |||
| 1573 | return infinite | ||
| 1574 | |||
| 1575 | |||
| 1576 | # ----------------------------------------------------------------------------- | ||
| 1577 | # undefined_symbols() | ||
| 1578 | # | ||
| 1579 | # Find all symbols that were used the grammar, but not defined as tokens or | ||
| 1580 | # grammar rules. Returns a list of tuples (sym, prod) where sym in the symbol | ||
| 1581 | # and prod is the production where the symbol was used. | ||
| 1582 | # ----------------------------------------------------------------------------- | ||
| 1583 | def undefined_symbols(self): | ||
| 1584 | result = [] | ||
| 1585 | for p in self.Productions: | ||
| 1586 | if not p: continue | ||
| 1587 | |||
| 1588 | for s in p.prod: | ||
| 1589 | if not s in self.Prodnames and not s in self.Terminals and s != 'error': | ||
| 1590 | result.append((s,p)) | ||
| 1591 | return result | ||
| 1592 | |||
| 1593 | # ----------------------------------------------------------------------------- | ||
| 1594 | # unused_terminals() | ||
| 1595 | # | ||
| 1596 | # Find all terminals that were defined, but not used by the grammar. Returns | ||
| 1597 | # a list of all symbols. | ||
| 1598 | # ----------------------------------------------------------------------------- | ||
| 1599 | def unused_terminals(self): | ||
| 1600 | unused_tok = [] | ||
| 1601 | for s,v in self.Terminals.items(): | ||
| 1602 | if s != 'error' and not v: | ||
| 1603 | unused_tok.append(s) | ||
| 1604 | |||
| 1605 | return unused_tok | ||
| 1606 | |||
| 1607 | # ------------------------------------------------------------------------------ | ||
| 1608 | # unused_rules() | ||
| 1609 | # | ||
| 1610 | # Find all grammar rules that were defined, but not used (maybe not reachable) | ||
| 1611 | # Returns a list of productions. | ||
| 1612 | # ------------------------------------------------------------------------------ | ||
| 1613 | |||
| 1614 | def unused_rules(self): | ||
| 1615 | unused_prod = [] | ||
| 1616 | for s,v in self.Nonterminals.items(): | ||
| 1617 | if not v: | ||
| 1618 | p = self.Prodnames[s][0] | ||
| 1619 | unused_prod.append(p) | ||
| 1620 | return unused_prod | ||
| 1621 | |||
| 1622 | # ----------------------------------------------------------------------------- | ||
| 1623 | # unused_precedence() | ||
| 1624 | # | ||
| 1625 | # Returns a list of tuples (term,precedence) corresponding to precedence | ||
| 1626 | # rules that were never used by the grammar. term is the name of the terminal | ||
| 1627 | # on which precedence was applied and precedence is a string such as 'left' or | ||
| 1628 | # 'right' corresponding to the type of precedence. | ||
| 1629 | # ----------------------------------------------------------------------------- | ||
| 1630 | |||
| 1631 | def unused_precedence(self): | ||
| 1632 | unused = [] | ||
| 1633 | for termname in self.Precedence: | ||
| 1634 | if not (termname in self.Terminals or termname in self.UsedPrecedence): | ||
| 1635 | unused.append((termname,self.Precedence[termname][0])) | ||
| 1636 | |||
| 1637 | return unused | ||
| 1638 | |||
| 1639 | # ------------------------------------------------------------------------- | ||
| 1640 | # _first() | ||
| 1641 | # | ||
| 1642 | # Compute the value of FIRST1(beta) where beta is a tuple of symbols. | ||
| 1643 | # | ||
| 1644 | # During execution of compute_first1, the result may be incomplete. | ||
| 1645 | # Afterward (e.g., when called from compute_follow()), it will be complete. | ||
| 1646 | # ------------------------------------------------------------------------- | ||
| 1647 | def _first(self,beta): | ||
| 1648 | |||
| 1649 | # We are computing First(x1,x2,x3,...,xn) | ||
| 1650 | result = [ ] | ||
| 1651 | for x in beta: | ||
| 1652 | x_produces_empty = 0 | ||
| 1653 | |||
| 1654 | # Add all the non-<empty> symbols of First[x] to the result. | ||
| 1655 | for f in self.First[x]: | ||
| 1656 | if f == '<empty>': | ||
| 1657 | x_produces_empty = 1 | ||
| 1658 | else: | ||
| 1659 | if f not in result: result.append(f) | ||
| 1660 | |||
| 1661 | if x_produces_empty: | ||
| 1662 | # We have to consider the next x in beta, | ||
| 1663 | # i.e. stay in the loop. | ||
| 1664 | pass | ||
| 1665 | else: | ||
| 1666 | # We don't have to consider any further symbols in beta. | ||
| 1667 | break | ||
| 1668 | else: | ||
| 1669 | # There was no 'break' from the loop, | ||
| 1670 | # so x_produces_empty was true for all x in beta, | ||
| 1671 | # so beta produces empty as well. | ||
| 1672 | result.append('<empty>') | ||
| 1673 | |||
| 1674 | return result | ||
| 1675 | |||
| 1676 | # ------------------------------------------------------------------------- | ||
| 1677 | # compute_first() | ||
| 1678 | # | ||
| 1679 | # Compute the value of FIRST1(X) for all symbols | ||
| 1680 | # ------------------------------------------------------------------------- | ||
| 1681 | def compute_first(self): | ||
| 1682 | if self.First: | ||
| 1683 | return self.First | ||
| 1684 | |||
| 1685 | # Terminals: | ||
| 1686 | for t in self.Terminals: | ||
| 1687 | self.First[t] = [t] | ||
| 1688 | |||
| 1689 | self.First['$end'] = ['$end'] | ||
| 1690 | |||
| 1691 | # Nonterminals: | ||
| 1692 | |||
| 1693 | # Initialize to the empty set: | ||
| 1694 | for n in self.Nonterminals: | ||
| 1695 | self.First[n] = [] | ||
| 1696 | |||
| 1697 | # Then propagate symbols until no change: | ||
| 1698 | while 1: | ||
| 1699 | some_change = 0 | ||
| 1700 | for n in self.Nonterminals: | ||
| 1701 | for p in self.Prodnames[n]: | ||
| 1702 | for f in self._first(p.prod): | ||
| 1703 | if f not in self.First[n]: | ||
| 1704 | self.First[n].append( f ) | ||
| 1705 | some_change = 1 | ||
| 1706 | if not some_change: | ||
| 1707 | break | ||
| 1708 | |||
| 1709 | return self.First | ||
| 1710 | |||
| 1711 | # --------------------------------------------------------------------- | ||
| 1712 | # compute_follow() | ||
| 1713 | # | ||
| 1714 | # Computes all of the follow sets for every non-terminal symbol. The | ||
| 1715 | # follow set is the set of all symbols that might follow a given | ||
| 1716 | # non-terminal. See the Dragon book, 2nd Ed. p. 189. | ||
| 1717 | # --------------------------------------------------------------------- | ||
| 1718 | def compute_follow(self,start=None): | ||
| 1719 | # If already computed, return the result | ||
| 1720 | if self.Follow: | ||
| 1721 | return self.Follow | ||
| 1722 | |||
| 1723 | # If first sets not computed yet, do that first. | ||
| 1724 | if not self.First: | ||
| 1725 | self.compute_first() | ||
| 1726 | |||
| 1727 | # Add '$end' to the follow list of the start symbol | ||
| 1728 | for k in self.Nonterminals: | ||
| 1729 | self.Follow[k] = [ ] | ||
| 1730 | |||
| 1731 | if not start: | ||
| 1732 | start = self.Productions[1].name | ||
| 1733 | |||
| 1734 | self.Follow[start] = [ '$end' ] | ||
| 1735 | |||
| 1736 | while 1: | ||
| 1737 | didadd = 0 | ||
| 1738 | for p in self.Productions[1:]: | ||
| 1739 | # Here is the production set | ||
| 1740 | for i in range(len(p.prod)): | ||
| 1741 | B = p.prod[i] | ||
| 1742 | if B in self.Nonterminals: | ||
| 1743 | # Okay. We got a non-terminal in a production | ||
| 1744 | fst = self._first(p.prod[i+1:]) | ||
| 1745 | hasempty = 0 | ||
| 1746 | for f in fst: | ||
| 1747 | if f != '<empty>' and f not in self.Follow[B]: | ||
| 1748 | self.Follow[B].append(f) | ||
| 1749 | didadd = 1 | ||
| 1750 | if f == '<empty>': | ||
| 1751 | hasempty = 1 | ||
| 1752 | if hasempty or i == (len(p.prod)-1): | ||
| 1753 | # Add elements of follow(a) to follow(b) | ||
| 1754 | for f in self.Follow[p.name]: | ||
| 1755 | if f not in self.Follow[B]: | ||
| 1756 | self.Follow[B].append(f) | ||
| 1757 | didadd = 1 | ||
| 1758 | if not didadd: break | ||
| 1759 | return self.Follow | ||
| 1760 | |||
| 1761 | |||
| 1762 | # ----------------------------------------------------------------------------- | ||
| 1763 | # build_lritems() | ||
| 1764 | # | ||
| 1765 | # This function walks the list of productions and builds a complete set of the | ||
| 1766 | # LR items. The LR items are stored in two ways: First, they are uniquely | ||
| 1767 | # numbered and placed in the list _lritems. Second, a linked list of LR items | ||
| 1768 | # is built for each production. For example: | ||
| 1769 | # | ||
| 1770 | # E -> E PLUS E | ||
| 1771 | # | ||
| 1772 | # Creates the list | ||
| 1773 | # | ||
| 1774 | # [E -> . E PLUS E, E -> E . PLUS E, E -> E PLUS . E, E -> E PLUS E . ] | ||
| 1775 | # ----------------------------------------------------------------------------- | ||
| 1776 | |||
| 1777 | def build_lritems(self): | ||
| 1778 | for p in self.Productions: | ||
| 1779 | lastlri = p | ||
| 1780 | i = 0 | ||
| 1781 | lr_items = [] | ||
| 1782 | while 1: | ||
| 1783 | if i > len(p): | ||
| 1784 | lri = None | ||
| 1785 | else: | ||
| 1786 | lri = LRItem(p,i) | ||
| 1787 | # Precompute the list of productions immediately following | ||
| 1788 | try: | ||
| 1789 | lri.lr_after = self.Prodnames[lri.prod[i+1]] | ||
| 1790 | except (IndexError,KeyError): | ||
| 1791 | lri.lr_after = [] | ||
| 1792 | try: | ||
| 1793 | lri.lr_before = lri.prod[i-1] | ||
| 1794 | except IndexError: | ||
| 1795 | lri.lr_before = None | ||
| 1796 | |||
| 1797 | lastlri.lr_next = lri | ||
| 1798 | if not lri: break | ||
| 1799 | lr_items.append(lri) | ||
| 1800 | lastlri = lri | ||
| 1801 | i += 1 | ||
| 1802 | p.lr_items = lr_items | ||
| 1803 | |||
| 1804 | # ----------------------------------------------------------------------------- | ||
| 1805 | # == Class LRTable == | ||
| 1806 | # | ||
| 1807 | # This basic class represents a basic table of LR parsing information. | ||
| 1808 | # Methods for generating the tables are not defined here. They are defined | ||
| 1809 | # in the derived class LRGeneratedTable. | ||
| 1810 | # ----------------------------------------------------------------------------- | ||
| 1811 | |||
| 1812 | class VersionError(YaccError): pass | ||
| 1813 | |||
| 1814 | class LRTable(object): | ||
| 1815 | def __init__(self): | ||
| 1816 | self.lr_action = None | ||
| 1817 | self.lr_goto = None | ||
| 1818 | self.lr_productions = None | ||
| 1819 | self.lr_method = None | ||
| 1820 | |||
| 1821 | def read_table(self,module): | ||
| 1822 | if isinstance(module,types.ModuleType): | ||
| 1823 | parsetab = module | ||
| 1824 | else: | ||
| 1825 | if sys.version_info[0] < 3: | ||
| 1826 | exec("import %s as parsetab" % module) | ||
| 1827 | else: | ||
| 1828 | env = { } | ||
| 1829 | exec("import %s as parsetab" % module, env, env) | ||
| 1830 | parsetab = env['parsetab'] | ||
| 1831 | |||
| 1832 | if parsetab._tabversion != __tabversion__: | ||
| 1833 | raise VersionError("yacc table file version is out of date") | ||
| 1834 | |||
| 1835 | self.lr_action = parsetab._lr_action | ||
| 1836 | self.lr_goto = parsetab._lr_goto | ||
| 1837 | |||
| 1838 | self.lr_productions = [] | ||
| 1839 | for p in parsetab._lr_productions: | ||
| 1840 | self.lr_productions.append(MiniProduction(*p)) | ||
| 1841 | |||
| 1842 | self.lr_method = parsetab._lr_method | ||
| 1843 | return parsetab._lr_signature | ||
| 1844 | |||
| 1845 | def read_pickle(self,filename): | ||
| 1846 | try: | ||
| 1847 | import cPickle as pickle | ||
| 1848 | except ImportError: | ||
| 1849 | import pickle | ||
| 1850 | |||
| 1851 | in_f = open(filename,"rb") | ||
| 1852 | |||
| 1853 | tabversion = pickle.load(in_f) | ||
| 1854 | if tabversion != __tabversion__: | ||
| 1855 | raise VersionError("yacc table file version is out of date") | ||
| 1856 | self.lr_method = pickle.load(in_f) | ||
| 1857 | signature = pickle.load(in_f) | ||
| 1858 | self.lr_action = pickle.load(in_f) | ||
| 1859 | self.lr_goto = pickle.load(in_f) | ||
| 1860 | productions = pickle.load(in_f) | ||
| 1861 | |||
| 1862 | self.lr_productions = [] | ||
| 1863 | for p in productions: | ||
| 1864 | self.lr_productions.append(MiniProduction(*p)) | ||
| 1865 | |||
| 1866 | in_f.close() | ||
| 1867 | return signature | ||
| 1868 | |||
| 1869 | # Bind all production function names to callable objects in pdict | ||
| 1870 | def bind_callables(self,pdict): | ||
| 1871 | for p in self.lr_productions: | ||
| 1872 | p.bind(pdict) | ||
| 1873 | |||
| 1874 | # ----------------------------------------------------------------------------- | ||
| 1875 | # === LR Generator === | ||
| 1876 | # | ||
| 1877 | # The following classes and functions are used to generate LR parsing tables on | ||
| 1878 | # a grammar. | ||
| 1879 | # ----------------------------------------------------------------------------- | ||
| 1880 | |||
| 1881 | # ----------------------------------------------------------------------------- | ||
| 1882 | # digraph() | ||
| 1883 | # traverse() | ||
| 1884 | # | ||
| 1885 | # The following two functions are used to compute set valued functions | ||
| 1886 | # of the form: | ||
| 1887 | # | ||
| 1888 | # F(x) = F'(x) U U{F(y) | x R y} | ||
| 1889 | # | ||
| 1890 | # This is used to compute the values of Read() sets as well as FOLLOW sets | ||
| 1891 | # in LALR(1) generation. | ||
| 1892 | # | ||
| 1893 | # Inputs: X - An input set | ||
| 1894 | # R - A relation | ||
| 1895 | # FP - Set-valued function | ||
| 1896 | # ------------------------------------------------------------------------------ | ||
| 1897 | |||
| 1898 | def digraph(X,R,FP): | ||
| 1899 | N = { } | ||
| 1900 | for x in X: | ||
| 1901 | N[x] = 0 | ||
| 1902 | stack = [] | ||
| 1903 | F = { } | ||
| 1904 | for x in X: | ||
| 1905 | if N[x] == 0: traverse(x,N,stack,F,X,R,FP) | ||
| 1906 | return F | ||
| 1907 | |||
| 1908 | def traverse(x,N,stack,F,X,R,FP): | ||
| 1909 | stack.append(x) | ||
| 1910 | d = len(stack) | ||
| 1911 | N[x] = d | ||
| 1912 | F[x] = FP(x) # F(X) <- F'(x) | ||
| 1913 | |||
| 1914 | rel = R(x) # Get y's related to x | ||
| 1915 | for y in rel: | ||
| 1916 | if N[y] == 0: | ||
| 1917 | traverse(y,N,stack,F,X,R,FP) | ||
| 1918 | N[x] = min(N[x],N[y]) | ||
| 1919 | for a in F.get(y,[]): | ||
| 1920 | if a not in F[x]: F[x].append(a) | ||
| 1921 | if N[x] == d: | ||
| 1922 | N[stack[-1]] = MAXINT | ||
| 1923 | F[stack[-1]] = F[x] | ||
| 1924 | element = stack.pop() | ||
| 1925 | while element != x: | ||
| 1926 | N[stack[-1]] = MAXINT | ||
| 1927 | F[stack[-1]] = F[x] | ||
| 1928 | element = stack.pop() | ||
| 1929 | |||
| 1930 | class LALRError(YaccError): pass | ||
| 1931 | |||
| 1932 | # ----------------------------------------------------------------------------- | ||
| 1933 | # == LRGeneratedTable == | ||
| 1934 | # | ||
| 1935 | # This class implements the LR table generation algorithm. There are no | ||
| 1936 | # public methods except for write() | ||
| 1937 | # ----------------------------------------------------------------------------- | ||
| 1938 | |||
| 1939 | class LRGeneratedTable(LRTable): | ||
| 1940 | def __init__(self,grammar,method='LALR',log=None): | ||
| 1941 | if method not in ['SLR','LALR']: | ||
| 1942 | raise LALRError("Unsupported method %s" % method) | ||
| 1943 | |||
| 1944 | self.grammar = grammar | ||
| 1945 | self.lr_method = method | ||
| 1946 | |||
| 1947 | # Set up the logger | ||
| 1948 | if not log: | ||
| 1949 | log = NullLogger() | ||
| 1950 | self.log = log | ||
| 1951 | |||
| 1952 | # Internal attributes | ||
| 1953 | self.lr_action = {} # Action table | ||
| 1954 | self.lr_goto = {} # Goto table | ||
| 1955 | self.lr_productions = grammar.Productions # Copy of grammar Production array | ||
| 1956 | self.lr_goto_cache = {} # Cache of computed gotos | ||
| 1957 | self.lr0_cidhash = {} # Cache of closures | ||
| 1958 | |||
| 1959 | self._add_count = 0 # Internal counter used to detect cycles | ||
| 1960 | |||
| 1961 | # Diagonistic information filled in by the table generator | ||
| 1962 | self.sr_conflict = 0 | ||
| 1963 | self.rr_conflict = 0 | ||
| 1964 | self.conflicts = [] # List of conflicts | ||
| 1965 | |||
| 1966 | self.sr_conflicts = [] | ||
| 1967 | self.rr_conflicts = [] | ||
| 1968 | |||
| 1969 | # Build the tables | ||
| 1970 | self.grammar.build_lritems() | ||
| 1971 | self.grammar.compute_first() | ||
| 1972 | self.grammar.compute_follow() | ||
| 1973 | self.lr_parse_table() | ||
| 1974 | |||
| 1975 | # Compute the LR(0) closure operation on I, where I is a set of LR(0) items. | ||
| 1976 | |||
| 1977 | def lr0_closure(self,I): | ||
| 1978 | self._add_count += 1 | ||
| 1979 | |||
| 1980 | # Add everything in I to J | ||
| 1981 | J = I[:] | ||
| 1982 | didadd = 1 | ||
| 1983 | while didadd: | ||
| 1984 | didadd = 0 | ||
| 1985 | for j in J: | ||
| 1986 | for x in j.lr_after: | ||
| 1987 | if getattr(x,"lr0_added",0) == self._add_count: continue | ||
| 1988 | # Add B --> .G to J | ||
| 1989 | J.append(x.lr_next) | ||
| 1990 | x.lr0_added = self._add_count | ||
| 1991 | didadd = 1 | ||
| 1992 | |||
| 1993 | return J | ||
| 1994 | |||
| 1995 | # Compute the LR(0) goto function goto(I,X) where I is a set | ||
| 1996 | # of LR(0) items and X is a grammar symbol. This function is written | ||
| 1997 | # in a way that guarantees uniqueness of the generated goto sets | ||
| 1998 | # (i.e. the same goto set will never be returned as two different Python | ||
| 1999 | # objects). With uniqueness, we can later do fast set comparisons using | ||
| 2000 | # id(obj) instead of element-wise comparison. | ||
| 2001 | |||
| 2002 | def lr0_goto(self,I,x): | ||
| 2003 | # First we look for a previously cached entry | ||
| 2004 | g = self.lr_goto_cache.get((id(I),x),None) | ||
| 2005 | if g: return g | ||
| 2006 | |||
| 2007 | # Now we generate the goto set in a way that guarantees uniqueness | ||
| 2008 | # of the result | ||
| 2009 | |||
| 2010 | s = self.lr_goto_cache.get(x,None) | ||
| 2011 | if not s: | ||
| 2012 | s = { } | ||
| 2013 | self.lr_goto_cache[x] = s | ||
| 2014 | |||
| 2015 | gs = [ ] | ||
| 2016 | for p in I: | ||
| 2017 | n = p.lr_next | ||
| 2018 | if n and n.lr_before == x: | ||
| 2019 | s1 = s.get(id(n),None) | ||
| 2020 | if not s1: | ||
| 2021 | s1 = { } | ||
| 2022 | s[id(n)] = s1 | ||
| 2023 | gs.append(n) | ||
| 2024 | s = s1 | ||
| 2025 | g = s.get('$end',None) | ||
| 2026 | if not g: | ||
| 2027 | if gs: | ||
| 2028 | g = self.lr0_closure(gs) | ||
| 2029 | s['$end'] = g | ||
| 2030 | else: | ||
| 2031 | s['$end'] = gs | ||
| 2032 | self.lr_goto_cache[(id(I),x)] = g | ||
| 2033 | return g | ||
| 2034 | |||
| 2035 | # Compute the LR(0) sets of item function | ||
| 2036 | def lr0_items(self): | ||
| 2037 | |||
| 2038 | C = [ self.lr0_closure([self.grammar.Productions[0].lr_next]) ] | ||
| 2039 | i = 0 | ||
| 2040 | for I in C: | ||
| 2041 | self.lr0_cidhash[id(I)] = i | ||
| 2042 | i += 1 | ||
| 2043 | |||
| 2044 | # Loop over the items in C and each grammar symbols | ||
| 2045 | i = 0 | ||
| 2046 | while i < len(C): | ||
| 2047 | I = C[i] | ||
| 2048 | i += 1 | ||
| 2049 | |||
| 2050 | # Collect all of the symbols that could possibly be in the goto(I,X) sets | ||
| 2051 | asyms = { } | ||
| 2052 | for ii in I: | ||
| 2053 | for s in ii.usyms: | ||
| 2054 | asyms[s] = None | ||
| 2055 | |||
| 2056 | for x in asyms: | ||
| 2057 | g = self.lr0_goto(I,x) | ||
| 2058 | if not g: continue | ||
| 2059 | if id(g) in self.lr0_cidhash: continue | ||
| 2060 | self.lr0_cidhash[id(g)] = len(C) | ||
| 2061 | C.append(g) | ||
| 2062 | |||
| 2063 | return C | ||
| 2064 | |||
| 2065 | # ----------------------------------------------------------------------------- | ||
| 2066 | # ==== LALR(1) Parsing ==== | ||
| 2067 | # | ||
| 2068 | # LALR(1) parsing is almost exactly the same as SLR except that instead of | ||
| 2069 | # relying upon Follow() sets when performing reductions, a more selective | ||
| 2070 | # lookahead set that incorporates the state of the LR(0) machine is utilized. | ||
| 2071 | # Thus, we mainly just have to focus on calculating the lookahead sets. | ||
| 2072 | # | ||
| 2073 | # The method used here is due to DeRemer and Pennelo (1982). | ||
| 2074 | # | ||
| 2075 | # DeRemer, F. L., and T. J. Pennelo: "Efficient Computation of LALR(1) | ||
| 2076 | # Lookahead Sets", ACM Transactions on Programming Languages and Systems, | ||
| 2077 | # Vol. 4, No. 4, Oct. 1982, pp. 615-649 | ||
| 2078 | # | ||
| 2079 | # Further details can also be found in: | ||
| 2080 | # | ||
| 2081 | # J. Tremblay and P. Sorenson, "The Theory and Practice of Compiler Writing", | ||
| 2082 | # McGraw-Hill Book Company, (1985). | ||
| 2083 | # | ||
| 2084 | # ----------------------------------------------------------------------------- | ||
| 2085 | |||
| 2086 | # ----------------------------------------------------------------------------- | ||
| 2087 | # compute_nullable_nonterminals() | ||
| 2088 | # | ||
| 2089 | # Creates a dictionary containing all of the non-terminals that might produce | ||
| 2090 | # an empty production. | ||
| 2091 | # ----------------------------------------------------------------------------- | ||
| 2092 | |||
| 2093 | def compute_nullable_nonterminals(self): | ||
| 2094 | nullable = {} | ||
| 2095 | num_nullable = 0 | ||
| 2096 | while 1: | ||
| 2097 | for p in self.grammar.Productions[1:]: | ||
| 2098 | if p.len == 0: | ||
| 2099 | nullable[p.name] = 1 | ||
| 2100 | continue | ||
| 2101 | for t in p.prod: | ||
| 2102 | if not t in nullable: break | ||
| 2103 | else: | ||
| 2104 | nullable[p.name] = 1 | ||
| 2105 | if len(nullable) == num_nullable: break | ||
| 2106 | num_nullable = len(nullable) | ||
| 2107 | return nullable | ||
| 2108 | |||
| 2109 | # ----------------------------------------------------------------------------- | ||
| 2110 | # find_nonterminal_trans(C) | ||
| 2111 | # | ||
| 2112 | # Given a set of LR(0) items, this functions finds all of the non-terminal | ||
| 2113 | # transitions. These are transitions in which a dot appears immediately before | ||
| 2114 | # a non-terminal. Returns a list of tuples of the form (state,N) where state | ||
| 2115 | # is the state number and N is the nonterminal symbol. | ||
| 2116 | # | ||
| 2117 | # The input C is the set of LR(0) items. | ||
| 2118 | # ----------------------------------------------------------------------------- | ||
| 2119 | |||
| 2120 | def find_nonterminal_transitions(self,C): | ||
| 2121 | trans = [] | ||
| 2122 | for state in range(len(C)): | ||
| 2123 | for p in C[state]: | ||
| 2124 | if p.lr_index < p.len - 1: | ||
| 2125 | t = (state,p.prod[p.lr_index+1]) | ||
| 2126 | if t[1] in self.grammar.Nonterminals: | ||
| 2127 | if t not in trans: trans.append(t) | ||
| 2128 | state = state + 1 | ||
| 2129 | return trans | ||
| 2130 | |||
| 2131 | # ----------------------------------------------------------------------------- | ||
| 2132 | # dr_relation() | ||
| 2133 | # | ||
| 2134 | # Computes the DR(p,A) relationships for non-terminal transitions. The input | ||
| 2135 | # is a tuple (state,N) where state is a number and N is a nonterminal symbol. | ||
| 2136 | # | ||
| 2137 | # Returns a list of terminals. | ||
| 2138 | # ----------------------------------------------------------------------------- | ||
| 2139 | |||
| 2140 | def dr_relation(self,C,trans,nullable): | ||
| 2141 | dr_set = { } | ||
| 2142 | state,N = trans | ||
| 2143 | terms = [] | ||
| 2144 | |||
| 2145 | g = self.lr0_goto(C[state],N) | ||
| 2146 | for p in g: | ||
| 2147 | if p.lr_index < p.len - 1: | ||
| 2148 | a = p.prod[p.lr_index+1] | ||
| 2149 | if a in self.grammar.Terminals: | ||
| 2150 | if a not in terms: terms.append(a) | ||
| 2151 | |||
| 2152 | # This extra bit is to handle the start state | ||
| 2153 | if state == 0 and N == self.grammar.Productions[0].prod[0]: | ||
| 2154 | terms.append('$end') | ||
| 2155 | |||
| 2156 | return terms | ||
| 2157 | |||
| 2158 | # ----------------------------------------------------------------------------- | ||
| 2159 | # reads_relation() | ||
| 2160 | # | ||
| 2161 | # Computes the READS() relation (p,A) READS (t,C). | ||
| 2162 | # ----------------------------------------------------------------------------- | ||
| 2163 | |||
| 2164 | def reads_relation(self,C, trans, empty): | ||
| 2165 | # Look for empty transitions | ||
| 2166 | rel = [] | ||
| 2167 | state, N = trans | ||
| 2168 | |||
| 2169 | g = self.lr0_goto(C[state],N) | ||
| 2170 | j = self.lr0_cidhash.get(id(g),-1) | ||
| 2171 | for p in g: | ||
| 2172 | if p.lr_index < p.len - 1: | ||
| 2173 | a = p.prod[p.lr_index + 1] | ||
| 2174 | if a in empty: | ||
| 2175 | rel.append((j,a)) | ||
| 2176 | |||
| 2177 | return rel | ||
| 2178 | |||
| 2179 | # ----------------------------------------------------------------------------- | ||
| 2180 | # compute_lookback_includes() | ||
| 2181 | # | ||
| 2182 | # Determines the lookback and includes relations | ||
| 2183 | # | ||
| 2184 | # LOOKBACK: | ||
| 2185 | # | ||
| 2186 | # This relation is determined by running the LR(0) state machine forward. | ||
| 2187 | # For example, starting with a production "N : . A B C", we run it forward | ||
| 2188 | # to obtain "N : A B C ." We then build a relationship between this final | ||
| 2189 | # state and the starting state. These relationships are stored in a dictionary | ||
| 2190 | # lookdict. | ||
| 2191 | # | ||
| 2192 | # INCLUDES: | ||
| 2193 | # | ||
| 2194 | # Computes the INCLUDE() relation (p,A) INCLUDES (p',B). | ||
| 2195 | # | ||
| 2196 | # This relation is used to determine non-terminal transitions that occur | ||
| 2197 | # inside of other non-terminal transition states. (p,A) INCLUDES (p', B) | ||
| 2198 | # if the following holds: | ||
| 2199 | # | ||
| 2200 | # B -> LAT, where T -> epsilon and p' -L-> p | ||
| 2201 | # | ||
| 2202 | # L is essentially a prefix (which may be empty), T is a suffix that must be | ||
| 2203 | # able to derive an empty string. State p' must lead to state p with the string L. | ||
| 2204 | # | ||
| 2205 | # ----------------------------------------------------------------------------- | ||
| 2206 | |||
| 2207 | def compute_lookback_includes(self,C,trans,nullable): | ||
| 2208 | |||
| 2209 | lookdict = {} # Dictionary of lookback relations | ||
| 2210 | includedict = {} # Dictionary of include relations | ||
| 2211 | |||
| 2212 | # Make a dictionary of non-terminal transitions | ||
| 2213 | dtrans = {} | ||
| 2214 | for t in trans: | ||
| 2215 | dtrans[t] = 1 | ||
| 2216 | |||
| 2217 | # Loop over all transitions and compute lookbacks and includes | ||
| 2218 | for state,N in trans: | ||
| 2219 | lookb = [] | ||
| 2220 | includes = [] | ||
| 2221 | for p in C[state]: | ||
| 2222 | if p.name != N: continue | ||
| 2223 | |||
| 2224 | # Okay, we have a name match. We now follow the production all the way | ||
| 2225 | # through the state machine until we get the . on the right hand side | ||
| 2226 | |||
| 2227 | lr_index = p.lr_index | ||
| 2228 | j = state | ||
| 2229 | while lr_index < p.len - 1: | ||
| 2230 | lr_index = lr_index + 1 | ||
| 2231 | t = p.prod[lr_index] | ||
| 2232 | |||
| 2233 | # Check to see if this symbol and state are a non-terminal transition | ||
| 2234 | if (j,t) in dtrans: | ||
| 2235 | # Yes. Okay, there is some chance that this is an includes relation | ||
| 2236 | # the only way to know for certain is whether the rest of the | ||
| 2237 | # production derives empty | ||
| 2238 | |||
| 2239 | li = lr_index + 1 | ||
| 2240 | while li < p.len: | ||
| 2241 | if p.prod[li] in self.grammar.Terminals: break # No forget it | ||
| 2242 | if not p.prod[li] in nullable: break | ||
| 2243 | li = li + 1 | ||
| 2244 | else: | ||
| 2245 | # Appears to be a relation between (j,t) and (state,N) | ||
| 2246 | includes.append((j,t)) | ||
| 2247 | |||
| 2248 | g = self.lr0_goto(C[j],t) # Go to next set | ||
| 2249 | j = self.lr0_cidhash.get(id(g),-1) # Go to next state | ||
| 2250 | |||
| 2251 | # When we get here, j is the final state, now we have to locate the production | ||
| 2252 | for r in C[j]: | ||
| 2253 | if r.name != p.name: continue | ||
| 2254 | if r.len != p.len: continue | ||
| 2255 | i = 0 | ||
| 2256 | # This look is comparing a production ". A B C" with "A B C ." | ||
| 2257 | while i < r.lr_index: | ||
| 2258 | if r.prod[i] != p.prod[i+1]: break | ||
| 2259 | i = i + 1 | ||
| 2260 | else: | ||
| 2261 | lookb.append((j,r)) | ||
| 2262 | for i in includes: | ||
| 2263 | if not i in includedict: includedict[i] = [] | ||
| 2264 | includedict[i].append((state,N)) | ||
| 2265 | lookdict[(state,N)] = lookb | ||
| 2266 | |||
| 2267 | return lookdict,includedict | ||
| 2268 | |||
| 2269 | # ----------------------------------------------------------------------------- | ||
| 2270 | # compute_read_sets() | ||
| 2271 | # | ||
| 2272 | # Given a set of LR(0) items, this function computes the read sets. | ||
| 2273 | # | ||
| 2274 | # Inputs: C = Set of LR(0) items | ||
| 2275 | # ntrans = Set of nonterminal transitions | ||
| 2276 | # nullable = Set of empty transitions | ||
| 2277 | # | ||
| 2278 | # Returns a set containing the read sets | ||
| 2279 | # ----------------------------------------------------------------------------- | ||
| 2280 | |||
| 2281 | def compute_read_sets(self,C, ntrans, nullable): | ||
| 2282 | FP = lambda x: self.dr_relation(C,x,nullable) | ||
| 2283 | R = lambda x: self.reads_relation(C,x,nullable) | ||
| 2284 | F = digraph(ntrans,R,FP) | ||
| 2285 | return F | ||
| 2286 | |||
| 2287 | # ----------------------------------------------------------------------------- | ||
| 2288 | # compute_follow_sets() | ||
| 2289 | # | ||
| 2290 | # Given a set of LR(0) items, a set of non-terminal transitions, a readset, | ||
| 2291 | # and an include set, this function computes the follow sets | ||
| 2292 | # | ||
| 2293 | # Follow(p,A) = Read(p,A) U U {Follow(p',B) | (p,A) INCLUDES (p',B)} | ||
| 2294 | # | ||
| 2295 | # Inputs: | ||
| 2296 | # ntrans = Set of nonterminal transitions | ||
| 2297 | # readsets = Readset (previously computed) | ||
| 2298 | # inclsets = Include sets (previously computed) | ||
| 2299 | # | ||
| 2300 | # Returns a set containing the follow sets | ||
| 2301 | # ----------------------------------------------------------------------------- | ||
| 2302 | |||
| 2303 | def compute_follow_sets(self,ntrans,readsets,inclsets): | ||
| 2304 | FP = lambda x: readsets[x] | ||
| 2305 | R = lambda x: inclsets.get(x,[]) | ||
| 2306 | F = digraph(ntrans,R,FP) | ||
| 2307 | return F | ||
| 2308 | |||
| 2309 | # ----------------------------------------------------------------------------- | ||
| 2310 | # add_lookaheads() | ||
| 2311 | # | ||
| 2312 | # Attaches the lookahead symbols to grammar rules. | ||
| 2313 | # | ||
| 2314 | # Inputs: lookbacks - Set of lookback relations | ||
| 2315 | # followset - Computed follow set | ||
| 2316 | # | ||
| 2317 | # This function directly attaches the lookaheads to productions contained | ||
| 2318 | # in the lookbacks set | ||
| 2319 | # ----------------------------------------------------------------------------- | ||
| 2320 | |||
| 2321 | def add_lookaheads(self,lookbacks,followset): | ||
| 2322 | for trans,lb in lookbacks.items(): | ||
| 2323 | # Loop over productions in lookback | ||
| 2324 | for state,p in lb: | ||
| 2325 | if not state in p.lookaheads: | ||
| 2326 | p.lookaheads[state] = [] | ||
| 2327 | f = followset.get(trans,[]) | ||
| 2328 | for a in f: | ||
| 2329 | if a not in p.lookaheads[state]: p.lookaheads[state].append(a) | ||
| 2330 | |||
| 2331 | # ----------------------------------------------------------------------------- | ||
| 2332 | # add_lalr_lookaheads() | ||
| 2333 | # | ||
| 2334 | # This function does all of the work of adding lookahead information for use | ||
| 2335 | # with LALR parsing | ||
| 2336 | # ----------------------------------------------------------------------------- | ||
| 2337 | |||
| 2338 | def add_lalr_lookaheads(self,C): | ||
| 2339 | # Determine all of the nullable nonterminals | ||
| 2340 | nullable = self.compute_nullable_nonterminals() | ||
| 2341 | |||
| 2342 | # Find all non-terminal transitions | ||
| 2343 | trans = self.find_nonterminal_transitions(C) | ||
| 2344 | |||
| 2345 | # Compute read sets | ||
| 2346 | readsets = self.compute_read_sets(C,trans,nullable) | ||
| 2347 | |||
| 2348 | # Compute lookback/includes relations | ||
| 2349 | lookd, included = self.compute_lookback_includes(C,trans,nullable) | ||
| 2350 | |||
| 2351 | # Compute LALR FOLLOW sets | ||
| 2352 | followsets = self.compute_follow_sets(trans,readsets,included) | ||
| 2353 | |||
| 2354 | # Add all of the lookaheads | ||
| 2355 | self.add_lookaheads(lookd,followsets) | ||
| 2356 | |||
| 2357 | # ----------------------------------------------------------------------------- | ||
| 2358 | # lr_parse_table() | ||
| 2359 | # | ||
| 2360 | # This function constructs the parse tables for SLR or LALR | ||
| 2361 | # ----------------------------------------------------------------------------- | ||
| 2362 | def lr_parse_table(self): | ||
| 2363 | Productions = self.grammar.Productions | ||
| 2364 | Precedence = self.grammar.Precedence | ||
| 2365 | goto = self.lr_goto # Goto array | ||
| 2366 | action = self.lr_action # Action array | ||
| 2367 | log = self.log # Logger for output | ||
| 2368 | |||
| 2369 | actionp = { } # Action production array (temporary) | ||
| 2370 | |||
| 2371 | log.info("Parsing method: %s", self.lr_method) | ||
| 2372 | |||
| 2373 | # Step 1: Construct C = { I0, I1, ... IN}, collection of LR(0) items | ||
| 2374 | # This determines the number of states | ||
| 2375 | |||
| 2376 | C = self.lr0_items() | ||
| 2377 | |||
| 2378 | if self.lr_method == 'LALR': | ||
| 2379 | self.add_lalr_lookaheads(C) | ||
| 2380 | |||
| 2381 | # Build the parser table, state by state | ||
| 2382 | st = 0 | ||
| 2383 | for I in C: | ||
| 2384 | # Loop over each production in I | ||
| 2385 | actlist = [ ] # List of actions | ||
| 2386 | st_action = { } | ||
| 2387 | st_actionp = { } | ||
| 2388 | st_goto = { } | ||
| 2389 | log.info("") | ||
| 2390 | log.info("state %d", st) | ||
| 2391 | log.info("") | ||
| 2392 | for p in I: | ||
| 2393 | log.info(" (%d) %s", p.number, str(p)) | ||
| 2394 | log.info("") | ||
| 2395 | |||
| 2396 | for p in I: | ||
| 2397 | if p.len == p.lr_index + 1: | ||
| 2398 | if p.name == "S'": | ||
| 2399 | # Start symbol. Accept! | ||
| 2400 | st_action["$end"] = 0 | ||
| 2401 | st_actionp["$end"] = p | ||
| 2402 | else: | ||
| 2403 | # We are at the end of a production. Reduce! | ||
| 2404 | if self.lr_method == 'LALR': | ||
| 2405 | laheads = p.lookaheads[st] | ||
| 2406 | else: | ||
| 2407 | laheads = self.grammar.Follow[p.name] | ||
| 2408 | for a in laheads: | ||
| 2409 | actlist.append((a,p,"reduce using rule %d (%s)" % (p.number,p))) | ||
| 2410 | r = st_action.get(a,None) | ||
| 2411 | if r is not None: | ||
| 2412 | # Whoa. Have a shift/reduce or reduce/reduce conflict | ||
| 2413 | if r > 0: | ||
| 2414 | # Need to decide on shift or reduce here | ||
| 2415 | # By default we favor shifting. Need to add | ||
| 2416 | # some precedence rules here. | ||
| 2417 | sprec,slevel = Productions[st_actionp[a].number].prec | ||
| 2418 | rprec,rlevel = Precedence.get(a,('right',0)) | ||
| 2419 | if (slevel < rlevel) or ((slevel == rlevel) and (rprec == 'left')): | ||
| 2420 | # We really need to reduce here. | ||
| 2421 | st_action[a] = -p.number | ||
| 2422 | st_actionp[a] = p | ||
| 2423 | if not slevel and not rlevel: | ||
| 2424 | log.info(" ! shift/reduce conflict for %s resolved as reduce",a) | ||
| 2425 | self.sr_conflicts.append((st,a,'reduce')) | ||
| 2426 | Productions[p.number].reduced += 1 | ||
| 2427 | elif (slevel == rlevel) and (rprec == 'nonassoc'): | ||
| 2428 | st_action[a] = None | ||
| 2429 | else: | ||
| 2430 | # Hmmm. Guess we'll keep the shift | ||
| 2431 | if not rlevel: | ||
| 2432 | log.info(" ! shift/reduce conflict for %s resolved as shift",a) | ||
| 2433 | self.sr_conflicts.append((st,a,'shift')) | ||
| 2434 | elif r < 0: | ||
| 2435 | # Reduce/reduce conflict. In this case, we favor the rule | ||
| 2436 | # that was defined first in the grammar file | ||
| 2437 | oldp = Productions[-r] | ||
| 2438 | pp = Productions[p.number] | ||
| 2439 | if oldp.line > pp.line: | ||
| 2440 | st_action[a] = -p.number | ||
| 2441 | st_actionp[a] = p | ||
| 2442 | chosenp,rejectp = pp,oldp | ||
| 2443 | Productions[p.number].reduced += 1 | ||
| 2444 | Productions[oldp.number].reduced -= 1 | ||
| 2445 | else: | ||
| 2446 | chosenp,rejectp = oldp,pp | ||
| 2447 | self.rr_conflicts.append((st,chosenp,rejectp)) | ||
| 2448 | log.info(" ! reduce/reduce conflict for %s resolved using rule %d (%s)", a,st_actionp[a].number, st_actionp[a]) | ||
| 2449 | else: | ||
| 2450 | raise LALRError("Unknown conflict in state %d" % st) | ||
| 2451 | else: | ||
| 2452 | st_action[a] = -p.number | ||
| 2453 | st_actionp[a] = p | ||
| 2454 | Productions[p.number].reduced += 1 | ||
| 2455 | else: | ||
| 2456 | i = p.lr_index | ||
| 2457 | a = p.prod[i+1] # Get symbol right after the "." | ||
| 2458 | if a in self.grammar.Terminals: | ||
| 2459 | g = self.lr0_goto(I,a) | ||
| 2460 | j = self.lr0_cidhash.get(id(g),-1) | ||
| 2461 | if j >= 0: | ||
| 2462 | # We are in a shift state | ||
| 2463 | actlist.append((a,p,"shift and go to state %d" % j)) | ||
| 2464 | r = st_action.get(a,None) | ||
| 2465 | if r is not None: | ||
| 2466 | # Whoa have a shift/reduce or shift/shift conflict | ||
| 2467 | if r > 0: | ||
| 2468 | if r != j: | ||
| 2469 | raise LALRError("Shift/shift conflict in state %d" % st) | ||
| 2470 | elif r < 0: | ||
| 2471 | # Do a precedence check. | ||
| 2472 | # - if precedence of reduce rule is higher, we reduce. | ||
| 2473 | # - if precedence of reduce is same and left assoc, we reduce. | ||
| 2474 | # - otherwise we shift | ||
| 2475 | rprec,rlevel = Productions[st_actionp[a].number].prec | ||
| 2476 | sprec,slevel = Precedence.get(a,('right',0)) | ||
| 2477 | if (slevel > rlevel) or ((slevel == rlevel) and (rprec == 'right')): | ||
| 2478 | # We decide to shift here... highest precedence to shift | ||
| 2479 | Productions[st_actionp[a].number].reduced -= 1 | ||
| 2480 | st_action[a] = j | ||
| 2481 | st_actionp[a] = p | ||
| 2482 | if not rlevel: | ||
| 2483 | log.info(" ! shift/reduce conflict for %s resolved as shift",a) | ||
| 2484 | self.sr_conflicts.append((st,a,'shift')) | ||
| 2485 | elif (slevel == rlevel) and (rprec == 'nonassoc'): | ||
| 2486 | st_action[a] = None | ||
| 2487 | else: | ||
| 2488 | # Hmmm. Guess we'll keep the reduce | ||
| 2489 | if not slevel and not rlevel: | ||
| 2490 | log.info(" ! shift/reduce conflict for %s resolved as reduce",a) | ||
| 2491 | self.sr_conflicts.append((st,a,'reduce')) | ||
| 2492 | |||
| 2493 | else: | ||
| 2494 | raise LALRError("Unknown conflict in state %d" % st) | ||
| 2495 | else: | ||
| 2496 | st_action[a] = j | ||
| 2497 | st_actionp[a] = p | ||
| 2498 | |||
| 2499 | # Print the actions associated with each terminal | ||
| 2500 | _actprint = { } | ||
| 2501 | for a,p,m in actlist: | ||
| 2502 | if a in st_action: | ||
| 2503 | if p is st_actionp[a]: | ||
| 2504 | log.info(" %-15s %s",a,m) | ||
| 2505 | _actprint[(a,m)] = 1 | ||
| 2506 | log.info("") | ||
| 2507 | # Print the actions that were not used. (debugging) | ||
| 2508 | not_used = 0 | ||
| 2509 | for a,p,m in actlist: | ||
| 2510 | if a in st_action: | ||
| 2511 | if p is not st_actionp[a]: | ||
| 2512 | if not (a,m) in _actprint: | ||
| 2513 | log.debug(" ! %-15s [ %s ]",a,m) | ||
| 2514 | not_used = 1 | ||
| 2515 | _actprint[(a,m)] = 1 | ||
| 2516 | if not_used: | ||
| 2517 | log.debug("") | ||
| 2518 | |||
| 2519 | # Construct the goto table for this state | ||
| 2520 | |||
| 2521 | nkeys = { } | ||
| 2522 | for ii in I: | ||
| 2523 | for s in ii.usyms: | ||
| 2524 | if s in self.grammar.Nonterminals: | ||
| 2525 | nkeys[s] = None | ||
| 2526 | for n in nkeys: | ||
| 2527 | g = self.lr0_goto(I,n) | ||
| 2528 | j = self.lr0_cidhash.get(id(g),-1) | ||
| 2529 | if j >= 0: | ||
| 2530 | st_goto[n] = j | ||
| 2531 | log.info(" %-30s shift and go to state %d",n,j) | ||
| 2532 | |||
| 2533 | action[st] = st_action | ||
| 2534 | actionp[st] = st_actionp | ||
| 2535 | goto[st] = st_goto | ||
| 2536 | st += 1 | ||
| 2537 | |||
| 2538 | |||
| 2539 | # ----------------------------------------------------------------------------- | ||
| 2540 | # write() | ||
| 2541 | # | ||
| 2542 | # This function writes the LR parsing tables to a file | ||
| 2543 | # ----------------------------------------------------------------------------- | ||
| 2544 | |||
| 2545 | def write_table(self,modulename,outputdir='',signature=""): | ||
| 2546 | basemodulename = modulename.split(".")[-1] | ||
| 2547 | filename = os.path.join(outputdir,basemodulename) + ".py" | ||
| 2548 | try: | ||
| 2549 | f = open(filename,"w") | ||
| 2550 | |||
| 2551 | f.write(""" | ||
| 2552 | # %s | ||
| 2553 | # This file is automatically generated. Do not edit. | ||
| 2554 | _tabversion = %r | ||
| 2555 | |||
| 2556 | _lr_method = %r | ||
| 2557 | |||
| 2558 | _lr_signature = %r | ||
| 2559 | """ % (filename, __tabversion__, self.lr_method, signature)) | ||
| 2560 | |||
| 2561 | # Change smaller to 0 to go back to original tables | ||
| 2562 | smaller = 1 | ||
| 2563 | |||
| 2564 | # Factor out names to try and make smaller | ||
| 2565 | if smaller: | ||
| 2566 | items = { } | ||
| 2567 | |||
| 2568 | for s,nd in self.lr_action.items(): | ||
| 2569 | for name,v in nd.items(): | ||
| 2570 | i = items.get(name) | ||
| 2571 | if not i: | ||
| 2572 | i = ([],[]) | ||
| 2573 | items[name] = i | ||
| 2574 | i[0].append(s) | ||
| 2575 | i[1].append(v) | ||
| 2576 | |||
| 2577 | f.write("\n_lr_action_items = {") | ||
| 2578 | for k,v in items.items(): | ||
| 2579 | f.write("%r:([" % k) | ||
| 2580 | for i in v[0]: | ||
| 2581 | f.write("%r," % i) | ||
| 2582 | f.write("],[") | ||
| 2583 | for i in v[1]: | ||
| 2584 | f.write("%r," % i) | ||
| 2585 | |||
| 2586 | f.write("]),") | ||
| 2587 | f.write("}\n") | ||
| 2588 | |||
| 2589 | f.write(""" | ||
| 2590 | _lr_action = { } | ||
| 2591 | for _k, _v in _lr_action_items.items(): | ||
| 2592 | for _x,_y in zip(_v[0],_v[1]): | ||
| 2593 | if not _x in _lr_action: _lr_action[_x] = { } | ||
| 2594 | _lr_action[_x][_k] = _y | ||
| 2595 | del _lr_action_items | ||
| 2596 | """) | ||
| 2597 | |||
| 2598 | else: | ||
| 2599 | f.write("\n_lr_action = { "); | ||
| 2600 | for k,v in self.lr_action.items(): | ||
| 2601 | f.write("(%r,%r):%r," % (k[0],k[1],v)) | ||
| 2602 | f.write("}\n"); | ||
| 2603 | |||
| 2604 | if smaller: | ||
| 2605 | # Factor out names to try and make smaller | ||
| 2606 | items = { } | ||
| 2607 | |||
| 2608 | for s,nd in self.lr_goto.items(): | ||
| 2609 | for name,v in nd.items(): | ||
| 2610 | i = items.get(name) | ||
| 2611 | if not i: | ||
| 2612 | i = ([],[]) | ||
| 2613 | items[name] = i | ||
| 2614 | i[0].append(s) | ||
| 2615 | i[1].append(v) | ||
| 2616 | |||
| 2617 | f.write("\n_lr_goto_items = {") | ||
| 2618 | for k,v in items.items(): | ||
| 2619 | f.write("%r:([" % k) | ||
| 2620 | for i in v[0]: | ||
| 2621 | f.write("%r," % i) | ||
| 2622 | f.write("],[") | ||
| 2623 | for i in v[1]: | ||
| 2624 | f.write("%r," % i) | ||
| 2625 | |||
| 2626 | f.write("]),") | ||
| 2627 | f.write("}\n") | ||
| 2628 | |||
| 2629 | f.write(""" | ||
| 2630 | _lr_goto = { } | ||
| 2631 | for _k, _v in _lr_goto_items.items(): | ||
| 2632 | for _x,_y in zip(_v[0],_v[1]): | ||
| 2633 | if not _x in _lr_goto: _lr_goto[_x] = { } | ||
| 2634 | _lr_goto[_x][_k] = _y | ||
| 2635 | del _lr_goto_items | ||
| 2636 | """) | ||
| 2637 | else: | ||
| 2638 | f.write("\n_lr_goto = { "); | ||
| 2639 | for k,v in self.lr_goto.items(): | ||
| 2640 | f.write("(%r,%r):%r," % (k[0],k[1],v)) | ||
| 2641 | f.write("}\n"); | ||
| 2642 | |||
| 2643 | # Write production table | ||
| 2644 | f.write("_lr_productions = [\n") | ||
| 2645 | for p in self.lr_productions: | ||
| 2646 | if p.func: | ||
| 2647 | f.write(" (%r,%r,%d,%r,%r,%d),\n" % (p.str,p.name, p.len, p.func,p.file,p.line)) | ||
| 2648 | else: | ||
| 2649 | f.write(" (%r,%r,%d,None,None,None),\n" % (str(p),p.name, p.len)) | ||
| 2650 | f.write("]\n") | ||
| 2651 | f.close() | ||
| 2652 | |||
| 2653 | except IOError: | ||
| 2654 | e = sys.exc_info()[1] | ||
| 2655 | sys.stderr.write("Unable to create '%s'\n" % filename) | ||
| 2656 | sys.stderr.write(str(e)+"\n") | ||
| 2657 | return | ||
| 2658 | |||
| 2659 | |||
| 2660 | # ----------------------------------------------------------------------------- | ||
| 2661 | # pickle_table() | ||
| 2662 | # | ||
| 2663 | # This function pickles the LR parsing tables to a supplied file object | ||
| 2664 | # ----------------------------------------------------------------------------- | ||
| 2665 | |||
| 2666 | def pickle_table(self,filename,signature=""): | ||
| 2667 | try: | ||
| 2668 | import cPickle as pickle | ||
| 2669 | except ImportError: | ||
| 2670 | import pickle | ||
| 2671 | outf = open(filename,"wb") | ||
| 2672 | pickle.dump(__tabversion__,outf,pickle_protocol) | ||
| 2673 | pickle.dump(self.lr_method,outf,pickle_protocol) | ||
| 2674 | pickle.dump(signature,outf,pickle_protocol) | ||
| 2675 | pickle.dump(self.lr_action,outf,pickle_protocol) | ||
| 2676 | pickle.dump(self.lr_goto,outf,pickle_protocol) | ||
| 2677 | |||
| 2678 | outp = [] | ||
| 2679 | for p in self.lr_productions: | ||
| 2680 | if p.func: | ||
| 2681 | outp.append((p.str,p.name, p.len, p.func,p.file,p.line)) | ||
| 2682 | else: | ||
| 2683 | outp.append((str(p),p.name,p.len,None,None,None)) | ||
| 2684 | pickle.dump(outp,outf,pickle_protocol) | ||
| 2685 | outf.close() | ||
| 2686 | |||
| 2687 | # ----------------------------------------------------------------------------- | ||
| 2688 | # === INTROSPECTION === | ||
| 2689 | # | ||
| 2690 | # The following functions and classes are used to implement the PLY | ||
| 2691 | # introspection features followed by the yacc() function itself. | ||
| 2692 | # ----------------------------------------------------------------------------- | ||
| 2693 | |||
| 2694 | # ----------------------------------------------------------------------------- | ||
| 2695 | # get_caller_module_dict() | ||
| 2696 | # | ||
| 2697 | # This function returns a dictionary containing all of the symbols defined within | ||
| 2698 | # a caller further down the call stack. This is used to get the environment | ||
| 2699 | # associated with the yacc() call if none was provided. | ||
| 2700 | # ----------------------------------------------------------------------------- | ||
| 2701 | |||
| 2702 | def get_caller_module_dict(levels): | ||
| 2703 | try: | ||
| 2704 | raise RuntimeError | ||
| 2705 | except RuntimeError: | ||
| 2706 | e,b,t = sys.exc_info() | ||
| 2707 | f = t.tb_frame | ||
| 2708 | while levels > 0: | ||
| 2709 | f = f.f_back | ||
| 2710 | levels -= 1 | ||
| 2711 | ldict = f.f_globals.copy() | ||
| 2712 | if f.f_globals != f.f_locals: | ||
| 2713 | ldict.update(f.f_locals) | ||
| 2714 | |||
| 2715 | return ldict | ||
| 2716 | |||
| 2717 | # ----------------------------------------------------------------------------- | ||
| 2718 | # parse_grammar() | ||
| 2719 | # | ||
| 2720 | # This takes a raw grammar rule string and parses it into production data | ||
| 2721 | # ----------------------------------------------------------------------------- | ||
| 2722 | def parse_grammar(doc,file,line): | ||
| 2723 | grammar = [] | ||
| 2724 | # Split the doc string into lines | ||
| 2725 | pstrings = doc.splitlines() | ||
| 2726 | lastp = None | ||
| 2727 | dline = line | ||
| 2728 | for ps in pstrings: | ||
| 2729 | dline += 1 | ||
| 2730 | p = ps.split() | ||
| 2731 | if not p: continue | ||
| 2732 | try: | ||
| 2733 | if p[0] == '|': | ||
| 2734 | # This is a continuation of a previous rule | ||
| 2735 | if not lastp: | ||
| 2736 | raise SyntaxError("%s:%d: Misplaced '|'" % (file,dline)) | ||
| 2737 | prodname = lastp | ||
| 2738 | syms = p[1:] | ||
| 2739 | else: | ||
| 2740 | prodname = p[0] | ||
| 2741 | lastp = prodname | ||
| 2742 | syms = p[2:] | ||
| 2743 | assign = p[1] | ||
| 2744 | if assign != ':' and assign != '::=': | ||
| 2745 | raise SyntaxError("%s:%d: Syntax error. Expected ':'" % (file,dline)) | ||
| 2746 | |||
| 2747 | grammar.append((file,dline,prodname,syms)) | ||
| 2748 | except SyntaxError: | ||
| 2749 | raise | ||
| 2750 | except Exception: | ||
| 2751 | raise SyntaxError("%s:%d: Syntax error in rule '%s'" % (file,dline,ps.strip())) | ||
| 2752 | |||
| 2753 | return grammar | ||
| 2754 | |||
| 2755 | # ----------------------------------------------------------------------------- | ||
| 2756 | # ParserReflect() | ||
| 2757 | # | ||
| 2758 | # This class represents information extracted for building a parser including | ||
| 2759 | # start symbol, error function, tokens, precedence list, action functions, | ||
| 2760 | # etc. | ||
| 2761 | # ----------------------------------------------------------------------------- | ||
| 2762 | class ParserReflect(object): | ||
| 2763 | def __init__(self,pdict,log=None): | ||
| 2764 | self.pdict = pdict | ||
| 2765 | self.start = None | ||
| 2766 | self.error_func = None | ||
| 2767 | self.tokens = None | ||
| 2768 | self.files = {} | ||
| 2769 | self.grammar = [] | ||
| 2770 | self.error = 0 | ||
| 2771 | |||
| 2772 | if log is None: | ||
| 2773 | self.log = PlyLogger(sys.stderr) | ||
| 2774 | else: | ||
| 2775 | self.log = log | ||
| 2776 | |||
| 2777 | # Get all of the basic information | ||
| 2778 | def get_all(self): | ||
| 2779 | self.get_start() | ||
| 2780 | self.get_error_func() | ||
| 2781 | self.get_tokens() | ||
| 2782 | self.get_precedence() | ||
| 2783 | self.get_pfunctions() | ||
| 2784 | |||
| 2785 | # Validate all of the information | ||
| 2786 | def validate_all(self): | ||
| 2787 | self.validate_start() | ||
| 2788 | self.validate_error_func() | ||
| 2789 | self.validate_tokens() | ||
| 2790 | self.validate_precedence() | ||
| 2791 | self.validate_pfunctions() | ||
| 2792 | self.validate_files() | ||
| 2793 | return self.error | ||
| 2794 | |||
| 2795 | # Compute a signature over the grammar | ||
| 2796 | def signature(self): | ||
| 2797 | try: | ||
| 2798 | from hashlib import md5 | ||
| 2799 | except ImportError: | ||
| 2800 | from md5 import md5 | ||
| 2801 | try: | ||
| 2802 | sig = md5() | ||
| 2803 | if self.start: | ||
| 2804 | sig.update(self.start.encode('latin-1')) | ||
| 2805 | if self.prec: | ||
| 2806 | sig.update("".join(["".join(p) for p in self.prec]).encode('latin-1')) | ||
| 2807 | if self.tokens: | ||
| 2808 | sig.update(" ".join(self.tokens).encode('latin-1')) | ||
| 2809 | for f in self.pfuncs: | ||
| 2810 | if f[3]: | ||
| 2811 | sig.update(f[3].encode('latin-1')) | ||
| 2812 | except (TypeError,ValueError): | ||
| 2813 | pass | ||
| 2814 | return sig.digest() | ||
| 2815 | |||
| 2816 | # ----------------------------------------------------------------------------- | ||
| 2817 | # validate_file() | ||
| 2818 | # | ||
| 2819 | # This method checks to see if there are duplicated p_rulename() functions | ||
| 2820 | # in the parser module file. Without this function, it is really easy for | ||
| 2821 | # users to make mistakes by cutting and pasting code fragments (and it's a real | ||
| 2822 | # bugger to try and figure out why the resulting parser doesn't work). Therefore, | ||
| 2823 | # we just do a little regular expression pattern matching of def statements | ||
| 2824 | # to try and detect duplicates. | ||
| 2825 | # ----------------------------------------------------------------------------- | ||
| 2826 | |||
| 2827 | def validate_files(self): | ||
| 2828 | # Match def p_funcname( | ||
| 2829 | fre = re.compile(r'\s*def\s+(p_[a-zA-Z_0-9]*)\(') | ||
| 2830 | |||
| 2831 | for filename in self.files.keys(): | ||
| 2832 | base,ext = os.path.splitext(filename) | ||
| 2833 | if ext != '.py': return 1 # No idea. Assume it's okay. | ||
| 2834 | |||
| 2835 | try: | ||
| 2836 | f = open(filename) | ||
| 2837 | lines = f.readlines() | ||
| 2838 | f.close() | ||
| 2839 | except IOError: | ||
| 2840 | continue | ||
| 2841 | |||
| 2842 | counthash = { } | ||
| 2843 | for linen,l in enumerate(lines): | ||
| 2844 | linen += 1 | ||
| 2845 | m = fre.match(l) | ||
| 2846 | if m: | ||
| 2847 | name = m.group(1) | ||
| 2848 | prev = counthash.get(name) | ||
| 2849 | if not prev: | ||
| 2850 | counthash[name] = linen | ||
| 2851 | else: | ||
| 2852 | self.log.warning("%s:%d: Function %s redefined. Previously defined on line %d", filename,linen,name,prev) | ||
| 2853 | |||
| 2854 | # Get the start symbol | ||
| 2855 | def get_start(self): | ||
| 2856 | self.start = self.pdict.get('start') | ||
| 2857 | |||
| 2858 | # Validate the start symbol | ||
| 2859 | def validate_start(self): | ||
| 2860 | if self.start is not None: | ||
| 2861 | if not isinstance(self.start,str): | ||
| 2862 | self.log.error("'start' must be a string") | ||
| 2863 | |||
| 2864 | # Look for error handler | ||
| 2865 | def get_error_func(self): | ||
| 2866 | self.error_func = self.pdict.get('p_error') | ||
| 2867 | |||
| 2868 | # Validate the error function | ||
| 2869 | def validate_error_func(self): | ||
| 2870 | if self.error_func: | ||
| 2871 | if isinstance(self.error_func,types.FunctionType): | ||
| 2872 | ismethod = 0 | ||
| 2873 | elif isinstance(self.error_func, types.MethodType): | ||
| 2874 | ismethod = 1 | ||
| 2875 | else: | ||
| 2876 | self.log.error("'p_error' defined, but is not a function or method") | ||
| 2877 | self.error = 1 | ||
| 2878 | return | ||
| 2879 | |||
| 2880 | eline = func_code(self.error_func).co_firstlineno | ||
| 2881 | efile = func_code(self.error_func).co_filename | ||
| 2882 | self.files[efile] = 1 | ||
| 2883 | |||
| 2884 | if (func_code(self.error_func).co_argcount != 1+ismethod): | ||
| 2885 | self.log.error("%s:%d: p_error() requires 1 argument",efile,eline) | ||
| 2886 | self.error = 1 | ||
| 2887 | |||
| 2888 | # Get the tokens map | ||
| 2889 | def get_tokens(self): | ||
| 2890 | tokens = self.pdict.get("tokens",None) | ||
| 2891 | if not tokens: | ||
| 2892 | self.log.error("No token list is defined") | ||
| 2893 | self.error = 1 | ||
| 2894 | return | ||
| 2895 | |||
| 2896 | if not isinstance(tokens,(list, tuple)): | ||
| 2897 | self.log.error("tokens must be a list or tuple") | ||
| 2898 | self.error = 1 | ||
| 2899 | return | ||
| 2900 | |||
| 2901 | if not tokens: | ||
| 2902 | self.log.error("tokens is empty") | ||
| 2903 | self.error = 1 | ||
| 2904 | return | ||
| 2905 | |||
| 2906 | self.tokens = tokens | ||
| 2907 | |||
| 2908 | # Validate the tokens | ||
| 2909 | def validate_tokens(self): | ||
| 2910 | # Validate the tokens. | ||
| 2911 | if 'error' in self.tokens: | ||
| 2912 | self.log.error("Illegal token name 'error'. Is a reserved word") | ||
| 2913 | self.error = 1 | ||
| 2914 | return | ||
| 2915 | |||
| 2916 | terminals = {} | ||
| 2917 | for n in self.tokens: | ||
| 2918 | if n in terminals: | ||
| 2919 | self.log.warning("Token '%s' multiply defined", n) | ||
| 2920 | terminals[n] = 1 | ||
| 2921 | |||
| 2922 | # Get the precedence map (if any) | ||
| 2923 | def get_precedence(self): | ||
| 2924 | self.prec = self.pdict.get("precedence",None) | ||
| 2925 | |||
| 2926 | # Validate and parse the precedence map | ||
| 2927 | def validate_precedence(self): | ||
| 2928 | preclist = [] | ||
| 2929 | if self.prec: | ||
| 2930 | if not isinstance(self.prec,(list,tuple)): | ||
| 2931 | self.log.error("precedence must be a list or tuple") | ||
| 2932 | self.error = 1 | ||
| 2933 | return | ||
| 2934 | for level,p in enumerate(self.prec): | ||
| 2935 | if not isinstance(p,(list,tuple)): | ||
| 2936 | self.log.error("Bad precedence table") | ||
| 2937 | self.error = 1 | ||
| 2938 | return | ||
| 2939 | |||
| 2940 | if len(p) < 2: | ||
| 2941 | self.log.error("Malformed precedence entry %s. Must be (assoc, term, ..., term)",p) | ||
| 2942 | self.error = 1 | ||
| 2943 | return | ||
| 2944 | assoc = p[0] | ||
| 2945 | if not isinstance(assoc,str): | ||
| 2946 | self.log.error("precedence associativity must be a string") | ||
| 2947 | self.error = 1 | ||
| 2948 | return | ||
| 2949 | for term in p[1:]: | ||
| 2950 | if not isinstance(term,str): | ||
| 2951 | self.log.error("precedence items must be strings") | ||
| 2952 | self.error = 1 | ||
| 2953 | return | ||
| 2954 | preclist.append((term,assoc,level+1)) | ||
| 2955 | self.preclist = preclist | ||
| 2956 | |||
| 2957 | # Get all p_functions from the grammar | ||
| 2958 | def get_pfunctions(self): | ||
| 2959 | p_functions = [] | ||
| 2960 | for name, item in self.pdict.items(): | ||
| 2961 | if name[:2] != 'p_': continue | ||
| 2962 | if name == 'p_error': continue | ||
| 2963 | if isinstance(item,(types.FunctionType,types.MethodType)): | ||
| 2964 | line = func_code(item).co_firstlineno | ||
| 2965 | file = func_code(item).co_filename | ||
| 2966 | p_functions.append((line,file,name,item.__doc__)) | ||
| 2967 | |||
| 2968 | # Sort all of the actions by line number | ||
| 2969 | p_functions.sort() | ||
| 2970 | self.pfuncs = p_functions | ||
| 2971 | |||
| 2972 | |||
| 2973 | # Validate all of the p_functions | ||
| 2974 | def validate_pfunctions(self): | ||
| 2975 | grammar = [] | ||
| 2976 | # Check for non-empty symbols | ||
| 2977 | if len(self.pfuncs) == 0: | ||
| 2978 | self.log.error("no rules of the form p_rulename are defined") | ||
| 2979 | self.error = 1 | ||
| 2980 | return | ||
| 2981 | |||
| 2982 | for line, file, name, doc in self.pfuncs: | ||
| 2983 | func = self.pdict[name] | ||
| 2984 | if isinstance(func, types.MethodType): | ||
| 2985 | reqargs = 2 | ||
| 2986 | else: | ||
| 2987 | reqargs = 1 | ||
| 2988 | if func_code(func).co_argcount > reqargs: | ||
| 2989 | self.log.error("%s:%d: Rule '%s' has too many arguments",file,line,func.__name__) | ||
| 2990 | self.error = 1 | ||
| 2991 | elif func_code(func).co_argcount < reqargs: | ||
| 2992 | self.log.error("%s:%d: Rule '%s' requires an argument",file,line,func.__name__) | ||
| 2993 | self.error = 1 | ||
| 2994 | elif not func.__doc__: | ||
| 2995 | self.log.warning("%s:%d: No documentation string specified in function '%s' (ignored)",file,line,func.__name__) | ||
| 2996 | else: | ||
| 2997 | try: | ||
| 2998 | parsed_g = parse_grammar(doc,file,line) | ||
| 2999 | for g in parsed_g: | ||
| 3000 | grammar.append((name, g)) | ||
| 3001 | except SyntaxError: | ||
| 3002 | e = sys.exc_info()[1] | ||
| 3003 | self.log.error(str(e)) | ||
| 3004 | self.error = 1 | ||
| 3005 | |||
| 3006 | # Looks like a valid grammar rule | ||
| 3007 | # Mark the file in which defined. | ||
| 3008 | self.files[file] = 1 | ||
| 3009 | |||
| 3010 | # Secondary validation step that looks for p_ definitions that are not functions | ||
| 3011 | # or functions that look like they might be grammar rules. | ||
| 3012 | |||
| 3013 | for n,v in self.pdict.items(): | ||
| 3014 | if n[0:2] == 'p_' and isinstance(v, (types.FunctionType, types.MethodType)): continue | ||
| 3015 | if n[0:2] == 't_': continue | ||
| 3016 | if n[0:2] == 'p_' and n != 'p_error': | ||
| 3017 | self.log.warning("'%s' not defined as a function", n) | ||
| 3018 | if ((isinstance(v,types.FunctionType) and func_code(v).co_argcount == 1) or | ||
| 3019 | (isinstance(v,types.MethodType) and func_code(v).co_argcount == 2)): | ||
| 3020 | try: | ||
| 3021 | doc = v.__doc__.split(" ") | ||
| 3022 | if doc[1] == ':': | ||
| 3023 | self.log.warning("%s:%d: Possible grammar rule '%s' defined without p_ prefix", | ||
| 3024 | func_code(v).co_filename, func_code(v).co_firstlineno,n) | ||
| 3025 | except Exception: | ||
| 3026 | pass | ||
| 3027 | |||
| 3028 | self.grammar = grammar | ||
| 3029 | |||
| 3030 | # ----------------------------------------------------------------------------- | ||
| 3031 | # yacc(module) | ||
| 3032 | # | ||
| 3033 | # Build a parser | ||
| 3034 | # ----------------------------------------------------------------------------- | ||
| 3035 | |||
| 3036 | def yacc(method='LALR', debug=yaccdebug, module=None, tabmodule=tab_module, start=None, | ||
| 3037 | check_recursion=1, optimize=0, write_tables=1, debugfile=debug_file,outputdir='', | ||
| 3038 | debuglog=None, errorlog = None, picklefile=None): | ||
| 3039 | |||
| 3040 | global parse # Reference to the parsing method of the last built parser | ||
| 3041 | |||
| 3042 | # If pickling is enabled, table files are not created | ||
| 3043 | |||
| 3044 | if picklefile: | ||
| 3045 | write_tables = 0 | ||
| 3046 | |||
| 3047 | if errorlog is None: | ||
| 3048 | errorlog = PlyLogger(sys.stderr) | ||
| 3049 | |||
| 3050 | # Get the module dictionary used for the parser | ||
| 3051 | if module: | ||
| 3052 | _items = [(k,getattr(module,k)) for k in dir(module)] | ||
| 3053 | pdict = dict(_items) | ||
| 3054 | else: | ||
| 3055 | pdict = get_caller_module_dict(2) | ||
| 3056 | |||
| 3057 | # Collect parser information from the dictionary | ||
| 3058 | pinfo = ParserReflect(pdict,log=errorlog) | ||
| 3059 | pinfo.get_all() | ||
| 3060 | |||
| 3061 | if pinfo.error: | ||
| 3062 | raise YaccError("Unable to build parser") | ||
| 3063 | |||
| 3064 | # Check signature against table files (if any) | ||
| 3065 | signature = pinfo.signature() | ||
| 3066 | |||
| 3067 | # Read the tables | ||
| 3068 | try: | ||
| 3069 | lr = LRTable() | ||
| 3070 | if picklefile: | ||
| 3071 | read_signature = lr.read_pickle(picklefile) | ||
| 3072 | else: | ||
| 3073 | read_signature = lr.read_table(tabmodule) | ||
| 3074 | if optimize or (read_signature == signature): | ||
| 3075 | try: | ||
| 3076 | lr.bind_callables(pinfo.pdict) | ||
| 3077 | parser = LRParser(lr,pinfo.error_func) | ||
| 3078 | parse = parser.parse | ||
| 3079 | return parser | ||
| 3080 | except Exception: | ||
| 3081 | e = sys.exc_info()[1] | ||
| 3082 | errorlog.warning("There was a problem loading the table file: %s", repr(e)) | ||
| 3083 | except VersionError: | ||
| 3084 | e = sys.exc_info() | ||
| 3085 | errorlog.warning(str(e)) | ||
| 3086 | except Exception: | ||
| 3087 | pass | ||
| 3088 | |||
| 3089 | if debuglog is None: | ||
| 3090 | if debug: | ||
| 3091 | debuglog = PlyLogger(open(debugfile,"w")) | ||
| 3092 | else: | ||
| 3093 | debuglog = NullLogger() | ||
| 3094 | |||
| 3095 | debuglog.info("Created by PLY version %s (http://www.dabeaz.com/ply)", __version__) | ||
| 3096 | |||
| 3097 | |||
| 3098 | errors = 0 | ||
| 3099 | |||
| 3100 | # Validate the parser information | ||
| 3101 | if pinfo.validate_all(): | ||
| 3102 | raise YaccError("Unable to build parser") | ||
| 3103 | |||
| 3104 | if not pinfo.error_func: | ||
| 3105 | errorlog.warning("no p_error() function is defined") | ||
| 3106 | |||
| 3107 | # Create a grammar object | ||
| 3108 | grammar = Grammar(pinfo.tokens) | ||
| 3109 | |||
| 3110 | # Set precedence level for terminals | ||
| 3111 | for term, assoc, level in pinfo.preclist: | ||
| 3112 | try: | ||
| 3113 | grammar.set_precedence(term,assoc,level) | ||
| 3114 | except GrammarError: | ||
| 3115 | e = sys.exc_info()[1] | ||
| 3116 | errorlog.warning("%s",str(e)) | ||
| 3117 | |||
| 3118 | # Add productions to the grammar | ||
| 3119 | for funcname, gram in pinfo.grammar: | ||
| 3120 | file, line, prodname, syms = gram | ||
| 3121 | try: | ||
| 3122 | grammar.add_production(prodname,syms,funcname,file,line) | ||
| 3123 | except GrammarError: | ||
| 3124 | e = sys.exc_info()[1] | ||
| 3125 | errorlog.error("%s",str(e)) | ||
| 3126 | errors = 1 | ||
| 3127 | |||
| 3128 | # Set the grammar start symbols | ||
| 3129 | try: | ||
| 3130 | if start is None: | ||
| 3131 | grammar.set_start(pinfo.start) | ||
| 3132 | else: | ||
| 3133 | grammar.set_start(start) | ||
| 3134 | except GrammarError: | ||
| 3135 | e = sys.exc_info()[1] | ||
| 3136 | errorlog.error(str(e)) | ||
| 3137 | errors = 1 | ||
| 3138 | |||
| 3139 | if errors: | ||
| 3140 | raise YaccError("Unable to build parser") | ||
| 3141 | |||
| 3142 | # Verify the grammar structure | ||
| 3143 | undefined_symbols = grammar.undefined_symbols() | ||
| 3144 | for sym, prod in undefined_symbols: | ||
| 3145 | errorlog.error("%s:%d: Symbol '%s' used, but not defined as a token or a rule",prod.file,prod.line,sym) | ||
| 3146 | errors = 1 | ||
| 3147 | |||
| 3148 | unused_terminals = grammar.unused_terminals() | ||
| 3149 | if unused_terminals: | ||
| 3150 | debuglog.info("") | ||
| 3151 | debuglog.info("Unused terminals:") | ||
| 3152 | debuglog.info("") | ||
| 3153 | for term in unused_terminals: | ||
| 3154 | errorlog.warning("Token '%s' defined, but not used", term) | ||
| 3155 | debuglog.info(" %s", term) | ||
| 3156 | |||
| 3157 | # Print out all productions to the debug log | ||
| 3158 | if debug: | ||
| 3159 | debuglog.info("") | ||
| 3160 | debuglog.info("Grammar") | ||
| 3161 | debuglog.info("") | ||
| 3162 | for n,p in enumerate(grammar.Productions): | ||
| 3163 | debuglog.info("Rule %-5d %s", n, p) | ||
| 3164 | |||
| 3165 | # Find unused non-terminals | ||
| 3166 | unused_rules = grammar.unused_rules() | ||
| 3167 | for prod in unused_rules: | ||
| 3168 | errorlog.warning("%s:%d: Rule '%s' defined, but not used", prod.file, prod.line, prod.name) | ||
| 3169 | |||
| 3170 | if len(unused_terminals) == 1: | ||
| 3171 | errorlog.warning("There is 1 unused token") | ||
| 3172 | if len(unused_terminals) > 1: | ||
| 3173 | errorlog.warning("There are %d unused tokens", len(unused_terminals)) | ||
| 3174 | |||
| 3175 | if len(unused_rules) == 1: | ||
| 3176 | errorlog.warning("There is 1 unused rule") | ||
| 3177 | if len(unused_rules) > 1: | ||
| 3178 | errorlog.warning("There are %d unused rules", len(unused_rules)) | ||
| 3179 | |||
| 3180 | if debug: | ||
| 3181 | debuglog.info("") | ||
| 3182 | debuglog.info("Terminals, with rules where they appear") | ||
| 3183 | debuglog.info("") | ||
| 3184 | terms = list(grammar.Terminals) | ||
| 3185 | terms.sort() | ||
| 3186 | for term in terms: | ||
| 3187 | debuglog.info("%-20s : %s", term, " ".join([str(s) for s in grammar.Terminals[term]])) | ||
| 3188 | |||
| 3189 | debuglog.info("") | ||
| 3190 | debuglog.info("Nonterminals, with rules where they appear") | ||
| 3191 | debuglog.info("") | ||
| 3192 | nonterms = list(grammar.Nonterminals) | ||
| 3193 | nonterms.sort() | ||
| 3194 | for nonterm in nonterms: | ||
| 3195 | debuglog.info("%-20s : %s", nonterm, " ".join([str(s) for s in grammar.Nonterminals[nonterm]])) | ||
| 3196 | debuglog.info("") | ||
| 3197 | |||
| 3198 | if check_recursion: | ||
| 3199 | unreachable = grammar.find_unreachable() | ||
| 3200 | for u in unreachable: | ||
| 3201 | errorlog.warning("Symbol '%s' is unreachable",u) | ||
| 3202 | |||
| 3203 | infinite = grammar.infinite_cycles() | ||
| 3204 | for inf in infinite: | ||
| 3205 | errorlog.error("Infinite recursion detected for symbol '%s'", inf) | ||
| 3206 | errors = 1 | ||
| 3207 | |||
| 3208 | unused_prec = grammar.unused_precedence() | ||
| 3209 | for term, assoc in unused_prec: | ||
| 3210 | errorlog.error("Precedence rule '%s' defined for unknown symbol '%s'", assoc, term) | ||
| 3211 | errors = 1 | ||
| 3212 | |||
| 3213 | if errors: | ||
| 3214 | raise YaccError("Unable to build parser") | ||
| 3215 | |||
| 3216 | # Run the LRGeneratedTable on the grammar | ||
| 3217 | if debug: | ||
| 3218 | errorlog.debug("Generating %s tables", method) | ||
| 3219 | |||
| 3220 | lr = LRGeneratedTable(grammar,method,debuglog) | ||
| 3221 | |||
| 3222 | if debug: | ||
| 3223 | num_sr = len(lr.sr_conflicts) | ||
| 3224 | |||
| 3225 | # Report shift/reduce and reduce/reduce conflicts | ||
| 3226 | if num_sr == 1: | ||
| 3227 | errorlog.warning("1 shift/reduce conflict") | ||
| 3228 | elif num_sr > 1: | ||
| 3229 | errorlog.warning("%d shift/reduce conflicts", num_sr) | ||
| 3230 | |||
| 3231 | num_rr = len(lr.rr_conflicts) | ||
| 3232 | if num_rr == 1: | ||
| 3233 | errorlog.warning("1 reduce/reduce conflict") | ||
| 3234 | elif num_rr > 1: | ||
| 3235 | errorlog.warning("%d reduce/reduce conflicts", num_rr) | ||
| 3236 | |||
| 3237 | # Write out conflicts to the output file | ||
| 3238 | if debug and (lr.sr_conflicts or lr.rr_conflicts): | ||
| 3239 | debuglog.warning("") | ||
| 3240 | debuglog.warning("Conflicts:") | ||
| 3241 | debuglog.warning("") | ||
| 3242 | |||
| 3243 | for state, tok, resolution in lr.sr_conflicts: | ||
| 3244 | debuglog.warning("shift/reduce conflict for %s in state %d resolved as %s", tok, state, resolution) | ||
| 3245 | |||
| 3246 | already_reported = {} | ||
| 3247 | for state, rule, rejected in lr.rr_conflicts: | ||
| 3248 | if (state,id(rule),id(rejected)) in already_reported: | ||
| 3249 | continue | ||
| 3250 | debuglog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) | ||
| 3251 | debuglog.warning("rejected rule (%s) in state %d", rejected,state) | ||
| 3252 | errorlog.warning("reduce/reduce conflict in state %d resolved using rule (%s)", state, rule) | ||
| 3253 | errorlog.warning("rejected rule (%s) in state %d", rejected, state) | ||
| 3254 | already_reported[state,id(rule),id(rejected)] = 1 | ||
| 3255 | |||
| 3256 | warned_never = [] | ||
| 3257 | for state, rule, rejected in lr.rr_conflicts: | ||
| 3258 | if not rejected.reduced and (rejected not in warned_never): | ||
| 3259 | debuglog.warning("Rule (%s) is never reduced", rejected) | ||
| 3260 | errorlog.warning("Rule (%s) is never reduced", rejected) | ||
| 3261 | warned_never.append(rejected) | ||
| 3262 | |||
| 3263 | # Write the table file if requested | ||
| 3264 | if write_tables: | ||
| 3265 | lr.write_table(tabmodule,outputdir,signature) | ||
| 3266 | |||
| 3267 | # Write a pickled version of the tables | ||
| 3268 | if picklefile: | ||
| 3269 | lr.pickle_table(picklefile,signature) | ||
| 3270 | |||
| 3271 | # Build the parser | ||
| 3272 | lr.bind_callables(pinfo.pdict) | ||
| 3273 | parser = LRParser(lr,pinfo.error_func) | ||
| 3274 | |||
| 3275 | parse = parser.parse | ||
| 3276 | return parser | ||
