Commit f9932bc7 authored by Igor Dejanovic's avatar Igor Dejanovic

Reorganized code for calc example.

parent f00ab04b
...@@ -16,8 +16,8 @@ try: ...@@ -16,8 +16,8 @@ try:
except: except:
text=str text=str
from arpeggio import Optional, ZeroOrMore, OneOrMore, EOF, SemanticAction,\ from arpeggio import Optional, ZeroOrMore, OneOrMore, EOF, \
ParserPython ParserPython, PTNodeVisitor, visit_parse_tree
from arpeggio import RegExMatch as _ from arpeggio import RegExMatch as _
def number(): return _(r'\d*\.\d*|\d+') def number(): return _(r'\d*\.\d*|\d+')
...@@ -28,32 +28,33 @@ def expression(): return term, ZeroOrMore(["+", "-"], term) ...@@ -28,32 +28,33 @@ def expression(): return term, ZeroOrMore(["+", "-"], term)
def calc(): return OneOrMore(expression), EOF def calc(): return OneOrMore(expression), EOF
# Semantic actions class CalcVisitor(PTNodeVisitor):
def to_floatSA(parser, node, children):
def visit_number(self, node, children):
""" """
Converts node value to float. Converts node value to float.
""" """
if parser.debug: if self.debug:
print("Converting {}.".format(node.value)) print("Converting {}.".format(node.value))
return float(node.value) return float(node.value)
def factorSA(parser, node, children): def visit_factor(self, node, children):
""" """
Removes parenthesis if exists and returns what was contained inside. Removes parenthesis if exists and returns what was contained inside.
""" """
if parser.debug: if self.debug:
print("Factor {}".format(children)) print("Factor {}".format(children))
if len(children) == 1: if len(children) == 1:
return children[0] return children[0]
sign = -1 if children[0] == '-' else 1 sign = -1 if children[0] == '-' else 1
return sign * children[-1] return sign * children[-1]
def termSA(parser, node, children): def visit_term(self, node, children):
""" """
Divides or multiplies factors. Divides or multiplies factors.
Factor nodes will be already evaluated. Factor nodes will be already evaluated.
""" """
if parser.debug: if self.debug:
print("Term {}".format(children)) print("Term {}".format(children))
term = children[0] term = children[0]
for i in range(2, len(children), 2): for i in range(2, len(children), 2):
...@@ -61,17 +62,16 @@ def termSA(parser, node, children): ...@@ -61,17 +62,16 @@ def termSA(parser, node, children):
term *= children[i] term *= children[i]
else: else:
term /= children[i] term /= children[i]
if parser.debug: if self.debug:
print("Term = {}".format(term)) print("Term = {}".format(term))
return term return term
def visit_expression(self, node, children):
def exprSA(parser, node, children):
""" """
Adds or substracts terms. Adds or substracts terms.
Term nodes will be already evaluated. Term nodes will be already evaluated.
""" """
if parser.debug: if self.debug:
print("Expression {}".format(children)) print("Expression {}".format(children))
expr = 0 expr = 0
start = 0 start = 0
...@@ -85,17 +85,12 @@ def exprSA(parser, node, children): ...@@ -85,17 +85,12 @@ def exprSA(parser, node, children):
else: else:
expr += children[i] expr += children[i]
if parser.debug: if self.debug:
print("Expression = {}".format(expr)) print("Expression = {}".format(expr))
return expr return expr
# Connecting rules with semantic actions
number.sem = to_floatSA
factor.sem = factorSA
term.sem = termSA
expression.sem = exprSA
def main(debug=False): def main(debug=False):
# First we will make a parser - an instance of the calc parser model. # First we will make a parser - an instance of the calc parser model.
...@@ -109,12 +104,11 @@ def main(debug=False): ...@@ -109,12 +104,11 @@ def main(debug=False):
# We create a parse tree out of textual input_expr # We create a parse tree out of textual input_expr
parse_tree = parser.parse(input_expr) parse_tree = parser.parse(input_expr)
result = parser.getASG() result = visit_parse_tree(parse_tree, CalcVisitor(debug=debug))
if debug: # visit_parse_tree will start semantic analysis.
# getASG will start semantic analysis.
# In this case semantic analysis will evaluate expression and # In this case semantic analysis will evaluate expression and
# returned value will be the result of the input_expr expression. # returned value will be evaluated result of the input_expr expression.
print("{} = {}".format(input_expr, result)) print("{} = {}".format(input_expr, result))
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -15,9 +15,8 @@ ...@@ -15,9 +15,8 @@
from __future__ import absolute_import, unicode_literals, print_function from __future__ import absolute_import, unicode_literals, print_function
from arpeggio.cleanpeg import ParserPEG from arpeggio.cleanpeg import ParserPEG
from arpeggio import visit_parse_tree
# Semantic actions from calc import CalcVisitor
from calc import to_floatSA, factorSA, termSA, exprSA
# Grammar is defined using textual specification based on PEG language. # Grammar is defined using textual specification based on PEG language.
calc_grammar = """ calc_grammar = """
...@@ -29,13 +28,6 @@ calc_grammar = """ ...@@ -29,13 +28,6 @@ calc_grammar = """
calc = expression+ EOF calc = expression+ EOF
""" """
# Rules are mapped to semantic actions
sem_actions = {
"number" : to_floatSA,
"factor" : factorSA,
"term" : termSA,
"expression" : exprSA,
}
def main(debug=False): def main(debug=False):
...@@ -50,12 +42,11 @@ def main(debug=False): ...@@ -50,12 +42,11 @@ def main(debug=False):
# Then parse tree is created out of the input_expr expression. # Then parse tree is created out of the input_expr expression.
parse_tree = parser.parse(input_expr) parse_tree = parser.parse(input_expr)
result = parser.getASG(sem_actions) result = visit_parse_tree(parse_tree, CalcVisitor(debug=debug))
# getASG will start semantic analysis. # visit_parse_tree will start semantic analysis.
# In this case semantic analysis will evaluate expression and # In this case semantic analysis will evaluate expression and
# returned value will be evaluated result of the input_expr expression. # returned value will be evaluated result of the input_expr expression.
# Semantic actions are supplied to the getASG function.
print("{} = {}".format(input_expr, result)) print("{} = {}".format(input_expr, result))
if __name__ == "__main__": if __name__ == "__main__":
......
#######################################################################
# Name: calc_peg.py
# Purpose: Simple expression evaluator example using PEG language and
# visitor pattern for semantic analysis.
# Author: 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
#
# This example is functionally equivalent to calc_peg.py.
# It is a demonstration of visitor pattern approach for semantic analysis.
# Parser model as well as parse tree exported to dot files should be
# the same as parser model and parse tree generated in calc.py example.
#######################################################################
from __future__ import absolute_import, unicode_literals, print_function
try:
text = unicode
except:
text = str
from arpeggio.cleanpeg import ParserPEG
from arpeggio import PTNodeVisitor, visit_parse_tree
# Grammar is defined using textual specification based on PEG language.
calc_grammar = """
number = r'\d*\.\d*|\d+'
factor = ("+" / "-")?
(number / "(" expression ")")
term = factor (( "*" / "/") factor)*
expression = term (("+" / "-") term)*
calc = expression+ EOF
"""
class CalcVisitor(PTNodeVisitor):
def visit_number(self, node, children):
"""
Converts node value to float.
"""
if self.debug:
print("Converting {}.".format(node.value))
return float(node.value)
def visit_factor(self, node, children):
"""
Removes parenthesis if exists and returns what was contained inside.
"""
if self.debug:
print("Factor {}".format(children))
if len(children) == 1:
return children[0]
sign = -1 if children[0] == '-' else 1
return sign * children[-1]
def visit_term(self, node, children):
"""
Divides or multiplies factors.
Factor nodes will be already evaluated.
"""
if self.debug:
print("Term {}".format(children))
term = children[0]
for i in range(2, len(children), 2):
if children[i-1] == "*":
term *= children[i]
else:
term /= children[i]
if self.debug:
print("Term = {}".format(term))
return term
def visit_expression(self, node, children):
"""
Adds or substracts terms.
Term nodes will be already evaluated.
"""
if self.debug:
print("Expression {}".format(children))
expr = 0
start = 0
# Check for unary + or - operator
if text(children[0]) in "+-":
start = 1
for i in range(start, len(children), 2):
if i and children[i - 1] == "-":
expr -= children[i]
else:
expr += children[i]
if self.debug:
print("Expression = {}".format(expr))
return expr
def main(debug=False):
# First we will make a parser - an instance of the calc parser model.
# Parser model is given in the form of PEG notation therefore we
# are using ParserPEG class. Root rule name (parsing expression) is "calc".
parser = ParserPEG(calc_grammar, "calc", debug=debug)
# An expression we want to evaluate
input_expr = "-(4-1)*5+(2+4.67)+5.89/(.2+7)"
# Then parse tree is created out of the input_expr expression.
parse_tree = parser.parse(input_expr)
result = visit_parse_tree(parse_tree, CalcVisitor(debug=debug))
# visit_parse_tree will start semantic analysis.
# In this case semantic analysis will evaluate expression and
# returned value will be evaluated result of the input_expr expression.
print("{} = {}".format(input_expr, result))
if __name__ == "__main__":
# In debug mode dot (graphviz) files for parser model
# and parse tree will be created for visualization.
# Checkout current folder for .dot files.
main(debug=False)
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