treetop 1.4.5 → 1.4.7

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 (53) hide show
  1. data/README.md +44 -20
  2. data/lib/treetop/compiler/metagrammar.rb +126 -33
  3. data/lib/treetop/compiler/metagrammar.treetop +46 -42
  4. data/lib/treetop/compiler/node_classes/repetition.rb +39 -5
  5. data/lib/treetop/version.rb +1 -1
  6. data/spec/compiler/and_predicate_spec.rb +36 -0
  7. data/spec/compiler/anything_symbol_spec.rb +44 -0
  8. data/spec/compiler/character_class_spec.rb +276 -0
  9. data/spec/compiler/choice_spec.rb +80 -0
  10. data/spec/compiler/circular_compilation_spec.rb +30 -0
  11. data/spec/compiler/failure_propagation_functional_spec.rb +21 -0
  12. data/spec/compiler/grammar_compiler_spec.rb +91 -0
  13. data/spec/compiler/grammar_spec.rb +41 -0
  14. data/spec/compiler/multibyte_chars_spec.rb +38 -0
  15. data/spec/compiler/nonterminal_symbol_spec.rb +40 -0
  16. data/spec/compiler/not_predicate_spec.rb +38 -0
  17. data/spec/compiler/occurrence_range_spec.rb +191 -0
  18. data/spec/compiler/one_or_more_spec.rb +35 -0
  19. data/spec/compiler/optional_spec.rb +37 -0
  20. data/spec/compiler/parenthesized_expression_spec.rb +19 -0
  21. data/spec/compiler/parsing_rule_spec.rb +61 -0
  22. data/spec/compiler/repeated_subrule_spec.rb +29 -0
  23. data/spec/compiler/semantic_predicate_spec.rb +175 -0
  24. data/spec/compiler/sequence_spec.rb +115 -0
  25. data/spec/compiler/terminal_spec.rb +81 -0
  26. data/spec/compiler/terminal_symbol_spec.rb +37 -0
  27. data/spec/compiler/test_grammar.treetop +7 -0
  28. data/spec/compiler/test_grammar.tt +7 -0
  29. data/spec/compiler/test_grammar_do.treetop +7 -0
  30. data/spec/compiler/tt_compiler_spec.rb +215 -0
  31. data/spec/compiler/zero_or_more_spec.rb +56 -0
  32. data/spec/composition/a.treetop +11 -0
  33. data/spec/composition/b.treetop +11 -0
  34. data/spec/composition/c.treetop +10 -0
  35. data/spec/composition/d.treetop +10 -0
  36. data/spec/composition/f.treetop +17 -0
  37. data/spec/composition/grammar_composition_spec.rb +40 -0
  38. data/spec/composition/subfolder/e_includes_c.treetop +15 -0
  39. data/spec/ruby_extensions/string_spec.rb +32 -0
  40. data/spec/runtime/compiled_parser_spec.rb +101 -0
  41. data/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
  42. data/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
  43. data/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
  44. data/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
  45. data/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
  46. data/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
  47. data/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
  48. data/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +163 -0
  49. data/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
  50. data/spec/runtime/syntax_node_spec.rb +77 -0
  51. data/spec/spec_helper.rb +110 -0
  52. data/treetop.gemspec +18 -0
  53. metadata +70 -9
@@ -0,0 +1,58 @@
1
+ #require 'runtime/interval_skip_list/spec_helper'
2
+ #
3
+ #MAX_INTERVAL = 100000
4
+ #
5
+ #describe IntervalSkipList do
6
+ # describe "#next_node_height" do
7
+ # attr_reader :list
8
+ #
9
+ # before do
10
+ # @list = IntervalSkipList.new
11
+ # end
12
+ #
13
+ # it "returns a number between 1 and the max_height of the list" do
14
+ # height = list.next_node_height
15
+ # height.should be <= list.max_height
16
+ # height.should be > 0
17
+ # end
18
+ # end
19
+ #
20
+ # describe "a list with 1000 random intervals" do
21
+ # attr_reader :list, :inserted_ranges
22
+ #
23
+ # before do
24
+ # @list = IntervalSkipList.new
25
+ # @inserted_ranges = []
26
+ #
27
+ # 0.upto(10) do |i|
28
+ # first, last = [rand(MAX_INTERVAL), rand(MAX_INTERVAL)].sort
29
+ # range = first..last
30
+ # list.insert(range, i)
31
+ # inserted_ranges.push(range)
32
+ # end
33
+ # end
34
+ #
35
+ # it "functions correctly for stabbing queries" do
36
+ # 10000.times do
37
+ # n = rand(MAX_INTERVAL)
38
+ # ranges = list.containing(n).sort
39
+ #
40
+ # expected_ranges = []
41
+ # inserted_ranges.each_with_index do |range,i|
42
+ # expected_ranges.push(i) if n > range.first && n < range.last
43
+ # end
44
+ # expected_ranges.sort!
45
+ # unless ranges == expected_ranges
46
+ # puts "N = #{n}"
47
+ # puts "Expected: " + expected_ranges.inspect
48
+ # puts "Actual: " + ranges.inspect
49
+ # expected_ranges.size.should be <= ranges.size
50
+ # puts "Missing containers: #{(expected_ranges.map {|o| o.object_id} - ranges.map {|o| o.object_id}).inspect}"
51
+ # puts "Unexpected containers: #{(ranges.map {|o| o.object_id} - expected_ranges.map {|o| o.object_id}).inspect}"
52
+ # puts "Inserted Ranges: #{inserted_ranges.inspect}"
53
+ # puts "Expected Ranges: #{expected_ranges.map {|i| inserted_ranges[i]}.inspect}"
54
+ # end
55
+ # end
56
+ # end
57
+ # end
58
+ #end
@@ -0,0 +1,23 @@
1
+ describe "the palindromic fixture", :shared => true do
2
+ attr_reader :list, :node
3
+ include IntervalSkipListSpecHelper
4
+
5
+ before do
6
+ @list = IntervalSkipList.new
7
+ end
8
+
9
+ it_should_behave_like "#next_node_height is deterministic"
10
+ def expected_node_heights
11
+ [3, 2, 1, 3, 1, 2, 3]
12
+ end
13
+
14
+ before do
15
+ list.insert(1..3, :a)
16
+ list.insert(1..5, :b)
17
+ list.insert(1..7, :c)
18
+ list.insert(1..9, :d)
19
+ list.insert(1..11, :e)
20
+ list.insert(1..13, :f)
21
+ list.insert(5..13, :g)
22
+ end
23
+ end
@@ -0,0 +1,163 @@
1
+ require 'runtime/interval_skip_list/spec_helper'
2
+
3
+ describe "The palindromic fixture" do
4
+ it_should_behave_like "the palindromic fixture"
5
+
6
+ describe " #nodes" do
7
+ describe "[0]" do
8
+ before do
9
+ @node = list.nodes[0]
10
+ end
11
+
12
+ it "has a key of 1 and a height of 3" do
13
+ node.key.should == 1
14
+ node.height.should == 3
15
+ end
16
+
17
+ it "has :c, :d, :e, and :f as its only forward markers at level 2" do
18
+ node.forward_markers[2].should have_markers(:c, :d, :e, :f)
19
+ end
20
+
21
+ it "has :a, :b as its only forward markers at level 1" do
22
+ node.forward_markers[1].should have_markers(:a, :b)
23
+ end
24
+
25
+ it "has no forward markers at level 0" do
26
+ node.forward_markers[0].should be_empty
27
+ end
28
+
29
+ it "has no markers" do
30
+ node.markers.should be_empty
31
+ end
32
+ end
33
+
34
+ describe "[1]" do
35
+ before do
36
+ @node = list.nodes[1]
37
+ end
38
+
39
+ it "has a key of 3 and a height of 2" do
40
+ node.key.should == 3
41
+ node.height.should == 2
42
+ end
43
+
44
+ it "has no forward markers at level 1" do
45
+ node.forward_markers[1].should be_empty
46
+ end
47
+
48
+ it "has :b as its only forward marker at level 0" do
49
+ node.forward_markers[0].should have_marker(:b)
50
+ end
51
+
52
+ it "has :a and :b as its only markers" do
53
+ node.markers.should have_markers(:a, :b)
54
+ end
55
+ end
56
+
57
+ describe "[2]" do
58
+ before do
59
+ @node = list.nodes[2]
60
+ end
61
+
62
+ it "has a key of 5 and a height of 1" do
63
+ node.key.should == 5
64
+ node.height.should == 1
65
+ end
66
+
67
+ it "has :g as its only forward marker at level 0" do
68
+ node.forward_markers[0].should have_marker(:g)
69
+ end
70
+
71
+ it "has :b as its only marker" do
72
+ node.markers.should have_marker(:b)
73
+ end
74
+ end
75
+
76
+ describe "[3]" do
77
+ before do
78
+ @node = list.nodes[3]
79
+ end
80
+
81
+ it "has a key of 7 and a height of 3" do
82
+ node.key.should == 7
83
+ node.height.should == 3
84
+ end
85
+
86
+ it "has :f and :g as its only forward markers at level 2" do
87
+ node.forward_markers[2].should have_markers(:f, :g)
88
+ end
89
+
90
+ it "has :e as its only forward markers at level 1" do
91
+ node.forward_markers[1].should have_marker(:e)
92
+ end
93
+
94
+ it "has :d as its only forward marker at level 0" do
95
+ node.forward_markers[0].should have_markers(:d)
96
+ end
97
+
98
+ it "has :c, :d, :e, :f and :g as its only markers" do
99
+ node.markers.should have_markers(:c, :d, :e, :f, :g)
100
+ end
101
+ end
102
+
103
+ describe "[4]" do
104
+ before do
105
+ @node = list.nodes[4]
106
+ end
107
+
108
+ it "has a key of 9 and a height of 1" do
109
+ node.key.should == 9
110
+ node.height.should == 1
111
+ end
112
+
113
+ it "has no forward markers at any level" do
114
+ node.forward_markers[0].should be_empty
115
+ end
116
+
117
+ it "has :d as its only marker" do
118
+ node.markers.should have_markers(:d)
119
+ end
120
+ end
121
+
122
+ describe "[5]" do
123
+ before do
124
+ @node = list.nodes[5]
125
+ end
126
+
127
+ it "has a key of 11 and a height of 2" do
128
+ node.key.should == 11
129
+ node.height.should == 2
130
+ end
131
+
132
+ it "has no forward markers at any level" do
133
+ node.forward_markers[0].should be_empty
134
+ node.forward_markers[1].should be_empty
135
+ end
136
+
137
+ it "has :e as its only marker" do
138
+ node.markers.should have_markers(:e)
139
+ end
140
+ end
141
+
142
+ describe "[6]" do
143
+ before do
144
+ @node = list.nodes[6]
145
+ end
146
+
147
+ it "has a key of 13 and a height of 3" do
148
+ node.key.should == 13
149
+ node.height.should == 3
150
+ end
151
+
152
+ it "has no forward markers at any level" do
153
+ node.forward_markers[0].should be_empty
154
+ node.forward_markers[1].should be_empty
155
+ node.forward_markers[2].should be_empty
156
+ end
157
+
158
+ it "has :f and :g as its only markers" do
159
+ node.markers.should have_markers(:f, :g)
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ class IntervalSkipList
4
+ public :insert_node, :delete_node, :nodes, :head, :next_node_height
5
+ end
6
+
7
+ describe "#next_node_height is deterministic", :shared => true do
8
+ before do
9
+ node_heights = expected_node_heights.dup
10
+ stub(list).next_node_height { node_heights.shift }
11
+ end
12
+ end
13
+
14
+ module IntervalSkipListSpecHelper
15
+ def contain_marker(marker)
16
+ ContainMarkers.new(list, [marker])
17
+ end
18
+
19
+ def contain_markers(*markers)
20
+ ContainMarkers.new(list, markers)
21
+ end
22
+
23
+ class ContainMarkers
24
+ attr_reader :failure_message
25
+
26
+ def initialize(list, expected_markers)
27
+ @list = list
28
+ @expected_markers = expected_markers
29
+ end
30
+
31
+ def matches?(target_range)
32
+ @target_range = target_range
33
+
34
+ @target_range.each do |i|
35
+ markers = @list.containing(i)
36
+
37
+ @expected_markers.each do |expected_marker|
38
+ unless markers.include?(expected_marker)
39
+ @failure_message = "Expected #{expected_marker.inspect} to contain #{i}, but it doesn't. #{i} is contained by: #{markers.inspect}."
40
+ return false
41
+ end
42
+ end
43
+
44
+ markers.each do |marker|
45
+ unless @expected_markers.include?(marker)
46
+ @failure_message = "Did not expect #{marker.inspect} to contain #{i}. Only expected #{@expected_markers.inspect}."
47
+ return false
48
+ end
49
+ end
50
+ end
51
+
52
+ true
53
+ end
54
+ end
55
+
56
+ def have_markers(*markers)
57
+ HaveMarkers.new(markers)
58
+ end
59
+
60
+ def have_marker(marker)
61
+ HaveMarkers.new([marker])
62
+ end
63
+
64
+ class HaveMarkers
65
+ def initialize(expected_markers)
66
+ @expected_markers = expected_markers
67
+ end
68
+
69
+ def matches?(target)
70
+ @target = target
71
+ return false unless @target.size == @expected_markers.size
72
+ @expected_markers.each do |expected_marker|
73
+ return false unless @target.include?(expected_marker)
74
+ end
75
+ true
76
+ end
77
+
78
+ def failure_message
79
+ "Expected #{@target.inspect} to include only #{@expected_markers.inspect}"
80
+ end
81
+ end
82
+ end
83
+
84
+ require 'runtime/interval_skip_list/palindromic_fixture'
@@ -0,0 +1,77 @@
1
+ require 'spec_helper'
2
+
3
+ module SyntaxNodeSpec
4
+ describe "A new terminal syntax node" do
5
+ attr_reader :node
6
+
7
+ before do
8
+ @node = Runtime::SyntaxNode.new("input", 0...3)
9
+ end
10
+
11
+ it "reports itself as terminal" do
12
+ node.should be_terminal
13
+ node.should_not be_nonterminal
14
+ end
15
+
16
+ it "has a text value based on the input and the interval" do
17
+ node.text_value.should == "inp"
18
+ end
19
+
20
+ it "has itself as its only element" do
21
+ node.elements.should be_nil
22
+ end
23
+ end
24
+
25
+ describe "A new nonterminal syntax node" do
26
+ attr_reader :node
27
+
28
+ before do
29
+ @elements = [Runtime::SyntaxNode.new('input', 0...3)]
30
+ @node = Runtime::SyntaxNode.new('input', 0...3, @elements)
31
+ end
32
+
33
+ it "reports itself as nonterminal" do
34
+ node.should be_nonterminal
35
+ node.should_not be_terminal
36
+ end
37
+
38
+ it "has a text value based on the input and the interval" do
39
+ node.text_value.should == "inp"
40
+ end
41
+
42
+ it "has the elements with which it was instantiated" do
43
+ node.elements.should == @elements
44
+ end
45
+
46
+ it "sets itself as the parent of its elements" do
47
+ node.elements.each do |element|
48
+ element.parent.should == node
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "A new nonterminal syntax node with all children lazily instantiated" do
54
+ attr_reader :node
55
+
56
+ it "should lazily instantiate its child nodes" do
57
+ @node = Runtime::SyntaxNode.new('input', 0...3, [true, true, true])
58
+ node.elements.size.should == 3
59
+ node.elements.first.interval.should == (0...1)
60
+ node.elements.first.parent.should == node
61
+ end
62
+
63
+ it "should lazily replace stand-in child nodes around real ones" do
64
+ @input = "input"
65
+ child1 = Runtime::SyntaxNode.new(@input, 1...2)
66
+ child2 = Runtime::SyntaxNode.new(@input, 3...4)
67
+ @node = Runtime::SyntaxNode.new(@input, 0...5, [true, child1, true, child2, true])
68
+ node.elements.size.should == 5
69
+
70
+ node.elements[0].interval.should == (0...1)
71
+ node.elements[0].parent.should == node
72
+ 0.upto(4) do |index|
73
+ node.elements[index].text_value.should == @input[index, 1]
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,110 @@
1
+ require 'rubygems'
2
+ require 'benchmark'
3
+ require 'spec'
4
+ require 'polyglot'
5
+
6
+ $LOAD_PATH.unshift File.expand_path('../../lib')
7
+ require 'treetop'
8
+ include Treetop
9
+
10
+ Spec::Runner.configure do |config|
11
+ config.mock_with :rr
12
+ end
13
+
14
+ module Treetop
15
+ class TreetopExampleGroup < Spec::Example::ExampleGroup
16
+ class << self
17
+ attr_accessor :parser_class_under_test
18
+
19
+ def testing_expression(expression_under_test)
20
+ testing_grammar(%{
21
+ grammar Test
22
+ rule expression_under_test
23
+ }+expression_under_test+%{
24
+ end
25
+ end
26
+ }.tabto(0))
27
+ end
28
+
29
+ def testing_grammar(grammar_under_test)
30
+ grammar_node = parse_with_metagrammar(grammar_under_test.strip, :grammar)
31
+ parser_code = grammar_node.compile
32
+ class_eval(parser_code)
33
+ self.parser_class_under_test = const_get(grammar_node.parser_name.to_sym)
34
+ end
35
+
36
+ def parse_with_metagrammar(input, root)
37
+ parser = Treetop::Compiler::MetagrammarParser.new
38
+ parser.root = root
39
+ node = parser.parse(input)
40
+ raise parser.failure_reason unless node
41
+ node
42
+ end
43
+
44
+ end
45
+
46
+ attr_reader :parser
47
+
48
+ def parse_with_metagrammar(input, root)
49
+ self.class.parse_with_metagrammar(input, root)
50
+ end
51
+
52
+ def parser_class_under_test
53
+ self.class.parser_class_under_test
54
+ end
55
+
56
+ def parse(input, options = {})
57
+ @parser = parser_class_under_test.new
58
+ unless options[:consume_all_input].nil?
59
+ parser.consume_all_input = options.delete(:consume_all_input)
60
+ end
61
+ result = parser.parse(input, options)
62
+ yield result if block_given?
63
+ result
64
+ end
65
+
66
+ def parse_multibyte(input, options = {})
67
+ require 'active_support/all'
68
+ parse(input.mb_chars, options)
69
+ end
70
+
71
+ def compiling_grammar(grammar_under_test)
72
+ lambda {
73
+ grammar_node = parse_with_metagrammar(grammar_under_test.strip, :grammar)
74
+ parser_code = grammar_node.compile
75
+ [grammar_node, parser_code]
76
+ }
77
+ end
78
+
79
+ def compiling_expression(expression_under_test)
80
+ compiling_grammar(%{
81
+ grammar Test
82
+ rule expression_under_test
83
+ #{expression_under_test}
84
+ end
85
+ end
86
+ }.tabto(0))
87
+ end
88
+
89
+ def optionally_benchmark(&block)
90
+ if BENCHMARK
91
+ Benchmark.bm do |x|
92
+ x.report(&block)
93
+ end
94
+ else
95
+ yield
96
+ end
97
+ end
98
+
99
+ Spec::Example::ExampleGroupFactory.register(:compiler, self)
100
+ Spec::Example::ExampleGroupFactory.register(:runtime, self)
101
+ end
102
+ end
103
+
104
+ class Symbol
105
+ def to_proc
106
+ lambda do |x|
107
+ x.send(self)
108
+ end
109
+ end unless method_defined?(:to_proc)
110
+ end