def test_do_while_statement(self): source_code = """ do while .t. return 1 enddo """ lexer = Lexer(source_code) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) do_while = program.statements[0] condition = do_while.condition self.assertEqual( True, condition.value, f'condition.value is not True, got={condition.value}') body = do_while.block.statements[0] integer = body.value self.assertEqual(1, integer.value, f'integer.value is not 1, got={integer.value}')
def test_function_with_params(self): source_code = """ function retorna_1(foo, bar) return 1 endfunc """ lexer = Lexer(source_code) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) func = program.statements[0] self.assertEqual("retorna_1", func.name.value) identi1 = func.params[0] self.assertEqual("foo", identi1.value, f'identi1.value no es foo, got={identi1.value}') identi2 = func.params[1] self.assertEqual("bar", identi2.value, f'identi1.value no es bar, got={identi2.value}') return_ast = func.body.statements[0] integer = return_ast.value self.assertEqual(1, integer.value, f'integer.value no es 1, got={integer.value}')
def test_unary_expressions(self): tests = [ ["!5", "!", 5], ["-15", "-", 15], ["!.t.", "!", True], ["!.f.", "!", False], ] for tt in tests: source = tt[0] operator = tt[1] value = tt[2] lexer = Lexer(source) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) exp = program.statements[0] self.assertEqual( exp.operator, operator, f"exp.operator is not '{operator}'. got={exp.operator}") self.assertEqual( exp.right.value, value, f'exp.right is not {value}. got={exp.right.value}')
def test_if_else_statement(self): source_code = """ if .t. return 1 else return 2 endif """ lexer = Lexer(source_code) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) if_stmt = program.statements[0] self.assertEqual(if_stmt.condition.value, True) # Condición del if (.t.) # Consecuencia return_if_ast = if_stmt.consequence.statements[0] integer_ast = return_if_ast.value self.assertEqual( 1, integer_ast.value, f'integer_ast.value is not 1, got={integer_ast.value}.') # Alternativa return_else_ast = if_stmt.alternative.statements[0] integer_ast = return_else_ast.value self.assertEqual( 2, integer_ast.value, f'integer_ast.value is not 2, got={integer_ast.value}.')
def assert_test_eval(self, source): lexer = Lexer(source) parser = Parser(lexer) program = parser.program() eva = Evaluator() env = Environment() return eva.eval(ast_node=program, env=env)
def test_null(self): lexer = Lexer(".null.") parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual(1, len(program.statements)) null = program.statements[0] self.assertEqual(None, null.value, f'boolean.value is not None. got={null.value}')
def test_function_call_no_params(self): lexer = Lexer("foo()") parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) func = program.statements[0] self.assertEqual("foo", func.name.value, f'func.name no es foo, got={func.name.value}')
def test_integer(self): lexer = Lexer("5") parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program has not enough statements. got={len(program.statements)}' ) integer = program.statements[0] self.assertEqual(5, integer.value, f'integer.value is not 5. got={integer.value}')
def test_string(self): lexer = Lexer('"hello world"') parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program has not enough statements. got={len(program.statements)}' ) string = program.statements[0] self.assertEqual( "hello world", string.value, f'string.value is not hello world. got={string.value}')
def test_identifier(self): lexer = Lexer("foobar") parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program has not enough statements. got={len(program.statements)}' ) ident = program.statements[0] self.assertEqual("foobar", ident.value, f'ident.value not "foobar" got={ident.value}')
def test_assignment_statement(self): lexer = Lexer("foo = bar") parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual(1, len(program.statements)) assignment = program.statements[0] ident_var = assignment.name ident_value = assignment.value self.assertEqual("foo", ident_var.value, f'ident.value is not foo. got={ident_var.value}') self.assertEqual( "bar", ident_value.value, f'ident_value.value is not bar. got={ident_value.value}')
def repl(): """ REPL: se llama así por cada una de las fases que realiza durante 1 ciclo: R: Read - Leer y analizar (parse) el código fuente. E: Eval - Evaluar el AST generado por el parser. P: Print - Imprimir el resultado en la consola. L: Loop - Repetir el ciclo. :return: """ print_header() env = Environment() source_code = '' while True: try: user_input = input(">> ") except EOFError: break if not user_input: continue """ Usamos el ';' para unir declaraciones de más de 1 línea. """ if user_input[len(user_input) - 1] == ';': source_code += '\n' + user_input[0:len(user_input) - 1] continue else: source_code += '\n' + user_input if user_input == 'quit': break lexer = Lexer(source_code) parser = Parser(lexer) program = parser.program() if len(parser.errors) != 0: print_parser_errors(parser.errors) break evaluator = Evaluator() evaluated = evaluator.eval(ast_node=program, env=env) if evaluated is not None: print(evaluated.to_string()) source_code = ''
def test_boolean(self): tests = [ [".t.", True], [".f.", False], ] for tt in tests: source = tt[0] expected = tt[1] lexer = Lexer(source) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual(1, len(program.statements)) boolean = program.statements[0] self.assertEqual( expected, boolean.value, f'boolean.value is not {expected}. got={boolean.value}')
def test_binary_expressions(self): tests = [ ["5 + 5", 5, "+", 5], ["5 - 5", 5, "-", 5], ["5 * 5", 5, "*", 5], ["5 / 5", 5, "/", 5], ["5 < 5", 5, "<", 5], ["5 > 5", 5, ">", 5], ["5 == 5", 5, "==", 5], ["5 != 5", 5, "!=", 5], [".t. == .t.", True, "==", True], [".t. != .f.", True, "!=", False], [".f. == .f.", False, "==", False], ] for tt in tests: source = tt[0] left = tt[1] operator = tt[2] right = tt[3] lexer = Lexer(source) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) exp = program.statements[0] self.assertEqual(exp.left.value, left, f'exp.left is not {left}. got={exp.left.value}') self.assertEqual( exp.operator, operator, f"exp.operator is not '{operator}'. got={exp.operator}") self.assertEqual( exp.right.value, right, f'exp.right is not {right}. got={exp.right.value}')
def test_function_call_with_args(self): lexer = Lexer("foo(1, 2)") parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) func = program.statements[0] self.assertEqual("foo", func.name.value, f'func.name no es foo, got={func.name.value}') # Validar los argumentos arg1 = func.arguments[0] arg2 = func.arguments[1] self.assertEqual(1, arg1.value, f'arg1.value no es 1, got={arg1.value}') self.assertEqual(2, arg2.value, f'arg2.value no es 2, got={arg2.value}')
def test_return_statement(self): tests = [["return \n", Boolean(value=True)], ["return .f.", Boolean(value=False)], ["return 5", Integer(value=5)], ["return foobar", Identifier(value="foobar")], ["return .null.", Null()]] for tt in tests: source = tt[0] expected = tt[1] lexer = Lexer(source) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual(1, len(program.statements)) return_stmt = program.statements[0] return_value = return_stmt.value self.assertEqual( expected.value, return_value.value, f'return_stmt.value is not {expected.value}. got={return_value.value}' )
def test_function_without_parenthesis(self): source_code = """ function retorna_1 return 1 endfunc """ lexer = Lexer(source_code) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual( 1, len(program.statements), f'program.statements does not contain 1 statements. got={len(program.statements)}' ) func = program.statements[0] self.assertEqual("retorna_1", func.name.value) return_ast = func.body.statements[0] integer = return_ast.value self.assertEqual(1, integer.value, f'integer.value no es 1, got={integer.value}')
def test_variable_declaration(self): tests = [ [ "public a", VariableDecl(name=Identifier(value="a"), scope='public') ], [ "local b", VariableDecl(name=Identifier(value="b"), scope='local') ], [ "private c", VariableDecl(name=Identifier(value="c"), scope='private') ], ] for tt in tests: source = tt[0] expected = tt[1] lexer = Lexer(source) parser = Parser(lexer) program = parser.program() self.assert_check_parser_errors(parser) self.assertEqual(1, len(program.statements)) variable = program.statements[0] self.assertEqual( expected.name.value, variable.name.value, f'expected.token.value is not {expected.name.value}. got={variable.name.value}' ) self.assertEqual( expected.scope, variable.scope, f'expected.scope is not {expected.scope}. got={variable.scope}' )
def test_tokens(self): source_code = """ && Ejemplo FoxLite x = 10 && Declaración de variable 'x' y = 20 && Declaración de variable 'y' && Prueba de IF if x >= y messagebox("x es mayor") else ? "x es menor" endif do while x < 99 x = x + 1 ? "Contando por", x if x == 55 return endif enddo a = .t. b = .f. c = .null. """ expected_tokens = [ Token(TokenType.IDENT, "x"), Token(TokenType.ASSIGN, "="), Token(TokenType.INT, "10"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IDENT, "y"), Token(TokenType.ASSIGN, "="), Token(TokenType.INT, "20"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IF, "if"), Token(TokenType.IDENT, "x"), Token(TokenType.GREATER_EQ, ">="), Token(TokenType.IDENT, "y"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.MESSAGEBOX, "messagebox"), Token(TokenType.LPAREN, "("), Token(TokenType.STRING, "x es mayor"), Token(TokenType.RPAREN, ")"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.ELSE, "else"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.PRINT, "?"), Token(TokenType.STRING, "x es menor"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.ENDIF, "endif"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.DO, "do"), Token(TokenType.WHILE, "while"), Token(TokenType.IDENT, "x"), Token(TokenType.LESS, "<"), Token(TokenType.INT, "99"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IDENT, "x"), Token(TokenType.ASSIGN, "="), Token(TokenType.IDENT, "x"), Token(TokenType.PLUS, "+"), Token(TokenType.INT, "1"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.PRINT, "?"), Token(TokenType.STRING, "Contando por"), Token(TokenType.COMMA, ","), Token(TokenType.IDENT, "x"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IF, "if"), Token(TokenType.IDENT, "x"), Token(TokenType.EQUAL, "=="), Token(TokenType.INT, "55"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.RETURN, "return"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.ENDIF, "endif"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.ENDDO, "enddo"), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IDENT, "a"), Token(TokenType.ASSIGN, "="), Token(TokenType.TRUE, ".t."), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IDENT, "b"), Token(TokenType.ASSIGN, "="), Token(TokenType.FALSE, ".f."), Token(TokenType.LBREAK, "LBREAK"), Token(TokenType.IDENT, "c"), Token(TokenType.ASSIGN, "="), Token(TokenType.NULL, ".null."), Token(TokenType.LBREAK, "LBREAK"), ] lexer = Lexer(source_code=source_code) for expected_token in expected_tokens: actual = lexer.next_token() self.assertEqual(expected_token.type, actual.Type) self.assertEqual(expected_token.value, actual.value)