bracket_notation 1.0.5 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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