def quasiquote(ast): if not ispair(ast): return [Symbol('quote'), ast] elif ast[0] == 'unquote': return ast[1] elif ispair(ast[0]) and ast[0][0] == 'splice-unquote': return [Symbol('concat'), ast[0][1], quasiquote(ast[1:])] else: return [Symbol('cons'), quasiquote(ast[0]), quasiquote(ast[1:])]
def pprint_exprs(expr_list): for expr in expr_list: if type(expr) == Symbol: if expr == Symbol('#t') or expr == Symbol('#f'): print(expr) else: print(f'\'{str(expr)}') else: print(str(expr))
def equal(env, a, b): # recursive comparison if type(a) != List or type(b) != List: return Symbol('#t') if a == b else Symbol('#f') else: if len(a) != len(b): return Symbol('#f') for i in range(len(a)): if not equal(env, a[i], b[i]): return Symbol('#f') return Symbol('#t')
def test_parsing(): # symbols assert parse_str(r'foobar') == [Symbol('foobar')] assert parse_str(r' foobar ') == [Symbol('foobar')] # strings assert parse_str(r'"hello world"') == [String("hello world")] assert parse_str(r'"hello\nworld"') == [String("hello\nworld")] # integers assert parse_str(r"123") == [123] assert parse_str(r"-42") == [-42] # floats assert parse_str(r"3.14159265359") == [3.14159265359] assert parse_str(r"-2.7") == [-2.7] assert parse_str(r"2.99e8") == [2.99e8] # quote assert parse_str(r"'foobar") == [List([Symbol('quote'), Symbol('foobar')])] assert parse_str(r"'123") == [List([Symbol('quote'), Integer(123)])] assert parse_str(r"'()") == [List([Symbol('quote'), List([])])] # list assert parse_str(r'(1 2 3)') == [[1, 2, 3]] assert parse_str(r"('a 2 3)") == [ List([[Symbol('quote'), Symbol('a')], 2, 3]) ] assert parse_str("(+\n1\n2)") == [List([Symbol('+'), 1, 2])] assert parse_str("(define (add a b) (+ a b))") == [[ 'define', ['add', 'a', 'b'], ['+', 'a', 'b'] ]]
def eval(expr, env): # self-evaluating expressions if type(expr) in (Integer, Float, String): return expr # symbols elif type(expr) == Symbol: if expr == Symbol('#f') or expr == Symbol('#t'): return expr try: return env[str(expr)] except KeyError: raise EvalError( f'Symbol "{expr}" not found in current environment.') elif type(expr) == List: op, *args = expr # unpack proc = eval(op, env) if type(proc) not in (Procedure, BuiltinProcedure): raise EvalError(f'{op} is not a procedure.') if type(proc) == BuiltinProcedure: return proc.call(env, *args) # try: # return proc(env, *args) # except TypeError as e: # raise EvalError(str(e)) # except AssertionError as e: # raise EvalError(str(e)) else: vals = [eval(arg, env) for arg in args] if len(vals) != len(proc.arguments): raise EvalError( f'Received wrong number of arguments (expected {len(proc.arguments)} and got {len(vals)}).' ) last = List() for expr in proc.body: last = eval( expr, env.new_child( {k: vals[i] for i, k in enumerate(proc.arguments)})) return last else: raise ValueError(f'Invalid argument passed to eval: {expr}')
def parseatom(lexer): token = lexer.next() if match(integer_regex, token): return int(token) if match(string_regex, token): return token elif token == "nil": return None elif token == "true": return True elif token == "false": return False return Symbol(token)
def symbol(self, items): return Symbol(items[0])
def quote(self, items): return List([Symbol('quote')] + items)
def _bool(val): return Symbol('#t') if val else Symbol('#f')
'lambda': BuiltinProcedure(builtins._lambda), # equivalency 'eq?': _builtin(lambda env, a, b: _bool(_is(a, b))), 'eqv?': _builtin(builtins.eqv), 'equal?': _builtin(builtins.equal), # logical operators 'and': _builtin(builtins._and), 'or': _builtin(builtins._or), 'not': _builtin(lambda env, val: _bool(not val or val == Symbol('#f'))), # math '+': _builtin(lambda env, *args: Number( reduce(operator.add, args[1:], args[0]))), '-': _builtin(lambda env, *args: Number( reduce(operator.sub, args[1:], args[0]))), '*': _builtin(lambda env, *args: Number( reduce(operator.mul, args[1:], args[0]))), '/': _builtin(lambda env, *args: Number( reduce(operator.truediv, args[1:], args[0]))), 'pow': _builtin(lambda env, a, b: Number(a**b)),
def _or(env, *args): for arg in args: if arg or arg == Symbol('#t'): return Symbol('#t') return Symbol('#f')
def _and(env, *args): for arg in args: if not arg or arg == Symbol('#f'): return Symbol('#f') return Symbol('#t')
def eqv(env, a, b): if type(a) != type(b): return Symbol('#f') return Symbol('#t') if a == b else Symbol('#f')
def pair(env, val): if type(val) == Pair: return Symbol('#t') else: return Symbol('#t') if len(val) > 0 else Symbol('#f')
def _if(env, predicate, if_expr, else_expr): if scheme.eval(predicate, env) == Symbol('#t'): return scheme.eval(if_expr, env) else: return scheme.eval(else_expr, env)