/usr/share/pyshared/simpleparse/objectgenerator.py is in python-simpleparse 2.1.0a1-6build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 | """Object-oriented tag-table generator objects
The objectgenerator module is the core of the SimpleParse
system, the various element token classes defined here
implement transitions from EBNF-style abstractions into
the low-level (assembly-like) instructions to the
TextTools engine.
Each class within the module is a sub-class of ElementToken,
which provides a number of common facilities, the most
obvious of which is the permute method, which takes care of
the negative, optional, and repeating flags for the normal
case (with character ranges and literals being non-normal).
"""
from simpleparse.stt.TextTools.TextTools import *
### Direct use of BMS is deprecated now...
try:
TextSearch
except NameError:
TextSearch = BMS
from simpleparse.error import ParserSyntaxError
import copy
class ElementToken:
"""Abstract base class for all ElementTokens
Common Attributes:
negative -- the element token should match
a character if the "base" definition
would not match at the current position
optional -- the element token will match even
if the base definition would not match
at the current position
repeating -- if the element is successfully
matched, attempt to match it again.
lookahead -- if true, the scanning position
of the engine will be reset after the
element matches
errorOnFail -- if true, the engine will call the
object stored in errorOnFail as a text-
matching object iff the element token fails
to match. This is used to signal
SyntaxErrors.
Attributes only used for top-level Productions:
report -- if true, the production's results
will be added to the result tree
expanded -- if true, the production's children's
results will be added to the result tree
but the production's own result will be ignored
"""
negative = 0
optional = 0
repeating = 0
report = 1
# note that optional and errorOnFail are mutually exclusive
errorOnFail = None
# any item may be marked as expanded,
# which says that it's a top-level declaration
# and that links to it should automatically expand
# as if the name wasn't present...
expanded = 0
lookahead = 0
def __init__( self, **namedarguments ):
"""Initialize the object with named attributes
This method simply takes the named attributes and
updates the object's dictionary with them
"""
self.__dict__.update( namedarguments )
def toParser( self, generator, noReport=0 ):
"""Abstract interface for implementing the conversion to a text-tools table
generator -- an instance of generator.Generator
which provides various facilities for discovering
other productions.
noReport -- if true, we're being called recursively
for a terminal grammar fragment where one of our
parents has explicitly suppressed all reporting.
This method is called by the generator or by
another element-token's toParser method.
"""
raise NotImplementedError( '''Element token generator abstract function called''' )
def permute( self, basetable ):
'''Given a positive, required, non-repeating table, convert to appropriately configured table
This method applies generic logic for applying the
operational flags to a basic recipe for an element.
It is normally called from the elements-token's own
toParser method.
'''
flags = 0
if self.lookahead:
flags = flags + LookAhead
assert len(basetable) == 3, '''Attempt to permute a base table that already has fail flag set, can only permute unadorned tables'''
if self.negative:
# negative "matches" if it fails
# we add in the flags while we're at it...
basetable = (None, SubTable+flags, (
basetable + (1,2),
(None, EOF, Here,2,1), # if we hit eof, this didn't match, otherwise, we matched
(None, Fail, Here),# either hit eof or matched the client
(None,Skip,1),
))
elif flags:
# unpack, add the flags, and repack
tag, command, arg = basetable
basetable = ( tag, command+flags, arg)
if self.repeating:
### There are a number of problems with repetition that we'd like to solve
### via recursive table calls, but those are very expensive in the current
### implementation, so we need to use something a little more hacky...
if self.optional:
return [
## this would be the "simplistic" implementation...
## basetable + (1,0)
## it doesn't work because of cases
## where all-optional children "succeed" without consuming
## when within a repeating parent
## the EOF test isn't enough to fix the problem,
## as it's only checking a common case, not the underlying failure
basetable +(2,1), # fail, done, succeed, check for eof and if not, try matching again
# if we hit eof, no chance of further matches,
# consider ourselves done
(None, EOF, Here,-1,1),
]
elif self.errorOnFail:
return [
basetable+(1,2),
(None, Call, self.errorOnFail),
# as for optional...
basetable +(2,1),
(None, EOF, Here,-1,1),
]
else:
return [
basetable,
# as for optional...
basetable +(2,1),
(None, EOF, Here,-1,1),
]
else: # single
if self.optional:
return [
basetable +(1,1)
]
elif self.errorOnFail:
return [
basetable+(1,2),
(None, Call, self.errorOnFail),
]
else: # not optional
return [
basetable
]
def __repr__( self):
"""Return a readily recognisable version of ourself"""
from simpleparse import printers
return printers.asObject( self )
def terminal (self, generator):
"""Determine if this element is terminal for the generator"""
return 0
class Literal( ElementToken ):
"""Literal string value to be matched
Literals are one of the most common elements within
any grammar. The implementation tries to use the
most efficient mechanism available for matching/searching
for a literal value, so the Literal class does not
use the permute method, instead defining explicit
parsing methodologies for each flag and value combination
Literals in the SimpleParse EBNF grammar are defined like so:
"test", "test"?, "test"*, "test"+
-"test", -"test"?, -"test"*, -"test"+
Attributes:
value -- a string storing the literal's value
Notes:
Currently we don't support Unicode literals
See also:
CILiteral -- case-insensitive Literal values
"""
value = ""
def toParser( self, generator=None, noReport=0 ):
"""Create the parser for the element token"""
flags = 0
if self.lookahead:
flags = flags + LookAhead
base = self.baseToParser( generator )
if flags or self.errorOnFail:
if self.errorOnFail:
return [(None, SubTable+flags, tuple(base),1,2),(None, Call, self.errorOnFail)]
else:
return [(None, SubTable+flags, tuple(base))]
else:
return base
def baseToParser( self, generator=None ):
"""Parser generation without considering flag settings"""
svalue = self.value
if self.negative:
if self.repeating: # a repeating negative value, a "search" in effect
if self.optional: # if fails, then go to end of file
return [ (None, sWordStart, TextSearch( svalue ),1,2), (None, Move, ToEOF ) ]
else: # must first check to make sure the current position is not the word, then the same
return [
(None, Word, svalue, 2,1),
(None, Fail, Here),
(None, sWordStart, TextSearch( svalue ),1,2),
(None, Move, ToEOF )
]
#return [ (None, Word, svalue, 2,1),(None, Fail, Here),(None, WordStart, svalue,1,2), (None, Move, ToEOF ) ]
else: # a single-character test saying "not a this"
if self.optional: # test for a success, move back if success, move one forward if failure
if len(svalue) > 1:
return [ (None, Word, svalue, 2,1),
(None, Skip, -len(svalue), 2,2), # backup if this was the word to start of word, succeed
(None, Skip, 1 ) ] # else just move one character and succeed
else: # Uses Is test instead of Word test, should be faster I'd imagine
return [ (None, Is, svalue, 2,1),
(None, Skip, -1, 2,2), # backtrack
(None, Skip, 1 ) ] # else just move one character and succeed
else: # must find at least one character not part of the word, so
if len(svalue) > 1:
return [ (None, Word, svalue, 2,1),
(None, Fail, Here),
(None, Skip, 1 ) ] # else just move one character and succeed
else: #must fail if it finds or move one forward
return [ (None, Is, svalue, 2,1),
(None, Fail, Here),
(None, Skip, 1 ) ] # else just move one character and succeed
else: # positive
if self.repeating:
if self.optional:
if len(svalue) > 1:
return [ (None, Word, svalue, 1,0) ]
else:
return [ (None, Is, svalue, 1,0) ]
else: # not optional
if len(svalue) > 1:
return [ (None, Word, svalue),(None, Word, svalue,1,0) ]
else:
return [ (None, Is, svalue),(None, Is, svalue,1,0) ]
else: # not repeating
if self.optional:
if len(svalue) > 1:
return [ (None, Word, svalue, 1,1) ]
else:
return [ (None, Is, svalue, 1,1) ]
else: # not optional
if len(svalue) > 1:
return [ (None, Word, svalue) ]
else:
return [ (None, Word, svalue) ]
def terminal (self, generator):
"""Determine if this element is terminal for the generator"""
return 1
class _Range( ElementToken ):
"""Range of character values where any one of the characters may match
The Range token allows you to define a set of characters
(using a mini-grammar) of which any one may match. By using
the repetition flags, it is possible to easily create such
common structures as "names" and "numbers". For example:
name := [a-zA-Z]+
number := [0-9.eE]+
(Note: those are not beautifully defined examples :) ).
The mini-grammar for the simpleparsegrammar is defined as follows:
'[',CHARBRACE?,CHARDASH?, (CHARRANGE/CHARNOBRACE)*, CHARDASH?,']'
that is, if a literal ']' character is wanted, you must
define the character as the first item in the range. A literal
'-' character must appear as the first character after any
literal ']' character (or the beginning of the range) or as the
last character in the range.
Note: The expansion from the mini-grammar occurs before the
Range token is created (the simpleparse grammar does the
expansion), so the value attribute of the token is actually
the expanded string of characters.
"""
value = ""
requiresExpandedSet = 1
def toParser( self, generator=None, noReport=0 ):
"""Create the parser for the element token"""
flags = 0
if self.lookahead:
flags = flags + LookAhead
base = self.baseToParser( generator )
if flags or self.errorOnFail:
if self.errorOnFail:
return [(None, SubTable+flags, tuple(base),1,2),(None, Call, self.errorOnFail)]
else:
return [(None, SubTable+flags, tuple(base))]
else:
return base
# this should be a faster and more generic character set
# approach, but there's a bug with mxTextTools b3 which makes
# it non-functional, so for now I'm using the old version.
# Eventually this should also support the Unicode character sets
##try:
## CharSet
## class Range( _Range ):
## """Range type using the CharSet feature of mx.TextTools 2.1.0
##
## The CharSet type allows for both Unicode and 256-char strings,
## so we can use it as our 2.1.0 primary parsing mechanism.
## It also allows for simpler definitions (doesn't require that
## we pre-exand the character set). That's going to require support
## in the SimpleParse grammar, of course.
## """
## requiresExpandedSet = 0
## def baseToParser( self, generator=None ):
## """Parser generation without considering flag settings"""
## svalue = self.value
## print 'generating range for ', repr(svalue)
## if not svalue:
## raise ValueError( '''Range defined with no member values, would cause infinite loop %s'''%(self))
## if self.negative:
## svalue = '^' + svalue
## print ' generated', repr(svalue)
## svalue = CharSet(svalue)
## if self.repeating:
## if self.optional:
## return [ (None, AllInCharSet, svalue, 1 ) ]
## else: # not optional
## #return [ (None, AllInSet, svalue ) ]
## return [ (None, AllInCharSet, svalue ) ]
## else: # not repeating
## if self.optional:
## #return [ (None, IsInSet, svalue, 1 ) ]
## return [ (None, IsInCharSet, svalue, 1 ) ]
## else: # not optional
## #return [ (None, IsInSet, svalue ) ]
## return [ (None, IsInCharSet, svalue ) ]
##except NameError:
class Range( _Range ):
"""Range type which doesn't use the CharSet features in mx.TextTools
This is likely to be much slower than the CharSet version (below), and
is unable to handle unicode character sets. However, it will work with
TextTools 2.0.3, which may be needed in some cases.
"""
def baseToParser( self, generator=None ):
"""Parser generation without considering flag settings"""
svalue = self.value
if not svalue:
raise ValueError( '''Range defined with no member values, would cause infinite loop %s'''%(self))
if self.negative:
if self.repeating:
if self.optional:
#return [ (None, AllInSet, svalue, 1 ) ]
return [ (None, AllNotIn, svalue, 1 ) ]
else: # not optional
#return [ (None, AllInSet, svalue ) ]
return [ (None, AllNotIn, svalue ) ]
else: # not repeating
if self.optional:
#return [ (None, IsInSet, svalue, 1 ) ]
return [ (None, IsNotIn, svalue, 1 ) ]
else: # not optional
#return [ (None, IsInSet, svalue ) ]
return [ (None, IsNotIn, svalue ) ]
else:
if self.repeating:
if self.optional:
#return [ (None, AllInSet, svalue, 1 ) ]
return [ (None, AllIn, svalue, 1 ) ]
else: # not optional
#return [ (None, AllInSet, svalue ) ]
return [ (None, AllIn, svalue ) ]
else: # not repeating
if self.optional:
#return [ (None, IsInSet, svalue, 1 ) ]
return [ (None, IsIn, svalue, 1 ) ]
else: # not optional
#return [ (None, IsInSet, svalue ) ]
return [ (None, IsIn, svalue ) ]
def terminal (self, generator):
"""Determine if this element is terminal for the generator"""
return 1
class Group( ElementToken ):
"""Abstract base class for all group element tokens
The primary feature of a group is that it has a set
of element tokens stored in the attribute "children".
"""
children = ()
terminalValue = None
def terminal (self, generator):
"""Determine if this element is terminal for the generator"""
if self.terminalValue in (0,1):
return self.terminalValue
self.terminalValue = 0
for item in self.children:
if not item.terminal( generator):
return self.terminalValue
self.terminalValue = 1
return self.terminalValue
class SequentialGroup( Group ):
"""A sequence of element tokens which must match in a particular order
A sequential group must match each child in turn
and all children must be satisfied to consider the
group matched.
Within the simpleparsegrammar, the sequential group
is defined like so:
("a", b, c, "d")
i.e. a series of comma-separated element token definitions.
"""
def toParser( self, generator=None, noReport=0 ):
elset = []
for child in self.children:
elset.extend( child.toParser( generator, noReport ) )
basic = self.permute( (None, SubTable, tuple( elset)) )
if len(basic) == 1:
first = basic[0]
if len(first) == 3 and first[0] is None and first[1] == SubTable:
return tuple(first[2])
return basic
class CILiteral( SequentialGroup ):
"""Case-insensitive Literal values
The CILiteral is a sequence of literal and
character-range values, where each element is
positive and required. Literal values are
composed of those characters which are not
upper-case/lower-case pairs, while the ranges
are all two-character ranges with the upper
and lower forms.
CILiterals in the SimpleParse EBNF grammar are defined like so:
c"test", c"test"?, c"test"*, c"test"+
-c"test", -c"test"?, -c"test"*, -c"test"+
Attributes:
value -- a string storing the literal's value
Notes:
Currently we don't support Unicode literals
A CILiteral will be *much* slower than a
regular literal or character range
"""
value = ""
def toParser( self, generator=None, noReport=0 ):
elset = self.ciParse( self.value )
if len(elset) == 1:
# XXX should be compressing these out during optimisation...
# pointless declaration of case-insensitivity,
# or a single-character value
pass
basic = self.permute( (None, SubTable, tuple( elset)) )
if len(basic) == 1:
first = basic[0]
if len(first) == 3 and first[0] is None and first[1] == SubTable:
return tuple(first[2])
return basic
def ciParse( self, value ):
"""Break value into set of case-dependent groups..."""
def equalPrefix( a,b ):
for x in range(len(a)-1):
if a[x] != b[x]:
return x
result = []
a,b = value.upper(), value.lower()
while a and b:
# is there an equal literal run at the start?
stringPrefix = equalPrefix( a,b )
if stringPrefix:
result.append( (None, Word, a[:stringPrefix]) )
a,b = a[stringPrefix:],b[stringPrefix:]
# if we hit the end of the string, that's fine, just return
if not a and b:
break
# otherwise, the next character must be a case-differing pair
result.append( (None, IsIn, a[0]+b[0]) )
a,b = a[1:], b[1:]
return result
class ErrorOnFail(ElementToken):
"""When called as a matching function, raises a SyntaxError
Attributes:
expected -- list of strings describing expected productions
production -- string name of the production that's failing to parse
message -- overrides default message generation if non-null
(something,something)+!
(something,something)!
(something,something)+!"Unable to parse somethings in my production"
(something,something)!"Unable to parse somethings in my production"
if string -> give an explicit message (with optional % values)
else -> use a default string
"""
production = ""
message = ""
expected = ""
def __call__( self, text, position, end ):
"""Method called by mxTextTools iff the base production fails"""
error = ParserSyntaxError( self.message )
error.message = self.message
error.production = self.production
error.expected= self.expected
error.buffer = text
error.position = position
raise error
def copy( self ):
import copy
return copy.copy( self )
class FirstOfGroup( Group ):
"""Set of tokens that matches (and stops searching) with the first successful child
A FirstOf group attempts to match each child in turn,
declaring success with the first successful child,
or failure if none of the children match.
Within the simpleparsegrammar, the FirstOf group
is defined like so:
("a" / b / c / "d")
i.e. a series of slash-separated element token definitions.
"""
def toParser( self, generator=None, noReport=0 ):
elset = []
# should catch condition where a child is optional
# and we are repeating (which causes a crash during
# parsing), but doing so is rather complex and
# requires analysis of the whole grammar.
for el in self.children:
assert not el.optional, """Optional child of a FirstOf group created, this would cause an infinite recursion in the engine, child was %s"""%el
dataset = el.toParser( generator, noReport )
if len( dataset) == 1:# and len(dataset[0]) == 3: # we can alter the jump states with impunity
elset.append( dataset[0] )
else: # for now I'm eating the inefficiency and doing an extra SubTable for all elements to allow for easy calculation of jumps within the FO group
elset.append( (None, SubTable, tuple( dataset )) )
procset = []
for i in range( len( elset) -1): # note that we have to treat last el specially
procset.append( elset[i] + (1,len(elset)-i) ) # if success, jump past end
procset.append( elset[-1] ) # will cause a failure if last element doesn't match
procset = tuple(procset)
basetable = (None, SubTable, procset )
return self.permute( basetable )
class Prebuilt( ElementToken ):
"""Holder for pre-built TextTools tag tables
You can pass in a Pre-built tag table when
creating your grammar, doing so creates
Prebuilt element tokens which can be referenced
by the other element tokens in your grammar.
"""
value = ()
def toParser( self, generator=None, noReport=0 ):
return self.value
class LibraryElement( ElementToken ):
"""Holder for a prebuilt item with it's own generator"""
generator = None
production = ""
methodSource = None
def toParser( self, generator=None, noReport=0 ):
if self.methodSource is None:
source = generator.methodSource
else:
source = self.methodSource
basetable = self.generator.buildParser( self.production, source )
try:
if type(basetable[0]) == type(()):
if len(basetable) == 1 and len(basetable[0]) == 3:
basetable = basetable[0]
else:
# this is a table that got returned!
basetable = (None, SubTable, basetable)
return self.permute( basetable )
except:
print basetable
raise
class Name( ElementToken ):
"""Reference to another rule in the grammar
The Name element token allows you to reference another
production within the grammar. There are three major
sub-categories of reference depending on both the Name
element token and the referenced table's values.
if the Name token's report attribute is false,
or the target table's report attribute is false,
or the Name token negative attribute is true,
the Name reference will report nothing in the result tree
if the target's expand attribute is true, however,
the Name reference will report the children
of the target production without reporting the
target production's results (SubTable match)
finally:
if the target is not expanded and the Name token
should report something, the generator object is
asked to supply the tag object and flags for
processing the results of the target. See the
generator.MethodSource documentation for details.
Notes:
expanded and un-reported productions won't get any
methodsource methods called when
they are finished, that's just how I decided to
do it, not sure if there's some case where you'd
want it. As a result, it's possible to have a
method getting called for one instance (where a
name ref is reporting) and not for another (where
the name ref isn't reporting).
"""
value = ""
# following two flags are new ideas in the rewrite...
report = 1
def toParser( self, generator, noReport=0 ):
"""Create the table for parsing a name-reference
Note that currently most of the "compression" optimisations
occur here.
"""
sindex = generator.getNameIndex( self.value )
command = TableInList
target = generator.getRootObjects()[sindex]
reportSelf = (
(not noReport) and # parent hasn't suppressed reporting
self.report and # we are not suppressing ourselves
target.report and # target doesn't suppress reporting
(not self.negative) and # we aren't a negation, which doesn't report anything by itself
(not target.expanded) # we don't report the expanded production
)
reportChildren = (
(not noReport) and # parent hasn't suppressed reporting
self.report and # we are not suppressing ourselves
target.report and # target doesn't suppress reporting
(not self.negative) # we aren't a negation, which doesn't report anything by itself
)
if reportSelf:
svalue = self.value
else:
svalue = None
flags = 0
if target.expanded:
# the target is the root of an expandedname declaration
# so we need to do special processing to make sure that
# it gets properly reported...
command = SubTableInList
tagobject = None
# check for indirected reference to another name...
elif not reportSelf:
tagobject = svalue
else:
flags, tagobject = generator.getObjectForName( svalue )
if flags:
command = command | flags
if tagobject is None and not flags:
if self.terminal(generator):
if extractFlags(self,reportChildren) != extractFlags(target):
composite = compositeFlags(self,target, reportChildren)
partial = generator.getCustomTerminalParser( sindex,composite)
if partial is not None:
return partial
partial = tuple(copyToNewFlags(target, composite).toParser(
generator,
not reportChildren
))
generator.cacheCustomTerminalParser( sindex,composite, partial)
return partial
else:
partial = generator.getTerminalParser( sindex )
if partial is not None:
return partial
partial = tuple(target.toParser(
generator,
not reportChildren
))
generator.setTerminalParser( sindex, partial)
return partial
# base, required, positive table...
if (
self.terminal( generator ) and
(not flags) and
isinstance(target, (SequentialGroup,Literal,Name,Range))
):
partial = generator.getTerminalParser( sindex )
if partial is None:
partial = tuple(target.toParser(
generator,
#not reportChildren
))
generator.setTerminalParser( sindex, partial)
if len(partial) == 1 and len(partial[0]) == 3 and (
partial[0][0] is None or tagobject is None
):
# there is a single child
# it doesn't report anything, or we don't
partial = (partial[0][0] or tagobject,)+ partial[0][1:]
else:
partial = (tagobject, Table, tuple(partial))
return self.permute( partial )
basetable = (
tagobject,
command, (
generator.getParserList (),
sindex,
)
)
return self.permute( basetable )
terminalValue = None
def terminal (self, generator):
"""Determine if this element is terminal for the generator"""
if self.terminalValue in (0,1):
return self.terminalValue
self.terminalValue = 0
target = generator.getRootObject( self.value )
if target.terminal( generator):
self.terminalValue = 1
return self.terminalValue
def extractFlags( item, report=1 ):
"""Extract the flags from an item as a tuple"""
return (
item.negative,
item.optional,
item.repeating,
item.errorOnFail,
item.lookahead,
item.report and report,
)
def compositeFlags( first, second, report=1 ):
"""Composite flags from two items into overall flag-set"""
result = []
for a,b in map(None, extractFlags(first, report), extractFlags(second, report)):
result.append( a or b )
return tuple(result)
def copyToNewFlags( target, flags ):
"""Copy target using combined flags"""
new = copy.copy( target )
for name,value in map(None,
("negative","optional","repeating","errorOnFail","lookahead",'report'),
flags,
):
setattr(new, name,value)
return new
|