from core.builtin_concepts import BuiltinConcepts 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(source='" + self.source + "', ast=" + self.get_dump(self.ast_) + ")" def __repr__(self): ast_type = "expr" if isinstance(self.ast_, ast.Expression) else "module" return "PythonNode(" + ast_type + "='" + self.source + "')" def __eq__(self, other): if not isinstance(other, PythonNode): return False if self.source != other.source: return False self_dump = self.get_dump(self.ast_) other_dump = self.get_dump(other.ast_) return self_dump == other_dump def __hash__(self): return hash((self.source, self.ast_.hash)) @staticmethod def get_dump(ast_): dump = ast.dump(ast_) for to_remove in [", ctx=Load()", ", kind=None", ", type_ignores=[]"]: dump = dump.replace(to_remove, "") return dump class PythonParser(BaseParser): """ Parse Python scripts """ def __init__(self, source=""): BaseParser.__init__(self, "PythonParser") self.source = source def parse(self, context, text): text = text if isinstance(text, str) else self.get_text_from_tokens(text) text = text.strip() sheerka = context.sheerka # first, try to parse an expression res, tree, error = self.try_parse_expression(text) if not res: # then try to parse a statement res, tree, error = self.try_parse_statement(text) if not res: self.has_error = True error_node = PythonErrorNode(text, error) self.error_sink.append(error_node) ret = sheerka.ret( self.name, not self.has_error, sheerka.new( BuiltinConcepts.PARSER_RESULT, parser=self, source=text, body=self.error_sink if self.has_error else PythonNode(text, tree), try_parsed=None)) self.log_result(log, text, ret) return ret def try_parse_expression(self, text): try: return True, ast.parse(text, f"<{self.source}>", 'eval'), None except Exception as error: return False, None, error def try_parse_statement(self, text): try: return True, ast.parse(text, f"<{self.source}>", 'exec'), None except Exception as error: return False, None, error 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)