Commit f9932bc7 authored by Igor Dejanovic's avatar Igor Dejanovic

Reorganized code for calc example.

parent f00ab04b
......@@ -16,8 +16,8 @@ try:
except:
text=str
from arpeggio import Optional, ZeroOrMore, OneOrMore, EOF, SemanticAction,\
ParserPython
from arpeggio import Optional, ZeroOrMore, OneOrMore, EOF, \
ParserPython, PTNodeVisitor, visit_parse_tree
from arpeggio import RegExMatch as _
def number(): return _(r'\d*\.\d*|\d+')
......@@ -28,74 +28,69 @@ def expression(): return term, ZeroOrMore(["+", "-"], term)
def calc(): return OneOrMore(expression), EOF
# Semantic actions
def to_floatSA(parser, node, children):
"""
Converts node value to float.
"""
if parser.debug:
print("Converting {}.".format(node.value))
return float(node.value)
def factorSA(parser, node, children):
"""
Removes parenthesis if exists and returns what was contained inside.
"""
if parser.debug:
print("Factor {}".format(children))
if len(children) == 1:
return children[0]
sign = -1 if children[0] == '-' else 1
return sign * children[-1]
def termSA(parser, node, children):
"""
Divides or multiplies factors.
Factor nodes will be already evaluated.
"""
if parser.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 parser.debug:
print("Term = {}".format(term))
return term
def exprSA(parser, node, children):
"""
Adds or substracts terms.
Term nodes will be already evaluated.
"""
if parser.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 parser.debug:
print("Expression = {}".format(expr))
return expr
# Connecting rules with semantic actions
number.sem = to_floatSA
factor.sem = factorSA
term.sem = termSA
expression.sem = exprSA
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.
......@@ -109,13 +104,12 @@ def main(debug=False):
# We create a parse tree out of textual input_expr
parse_tree = parser.parse(input_expr)
result = parser.getASG()
result = visit_parse_tree(parse_tree, CalcVisitor(debug=debug))
if debug:
# getASG will start semantic analysis.
# In this case semantic analysis will evaluate expression and
# returned value will be the result of the input_expr expression.
print("{} = {}".format(input_expr, result))
# 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
......
......@@ -15,9 +15,8 @@
from __future__ import absolute_import, unicode_literals, print_function
from arpeggio.cleanpeg import ParserPEG
# Semantic actions
from calc import to_floatSA, factorSA, termSA, exprSA
from arpeggio import visit_parse_tree
from calc import CalcVisitor
# Grammar is defined using textual specification based on PEG language.
calc_grammar = """
......@@ -29,13 +28,6 @@ calc_grammar = """
calc = expression+ EOF
"""
# Rules are mapped to semantic actions
sem_actions = {
"number" : to_floatSA,
"factor" : factorSA,
"term" : termSA,
"expression" : exprSA,
}
def main(debug=False):
......@@ -50,12 +42,11 @@ def main(debug=False):
# Then parse tree is created out of the input_expr expression.
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
# returned value will be evaluated result of the input_expr expression.
# Semantic actions are supplied to the getASG function.
print("{} = {}".format(input_expr, result))
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