Esempio n. 1
0
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",
        "",
    ]
Esempio n. 2
0
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]
Esempio n. 3
0
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', )
Esempio n. 4
0
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', )
Esempio n. 5
0
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', )
Esempio n. 6
0
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', )
Esempio n. 7
0
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', )
Esempio n. 8
0
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', )
Esempio n. 9
0
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)
Esempio n. 10
0
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', )
Esempio n. 11
0
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",
        "",
    ]
Esempio n. 12
0
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",
        "",
    ]
Esempio n. 13
0
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