d968e671a44dad098ce1fbcf65115f3591621138
[lunaix-os.git] / lunaix-os / scripts / build-tools / lcfg2 / common.py
1 import ast
2
3 from lib.utils  import SourceLogger, Schema
4
5 class NodeProperty:
6     class PropertyAccessor:
7         def __init__(self, key):
8             self.__key = key
9         def __getitem__(self, node):
10             return node.get_property(self.__key)
11         def __setitem__(self, node, value):
12             node.set_property(self.__key, value)
13         def __delitem__(self, node):
14             node.set_property(self.__key, None)
15
16     Token       = PropertyAccessor("$token")
17     Value       = PropertyAccessor("$value")
18     Type        = PropertyAccessor("$type")
19     Enabled     = PropertyAccessor("$enabled")
20     Status      = PropertyAccessor("$status")
21     Dependency  = PropertyAccessor("$depends")
22     Linkage     = PropertyAccessor("$linkage")
23     Hidden      = PropertyAccessor("hidden")
24     Parent      = PropertyAccessor("parent")
25     Label       = PropertyAccessor("label")
26     Readonly    = PropertyAccessor("readonly")
27     HelpText    = PropertyAccessor("help")
28
29 class ConfigNodeError(Exception):
30     def __init__(self, node, *args):
31         super().__init__(*args)
32
33         self.__node = node
34         self.__msg = " ".join([str(x) for x in args])
35
36     def __str__(self):
37         node = self.__node
38         tok: ast.stmt = NodeProperty.Token[node]
39         return (
40             f"{node._filename}:{tok.lineno}:{tok.col_offset}:" +
41             f" fatal error: {node._name}: {self.__msg}"
42         )
43
44
45 class ValueTypeConstrain:
46     TypeMap = {
47         "str": str,
48         "int": int,
49         "bool": bool,
50     }
51
52     BinOpOr = Schema(ast.BinOp, op=ast.BitOr)
53
54     def __init__(self, node, rettype):
55         self.__node = node
56         self.__raw = rettype
57         
58         if isinstance(rettype, ast.Expr):
59             value = rettype.value
60         else:
61             value = rettype
62         
63         if isinstance(value, ast.Constant):
64             self.schema = Schema(value.value)
65         
66         elif isinstance(value, ast.Name):
67             self.schema = Schema(self.__parse_type(value.id))
68         
69         elif isinstance(value, ast.BinOp):
70             unions = self.__parse_binop(value)
71             self.schema = Schema(Schema.Union(*unions))
72         
73         else:
74             raise Exception(
75                 f"unsupported type definition: {ast.unparse(rettype)}")
76
77     def __parse_type(self, type):
78         if type not in ValueTypeConstrain.TypeMap:
79             raise Exception(f"unknown type: {type}")
80         
81         return ValueTypeConstrain.TypeMap[type]
82     
83     def __parse_binop(self, oproot):
84         if isinstance(oproot, ast.Constant):
85             return [oproot.value]
86         
87         if ValueTypeConstrain.BinOpOr != oproot:
88             SourceLogger.warn(
89                 self.__node, self.__raw, 
90                 "only OR is allowed. Ignoring...")
91             return []
92         
93         return self.__parse_binop(oproot.left) \
94              + self.__parse_binop(oproot.right)
95     
96     def check_type(self, value):
97         return self.schema.match(value)
98     
99     def ensure_type(self, node, val):
100         if self.check_type(val):
101            return
102
103         raise node.config_error(
104                 f"unmatched type:",
105                 f"expect: '{self.schema}',",
106                 f"got: '{val}' ({type(val)})") 
107
108 class NodeDependency:
109     class SimpleWalker(ast.NodeVisitor):
110         def __init__(self, deps):
111             super().__init__()
112             self.__deps = deps
113         
114         def visit_Name(self, node):
115             self.__deps._names.append(node.id)
116     
117     def __init__(self, nodes, expr: ast.expr):
118         self._names = []
119         self._expr  = ast.unparse(expr)
120
121         expr = ast.fix_missing_locations(expr)
122         self.__exec  = compile(ast.Expression(expr), "", mode='eval')
123
124         NodeDependency.SimpleWalker(self).visit(expr)
125
126     def evaluate(self, value_tables) -> bool:        
127         return eval(self.__exec, value_tables)
128     
129     @staticmethod
130     def try_create(node):
131         expr = NodeProperty.Dependency[node]
132         if not isinstance(expr, ast.expr):
133             return
134         
135         dep = NodeDependency(node, expr)
136         NodeProperty.Dependency[node] = dep
137
138
139 class NodeInverseDependency:
140     def __init__(self):
141         self.__map = {}
142
143     def add_linkage(self, name, expr):
144         if name not in self.__map:
145             self.__map[name] = [expr]
146         else:
147             self.__map[name].append(expr)
148
149     def linkages(self):
150         return self.__map.items()