|
|
@@ -1,7 +1,9 @@
|
|
|
# vim: set fileencoding=utf-8 :
|
|
|
+# XXX Used in doctests (we should use them in the __main__ section below too).
|
|
|
+from node import Leaf, Node
|
|
|
|
|
|
|
|
|
-def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
+def generate_graph(root, separator=' ', verbose=False):
|
|
|
"""
|
|
|
Return a text-based, utf-8 graph of a tree-like structure. Each tree node
|
|
|
is represented by a length-2 list. If a node has an attribute called
|
|
|
@@ -11,17 +13,17 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
>>> l0, l1 = Leaf(0), Leaf(1)
|
|
|
>>> n0 = Node('+', l0, l1)
|
|
|
>>> l2 = Leaf(2)
|
|
|
- >>> print generate_graph(n0, Node)
|
|
|
+ >>> print generate_graph(n0)
|
|
|
+
|
|
|
╭┴╮
|
|
|
0 1
|
|
|
>>> n1 = Node('-', l2)
|
|
|
- >>> print generate_graph(n1, Node)
|
|
|
+ >>> print generate_graph(n1)
|
|
|
-
|
|
|
│
|
|
|
2
|
|
|
>>> n2 = Node('*', n0, n1)
|
|
|
- >>> print generate_graph(n2, Node)
|
|
|
+ >>> print generate_graph(n2)
|
|
|
*
|
|
|
╭─┴╮
|
|
|
+ -
|
|
|
@@ -30,45 +32,55 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
"""
|
|
|
|
|
|
node_width = {}
|
|
|
+ node_middle = {}
|
|
|
|
|
|
separator_len = len(separator)
|
|
|
|
|
|
- def calculate_width(node):
|
|
|
+ def calculate_node_sizes(node):
|
|
|
title = node.title()
|
|
|
title_len = len(title)
|
|
|
|
|
|
# Leaves do not have children and therefore the length of its title is
|
|
|
# the width of the leaf.
|
|
|
- if not isinstance(node, node_type):
|
|
|
+ if not isinstance(node, Node):
|
|
|
node_width[node] = title_len
|
|
|
+ node_middle[node] = int(title_len / 2)
|
|
|
return title_len
|
|
|
|
|
|
+ node_len = len(node)
|
|
|
+
|
|
|
width = 0
|
|
|
+ middle = 0
|
|
|
+ middle_pos = int(node_len / 2)
|
|
|
+
|
|
|
+ for i, child in enumerate(node):
|
|
|
+ tmp = calculate_node_sizes(child)
|
|
|
+
|
|
|
+ if i < middle_pos:
|
|
|
+ middle += tmp
|
|
|
|
|
|
- for child in node:
|
|
|
- width += calculate_width(child)
|
|
|
+ width += tmp
|
|
|
+
|
|
|
+ middle += max(middle_pos - int(node_len % 2 == 0), 0) * separator_len
|
|
|
|
|
|
# Add a separator between each node (thus n - 1 separators).
|
|
|
width += separator_len * (len(node) - 1)
|
|
|
|
|
|
- # Odd numbers of children should be minus 1, since the middle child
|
|
|
- # can be placed directly below the parent. With even numbers, there
|
|
|
- # is no middle child, so the space below the parent cannot be used.
|
|
|
- #if len(node) % 2 == 1:
|
|
|
- # width -= 1
|
|
|
-
|
|
|
# If the title of the node is wider than the sum of its children, the
|
|
|
# title's width should be used.
|
|
|
- width = max(title_len, width)
|
|
|
+ if title_len > width:
|
|
|
+ width = title_len
|
|
|
+ middle = int(title_len / 2)
|
|
|
|
|
|
# print 'width of "%s":' % node.title(), width
|
|
|
|
|
|
node_width[node] = width
|
|
|
+ node_middle[node] = middle
|
|
|
|
|
|
return width
|
|
|
|
|
|
def format_lines(node):
|
|
|
- if not isinstance(node, node_type):
|
|
|
+ if not isinstance(node, Node):
|
|
|
# Leaf titles do not need to be centered, since the parent will
|
|
|
# center those lines. And if there are no parents, the entire graph
|
|
|
# consists of a single leaf, so in that case there still is no
|
|
|
@@ -98,11 +110,13 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
|
|
|
line_width = node_width[node]
|
|
|
|
|
|
+ # TODO: substitute box_widths with node_width
|
|
|
box_widths = [len(lines[0]) for lines in child_lines]
|
|
|
node_len = len(node)
|
|
|
middle_node = int(node_len / 2)
|
|
|
- middle = sum([box_widths[i] for i in range(middle_node)]) \
|
|
|
- + max(middle_node - int(node_len % 2 == 0), 0) * separator_len
|
|
|
+ #middle = sum([box_widths[i] for i in range(middle_node)]) \
|
|
|
+ # + max(middle_node - int(node_len % 2 == 0), 0) * separator_len
|
|
|
+ middle = node_middle[node]
|
|
|
|
|
|
title_line = center_text(node.title(), line_width, middle)
|
|
|
|
|
|
@@ -142,7 +156,7 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
marker = tsplit_dn_sign
|
|
|
|
|
|
edge_line += center_text(marker, box_widths[i],
|
|
|
- middle=0, left=dash_sign)
|
|
|
+ middle=0, left=dash_sign)
|
|
|
else:
|
|
|
# n-ary operators (n is even)
|
|
|
edge_line = ''
|
|
|
@@ -157,7 +171,8 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
if i < middle_node:
|
|
|
marker = (left_sign if i == 0 else tsplit_dn_sign)
|
|
|
edge_line += center_text(marker, box_widths[i],
|
|
|
- middle=0, right=dash_sign)
|
|
|
+ middle=0,
|
|
|
+ right=dash_sign)
|
|
|
else:
|
|
|
if i == node_len - 1:
|
|
|
marker = right_sign
|
|
|
@@ -165,7 +180,8 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
marker = tsplit_dn_sign
|
|
|
|
|
|
edge_line += center_text(marker, box_widths[i],
|
|
|
- middle=0, left=dash_sign)
|
|
|
+ middle=0,
|
|
|
+ left=dash_sign)
|
|
|
|
|
|
try:
|
|
|
assert len(title_line) == len(edge_line)
|
|
|
@@ -182,12 +198,12 @@ def generate_graph(root, node_type, separator=' ', verbose=False):
|
|
|
# Add the line of this node before all child lines.
|
|
|
return [title_line, edge_line] + result
|
|
|
|
|
|
- calculate_width(root)
|
|
|
+ calculate_node_sizes(root)
|
|
|
|
|
|
#if verbose:
|
|
|
- # print '------- node_width ---------'
|
|
|
+ # print '------- node_{width,middle} ---------'
|
|
|
# for node, width in node_width.iteritems():
|
|
|
- # print node.title(), 'width:', width
|
|
|
+ # print node.title(), 'width:', width, 'middle:', node_middle[node]
|
|
|
|
|
|
lines = format_lines(root)
|
|
|
|
|
|
@@ -201,6 +217,19 @@ def center_text(text, width, middle=0, left=' ', right=' '):
|
|
|
│
|
|
|
>>> center_text('+', 15, 11)
|
|
|
' + '
|
|
|
+ >>> left = center_text('╭'.decode('utf-8'), 11, 8, right='─'.decode('utf-8'))
|
|
|
+ >>> len(left) == 11
|
|
|
+ True
|
|
|
+ >>> right = center_text('╮'.decode('utf-8'), 3, 2, left='─'.decode('utf-8'))
|
|
|
+ >>> len(right) == 3
|
|
|
+ True
|
|
|
+ >>> edge_line = left + '┴'.decode('utf-8') + right
|
|
|
+ >>> len(edge_line) == 15
|
|
|
+ True
|
|
|
+ >>> title_line = center_text('+', 15, 11)
|
|
|
+ >>> print '|%s|\\n|%s|' % (title_line, edge_line.encode('utf-8'))
|
|
|
+ | + |
|
|
|
+ | ╭──┴──╮|
|
|
|
"""
|
|
|
text_len = len(text)
|
|
|
text_mid = text_len / 2
|