Shorter version that lets Python's parsing do more of the work (with some help from re).
Handles some of the edge cases of lisp such as (+) and (- 1); see https://www.gnu.org/software/emacs/manual/html_node/elisp/Arithmetic-Operations.html.
import re from functools import reduce def op_add(base=0, *args): return base + sum(args) def op_mul(base=1, *args): return base * reduce(lambda a, b: a * b, args, 1) def op_sub(base=0, *args): return - base if not args else reduce(lambda a, b: a - b, args, base) def op_div(base, divisor, *args): return reduce(lambda a, b: a / b, args, base / divisor) ops = {'+': op_add, '-':op_sub, '*': op_mul, '/': op_div} def arith(lisp_list): # if this is a list treat teh frist elemnt as a function name and # recurse the expression tree by passing all the remaining args to this function # and then passing all the results to the function if not isinstance(lisp_list, tuple): return lisp_list return ops[lisp_list[0]](*[arith(x) for x in lisp_list[1:]]) def run(exp): if not exp: raise ValueError('invalid lisp expression') try: # do some substitutions to make the lisp lists into valid python lists and eval the results #if anything goes wring then then the string wasn't properly formatted lisp math exp = re.sub(r'\(([+/*\-])(\s|\))', r'("\1"\2', exp) lisp_list = re.sub(r'\s*(\s[0-9]*([.][0-9]+)?|(\s[(])|([)]))', r',\1', exp) return arith(eval(lisp_list)) except: raise ValueError('invalid lisp expression')
def run(code):def func(operator):from functools import reduceadd = lambda a, b: float(a) + float(b)mul = lambda a, b: float(a) * float(b)div = lambda a, b: float(a) / float(b)deduct = lambda a, b: float(a) - float(b)d = {'+': lambda arr: reduce(add, arr),'*': lambda arr: reduce(mul, arr),'/': lambda arr: reduce(div, arr),'-': lambda arr: reduce(deduct, arr)}return d[operator]def lex(token):if token in ('+', '-', '/', '*', '%'):return "operator"elif token == '(':return "lbracket"elif token == ')':return "rbracket"elif token[0].isalpha():return "name"elif token[0] == token[-1] and token[0] in ('"', "'"):return "string"else:try:float(token)return "number"except:raise ValueErrordef getArgs(words):args = []arg = []i = 0for word in words[2:]:if word == '(':i += 1arg.append(word)elif word == ')':i -= 1arg.append(word)if i == 0:args.append(arg)arg = []elif i == 0:arg.append(word)args.append(arg)arg = []else:arg.append(word)return args- import re
- from functools import reduce
- def op_add(base=0, *args):
- return base + sum(args)
- def op_mul(base=1, *args):
- return base * reduce(lambda a, b: a * b, args, 1)
- def op_sub(base=0, *args):
- return - base if not args else reduce(lambda a, b: a - b, args, base)
- def op_div(base, divisor, *args):
- return reduce(lambda a, b: a / b, args, base / divisor)
- ops = {'+': op_add, '-':op_sub, '*': op_mul, '/': op_div}
- def arith(lisp_list):
- # if this is a list treat teh frist elemnt as a function name and
- # recurse the expression tree by passing all the remaining args to this function
- # and then passing all the results to the function
- if not isinstance(lisp_list, tuple):
- return lisp_list
- return ops[lisp_list[0]](*[arith(x) for x in lisp_list[1:]])
def expr(words):args = getArgs(words)args_ = []for arg in args:if len(arg) == 1:args_.append(arg)else:args_.append(expr(arg))if lex(words[1]) == "operator":return func(words[1])(list(map(lambda a: (type(a) in (list, tuple) and a[0]) or a, args_)))lines = code.split("\n")for line in lines:word = ''words = []chars = tuple(line)for i in tuple(line):if i in ('(', ')'):if word: words.append((word, lex(word)))words.append((i, lex(i)))word = ''elif i == ' ':if word: words.append((word, lex(word)))word = ''else:word += iif word: words.append((word, lex(word)))words_ = list(map(lambda arr: arr[0], words))return(expr(words_))- def run(exp):
- if not exp:
- raise ValueError('invalid lisp expression')
- try:
- # do some substitutions to make the lisp lists into valid python lists and eval the results
- #if anything goes wring then then the string wasn't properly formatted lisp math
- exp = re.sub(r'\(([+/*\-])(\s|\))', r'("\1"\2', exp)
- lisp_list = re.sub(r'\s*(\s[0-9]*([.][0-9]+)?|(\s[(])|([)]))', r',\1', exp)
- return arith(eval(lisp_list))
- except:
- raise ValueError('invalid lisp expression')