X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/bcc25888b3299758ad36721530cca3b899b7166c..c043fa535514a76091be87a45ad472a505f9dd33:/lunaix-os/scripts/build-tools/lcfg2/builder.py diff --git a/lunaix-os/scripts/build-tools/lcfg2/builder.py b/lunaix-os/scripts/build-tools/lcfg2/builder.py new file mode 100644 index 0000000..832807a --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg2/builder.py @@ -0,0 +1,118 @@ +import ast +import textwrap +import os + +from lib.utils import ConfigAST, ConfigASTVisitor +from .common import NodeProperty, ConfigNodeError, ValueTypeConstrain +from .nodes import GroupNode, TermNode +from .sanitiser import TreeSanitiser + +class NodeBuilder(ConfigASTVisitor): + def __init__(self, env): + super().__init__() + + self.__env = env + self.__level = [] + self.__noncfg_astns = [] + + def __pop_and_merge(self): + if len(self.__level) == 0: + return + + if len(self.__level) == 1: + self.__level.pop() + return + + node = self.__level.pop() + prev = self.__level[-1] + + assert isinstance(prev, GroupNode) + + prev.add_child(node) + + def __push(self, cfg_node): + self.__level.append(cfg_node) + + def __check_literal_expr(self, node): + return ( + isinstance(node, ast.Expr) + and isinstance(node.value, ast.Constant) + and isinstance(node.value.value, str) + ) + + def _visit_fndef(self, node): + if hasattr(node, "__builtin"): + self.__noncfg_astns.append(node) + return + + cfgn_type = TermNode + if not node.returns: + cfgn_type = GroupNode + + cfgn = cfgn_type(self.__env, self.current_file(), node.name) + + self.__push(cfgn) + + try: + for decor in node.decorator_list: + cfgn.apply_decorator(decor) + + astns = [] + help_text = "" + for sub in node.body: + if isinstance(sub, ast.FunctionDef): + self._visit_fndef(sub) + continue + + if self.__check_literal_expr(sub): + help_text += sub.value.value + continue + + astns.append(sub) + + NodeProperty.Token[cfgn] = node + NodeProperty.HelpText[cfgn] = textwrap.dedent(help_text) + + if cfgn_type is TermNode: + NodeProperty.Type[cfgn] = ValueTypeConstrain(cfgn, node.returns) + + astns.append(ast.Pass()) + cfgn.set_node_body(astns) + + self.__env.register_node(cfgn) + + except ConfigNodeError as e: + raise e + except Exception as e: + msg = e.args[0] if len(e.args) > 0 else type(e).__name__ + raise cfgn.config_error(msg) + + self.__pop_and_merge() + + def visit(self, node): + super().visit(node) + + if isinstance(node, ast.Module): + return + + if not isinstance(node, ast.FunctionDef): + self.__noncfg_astns.append(node) + + @staticmethod + def build(env, rootfile): + if not os.path.exists(rootfile): + print(f"warning: config file '{rootfile}' not detected, skipped") + return + + build = NodeBuilder(env) + ast = ConfigAST(rootfile) + + env.reset() + ast.visit(TreeSanitiser()) + ast.visit(build) + + for node in env.nodes(): + node.apply_node_body() + + env.set_exec_context(build.__noncfg_astns) + env.relocate_children() \ No newline at end of file