16498Snate@binkert.org# -----------------------------------------------------------------------------
26498Snate@binkert.org# calc.py
36498Snate@binkert.org#
46498Snate@binkert.org# A calculator parser that makes use of closures. The function make_calculator()
56498Snate@binkert.org# returns a function that accepts an input string and returns a result.  All
66498Snate@binkert.org# lexing rules, parsing rules, and internal state are held inside the function.
76498Snate@binkert.org# -----------------------------------------------------------------------------
86498Snate@binkert.org
96498Snate@binkert.orgimport sys
106498Snate@binkert.orgsys.path.insert(0,"../..")
116498Snate@binkert.org
126498Snate@binkert.orgif sys.version_info[0] >= 3:
136498Snate@binkert.org    raw_input = input
146498Snate@binkert.org
156498Snate@binkert.org# Make a calculator function
166498Snate@binkert.org
176498Snate@binkert.orgdef make_calculator():
186498Snate@binkert.org    import ply.lex as lex
196498Snate@binkert.org    import ply.yacc as yacc
206498Snate@binkert.org
216498Snate@binkert.org    # ------- Internal calculator state
226498Snate@binkert.org
236498Snate@binkert.org    variables = { }       # Dictionary of stored variables
246498Snate@binkert.org
256498Snate@binkert.org    # ------- Calculator tokenizing rules
266498Snate@binkert.org
276498Snate@binkert.org    tokens = (
286498Snate@binkert.org        'NAME','NUMBER',
296498Snate@binkert.org    )
306498Snate@binkert.org
316498Snate@binkert.org    literals = ['=','+','-','*','/', '(',')']
326498Snate@binkert.org
336498Snate@binkert.org    t_ignore = " \t"
346498Snate@binkert.org
356498Snate@binkert.org    t_NAME    = r'[a-zA-Z_][a-zA-Z0-9_]*'
366498Snate@binkert.org
376498Snate@binkert.org    def t_NUMBER(t):
386498Snate@binkert.org        r'\d+'
396498Snate@binkert.org        t.value = int(t.value)
406498Snate@binkert.org        return t
416498Snate@binkert.org
426498Snate@binkert.org    def t_newline(t):
436498Snate@binkert.org        r'\n+'
446498Snate@binkert.org        t.lexer.lineno += t.value.count("\n")
456498Snate@binkert.org
466498Snate@binkert.org    def t_error(t):
476498Snate@binkert.org        print("Illegal character '%s'" % t.value[0])
486498Snate@binkert.org        t.lexer.skip(1)
496498Snate@binkert.org
506498Snate@binkert.org    # Build the lexer
516498Snate@binkert.org    lexer = lex.lex()
526498Snate@binkert.org
536498Snate@binkert.org    # ------- Calculator parsing rules
546498Snate@binkert.org
556498Snate@binkert.org    precedence = (
566498Snate@binkert.org        ('left','+','-'),
576498Snate@binkert.org        ('left','*','/'),
586498Snate@binkert.org        ('right','UMINUS'),
596498Snate@binkert.org    )
606498Snate@binkert.org
616498Snate@binkert.org    def p_statement_assign(p):
626498Snate@binkert.org        'statement : NAME "=" expression'
636498Snate@binkert.org        variables[p[1]] = p[3]
646498Snate@binkert.org        p[0] = None
656498Snate@binkert.org
666498Snate@binkert.org    def p_statement_expr(p):
676498Snate@binkert.org        'statement : expression'
686498Snate@binkert.org        p[0] = p[1]
696498Snate@binkert.org
706498Snate@binkert.org    def p_expression_binop(p):
716498Snate@binkert.org        '''expression : expression '+' expression
726498Snate@binkert.org                      | expression '-' expression
736498Snate@binkert.org                      | expression '*' expression
746498Snate@binkert.org                      | expression '/' expression'''
756498Snate@binkert.org        if p[2] == '+'  : p[0] = p[1] + p[3]
766498Snate@binkert.org        elif p[2] == '-': p[0] = p[1] - p[3]
776498Snate@binkert.org        elif p[2] == '*': p[0] = p[1] * p[3]
786498Snate@binkert.org        elif p[2] == '/': p[0] = p[1] / p[3]
796498Snate@binkert.org
806498Snate@binkert.org    def p_expression_uminus(p):
816498Snate@binkert.org        "expression : '-' expression %prec UMINUS"
826498Snate@binkert.org        p[0] = -p[2]
836498Snate@binkert.org
846498Snate@binkert.org    def p_expression_group(p):
856498Snate@binkert.org        "expression : '(' expression ')'"
866498Snate@binkert.org        p[0] = p[2]
876498Snate@binkert.org
886498Snate@binkert.org    def p_expression_number(p):
896498Snate@binkert.org        "expression : NUMBER"
906498Snate@binkert.org        p[0] = p[1]
916498Snate@binkert.org
926498Snate@binkert.org    def p_expression_name(p):
936498Snate@binkert.org        "expression : NAME"
946498Snate@binkert.org        try:
956498Snate@binkert.org            p[0] = variables[p[1]]
966498Snate@binkert.org        except LookupError:
976498Snate@binkert.org            print("Undefined name '%s'" % p[1])
986498Snate@binkert.org            p[0] = 0
996498Snate@binkert.org
1006498Snate@binkert.org    def p_error(p):
1016498Snate@binkert.org        if p:
1026498Snate@binkert.org            print("Syntax error at '%s'" % p.value)
1036498Snate@binkert.org        else:
1046498Snate@binkert.org            print("Syntax error at EOF")
1056498Snate@binkert.org
1066498Snate@binkert.org
1076498Snate@binkert.org    # Build the parser
1086498Snate@binkert.org    parser = yacc.yacc()
1096498Snate@binkert.org
1106498Snate@binkert.org    # ------- Input function
1116498Snate@binkert.org
1126498Snate@binkert.org    def input(text):
1136498Snate@binkert.org        result = parser.parse(text,lexer=lexer)
1146498Snate@binkert.org        return result
1156498Snate@binkert.org
1166498Snate@binkert.org    return input
1176498Snate@binkert.org
1186498Snate@binkert.org# Make a calculator object and use it
1196498Snate@binkert.orgcalc = make_calculator()
1206498Snate@binkert.org
1216498Snate@binkert.orgwhile True:
1226498Snate@binkert.org    try:
1236498Snate@binkert.org        s = raw_input("calc > ")
1246498Snate@binkert.org    except EOFError:
1256498Snate@binkert.org        break
1266498Snate@binkert.org    r = calc(s)
1276498Snate@binkert.org    if r:
1286498Snate@binkert.org        print(r)
1296498Snate@binkert.org
1306498Snate@binkert.org
131