def test_other_instance_field_access(): ast = project_10.parse_class(""" class BoxedInt { field int value; method boolean compare(BoxedInt other) { return value < other.value; } } """) asm = AssemblySource() project_11.compile_class(ast, asm) assert asm.lines == [ " function BoxedInt.compare 1", " push argument 0", " pop pointer 0", " push this 0", " push argument 1", " pop pointer 1", " push that 0", " lt", " return", "", ]
def test_program_seven_opcodes(): """This program is so simple that there's probably only one reasonable way to compile it, so just compare the VM opcodes.""" with open("examples/project_11/Seven/Main.jack") as f: src = f.read() ast = project_10.parse_class(src) asm = AssemblySource() project_11.compile_class(ast, asm) expected = """ function Main.main 1 push constant 1 push constant 2 push constant 3 call Math.multiply 2 add call Output.printInt 1 pop temp 0 push constant 0 return """ assert list(asm.lines) == expected.split("\n")[1:-1]
def test_missing_return(): """Another very common error.""" ast = project_10.parse_class(""" class Foo { function void noReturn() { // oops, forgot to `return;` here } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ('Missing "return" in Foo.noReturn', )
def test_no_this(): """This could be confusing, so make it an error.""" ast = project_10.parse_class(""" class Foo { function void run() { return this; } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ( 'Undefined "this" in static context: function Foo.run', )
def test_constructor_void_return(): """Constructor must return "this", or the caller will be surprised.""" ast = project_10.parse_class(""" class Foo { constructor Foo new() { return; // meaning "null" } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ( 'Does not return "this": constructor Foo.new', )
def test_constructor_bad_result_type(): """This could be confusing, so make it an error.""" ast = project_10.parse_class(""" class Foo { constructor Bar new() { return this; } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ( 'Result type does not match: constructor Foo.new', )
def test_constructor_wrong_name(): """This could be confusing, so make it an error.""" ast = project_10.parse_class(""" class Foo { constructor Foo notNew() { return this; } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ( 'Must be named "new": constructor Foo.notNew', )
def test_call_method_from_function_context(): """A common error case; referring to a function using the method-call syntax.""" ast = project_10.parse_class(""" class Foo { function void run() { do go(); // Probably meant `Foo.go()` return; } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ( 'Tried to use implicit "this" in static (function) context: Foo.run', )
def test_return_on_both_branches(): """Accept this common pattern.""" ast = project_10.parse_class(""" class Foo { function void toInt(boolean x) { if (x) { return 1; } else { return 0; } } } """) asm = AssemblySource() project_11.compile_class(ast, asm)
def test_field_in_function_context(): """A common error case; referring to an instance member within a function.""" ast = project_10.parse_class(""" class Foo { field int x; function int run() { return x; } } """) with pytest.raises(Exception) as exc_info: asm = AssemblySource() project_11.compile_class(ast, asm) assert exc_info.value.args == ( 'Tried to use field "x" in static context: function Foo.run', )
def test_call_function_from_function_context(): ast = project_10.parse_class(""" class Foo { function void run() { do Bar.go(); return; } } """) asm = AssemblySource() project_11.compile_class(ast, asm) assert asm.lines == [ " function Foo.run 1", " call Bar.go 0", " pop temp 0", " push constant 0", " return", "", ]
def test_call_method_from_method_context(): ast = project_10.parse_class(""" class Foo { method void run() { do go(); return; } } """) asm = AssemblySource() project_11.compile_class(ast, asm) assert asm.lines == [ " function Foo.run 1", " push argument 0", " pop pointer 0", " push pointer 0", # implicit `this` for self call " call Foo.go 1", " pop temp 0", " push constant 0", " return", "", ]
def test_parse_array_test(): ast = project_10.parse_class(ARRAY_TEST) print(ast) expected = Class("Main", [], [ SubroutineDec( "function", None, "main", [], SubroutineBody([ VarDec(Type("Array"), ["a"]), VarDec(Type("int"), ["length"]), VarDec(Type("int"), ["i", "sum"]), ], [ LetStatement( "length", None, SubroutineCall(class_name="Keyboard", var_name=None, sub_name="readInt", args=[StringConstant("HOW MANY NUMBERS? ") ])), LetStatement( "a", None, SubroutineCall(class_name="Array", var_name=None, sub_name="new", args=[VarRef("length")])), LetStatement("i", None, IntegerConstant(0)), WhileStatement( BinaryExpression(VarRef("i"), Op("<"), VarRef("length")), [ LetStatement( "a", VarRef("i"), SubroutineCall( class_name="Keyboard", var_name=None, sub_name="readInt", args=[ StringConstant("ENTER THE NEXT NUMBER: ") ])), LetStatement( "i", None, BinaryExpression(VarRef("i"), Op("+"), IntegerConstant(1))), ]), LetStatement("i", None, IntegerConstant(0)), LetStatement("sum", None, IntegerConstant(0)), WhileStatement( BinaryExpression(VarRef("i"), Op("<"), VarRef("length")), [ LetStatement( "sum", None, BinaryExpression(VarRef("sum"), Op("+"), ArrayRef("a", VarRef("i")))), LetStatement( "i", None, BinaryExpression(VarRef("i"), Op("+"), IntegerConstant(1))), ]), DoStatement( SubroutineCall(class_name="Output", var_name=None, sub_name="printString", args=[StringConstant("THE AVERAGE IS: ")])), DoStatement( SubroutineCall(class_name="Output", var_name=None, sub_name="printInt", args=[ BinaryExpression( VarRef("sum"), Op("/"), VarRef("length")) ])), DoStatement(expr=SubroutineCall(class_name="Output", var_name=None, sub_name="println", args=[])), ReturnStatement(expr=None), ]), ), ]) assert ast == expected