3 from lib.utils import SourceLogger, Schema
6 class PropertyAccessor:
7 def __init__(self, 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)
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")
29 class ConfigNodeError(Exception):
30 def __init__(self, node, *args):
31 super().__init__(*args)
34 self.__msg = " ".join([str(x) for x in args])
38 tok: ast.stmt = NodeProperty.Token[node]
40 f"{node._filename}:{tok.lineno}:{tok.col_offset}:" +
41 f" fatal error: {node._name}: {self.__msg}"
45 class ValueTypeConstrain:
52 BinOpOr = Schema(ast.BinOp, op=ast.BitOr)
54 def __init__(self, node, rettype):
58 if isinstance(rettype, ast.Expr):
63 if isinstance(value, ast.Constant):
64 self.schema = Schema(value.value)
66 elif isinstance(value, ast.Name):
67 self.schema = Schema(self.__parse_type(value.id))
69 elif isinstance(value, ast.BinOp):
70 unions = self.__parse_binop(value)
71 self.schema = Schema(Schema.Union(*unions))
75 f"unsupported type definition: {ast.unparse(rettype)}")
77 def __parse_type(self, type):
78 if type not in ValueTypeConstrain.TypeMap:
79 raise Exception(f"unknown type: {type}")
81 return ValueTypeConstrain.TypeMap[type]
83 def __parse_binop(self, oproot):
84 if isinstance(oproot, ast.Constant):
87 if ValueTypeConstrain.BinOpOr != oproot:
89 self.__node, self.__raw,
90 "only OR is allowed. Ignoring...")
93 return self.__parse_binop(oproot.left) \
94 + self.__parse_binop(oproot.right)
96 def check_type(self, value):
97 return self.schema.match(value)
99 def ensure_type(self, node, val):
100 if self.check_type(val):
103 raise node.config_error(
105 f"expect: '{self.schema}',",
106 f"got: '{val}' ({type(val)})")
108 class NodeDependency:
109 class SimpleWalker(ast.NodeVisitor):
110 def __init__(self, deps):
114 def visit_Name(self, node):
115 self.__deps._names.append(node.id)
117 def __init__(self, nodes, expr: ast.expr):
119 self._expr = ast.unparse(expr)
121 expr = ast.fix_missing_locations(expr)
122 self.__exec = compile(ast.Expression(expr), "", mode='eval')
124 NodeDependency.SimpleWalker(self).visit(expr)
126 def evaluate(self, value_tables) -> bool:
127 return eval(self.__exec, value_tables)
130 def try_create(node):
131 expr = NodeProperty.Dependency[node]
132 if not isinstance(expr, ast.expr):
135 dep = NodeDependency(node, expr)
136 NodeProperty.Dependency[node] = dep
139 class NodeInverseDependency:
143 def add_linkage(self, name, expr):
144 if name not in self.__map:
145 self.__map[name] = [expr]
147 self.__map[name].append(expr)
150 return self.__map.items()