Commit 2d460242 authored by Igor Dejanovic's avatar Igor Dejanovic

Fixing peg language

parent 8609d13a
...@@ -16,6 +16,17 @@ from arpeggio.export import PMDOTExporter, PTDOTExporter ...@@ -16,6 +16,17 @@ from arpeggio.export import PMDOTExporter, PTDOTExporter
__all__ = ['ParserPEG'] __all__ = ['ParserPEG']
# PEG syntax rules
def peggrammar(): return OneOrMore(rule), EOF
def rule(): return rule_name, LEFT_ARROW, ordered_choice, ";"
def ordered_choice(): return sequence, ZeroOrMore(SLASH, sequence)
def sequence(): return OneOrMore(prefix)
def prefix(): return Optional([AND,NOT]), sufix
def sufix(): return expression, Optional([QUESTION, STAR, PLUS])
def expression(): return [regex, rule_crossref,
(OPEN, ordered_choice, CLOSE),
str_match]
# PEG Lexical rules # PEG Lexical rules
def LEFT_ARROW(): return "<-" def LEFT_ARROW(): return "<-"
def SLASH(): return "/" def SLASH(): return "/"
...@@ -33,38 +44,49 @@ def rule_crossref(): return rule_name ...@@ -33,38 +44,49 @@ def rule_crossref(): return rule_name
def str_match(): return _(r'(\'(\\\'|[^\'])*\')|("[^"]*")') def str_match(): return _(r'(\'(\\\'|[^\'])*\')|("[^"]*")')
def comment(): return "//", _(".*\n") def comment(): return "//", _(".*\n")
# PEG syntax rules
def peggrammar(): return OneOrMore(rule), EOF
def rule(): return rule_name, LEFT_ARROW, ordered_choice, ";"
def ordered_choice(): return sequence, ZeroOrMore(SLASH, sequence)
def sequence(): return OneOrMore(prefix)
def prefix(): return Optional([AND,NOT]), sufix
def sufix(): return expression, Optional([QUESTION, STAR, PLUS])
def expression(): return [regex, rule_crossref,
(OPEN, ordered_choice, CLOSE),
str_match]
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# PEG Semantic Actions # PEG Semantic Actions
class PEGSemanticAction(SemanticAction): class SemGrammar(SemanticAction):
def _resolve(self, parser, rule_name): def first_pass(self, parser, node, children):
return parser.peg_rules[parser.root_rule_name]
if rule_name in parser.peg_rules: def _resolve(self, parser, node):
resolved_rule = parser.peg_rules[rule_name]
if type(resolved_rule) is CrossRef:
resolved_rule = self._resolve(parser, resolved_rule.rule_name)
if parser.debug: def get_rule_by_name(rule_name):
print("Resolving: CrossRef {} => {}".format(rule_name, if rule_name in parser.peg_rules:
resolved_rule.name)) return parser.peg_rules[rule_name]
else:
raise SemanticError("Rule \"{}\" does not exists."
.format(n.rule_name))
for i, n in enumerate(node.nodes):
if isinstance(n, CrossRef):
rule_name = n.rule_name
if parser.debug:
print("Resolving crossref {}".format(rule_name))
resolved_rule = get_rule_by_name(rule_name)
while type(resolved_rule) is CrossRef:
target_rule = resolved_rule.rule_name
resolved_rule = get_rule_by_name(target_rule)
# If resolved rule hasn't got the same name it
# should be cloned and preserved in the peg_rules cache
if resolved_rule.rule_name != rule_name:
resolved_rule = copy.copy(resolved_rule)
resolved_rule.rule_name = rule_name
parser.peg_rules[rule_name] = resolved_rule
if parser.debug:
print("Resolving: cloned to {} = > {}"\
.format(resolved_rule.rule_name, resolved_rule.name))
node.nodes[i] = resolved_rule
else:
resolved_rule = n
return resolved_rule if not resolved_rule in self.resolved:
else: self.resolved.add(resolved_rule)
raise SemanticError("Rule \"{}\" does not exists." self._resolve(parser, resolved_rule)
.format(rule_name))
def second_pass(self, parser, node): def second_pass(self, parser, node):
''' '''
...@@ -73,148 +95,106 @@ class PEGSemanticAction(SemanticAction): ...@@ -73,148 +95,106 @@ class PEGSemanticAction(SemanticAction):
if parser.debug: if parser.debug:
print("Second pass:", type(node), str(node)) print("Second pass:", type(node), str(node))
if isinstance(node, ParsingExpression): self.resolved = set()
self._resolve(parser, node)
for i, n in enumerate(node.nodes): return node
if isinstance(n, CrossRef): peggrammar.sem = SemGrammar()
resolved_rule = self._resolve(parser, n.rule_name)
# If resolved rule hasn't got the same name it
# should be cloned and preserved in the peg_rules cache
if resolved_rule.rule_name != n.rule_name:
resolved_rule = copy.copy(resolved_rule)
resolved_rule.rule_name = n.rule_name
parser.peg_rules[resolved_rule.rule_name] = resolved_rule
if parser.debug:
print("Resolving: cloned to {} = > {}"\
.format(resolved_rule.rule_name, resolved_rule.name))
node.nodes[i] = resolved_rule
return node
elif not isinstance(node, CrossRef):
raise SemanticError("Invalid type '{}'({}) after first pass."
.format(type(node), str(node)))
class SemGrammar(SemanticAction):
def first_pass(self, parser, node, children):
return parser.peg_rules[parser.root_rule_name]
def sem_rule(parser, node, children):
rule_name = children[0]
if len(children) > 2:
retval = Sequence(nodes=children[1:])
else:
retval = children[1]
class SemRule(PEGSemanticAction): # CrossRef already has rule_name set
def first_pass(self, parser, node, children): # that attrib is a target rule name
rule_name = children[0] if type(retval) is not CrossRef:
if len(children) > 2:
retval = Sequence(nodes=children[1:])
else:
retval = children[1]
retval.rule_name = rule_name retval.rule_name = rule_name
retval.root = True retval.root = True
if not hasattr(parser, "peg_rules"): if not hasattr(parser, "peg_rules"):
parser.peg_rules = {} # Used for linking phase parser.peg_rules = {} # Used for linking phase
parser.peg_rules["EOF"] = EndOfFile() parser.peg_rules["EOF"] = EndOfFile()
# Keep a map of parser rules for cross reference # Keep a map of parser rules for cross reference
# resolving. # resolving.
parser.peg_rules[rule_name] = retval parser.peg_rules[rule_name] = retval
return retval return retval
rule.sem = sem_rule
class SemSequence(PEGSemanticAction): def sem_sequence(parser, node, children):
def first_pass(self, parser, node, children): if len(children) > 1:
if len(children) > 1: return Sequence(nodes=children[:])
return Sequence(nodes=children[:]) else:
# If only one child rule exists reduce.
return children[0]
sequence.sem = sem_sequence
def sem_ordered_choice(parser, node, children):
if len(children) > 1:
retval = OrderedChoice(nodes=children[:])
else:
# If only one child rule exists reduce.
retval = children[0]
return retval
ordered_choice.sem = sem_ordered_choice
def sem_prefix(parser, node, children):
if len(children) == 2:
if children[0] == NOT():
retval = Not()
else: else:
# If only one child rule exists reduce. retval = And()
return children[0] if type(children[1]) is list:
retval.nodes = children[1]
class SemOrderedChoice(PEGSemanticAction):
def first_pass(self, parser, node, children):
if len(children) > 1:
retval = OrderedChoice(nodes=children[:])
else: else:
# If only one child rule exists reduce. retval.nodes = [children[1]]
retval = children[0] else:
return retval # If there is no optional prefix reduce.
retval = children[0]
class SemPrefix(PEGSemanticAction): return retval
def first_pass(self, parser, node, children): prefix.sem = sem_prefix
if len(children) == 2:
if children[0] == NOT(): def sem_sufix(parser, node, children):
retval = Not() if len(children) == 2:
else: if children[1] == STAR():
retval = And() retval = ZeroOrMore(children[0])
if type(children[1]) is list: elif children[1] == QUESTION():
retval.nodes = children[1] retval = Optional(children[0])
else:
retval.nodes = [children[1]]
else: else:
# If there is no optional prefix reduce. retval = OneOrMore(children[0])
retval = children[0] if type(children[0]) is list:
retval.nodes = children[0]
return retval
class SemSufix(PEGSemanticAction):
def first_pass(self, parser, node, children):
if len(children) == 2:
if children[1] == STAR():
retval = ZeroOrMore(children[0])
elif children[1] == QUESTION():
retval = Optional(children[0])
else:
retval = OneOrMore(children[0])
if type(children[0]) is list:
retval.nodes = children[0]
else:
retval.nodes = [children[0]]
else: else:
retval = children[0] retval.nodes = [children[0]]
else:
return retval retval = children[0]
return retval
class SemExpression(PEGSemanticAction): sufix.sem = sem_sufix
def first_pass(self, parser, node, children):
return children[0] def sem_rule_crossref(parser, node, children):
return CrossRef(node.value)
rule_crossref.sem = sem_rule_crossref
class SemRuleCrossRef(SemanticAction):
def first_pass(self, parser, node, children): def sem_regex(parser, node, children):
return CrossRef(node.value) match = RegExMatch(children[0],
ignore_case=parser.ignore_case)
match.compile()
class SemRegEx(SemanticAction): return match
def first_pass(self, parser, node, children): regex.sem = sem_regex
match = RegExMatch(children[0],
ignore_case=parser.ignore_case) def sem_strmatch(parser, node, children):
match.compile() match_str = node.value[1:-1]
return match match_str = match_str.replace("\\'", "'")
match_str = match_str.replace("\\\\", "\\")
class SemStrMatch(SemanticAction): return StrMatch(match_str, ignore_case=parser.ignore_case)
def first_pass(self, parser, node, children): str_match.sem = sem_strmatch
match_str = node.value[1:-1]
match_str = match_str.replace("\\'", "'") expression.sem = SemanticActionSingleChild()
match_str = match_str.replace("\\\\", "\\")
return StrMatch(match_str, ignore_case=parser.ignore_case)
peggrammar.sem = SemGrammar()
rule.sem = SemRule()
ordered_choice.sem = SemOrderedChoice()
sequence.sem = SemSequence()
prefix.sem = SemPrefix()
sufix.sem = SemSufix()
expression.sem = SemExpression()
rule_crossref.sem = SemRuleCrossRef()
regex.sem = SemRegEx()
str_match.sem = SemStrMatch()
class ParserPEG(Parser): class ParserPEG(Parser):
......
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