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 Hidden = PropertyAccessor("hidden")
23 Parent = PropertyAccessor("parent")
24 Label = PropertyAccessor("label")
25 Readonly = PropertyAccessor("readonly")
26 HelpText = PropertyAccessor("help")
28 class ConfigNodeError(Exception):
29 def __init__(self, node, *args):
30 super().__init__(*args)
33 self.__msg = " ".join([str(x) for x in args])
37 tok: ast.stmt = NodeProperty.Token[node]
39 f"{node._filename}:{tok.lineno}:{tok.col_offset}:" +
40 f" fatal error: {node._name}: {self.__msg}"
44 class ValueTypeConstrain:
51 BinOpOr = Schema(ast.BinOp, op=ast.BitOr)
53 def __init__(self, node, rettype):
57 if isinstance(rettype, ast.Expr):
62 if isinstance(value, ast.Constant):
63 self.schema = Schema(value.value)
65 elif isinstance(value, ast.Name):
66 self.schema = Schema(self.__parse_type(value.id))
68 elif isinstance(value, ast.BinOp):
69 unions = self.__parse_binop(value)
70 self.schema = Schema(Schema.Union(*unions))
74 f"unsupported type definition: {ast.unparse(rettype)}")
76 def __parse_type(self, type):
77 if type not in ValueTypeConstrain.TypeMap:
78 raise Exception(f"unknown type: {type}")
80 return ValueTypeConstrain.TypeMap[type]
82 def __parse_binop(self, oproot):
83 if isinstance(oproot, ast.Constant):
86 if ValueTypeConstrain.BinOpOr != oproot:
88 self.__node, self.__raw,
89 "only OR is allowed. Ignoring...")
92 return self.__parse_binop(oproot.left) \
93 + self.__parse_binop(oproot.right)
95 def check_type(self, value):
96 return self.schema.match(value)
98 def ensure_type(self, node, val):
99 if self.check_type(val):
102 raise node.config_error(
104 f"expect: '{self.schema}',",
105 f"got: '{val}' ({type(val)})")
107 class NodeDependency:
108 class SimpleWalker(ast.NodeVisitor):
109 def __init__(self, deps):
113 def visit_Name(self, node):
114 self.__deps._names.append(node.id)
116 def __init__(self, nodes, expr: ast.expr):
118 self._expr = ast.unparse(expr)
120 expr = ast.fix_missing_locations(expr)
121 self.__exec = compile(ast.Expression(expr), "", mode='eval')
123 NodeDependency.SimpleWalker(self).visit(expr)
125 def evaluate(self, value_tables) -> bool:
126 return eval(self.__exec, value_tables)
129 def try_create(node):
130 expr = NodeProperty.Dependency[node]
131 if not isinstance(expr, ast.expr):
134 dep = NodeDependency(node, expr)
135 NodeProperty.Dependency[node] = dep