bracket_notation 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. data/CHANGELOG +8 -0
  2. data/Manifest +17 -1
  3. data/README.rdoc +31 -7
  4. data/Rakefile +2 -1
  5. data/bracket_notation.gemspec +9 -6
  6. data/init.rb +1 -1
  7. data/lib/bracket_notation.rb +3 -1
  8. data/lib/bracket_notation/evaluator.rb +2 -1
  9. data/lib/bracket_notation/expressions.rb +1 -1
  10. data/lib/bracket_notation/expressions/expression.rb +1 -1
  11. data/lib/bracket_notation/expressions/identifier.rb +1 -1
  12. data/lib/bracket_notation/expressions/terminal.rb +1 -1
  13. data/lib/bracket_notation/geometry.rb +31 -0
  14. data/lib/bracket_notation/geometry/point.rb +69 -0
  15. data/lib/bracket_notation/geometry/rect.rb +64 -0
  16. data/lib/bracket_notation/geometry/size.rb +69 -0
  17. data/lib/bracket_notation/parser.rb +1 -1
  18. data/lib/bracket_notation/scanner.rb +1 -1
  19. data/lib/bracket_notation/token.rb +1 -1
  20. data/lib/bracket_notation/version.rb +3 -3
  21. data/lib/bracket_notation/views.rb +33 -0
  22. data/lib/bracket_notation/views/branch.rb +40 -0
  23. data/lib/bracket_notation/views/leaf.rb +34 -0
  24. data/lib/bracket_notation/views/node.rb +152 -0
  25. data/lib/bracket_notation/views/tree.rb +181 -0
  26. data/test/functional/evaluator_test.rb +1 -1
  27. data/test/functional/node_test.rb +132 -0
  28. data/test/functional/parser_test.rb +1 -1
  29. data/test/functional/scanner_test.rb +1 -1
  30. data/test/functional/tree_test.rb +67 -0
  31. data/test/integration/layout_test.rb +151 -0
  32. data/test/integration/parsing_test.rb +1 -1
  33. data/test/test_helper.rb +1 -1
  34. data/test/unit/expression_test.rb +1 -1
  35. data/test/unit/point_test.rb +69 -0
  36. data/test/unit/rect_test.rb +58 -0
  37. data/test/unit/size_test.rb +69 -0
  38. data/test/unit/token_test.rb +1 -1
  39. metadata +48 -7
@@ -23,7 +23,7 @@
23
23
  # and small portions of his code have been incorporated in the parser.
24
24
  #
25
25
  # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
- # Copyright:: Copyright (c) 2010 Cody Brimhall
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
27
  # License:: Distributed under the terms of the GNU General Public License, v. 3
28
28
 
29
29
  module BracketNotation # :nodoc:
@@ -23,7 +23,7 @@
23
23
  # and small portions of his code have been incorporated in the parser.
24
24
  #
25
25
  # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
- # Copyright:: Copyright (c) 2010 Cody Brimhall
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
27
  # License:: Distributed under the terms of the GNU General Public License, v. 3
28
28
 
29
29
  module BracketNotation # :nodoc:
@@ -23,7 +23,7 @@
23
23
  # and small portions of his code have been incorporated in the parser.
24
24
  #
25
25
  # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
- # Copyright:: Copyright (c) 2010 Cody Brimhall
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
27
  # License:: Distributed under the terms of the GNU General Public License, v. 3
28
28
 
29
29
  module BracketNotation # :nodoc:
@@ -23,14 +23,14 @@
23
23
  # and small portions of his code have been incorporated in the parser.
24
24
  #
25
25
  # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
- # Copyright:: Copyright (c) 2010 Cody Brimhall
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
27
  # License:: Distributed under the terms of the GNU General Public License, v. 3
28
28
 
29
29
  module BracketNotation # :nodoc:
30
30
  module Version # :nodoc:
31
31
  MAJOR = 1
32
- MINOR = 0
33
- MAINT = 5
32
+ MINOR = 1
33
+ MAINT = 0
34
34
 
35
35
  # Returns the current version string.
36
36
  def self.to_s;
@@ -0,0 +1,33 @@
1
+ #--
2
+ # This file is part of BracketNotation.
3
+ #
4
+ # BracketNotation is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # BracketNotation is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with BracketNotation. If not, see <http://www.gnu.org/licenses/>.
16
+ #++
17
+ # BracketNotation is a parser for generating syntax trees from sentences
18
+ # annotated with the kind of bracket notation that is commonly used by
19
+ # linguists. The result is a tree structure with nodes that describe the phrases
20
+ # and constituents of the sentence.
21
+ #
22
+ # BracketNotation was inspired by Yoichiro Hasebe's RSyntaxTree[http://yohasebe.com/rsyntaxtree/],
23
+ # and small portions of his code have been incorporated in the parser.
24
+ #
25
+ # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
+ # License:: Distributed under the terms of the GNU General Public License, v. 3
28
+
29
+ require 'rmagick'
30
+ require 'bracket_notation/views/node'
31
+ require 'bracket_notation/views/branch'
32
+ require 'bracket_notation/views/leaf'
33
+ require 'bracket_notation/views/tree'
@@ -0,0 +1,40 @@
1
+ #--
2
+ # This file is part of BracketNotation.
3
+ #
4
+ # BracketNotation is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # BracketNotation is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with BracketNotation. If not, see <http://www.gnu.org/licenses/>.
16
+ #++
17
+ # BracketNotation is a parser for generating syntax trees from sentences
18
+ # annotated with the kind of bracket notation that is commonly used by
19
+ # linguists. The result is a tree structure with nodes that describe the phrases
20
+ # and constituents of the sentence.
21
+ #
22
+ # BracketNotation was inspired by Yoichiro Hasebe's RSyntaxTree[http://yohasebe.com/rsyntaxtree/],
23
+ # and small portions of his code have been incorporated in the parser.
24
+ #
25
+ # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
+ # License:: Distributed under the terms of the GNU General Public License, v. 3
28
+
29
+ module BracketNotation # :nodoc:
30
+ module View # :nodoc:
31
+ class Branch < Node
32
+ attr_accessor :roll_up
33
+
34
+ def initialize(tree, content)
35
+ super
36
+ @roll_up = false
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,34 @@
1
+ #--
2
+ # This file is part of BracketNotation.
3
+ #
4
+ # BracketNotation is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # BracketNotation is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with BracketNotation. If not, see <http://www.gnu.org/licenses/>.
16
+ #++
17
+ # BracketNotation is a parser for generating syntax trees from sentences
18
+ # annotated with the kind of bracket notation that is commonly used by
19
+ # linguists. The result is a tree structure with nodes that describe the phrases
20
+ # and constituents of the sentence.
21
+ #
22
+ # BracketNotation was inspired by Yoichiro Hasebe's RSyntaxTree[http://yohasebe.com/rsyntaxtree/],
23
+ # and small portions of his code have been incorporated in the parser.
24
+ #
25
+ # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
+ # License:: Distributed under the terms of the GNU General Public License, v. 3
28
+
29
+ module BracketNotation # :nodoc:
30
+ module View # :nodoc:
31
+ class Leaf < Node
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,152 @@
1
+ #--
2
+ # This file is part of BracketNotation.
3
+ #
4
+ # BracketNotation is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # BracketNotation is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with BracketNotation. If not, see <http://www.gnu.org/licenses/>.
16
+ #++
17
+ # BracketNotation is a parser for generating syntax trees from sentences
18
+ # annotated with the kind of bracket notation that is commonly used by
19
+ # linguists. The result is a tree structure with nodes that describe the phrases
20
+ # and constituents of the sentence.
21
+ #
22
+ # BracketNotation was inspired by Yoichiro Hasebe's RSyntaxTree[http://yohasebe.com/rsyntaxtree/],
23
+ # and small portions of his code have been incorporated in the parser.
24
+ #
25
+ # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
+ # License:: Distributed under the terms of the GNU General Public License, v. 3
28
+
29
+ require 'bracket_notation/geometry'
30
+
31
+ module BracketNotation # :nodoc:
32
+ module View # :nodoc:
33
+ class Node
34
+ attr_accessor :tree, :content, :parent, :children, :rect, :align_to_grid
35
+
36
+ def initialize(tree, content)
37
+ @tree = tree
38
+ @content = content
39
+ @parent = nil
40
+ @children = []
41
+ @rect = BracketNotation::Geometry::Rect.new
42
+ @align_to_grid = true
43
+ end
44
+
45
+ # Custom setter for the node rect. If @align_to_grid is true, rect co-
46
+ # ordinates and dimensions will be rounded to the nearest integer.
47
+ def rect=(rvalue)
48
+ return if @rect == rvalue
49
+
50
+ @rect = if @align_to_grid
51
+ adjusted_origin = BracketNotation::Geometry::Point.new(rvalue.origin.x.round, rvalue.origin.y.round)
52
+ adjusted_size = BracketNotation::Geometry::Size.new(rvalue.size.width.round, rvalue.size.height.round)
53
+ BracketNotation::Geometry::Rect.new(adjusted_origin, adjusted_size)
54
+ else
55
+ rvalue
56
+ end
57
+ end
58
+
59
+ # Return the node's left sibling, or nil if the node is the leftmost child of
60
+ # its parent.
61
+ def left_sibling
62
+ return nil if @parent.nil?
63
+
64
+ left_sibling = nil
65
+ self_index = @parent.children.index(self)
66
+ left_sibling = @parent.children[self_index - 1] if self_index > 0
67
+
68
+ return left_sibling
69
+ end
70
+
71
+ # Return the node's right sibling, or nil if the node is the rightmost child
72
+ # of its parent.
73
+ def right_sibling
74
+ return nil if @parent.nil?
75
+
76
+ self_index = @parent.children.index(self)
77
+ right_sibling = @parent.children[self_index + 1]
78
+
79
+ return right_sibling
80
+ end
81
+
82
+ # Return the list of nodes leading from the current node to the tree root,
83
+ # starting with the current node's parent.
84
+ def ancestors
85
+ node_list = []
86
+ next_up = self
87
+ node_list << next_up while next_up = next_up.parent
88
+
89
+ return node_list
90
+ end
91
+
92
+ # Return the dimensions of the rect that contains the nod and all of its
93
+ # descendants
94
+ def subtree_size
95
+ return @rect.size if kind_of? Leaf or @children.count == 0
96
+
97
+ new_subtree_size = BracketNotation::Geometry::Size.new(0, @rect.size.height)
98
+ subtree_widths = []
99
+ subtree_heights = []
100
+
101
+ @children.each do |child|
102
+ child_subtree_size = child.subtree_size
103
+ new_subtree_size = new_subtree_size.size_by_adding_to_width(child_subtree_size.width)
104
+ subtree_heights << child_subtree_size.height
105
+ end
106
+
107
+ new_subtree_size = new_subtree_size.size_by_adding_to_width_and_height(@tree.node_h_margin * (children.count - 1), @tree.node_v_margin + subtree_heights.sort.last)
108
+ return BracketNotation::Geometry::Size.new(@rect.size.width > new_subtree_size.width ? @rect.size.width : new_subtree_size.width, new_subtree_size.height)
109
+ end
110
+
111
+ # Return the coordinates of the node's top left corner.
112
+ def corner_top_left
113
+ @rect.origin
114
+ end
115
+
116
+ # Return the coordinates of the node's top right corner.
117
+ def corner_top_right
118
+ BracketNotation::Geometry::Point.new(@rect.origin.x + @rect.size.width, @rect.origin.y)
119
+ end
120
+
121
+ # Return the coordinates of the node's bottom right corner.
122
+ def corner_bottom_right
123
+ BracketNotation::Geometry::Point.new(@rect.origin.x + @rect.size.width, @rect.origin.y + @rect.size.height)
124
+ end
125
+
126
+ # Return the coordinates of the node's bottom left corner.
127
+ def corner_bottom_left
128
+ BracketNotation::Geometry::Point.new(@rect.origin.x, @rect.origin.y + @rect.size.height)
129
+ end
130
+
131
+ # Return the coordinates of the middle of the node's top side.
132
+ def side_middle_top
133
+ BracketNotation::Geometry::Point.new(@rect.origin.x + (@rect.size.width / 2), @rect.origin.y)
134
+ end
135
+
136
+ # Return the coordinates of the middle of the node's right side.
137
+ def side_middle_right
138
+ BracketNotation::Geometry::Point.new(@rect.origin.x + @rect.size.width, @rect.origin.y + (@rect.size.height / 2))
139
+ end
140
+
141
+ # Return the coordinates of the middle of the node's bottom side.
142
+ def side_middle_bottom
143
+ BracketNotation::Geometry::Point.new(@rect.origin.x + (@rect.size.width / 2), @rect.origin.y + @rect.size.height)
144
+ end
145
+
146
+ # Return the coordinates of the middle of the node's left side.
147
+ def side_middle_left
148
+ BracketNotation::Geometry::Point.new(@rect.origin.x, @rect.origin.y + (@rect.size.height / 2))
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,181 @@
1
+ #--
2
+ # This file is part of BracketNotation.
3
+ #
4
+ # BracketNotation is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # BracketNotation is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with BracketNotation. If not, see <http://www.gnu.org/licenses/>.
16
+ #++
17
+ # BracketNotation is a parser for generating syntax trees from sentences
18
+ # annotated with the kind of bracket notation that is commonly used by
19
+ # linguists. The result is a tree structure with nodes that describe the phrases
20
+ # and constituents of the sentence.
21
+ #
22
+ # BracketNotation was inspired by Yoichiro Hasebe's RSyntaxTree[http://yohasebe.com/rsyntaxtree/],
23
+ # and small portions of his code have been incorporated in the parser.
24
+ #
25
+ # Author:: Cody Brimhall (mailto:brimhall@somuchwit.com)
26
+ # Copyright:: Copyright (c) 2010-2011 Cody Brimhall
27
+ # License:: Distributed under the terms of the GNU General Public License, v. 3
28
+
29
+ module BracketNotation # :nodoc:
30
+ module View # :nodoc:
31
+ class Tree
32
+ attr_accessor :input, :root, :font_name, :font_point_size, :tree_padding, :node_h_margin, :node_v_margin, :node_padding, :color_bg, :color_fg, :color_line, :color_branch, :color_leaf
33
+
34
+ # Throws :prune, which is caught by #traverse, signaling that method to cease
35
+ # visiting nodes on the current branch.
36
+ def self.prune
37
+ throw(:prune)
38
+ end
39
+
40
+ def initialize(input)
41
+ @input = input
42
+ @font_name = "Helvetica"
43
+ @font_point_size = 40
44
+ @tree_padding = 20
45
+ @node_h_margin = 50
46
+ @node_v_margin = 30
47
+ @node_padding = 10
48
+ @color_bg = "white"
49
+ @color_fg = "black"
50
+ @color_line = "red"
51
+ @color_branch = "blue"
52
+ @color_leaf = "green"
53
+ end
54
+
55
+ # Traverse the tree, passing each visited node and the current depth to the
56
+ # given block.
57
+ #
58
+ # Options are:
59
+ # * <tt>:depth</tt> - The starting value of the depth counter. The default is
60
+ # +0+.
61
+ # * <tt>:order</tt> - The traversal order to follow. Allowed values are:
62
+ # +:preorder+ (or +:pre+), +:postorder+ (or +:post+), +:breadthfirst+ (or
63
+ # +:breadth+). The default is +:preorder+.
64
+ # * <tt>:root</tt> - The root node of the subtree to be traversed. The default
65
+ # is the root node of the entire tree.
66
+ #
67
+ def traverse(options = {}, &block)
68
+ options[:order] ||= :preorder
69
+ options[:depth] ||= 0
70
+ options[:root] ||= @root
71
+
72
+ return if @root.nil?
73
+
74
+ if [:breadth, :breadthfirst].include? options[:order]
75
+ node_queue = [options[:root]]
76
+
77
+ while node = node_queue.shift
78
+ yield node, node.ancestors.length
79
+ node_queue += node.children
80
+ end
81
+ else
82
+ catch(:prune) do
83
+ case options[:order]
84
+ when :pre, :preorder
85
+ yield options[:root], options[:depth]
86
+ options[:root].children.each {|child| traverse({:order => :pre, :root => child, :depth => options[:depth] + 1}, &block) }
87
+ when :post, :postorder
88
+ options[:root].children.each {|child| traverse({:order => :post, :root => child, :depth => options[:depth] + 1}, &block) }
89
+ yield options[:root], options[:depth]
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # Computes the node tree layout, setting the correct origin and dimensions
96
+ # for each node.
97
+ def compute_layout
98
+ layout_nodes
99
+
100
+ old_root_origin_x = @root.rect.origin.x
101
+ new_root_origin_x = ((@root.subtree_size.width + (@tree_padding * 2)) / 2) - (@root.rect.size.width / 2)
102
+ delta = new_root_origin_x - old_root_origin_x
103
+
104
+ traverse {|node, depth| node.rect = BracketNotation::Geometry::Rect.new(node.rect.origin.point_by_adding_to_x(delta), node.rect.size) }
105
+ end
106
+
107
+ def inspect
108
+ leaf_content = []
109
+ traverse {|node, depth| leaf_content << node.content if node.kind_of? Leaf }
110
+ return "#{@input} >> \"#{leaf_content.join(" ")}\""
111
+ end
112
+
113
+ alias :to_s :inspect
114
+
115
+ def pretty_print
116
+ traverse do |node, depth|
117
+ depth.times { print "--" }
118
+ print " " if depth > 0
119
+ puts node.to_s
120
+ end
121
+ end
122
+
123
+ private
124
+
125
+ # Walks the node tree, setting origins and dimensions
126
+ def layout_nodes(node = @root, depth = 0)
127
+ compute_node_dimensions(node)
128
+ compute_node_origin_y(node)
129
+
130
+ node.children.each {|child| layout_nodes(child, depth + 1) }
131
+
132
+ compute_subtree_origin_x(node)
133
+ end
134
+
135
+ def compute_node_dimensions(node)
136
+ background = Magick::Image.new(500, 250)
137
+ gc = Magick::Draw.new
138
+ font = @font_name
139
+ pointsize = @font_point_size
140
+
141
+ # Write the node content in a scratch image to calculate the text metrics
142
+ gc.annotate(background, 0, 0, 0, 0, node.content) do |gc|
143
+ gc.font = font
144
+ gc.pointsize = pointsize
145
+ gc.gravity = Magick::CenterGravity
146
+ gc.stroke = "none"
147
+ end
148
+
149
+ metrics = gc.get_type_metrics(background, node.content)
150
+ node.rect = BracketNotation::Geometry::Rect.new(node.rect.origin, BracketNotation::Geometry::Size.new(metrics.width, @font_point_size + (@node_padding * 2)))
151
+ end
152
+
153
+ def compute_node_origin_y(node)
154
+ adjusted_origin_y = node.parent.nil? ? @tree_padding : node.parent.rect.origin.y + node.parent.rect.size.height + @node_v_margin
155
+ new_origin = BracketNotation::Geometry::Point.new(node.rect.origin.x, adjusted_origin_y)
156
+ node.rect = BracketNotation::Geometry::Rect.new(BracketNotation::Geometry::Point.new(node.rect.origin.x, adjusted_origin_y), node.rect.size)
157
+ end
158
+
159
+ def compute_subtree_origin_x(node)
160
+ return if node.kind_of? Leaf or node.children.count == 0
161
+
162
+ node_middle = node.side_middle_top.x
163
+ children_subtree_widths = node.children.collect {|child| child.subtree_size.width}
164
+ max_subtree_width = children_subtree_widths.sort.last
165
+ subtree_width = (max_subtree_width * children_subtree_widths.count) + (@node_h_margin * (node.children.count - 1))
166
+ subtree_origin_x = node_middle - (subtree_width / 2)
167
+
168
+ node.children.each do |child|
169
+ child_subtree_width = max_subtree_width
170
+ child_node_middle = child.side_middle_top.x
171
+ old_subtree_origin_x = child_node_middle - (child_subtree_width / 2)
172
+ delta = subtree_origin_x - old_subtree_origin_x
173
+
174
+ traverse(:root => child) {|node, depth| node.rect = BracketNotation::Geometry::Rect.new(node.rect.origin.point_by_adding_to_x(delta), node.rect.size) }
175
+
176
+ subtree_origin_x += max_subtree_width + @node_h_margin
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end