Rewrite the lunabuild toolchain with enhanced feature (#60)
[lunaix-os.git] / lunaix-os / scripts / build-tools / lcfg2 / lazy.py
1 import ast
2
3 from .common import NodeProperty
4 from lib.utils import Schema, SourceLogger
5
6 class LazyLookup:
7     def __init__(self):
8         self.__tab = {}
9     
10     def put(self, lazy):
11         self.__tab[lazy.get_key()] = lazy
12
13     def get(self, key):
14         return self.__tab[key] if key in self.__tab else None
15     
16     def __setitem__(self, key, val):
17         lz = self.__tab[key]
18         lz.resolve_set(val)
19
20     def __getitem__(self, key):
21         lz = self.__tab[key]
22         return lz.resolve_get()
23
24 class Lazy:
25     NodeValue     = 'val'
26
27     LazyTypes = Schema.Union(NodeValue)
28     Syntax  = Schema(ast.Attribute, attr=LazyTypes, value=ast.Name)
29
30     def __init__(self, source, type, target, env):
31         self.target = target
32         self.type   = type
33         self.env    = env
34         self.source = source
35
36     def __resolve_type(self):
37         if self.type == Lazy.NodeValue:
38             return NodeProperty.Value
39         return None
40         
41     def resolve_get(self):
42         node = self.env.get_node(self.target)
43
44         accessor = self.__resolve_type()
45         if not accessor:
46             return None
47         
48         status = NodeProperty.Status[node]
49         if status == "Updating":
50             tok = NodeProperty.Token[self.source]
51             SourceLogger.warn(self.source, tok, 
52                     f"cyclic dependency detected: {self.source._name} <-> {self.target}." + 
53                     f"Reusing cached value, maybe staled.")
54         else:
55             node.update()
56
57         return accessor[node] if accessor else None
58     
59     def resolve_set(self, val):
60         node = self.env.get_node(self.target)
61         accessor = self.__resolve_type()
62
63         if NodeProperty.Readonly[node]:
64             raise self.source.config_error(
65                 f"{self.target} is readonly")
66
67         if accessor:
68             accessor[node] = val
69         else:
70             raise self.source.config_error(
71                 f"invalid type {self.type} for {self.target}")
72
73     def get_key(self):
74         return Lazy.get_key_from(self.type, self.target)
75     
76     @staticmethod
77     def get_key_from(type, target):
78         return f"{type}${target}"
79     
80     @staticmethod
81     def from_astn(cfgnode, astn):
82         if Lazy.Syntax != astn:
83             return None
84         
85         type_ = astn.attr
86         target = astn.value.id
87        
88         return Lazy.from_type(cfgnode, type_, target)
89     
90     @staticmethod
91     def from_type(cfgnode, type_, target):   
92         key = Lazy.get_key_from(type_, target)
93         lz = cfgnode._lazy_table.get(key)
94         
95         if lz:
96             return key
97
98         lz = Lazy(cfgnode, type_, target, cfgnode._env)
99         cfgnode._lazy_table.put(lz)
100
101         return key
102