def test_if_else_expression(self) -> None: source: str = 'si (a > b) { x } si_no { z; x; }' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program, 1) # Test correct node types if_expression = cast(If, cast(ExpressionStatement, program.statements[0]).expression) self.assertIsInstance(if_expression, If) # Test condition assert if_expression.condition is not None self._test_infix_expression(cast(Expression, if_expression.condition), "a", ">", "b") # Test consequence assert if_expression.consequence is not None self._test_block(if_expression.consequence, 1, ["x"]) # Test alternative assert if_expression.alternative is not None self._test_block(if_expression.alternative, 2, ["z", "x"])
def test_let_statements(self) -> None: source: str = ''' variable x = 5; variable y = 10; variable foo = 20; variable bar = verdadero; ''' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self.assertEqual(len(program.statements), 4) expected_identifiers_and_values: List[Tuple[str,Any]] = [ ('x',5), ('y',10), ('foo',20), ('bar',True), ] for statement, (expected_identifier, expected_value) in zip(program.statements, expected_identifiers_and_values): self.assertEqual(statement.token_literan(), 'variable') self.assertIsInstance(statement, LetStatement) let_statement = cast(LetStatement,statement) assert let_statement.name is not None self._test_identifier(let_statement.name, expected_identifier) assert let_statement.value is not None self._test_literal_expression(let_statement.value, expected_value)
def test_function_literal(self) -> None: source: str = "funcion(x, y) { x + y}" lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program, 1) # Test correct node type function_literal = cast(Function, cast(ExpressionStatement, program.statements[0]).expression) self.assertIsInstance(function_literal, Function) # Test params self.assertEquals(len(function_literal.parameters), 2) self._test_literal_expression(function_literal.parameters[0], "x") self._test_literal_expression(function_literal.parameters[1], "y") # Test body assert function_literal.body is not None self.assertEquals(len(function_literal.body.statements), 1) body = cast(ExpressionStatement, function_literal.body.statements[0]) assert body.expression is not None self._test_infix_expression(body.expression, "x", "+", "y")
def test_infix_expression(self) -> None: source: str = """ 5 + 5; 5 - 5; 5 * 5; 5 / 5; 5 > 5; 5 < 5; 5 >= 5; 5 <= 5; 5 == 5; 5 === 5; 5 != 5; 5 !== 5; verdadero == verdadero verdadero === verdadero falso != falso falso !== falso """ lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program, expected_statement_count=16) expected_operators_and_values: List[Tuple[Any, str, Any]] = [ (5, "+", 5), (5, "-", 5), (5, "*", 5), (5, "/", 5), (5, ">", 5), (5, "<", 5), (5, ">=", 5), (5, "<=", 5), (5, "==", 5), (5, "===", 5), (5, "!=", 5), (5, "!==", 5), (True, "==", True), (True, "===", True), (False, "!=", False), (False, "!==", False), ] for statement, (expected_left, expected_operator, expected_right) in zip( program.statements, expected_operators_and_values): statement = cast(ExpressionStatement, statement) assert statement.expression is not None self.assertIsInstance(statement.expression, Infix) self._test_infix_expression(statement.expression, expected_left, expected_operator, expected_right)
def test_if_expression(self) -> None: source = ''' si (x < y) { z } ''' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program) #Test correct node type if_expression = cast(If, cast(ExpressionStatement, program.statements[0]).expression) self.assertIsInstance(if_expression, If) #Test condition assert if_expression.condition is not None self._test_infix_expression(if_expression.condition, 'x','<','y') #Test consequence assert if_expression.consequence is not None self.assertIsInstance(if_expression.consequence, Block) self.assertEquals(len(if_expression.consequence.statements),1) consequence_statement = cast(ExpressionStatement, if_expression.consequence.statements[0]) assert consequence_statement.expression is not None self._test_identifier(consequence_statement.expression, 'z') #Test alternative self.assertIsNone(if_expression.alternative)
def test_function_call(self) -> None: source: str = """ variable resultado = suma(dos, tres); """ lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(10): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.LET, "variable"), Token(TokenType.IDENT, "resultado"), Token(TokenType.ASSIGN, "="), Token(TokenType.IDENT, "suma"), Token(TokenType.LPAREN, "("), Token(TokenType.IDENT, "dos"), Token(TokenType.COMMA, ","), Token(TokenType.IDENT, "tres"), Token(TokenType.RPAREN, ")"), Token(TokenType.SEMICOLON, ";"), ] self.assertEquals(tokens, expected_tokens)
def test_if_expression(self) -> None: source: str = "si (x < y) { z }" lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program, 1) # Test correct node types if_expression = cast(If, cast(ExpressionStatement, program.statements[0]).expression) self.assertIsInstance(if_expression, If) # Test condition assert if_expression.condition is not None self._test_infix_expression(cast(Expression, if_expression.condition), "x", "<", "y") # Test consequence assert if_expression.consequence is not None self._test_block(if_expression.consequence, 1, ["z"]) # Test alternative self.assertIsNone(if_expression.alternative)
def test_control_statement(self) -> None: source: str = ''' si (5 < 10) { regresa verdadero; } si_no { regresa falso; } ''' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(17): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.IF, 'si'), Token(TokenType.LPAREN, '('), Token(TokenType.INT, '5'), Token(TokenType.LT, '<'), Token(TokenType.INT, '10'), Token(TokenType.RPAREN, ')'), Token(TokenType.LBRACE, '{'), Token(TokenType.RETURN, 'regresa'), Token(TokenType.TRUE, 'verdadero'), Token(TokenType.SEMICOLON, ';'), Token(TokenType.RBRACE, '}'), Token(TokenType.ELSE, 'si_no'), Token(TokenType.LBRACE, '{'), Token(TokenType.RETURN, 'regresa'), Token(TokenType.FALSE, 'falso'), Token(TokenType.SEMICOLON, ';'), Token(TokenType.RBRACE, '}'), ] self.assertEquals(tokens, expected_tokens)
def test_function_declaration(self) -> None: source: str = ''' variable suma = procedimiento(x, y) { x + y; }; ''' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(16): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.LET, 'variable'), Token(TokenType.IDENT, 'suma'), Token(TokenType.ASSIGN, '='), Token(TokenType.FUNCTION, 'procedimiento'), Token(TokenType.LPAREN, '('), Token(TokenType.IDENT, 'x'), Token(TokenType.COMMA, ','), Token(TokenType.IDENT, 'y'), Token(TokenType.RPAREN, ')'), Token(TokenType.LBRACE, '{'), Token(TokenType.IDENT, 'x'), Token(TokenType.PLUS, '+'), Token(TokenType.IDENT, 'y'), Token(TokenType.SEMICOLON, ';'), Token(TokenType.RBRACE, '}'), Token(TokenType.SEMICOLON, ';'), ] self.assertEquals(tokens, expected_tokens)
def test_function_parameters(self) -> None: tests = [ { "input": "funcion() {};", "expected_params": [] }, { "input": "funcion(x) {};", "expected_params": ["x"] }, { "input": "funcion(x, y, z) {};", "expected_params": ["x", "y", "z"] } ] for test in tests: lexer: Lexer = Lexer(test["input"]) # type: ignore parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program, 1) function = cast(Function, cast(ExpressionStatement, program.statements[0]).expression) self.assertEquals(len(function.parameters), len(test["expected_params"])) for idx, param in enumerate(test["expected_params"]): self._test_literal_expression(function.parameters[idx], param)
def test_three_character_operator(self) -> None: source: str = """ 10 === 10; 10 !== 9; """ lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(8): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.INT, "10"), Token(TokenType.SIMILAR, "==="), Token(TokenType.INT, "10"), Token(TokenType.SEMICOLON, ";"), Token(TokenType.INT, "10"), Token(TokenType.DIFF, "!=="), Token(TokenType.INT, "9"), Token(TokenType.SEMICOLON, ";"), ] self.assertEquals(tokens, expected_tokens)
def test_return_statements(self) -> None: source: str = """ regresa 5; regresa foo; regresa verdadero; regresa falso; """ lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() expected_return_values: List[Any] = [ 5, 'foo', True, False, ] self.assertEqual(len(program.statements), 4) for statement, expected_return_value in zip( program.statements, expected_return_values): self.assertEqual(statement.token_literal(), 'regresa') self.assertIsInstance(statement, ReturnStatement) return_statement = cast(ReturnStatement, statement) assert return_statement.return_value is not None self._test_literal_expression(return_statement.return_value, expected_return_value)
def test_function_parameters(self) -> None: tests = [ { 'input': 'procedimiento() {};', 'expected_params': [] }, { 'input': 'procedimiento(x) {};', 'expected_params': ['x'] }, { 'input': 'procedimiento(x, y, z) {};', 'expected_params': ['x', 'y', 'z'] }, ] for test in tests: lexer: Lexer = Lexer(test['input']) # type: ignore parser: Parser = Parser(lexer) program: Program = parser.parse_program() function = cast( Function, cast(ExpressionStatement, program.statements[0]).expression) self.assertEquals(len(function.parameters), len(test['expected_params'])) for idx, param in enumerate(test['expected_params']): self._test_literal_expression(function.parameters[idx], param)
def test_operator_precedence(self) ->None: test_sources: List[Tuple[str, str, int]] = [ ('-a * b;','((-a) * b)',1), ('!-a;','(!(-a))',1), ('a + b / c;','(a + (b / c))',1), ('3 + 4; -5 * 5;','(3 + 4)((-5) * 5)',2), ('verdadero;falso;verdadero;','(verdadero)(falso)(verdadero)',3), ('!verdadero;!falso;verdadero;','(!(verdadero))(!(falso))(verdadero)',3), ('verdadero + verdadero;','((verdadero) + (verdadero))',1), ('5 / 9 * 10','((5 / 9) * 10)',1), ('5 / 9 * 10; verdadero + verdadero;','((5 / 9) * 10)((verdadero) + (verdadero))',2), ('3 < 5 == verdadero','((3 < 5) == (verdadero))',1), ('1 + (2 + 3) + 4;', '((1 + (2 + 3)) + 4)', 1), ('(5 + 5) * 2;', '((5 + 5) * 2)', 1), ('2 / (5 + 5);', '(2 / (5 + 5))', 1), ('-(5 + 5);', '(-(5 + 5))', 1), ('a + suma(b * c) + d;', '((a + suma((b * c))) + d)', 1), ('suma(a, b, 1, 2 * 3, 4 + 5, suma(6, 7 * 8));', 'suma(a, b, 1, (2 * 3), (4 + 5), suma(6, (7 * 8)))', 1), ('suma(a + b + c * d / f + g);', 'suma((((a + b) + ((c * d) / f)) + g))', 1), ] for source, expected_result, expected_statement_count in test_sources: lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program, expected_statement_count) self.assertEquals(str(program), expected_result)
def test_parse_error(self) -> None: source: str = 'variable x 5' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self.assertEquals(len(parser.errors), 1)
def _load_n_tokens(self, source: str, size: int) -> List[Token]: lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(size): tokens.append(lexer.next_token()) return tokens
def test_parse_program(self) -> None: source: str = 'variable x = 5;' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self.assertIsNotNone(program) self.assertIsInstance(program, Program)
def _evaluate_tests(self, source: str) -> Object: lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() env: Environment = Environment() evaluated = evaluate(program, env) assert evaluated is not None return evaluated
def test_string_literal_expression(self) -> None: source: str = '"hello world!"' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() expression_ststement= cast(ExpressionStatement, program.statements[0]) string_literal = cast(StringLiteral, expression_ststement.expression) self.assertIsInstance(string_literal, StringLiteral) self.assertEquals(string_literal.value, 'hello world!')
def test_identifier_expression(self) -> None: source: str = 'foobar;' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program) expression_statement = cast(ExpressionStatement, program.statements[0]) self._test_literal_expression(expression_statement.expression, 'foobar')
def test_eof(self) -> None: source: str = '+' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(len(source) + 1): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.PLUS, '+'), Token(TokenType.EOF, ''), ] self.assertEquals(tokens, expected_tokens)
def test_integer_expressions(self) -> None: source = str = '5'; lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() #si hay un error en el parser este test no va a decir que hay un error self._test_program_statement(parser, program) exprassion_statement = cast(ExpressionStatement, program.statements[0]) assert exprassion_statement.expression is not None self._test_literal_expression(exprassion_statement.expression, 5)
def test_illegal(self) -> None: source: str = '¡¿@' lexer: Lexer = Lexer(source) tokens = self._load_tokens(source) expected_tokens: List[Token] = [ Token(TokenType.ILLEGAL, '¡',1), Token(TokenType.ILLEGAL, '¿',1), Token(TokenType.ILLEGAL, '@',1), ] self.assertEqual(tokens, expected_tokens)
def test_integer_expressions(self) -> None: source: str = '5;' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program) expression_statement = cast(ExpressionStatement, program.statements[0]) assert expression_statement.expression is not None self._test_literal_expression(expression_statement.expression, 5)
def test_illegal(self) -> None: source: str = '¡¿@' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(len(source)): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.ILLEGAL, '¡'), Token(TokenType.ILLEGAL, '¿'), Token(TokenType.ILLEGAL, '@'), ] self.assertEquals(tokens, expected_tokens)
def test_assignment(self) -> None: source: str = 'variable cinco = 5;' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(5): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.LET, 'variable'), Token(TokenType.IDENT, 'cinco'), Token(TokenType.ASSIGN, '='), Token(TokenType.INT, '5'), Token(TokenType.SEMICOLON, ';'), ] self.assertEquals(tokens, expected_tokens)
def test_delimiters(self) -> None: source: str = '(){},;' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(len(source)): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.LPAREN, '('), Token(TokenType.RPAREN, ')'), Token(TokenType.LBRACE, '{'), Token(TokenType.RBRACE, '}'), Token(TokenType.COMMA, ','), Token(TokenType.SEMICOLON, ';'), ] self.assertEquals(tokens, expected_tokens)
def test_boolean_expression(self) -> None: source: str = 'verdadero; falso;' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program, expedted_statement_count=2) expected_values: List[bool] = [True, False] for statement, expected_value in zip(program.statements, expected_values): expression_statement = cast(ExpressionStatement, statement) assert expression_statement is not None self._test_literal_expression(expression_statement.expression, expected_value)
def test_infix_expression(self) -> None: source: str = ''' 5 + 5; 5 - 5; 5 * 5; 5 / 5; 5 > 5; 5 < 5; 5 == 5; 5 != 5; falso == falso; verdadero != falso; ''' #TODO algo de booleano -- esto rompe verdadero == verdadero; lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program, expedted_statement_count=10) expected_operators_and_values: List[Tuple[Any, str, Any]] = [ (5,'+',5), (5,'-',5), (5,'*',5), (5,'/',5), (5,'>',5), (5,'<',5), (5,'==',5), (5,'!=',5), (False, '==', False), (True, '!=', False) ] for statement, (expected_left, expected_operator, expected_right) in zip( program.statements, expected_operators_and_values ): statement = cast(ExpressionStatement, statement) assert statement.expression is not None self.assertIsInstance(statement.expression, Infix) self._test_infix_expression( statement.expression, expected_left, expected_operator, expected_right )
def test_string(self) -> None: source: str = ''' "foo"; "Platzi es la mejor escuela de CS"; ''' lexer: Lexer = Lexer(source) tokens: List[Token] = [] for i in range(4): tokens.append(lexer.next_token()) expected_tokens: List[Token] = [ Token(TokenType.STRING, 'foo'), Token(TokenType.SEMICOLON, ';'), Token(TokenType.STRING, 'Platzi es la mejor escuela de CS'), Token(TokenType.SEMICOLON, ';'), ] self.assertEquals(tokens, expected_tokens)