Example #1
0
 def run(self, env: EvalEnv) -> None:
     lhs = env[self.lhs]
     rhs = env[self.rhs]
     if self.op == Binop.SYM_EQ:
         assert isinstance(lhs, SSym) and isinstance(rhs, SSym)
         env[self.dest] = SBool(lhs == rhs)
     elif self.op == Binop.PTR_EQ:
         env[self.dest] = SBool(lhs.address() == rhs.address())
     else:
         assert isinstance(lhs, SNum) and isinstance(rhs, SNum)
         if self.op == Binop.ADD:
             env[self.dest] = SNum(lhs.value + rhs.value)
         elif self.op == Binop.SUB:
             env[self.dest] = SNum(lhs.value - rhs.value)
         elif self.op == Binop.MUL:
             env[self.dest] = SNum(lhs.value * rhs.value)
         elif self.op == Binop.DIV:
             assert rhs.value != 0
             env[self.dest] = SNum(lhs.value // rhs.value)
         elif self.op == Binop.MOD:
             assert rhs.value != 0
             env[self.dest] = SNum(lhs.value % rhs.value)
         elif self.op == Binop.NUM_EQ:
             env[self.dest] = SBool(lhs == rhs)
         elif self.op == Binop.NUM_LT:
             env[self.dest] = SBool(lhs < rhs)
         else:
             raise ValueError(f"Unexpected op {self.op}")
Example #2
0
    def run_abstract(self, env: EvalEnv, types: TypeMap,
                     values: ValueMap) -> None:
        # Type-based transfer function
        if self.op in (Binop.ADD, Binop.SUB, Binop.MUL, Binop.DIV, Binop.MOD):
            types[self.dest] = scheme_types.SchemeNum
        elif self.op in (Binop.SYM_EQ, Binop.PTR_EQ, Binop.NUM_EQ,
                         Binop.NUM_LT):
            types[self.dest] = scheme_types.SchemeBool
        else:
            raise ValueError(f"Unexpected op {self.op}")

        lhs, rhs = values[self.lhs], values[self.rhs]
        values[self.dest] = None
        if lhs is None or rhs is None:
            # Cannot do any constant folding
            return

        if self.op == Binop.SYM_EQ:
            if not (isinstance(lhs, SSym) and isinstance(rhs, SSym)):
                print("Unexpected args to SYM_EQ {self} ({lhs}, {rhs})")
                return

            types[self.dest] = scheme_types.SchemeBool
            values[self.dest] = SBool(lhs == rhs)
        elif self.op == Binop.PTR_EQ:
            types[self.dest] = scheme_types.SchemeBool
            values[self.dest] = SBool(lhs.address() == rhs.address())
        else:
            if not (isinstance(lhs, SNum) and isinstance(rhs, SNum)):
                print("Unexpected args to arith {self} ({lhs}, {rhs})")
                return

            res: Value
            if self.op == Binop.ADD:
                res = SNum(lhs.value + rhs.value)
            elif self.op == Binop.SUB:
                res = SNum(lhs.value - rhs.value)
            elif self.op == Binop.MUL:
                res = SNum(lhs.value * rhs.value)
            elif self.op == Binop.DIV:
                if rhs.value == 0:
                    print("Unexpected div by zero {self} ({rhs})")
                    return
                res = SNum(lhs.value // rhs.value)
            elif self.op == Binop.MOD:
                if rhs.value == 0:
                    print("Unexpected mod by zero {self} ({rhs})")
                    return
                res = SNum(lhs.value % rhs.value)
            elif self.op == Binop.NUM_EQ:
                res = SBool(lhs == rhs)
            elif self.op == Binop.NUM_LT:
                res = SBool(lhs < rhs)
            else:
                raise ValueError(f"Unexpected op {self.op}")
            values[self.dest] = res
            types[self.dest] = scheme_types.get_type(res)
Example #3
0
    def test_prelude(self) -> None:
        env = bytecode.EvalEnv()
        runner.add_intrinsics(env)
        runner.add_builtins(env)
        runner.add_prelude(env)

        self.assertEqual(
            run(env, "(= (cons 42 (cons 13 (cons 'a []))) '(42 13 a))"),
            SBool(True))
        self.assertEqual(
            run(env, "(= [1 2 [3 4 5] 6 [[7]]] [1 2 [3 4 5] 6 [[7]]])"),
            SBool(True))
        self.assertEqual(
            run(env, "(= [1 2 [3 4 5] 6 [[7]]] [1 2 [3 4 5] 6 [[7]]])"),
            SBool(True))
Example #4
0
 def remove_dead_code(self) -> None:
     for block in self.func.blocks():
         for i in reversed(range(len(block.instructions))):
             inst = block.instructions[i]
             if isinstance(inst, (BrInst, BrnInst)):
                 will_jump = isinstance(inst, BrInst)
                 if inst.cond == BoolLit(SBool(will_jump)):
                     block.instructions[i] = JmpInst(inst.target)
                 elif inst.cond == BoolLit(SBool(not will_jump)):
                     block.instructions.pop(i)
             elif isinstance(inst, (JmpInst, TrapInst)):
                 block.instructions = block.instructions[:i + 1]
             elif inst.pure():
                 if not any(self.is_used(x, block) for x in inst.dests()):
                     # Its result is read nowhere, it can be deleted.
                     block.instructions.pop(i)
Example #5
0
    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))
Example #6
0
    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)")
Example #7
0
    def form_sexp(self):
        """Form an S-expression from lexical tokens"""
        
        if len(self._tokens) == 0:
            raise ParserError("expected an (' but end of string")

        tok = self._tokens.pop(0)
        if tok.value == '(':  # S-expression
            L = SExp(tok, self.new_id())
            while self._tokens[0].value != ')':
                L.append(self.form_sexp())
            self._tokens.pop(0) # delete ')'
            return L
        elif tok.type == 'ID':
            return SSymbol(tok, self.new_id())
        elif tok.type == 'INT':
            return SInt(tok, self.new_id())
        elif tok.type == 'BOOL':
            return SBool(tok, self.new_id())
        else:
            raise ParserError("Unrecognized token '%s' at line %d, col %d" % 
                (tok.raw, tok.lineno, tok.colno))
Example #8
0
    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))
Example #9
0
    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)]))
Example #10
0
    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))
Example #11
0
 def test_conditional(self) -> None:
     prog = '(if true 42 43) (if false 44 45)'
     self.assertEqual([
         SConditional(SBool(True), SNum(42), SNum(43)),
         SConditional(SBool(False), SNum(44), SNum(45)),
     ], sexp.parse(prog))