less 0.8.13 → 1.0.4

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 (156) hide show
  1. data/README.md +10 -1
  2. data/Rakefile +21 -3
  3. data/VERSION +1 -1
  4. data/bin/lessc +0 -8
  5. data/less.gemspec +138 -15
  6. data/lib/less.rb +67 -11
  7. data/lib/less/command.rb +24 -25
  8. data/lib/less/engine.rb +36 -140
  9. data/lib/less/engine/builder.rb +8 -0
  10. data/lib/less/engine/less.tt +374 -0
  11. data/lib/less/engine/nodes.rb +8 -0
  12. data/lib/less/engine/nodes/element.rb +150 -0
  13. data/lib/less/engine/nodes/entity.rb +73 -0
  14. data/lib/less/engine/nodes/function.rb +82 -0
  15. data/lib/less/engine/nodes/literal.rb +135 -0
  16. data/lib/less/engine/nodes/property.rb +112 -0
  17. data/lib/less/engine/nodes/selector.rb +39 -0
  18. data/lib/less/engine/parser.rb +3860 -0
  19. data/lib/vendor/treetop/.gitignore +7 -0
  20. data/lib/vendor/treetop/LICENSE +19 -0
  21. data/lib/vendor/treetop/README +164 -0
  22. data/lib/vendor/treetop/Rakefile +19 -0
  23. data/lib/vendor/treetop/benchmark/seqpar.gnuplot +15 -0
  24. data/lib/vendor/treetop/benchmark/seqpar.treetop +16 -0
  25. data/lib/vendor/treetop/benchmark/seqpar_benchmark.rb +107 -0
  26. data/lib/vendor/treetop/bin/tt +28 -0
  27. data/lib/vendor/treetop/lib/treetop.rb +8 -0
  28. data/lib/vendor/treetop/lib/treetop/bootstrap_gen_1_metagrammar.rb +45 -0
  29. data/lib/vendor/treetop/lib/treetop/compiler.rb +6 -0
  30. data/lib/vendor/treetop/lib/treetop/compiler/grammar_compiler.rb +42 -0
  31. data/lib/vendor/treetop/lib/treetop/compiler/lexical_address_space.rb +17 -0
  32. data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.rb +3097 -0
  33. data/lib/vendor/treetop/lib/treetop/compiler/metagrammar.treetop +408 -0
  34. data/lib/vendor/treetop/lib/treetop/compiler/node_classes.rb +19 -0
  35. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/anything_symbol.rb +18 -0
  36. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/atomic_expression.rb +14 -0
  37. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/character_class.rb +23 -0
  38. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/choice.rb +31 -0
  39. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/declaration_sequence.rb +24 -0
  40. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/grammar.rb +28 -0
  41. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/inline_module.rb +27 -0
  42. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/nonterminal.rb +13 -0
  43. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/optional.rb +19 -0
  44. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parenthesized_expression.rb +9 -0
  45. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_expression.rb +146 -0
  46. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/parsing_rule.rb +55 -0
  47. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/predicate.rb +45 -0
  48. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/repetition.rb +55 -0
  49. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/sequence.rb +68 -0
  50. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/terminal.rb +20 -0
  51. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/transient_prefix.rb +9 -0
  52. data/lib/vendor/treetop/lib/treetop/compiler/node_classes/treetop_file.rb +9 -0
  53. data/lib/vendor/treetop/lib/treetop/compiler/ruby_builder.rb +113 -0
  54. data/lib/vendor/treetop/lib/treetop/ruby_extensions.rb +2 -0
  55. data/lib/vendor/treetop/lib/treetop/ruby_extensions/string.rb +42 -0
  56. data/lib/vendor/treetop/lib/treetop/runtime.rb +5 -0
  57. data/lib/vendor/treetop/lib/treetop/runtime/compiled_parser.rb +109 -0
  58. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list.rb +4 -0
  59. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/head_node.rb +15 -0
  60. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/interval_skip_list.rb +200 -0
  61. data/lib/vendor/treetop/lib/treetop/runtime/interval_skip_list/node.rb +164 -0
  62. data/lib/vendor/treetop/lib/treetop/runtime/syntax_node.rb +90 -0
  63. data/lib/vendor/treetop/lib/treetop/runtime/terminal_parse_failure.rb +16 -0
  64. data/lib/vendor/treetop/lib/treetop/runtime/terminal_syntax_node.rb +17 -0
  65. data/lib/vendor/treetop/lib/treetop/version.rb +9 -0
  66. data/lib/vendor/treetop/spec/compiler/and_predicate_spec.rb +36 -0
  67. data/lib/vendor/treetop/spec/compiler/anything_symbol_spec.rb +44 -0
  68. data/lib/vendor/treetop/spec/compiler/character_class_spec.rb +247 -0
  69. data/lib/vendor/treetop/spec/compiler/choice_spec.rb +80 -0
  70. data/lib/vendor/treetop/spec/compiler/circular_compilation_spec.rb +28 -0
  71. data/lib/vendor/treetop/spec/compiler/failure_propagation_functional_spec.rb +21 -0
  72. data/lib/vendor/treetop/spec/compiler/grammar_compiler_spec.rb +84 -0
  73. data/lib/vendor/treetop/spec/compiler/grammar_spec.rb +41 -0
  74. data/lib/vendor/treetop/spec/compiler/nonterminal_symbol_spec.rb +40 -0
  75. data/lib/vendor/treetop/spec/compiler/not_predicate_spec.rb +38 -0
  76. data/lib/vendor/treetop/spec/compiler/one_or_more_spec.rb +35 -0
  77. data/lib/vendor/treetop/spec/compiler/optional_spec.rb +37 -0
  78. data/lib/vendor/treetop/spec/compiler/parenthesized_expression_spec.rb +19 -0
  79. data/lib/vendor/treetop/spec/compiler/parsing_rule_spec.rb +32 -0
  80. data/lib/vendor/treetop/spec/compiler/sequence_spec.rb +115 -0
  81. data/lib/vendor/treetop/spec/compiler/terminal_spec.rb +81 -0
  82. data/lib/vendor/treetop/spec/compiler/terminal_symbol_spec.rb +37 -0
  83. data/lib/vendor/treetop/spec/compiler/test_grammar.treetop +7 -0
  84. data/lib/vendor/treetop/spec/compiler/test_grammar.tt +7 -0
  85. data/lib/vendor/treetop/spec/compiler/test_grammar_do.treetop +7 -0
  86. data/lib/vendor/treetop/spec/compiler/zero_or_more_spec.rb +56 -0
  87. data/lib/vendor/treetop/spec/composition/a.treetop +11 -0
  88. data/lib/vendor/treetop/spec/composition/b.treetop +11 -0
  89. data/lib/vendor/treetop/spec/composition/c.treetop +10 -0
  90. data/lib/vendor/treetop/spec/composition/d.treetop +10 -0
  91. data/lib/vendor/treetop/spec/composition/f.treetop +17 -0
  92. data/lib/vendor/treetop/spec/composition/grammar_composition_spec.rb +40 -0
  93. data/lib/vendor/treetop/spec/composition/subfolder/e_includes_c.treetop +15 -0
  94. data/lib/vendor/treetop/spec/ruby_extensions/string_spec.rb +32 -0
  95. data/lib/vendor/treetop/spec/runtime/compiled_parser_spec.rb +101 -0
  96. data/lib/vendor/treetop/spec/runtime/interval_skip_list/delete_spec.rb +147 -0
  97. data/lib/vendor/treetop/spec/runtime/interval_skip_list/expire_range_spec.rb +349 -0
  98. data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_and_delete_node.rb +385 -0
  99. data/lib/vendor/treetop/spec/runtime/interval_skip_list/insert_spec.rb +660 -0
  100. data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.graffle +6175 -0
  101. data/lib/vendor/treetop/spec/runtime/interval_skip_list/interval_skip_list_spec.rb +58 -0
  102. data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture.rb +23 -0
  103. data/lib/vendor/treetop/spec/runtime/interval_skip_list/palindromic_fixture_spec.rb +164 -0
  104. data/lib/vendor/treetop/spec/runtime/interval_skip_list/spec_helper.rb +84 -0
  105. data/lib/vendor/treetop/spec/runtime/syntax_node_spec.rb +68 -0
  106. data/lib/vendor/treetop/spec/spec_helper.rb +106 -0
  107. data/lib/vendor/treetop/spec/spec_suite.rb +4 -0
  108. data/lib/vendor/treetop/treetop.gemspec +17 -0
  109. data/spec/command_spec.rb +2 -6
  110. data/spec/css/accessors-1.0.css +18 -0
  111. data/spec/css/big-1.0.css +3768 -0
  112. data/spec/css/comments-1.0.css +9 -0
  113. data/spec/css/css-1.0.css +40 -0
  114. data/spec/css/functions-1.0.css +6 -0
  115. data/spec/css/import-1.0.css +11 -0
  116. data/spec/css/mixins-1.0.css +28 -0
  117. data/spec/css/operations-1.0.css +28 -0
  118. data/spec/css/rulesets-1.0.css +17 -0
  119. data/spec/css/scope-1.0.css +14 -0
  120. data/spec/css/strings-1.0.css +12 -0
  121. data/spec/css/variables-1.0.css +6 -0
  122. data/spec/css/whitespace-1.0.css +9 -0
  123. data/spec/engine_spec.rb +66 -18
  124. data/spec/less/accessors-1.0.less +20 -0
  125. data/spec/less/big-1.0.less +4810 -0
  126. data/spec/less/colors-1.0.less +0 -0
  127. data/spec/less/comments-1.0.less +46 -0
  128. data/spec/less/css-1.0.less +84 -0
  129. data/spec/less/exceptions/mixed-units-error.less +3 -0
  130. data/spec/less/exceptions/name-error-1.0.less +3 -0
  131. data/spec/less/exceptions/syntax-error-1.0.less +3 -0
  132. data/spec/less/functions-1.0.less +6 -0
  133. data/spec/less/import-1.0.less +7 -0
  134. data/spec/less/import/import-test-a.less +2 -0
  135. data/spec/less/import/import-test-b.less +8 -0
  136. data/spec/less/import/import-test-c.less +5 -0
  137. data/spec/less/mixins-1.0.less +43 -0
  138. data/spec/less/operations-1.0.less +39 -0
  139. data/spec/less/rulesets-1.0.less +30 -0
  140. data/spec/less/scope-1.0.less +33 -0
  141. data/spec/less/strings-1.0.less +14 -0
  142. data/spec/less/variables-1.0.less +18 -0
  143. data/spec/less/whitespace-1.0.less +21 -0
  144. data/spec/spec.css +79 -24
  145. data/spec/spec.less +2 -3
  146. data/spec/spec_helper.rb +4 -1
  147. metadata +136 -13
  148. data/lib/less/tree.rb +0 -82
  149. data/spec/css/less-0.8.10.css +0 -30
  150. data/spec/css/less-0.8.11.css +0 -31
  151. data/spec/css/less-0.8.12.css +0 -28
  152. data/spec/css/less-0.8.5.css +0 -24
  153. data/spec/css/less-0.8.6.css +0 -24
  154. data/spec/css/less-0.8.7.css +0 -24
  155. data/spec/css/less-0.8.8.css +0 -25
  156. data/spec/tree_spec.rb +0 -5
@@ -0,0 +1,164 @@
1
+ class IntervalSkipList
2
+ class Node < HeadNode
3
+ attr_accessor :key
4
+ attr_reader :markers, :endpoint_of
5
+
6
+ def initialize(key, height, path)
7
+ super(height)
8
+ @key = key
9
+ @markers = []
10
+ @endpoint_of = []
11
+ update_forward_pointers(path)
12
+ promote_markers(path)
13
+ end
14
+
15
+ def all_forward_markers
16
+ markers.flatten
17
+ end
18
+
19
+ def delete(path)
20
+ 0.upto(top_level) do |i|
21
+ path[i].forward[i] = forward[i]
22
+ end
23
+ demote_markers(path)
24
+ end
25
+
26
+ def propagate_length_change(length_change)
27
+ cur_node = self
28
+ while cur_node do
29
+ cur_node.key += length_change
30
+ cur_node = cur_node.forward[0]
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def update_forward_pointers(path)
37
+ 0.upto(top_level) do |i|
38
+ forward[i] = path[i].forward[i]
39
+ path[i].forward[i] = self
40
+ end
41
+ end
42
+
43
+ def promote_markers(path)
44
+ promoted = []
45
+ new_promoted = []
46
+ 0.upto(top_level) do |i|
47
+ incoming_markers = path[i].forward_markers[i]
48
+ markers.concat(incoming_markers)
49
+
50
+ incoming_markers.each do |marker|
51
+ if can_be_promoted_higher?(marker, i)
52
+ new_promoted.push(marker)
53
+ forward[i].delete_marker_from_path(marker, i, forward[i+1])
54
+ else
55
+ forward_markers[i].push(marker)
56
+ end
57
+ end
58
+
59
+ promoted.each do |marker|
60
+ if can_be_promoted_higher?(marker, i)
61
+ new_promoted.push(marker)
62
+ forward[i].delete_marker_from_path(marker, i, forward[i+1])
63
+ else
64
+ forward_markers[i].push(marker)
65
+ end
66
+ end
67
+
68
+ promoted = new_promoted
69
+ new_promoted = []
70
+ end
71
+ end
72
+
73
+
74
+ def can_be_promoted_higher?(marker, level)
75
+ level < top_level && forward[level + 1] && forward[level + 1].markers.include?(marker)
76
+ end
77
+
78
+ def delete_marker_from_path(marker, level, terminus)
79
+ cur_node = self
80
+ until cur_node == terminus
81
+ cur_node.forward_markers[level].delete(marker)
82
+ cur_node.markers.delete(marker)
83
+ cur_node = cur_node.forward[level]
84
+ end
85
+ end
86
+
87
+ def demote_markers(path)
88
+ demote_inbound_markers(path)
89
+ demote_outbound_markers(path)
90
+ end
91
+
92
+ def demote_inbound_markers(path)
93
+ demoted = []
94
+ new_demoted = []
95
+
96
+ top_level.downto(0) do |i|
97
+ incoming_markers = path[i].forward_markers[i].dup
98
+ incoming_markers.each do |marker|
99
+ unless forward_node_with_marker_at_or_above_level?(marker, i)
100
+ path[i].forward_markers[i].delete(marker)
101
+ new_demoted.push(marker)
102
+ end
103
+ end
104
+
105
+ demoted.each do |marker|
106
+ path[i + 1].place_marker_on_inbound_path(marker, i, path[i])
107
+
108
+ if forward[i].markers.include?(marker)
109
+ path[i].forward_markers[i].push(marker)
110
+ else
111
+ new_demoted.push(marker)
112
+ end
113
+ end
114
+
115
+ demoted = new_demoted
116
+ new_demoted = []
117
+ end
118
+ end
119
+
120
+ def demote_outbound_markers(path)
121
+ demoted = []
122
+ new_demoted = []
123
+
124
+ top_level.downto(0) do |i|
125
+ forward_markers[i].each do |marker|
126
+ new_demoted.push(marker) unless path[i].forward_markers[i].include?(marker)
127
+ end
128
+
129
+ demoted.each do |marker|
130
+ forward[i].place_marker_on_outbound_path(marker, i, forward[i + 1])
131
+ new_demoted.push(marker) unless path[i].forward_markers[i].include?(marker)
132
+ end
133
+
134
+ demoted = new_demoted
135
+ new_demoted = []
136
+ end
137
+ end
138
+
139
+ def forward_node_with_marker_at_or_above_level?(marker, level)
140
+ level.upto(top_level) do |i|
141
+ return true if forward[i].markers.include?(marker)
142
+ end
143
+ false
144
+ end
145
+
146
+ def place_marker_on_outbound_path(marker, level, terminus)
147
+ cur_node = self
148
+ until cur_node == terminus
149
+ cur_node.forward_markers[level].push(marker)
150
+ cur_node.markers.push(marker)
151
+ cur_node = cur_node.forward[level]
152
+ end
153
+ end
154
+
155
+ def place_marker_on_inbound_path(marker, level, terminus)
156
+ cur_node = self
157
+ until cur_node == terminus
158
+ cur_node.forward_markers[level].push(marker)
159
+ cur_node = cur_node.forward[level]
160
+ cur_node.markers.push(marker)
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,90 @@
1
+ module Treetop
2
+ module Runtime
3
+ class SyntaxNode
4
+ attr_reader :input, :interval
5
+ attr_accessor :parent
6
+
7
+ def initialize(input, interval, elements = nil)
8
+ @input = input
9
+ @interval = interval
10
+ if @elements = elements
11
+ @comprehensive_elements = @elements unless @elements.delete(true)
12
+ @elements.each do |element|
13
+ element.parent = self
14
+ end
15
+ end
16
+ end
17
+
18
+ def elements
19
+ return @elements if terminal?
20
+ # fill in any gaps in the sequence (lazy instantiation) if needed
21
+ @comprehensive_elements ||= interval.inject(@elements) do |elements, index|
22
+ unless @elements.any? {|element| element.interval.include?(index) }
23
+ node = SyntaxNode.new(input, index...(index + 1))
24
+ node.parent = self
25
+ elements << node
26
+ end
27
+ elements
28
+ end.sort
29
+ end
30
+
31
+ def terminal?
32
+ @elements.nil?
33
+ end
34
+
35
+ def nonterminal?
36
+ !terminal?
37
+ end
38
+
39
+ def text_value
40
+ input[interval]
41
+ end
42
+
43
+ def empty?
44
+ interval.first == interval.last && interval.exclude_end?
45
+ end
46
+
47
+ def <=>(other)
48
+ self.interval.first <=> other.interval.first
49
+ end
50
+
51
+ def extension_modules
52
+ local_extensions =
53
+ class <<self
54
+ included_modules-Object.included_modules
55
+ end
56
+ if local_extensions.size > 0
57
+ local_extensions
58
+ else
59
+ [] # There weren't any; must be a literal node
60
+ end
61
+ end
62
+
63
+ def inspect(indent="")
64
+ em = extension_modules
65
+ interesting_methods = methods-[em.last ? em.last.methods : nil]-self.class.instance_methods
66
+ im = interesting_methods.size > 0 ? " (#{interesting_methods.join(",")})" : ""
67
+ tv = text_value
68
+ tv = "...#{tv[-20..-1]}" if tv.size > 20
69
+
70
+ indent +
71
+ self.class.to_s.sub(/.*:/,'') +
72
+ em.map{|m| "+"+m.to_s.sub(/.*:/,'')}*"" +
73
+ " offset=#{interval.first}" +
74
+ ", #{tv.inspect}" +
75
+ im +
76
+ (elements && elements.size > 0 ?
77
+ ":" +
78
+ (@elements||[]).map{|e|
79
+ begin
80
+ "\n"+e.inspect(indent+" ")
81
+ rescue # Defend against inspect not taking a parameter
82
+ "\n"+indent+" "+e.inspect
83
+ end
84
+ }.join("") :
85
+ ""
86
+ )
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,16 @@
1
+ module Treetop
2
+ module Runtime
3
+ class TerminalParseFailure
4
+ attr_reader :index, :expected_string
5
+
6
+ def initialize(index, expected_string)
7
+ @index = index
8
+ @expected_string = expected_string
9
+ end
10
+
11
+ def to_s
12
+ "String matching #{expected_string} expected."
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Treetop
2
+ module Runtime
3
+ class TerminalSyntaxNode < SyntaxNode
4
+
5
+ def initialize(input, interval)
6
+ super(input, interval, [])
7
+ end
8
+
9
+ def inspect(indent="")
10
+ indent+
11
+ self.class.to_s.sub(/.*:/,'') +
12
+ " offset=#{interval.first}" +
13
+ " #{text_value.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Treetop #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 1
4
+ MINOR = 2
5
+ TINY = 6
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module AndPredicateSpec
4
+ describe "An &-predicated terminal symbol" do
5
+ testing_expression '&"foo"'
6
+
7
+ it "successfully parses input matching the terminal symbol, returning an epsilon syntax node" do
8
+ parse('foo', :consume_all_input => false) do |result|
9
+ result.should_not be_nil
10
+ result.interval.should == (0...0)
11
+ end
12
+ end
13
+ end
14
+
15
+ describe "A sequence of a terminal and an and another &-predicated terminal" do
16
+ testing_expression '"foo" &"bar"'
17
+
18
+ it "matches input matching both terminals, but only consumes the first" do
19
+ parse('foobar', :consume_all_input => false) do |result|
20
+ result.should_not be_nil
21
+ result.text_value.should == 'foo'
22
+ end
23
+ end
24
+
25
+ it "fails to parse input matching only the first terminal, with a terminal failure recorded at index 3" do
26
+ parse('foo') do |result|
27
+ result.should be_nil
28
+ terminal_failures = parser.terminal_failures
29
+ terminal_failures.size.should == 1
30
+ failure = terminal_failures[0]
31
+ failure.index.should == 3
32
+ failure.expected_string.should == 'bar'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path("#{File.dirname(__FILE__)}/../spec_helper")
2
+
3
+ module AnythingSymbolSpec
4
+ class Foo < Treetop::Runtime::SyntaxNode
5
+ end
6
+
7
+ describe "an anything symbol followed by a node class declaration and a block" do
8
+ testing_expression '. <AnythingSymbolSpec::Foo> { def a_method; end }'
9
+
10
+ it "matches any single character in a big range, returning an instance of the declared node class that responds to methods defined in the inline module" do
11
+ (33..127).each do |digit|
12
+ parse(digit.chr) do |result|
13
+ result.should_not be_nil
14
+ result.should be_an_instance_of(Foo)
15
+ result.should respond_to(:a_method)
16
+ result.interval.should == (0...1)
17
+ end
18
+ end
19
+ end
20
+
21
+ it "fails to parse epsilon" do
22
+ parse('').should be_nil
23
+ end
24
+ end
25
+
26
+ module ModFoo
27
+ end
28
+
29
+ describe "an anything symbol followed by a module declaration and a block" do
30
+ testing_expression '. <AnythingSymbolSpec::ModFoo> { def a_method; end }'
31
+
32
+ it "matches any single character in a big range, returning an instance of SyntaxNode extended by the declared module that responds to methods defined in the inline module" do
33
+ (33..127).each do |digit|
34
+ parse(digit.chr) do |result|
35
+ result.should_not be_nil
36
+ result.should be_an_instance_of(Treetop::Runtime::SyntaxNode)
37
+ result.should be_a_kind_of(ModFoo)
38
+ result.should respond_to(:a_method)
39
+ result.interval.should == (0...1)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,247 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ module CharacterClassSpec
4
+ class Foo < Treetop::Runtime::SyntaxNode
5
+ end
6
+
7
+ describe "a character class followed by a node class declaration and a block" do
8
+
9
+ testing_expression "[A-Z] <CharacterClassSpec::Foo> { def a_method; end }"
10
+
11
+ it "matches single characters within that range, returning instances of the declared node class that respond to the method defined in the inline module" do
12
+ result = parse('A')
13
+ result.should be_an_instance_of(Foo)
14
+ result.should respond_to(:a_method)
15
+ result = parse('N')
16
+ result.should be_an_instance_of(Foo)
17
+ result.should respond_to(:a_method)
18
+ result = parse('Z')
19
+ result.should be_an_instance_of(Foo)
20
+ result.should respond_to(:a_method)
21
+ end
22
+
23
+ it "does not match single characters outside of that range" do
24
+ parse('8').should be_nil
25
+ parse('a').should be_nil
26
+ end
27
+
28
+ it "matches a single character within that range at index 1" do
29
+ parse(' A', :index => 1).should_not be_nil
30
+ end
31
+
32
+ it "fails to match a single character out of that range at index 1" do
33
+ parse(' 1', :index => 1).should be_nil
34
+ end
35
+ end
36
+
37
+ module ModFoo
38
+ end
39
+
40
+ describe "a character class followed by a node module declaration and a block" do
41
+
42
+ testing_expression "[A-Z] <CharacterClassSpec::ModFoo> { def a_method; end }"
43
+
44
+ it "matches single characters within that range, returning instances of SyntaxNode extended by the specified module" do
45
+ result = parse('A')
46
+ result.should be_an_instance_of(Treetop::Runtime::SyntaxNode)
47
+ result.should be_a_kind_of(ModFoo)
48
+ result.should respond_to(:a_method)
49
+ result = parse('N')
50
+ result.should be_an_instance_of(Treetop::Runtime::SyntaxNode)
51
+ result.should be_a_kind_of(ModFoo)
52
+ result.should respond_to(:a_method)
53
+ result = parse('Z')
54
+ result.should be_an_instance_of(Treetop::Runtime::SyntaxNode)
55
+ result.should be_a_kind_of(ModFoo)
56
+ result.should respond_to(:a_method)
57
+ end
58
+
59
+ it "does not match single characters outside of that range" do
60
+ parse('8').should be_nil
61
+ parse('a').should be_nil
62
+ end
63
+
64
+ it "matches a single character within that range at index 1" do
65
+ parse(' A', :index => 1).should_not be_nil
66
+ end
67
+
68
+ it "fails to match a single character out of that range at index 1" do
69
+ parse(' 1', :index => 1).should be_nil
70
+ end
71
+ end
72
+
73
+ describe "a character class followed by a node class declaration and a block" do
74
+
75
+ testing_expression "[A-Z] <CharacterClassSpec::Foo>"
76
+
77
+ it "actively generates nodes for the character when it is the primary node" do
78
+ result = parse('A')
79
+ result.should be_a(Treetop::Runtime::SyntaxNode)
80
+ result.elements.should be_nil
81
+ end
82
+
83
+ end
84
+
85
+ describe "A character class containing quotes" do
86
+ testing_expression "[\"']"
87
+
88
+ it "matches a quote" do
89
+ parse("'").should_not be_nil
90
+ end
91
+
92
+ it "matches a double-quote" do
93
+ parse('"').should_not be_nil
94
+ end
95
+ end
96
+
97
+ describe "A character class containing a special character" do
98
+ testing_expression "[\t]"
99
+ it "matches that character only" do
100
+ parse("\t").should_not be_nil
101
+ parse('t').should be_nil
102
+ end
103
+ end
104
+
105
+ describe "A character class containing an escaped backslash" do
106
+ slash = "\\" # Make it explicit that there are *two* backslashes here
107
+ testing_expression "[#{slash}#{slash}]"
108
+ it "matches a backslash only" do
109
+ parse("\\").should_not be_nil
110
+ parse('t').should be_nil
111
+ end
112
+ end
113
+
114
+ describe "A character class containing a hex escape" do
115
+ slash = "\\"
116
+ testing_expression "[#{slash}x41]"
117
+ it "matches that character only" do
118
+ parse('A').should_not be_nil
119
+ parse('\\').should be_nil
120
+ parse('x').should be_nil
121
+ parse('4').should be_nil
122
+ parse('1').should be_nil
123
+ end
124
+ end
125
+
126
+ describe "A character class containing an octal escape" do
127
+ slash = "\\"
128
+ testing_expression "[#{slash}101]"
129
+ it "matches that character only" do
130
+ parse('A').should_not be_nil
131
+ parse('\\').should be_nil
132
+ parse('1').should be_nil
133
+ parse('0').should be_nil
134
+ end
135
+ end
136
+
137
+ describe "A character class containing a \\c control-char escape" do
138
+ slash = "\\"
139
+ testing_expression "[#{slash}cC]"
140
+ it "matches that character only" do
141
+ parse("\003").should_not be_nil
142
+ parse('\\').should be_nil
143
+ parse('c').should be_nil
144
+ parse('C').should be_nil
145
+ end
146
+ end
147
+
148
+ describe "A character class containing a \\C- control-char escape" do
149
+ slash = "\\"
150
+ testing_expression "[#{slash}C-C]"
151
+ it "matches that character only" do
152
+ parse("\003").should_not be_nil
153
+ parse('\\').should be_nil
154
+ parse('C').should be_nil
155
+ parse('-').should be_nil
156
+ end
157
+ end
158
+
159
+ describe "A character class containing a \\M- meta-char escape" do
160
+ slash = "\\"
161
+ testing_expression "[#{slash}M- ]"
162
+ it "matches that character only" do
163
+ parse("\240").should_not be_nil
164
+ parse('\\').should be_nil
165
+ parse('M').should be_nil
166
+ parse('-').should be_nil
167
+ parse(' ').should be_nil
168
+ end
169
+ end
170
+
171
+ describe "A character class containing an escaped non-special character" do
172
+ slash = "\\"
173
+ testing_expression "[#{slash}y]"
174
+ it "matches that character only" do
175
+ parse("y").should_not be_nil
176
+ parse('\\').should be_nil
177
+ end
178
+ end
179
+
180
+ describe "A character class containing an \#{...} insertion" do
181
+ testing_expression "[\#{raise 'error'}]"
182
+ it "doesn't evaluate the insertion" do
183
+ x = true
184
+ lambda{
185
+ x = parse("y")
186
+ }.should_not raise_error
187
+ x.should be_nil
188
+ parse('#').should_not be_nil
189
+ parse("'").should_not be_nil
190
+ parse("0").should be_nil
191
+ end
192
+ end
193
+
194
+ describe "a character class" do
195
+ testing_expression "[A-Z]"
196
+ it "actively generates a node for the character because it is the primary node" do
197
+ result = parse('A')
198
+ result.should be_a(Treetop::Runtime::SyntaxNode)
199
+ result.elements.should be_nil
200
+ end
201
+ end
202
+
203
+ describe "a character class mixed with other expressions" do
204
+ testing_expression '[A-Z] "a"'
205
+ it "lazily instantiates a node for the character" do
206
+ result = parse('Aa')
207
+ result.instance_variable_get("@elements").size.should == 1
208
+ result.elements.size.should == 2
209
+ end
210
+ end
211
+
212
+ describe "a character class with a node class declaration mixed with other expressions" do
213
+ testing_expression '([A-Z] <CharacterClassSpec::Foo>) "a"'
214
+ it "actively generates a node for the character because it has a node class declared" do
215
+ result = parse('Aa')
216
+ result.instance_variable_get("@elements").size.should == 2
217
+ result.elements.size.should == 2
218
+ end
219
+ end
220
+
221
+ describe "a character class with a node module declaration mixed with other expressions" do
222
+ testing_expression '([A-Z] <CharacterClassSpec::ModFoo>) "a"'
223
+ it "actively generates a node for the character because it has a node module declared" do
224
+ result = parse('Aa')
225
+ result.instance_variable_get("@elements").size.should == 2
226
+ result.elements.size.should == 2
227
+ end
228
+ end
229
+
230
+ describe "a character class with an inline block mixed with other expressions" do
231
+ testing_expression '([A-Z] { def a_method; end }) "a"'
232
+ it "actively generates a node for the character because it has an inline block" do
233
+ result = parse('Aa')
234
+ result.instance_variable_get("@elements").size.should == 2
235
+ result.elements.size.should == 2
236
+ end
237
+ end
238
+
239
+ describe "a character class with a label mixed with other expressions" do
240
+ testing_expression 'upper:([A-Z]) "b"'
241
+ it "returns the correct element for the labeled expression" do
242
+ result = parse('Ab')
243
+ result.upper.text_value.should == "A"
244
+ end
245
+ end
246
+
247
+ end