Skip to content

Instantly share code, notes, and snippets.

@pamelafox
Created April 22, 2022 05:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pamelafox/caffb0526227d584ba74c50c4f152712 to your computer and use it in GitHub Desktop.
Save pamelafox/caffb0526227d584ba74c50c4f152712 to your computer and use it in GitHub Desktop.
BNF-based Calculator interpreter, 3 ways
# A Simple Calculator Using Scheme Syntax.
# Implemented using the Lark package in Python.
import sys
from lark import Lark
grammar = """
?start: calc_expr
?calc_expr : NUMBER | calc_op
calc_op: "(" OPERATOR calc_expr* ")"
OPERATOR: "+" | "-" | "*" | "/"
%ignore /\s+/
%import common.NUMBER
"""
parser = Lark(grammar)
def read_print():
while True:
try:
line = input("calc> ")
tree = parser.parse(line)
print(tree)
print(tree.pretty())
except EOFError:
sys.exit(0)
except:
print(f"Bad input: {line}", file=sys.stderr)
if __name__ == "__main__":
read_print()
# A Simple Calculator Using Scheme Syntax.
# Implemented using the Lark package in Python.
import sys
from functools import reduce
from operator import add, sub, mul, truediv
from lark import Lark, Tree, Token
grammar = """
?start: calc_expr
?calc_expr : NUMBER | calc_op
calc_op: "(" OPERATOR calc_expr* ")"
OPERATOR: "+" | "-" | "*" | "/"
%ignore /\s+/
%import common.NUMBER
"""
parser = Lark(grammar)
def calc_eval(exp):
"""Evaluate a Calculator expression.
>>> t1 = Tree('calc_op', [Token('OPERATOR', '+'), Token('NUMBER', '2'), Tree('calc_op', [Token('OPERATOR', '*'), Token('NUMBER', '4'), Token('NUMBER', '6')])])
>>> calc_eval(t1)
26
>>> t2 = Tree('calc_op', [Token('OPERATOR', '+'), Token('NUMBER', '2'), Tree('calc_op', [Token('OPERATOR', '/'), Token('NUMBER', '40'), Token('NUMBER', '5')])])
>>> calc_eval(t2)
10.0
"""
if isinstance(exp, Token) and exp.type == 'NUMBER':
return numberify(exp.value)
elif isinstance(exp, Tree):
operator = exp.children[0].value
arguments = [calc_eval(child) for child in exp.children[1:]]
return calc_apply(operator, arguments)
else:
raise TypeError(str(exp) + ' is not a number or call expression')
def calc_apply(operator, args):
"""Apply the named operator to a list of args.
>>> calc_apply('+', [2, 4])
6
>>> calc_apply('-', [10, 1, 2, 3])
4
>>> calc_apply('-', [10])
-10
>>> calc_apply('*', [])
1
>>> calc_apply('*', [1, 2, 3, 4, 5])
120
>>> calc_apply('/', [40, 5])
8.0
>>> calc_apply('/', [10])
0.1
"""
if not isinstance(operator, str):
raise TypeError(str(operator) + ' is not a symbol')
if operator == '+':
return reduce(add, args, 0)
elif operator == '-':
if len(args) == 0:
raise TypeError(operator + ' requires at least 1 argument')
elif len(args) == 1:
return -args[0]
else:
return reduce(sub, args)
elif operator == '*':
return reduce(mul, args, 1)
elif operator == '/':
if len(args) == 1:
return 1/args[0]
else:
return reduce(truediv, args)
else:
raise TypeError(operator + ' is an unknown operator')
def numberify(s):
try:
return int(s)
except ValueError:
return float(s)
def read_eval_print():
while True:
try:
line = input("calc> ")
tree = parser.parse(line)
result = calc_eval(tree)
print(result)
except EOFError:
sys.exit(0)
except:
print(f"Bad input: {line}", file=sys.stderr)
if __name__ == "__main__":
read_eval_print()
# A Simple Calculator Using Scheme Syntax.
# Implemented using the Lark package in Python.
import sys
from functools import reduce
from operator import add, sub, mul, truediv
from lark import Lark, Tree, Token, Transformer
grammar = """
?start: calc_expr
?calc_expr : NUMBER | calc_op
calc_op: "(" OPERATOR calc_expr* ")"
OPERATOR: "+" | "-" | "*" | "/"
%ignore /\s+/
%import common.NUMBER
"""
class Eval(Transformer):
def start(self, args):
return args[0]
def calc_op(self, args):
op = args[0]
operands = args[1:]
if op == '+':
return sum(operands)
elif op == '-':
if len(operands) == 1:
return -operands[0]
else:
return operands[0] - sum(operands[1:])
elif op == '*':
return reduce(mul, operands)
elif op == '/':
return reduce(truediv, operands)
def NUMBER(self, num):
return numberify(num)
def numberify(s):
try:
return int(s)
except ValueError:
return float(s)
def read_eval_print():
parser = Lark(grammar)
evaluator = Eval()
while True:
try:
line = input("calc> ")
tree = parser.parse(line)
result = evaluator.transform(tree)
print(result)
except EOFError:
sys.exit(0)
except:
print(f"Bad input: {line}", file=sys.stderr)
if __name__ == "__main__":
read_eval_print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment