treetop 1.4.5 → 1.4.7

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