Rewrite the lunabuild toolchain with enhanced feature (#60)
[lunaix-os.git] / lunaix-os / scripts / build-tools / lcfg2 / nodes.py
1 import ast
2
3 from lib.utils   import SourceLogger, Schema
4 from .common     import NodeProperty, ConfigNodeError, NodeDependency
5 from .lazy       import LazyLookup
6 from .rewriter   import ConfigNodeASTRewriter
7
8 from .ast_validator import NodeValidator
9 from .rules import SyntaxRule
10
11 validator = NodeValidator(SyntaxRule())
12
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")
18     SetParent   = Schema(
19                     ast.NamedExpr, 
20                         target=Schema(ast.Name, id="parent"), 
21                         value=Schema(ast.Name))
22     SetProperty = Schema(
23                     ast.NamedExpr,
24                         target=Schema(ast.Name), 
25                         value=Schema(ast.Name))
26
27
28 class ConfigNode:
29     def __init__(self, env, filename, name):
30         self.__props = {}
31         self.__exec = None
32         self._env = env
33         self._filename = filename
34         self._lazy_table = LazyLookup()
35
36         self._name = name
37         
38         NodeProperty.Enabled[self]  = True
39         NodeProperty.Hidden[self]   = False
40         NodeProperty.Readonly[self] = False
41         NodeProperty.Status[self]   = "Empty"
42
43     def set_node_body(self, ast_nodes):
44         self.__exec = ast.Module(ast_nodes, [])
45         
46     def apply_node_body(self, rewriter = ConfigNodeASTRewriter):
47         assert isinstance(self.__exec, ast.Module)
48
49         new_ast = self.__exec
50         validator.validate(self, new_ast)
51         
52         new_ast = rewriter(self).rewrite(new_ast)
53         NodeDependency.try_create(self)
54
55         fn_name = f"__fn_{self._name}"
56         args = ast.arguments([], [], None, [], [], None, [])
57         module = ast.Module([
58             ast.FunctionDef(fn_name, args, new_ast.body, []),
59             ast.Assign(
60                 [ast.Name("_result", ctx=ast.Store())],
61                 ast.Call(ast.Name(fn_name,ctx=ast.Load()), [], [])
62             )
63         ], [])
64
65         module = ast.fix_missing_locations(module)
66         self.__exec = compile(module, self._filename, mode='exec')
67
68     
69     def apply_decorator(self, decor):
70         if ConfigDecorator.Label == decor:
71             NodeProperty.Label[self] = str(decor.value)
72         
73         elif ConfigDecorator.Readonly == decor:
74             NodeProperty.Readonly[self] = True
75         
76         elif ConfigDecorator.Hidden == decor:
77             NodeProperty.Hidden[self] = True  
78         
79         elif ConfigDecorator.SetParent == decor:
80             NodeProperty.Parent[self] = decor.value.id
81
82         elif ConfigDecorator.Flag == decor:
83             NodeProperty.Hidden[self] = True
84             NodeProperty.Readonly[self] = True
85
86         elif ConfigDecorator.SetProperty == decor:
87             self.set_property(decor.target.id, decor.value.id)
88
89         else:
90             fname = self._filename
91             line  = decor.lineno
92             col   = decor.col_offset
93             msg   = f"unknown decorator: {ast.unparse(decor)}"
94             msg   = SourceLogger.fmt_warning(fname, line, col, msg)
95             print(msg)
96
97     def update(self):
98         self.update_status("Updating")
99         
100         try:
101             env = self._env
102             local = {}
103             globl = {
104                 **env.context(),
105                 "__lzLut__": self._lazy_table
106             }
107
108             exec(self.__exec, globl, local)
109             self.update_status("Latest")
110         
111             return local["_result"]
112         
113         except ConfigNodeError as e:
114             self.update_status("Error")
115             raise e
116         
117         except Exception as e:
118             self.update_status("Error")
119             raise self.config_error(e)
120         
121     def sanity_check(self):
122         pass
123
124     def get_property(self, key):
125         return self.__props[key] if key in self.__props else None
126     
127     def set_property(self, key, value):
128         if value is None:
129             del self.__props[key]
130         else:
131             self.__props[key] = value
132
133     def enabled(self):
134         val = NodeProperty.Value[self]
135         en  = NodeProperty.Enabled[self]
136         parent = NodeProperty.Parent[self]
137
138         if isinstance(val, bool):
139             en = en and val
140         
141         if isinstance(parent, ConfigNode):
142             en = en and parent.enabled()
143         
144         return en
145     
146     def config_error(self, *args):
147         return ConfigNodeError(self, *args)
148     
149     def update_status(self, status):
150         NodeProperty.Status[self] = status
151
152
153 class GroupNode(ConfigNode):
154     def __init__(self, env, filename, name):
155         super().__init__(env, filename, name)
156         
157         self._subnodes = {}
158
159     def add_child(self, node):
160         if node._name in self._subnodes:
161             return
162         
163         NodeProperty.Parent[node] = self
164         self._subnodes[node._name] = node
165
166
167 class TermNode(ConfigNode):
168     def __init__(self, env, filename, name):
169         super().__init__(env, filename, name)
170
171     def update(self):
172         result = super().update()
173         
174         if NodeProperty.Readonly[self]:
175             NodeProperty.Value[self] = result
176         
177         elif NodeProperty.Value[self] is None:
178             NodeProperty.Value[self] = result
179         
180         return result
181     
182     def sanity_check(self):
183         val = NodeProperty.Value[self]
184         value_typer = NodeProperty.Type[self]
185         value_typer.ensure_type(self, val)
186         
187         super().sanity_check()
188