Commit 3eadb8fa authored by Igor Dejanovic's avatar Igor Dejanovic

PEG modules refactored to use visitor pattern in semantic analysis

parent c38397ff
...@@ -974,6 +974,7 @@ def visit_parse_tree(parse_tree, visitor): ...@@ -974,6 +974,7 @@ def visit_parse_tree(parse_tree, visitor):
if visitor.debug: if visitor.debug:
print("ASG: First pass") print("ASG: First pass")
# Visit tree. # Visit tree.
result = parse_tree.visit(visitor) result = parse_tree.visit(visitor)
......
...@@ -13,7 +13,7 @@ from __future__ import print_function, unicode_literals ...@@ -13,7 +13,7 @@ from __future__ import print_function, unicode_literals
from arpeggio import * from arpeggio import *
from arpeggio import RegExMatch as _ from arpeggio import RegExMatch as _
from .peg import sem_actions from .peg import PEGVisitor
from .peg import ParserPEG as ParserPEGOrig from .peg import ParserPEG as ParserPEGOrig
__all__ = ['ParserPEG'] __all__ = ['ParserPEG']
...@@ -49,13 +49,14 @@ def comment(): return _("#.*\n") ...@@ -49,13 +49,14 @@ def comment(): return _("#.*\n")
class ParserPEG(ParserPEGOrig): class ParserPEG(ParserPEGOrig):
def _from_peg(self, language_def): def _from_peg(self, language_def):
parser = ParserPython(peggrammar, comment, reduce_tree=False, parser = ParserPython(peggrammar, comment, reduce_tree=False,
debug=self.debug) debug=self.debug)
parser.root_rule_name = self.root_rule_name parser.root_rule_name = self.root_rule_name
parser.parse(language_def) parse_tree = parser.parse(language_def)
# Initialise cross-ref counter
parser._crossref_cnt = 0
return parser.getASG(sem_actions=sem_actions) # return parser.getASG(sem_actions=sem_actions)
return visit_parse_tree(parse_tree, PEGVisitor(self.root_rule_name,
self.ignore_case,
debug=self.debug))
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# Name: peg.py # Name: peg.py
# Purpose: Implementing PEG language # Purpose: Implementing PEG language
# Author: Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com> # Author: Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com>
# Copyright: (c) 2009 Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com> # Copyright: (c) 2009-2014 Igor R. Dejanovic <igor DOT dejanovic AT gmail DOT com>
# License: MIT License # License: MIT License
####################################################################### #######################################################################
...@@ -49,32 +49,30 @@ def rule_crossref(): return rule_name ...@@ -49,32 +49,30 @@ 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 Semantic Actions
class SemGrammar(SemanticAction):
def first_pass(self, parser, node, children): class PEGVisitor(PTNodeVisitor):
# Find root rule
for rule in children:
if rule.rule_name == parser.root_rule_name:
self.resolved = set()
resolved_rule = self._resolve(parser, rule)
return resolved_rule def __init__(self, root_rule_name, ignore_case, *args, **kwargs):
super(PEGVisitor, self).__init__(*args, **kwargs)
assert False, "Root rule not found!" self.root_rule_name = root_rule_name
self.ignore_case = ignore_case
# Used for linking phase
self.peg_rules = {
"EOF": EndOfFile()
}
def _resolve(self, parser, node): def visit_peggrammar(self, node, children):
def _resolve(node):
def get_rule_by_name(rule_name): def get_rule_by_name(rule_name):
if rule_name in parser.peg_rules: if rule_name in self.peg_rules:
return parser.peg_rules[rule_name] return self.peg_rules[rule_name]
else: else:
raise SemanticError("Rule \"{}\" does not exists." raise SemanticError("Rule \"{}\" does not exists."
.format(rule_name)) .format(rule_name))
def resolve_rule_by_name(rule_name): def resolve_rule_by_name(rule_name):
if parser.debug: if self.debug:
print("Resolving crossref {}".format(rule_name)) print("Resolving crossref {}".format(rule_name))
resolved_rule = get_rule_by_name(rule_name) resolved_rule = get_rule_by_name(rule_name)
while type(resolved_rule) is CrossRef: while type(resolved_rule) is CrossRef:
...@@ -85,8 +83,8 @@ class SemGrammar(SemanticAction): ...@@ -85,8 +83,8 @@ class SemGrammar(SemanticAction):
if resolved_rule.rule_name != rule_name: if resolved_rule.rule_name != rule_name:
resolved_rule = copy.copy(resolved_rule) resolved_rule = copy.copy(resolved_rule)
resolved_rule.rule_name = rule_name resolved_rule.rule_name = rule_name
parser.peg_rules[rule_name] = resolved_rule self.peg_rules[rule_name] = resolved_rule
if parser.debug: if self.debug:
print("Resolving: cloned to {} = > {}" print("Resolving: cloned to {} = > {}"
.format(resolved_rule.rule_name, .format(resolved_rule.rule_name,
resolved_rule.name)) resolved_rule.name))
...@@ -97,17 +95,28 @@ class SemGrammar(SemanticAction): ...@@ -97,17 +95,28 @@ class SemGrammar(SemanticAction):
resolved_rule = resolve_rule_by_name(node.target_rule_name) resolved_rule = resolve_rule_by_name(node.target_rule_name)
if resolved_rule not in self.resolved: if resolved_rule not in self.resolved:
self.resolved.add(resolved_rule) self.resolved.add(resolved_rule)
self._resolve(parser, resolved_rule) _resolve(resolved_rule)
return resolved_rule return resolved_rule
else: else:
# Resolve children nodes # Resolve children nodes
for i, n in enumerate(node.nodes): for i, n in enumerate(node.nodes):
node.nodes[i] = self._resolve(parser, n) node.nodes[i] = _resolve(n)
self.resolved.add(node) self.resolved.add(node)
return node return node
# Find root rule
for rule in children:
print("RULENODE", rule.rule_name, self.root_rule_name)
if rule.rule_name == self.root_rule_name:
print("ROOT", rule.rule_name)
self.resolved = set()
resolved_rule = _resolve(rule)
return resolved_rule
assert False, "Root rule not found!"
def sem_rule(parser, node, children): def visit_rule(self, node, children):
rule_name = children[0] rule_name = children[0]
if len(children) > 2: if len(children) > 2:
retval = Sequence(nodes=children[1:]) retval = Sequence(nodes=children[1:])
...@@ -117,25 +126,19 @@ def sem_rule(parser, node, children): ...@@ -117,25 +126,19 @@ def sem_rule(parser, node, children):
retval.rule_name = rule_name retval.rule_name = rule_name
retval.root = True retval.root = True
if not hasattr(parser, "peg_rules"):
parser.peg_rules = {} # Used for linking phase
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 self.peg_rules[rule_name] = retval
return retval return retval
def visit_sequence(self, node, children):
def sem_sequence(parser, node, children):
if len(children) > 1: if len(children) > 1:
return Sequence(nodes=children[:]) return Sequence(nodes=children[:])
else: else:
# If only one child rule exists reduce. # If only one child rule exists reduce.
return children[0] return children[0]
def visit_ordered_choice(self, node, children):
def sem_ordered_choice(parser, node, children):
if len(children) > 1: if len(children) > 1:
retval = OrderedChoice(nodes=children[:]) retval = OrderedChoice(nodes=children[:])
else: else:
...@@ -143,8 +146,7 @@ def sem_ordered_choice(parser, node, children): ...@@ -143,8 +146,7 @@ def sem_ordered_choice(parser, node, children):
retval = children[0] retval = children[0]
return retval return retval
def visit_prefix(self, node, children):
def sem_prefix(parser, node, children):
if len(children) == 2: if len(children) == 2:
if children[0] == NOT(): if children[0] == NOT():
retval = Not() retval = Not()
...@@ -160,8 +162,7 @@ def sem_prefix(parser, node, children): ...@@ -160,8 +162,7 @@ def sem_prefix(parser, node, children):
return retval return retval
def visit_sufix(self, node, children):
def sem_sufix(parser, node, children):
if len(children) == 2: if len(children) == 2:
if children[1] == STAR(): if children[1] == STAR():
retval = ZeroOrMore(children[0]) retval = ZeroOrMore(children[0])
...@@ -178,37 +179,20 @@ def sem_sufix(parser, node, children): ...@@ -178,37 +179,20 @@ def sem_sufix(parser, node, children):
return retval return retval
def visit_rule_crossref(self, node, children):
def sem_rule_crossref(parser, node, children):
return CrossRef(node.value) return CrossRef(node.value)
def visit_regex(self, node, children):
def sem_regex(parser, node, children):
match = RegExMatch(children[0], match = RegExMatch(children[0],
ignore_case=parser.ignore_case) ignore_case=self.ignore_case)
match.compile() match.compile()
return match return match
def visit_str_match(self, node, children):
def sem_strmatch(parser, node, children):
match_str = node.value[1:-1] match_str = node.value[1:-1]
match_str = match_str.replace("\\'", "'") match_str = match_str.replace("\\'", "'")
match_str = match_str.replace("\\\\", "\\") match_str = match_str.replace("\\\\", "\\")
return StrMatch(match_str, ignore_case=parser.ignore_case) return StrMatch(match_str, ignore_case=self.ignore_case)
sem_actions = {
'peggrammar': SemGrammar(),
'rule': sem_rule,
'sequence': sem_sequence,
'ordered_choice': sem_ordered_choice,
'prefix': sem_prefix,
'sufix': sem_sufix,
'rule_crossref': sem_rule_crossref,
'regex': sem_regex,
'str_match': sem_strmatch,
'expression': SemanticActionSingleChild(),
}
class ParserPEG(Parser): class ParserPEG(Parser):
...@@ -240,6 +224,9 @@ class ParserPEG(Parser): ...@@ -240,6 +224,9 @@ class ParserPEG(Parser):
parser = ParserPython(peggrammar, comment, reduce_tree=False, parser = ParserPython(peggrammar, comment, reduce_tree=False,
debug=self.debug) debug=self.debug)
parser.root_rule_name = self.root_rule_name parser.root_rule_name = self.root_rule_name
parser.parse(language_def) parse_tree = parser.parse(language_def)
return parser.getASG(sem_actions=sem_actions) # return parser.getASG(sem_actions=sem_actions)
return visit_parse_tree(parse_tree, PEGVisitor(self.root_rule_name,
self.ignore_case,
debug=self.debug))
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