Commit 70a1728a authored by eckhart's avatar eckhart
Browse files

- minor bug fixes

parent a50710a3
......@@ -1496,7 +1496,7 @@ class Alternative(NaryOperator):
assert len(self.parsers) >= 1
# only the last alternative may be optional. Could this be checked at compile time?
assert all(not isinstance(p, Option) for p in self.parsers[:-1]), \
"Parser-specification Error (EBNF): only the last alternative may be optional!"
"Parser-specification Error: only the last alternative may be optional!"
def __call__(self, text: StringView) -> Tuple[Node, StringView]:
for parser in self.parsers:
......@@ -1533,14 +1533,14 @@ class Alternative(NaryOperator):
return self
class FullSet(NaryOperator):
class AllOf(NaryOperator):
"""
Matches if all elemtns of a set of parsers match. Each parser must
Matches if all elements of a set of parsers match. Each parser must
match exactly once. Other than in a sequence, the order in which
the parsers match is arbitrary, however.
Example:
>>> prefixes = FullSet(Token("A"), Token("B"))
>>> prefixes = AllOf(Token("A"), Token("B"))
>>> Grammar(prefixes)('A B').content()
'A B'
>>> Grammar(prefixes)('B A').content()
......@@ -1549,67 +1549,90 @@ class FullSet(NaryOperator):
EBNF-Notation: `<... ...>` (sequence of parsers enclosed by angular brackets)
EBNF-Example: `set = <letter letter_or_digit>`
"""
# TODO: Implement set
RX_ARGUMENT = re.compile(r'\s(\S)')
NOPE = 1000
def __init__(self, *parsers: Parser, mandatory: int = NOPE, name: str = '') -> None:
super(Series, self).__init__(*parsers, name=name)
L = len(self.parsers)
assert 1 <= L < Series.NOPE, 'Length %i of series exceeds maximum length of %i' \
% (L, Series.NOPE)
if mandatory < 0: mandatory += L
assert 0 <= mandatory < L or mandatory == Series.NOPE
self.mandatory = mandatory
def __init__(self, *parsers: Parser, name: str = '') -> None:
if len(parsers) == 1:
assert isinstance(parsers[0], Series), \
"Parser-specification Error: No single arguments other than a Series " \
"allowed as arguments for AllOf-Parser !"
parsers = parsers[0].parsers
super().__init__(*parsers, name=name)
def __deepcopy__(self, memo):
parsers = copy.deepcopy(self.parsers, memo)
return self.__class__(*parsers, mandatory=self.mandatory, name=self.name)
def __call__(self, text: StringView) -> Tuple[Node, StringView]:
results = () # type: Tuple[Node, ...]
text_ = text # type: StringView
pos = 0
for parser in self.parsers:
node, text_ = parser(text_)
if not node:
if pos < self.mandatory:
return None, text
else:
# Provide useful error messages
m = text.search(Series.RX_ARGUMENT)
i = max(1, text.index(m.regs[1][0])) if m else 1
node = Node(self, text_[:i])
node.add_error('%s expected; "%s" found!' % (str(parser), text_[:10]),
code=Error.MANDATORY_CONTINUATION)
text_ = text_[i:]
results += (node,)
# if node.error_flag: # break on first error
# break
pos += 1
pset = set(self.parsers) # type: Set[Parser]
while pset:
# TODO: Ordnung berücksichtigen, kein SET verwenden!
for parser in pset:
node, text__ = parser(text_)
if node:
results += (node,)
text_ = text__
pset.remove(parser)
break
else:
return None, text
assert len(results) <= len(self.parsers)
return Node(self, results), text_
def __repr__(self):
return " ".join([parser.repr for parser in self.parsers[:self.mandatory]]
+ (['§'] if self.mandatory != Series.NOPE else [])
+ [parser.repr for parser in self.parsers[self.mandatory:]])
return '<' + ' '.join(parser.repr for parser in self.parsers) + '>'
# The following operator definitions add syntactical sugar, so one can write:
# `RE('\d+') + Optional(RE('\.\d+)` instead of `Series(RE('\d+'), Optional(RE('\.\d+))`
@staticmethod
def combined_mandatory(left, right):
left_mandatory, left_length = (left.mandatory, len(left.parsers)) \
if isinstance(left, Series) else (Series.NOPE, 1)
if left_mandatory != Series.NOPE:
return left_mandatory
right_mandatory = right.mandatory if isinstance(right, Series) else Series.NOPE
if right_mandatory != Series.NOPE:
return right_mandatory + left_length
return Series.NOPE
class SomeOf(NaryOperator):
"""
Matches if at least one element of a set of parsers match. No parser
must match more than once . Other than in a sequence, the order in which
the parsers match is arbitrary, however.
Example:
>>> prefixes = SomeOf(Token("A"), Token("B"))
>>> Grammar(prefixes)('A B').content()
'A B'
>>> Grammar(prefixes)('B A').content()
'B A'
>>> Grammar(prefixes)('B').content()
'B'
EBNF-Notation: `<... ...>` (sequence of parsers enclosed by angular brackets)
EBNF-Example: `set = <letter letter_or_digit>`
"""
def __init__(self, *parsers: Parser, name: str = '') -> None:
if len(parsers) == 1:
assert isinstance(parsers[0], Alternative), \
"Parser-specification Error: No single arguments other than a Alternative " \
"allowed as arguments for SomeOf-Parser !"
parsers = parsers[0].parsers
super().__init__(*parsers, name=name)
def __call__(self, text: StringView) -> Tuple[Node, StringView]:
results = () # type: Tuple[Node, ...]
text_ = text # type: StringView
pset = set(self.parsers) # type: Set[Parser]
while pset:
# TODO: Ordnung berücksichtigen, kein Set verwenden!!!
for parser in pset:
node, text__ = parser(text_)
if node:
results += (node,)
text_ = text__
pset.remove(parser)
break
else:
pset = set()
assert len(results) <= len(self.parsers)
if results:
return Node(self, results), text_
else:
return None, text
def __repr__(self):
return '<' + ' | '.join(parser.repr for parser in self.parsers) + '>'
########################################################################
#
......
......@@ -24,7 +24,7 @@ flowmarker = "!" | "&" # '!' negative lookahead, '&' p
retrieveop = "::" | ":" # '::' pop, ':' retrieve
group = "(" §expression ")"
# set = "<" §expression ">"
set = "<" §expression ">"
oneormore = "{" expression "}+"
repetition = "{" §expression "}"
option = "[" §expression "]"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment