#######################################################################
# Name: calc.py
# Purpose: Simple expression evaluator example
# 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 demonstrates grammar definition using python constructs as
# well as using semantic actions to evaluate simple expression in infix
# notation.
#######################################################################

from __future__ import unicode_literals, print_function
try:
    text=unicode
except:
    text=str

from arpeggio import Optional, ZeroOrMore, OneOrMore, EOF, SemanticAction,\
    ParserPython
from arpeggio import RegExMatch as _

def number():     return _(r'\d*\.\d*|\d+')
def factor():     return Optional(["+","-"]), [number,
                          ("(", expression, ")")]
def term():       return factor, ZeroOrMore(["*","/"], factor)
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

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 python constructs therefore we
    # are using ParserPython class.
    parser = ParserPython(calc, debug=debug)

    # An expression we want to evaluate
    input_expr = "-(4-1)*5+(2+4.67)+5.89/(.2+7)"

    # We create a parse tree out of textual input_expr
    parse_tree = parser.parse(input_expr)

    result = parser.getASG()

    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))

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=True)