a86ab52852079cbe5f801f9f7bde8f2363350156
[lunaix-os.git] / lunaix-os / scripts / build-tools / lcfg2 / rewriter.py
1 import ast
2
3 from lib.utils  import Schema
4 from .lazy       import Lazy
5 from .common     import NodeProperty, NodeInverseDependency
6
7 class RewriteRule:
8     MaybeBuiltin = Schema(
9                         ast.Call, 
10                             func=Schema(ast.Name),
11                             args=[ast.expr])
12
13     WhenTogglerItem = Schema(
14                             ast.Compare,
15                                 left=ast.Name,
16                                 ops=[Schema.Union(ast.Is, ast.IsNot)],
17                                 comparators=[ast.Constant])
18     
19     WhenToggler = Schema(
20                     Schema.Union(
21                         WhenTogglerItem,
22                         Schema(ast.BoolOp, 
23                             op=ast.And, 
24                             values=Schema.List(WhenTogglerItem))))
25
26 class ConfigNodeASTRewriter(ast.NodeTransformer):
27     
28
29     def __init__(self, cfg_node):
30         super().__init__()
31
32         self.__cfg_node = cfg_node
33
34         self.__when_epxr = None
35
36     def __put_linkage(self, to_node, item):
37         node = self.__cfg_node._env.get_node(to_node)
38         link = NodeProperty.Linkage[node]
39         
40         if not link:
41             link = NodeInverseDependency()
42             NodeProperty.Linkage[node] = link
43         
44         link.add_linkage(self.__cfg_node._name, ast.unparse(item))
45
46     def __subscript_accessor(self, name, ctx, token):
47         return ast.Subscript(
48             value=ast.Name("__lzLut__", ctx=ast.Load()),
49             slice=ast.Constant(name),
50             ctx=ctx,
51         )
52
53     def __gen_accessor(self, orig):
54         key = Lazy.from_astn(self.__cfg_node, orig)
55         if not key:
56             return self.generic_visit(orig)
57         
58         return self.__subscript_accessor(key, orig.ctx, orig)
59     
60     def __gen_dependency(self, node):
61         cfgn = self.__cfg_node
62         dep_expr = NodeProperty.Dependency[cfgn]
63         if dep_expr is None:
64             NodeProperty.Dependency[cfgn] = node
65             return
66
67         if not isinstance(dep_expr, ast.expr):
68             raise cfgn.config_error(
69                 f"invalid dependency state: {dep_expr}")
70         
71         dep_expr = ast.BoolOp(ast.And(), [dep_expr, node])
72         NodeProperty.Dependency[cfgn] = dep_expr
73
74     def __gen_when_expr(self, node):
75         and_list = []
76         cfgn = self.__cfg_node
77
78         if RewriteRule.WhenToggler != node:
79             raise cfgn.config_error(
80                 f"when(...): invalid expression: {ast.unparse(node)}")
81
82         if RewriteRule.WhenTogglerItem == node:
83             and_list.append(node)
84         else:
85             and_list += node.values
86         
87         for i in range(len(and_list)):
88             item = and_list[i]
89             operator = item.ops[0]
90
91             if RewriteRule.WhenTogglerItem != item:
92                 raise cfgn.config_error(
93                         f"when(...): non-trivial subclause : {ast.unparse(node)}")
94             
95             name = Lazy.from_type(cfgn, Lazy.NodeValue, item.left.id)
96             acc = self.__subscript_accessor(name, ast.Load(), node)
97             
98             self.__put_linkage(item.left.id, item)
99
100             if isinstance(operator, ast.Is):
101                 operator = ast.Eq()
102             else:
103                 operator = ast.NotEq()
104             
105             item.left = acc
106             item.ops  = [operator]
107             
108         
109         current = ast.BoolOp(
110                         op=ast.And(), 
111                         values=[ast.Constant(True), *and_list])
112         
113         expr = self.__when_epxr
114         if expr:
115             assert isinstance(expr, ast.expr)
116             current = ast.BoolOp(op=ast.Or(), values=[expr, current])
117
118         self.__when_epxr = current
119
120     def visit_Attribute(self, node):
121         return self.__gen_accessor(node)
122     
123     def visit_Expr(self, node):
124         val = node.value
125         
126         if RewriteRule.MaybeBuiltin != val:
127             return self.generic_visit(node)
128         
129         # Process marker functions
130         name = val.func.id
131         if name == "require":
132             self.__gen_dependency(val.args[0])
133         elif name == "when":
134             self.__gen_when_expr(val.args[0])
135         else:
136             return self.generic_visit(node)
137         
138         return None
139     
140     def visit_Return(self, node):
141         if self.__when_epxr:
142             return None
143         return self.generic_visit(node)
144     
145     def visit_Is(self, node):
146         return ast.Eq()
147     
148     def rewrite(self, node):
149         assert isinstance(node, ast.Module)
150         node = self.visit(node)
151
152         expr = self.__when_epxr
153         if not expr:
154             return node
155
156         node.body.append(ast.Return(expr, lineno=0, col_offset=0))
157         return node