def test_parse_if_statements(): assert (project_10.IfStatementP.parse( project_10.lex( "if (debugEnabled) { do Output.printString(\"got here\"); }")) == IfStatement(VarRef("debugEnabled"), [ DoStatement( SubroutineCall(class_name="Output", var_name=None, sub_name="printString", args=[StringConstant("got here")])) ], None)) assert (project_10.IfStatementP.parse( project_10.lex( "if (true) { } else { do firstThing(); do secondThing(); }")) == IfStatement(KeywordConstant(True), [], [ DoStatement( SubroutineCall(class_name=None, var_name=None, sub_name="firstThing", args=[])), DoStatement( SubroutineCall(class_name=None, var_name=None, sub_name="secondThing", args=[])), ]))
def test_integer_overflow(): try: project_10.lex("12345678") assert False except: pass
def test_parse_return_statements(): assert (project_10.ReturnStatementP.parse( project_10.lex("return;")) == ReturnStatement(None)) assert (project_10.ReturnStatementP.parse( project_10.lex("return 137;")) == ReturnStatement( IntegerConstant(137)))
def test_parse_class_var_decs(): assert (project_10.ClassVarDecP.parse( project_10.lex("static Game instance;")) == ClassVarDec( static=True, type=Type("Game"), names=["instance"])) assert (project_10.ClassVarDecP.parse( project_10.lex("field int count, limit;")) == ClassVarDec( static=False, type=Type("int"), names=["count", "limit"]))
def test_parse_types(): assert project_10.TypeP.parse(project_10.lex("int")) == Type("int") assert project_10.TypeP.parse(project_10.lex("char")) == Type("char") assert project_10.TypeP.parse(project_10.lex("boolean")) == Type("boolean") assert project_10.TypeP.parse(project_10.lex("MyClass")) == Type("MyClass") with pytest.raises(parsing.ParseFailure): project_10.TypeP.parse(project_10.lex("text"))
def test_parse_let_statements(): assert (project_10.LetStatementP.parse( project_10.lex("let x = 1;")) == LetStatement("x", None, IntegerConstant(1))) assert (project_10.LetStatementP.parse( project_10.lex("let a[0] = x + 1;")) == LetStatement( "a", IntegerConstant(0), BinaryExpression(VarRef("x"), Op("+"), IntegerConstant(1))))
def test_parse_classes(): assert (project_10.ClassP.parse(project_10.lex("class Foo {}")) == Class( "Foo", [], [])) assert (project_10.ClassP.parse( project_10.lex( "class Counter { field int value; method void reset() { let value = 0; } }" )) == Class("Counter", [ClassVarDec(False, Type("int"), ["value"])], [ SubroutineDec( "method", None, "reset", [], SubroutineBody( [], [LetStatement("value", None, IntegerConstant(0))])) ]))
def test_parse_do_statements(): assert (project_10.DoStatementP.parse( project_10.lex("do Output.printInt(42);")) == DoStatement( SubroutineCall(class_name="Output", var_name=None, sub_name="printInt", args=[IntegerConstant(42)])))
def test_parse_terms(): assert (project_10.ExpressionP.parse( project_10.lex("x + 1")) == BinaryExpression(VarRef("x"), Op("+"), IntegerConstant(1))) # Note: all operators associate to the right, which is almost definitely *not* what you want assert (project_10.ExpressionP.parse( project_10.lex("x + y + z")) == BinaryExpression( VarRef("x"), Op("+"), (BinaryExpression(VarRef("y"), Op("+"), VarRef("z"))))) # Parens required here. assert (project_10.ExpressionP.parse( project_10.lex("(x - y) + z")) == BinaryExpression( (BinaryExpression(VarRef("x"), Op("-"), VarRef("y"))), Op("+"), VarRef("z")))
def test_parse_while_statements(): assert (project_10.WhileStatementP.parse( project_10.lex("while (x > 1) { let x = x - 1; }")) == WhileStatement( BinaryExpression(VarRef("x"), Op(">"), IntegerConstant(1)), [ LetStatement( "x", None, BinaryExpression(VarRef("x"), Op("-"), IntegerConstant(1))) ]))
def test_simple_statement(): tokens = project_10.lex("let x = 10;") assert tokens == [ ("keyword", "let"), ("identifier", "x"), ("symbol", "="), ("integerConstant", "10"), ("symbol", ";"), ]
def test_parse_subroutine_decs(): assert (project_10.SubroutineDecP.parse( project_10.lex("function void foo() { }")) == SubroutineDec( "function", None, "foo", [], SubroutineBody([], []))) assert (project_10.SubroutineDecP.parse( project_10.lex( "constructor Thing new(int width, int height) { let area = width*height; return this; }" )) == SubroutineDec( "constructor", Type("Thing"), "new", [ Parameter(Type("int"), "width"), Parameter(Type("int"), "height") ], SubroutineBody([], [ LetStatement( "area", None, BinaryExpression(VarRef("width"), Op("*"), VarRef("height"))), ReturnStatement(KeywordConstant("this")), ])))
def test_trivial_expression(): ast = project_10.ExpressionP.parse(project_10.lex("1 + 2")) symbol_table = project_11.SymbolTable("Main") asm = AssemblySource() project_11.compile_expression(ast, symbol_table, asm) assert asm.lines == [ " push constant 1", " push constant 2", " add", ]
def test_comment_multiline(): tokens = project_10.lex(""" /** A comment that * spans more than * one line. */ class Foo { /* Another multi-line comment. */ } """) assert tokens == [("keyword", "class"), ("identifier", "Foo"), ("symbol", "{"), ("symbol", "}")]
def test_parse_var_decs(): assert project_10.VarDecP.parse(project_10.lex("var int x;")) == VarDec( Type("int"), ["x"]) assert project_10.VarDecP.parse( project_10.lex("var boolean isBlue, isOvine;")) == VarDec( Type("boolean"), ["isBlue", "isOvine"])
def test_identifier(): tokens = project_10.lex("done") assert tokens == [("identifier", "done")]
def test_comment_simple(): tokens = project_10.lex("// A simple comment\n") assert tokens == []
def test_stringConstant_escaped(): """It's confusing, but this string has two escaped double quotes and an escaped backslash.""" tokens = project_10.lex(r'"/ \"Hello\" \\"') assert tokens == [("stringConstant", '/ "Hello" \\')]
def test_white_space(): tokens = project_10.lex(" \n\t\n ") assert tokens == []
def test_stringConstant(): tokens = project_10.lex('"abc def"') assert tokens == [("stringConstant", "abc def")]
def test_symbol(): tokens = project_10.lex("+") assert tokens == [("symbol", "+")]
def test_integerConstant(): tokens = project_10.lex("012345") # Note: the odd formatting surivives the lexer, just because it makes the behavior more # consistent. In this case, it's not a problem for the parser to deal with it. assert tokens == [("integerConstant", "012345")]
def parse_classVarDecs(src): return parsing.ManyP(project_10.ClassVarDecP).parse(project_10.lex(src))
def test_keyword(): tokens = project_10.lex("do") assert tokens == [("keyword", "do")]
def parse_subroutineDec(src): return project_10.SubroutineDecP.parse(project_10.lex(src))
def test_lex_array_test(): tokens = project_10.lex(ARRAY_TEST) assert tokens == [ # 0 ("keyword", "class"), ("identifier", "Main"), ("symbol", "{"), ("keyword", "function"), ("keyword", "void"), ("identifier", "main"), ("symbol", "("), ("symbol", ")"), ("symbol", "{"), ("keyword", "var"), # 10 ("identifier", "Array"), ("identifier", "a"), ("symbol", ";"), ("keyword", "var"), ("keyword", "int"), ("identifier", "length"), ("symbol", ";"), ("keyword", "var"), ("keyword", "int"), ("identifier", "i"), # 20 ("symbol", ","), ("identifier", "sum"), ("symbol", ";"), ("keyword", "let"), ("identifier", "length"), ("symbol", "="), ("identifier", "Keyboard"), ("symbol", "."), ("identifier", "readInt"), ("symbol", "("), # 30 ("stringConstant", "HOW MANY NUMBERS? "), ("symbol", ")"), ("symbol", ";"), ("keyword", "let"), ("identifier", "a"), ("symbol", "="), ("identifier", "Array"), ("symbol", "."), ("identifier", "new"), ("symbol", "("), # 40 ("identifier", "length"), ("symbol", ")"), ("symbol", ";"), ("keyword", "let"), ("identifier", "i"), ("symbol", "="), ("integerConstant", "0"), ("symbol", ";"), ("keyword", "while"), ("symbol", "("), # 50 ("identifier", "i"), ("symbol", "<"), ("identifier", "length"), ("symbol", ")"), ("symbol", "{"), ("keyword", "let"), ("identifier", "a"), ("symbol", "["), ("identifier", "i"), ("symbol", "]"), # 60 ("symbol", "="), ("identifier", "Keyboard"), ("symbol", "."), ("identifier", "readInt"), ("symbol", "("), ("stringConstant", "ENTER THE NEXT NUMBER: "), ("symbol", ")"), ("symbol", ";"), ("keyword", "let"), ("identifier", "i"), # 70 ("symbol", "="), ("identifier", "i"), ("symbol", "+"), ("integerConstant", "1"), ("symbol", ";"), ("symbol", "}"), ("keyword", "let"), ("identifier", "i"), ("symbol", "="), ("integerConstant", "0"), # 80 ("symbol", ";"), ("keyword", "let"), ("identifier", "sum"), ("symbol", "="), ("integerConstant", "0"), ("symbol", ";"), ("keyword", "while"), ("symbol", "("), ("identifier", "i"), ("symbol", "<"), # 90 ("identifier", "length"), ("symbol", ")"), ("symbol", "{"), ("keyword", "let"), ("identifier", "sum"), ("symbol", "="), ("identifier", "sum"), ("symbol", "+"), ("identifier", "a"), ("symbol", "["), # 100 ("identifier", "i"), ("symbol", "]"), ("symbol", ";"), ("keyword", "let"), ("identifier", "i"), ("symbol", "="), ("identifier", "i"), ("symbol", "+"), ("integerConstant", "1"), ("symbol", ";"), # 110 ("symbol", "}"), ("keyword", "do"), ("identifier", "Output"), ("symbol", "."), ("identifier", "printString"), ("symbol", "("), ("stringConstant", "THE AVERAGE IS: "), ("symbol", ")"), ("symbol", ";"), ("keyword", "do"), # 120 ("identifier", "Output"), ("symbol", "."), ("identifier", "printInt"), ("symbol", "("), ("identifier", "sum"), ("symbol", "/"), ("identifier", "length"), ("symbol", ")"), ("symbol", ";"), ("keyword", "do"), # 130 ("identifier", "Output"), ("symbol", "."), ("identifier", "println"), ("symbol", "("), ("symbol", ")"), ("symbol", ";"), ("keyword", "return"), ("symbol", ";"), ("symbol", "}"), ("symbol", "}"), ]