def test_expr_assignment_with_ids(self): '''Can parse assignment to expression with ids: `duck = (goose + 10) * duck;`''' given = iter([ Token('ID', 'duck'), Token('EQUAL', '='), Token('LPAREN', '('), Token('ID', 'goose'), Token('PLUS', '+'), Token('INTEGER', '10'), Token('RPAREN', ')'), Token('MUL', '*'), Token('ID', 'duck'), Token('SEMI', ';') ]) result = parser.parse(given) expected = ast.Block([ ast.Assignment( ast.ID('duck'), ast.BinOp( '*', ast.BinOp('+', ast.ID('goose'), ast.Integer(10)), ast.ID('duck') ) ) ]) assert result == expected
def test_relational_op_order(self): '''Relational ops have lowest precedence: `3 * 5 + 3 == 18;`''' given = iter([ Token('INTEGER', '3'), Token('MUL', '*'), Token('INTEGER', '5'), Token('PLUS', '+'), Token('INTEGER', '3'), Token('EQUAL_EQUAL', '=='), Token('INTEGER', '18'), Token('SEMI', ';') ]) result = parser.parse(given) expected = ast.Block([ ast.Statement( ast.BinOp( '==', ast.BinOp( '+', ast.BinOp('*', ast.Integer(3), ast.Integer(5)), ast.Integer(3) ), ast.Integer(18) ) ) ]) assert result == expected
def test_parens(self): '''Honors parenthesis: `(1 + 5) * 20;`''' given = iter([ Token('LPAREN', '('), Token('INTEGER', '1'), Token('PLUS', '+'), Token('INTEGER', '5'), Token('RPAREN', ')'), Token('MUL', '*'), Token('INTEGER', '20'), Token('SEMI', ';') ]) result = parser.parse(given) expected = ast.Block([ ast.Statement( ast.BinOp( '*', ast.BinOp('+', ast.Integer(1), ast.Integer(5)), ast.Integer(20) ) ) ]) assert result == expected
def test_unary_op_expression(self): '''Unary operations are supported: `(5 + -1) - !3;`''' given = iter([ Token('LPAREN', '('), Token('INTEGER', '5'), Token('PLUS', '+'), Token('MINUS', '-'), Token('INTEGER', '1'), Token('RPAREN', ')'), Token('MINUS', '-'), Token('BANG', '!'), Token('INTEGER', '3'), Token('SEMI', ';') ]) result = parser.parse(given) expected = ast.Block([ ast.Statement( ast.BinOp( '-', ast.BinOp( '+', ast.Integer(5), ast.UnaryOp('-', ast.Integer(1)) ), ast.UnaryOp('!', ast.Integer(3)) ) ) ]) assert result == expected
def test_mixed_expression(self): '''An expression can have both numbers and ids: `x * (y + 10);`''' given = iter([ Token('ID', 'x'), Token('MUL', '*'), Token('LPAREN', '('), Token('ID', 'y'), Token('PLUS', '+'), Token('INTEGER', '10'), Token('RPAREN', ')'), Token('SEMI', ';') ]) result = parser.parse(given) expected = ast.Block([ ast.Statement( ast.BinOp( '*', ast.ID('x'), ast.BinOp('+', ast.ID('y'), ast.Integer(10)) ) ) ]) assert result == expected
def test_id_expr_error(self): '''No error with given program: `int ducks; float wildcats; ducks = 100; wildcats = 200 % ducks;`''' given = ast.Block([ ast.Declaration('int', [ast.ID('ducks')]), ast.Declaration('float', [ast.ID('wildcats')]), ast.Assignment(ast.ID('ducks'), ast.Integer(100)), ast.Assignment(ast.ID('wildcats'), ast.BinOp('%', ast.Integer(200), ast.ID('ducks'))) ]) report = typechecker.typecheck(given) expected_errors = [ TypecheckerError( ast.Assignment( ast.ID('wildcats'), ast.BinOp('%', ast.Integer(200), ast.ID('ducks')))) ] for (r, e) in zip_longest(report.get_errors(), expected_errors): assert r == e
def test_simple_program_correct(self): '''Can typecheck a simple program: `int wildcat, animals[2]; float duck; wildcat = 1; duck = 1.0; if(duck) { animals[0] = wildcat; animals[1] = 2; } else { animals[0] = 1; } duck = (duck + 2.5) * 33.0 / 2.0; ` ''' given = ast.Block([ ast.Declaration('int', [ ast.ID('wildcat'), ast.ArrayRef(ast.ID('animals'), ast.Integer(2)) ]), ast.Declaration('float', [ast.ID('duck')]), ast.Assignment(ast.ID('wildcat'), ast.Integer(1)), ast.Assignment(ast.ID('duck'), ast.Float(1.0)), ast.IfStatement( ast.ID('duck'), ast.Block([ ast.Assignment( ast.ArrayRef(ast.ID('animals'), ast.Integer(0)), ast.ID('wildcat')), ast.Assignment( ast.ArrayRef(ast.ID('animals'), ast.Integer(1)), ast.Integer(2)) ]), ast.Block([ ast.Assignment( ast.ArrayRef(ast.ID('animals'), ast.Integer(0)), ast.Integer(1)) ])), ast.Assignment( ast.ID('duck'), ast.BinOp( '/', ast.BinOp('*', ast.BinOp('+', ast.ID('duck'), ast.Float(2.5)), ast.Float(33.0)), ast.Float(2.0))) ]) report = typechecker.typecheck(given) expected_errors = [] for (r, e) in zip_longest(report.get_errors(), expected_errors): assert r == e
def test_multiline_assignment(self): '''Can parse multiple assignments: `x = 5; y = 10 * z; sum_times_five = (x + y) * 5;`''' given = iter([ Token('ID', 'x'), Token('EQUAL', '='), Token('INTEGER', '5'), Token('SEMI', ';'), Token('ID', 'y'), Token('EQUAL', '='), Token('INTEGER', '10'), Token('MUL', '*'), Token('ID', 'z'), Token('SEMI', ';'), Token('ID', 'sum_times_five'), Token('EQUAL', '='), Token('LPAREN', '('), Token('ID', 'x'), Token('PLUS', '+'), Token('ID', 'y'), Token('RPAREN', ')'), Token('MUL', '*'), Token('INTEGER', '5'), Token('SEMI', ';'), ]) result = parser.parse(given) expected = ast.Block([ ast.Assignment( ast.ID('x'), ast.Integer(5) ), ast.Assignment( ast.ID('y'), ast.BinOp('*', ast.Integer(10), ast.ID('z')) ), ast.Assignment( ast.ID('sum_times_five'), ast.BinOp( '*', ast.BinOp('+', ast.ID('x'), ast.ID('y')), ast.Integer(5) ) ) ]) assert result == expected
def test_binop_error(self): '''An error with given program: `int more_cowbell; more_cowbell = 2000.0 + 1;`''' given = ast.Block([ ast.Declaration('int', [ast.ID('more_cowbell')]), ast.Assignment(ast.ID('more_cowbell'), ast.BinOp('+', ast.Float(2000.0), ast.Integer(1))) ]) report = typechecker.typecheck(given) expected_errors = [ TypecheckerError( ast.Assignment( ast.ID('more_cowbell'), ast.BinOp('+', ast.Float(2000.0), ast.Integer(1)))) ] for (r, e) in zip_longest(report.get_errors(), expected_errors): assert r == e
def test_op_order(self): '''Enforces the right op. precedence: `1 + 5 * 20;`''' given = iter([ Token('INTEGER', '1'), Token('PLUS', '+'), Token('INTEGER', '5'), Token('MUL', '*'), Token('INTEGER', '20'), Token('SEMI', ';') ]) expected = ast.Block([ ast.Statement( ast.BinOp( '+', ast.Integer(1), ast.BinOp('*', ast.Integer(5), ast.Integer(20)) ) ) ]) result = parser.parse(given) assert expected == result
def test_if_else_statement(self): '''Can parse an if-else statement `if (x + 3) { duck = x; } else { goose = x; }` ''' given = iter([ Token('IF', 'if'), Token('LPAREN', '('), Token('ID', 'x'), Token('PLUS', '+'), Token('INTEGER', '3'), Token('RPAREN', ')'), Token('LCURLY', '{'), Token('ID', 'duck'), Token('EQUAL', '='), Token('ID', 'x'), Token('SEMI', ';'), Token('RCURLY', '}'), Token('ELSE', 'else'), Token('LCURLY', '{'), Token('ID', 'goose'), Token('EQUAL', '='), Token('ID', 'x'), Token('SEMI', ';'), Token('RCURLY', '}'), ]) expected = ast.Block([ ast.IfStatement( ast.BinOp('+', ast.ID('x'), ast.Integer(3)), ast.Block([ ast.Assignment(ast.ID('duck'), ast.ID('x')) ]), ast.Block([ ast.Assignment(ast.ID('goose'), ast.ID('x')) ]) ) ]) result = parser.parse(given) assert expected == result
def test_add(self): '''Can parse a simple addition: `1 + 1;`''' given = iter([ Token('INTEGER', '1'), Token('PLUS', '+'), Token('INTEGER', '1'), Token('SEMI', ';') ]) expected = ast.Block([ ast.Statement(ast.BinOp('+', ast.Integer(1), ast.Integer(1))) ]) result = parser.parse(given) assert expected == result
def test_float_expression(self): '''Expression with floats and integers: `1.0 + 2;`''' given = iter([ Token('FLOAT', '1.0'), Token('PLUS', '+'), Token('INTEGER', '2'), Token('SEMI', ';') ]) result = parser.parse(given) expected = ast.Block([ ast.Statement( ast.BinOp( '+', ast.Float(1.0), ast.Integer(2) ) ) ]) assert result == expected
def test_simple_program(self): '''Can parse a simple program `int x[3], duck, goose, wildcat; duck = 1; goose = 2; x[0] = duck; x[duck] = goose; if (duck + x[duck]) { wildcat = duck; } else { wildcat = goose; } x[2] = wildcat;` ''' given = iter([ Token('INT_TYPE', 'int'), Token('ID', 'x'), Token('LBRACE', '['), Token('INTEGER', '3'), Token('RBRACE', ']'), Token('COMMA', ','), Token('ID', 'duck'), Token('COMMA', ','), Token('ID', 'goose'), Token('COMMA', ','), Token('ID', 'wildcat'), Token('SEMI', ';'), Token('ID', 'duck'), Token('EQUAL', '='), Token('INTEGER', '1'), Token('SEMI', ';'), Token('ID', 'goose'), Token('EQUAL', '='), Token('INTEGER', '2'), Token('SEMI', ';'), Token('ID', 'x'), Token('LBRACE', '['), Token('INTEGER', '0'), Token('RBRACE', ']'), Token('EQUAL', '='), Token('ID', 'duck'), Token('SEMI', ';'), Token('ID', 'x'), Token('LBRACE', '['), Token('ID', 'duck'), Token('RBRACE', ']'), Token('EQUAL', '='), Token('ID', 'goose'), Token('SEMI', ';'), Token('IF', 'if'), Token('LPAREN', '('), Token('ID', 'duck'), Token('PLUS', '+'), Token('ID', 'x'), Token('LBRACE', '['), Token('ID', 'duck'), Token('RBRACE', ']'), Token('RPAREN', ')'), Token('LCURLY', '{'), Token('ID', 'wildcat'), Token('EQUAL', '='), Token('ID', 'duck'), Token('SEMI', ';'), Token('RCURLY', '}'), Token('ELSE', 'else'), Token('LCURLY', '{'), Token('ID', 'wildcat'), Token('EQUAL', '='), Token('ID', 'goose'), Token('SEMI', ';'), Token('RCURLY', '}'), Token('ID', 'x'), Token('LBRACE', '['), Token('INTEGER', '2'), Token('RBRACE', ']'), Token('EQUAL', '='), Token('ID', 'wildcat'), Token('SEMI', ';') ]) expected = ast.Block([ ast.Declaration( 'int', [ast.ArrayRef(ast.ID('x'), ast.Integer(3)), ast.ID('duck'), ast.ID('goose'), ast.ID('wildcat')] ), ast.Assignment(ast.ID('duck'), ast.Integer(1)), ast.Assignment(ast.ID('goose'), ast.Integer(2)), ast.Assignment(ast.ArrayRef(ast.ID('x'), ast.Integer(0)), ast.ID('duck')), ast.Assignment(ast.ArrayRef(ast.ID('x'), ast.ID('duck')), ast.ID('goose')), ast.IfStatement( ast.BinOp('+', ast.ID('duck'), ast.ArrayRef(ast.ID('x'), ast.ID('duck'))), ast.Block([ ast.Assignment(ast.ID('wildcat'), ast.ID('duck')) ]), ast.Block([ ast.Assignment(ast.ID('wildcat'), ast.ID('goose')) ]) ), ast.Assignment(ast.ArrayRef(ast.ID('x'), ast.Integer(2)), ast.ID('wildcat')), ]) result = parser.parse(given) assert expected == result
def expr_binop(s): return ast.BinOp(s[1].getstr(), s[0], s[2])