| from .node import NodeVisitor, ValueNode, ListNode, BinaryExpressionNode |
| from .parser import atoms, precedence |
| |
| atom_names = {v:"@%s" % k for (k,v) in atoms.items()} |
| |
| named_escapes = {"\a", "\b", "\f", "\n", "\r", "\t", "\v"} |
| |
| def escape(string, extras=""): |
| # Assumes input bytes are either UTF8 bytes or unicode. |
| rv = "" |
| for c in string: |
| if c in named_escapes: |
| rv += c.encode("unicode_escape") |
| elif c == "\\": |
| rv += "\\\\" |
| elif c < '\x20': |
| rv += "\\x%02x" % ord(c) |
| elif c in extras: |
| rv += "\\" + c |
| else: |
| rv += c |
| if isinstance(rv, unicode): |
| return rv.encode("utf8") |
| else: |
| return rv |
| |
| |
| class ManifestSerializer(NodeVisitor): |
| def __init__(self, skip_empty_data=False): |
| self.skip_empty_data = skip_empty_data |
| |
| def serialize(self, root): |
| self.indent = 2 |
| rv = "\n".join(self.visit(root)) |
| if not rv: |
| return rv |
| if rv[-1] != "\n": |
| rv = rv + "\n" |
| return rv |
| |
| def visit_DataNode(self, node): |
| rv = [] |
| if not self.skip_empty_data or node.children: |
| if node.data: |
| rv.append("[%s]" % escape(node.data, extras="]")) |
| indent = self.indent * " " |
| else: |
| indent = "" |
| |
| for child in node.children: |
| rv.extend("%s%s" % (indent if item else "", item) for item in self.visit(child)) |
| |
| if node.parent: |
| rv.append("") |
| |
| return rv |
| |
| def visit_KeyValueNode(self, node): |
| rv = [escape(node.data, ":") + ":"] |
| indent = " " * self.indent |
| |
| if len(node.children) == 1 and isinstance(node.children[0], (ValueNode, ListNode)): |
| rv[0] += " %s" % self.visit(node.children[0])[0] |
| else: |
| for child in node.children: |
| rv.append(indent + self.visit(child)[0]) |
| |
| return rv |
| |
| def visit_ListNode(self, node): |
| rv = ["["] |
| rv.extend(", ".join(self.visit(child)[0] for child in node.children)) |
| rv.append("]") |
| return ["".join(rv)] |
| |
| def visit_ValueNode(self, node): |
| if not isinstance(node.data, (str, unicode)): |
| data = unicode(node.data) |
| else: |
| data = node.data |
| if "#" in data or (isinstance(node.parent, ListNode) and |
| ("," in data or "]" in data)): |
| if "\"" in data: |
| quote = "'" |
| else: |
| quote = "\"" |
| else: |
| quote = "" |
| return [quote + escape(data, extras=quote) + quote] |
| |
| def visit_AtomNode(self, node): |
| return [atom_names[node.data]] |
| |
| def visit_ConditionalNode(self, node): |
| return ["if %s: %s" % tuple(self.visit(item)[0] for item in node.children)] |
| |
| def visit_StringNode(self, node): |
| rv = ["\"%s\"" % escape(node.data, extras="\"")] |
| for child in node.children: |
| rv[0] += self.visit(child)[0] |
| return rv |
| |
| def visit_NumberNode(self, node): |
| return [str(node.data)] |
| |
| def visit_VariableNode(self, node): |
| rv = escape(node.data) |
| for child in node.children: |
| rv += self.visit(child) |
| return [rv] |
| |
| def visit_IndexNode(self, node): |
| assert len(node.children) == 1 |
| return ["[%s]" % self.visit(node.children[0])[0]] |
| |
| def visit_UnaryExpressionNode(self, node): |
| children = [] |
| for child in node.children: |
| child_str = self.visit(child)[0] |
| if isinstance(child, BinaryExpressionNode): |
| child_str = "(%s)" % child_str |
| children.append(child_str) |
| return [" ".join(children)] |
| |
| def visit_BinaryExpressionNode(self, node): |
| assert len(node.children) == 3 |
| children = [] |
| for child_index in [1, 0, 2]: |
| child = node.children[child_index] |
| child_str = self.visit(child)[0] |
| if (isinstance(child, BinaryExpressionNode) and |
| precedence(node.children[0]) < precedence(child.children[0])): |
| child_str = "(%s)" % child_str |
| children.append(child_str) |
| return [" ".join(children)] |
| |
| def visit_UnaryOperatorNode(self, node): |
| return [str(node.data)] |
| |
| def visit_BinaryOperatorNode(self, node): |
| return [str(node.data)] |
| |
| |
| def serialize(tree, *args, **kwargs): |
| s = ManifestSerializer(*args, **kwargs) |
| return s.serialize(tree) |