3 from lib.utils import SourceLogger, Schema
4 from .common import NodeProperty, ConfigNodeError, NodeDependency
5 from .lazy import LazyLookup
6 from .rewriter import ConfigNodeASTRewriter
8 from .ast_validator import NodeValidator
9 from .rules import SyntaxRule
11 validator = NodeValidator(SyntaxRule())
13 class ConfigDecorator:
14 Label = Schema(ast.Constant)
15 Readonly = Schema(ast.Name, id="readonly")
16 Hidden = Schema(ast.Name, id="hidden")
17 Flag = Schema(ast.Name, id="flag")
20 target=Schema(ast.Name, id="parent"),
21 value=Schema(ast.Name))
24 target=Schema(ast.Name),
25 value=Schema(ast.Name))
29 def __init__(self, env, filename, name):
33 self._filename = filename
34 self._lazy_table = LazyLookup()
38 NodeProperty.Enabled[self] = True
39 NodeProperty.Hidden[self] = False
40 NodeProperty.Readonly[self] = False
41 NodeProperty.Status[self] = "Empty"
43 def set_node_body(self, ast_nodes):
44 self.__exec = ast.Module(ast_nodes, [])
46 def apply_node_body(self, rewriter = ConfigNodeASTRewriter):
47 assert isinstance(self.__exec, ast.Module)
50 validator.validate(self, new_ast)
52 new_ast = rewriter(self).rewrite(new_ast)
53 NodeDependency.try_create(self)
55 fn_name = f"__fn_{self._name}"
56 args = ast.arguments([], [], None, [], [], None, [])
58 ast.FunctionDef(fn_name, args, new_ast.body, []),
60 [ast.Name("_result", ctx=ast.Store())],
61 ast.Call(ast.Name(fn_name,ctx=ast.Load()), [], [])
65 module = ast.fix_missing_locations(module)
66 self.__exec = compile(module, self._filename, mode='exec')
69 def apply_decorator(self, decor):
70 if ConfigDecorator.Label == decor:
71 NodeProperty.Label[self] = str(decor.value)
73 elif ConfigDecorator.Readonly == decor:
74 NodeProperty.Readonly[self] = True
76 elif ConfigDecorator.Hidden == decor:
77 NodeProperty.Hidden[self] = True
79 elif ConfigDecorator.SetParent == decor:
80 NodeProperty.Parent[self] = decor.value.id
82 elif ConfigDecorator.Flag == decor:
83 NodeProperty.Hidden[self] = True
84 NodeProperty.Readonly[self] = True
86 elif ConfigDecorator.SetProperty == decor:
87 self.set_property(decor.target.id, decor.value.id)
90 fname = self._filename
92 col = decor.col_offset
93 msg = f"unknown decorator: {ast.unparse(decor)}"
94 msg = SourceLogger.fmt_warning(fname, line, col, msg)
98 self.update_status("Updating")
105 "__lzLut__": self._lazy_table
108 exec(self.__exec, globl, local)
109 self.update_status("Latest")
111 return local["_result"]
113 except ConfigNodeError as e:
114 self.update_status("Error")
117 except Exception as e:
118 self.update_status("Error")
119 raise self.config_error(e)
121 def sanity_check(self):
124 def get_property(self, key):
125 return self.__props[key] if key in self.__props else None
127 def set_property(self, key, value):
129 del self.__props[key]
131 self.__props[key] = value
134 val = NodeProperty.Value[self]
135 en = NodeProperty.Enabled[self]
136 parent = NodeProperty.Parent[self]
138 if isinstance(val, bool):
141 if isinstance(parent, ConfigNode):
142 en = en and parent.enabled()
146 def config_error(self, *args):
147 return ConfigNodeError(self, *args)
149 def update_status(self, status):
150 NodeProperty.Status[self] = status
153 class GroupNode(ConfigNode):
154 def __init__(self, env, filename, name):
155 super().__init__(env, filename, name)
159 def add_child(self, node):
160 if node._name in self._subnodes:
163 NodeProperty.Parent[node] = self
164 self._subnodes[node._name] = node
167 class TermNode(ConfigNode):
168 def __init__(self, env, filename, name):
169 super().__init__(env, filename, name)
172 result = super().update()
174 if NodeProperty.Readonly[self]:
175 NodeProperty.Value[self] = result
177 elif NodeProperty.Value[self] is None:
178 NodeProperty.Value[self] = result
182 def sanity_check(self):
183 val = NodeProperty.Value[self]
184 value_typer = NodeProperty.Type[self]
185 value_typer.ensure_type(self, val)
187 super().sanity_check()