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,74 +28,69 @@ def expression(): return term, ZeroOrMore(["+", "-"], term) ...@@ -28,74 +28,69 @@ 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: """
print("Converting {}.".format(node.value)) if self.debug:
return float(node.value) print("Converting {}.".format(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: """
print("Factor {}".format(children)) if self.debug:
if len(children) == 1: print("Factor {}".format(children))
return children[0] if len(children) == 1:
sign = -1 if children[0] == '-' else 1 return children[0]
return sign * children[-1] sign = -1 if children[0] == '-' else 1
return sign * children[-1]
def termSA(parser, node, children):
""" def visit_term(self, node, children):
Divides or multiplies factors. """
Factor nodes will be already evaluated. Divides or multiplies factors.
""" Factor nodes will be already evaluated.
if parser.debug: """
print("Term {}".format(children)) if self.debug:
term = children[0] print("Term {}".format(children))
for i in range(2, len(children), 2): term = children[0]
if children[i-1] == "*": for i in range(2, len(children), 2):
term *= children[i] if children[i-1] == "*":
else: term *= children[i]
term /= children[i] else:
if parser.debug: term /= children[i]
print("Term = {}".format(term)) if self.debug:
return term print("Term = {}".format(term))
return term
def exprSA(parser, node, children): def visit_expression(self, 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
# Check for unary + or - operator # Check for unary + or - operator
if text(children[0]) in "+-": if text(children[0]) in "+-":
start = 1 start = 1
for i in range(start, len(children), 2): for i in range(start, len(children), 2):
if i and children[i - 1] == "-": if i and children[i - 1] == "-":
expr -= children[i] expr -= children[i]
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,13 +104,12 @@ def main(debug=False): ...@@ -109,13 +104,12 @@ 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 evaluated result of the input_expr expression.
# returned value will be the result of the input_expr expression. print("{} = {}".format(input_expr, result))
print("{} = {}".format(input_expr, result))
if __name__ == "__main__": if __name__ == "__main__":
# In debug mode dot (graphviz) files for parser model # In debug mode dot (graphviz) files for parser model
......
...@@ -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