X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/b1644f824d7f4989a94b8a752aadee26cae25069..d15268ba6eadf89a38087995ff407f50418485fb:/lunaix-os/scripts/build-tools/lcfg2/common.py diff --git a/lunaix-os/scripts/build-tools/lcfg2/common.py b/lunaix-os/scripts/build-tools/lcfg2/common.py new file mode 100644 index 0000000..94cdcea --- /dev/null +++ b/lunaix-os/scripts/build-tools/lcfg2/common.py @@ -0,0 +1,135 @@ +import ast + +from lib.utils import SourceLogger, Schema + +class NodeProperty: + class PropertyAccessor: + def __init__(self, key): + self.__key = key + def __getitem__(self, node): + return node.get_property(self.__key) + def __setitem__(self, node, value): + node.set_property(self.__key, value) + def __delitem__(self, node): + node.set_property(self.__key, None) + + Token = PropertyAccessor("$token") + Value = PropertyAccessor("$value") + Type = PropertyAccessor("$type") + Enabled = PropertyAccessor("$enabled") + Status = PropertyAccessor("$status") + Dependency = PropertyAccessor("$depends") + Hidden = PropertyAccessor("hidden") + Parent = PropertyAccessor("parent") + Label = PropertyAccessor("label") + Readonly = PropertyAccessor("readonly") + HelpText = PropertyAccessor("help") + +class ConfigNodeError(Exception): + def __init__(self, node, *args): + super().__init__(*args) + + self.__node = node + self.__msg = " ".join([str(x) for x in args]) + + def __str__(self): + node = self.__node + tok: ast.stmt = NodeProperty.Token[node] + return ( + f"{node._filename}:{tok.lineno}:{tok.col_offset}:" + + f" fatal error: {node._name}: {self.__msg}" + ) + + +class ValueTypeConstrain: + TypeMap = { + "str": str, + "int": int, + "bool": bool, + } + + BinOpOr = Schema(ast.BinOp, op=ast.BitOr) + + def __init__(self, node, rettype): + self.__node = node + self.__raw = rettype + + if isinstance(rettype, ast.Expr): + value = rettype.value + else: + value = rettype + + if isinstance(value, ast.Constant): + self.schema = Schema(value.value) + + elif isinstance(value, ast.Name): + self.schema = Schema(self.__parse_type(value.id)) + + elif isinstance(value, ast.BinOp): + unions = self.__parse_binop(value) + self.schema = Schema(Schema.Union(*unions)) + + else: + raise Exception( + f"unsupported type definition: {ast.unparse(rettype)}") + + def __parse_type(self, type): + if type not in ValueTypeConstrain.TypeMap: + raise Exception(f"unknown type: {type}") + + return ValueTypeConstrain.TypeMap[type] + + def __parse_binop(self, oproot): + if isinstance(oproot, ast.Constant): + return [oproot.value] + + if ValueTypeConstrain.BinOpOr != oproot: + SourceLogger.warn( + self.__node, self.__raw, + "only OR is allowed. Ignoring...") + return [] + + return self.__parse_binop(oproot.left) \ + + self.__parse_binop(oproot.right) + + def check_type(self, value): + return self.schema.match(value) + + def ensure_type(self, node, val): + if self.check_type(val): + return + + raise node.config_error( + f"unmatched type:", + f"expect: '{self.schema}',", + f"got: '{val}' ({type(val)})") + +class NodeDependency: + class SimpleWalker(ast.NodeVisitor): + def __init__(self, deps): + super().__init__() + self.__deps = deps + + def visit_Name(self, node): + self.__deps._names.append(node.id) + + def __init__(self, nodes, expr: ast.expr): + self._names = [] + self._expr = ast.unparse(expr) + + expr = ast.fix_missing_locations(expr) + self.__exec = compile(ast.Expression(expr), "", mode='eval') + + NodeDependency.SimpleWalker(self).visit(expr) + + def evaluate(self, value_tables) -> bool: + return eval(self.__exec, value_tables) + + @staticmethod + def try_create(node): + expr = NodeProperty.Dependency[node] + if not isinstance(expr, ast.expr): + return + + dep = NodeDependency(node, expr) + NodeProperty.Dependency[node] = dep \ No newline at end of file