from parsers.BaseParser import BaseParser, Node, ErrorNode from dataclasses import dataclass import ast import copy import logging log = logging.getLogger(__name__) @dataclass() class PythonErrorNode(ErrorNode): source: str exception: Exception def __post_init__(self): log.debug("-> PythonErrorNode: " + str(self.exception)) @dataclass() class PythonNode(Node): source: str ast: ast.AST def __repr__(self): return "PythonNode(" + ast.dump(self.ast) + ")" # return "PythonNode(" + self.source + ")" class PythonParser(BaseParser): def __init__(self, text, source=""): text = text if isinstance(text, str) else self.get_text_from_tokens(text) text = text.strip() BaseParser.__init__(self, "PythonParser", text) self.source = source def parse(self): # first, try to parse an expression res, tree, error = self.try_parse_expression() if not res: # then try to parse a statement res, tree, error = self.try_parse_statement() if not res: self.has_error = True error_node = PythonErrorNode(self.text, error) self.error_sink.append(error_node) return error_node log.debug("Recognized python code.") return PythonNode(self.text, tree) def try_parse_expression(self): try: return True, ast.parse(self.text, f"<{self.source}>", 'eval'), None except Exception as error: return False, None, error def try_parse_statement(self): try: return True, ast.parse(self.text, f"<{self.source}>", 'exec'), None except Exception as error: return False, None, error def expr_to_expression(self, expr): expr.lineno = 0 expr.col_offset = 0 result = ast.Expression(expr.value, lineno=0, col_offset=0) return result def exec_with_return(self, code): code_ast = ast.parse(code) init_ast = copy.deepcopy(code_ast) init_ast.body = code_ast.body[:-1] last_ast = copy.deepcopy(code_ast) last_ast.body = code_ast.body[-1:] exec(compile(init_ast, "", "exec"), globals()) if type(last_ast.body[0]) == ast.Expr: return eval(compile(self.expr_to_expression(last_ast.body[0]), "", "eval"), globals()) else: exec(compile(last_ast, "", "exec"), globals()) class PythonGetNamesVisitor(ast.NodeVisitor): """ This visitor will find all the name declared in the ast """ def __init__(self): self.names = set() log.debug("Searching for names.") def visit_Name(self, node): log.debug(f"Found name : {node.id}") self.names.add(node.id)