bison マニュアルの多機能電卓 練習問題2

定数 PI, E を追加

#!/usr/bin/env python

import ply.lex as lex

tokens = (
  'NUM',
  'VAR',
  'FNCT',
)

literals = ['+', '-', '*', '/', '^', '(', ')', '=']

def t_NUM(t):
    r'\d+(\.\d+)*'
    try:
        t.value = float(t.value)
    except ValueError:
        print "Integer value too large", t.value
        t.value = 0
    return t

def t_VAR(t):
    r'[a-zA-Z_][a-zA-Z0-9_]*'
    s = getsym(t.value)
    if not s:
        s = putsym(t.value, "VAR")
    t.type = s["TYPE"]
    return t

t_ignore = " \t"

def t_error(t):
    print "Illegal character '%s'" % t.value[0]
    t.lexer.skip(1)

lex.lex()


import ply.yacc as yacc

precedence = (
    ('right', '='),
    ('left',  '+', '-'),
    ('left',  '*', '/'),
    ('left',  'NEG'),
    ('right', '^'),
    )

def p_input(p):
    "input : exp"
    print "\t%f" % p[1]

def p_input_error(p):
    "input : exp error"

def p_exp_num(p):
    "exp : NUM"
    p[0] = p[1]

def p_exp_var(p):
    "exp : VAR"
    p[0] = sym[p[1]]["VAL"]

def p_exp(p):
    '''exp : VAR '=' exp
           | FNCT '(' exp ')'
           | exp '+' exp
           | exp '-' exp
           | exp '*' exp
           | exp '/' exp
           | '-' exp %prec NEG
           | exp '^' exp
           | '(' exp ')' '''
    if   p[1] == '(' : p[0] =  p[2]
    elif p[1] == '-' : p[0] = -p[2]
    elif p[2] == '=' : p[0] =  p[3]; putsym(p[1], "VAR", p[3])
    elif p[2] == '(' : p[0] =  func(p[1], p[3])
    elif p[2] == '+' : p[0] =  p[1] + p[3]
    elif p[2] == '-' : p[0] =  p[1] - p[3]
    elif p[2] == '*' : p[0] =  p[1] * p[3]
    elif p[2] == '/' : p[0] =  p[1] / p[3]
    elif p[2] == '^' : p[0] =  p[1] ** p[3]

def p_error(p):
    print "Syntax error at '%s'" % p.value

yacc.yacc()


sym = {}

def putsym(sym_name, sym_type, value = 0):
    global sym
    sym[sym_name] = { "NAME" : sym_name,
                      "TYPE" : sym_type,
                      "VAL"  : value }
    return sym[sym_name]

def getsym(sym_name):
    global sym
    if sym.has_key(sym_name):
        return sym[sym_name]
    else:
        return None

def func(sym_name, arg):
    import math
    
    if   sym_name == "sin":
        return math.sin(arg)
    elif sym_name == "cos":
        return math.cos(arg)
    elif sym_name == "atan":
        return math.atan(arg)
    elif sym_name == "ln":
        return math.log(arg)
    elif sym_name == "exp":
        return math.exp(arg)
    elif sym_name == "sqrt":
        return math.sqrt(arg)

def init_table():
    for f in ["sin", "cos", "atan", "ln", "exp", "sqrt"]:
        putsym(f, "FNCT")

def init_const_table():
    putsym("PI", "VAR", 3.14159265358979)
    putsym("E",  "VAR", 2.71828182845905)

init_table()
init_const_table()

while 1:
    try:
        s = raw_input('input > ')
    except EOFError:
        break
    if not s: continue
    yacc.parse(s)

で、

yacc: 5 shift/reduce conflicts
input > E
	2.718282
input > PI
	3.141593
input > ln(E)
	1.000000
input > pi = 3.141592653589
	3.141593
input > sin(pi)
	0.000000
input > alpha = beta1 = 2.3
	2.300000
input > alpha
	2.300000
input > ln(alpha)
	0.832909
input > exp(ln(beta1))
	2.300000
input > 4 + 4.5 - (34/(8*3+-3))
	6.880952
input > -56 + 2
	-54.000000
input > 3 ^ 2
	9.000000