def code(self, codegen): lhs = self.lhs rhs = self.rhs left = codegen.visit(lhs) right = codegen.visit(rhs) if left.type == Integer.as_llvm() and right.type == Integer.as_llvm(): return int_ops(codegen.builder, left, right, self) return float_ops(codegen.builder, left, right, self)
def test_of_different_types_cant_be_compared(self): v1 = Integer(1) add = Add(Integer(81), Integer(14)) expected_message = f'You can\'t compare a Value and {add.__class__.__name__}.' \ f'\nTokens being compared:\n{v1.dump()}\n{add}' # noinspection PyUnresolvedReferences v1.__eq__.when.called_with(add).should.throw(LogicError, expected_message)
def test_of_different_types_cant_be_compared(self): add = Add(Integer(18), Integer(120)) v1 = Integer(1) expected_message = f'You can\'t compare a BinaryOp and {v1.__class__.__name__}.' \ f'\nTokens being compared:\n{add.dump()}\n{v1.dump}' # noinspection PyUnresolvedReferences add.__eq__.when.called_with(v1).should.throw(LogicError, expected_message)
def cast(self, from_, to): if from_.type == Integer.as_llvm() and to is Bool: result = self.alloc_and_store(from_, Integer.as_llvm()) result = self.load(result) return self.builder.icmp_signed('!=', result, self.const(0)) if from_.type == Float.as_llvm() and to is Bool: result = self.alloc_and_store(from_, Float.as_llvm()) result = self.load(result) return self.builder.fcmp_ordered('!=', result, self.const(0.0)) raise NotImplementedError('Unsupported cast')
def test_can_be_created_from_a_list_of_statements(self): s1 = Add(Integer(8), Integer(10)) s2 = Mul(Integer(18), Integer(1)) body = [ s1, s2, ] b1 = Block(body) b1.statements.should.contain(s1) b1.statements.should.contain(s2)
def test_respects_precedences(self): parse("1 + 2 * 3").should.contain( Add(Integer(1), Mul(Integer(2), Integer(3)))) parse("2 / 3 - 1").should.contain( Sub(Div(Integer(2), Integer(3)), Integer(1))) parse("5 * 2 - 3").should.contain( Sub(Mul(Integer(5), Integer(2)), Integer(3)))
def code(self, codegen): init_block = codegen.add_block('for.init') cond_block = codegen.add_block('for.cond') codegen.loop_cond_blocks.append(cond_block) body_block = codegen.add_block('for.body') end_block = codegen.add_block('for.end') codegen.loop_end_blocks.append(end_block) codegen.branch(init_block) codegen.position_at_end(init_block) vector = codegen.visit(self.iterable) size = codegen.call('vector_size', [vector]) size = codegen.alloc_and_store(size, Integer.as_llvm(), name='size') index = codegen.alloc_and_store(codegen.const(0), Integer.as_llvm(), 'index') codegen.branch(cond_block) codegen.position_at_end(cond_block) should_go_on = codegen.builder.icmp_signed('<', codegen.load(index), codegen.load(size)) codegen.cbranch(should_go_on, body_block, end_block) codegen.position_at_end(body_block) pos = codegen.load(index) val = codegen.vector_get(vector, pos) codegen.assign(self.var.val, val, Integer.as_llvm()) codegen.visit(self.body) if not codegen.is_break: codegen.builder.store(codegen.builder.add(codegen.const(1), pos), index) codegen.branch(cond_block) else: codegen.is_break = False codegen.position_at_end(end_block) codegen.loop_end_blocks.pop() codegen.loop_cond_blocks.pop()
def const(self, val): # has to come first because freaking `isinstance(True, int) == True` if isinstance(val, bool): return ir.Constant(Bool.as_llvm(), val and 1 or 0) if isinstance(val, int): return ir.Constant(Integer.as_llvm(), val) if isinstance(val, float): return ir.Constant(Float.as_llvm(), val) raise NotImplementedError
def _add_builtins(self): malloc_ty = ir.FunctionType(Int8.as_llvm().as_pointer(), [Integer.as_llvm()]) ir.Function(self.module, malloc_ty, 'malloc') free_ty = ir.FunctionType(Any.as_llvm(), [Int8.as_llvm().as_pointer()]) ir.Function(self.module, free_ty, 'free') puts_ty = ir.FunctionType(Integer.as_llvm(), [Int8.as_llvm().as_pointer()]) ir.Function(self.module, puts_ty, 'puts') int_to_string_ty = ir.FunctionType(Int8.as_llvm().as_pointer(), [ Integer.as_llvm(), Int8.as_llvm().as_pointer(), Integer.as_llvm() ]) ir.Function(self.module, int_to_string_ty, 'int_to_string') printf_ty = ir.FunctionType(Integer.as_llvm(), [Int8.as_llvm().as_pointer()], var_arg=True) ir.Function(self.module, printf_ty, 'printf') vector_init_ty = ir.FunctionType(Any.as_llvm(), [List.as_llvm().as_pointer()]) ir.Function(self.module, vector_init_ty, 'vector_init') vector_append_ty = ir.FunctionType( Any.as_llvm(), [List.as_llvm().as_pointer(), Int8.as_llvm().as_pointer()]) ir.Function(self.module, vector_append_ty, 'vector_append') vector_get_ty = ir.FunctionType( Int8.as_llvm().as_pointer(), [List.as_llvm().as_pointer(), Integer.as_llvm()]) ir.Function(self.module, vector_get_ty, 'vector_get') vector_size_ty = ir.FunctionType(Integer.as_llvm(), [List.as_llvm().as_pointer()]) ir.Function(self.module, vector_size_ty, 'vector_size')
def test_distinguish_by_type(self): Integer(1).should_not.be.equal(Float(1.0)) Float(1.0).should_not.be.equal(Integer(1))
def int(self, const): return Integer(const.value)
def test_expr(self): parse(f'print(2 / 3 - 1)').should.contain( Print(Sub(Div(Integer(2), Integer(3)), Integer(1))))
def vector_get(self, vector, index): val = self.call('vector_get', [vector, index]) val = self.builder.ptrtoint(val, Integer.as_llvm()) return val
def test_division_floors_down(self): parse("15 / 2").should.contain(Div(Integer(15), Integer(2)))
def test_multiplication(self): parse("7 * 2").should.contain(Mul(Integer(7), Integer(2)))
def test_addition(self): parse("1 + 1").should.contain(Add(Integer(1), Integer(1)))
def test_can_be_created_from_single_statement(self): body = Add(Integer(8), Integer(10)) b1 = Block(body) b1.statements.should.contain(body)
def test_works_for_programs(self): p1 = Program(Block(Add(Integer(8), Integer(10)))) p2 = Program(Block(Add(Integer(8), Integer(10)))) # noinspection PyUnresolvedReferences p1.should.be.equal(p2)
def test_supports_multiple_lines(self): parse("1 - 1\n2 * 3\n", only_statements=False).block.statements.should.be.equal([ Sub(Integer(1), Integer(1)), Mul(Integer(2), Integer(3)), ])
def test_cast_to_int_when_initialized_with_strings(self): v1 = Integer('1') v1.val.should.be.a(int)
def test_subtraction(self): parse("6 - 2").should.contain(Sub(Integer(6), Integer(2)))
def test_has_a_block(self): prog = parse("1 + 1") prog.block.should.equal(Block(Add(Integer(1), Integer(1))))
def test_division(self): parse("14 / 2").should.contain(Div(Integer(14), Integer(2)))
def test_has_a_llvm_representation(self): Integer.as_llvm().should.be.equal(ir.IntType(32))
def test_supports_negative_numbers(self): parse("1 + -1").should.contain(Sub(Integer(1), Integer(-1)))
def test_can_be_compared(self): Integer(1).should.be.equal(Integer(1)) Integer(1).should_not.be.equal(Integer(2))
def test_int(self): integer = 42 parse(f'print({integer})').should.contain(Print(Integer(integer)))
def test_has_a_block_with_expression(self): prog = parse("1 + 1") prog.block.statements.should.contain(Add(Integer(1), Integer(1)))
def test_expr_with_parenthesis(self): parse(f'print(2 / (3 - 1))').should.contain( Print(Div(Integer(2), Sub(Integer(3), Integer(1)))))
def test_can_be_compared(self): Print(Integer(10)).should.be.equal(Print(Integer(10))) Print(Integer(10)).should_not.be.equal(Print(Integer(11)))