bison マニュアルの多機能電卓

練習問題2

import re

globalvars = {
  'PI' : 3.14159265358979,
  'E'  : 2.71828182845905
}

def lookup(name):
  if not globalvars.has_key(name): print 'Undefined (defaulting to 0):', name
  return globalvars.get(name, 0)

def power(a):
  if len(a) >= 2:
    x = a.pop(0)
    return x ** power(a)
  else:
    return a[0]

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)

%%
parser Test:

    ignore:    "[ \r\t\n]+"

    token END  : "$"
    token NUM  : "\d+(\.\d+)?"
    token FNCT : "(sin|cos|atan|ln|exp|sqrt)"
    token AID  : "([a-zA-Z_][a-zA-Z0-9_]*)\s*="
    token VAR  : "[a-zA-Z_][a-zA-Z0-9_]*"

    rule start : expr END             {{ print "\t%f" % expr }}

    rule expr  : AID expr             {{ id = re.sub("\s*=$", "", AID); globalvars[id] = expr; return expr }}
               | term                 {{ n  = term   }}
                 ( "[+]" term         {{ n += term   }}
                 | "-"   term         {{ n -= term   }}
                 )*                   {{ return n    }}

    rule term  :  term2               {{ n  = term2  }}
                 ( "[*]" term2        {{ n *= term2  }}
                 | "/"   term2        {{ n /= term2  }}
                 )*                   {{ return n    }}

    rule term2 :  factor              {{ a = [factor]       }}
                 ( "\^" factor        {{ a.append(factor)   }}
                 )*                   {{ return power(a)    }}

    rule factor: NUM                  {{ return float(NUM)  }}
               | FNCT "\(" expr "\)"  {{ return func(FNCT, expr) }}
               | VAR                  {{ return lookup(VAR) }}
               | "\(" expr "\)"       {{ return expr        }}
               | "-" factor           {{ return -factor     }}

%%
if __name__=='__main__':
    while 1:
        try: s = raw_input('>>> ')
        except EOFError: break
        if not s.strip(): break
        parse('start', s)
    print 'Bye.'

で、

>>> PI
	3.141593
>>> E
	2.718282
>>> cos(PI/2)
	0.000000