From 214c222002e3a8061d42d615a3514e4b33410a02 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Wed, 1 Nov 2017 13:26:10 +0100 Subject: [PATCH] Add StringTree and friends Signed-off-by: Jan Lindemann --- tools/python/jwutils/stree/Makefile | 4 + tools/python/jwutils/stree/StringTree.py | 150 +++++++++++++++++++++++ tools/python/jwutils/stree/serdes.py | 39 ++++++ 3 files changed, 193 insertions(+) create mode 100644 tools/python/jwutils/stree/Makefile create mode 100644 tools/python/jwutils/stree/StringTree.py create mode 100644 tools/python/jwutils/stree/serdes.py diff --git a/tools/python/jwutils/stree/Makefile b/tools/python/jwutils/stree/Makefile new file mode 100644 index 0000000..b77eb55 --- /dev/null +++ b/tools/python/jwutils/stree/Makefile @@ -0,0 +1,4 @@ +TOPDIR = ../../../.. + +include $(TOPDIR)/make/proj.mk +include $(MODDIR)/make/py-mod.mk diff --git a/tools/python/jwutils/stree/StringTree.py b/tools/python/jwutils/stree/StringTree.py new file mode 100644 index 0000000..244371b --- /dev/null +++ b/tools/python/jwutils/stree/StringTree.py @@ -0,0 +1,150 @@ +from collections import OrderedDict +from jwutils.log import * + +def quote(s): + if is_quoted(s): + return s + s = s.strip() + if len(s) > 0: + if s[0] == '"': + return "'" + s + "'" + return '"' + s + '"' + +def is_quoted(s): + if isinstance(s, StringTree): + return False + s = s.strip() + if len(s) < 2: + return False + d = s[0] + if d == s[-1] and d in [ '"', "'" ]: + return True + return False + +def cleanup_string(s): + if isinstance(s, StringTree): + return s + s = s.strip() + if is_quoted(s): + return s[1:-1] + return s + +class StringTree: # export + + def __init__(self, path, content): + slog(DEBUG, "ctor, path =", path, "content =", content) + self.children = OrderedDict() + self.content = None + self.__set(path, content) + assert(hasattr(self, "content")) + #assert self.content is not None + + # root (content = [ symbols ]) + # symbols (content = [ regex ]) + # regex ( content ='[ \n\t\r]+' ) + + # root (content = root, children = [ symbols ]) + # symbols (content = symbols, children = [ regex ]) + # regex ( content = regex, children = [ '[ \n\t\r]+' ] ) + # '[ \n\t\r]+)' ( content = '\n\t\r]+)', children = [] ) + + def __set(self, path_, content): + logcontent = "not-yet" + if hasattr(self, "content"): + logcontent = self.content + slog(DEBUG, "+ setting", content, "at path \"" + str(path_) + "\" to", logcontent) + if content is not None: + content = cleanup_string(content) + if path_ is None: + self.content = content + return self + path = cleanup_string(path_) + components = path.split('.') + l = len(components) + if len(path) == 0 or l == 0: + assert self.content is None + self.content = content + #self.children[content] = StringTree(None, content) + return self + + assert self.content is not None + + nibble = components[0] + rest = '.'.join(components[1:]) + if nibble not in self.children: + self.children[nibble] = StringTree('', content=nibble) + if l > 1: + assert len(rest) > 0 + return self.children[nibble].__set(rest, content=content) + if content is not None: + self.children[nibble].children[content] = StringTree('', content=content) + return self.children[nibble] + + def __getitem__(self, path): + r = self.get(path) + if r is None: + raise KeyError(path) + return r + + def __setitem__(self, key, value): + return self.__set(key, value) + + def __dump(self, prio, indent=0, caller=None, *args): + if caller is None: + caller = get_caller_pos(1) + slog(prio, (' ' * indent) + str(self.content), caller=caller) + indent += 2 + for name, child in self.children.iteritems(): + child.__dump(prio, indent=indent, caller=caller) + + def keys(self): + return self.children.keys() + + def iteritems(self): + return self.children.iteritems() + + def set_content(self, content): + if content is None: + raise Exception("Tried to set none content") + content = cleanup_string(content) + if len(content) == 0: + raise Exception("Tried to set empty content") + self.content = content + + def add(self, path, content = None): + slog(DEBUG, "adding", content, "at", path, "to", self.content) + return self.__set(path, content) + + def get(self, path_): + slog(DEBUG, "looking for", path_, "in", self.content) + path = cleanup_string(path_) + if len(path) == 0: + slog(DEBUG, "returning myself") + return self + if is_quoted(path_): + if not path in self.children.keys(): + return None + return self.children[path] + components = path.split('.') + if len(components) == 0: + return self + name = cleanup_string(components[0]) + if not hasattr(self, "children"): + return None + if not name in self.children.keys(): + slog(DEBUG, "Name \"" + name + "\" is not in children of", self.content) + for child in self.children: + slog(DEBUG, "child = ", child) + return None + relpath = '.'.join(components[1:]) + return self.children[name].get(relpath) + + def value(self): + if len(self.children) == 0: + return None + return next(reversed(self.children)) + + def dump(self, prio, caller=None, *args): + if caller is None: + caller = get_caller_pos(1) + self.__dump(prio, indent=0, caller=caller) diff --git a/tools/python/jwutils/stree/serdes.py b/tools/python/jwutils/stree/serdes.py new file mode 100644 index 0000000..953fc53 --- /dev/null +++ b/tools/python/jwutils/stree/serdes.py @@ -0,0 +1,39 @@ +from StringTree import * +from jwutils.log import * + +def parse(s): # export + slog(DEBUG, "parsing", s) + root = StringTree('', content='root') + sec = '' + for line in s.splitlines(): + slog(DEBUG, "line=", line) + line = line.strip() + if not len(line): + continue + if line[0] == '[': + if line[-1] == ']': + sec = line[1:-1] + elif line[-1] == '[': + if len(sec): + sec += '.' + sec += line[1:-1] + else: + raise Exception("failed to parse section line", line) + if root.get(sec) is None: + root.add(sec) + continue + elif line[0] == ']': + assert(len(sec) > 0) + sec = '.'.join(sec.split('.')[0:-1]) + continue + assignment = line.split('=') + if len(assignment) != 2: + raise Exception("failed to parse assignment", line) + slog(DEBUG, "sec=", sec, "assignment=", assignment) + root.add(sec + '.' + cleanup_string(assignment[0]), quote(assignment[1])) + return root + +def read(path): # export + with open(path, 'r') as infile: + s = infile.read() + return parse(s)