def define(env, id, *body): if type(id) == List: # procedure definition head, *args = id env[str(head)] = Procedure(args, body) return List() else: assert len(body) > 0, "Expression required for assignment." assert len( body) < 2, "Cannot assign multiple values to single identifier." env[str(id)] = scheme.eval(body[0], env) return List()
def read_exprs(session, env): completer = WordCompleter((word for word in env.keys())) expr_str = '' prompt = '> ' while True: try: expr_str += session.prompt(prompt, completer=completer) + '\n' tree = parser.parse(expr_str) except ParseError: prompt = ' ' continue except UnexpectedCharacters as e: print(e) return List([]) except KeyboardInterrupt: return List([]) break return transformer.transform(tree)
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 quote(self, items): return List([Symbol('quote')] + items)
def list(self, items): return List(items)
lambda env, *args: _bool(reduce(operator.eq, args[1:], args[0]))), '<': _builtin( lambda env, *args: _bool(reduce(operator.lt, args[1:], args[0]))), '>': _builtin( lambda env, *args: _bool(reduce(operator.gt, args[1:], args[0]))), '<=': _builtin( lambda env, *args: _bool(reduce(operator.le, args[1:], args[0]))), '>=': _builtin( lambda env, *args: _bool(reduce(operator.ge, args[1:], args[0]))), # list operations 'list': _builtin(lambda env, *args: List(args)), 'length': _builtin(lambda env, l: len(l)), 'cons': _builtin(lambda env, a, b: Pair(a, b)), 'car': _builtin(builtins.car), 'cdr': _builtin(builtins.cdr), 'null?': _builtin(lambda env, l: _bool(len(l) == 0)), 'pair?': _builtin(builtins.pair), # system 'exit': BuiltinProcedure(lambda env: sys.exit())
def _set(env, id, val): env[str(id)] = scheme.eval(val, env) return List()
def cdr(env, val): if type(val) == Pair: return val.cdr else: return List(val[1:])
def begin(env, *args): last = List() for expr in args: last = scheme.eval(expr, env) return last