def test_intrinsics(self) -> None: env = bytecode.EvalEnv() runner.add_intrinsics(env) self.assertEqual(run(env, '(inst/typeof 42)'), SSym('number')) self.assertEqual(run(env, '(inst/typeof [1 2 3])'), SSym('vector')) self.assertEqual(run(env, "(inst/typeof 'hi)"), SSym('symbol')) with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(inst/trap)') self.assertEqual(run(env, '(inst/length (inst/alloc 42))'), SNum(42)) self.assertEqual( run(env, '(inst/load (inst/store (inst/alloc 8) 3 42) 3)'), SNum(42)) self.assertEqual(run(env, '(inst/+ 18 24)'), SNum(18 + 24)) self.assertEqual(run(env, '(inst/- 18 24)'), SNum(18 - 24)) self.assertEqual(run(env, '(inst/* 18 24)'), SNum(18 * 24)) self.assertEqual(run(env, '(inst// 18 24)'), SNum(18 // 24)) self.assertEqual(run(env, '(inst/% 18 24)'), SNum(18 % 24)) self.assertEqual(run(env, '(inst/number= 18 18)'), SBool(True)) self.assertEqual(run(env, '(inst/number= 18 -18)'), SBool(False)) self.assertEqual(run(env, "(inst/symbol= 'hi 'hey)"), SBool(False)) self.assertEqual(run(env, "(inst/symbol= 'hi 'hi)"), SBool(True)) self.assertEqual(run(env, "(inst/pointer= [] 0)"), SBool(False)) # Use a copy of the environment, since (lambda) adds a name to # the environment. self.assertEqual( run(bytecode.EvalEnv({}, dict(env._global_env)), '((lambda (x) (inst/pointer= x x)) [1])'), SBool(True)) self.assertEqual(run(env, '(inst/number< -1 0)'), SBool(True))
def test_dataflow_unstable_type(self) -> None: func, blocks = make_branch_func_object() bb0, bb1, bb2, bb3 = blocks opt = FunctionOptimizer(func) opt.dataflow(bytecode.EvalEnv()) after_0 = (TypeMap({Var('v0'): SchemeNum}), ValueMap({Var('v0'): SNum(42)})) self.assertEqual(opt.block_input_maps(bb1), after_0) self.assertEqual(opt.block_input_maps(bb2), after_0) self.assertEqual( opt.block_input_maps(bb3), (TypeMap({Var('v0'): SchemeObject}), ValueMap()), ) self.assertEqual( opt.info, { id(bb0): [(TypeMap(), ValueMap()), after_0, after_0, after_0], id(bb1): [ after_0, (TypeMap({Var('v0'): SchemeNum }), ValueMap({Var('v0'): SNum(69)})), (TypeMap({Var('v0'): SchemeNum }), ValueMap({Var('v0'): SNum(69)})), ], id(bb2): [ after_0, (TypeMap({Var('v0'): SchemeSym }), ValueMap({Var('v0'): SSym('hi')})), (TypeMap({Var('v0'): SchemeSym }), ValueMap({Var('v0'): SSym('hi')})), ], id(bb3): [ (TypeMap({Var('v0'): SchemeObject}), ValueMap()), (TypeMap({Var('v0'): SchemeObject}), ValueMap()), ], })
def test_parse_atoms(self) -> None: self.assertEqual(sexp.parse("hi"), [SSym("hi")]) self.assertEqual(sexp.parse("hi hey hoi"), [ SSym("hi"), SSym("hey"), SSym("hoi"), ]) self.assertEqual(sexp.parse("42 foo"), [SNum(42), SSym("foo")])
def test_quote(self) -> None: self.assertEqual([Quote(SSym('spam'))], sexp.parse("'spam")) self.assertEqual([Quote(Nil)], sexp.parse("'()")) self.assertEqual([Quote(Nil)], sexp.parse("(quote ())")) self.assertEqual([ Quote(sexp.to_slist( [SSym('if'), SBool(True), SNum(2), SNum(3)])) ], sexp.parse("'(if true 2 3)")) self.assertEqual( [Quote(sexp.to_slist([SNum(1), SNum(2), SNum(3)]))], sexp.parse("(quote (1 2 3))")) self.assertEqual(sexp.parse("'(1 2 3)"), sexp.parse("(quote (1 2 3))")) self.assertEqual(str(sexp.parse("(quote (1 2 3))")[0]), "'(1 2 3)")
def test_function_def(self) -> None: prog = '(define (funcy spam egg) (+ spam egg)) (funcy 42 43)' self.assertEqual([ SFunction( SSym('funcy'), [SSym('spam'), SSym('egg')], sexp.to_slist([SCall(SSym('+'), [SSym('spam'), SSym('egg')])])), SCall(SSym('funcy'), [SNum(42), SNum(43)]) ], sexp.parse(prog))
def test_example_tail_call(self) -> None: """ function list? (v0) entry=bb0 bb0: v1 = lookup 'nil? v2 = call v1 (v0) br v2 bb1 v3 = lookup 'pair? v4 = call v3 (v0) brn v4 bb2 v5 = lookup 'cdr v0 = call v5 (v0) jmp bb0 bb1: return 'true bb2: return 'false """ bb0 = bytecode.BasicBlock("bb0") bb1 = bytecode.BasicBlock("bb1") bb2 = bytecode.BasicBlock("bb2") is_list = bytecode.Function([Var("v0")], bb0) bb0.add_inst(bytecode.LookupInst(Var("v1"), SymLit(SSym("nil?")))) bb0.add_inst(bytecode.CallInst(Var("v2"), Var("v1"), [Var("v0")])) bb0.add_inst(bytecode.BrInst(Var("v2"), bb1)) bb0.add_inst(bytecode.LookupInst(Var("v3"), SymLit(SSym("pair?")))) bb0.add_inst(bytecode.CallInst(Var("v4"), Var("v3"), [Var("v0")])) bb0.add_inst(bytecode.BrnInst(Var("v4"), bb2)) bb0.add_inst(bytecode.LookupInst(Var("v5"), SymLit(SSym("global")))) bb0.add_inst(bytecode.CallInst(Var("v0"), Var("v5"), [Var("v0")])) bb0.add_inst(bytecode.JmpInst(bb0)) bb1.add_inst(bytecode.ReturnInst(SymLit(SSym("true")))) bb2.add_inst(bytecode.ReturnInst(SymLit(SSym("false")))) assert is_list
def test_call_specialized(self) -> None: bb0 = bytecode.BasicBlock("bb0") bb0.add_inst(bytecode.ReturnInst(BoolLit(SBool(False)))) byte_func = bytecode.Function([Var("x")], bb0) bb0_specialized = bytecode.BasicBlock("bb0") bb0_specialized.add_inst(bytecode.ReturnInst(BoolLit(SBool(True)))) byte_func_specialized = bytecode.Function([Var("x")], bb0_specialized) func = sexp.SFunction( SSym("func"), [SSym("x")], sexp.to_slist([]), code=byte_func, is_lambda=False, specializations={ (scheme_types.SchemeSym, ): byte_func_specialized }, ) env = bytecode.EvalEnv(local_env={Var('f'): func}) bytecode.CallInst(Var('y'), Var('f'), [NumLit(SNum(42))]).run(env) assert env[Var('y')] == SBool(False) bytecode.CallInst(Var('y'), Var('f'), [SymLit(SSym('x'))]).run(env) assert env[Var('y')] == SBool(True) bytecode.CallInst(Var('y'), Var('f'), [SymLit(SSym('x'))], specialization=(scheme_types.SchemeSym, )).run(env) assert env[Var('y')] == SBool(True) # If specialization not found, fall back to dynamic dispatch bytecode.CallInst(Var('y'), Var('f'), [SymLit(SSym('x'))], specialization=(scheme_types.SchemeNum, )).run(env) self.assertEqual(env[Var('y')], SBool(True)) bytecode.CallInst(Var('y'), Var('f'), [NumLit(SNum(42))], specialization=(scheme_types.SchemeNum, )).run(env) self.assertEqual(env[Var('y')], SBool(False))
def inst_function( name: SSym, params: List[Var], return_val: Optional[bytecode.Parameter], *insts: Inst, ) -> SFunction: """Create a function out of the instructions in insts.""" begin = BasicBlock('bb0') for inst in insts: begin.add_inst(inst) if return_val is not None: begin.add_inst(bytecode.ReturnInst(return_val)) code = Function(params, begin) param_syms = [SSym(p.name) for p in params] return SFunction(name, param_syms, Nil, code, False)
def test_lambda(self) -> None: prog = '(lambda (spam egg) (+ spam egg)) (lambda () 42)' self.assertEqual([ SFunction( SSym('__lambda0'), [SSym('spam'), SSym('egg')], sexp.to_slist([SCall(SSym('+'), [SSym('spam'), SSym('egg')])]), is_lambda=True), SFunction(SSym('__lambda1'), [], sexp.to_slist([SNum(42)]), is_lambda=True), ], sexp.parse(prog))
def test_comments(self) -> None: prog = """ ;;; We want to define a cool function here! (define ; hi ;; A function name (cool-func x y) ; wow ;; branches are cheaper than subtraction, right? :P (if (= x y) 0 (- x y))) """ self.assertEqual([ SFunction( SSym('cool-func'), [SSym('x'), SSym('y')], sexp.to_slist([ SConditional( SCall(SSym('='), [SSym('x'), SSym('y')]), SNum(0), SCall(SSym('-'), [SSym('x'), SSym('y')])) ])) ], sexp.parse(prog))
def make_branch_func_object() -> Tuple[Function, Tuple[BasicBlock, ...]]: bb0 = BasicBlock("bb0") bb1 = BasicBlock("bb1") bb2 = BasicBlock("bb2") bb3 = BasicBlock("bb3") bb0.add_inst(bytecode.CopyInst(Var('v0'), NumLit(SNum(42)))) bb0.add_inst(bytecode.BrInst(Var('x'), bb1)) bb0.add_inst(bytecode.JmpInst(bb2)) bb1.add_inst( bytecode.BinopInst(Var('v0'), Binop.ADD, Var('v0'), NumLit(SNum(27)))) bb1.add_inst(bytecode.JmpInst(bb3)) bb2.add_inst(bytecode.CopyInst(Var('v0'), SymLit(SSym('hi')))) bb2.add_inst(bytecode.JmpInst(bb3)) bb3.add_inst(bytecode.ReturnInst(Var('v0'))) return Function([Var('x')], bb0), (bb0, bb1, bb2, bb3)
def test_lambda_called_inline(self) -> None: self.maxDiff = None prog = '((lambda (spam egg) (+ spam egg)) 42 43)' self.assertEqual([ SCall( SFunction(SSym('__lambda0'), [SSym('spam'), SSym('egg')], sexp.to_slist( [SCall(SSym('+'), [SSym('spam'), SSym('egg')])]), is_lambda=True), [SNum(42), SNum(43)]) ], sexp.parse(prog))
def test_specialize_value(self) -> None: env = get_builtins() func = env._global_env[SSym('+')] assert isinstance(func, sexp.SFunction) assert func.code code = copy.deepcopy(func.code) opt = FunctionOptimizer(code) opt.specialization = (SchemeNum, SchemeNum) opt.inputs = (SNum(42), SNum(27)) opt.optimize(env) self.assertEqual( str(code), ''' function (? a b) entry=bb0 bb0: return 69 '''.strip())
def test_optimize(self) -> None: env = get_builtins() func = env._global_env[SSym('+')] assert isinstance(func, sexp.SFunction) assert func.code code = copy.deepcopy(func.code) self.assertEqual( str(code), ''' function (? a b) entry=bb0 bb0: inl4@inl1@inl0@result = typeof a inl4@inl0@inl0@result = Binop.SYM_EQ inl4@inl1@inl0@result 'number brn inl4@inl0@inl0@result bb0.split5 inl2@inl1@inl0@result = typeof b inl2@inl0@inl0@result = Binop.SYM_EQ inl2@inl1@inl0@result 'number brn inl2@inl0@inl0@result bb0.split6 inl0@result = Binop.ADD a b return inl0@result bb0.split5: trap '(trap)' bb0.split6: trap '(trap)' '''.strip()) opt = FunctionOptimizer(code) opt.specialization = (SchemeNum, SchemeNum) opt.optimize(env) self.assertEqual( str(code), ''' function (? a b) entry=bb0 bb0: inl0@result = Binop.ADD a b return inl0@result '''.strip())
def test_example_recursive(self) -> None: """ function list? (v0) entry=bb0 bb0: v1 = lookup 'nil? v2 = call v1 (v0) br v2 bb1 v3 = lookup 'pair? v4 = call v3 (v0) brn v4 bb2 v5 = lookup 'cdr v6 = call v5 (v0) v7 = lookup 'list? v8 = call v7 (v6) return v8 bb1: return 'true bb2: return 'false """ bb0 = bytecode.BasicBlock("bb0") bb1 = bytecode.BasicBlock("bb1") bb2 = bytecode.BasicBlock("bb2") is_list = bytecode.Function([Var("v0")], bb0) bb0.add_inst(bytecode.LookupInst(Var("v1"), SymLit(SSym("nil?")))) bb0.add_inst(bytecode.CallInst(Var("v2"), Var("v1"), [Var("v1")])) bb0.add_inst(bytecode.BrInst(Var("v2"), bb1)) bb0.add_inst(bytecode.LookupInst(Var("v3"), SymLit(SSym("pair?")))) bb0.add_inst(bytecode.CallInst(Var("v4"), Var("v3"), [Var("v0")])) bb0.add_inst(bytecode.BrnInst(Var("v4"), bb2)) bb0.add_inst(bytecode.LookupInst(Var("v5"), SymLit(SSym("cdr")))) bb0.add_inst(bytecode.CallInst(Var("v6"), Var("v5"), [Var("v0")])) bb0.add_inst(bytecode.LookupInst(Var("v7"), SymLit(SSym("list?")))) bb0.add_inst(bytecode.CallInst(Var("v8"), Var("v7"), [Var("v6")])) bb0.add_inst(bytecode.ReturnInst(Var("v8"))) bb1.add_inst(bytecode.ReturnInst(SymLit(SSym("true")))) bb2.add_inst(bytecode.ReturnInst(SymLit(SSym("false")))) # These tests are just to test the API assert is_list
def run_code(env: EvalEnv, code: SExp, context: str = "top-level") -> Value: """Run a piece of code in an environment, returning its result.""" if isinstance(code, sexp.SFunction): tail_calls = None if env.optimize_tail_calls: tail_call_finder = TailCallFinder() tail_call_finder.visit(code) tail_calls = tail_call_finder.tail_calls type_analyzer = None if env.jit: type_analyzer = scheme_types.FunctionTypeAnalyzer( {}, env._global_env) type_analyzer.visit(code) emitter = FunctionEmitter( env._global_env, tail_calls=tail_calls, expr_types=type_analyzer) emitter.visit(code) _add_func_to_env(code, emitter, env) assert code.code if env.bytecode_jit: if env.print_optimizations: print(f"Optimizing {context} function {code.name}...") opt = FunctionOptimizer(code.code) opt.optimize(env) return env._global_env[code.name] else: name = SSym(f'{next(eval_names)}') code = sexp.SFunction( name, [], sexp.to_slist([code]), is_lambda=True) emitter = FunctionEmitter(env._global_env) emitter.visit(code) function = emitter.get_emitted_func() gen = bytecode.ResultGenerator(function.run(env)) gen.run() assert gen.value is not None return gen.value
def symbol(self) -> Optional[sexp.SSym]: return SSym('vector')
def test_parse_list(self) -> None: self.assertEqual(sexp.parse("()"), [Nil]) self.assertEqual( sexp.parse("(func 2 3)"), [SCall(SSym('func'), [SNum(2), SNum(3)])], )
def add_intrinsics(eval_env: EvalEnv) -> None: """Add intrinsics to the environment.""" def inst_function( name: SSym, params: List[Var], return_val: Optional[bytecode.Parameter], *insts: Inst, ) -> SFunction: """Create a function out of the instructions in insts.""" begin = BasicBlock('bb0') for inst in insts: begin.add_inst(inst) if return_val is not None: begin.add_inst(bytecode.ReturnInst(return_val)) code = Function(params, begin) param_syms = [SSym(p.name) for p in params] return SFunction(name, param_syms, Nil, code, False) def binop(name: SSym, op: Binop) -> SFunction: return inst_function( name, [Var('a'), Var('b')], result, bytecode.BinopInst(result, op, Var('a'), Var('b'))) result = Var('result') env = eval_env._global_env env[SSym('inst/typeof')] = inst_function( SSym('inst/typeof'), [Var('x')], result, bytecode.TypeofInst(result, Var('x'))) env[SSym('inst/trap')] = inst_function( SSym('inst/trap'), [], None, bytecode.TrapInst("(trap)")) env[SSym('inst/trace')] = inst_function( SSym('inst/trace'), [Var('x')], Var('x'), bytecode.TraceInst(Var('x'))) env[SSym('inst/display')] = inst_function( SSym('inst/display'), [Var('x')], bytecode.NumLit(sexp.SNum(0)), bytecode.DisplayInst(Var('x'))) env[SSym('inst/newline')] = inst_function( SSym('inst/newline'), [], bytecode.NumLit(sexp.SNum(0)), bytecode.NewlineInst()) env[SSym('inst/breakpoint')] = inst_function( SSym('inst/breakpoint'), [], bytecode.NumLit(sexp.SNum(0)), bytecode.BreakpointInst()) # Memory operations env[SSym('inst/alloc')] = inst_function( SSym('inst/alloc'), [Var('n')], result, bytecode.AllocInst(result, Var('n'))) env[SSym('inst/load')] = inst_function( SSym('inst/load'), [Var('v'), Var('n')], result, bytecode.LoadInst(result, Var('v'), Var('n'))) env[SSym('inst/store')] = inst_function( SSym('inst/store'), [Var('v'), Var('n'), Var('x')], Var('v'), bytecode.StoreInst(Var('v'), Var('n'), Var('x'))) env[SSym('inst/length')] = inst_function( SSym('inst/length'), [Var('v')], result, bytecode.LengthInst(result, Var('v'))) # Binary operators env[SSym('inst/+')] = binop(SSym('inst/+'), Binop.ADD) env[SSym('inst/-')] = binop(SSym('inst/-'), Binop.SUB) env[SSym('inst/*')] = binop(SSym('inst/*'), Binop.MUL) env[SSym('inst//')] = binop(SSym('inst//'), Binop.DIV) env[SSym('inst/%')] = binop(SSym('inst/%'), Binop.MOD) env[SSym('inst/number=')] = binop(SSym('inst/number='), Binop.NUM_EQ) env[SSym('inst/symbol=')] = binop(SSym('inst/symbol='), Binop.SYM_EQ) env[SSym('inst/pointer=')] = binop(SSym('inst/pointer='), Binop.PTR_EQ) env[SSym('inst/number<')] = binop(SSym('inst/number<'), Binop.NUM_LT)
def test_example_inlined(self) -> None: """ function list? (v0) entry=bb0 bb0: v1 = typeof v0 v2 = sym_eq v1 'vector brn v2 false v3 = length v0 v4 = num_eq v3 0 br v4 true v5 = num_eq v3 2 brn v5 false v0 = load v0 [1] jmp bb0 true: return 'true false: return 'false """ bb0 = bytecode.BasicBlock("bb0") true = bytecode.BasicBlock("true") false = bytecode.BasicBlock("false") is_list = bytecode.Function([Var("v0")], bb0) # These tests are just to test the API bb0.add_inst(bytecode.TypeofInst(Var("v1"), Var("v0"))) bb0.add_inst( bytecode.BinopInst(Var("v2"), Binop.SYM_EQ, Var("v1"), SymLit(SSym("vector")))) bb0.add_inst(bytecode.BrnInst(Var("v2"), false)) bb0.add_inst(bytecode.LengthInst(Var("v3"), Var("v0"))) bb0.add_inst( bytecode.BinopInst(Var("v4"), Binop.NUM_EQ, Var("v3"), NumLit(SNum(0)))) bb0.add_inst(bytecode.BrInst(Var("v4"), true)) bb0.add_inst( bytecode.BinopInst(Var("v5"), Binop.NUM_EQ, Var("v3"), NumLit(SNum(2)))) bb0.add_inst(bytecode.BrnInst(Var("v5"), false)) bb0.add_inst(bytecode.LoadInst(Var("v0"), Var("v0"), NumLit(SNum(1)))) bb0.add_inst(bytecode.JmpInst(bb0)) true.add_inst(bytecode.ReturnInst(BoolLit(SBool(True)))) false.add_inst(bytecode.ReturnInst(BoolLit(SBool(False)))) env = bytecode.EvalEnv(local_env={Var("v0"): SNum(42)}) gen = bytecode.ResultGenerator(is_list.run(env)) gen.run() self.assertEqual(gen.value, SBool(False)) env = bytecode.EvalEnv(local_env={ Var("v0"): SVect([SNum(42), SVect([SNum(69), SVect([])])]) }) gen = bytecode.ResultGenerator(is_list.run(env)) gen.run() self.assertEqual(gen.value, SBool(True))
def symbol(self) -> Optional[sexp.SSym]: return SSym('number')
def symbol(self) -> Optional[sexp.SSym]: return SSym('symbol')
def test_builtins(self) -> None: env = bytecode.EvalEnv() runner.add_intrinsics(env) runner.add_builtins(env) with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(trap)') with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(assert false)') self.assertEqual(run(env, '(typeof 42)'), SSym('number')) self.assertEqual(run(env, '(typeof [1 2 3])'), SSym('vector')) self.assertEqual(run(env, "(typeof 'hi)"), SSym('symbol')) self.assertEqual(run(env, '(not true)'), SBool(False)) self.assertEqual(run(env, '(not false)'), SBool(True)) with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(not 42)') self.assertEqual(run(env, '(number? 42)'), SBool(True)) self.assertEqual(run(env, '(number? [])'), SBool(False)) self.assertEqual(run(env, "(symbol? 'hi)"), SBool(True)) self.assertEqual(run(env, '(symbol? 42)'), SBool(False)) self.assertEqual(run(env, '(vector? [])'), SBool(True)) self.assertEqual(run(env, '(vector? 42)'), SBool(False)) # Use a copy of the environment, since (lambda) adds a name to # the environment. self.assertEqual( run(bytecode.EvalEnv({}, dict(env._global_env)), '(function? (lambda () []))'), SBool(True)) self.assertEqual(run(env, '(function? 42)'), SBool(False)) self.assertEqual(run(env, '(bool? true)'), SBool(True)) self.assertEqual(run(env, '(bool? false)'), SBool(True)) self.assertEqual(run(env, '(bool? 42)'), SBool(False)) self.assertEqual(run(env, '(pair? 42)'), SBool(False)) self.assertEqual(run(env, '(pair? [])'), SBool(False)) self.assertEqual(run(env, '(pair? [1])'), SBool(False)) self.assertEqual(run(env, '(pair? [1 2])'), SBool(True)) self.assertEqual(run(env, '(nil? [1 2])'), SBool(False)) self.assertEqual(run(env, '(nil? [])'), SBool(True)) self.assertEqual(run(env, '(+ 39 13)'), SNum(39 + 13)) self.assertEqual(run(env, '(- 39 13)'), SNum(39 - 13)) self.assertEqual(run(env, '(* 39 13)'), SNum(39 * 13)) self.assertEqual(run(env, '(/ 39 13)'), SNum(39 // 13)) self.assertEqual(run(env, '(% 39 13)'), SNum(39 % 13)) for op in '+-*/%': with self.assertRaises(errors.Trap, msg="(trap)"): run(env, f'({op} 39 [])') with self.assertRaises(errors.Trap, msg="(trap)"): run(env, f'({op} [] 13)') with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(/ 1 0)') with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(% 1 0)') with self.assertRaises(errors.Trap, msg="(trap)"): run(env, '(symbol= 1 0)') self.assertEqual(run(env, "(symbol= 'a 'b)"), SBool(False)) self.assertEqual(run(env, "(symbol= 'a 'a)"), SBool(True)) # Use a copy of the environment, since (lambda) adds a name to # the environment. self.assertEqual( run(bytecode.EvalEnv({}, dict(env._global_env)), "(pointer= (lambda () 0) (lambda () 0))"), SBool(False)) self.assertEqual(run(env, "(define (func) 42) (pointer= func func)"), SBool(True)) self.assertEqual(run(env, "(pointer= 0 0)"), SBool(True)) with self.assertRaises(errors.Trap, msg="(trap)"): run(env, "(number= 'num 1)") self.assertEqual(run(env, "(number= 42 43)"), SBool(False)) self.assertEqual(run(env, "(number= 42 42)"), SBool(True)) with self.assertRaises(errors.Trap, msg="(trap)"): run(env, "(number< 'num 1)") self.assertEqual(run(env, "(number< 42 42)"), SBool(False)) self.assertEqual(run(env, "(number< 42 43)"), SBool(True)) self.assertEqual(run(env, '(vector-length [1 2 3])'), SNum(3)) self.assertEqual(run(env, '(vector-index [1 2 3] 1)'), SNum(2)) self.assertEqual(run(env, '(vector-set! [1 2 3] 1 42)'), SVect([SNum(1), SNum(42), SNum(3)])) self.assertEqual(run(env, '(vector-make 4 9)'), SVect([SNum(9), SNum(9), SNum(9), SNum(9)]))
def symbol(self) -> Optional[sexp.SSym]: return SSym('function')