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_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_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_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_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: 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_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_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_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_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 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_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_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_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_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_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 execute_program(scanned): lexer: Lexer = Lexer(" ".join(scanned)) parser: Parser = Parser(lexer) program: Program = parser.parse_program() env: Environment = Environment() if len(parser.errors) > 0: _print_parse_errors(parser.errors) return 0 evaluated = evaluate(program, env) if evaluated is not None: print(evaluated.inspect()) return 1
def test_let_statements(self) -> None: source: str = """ variable x = 5; variable y = 10; variable foo = 20; """ lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self.assertEqual(len(program.statements), 3) for statement in program.statements: self.assertEqual(statement.token_literal(), "variable") self.assertIsInstance(statement, LetStatement)
def start_repl() -> None: scanned: List[str] = [] while (source := input('>> ')) != 'salir()': scanned.append(source) lexer: Lexer = Lexer(' '.join(scanned)) parser: Parser = Parser(lexer) program: Program = parser.parse_program() env: Environment = Environment() if len(parser.errors) > 0: _print_parse_errors(parser.errors) continue evaluated = evaluate(program, env) if evaluated is not None: print(evaluated.inspect())
def test_operator_precedence(self) -> None: # El primer string representa el programa inicial # El segundo string representa cuál debería ser el órden de precedencia # El tercer int representa cuántos statements esperamos que tenga el programa test_sources: List[Tuple[str, str, int]] = [ ('-a * b;', '((-a) * b)', 1), ('!-a;', '(!(-a))', 1), ('a + b + c;', '((a + b) + c)', 1), ('a + b - c;', '((a + b) - c)', 1), ('a * b * c;', '((a * b) * c)', 1), ('a * b / c;', '((a * b) / c)', 1), ('a + b / c;', '(a + (b / c))', 1), ('a + b * c + d / e - f;', '(((a + (b * c)) + (d / e)) - f)', 1), ('3 + 4; -5 * 5;', '(3 + 4)((-5) * 5)', 2), ('5 > 4 == 3 < 4;', '((5 > 4) == (3 < 4))', 1), ('5 < 4 != 3 > 4;', '((5 < 4) != (3 > 4))', 1), ('3 + 4 * 5 == 3 * 1 + 4 * 5;', '((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))', 1), ('verdadero;', 'verdadero', 1), ('falso;', 'falso', 1), ('3 > 5 == verdadero;', '((3 > 5) == verdadero)', 1), ('3 < 5 == falso;', '((3 < 5) == falso)', 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_statements(parser, program, expected_statement_count) self.assertEquals(str(program), expected_result)
def test_names_in_let_statements(self) -> None: source: str = ''' variable x = 5; variable y = 10; variable foo = 20; ''' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() names: List[str] = [] for statement in program.statements: statement = cast(LetStatement, statement) assert statement.name is not None names.append(statement.name.value) expected_names: List[str] = ['x', 'y', 'foo'] self.assertEquals(names, expected_names)
def test_prefix_expression(self) -> None: source = '!5; -15; !falso; !verdadero' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program, expedted_statement_count=4) for statement, (expected_operator, expected_value) in zip( program.statements, (['!',5],['-',15],['!',False],['!',True]) ): statement = cast(ExpressionStatement, statement) self.assertIsInstance(statement.expression, Prefix) prefix = cast(Prefix, statement.expression) self.assertEquals(prefix.operator, expected_operator) assert prefix.right is not None self._test_literal_expression(prefix.right, expected_value)
def test_call_expression(self) -> None: source: str = 'suma(1, 2 *3, 4+5);' lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statement(parser, program) call = cast(Call, cast(ExpressionStatement, program.statements[0].expression)) self.assertIsInstance(call, Call) self._test_identifier(call.function, 'suma') #Test arguments assert call.arguments is not None self.assertEquals(len(call.arguments), 3) self._test_literal_expression(call.arguments[0], 1) self._test_infix_expression(call.arguments[1], 2, '*', 3) self._test_infix_expression(call.arguments[2], 4, '+' , 5)
def test_prefix_expression(self) -> None: source: str = "!5; -15; -verdadero; -falso" lexer: Lexer = Lexer(source) parser: Parser = Parser(lexer) program: Program = parser.parse_program() self._test_program_statements(parser, program, expected_statement_count=4) for statement, (expected_operator, expected_value) in zip( program.statements, [("!", 5), ("-", 15), ("-", True), ("-", False)]): statement = cast(ExpressionStatement, statement) self.assertIsInstance(statement.expression, Prefix) prefix = cast(Prefix, statement.expression) self.assertEquals(prefix.operator, expected_operator) assert prefix.right is not None self._test_literal_expression(prefix.right, expected_value)