Ad

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.

Code
Diff
  • 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 reduce
    • add = 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 ValueError
    • def getArgs(words):
    • args = []
    • arg = []
    • i = 0
    • for word in words[2:]:
    • if word == '(':
    • i += 1
    • arg.append(word)
    • elif word == ')':
    • i -= 1
    • arg.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 += i
    • if 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')