walrus 0.1

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 (208) hide show
  1. data/bin/walrus +44 -0
  2. data/ext/jindex/extconf.rb +11 -0
  3. data/ext/jindex/jindex.c +79 -0
  4. data/ext/mkdtemp/extconf.rb +11 -0
  5. data/ext/mkdtemp/mkdtemp.c +41 -0
  6. data/lib/walrus/additions/module.rb +36 -0
  7. data/lib/walrus/additions/string.rb +37 -0
  8. data/lib/walrus/additions/test/unit/error_collector.rb +62 -0
  9. data/lib/walrus/compile_error.rb +28 -0
  10. data/lib/walrus/compiler.rb +124 -0
  11. data/lib/walrus/contrib/spec/walruscloth_spec.rb +32 -0
  12. data/lib/walrus/contrib/walruscloth.rb +82 -0
  13. data/lib/walrus/diff.rb +89 -0
  14. data/lib/walrus/document.rb +98 -0
  15. data/lib/walrus/grammar/additions/proc.rb +20 -0
  16. data/lib/walrus/grammar/additions/regexp.rb +21 -0
  17. data/lib/walrus/grammar/additions/string.rb +52 -0
  18. data/lib/walrus/grammar/additions/symbol.rb +42 -0
  19. data/lib/walrus/grammar/and_predicate.rb +40 -0
  20. data/lib/walrus/grammar/array_result.rb +19 -0
  21. data/lib/walrus/grammar/continuation_wrapper_exception.rb +28 -0
  22. data/lib/walrus/grammar/left_recursion_exception.rb +27 -0
  23. data/lib/walrus/grammar/location_tracking.rb +105 -0
  24. data/lib/walrus/grammar/match_data_wrapper.rb +65 -0
  25. data/lib/walrus/grammar/memoizing.rb +41 -0
  26. data/lib/walrus/grammar/memoizing_cache.rb +94 -0
  27. data/lib/walrus/grammar/node.rb +60 -0
  28. data/lib/walrus/grammar/not_predicate.rb +40 -0
  29. data/lib/walrus/grammar/parse_error.rb +39 -0
  30. data/lib/walrus/grammar/parser_state.rb +181 -0
  31. data/lib/walrus/grammar/parslet.rb +28 -0
  32. data/lib/walrus/grammar/parslet_choice.rb +120 -0
  33. data/lib/walrus/grammar/parslet_combination.rb +26 -0
  34. data/lib/walrus/grammar/parslet_combining.rb +154 -0
  35. data/lib/walrus/grammar/parslet_merge.rb +88 -0
  36. data/lib/walrus/grammar/parslet_omission.rb +57 -0
  37. data/lib/walrus/grammar/parslet_repetition.rb +97 -0
  38. data/lib/walrus/grammar/parslet_repetition_default.rb +58 -0
  39. data/lib/walrus/grammar/parslet_sequence.rb +202 -0
  40. data/lib/walrus/grammar/predicate.rb +57 -0
  41. data/lib/walrus/grammar/proc_parslet.rb +52 -0
  42. data/lib/walrus/grammar/regexp_parslet.rb +73 -0
  43. data/lib/walrus/grammar/skipped_substring_exception.rb +36 -0
  44. data/lib/walrus/grammar/string_enumerator.rb +45 -0
  45. data/lib/walrus/grammar/string_parslet.rb +75 -0
  46. data/lib/walrus/grammar/string_result.rb +24 -0
  47. data/lib/walrus/grammar/symbol_parslet.rb +63 -0
  48. data/lib/walrus/grammar.rb +170 -0
  49. data/lib/walrus/no_parameter_marker.rb +19 -0
  50. data/lib/walrus/parser.rb +420 -0
  51. data/lib/walrus/runner.rb +356 -0
  52. data/lib/walrus/template.rb +75 -0
  53. data/lib/walrus/walrus_grammar/assignment_expression.rb +24 -0
  54. data/lib/walrus/walrus_grammar/block_directive.rb +28 -0
  55. data/lib/walrus/walrus_grammar/comment.rb +24 -0
  56. data/lib/walrus/walrus_grammar/def_directive.rb +64 -0
  57. data/lib/walrus/walrus_grammar/echo_directive.rb +44 -0
  58. data/lib/walrus/walrus_grammar/escape_sequence.rb +24 -0
  59. data/lib/walrus/walrus_grammar/import_directive.rb +44 -0
  60. data/lib/walrus/walrus_grammar/include_directive.rb +27 -0
  61. data/lib/walrus/walrus_grammar/instance_variable.rb +24 -0
  62. data/lib/walrus/walrus_grammar/literal.rb +24 -0
  63. data/lib/walrus/walrus_grammar/message_expression.rb +25 -0
  64. data/lib/walrus/walrus_grammar/multiline_comment.rb +54 -0
  65. data/lib/walrus/walrus_grammar/placeholder.rb +40 -0
  66. data/lib/walrus/walrus_grammar/raw_directive.rb +42 -0
  67. data/lib/walrus/walrus_grammar/raw_text.rb +45 -0
  68. data/lib/walrus/walrus_grammar/ruby_directive.rb +29 -0
  69. data/lib/walrus/walrus_grammar/ruby_expression.rb +31 -0
  70. data/lib/walrus/walrus_grammar/set_directive.rb +24 -0
  71. data/lib/walrus/walrus_grammar/silent_directive.rb +44 -0
  72. data/lib/walrus/walrus_grammar/slurp_directive.rb +25 -0
  73. data/lib/walrus/walrus_grammar/super_directive.rb +27 -0
  74. data/lib/walrus.rb +64 -0
  75. data/spec/acceptance/acceptance_spec.rb +97 -0
  76. data/spec/acceptance/block/basic_block.expected +1 -0
  77. data/spec/acceptance/block/basic_block.tmpl +3 -0
  78. data/spec/acceptance/block/nested_blocks.expected +5 -0
  79. data/spec/acceptance/block/nested_blocks.tmpl +11 -0
  80. data/spec/acceptance/comments/comments_and_text.expected +3 -0
  81. data/spec/acceptance/comments/comments_and_text.tmpl +6 -0
  82. data/spec/acceptance/comments/single_comment.expected +0 -0
  83. data/spec/acceptance/comments/single_comment.tmpl +1 -0
  84. data/spec/acceptance/def/alternative_def_calling_conventions.expected +3 -0
  85. data/spec/acceptance/def/alternative_def_calling_conventions.tmpl +18 -0
  86. data/spec/acceptance/def/basic_def_block_no_output.expected +0 -0
  87. data/spec/acceptance/def/basic_def_block_no_output.tmpl +17 -0
  88. data/spec/acceptance/def/defs_can_be_called_multiple_times.expected +3 -0
  89. data/spec/acceptance/def/defs_can_be_called_multiple_times.tmpl +6 -0
  90. data/spec/acceptance/def/defs_can_be_dynamic.expected +4 -0
  91. data/spec/acceptance/def/defs_can_be_dynamic.tmpl +12 -0
  92. data/spec/acceptance/echo/echo_directive_with_numeric_literal.expected +1 -0
  93. data/spec/acceptance/echo/echo_directive_with_numeric_literal.tmpl +1 -0
  94. data/spec/acceptance/echo/echo_expression_list.expected +1 -0
  95. data/spec/acceptance/echo/echo_expression_list.tmpl +1 -0
  96. data/spec/acceptance/echo/echo_short_notation.expected +1 -0
  97. data/spec/acceptance/echo/echo_short_notation.tmpl +1 -0
  98. data/spec/acceptance/echo/echo_simple_expression.expected +1 -0
  99. data/spec/acceptance/echo/echo_simple_expression.tmpl +1 -0
  100. data/spec/acceptance/echo/echo_single_quoted_string_literal.expected +1 -0
  101. data/spec/acceptance/echo/echo_single_quoted_string_literal.tmpl +1 -0
  102. data/spec/acceptance/echo/multiple_echo_statements.expected +1 -0
  103. data/spec/acceptance/echo/multiple_echo_statements.tmpl +2 -0
  104. data/spec/acceptance/includes/basic_included_file.txt +1 -0
  105. data/spec/acceptance/includes/basic_includer.complex +3 -0
  106. data/spec/acceptance/includes/basic_includer.expected +3 -0
  107. data/spec/acceptance/includes/basic_includer.rb +38 -0
  108. data/spec/acceptance/includes/complicated_included_file.txt +3 -0
  109. data/spec/acceptance/includes/complicated_includer.complex +3 -0
  110. data/spec/acceptance/includes/complicated_includer.expected +3 -0
  111. data/spec/acceptance/includes/complicated_includer.rb +41 -0
  112. data/spec/acceptance/includes/nested_include_1.txt +3 -0
  113. data/spec/acceptance/includes/nested_include_2.txt +1 -0
  114. data/spec/acceptance/includes/nested_includer.complex +3 -0
  115. data/spec/acceptance/includes/nested_includer.expected +4 -0
  116. data/spec/acceptance/includes/nested_includer.rb +41 -0
  117. data/spec/acceptance/inheritance/basic_child.complex +10 -0
  118. data/spec/acceptance/inheritance/basic_child.expected +9 -0
  119. data/spec/acceptance/inheritance/basic_child.rb +54 -0
  120. data/spec/acceptance/inheritance/basic_parent.complex +5 -0
  121. data/spec/acceptance/inheritance/basic_parent.expected +3 -0
  122. data/spec/acceptance/inheritance/basic_parent.rb +41 -0
  123. data/spec/acceptance/inheritance/importing_child.complex +8 -0
  124. data/spec/acceptance/inheritance/importing_child.expected +7 -0
  125. data/spec/acceptance/inheritance/importing_child.rb +46 -0
  126. data/spec/acceptance/inheritance/subdirectory/importing_child_in_subdirectory.complex +8 -0
  127. data/spec/acceptance/inheritance/subdirectory/importing_child_in_subdirectory.expected +7 -0
  128. data/spec/acceptance/inheritance/subdirectory/importing_child_in_subdirectory.rb +44 -0
  129. data/spec/acceptance/multiline_comments/multiline_comment_with_directives_inside.expected +0 -0
  130. data/spec/acceptance/multiline_comments/multiline_comment_with_directives_inside.tmpl +15 -0
  131. data/spec/acceptance/multiline_comments/simple_multiline_comment.expected +2 -0
  132. data/spec/acceptance/multiline_comments/simple_multiline_comment.tmpl +4 -0
  133. data/spec/acceptance/raw/complicated_raw_example.expected +57 -0
  134. data/spec/acceptance/raw/complicated_raw_example.tmpl +79 -0
  135. data/spec/acceptance/raw-text/UTF_8.expected +12 -0
  136. data/spec/acceptance/raw-text/UTF_8.tmpl +12 -0
  137. data/spec/acceptance/raw-text/empty_file.expected +0 -0
  138. data/spec/acceptance/raw-text/empty_file.tmpl +0 -0
  139. data/spec/acceptance/raw-text/multi_line.expected +4 -0
  140. data/spec/acceptance/raw-text/multi_line.tmpl +4 -0
  141. data/spec/acceptance/raw-text/single_line.expected +1 -0
  142. data/spec/acceptance/raw-text/single_line.tmpl +1 -0
  143. data/spec/acceptance/raw-text/single_line_whitespace.expected +1 -0
  144. data/spec/acceptance/raw-text/single_line_whitespace.tmpl +1 -0
  145. data/spec/acceptance/ruby/ruby_directive_is_just_like_silent.expected +1 -0
  146. data/spec/acceptance/ruby/ruby_directive_is_just_like_silent.tmpl +4 -0
  147. data/spec/acceptance/ruby/ruby_directive_using_here_doc.expected +1 -0
  148. data/spec/acceptance/ruby/ruby_directive_using_here_doc.tmpl +4 -0
  149. data/spec/acceptance/ruby/ruby_directive_using_here_doc_alt_syntax.expected +1 -0
  150. data/spec/acceptance/ruby/ruby_directive_using_here_doc_alt_syntax.tmpl +4 -0
  151. data/spec/acceptance/ruby/ruby_directive_with_accumulate.expected +1 -0
  152. data/spec/acceptance/ruby/ruby_directive_with_accumulate.tmpl +4 -0
  153. data/spec/acceptance/ruby/ruby_directive_with_accumulate_and_block.expected +1 -0
  154. data/spec/acceptance/ruby/ruby_directive_with_accumulate_and_block.tmpl +6 -0
  155. data/spec/acceptance/set/unused_set.expected +0 -0
  156. data/spec/acceptance/set/unused_set.tmpl +1 -0
  157. data/spec/acceptance/set/used_set.expected +1 -0
  158. data/spec/acceptance/set/used_set.tmpl +2 -0
  159. data/spec/acceptance/silent/silent_and_echo_combined.expected +1 -0
  160. data/spec/acceptance/silent/silent_and_echo_combined.tmpl +2 -0
  161. data/spec/acceptance/silent/silent_short_notation.expected +1 -0
  162. data/spec/acceptance/silent/silent_short_notation.tmpl +1 -0
  163. data/spec/acceptance/silent/simple_silent_directive.expected +0 -0
  164. data/spec/acceptance/silent/simple_silent_directive.tmpl +1 -0
  165. data/spec/acceptance/slurp/basic_slurp_demo.expected +1 -0
  166. data/spec/acceptance/slurp/basic_slurp_demo.tmpl +4 -0
  167. data/spec/acceptance/super/super_with_no_effect.expected +4 -0
  168. data/spec/acceptance/super/super_with_no_effect.tmpl +5 -0
  169. data/spec/additions/module_spec.rb +126 -0
  170. data/spec/additions/string_spec.rb +99 -0
  171. data/spec/compiler_spec.rb +55 -0
  172. data/spec/grammar/additions/proc_spec.rb +25 -0
  173. data/spec/grammar/additions/regexp_spec.rb +37 -0
  174. data/spec/grammar/additions/string_spec.rb +106 -0
  175. data/spec/grammar/and_predicate_spec.rb +29 -0
  176. data/spec/grammar/continuation_wrapper_exception_spec.rb +23 -0
  177. data/spec/grammar/match_data_wrapper_spec.rb +41 -0
  178. data/spec/grammar/memoizing_cache_spec.rb +112 -0
  179. data/spec/grammar/node_spec.rb +126 -0
  180. data/spec/grammar/not_predicate_spec.rb +29 -0
  181. data/spec/grammar/parser_state_spec.rb +172 -0
  182. data/spec/grammar/parslet_choice_spec.rb +49 -0
  183. data/spec/grammar/parslet_combining_spec.rb +287 -0
  184. data/spec/grammar/parslet_merge_spec.rb +33 -0
  185. data/spec/grammar/parslet_omission_spec.rb +58 -0
  186. data/spec/grammar/parslet_repetition_spec.rb +77 -0
  187. data/spec/grammar/parslet_sequence_spec.rb +49 -0
  188. data/spec/grammar/parslet_spec.rb +23 -0
  189. data/spec/grammar/predicate_spec.rb +53 -0
  190. data/spec/grammar/proc_parslet_spec.rb +52 -0
  191. data/spec/grammar/regexp_parslet_spec.rb +347 -0
  192. data/spec/grammar/string_enumerator_spec.rb +94 -0
  193. data/spec/grammar/string_parslet_spec.rb +143 -0
  194. data/spec/grammar/symbol_parslet_spec.rb +30 -0
  195. data/spec/grammar_spec.rb +545 -0
  196. data/spec/parser_spec.rb +1418 -0
  197. data/spec/spec_helper.rb +34 -0
  198. data/spec/walrus_grammar/comment_spec.rb +39 -0
  199. data/spec/walrus_grammar/echo_directive_spec.rb +63 -0
  200. data/spec/walrus_grammar/escape_sequence_spec.rb +85 -0
  201. data/spec/walrus_grammar/literal_spec.rb +41 -0
  202. data/spec/walrus_grammar/message_expression_spec.rb +37 -0
  203. data/spec/walrus_grammar/multiline_comment_spec.rb +58 -0
  204. data/spec/walrus_grammar/placeholder_spec.rb +48 -0
  205. data/spec/walrus_grammar/raw_directive_spec.rb +81 -0
  206. data/spec/walrus_grammar/raw_text_spec.rb +65 -0
  207. data/spec/walrus_grammar/silent_directive_spec.rb +34 -0
  208. metadata +291 -0
@@ -0,0 +1,49 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_choice_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using a Parslet Choice' do
15
+
16
+ setup do
17
+ @p1 = 'foo'.to_parseable
18
+ @p2 = 'bar'.to_parseable
19
+ end
20
+
21
+ it 'hashes should be the same if initialized with the same parseables' do
22
+ ParsletChoice.new(@p1, @p2).hash.should == ParsletChoice.new(@p1, @p2).hash
23
+ ParsletChoice.new(@p1, @p2).should eql(ParsletChoice.new(@p1, @p2))
24
+ end
25
+
26
+ it 'hashes should (ideally) be different if initialized with different parseables' do
27
+ ParsletChoice.new(@p1, @p2).hash.should_not == ParsletChoice.new('baz'.to_parseable, 'abc'.to_parseable).hash
28
+ ParsletChoice.new(@p1, @p2).should_not eql(ParsletChoice.new('baz'.to_parseable, 'abc'.to_parseable))
29
+ end
30
+
31
+ it 'hashes should be different compared to other similar classes even if initialized with the same parseables' do
32
+ ParsletChoice.new(@p1, @p2).hash.should_not == ParsletSequence.new(@p1, @p2).hash
33
+ ParsletChoice.new(@p1, @p2).should_not eql(ParsletSequence.new(@p1, @p2))
34
+ end
35
+
36
+ it 'should be able to use Parslet Choice instances as keys in a hash' do
37
+ hash = {}
38
+ key1 = ParsletChoice.new(@p1, @p2)
39
+ key2 = ParsletChoice.new('baz'.to_parseable, 'abc'.to_parseable)
40
+ hash[:key1] = 'foo'
41
+ hash[:key2] = 'bar'
42
+ hash[:key1].should == 'foo'
43
+ hash[:key2].should == 'bar'
44
+ end
45
+
46
+ end
47
+
48
+ end # class Grammar
49
+ end # module Walrus
@@ -0,0 +1,287 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_combining_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using shorthand operators to combine String, Symbol and Regexp parsers' do
15
+
16
+ it 'should be able to chain a String and a Regexp together' do
17
+
18
+ # try in one order
19
+ sequence = 'foo' & /\d+/
20
+ sequence.parse('foo1000').should == ['foo', '1000']
21
+ lambda { sequence.parse('foo') }.should raise_error(ParseError) # first part alone is not enough
22
+ lambda { sequence.parse('1000') }.should raise_error(ParseError) # neither is second part alone
23
+ lambda { sequence.parse('1000foo') }.should raise_error(ParseError) # order matters
24
+
25
+ # same test but in reverse order
26
+ sequence = /\d+/ & 'foo'
27
+ sequence.parse('1000foo').should == ['1000', 'foo']
28
+ lambda { sequence.parse('foo') }.should raise_error(ParseError) # first part alone is not enough
29
+ lambda { sequence.parse('1000') }.should raise_error(ParseError) # neither is second part alone
30
+ lambda { sequence.parse('foo1000') }.should raise_error(ParseError) # order matters
31
+
32
+ end
33
+
34
+ it 'should be able to choose between a String and a Regexp' do
35
+
36
+ # try in one order
37
+ sequence = 'foo' | /\d+/
38
+ sequence.parse('foo').should == 'foo'
39
+ sequence.parse('100').should == '100'
40
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
41
+
42
+ # same test but in reverse order
43
+ sequence = /\d+/ | 'foo'
44
+ sequence.parse('foo').should == 'foo'
45
+ sequence.parse('100').should == '100'
46
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
47
+
48
+ end
49
+
50
+ it 'should be able to freely intermix String and Regexp objects when chaining and choosing' do
51
+
52
+ sequence = 'foo' & /\d+/ | 'bar' & /[XYZ]{3}/
53
+ sequence.parse('foo123').should == ['foo', '123']
54
+ sequence.parse('barZYX').should == ['bar', 'ZYX']
55
+ lambda { sequence.parse('foo') }.should raise_error(ParseError)
56
+ lambda { sequence.parse('123') }.should raise_error(ParseError)
57
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
58
+ lambda { sequence.parse('XYZ') }.should raise_error(ParseError)
59
+ lambda { sequence.parse('barXY') }.should raise_error(ParseError)
60
+
61
+ end
62
+
63
+ it 'should be able to specify minimum and maximum repetition using shorthand methods' do
64
+
65
+ # optional (same as "?" in regular expressions)
66
+ sequence = 'foo'.optional
67
+ sequence.parse('foo').should == 'foo'
68
+ lambda { sequence.parse('bar') }.should throw_symbol(:ZeroWidthParseSuccess)
69
+
70
+ # zero_or_one (same as optional; "?" in regular expressions)
71
+ sequence = 'foo'.zero_or_one
72
+ sequence.parse('foo').should == 'foo'
73
+ lambda { sequence.parse('bar') }.should throw_symbol(:ZeroWidthParseSuccess)
74
+
75
+ # zero_or_more (same as "*" in regular expressions)
76
+ sequence = 'foo'.zero_or_more
77
+ sequence.parse('foo').should == 'foo'
78
+ sequence.parse('foofoofoobar').should == ['foo', 'foo', 'foo']
79
+ lambda { sequence.parse('bar') }.should throw_symbol(:ZeroWidthParseSuccess)
80
+
81
+ # one_or_more (same as "+" in regular expressions)
82
+ sequence = 'foo'.one_or_more
83
+ sequence.parse('foo').should == 'foo'
84
+ sequence.parse('foofoofoobar').should == ['foo', 'foo', 'foo']
85
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
86
+
87
+ # repeat (arbitary limits for min, max; same as {min, max} in regular expressions)
88
+ sequence = 'foo'.repeat(3, 5)
89
+ sequence.parse('foofoofoobar').should == ['foo', 'foo', 'foo']
90
+ sequence.parse('foofoofoofoobar').should == ['foo', 'foo', 'foo', 'foo']
91
+ sequence.parse('foofoofoofoofoobar').should == ['foo', 'foo', 'foo', 'foo', 'foo']
92
+ sequence.parse('foofoofoofoofoofoobar').should == ['foo', 'foo', 'foo', 'foo', 'foo']
93
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
94
+ lambda { sequence.parse('foo') }.should raise_error(ParseError)
95
+ lambda { sequence.parse('foofoo') }.should raise_error(ParseError)
96
+
97
+ end
98
+
99
+ it 'should be able to apply repetitions to other combinations wrapped in parentheses' do
100
+ sequence = ('foo' & 'bar').one_or_more
101
+ sequence.parse('foobar').should == ['foo', 'bar']
102
+ sequence.parse('foobarfoobar').should == [['foo', 'bar'], ['foo', 'bar']] # fails: just returns ['foo', 'bar']
103
+ end
104
+
105
+ it 'should be able to combine use of repetition shorthand methods with other shorthand methods' do
106
+
107
+ # first we test with chaining
108
+ sequence = 'foo'.optional & 'bar' & 'abc'.one_or_more
109
+ sequence.parse('foobarabc').should == ['foo', 'bar', 'abc']
110
+ sequence.parse('foobarabcabc').should == ['foo', 'bar', ['abc', 'abc']]
111
+ sequence.parse('barabc').should == ['bar', 'abc']
112
+ lambda { sequence.parse('abc') }.should raise_error(ParseError)
113
+
114
+ # similar test but with alternation
115
+ sequence = 'foo' | 'bar' | 'abc'.one_or_more
116
+ sequence.parse('foobarabc').should == 'foo'
117
+ sequence.parse('barabc').should == 'bar'
118
+ sequence.parse('abc').should == 'abc'
119
+ sequence.parse('abcabc').should == ['abc', 'abc']
120
+ lambda { sequence.parse('nothing') }.should raise_error(ParseError)
121
+
122
+ # test with defective sequence (makes no sense to use "optional" with alternation, will always succeed)
123
+ sequence = 'foo'.optional | 'bar' | 'abc'.one_or_more
124
+ sequence.parse('foobarabc').should == 'foo'
125
+ lambda { sequence.parse('nothing') }.should throw_symbol(:ZeroWidthParseSuccess)
126
+
127
+ end
128
+
129
+ it 'should be able to chain a "not predicate"' do
130
+ sequence = 'foo' & 'bar'.not!
131
+ sequence.parse('foo').should == 'foo' # fails with ['foo'] because that's the way ParserState works...
132
+ sequence.parse('foo...').should == 'foo' # same
133
+ lambda { sequence.parse('foobar') }.should raise_error(ParseError)
134
+ end
135
+
136
+ it 'an isolated "not predicate" should return a zero-width match' do
137
+ sequence = 'foo'.not!
138
+ lambda { sequence.parse('foo') }.should raise_error(ParseError)
139
+ lambda { sequence.parse('bar') }.should throw_symbol(:NotPredicateSuccess)
140
+ end
141
+
142
+ it 'two "not predicates" chained together should act like a union' do
143
+
144
+ # this means "not followed by 'foo' and not followed by 'bar'"
145
+ sequence = 'foo'.not! & 'bar'.not!
146
+ lambda { sequence.parse('foo') }.should raise_error(ParseError)
147
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
148
+ lambda { sequence.parse('abc') }.should throw_symbol(:NotPredicateSuccess)
149
+
150
+ end
151
+
152
+ it 'should be able to chain an "and predicate"' do
153
+ sequence = 'foo' & 'bar'.and?
154
+ sequence.parse('foobar').should == 'foo' # same problem, returns ['foo']
155
+ lambda { sequence.parse('foo...') }.should raise_error(ParseError)
156
+ lambda { sequence.parse('foo') }.should raise_error(ParseError)
157
+ end
158
+
159
+ it 'an isolated "and predicate" should return a zero-width match' do
160
+ sequence = 'foo'.and?
161
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
162
+ lambda { sequence.parse('foo') }.should throw_symbol(:AndPredicateSuccess)
163
+ end
164
+
165
+ it 'should be able to follow an "and predicate" with other parslets or combinations' do
166
+
167
+ # this is equivalent to "foo" if followed by "bar", or any three characters
168
+ sequence = 'foo' & 'bar'.and? | /.../
169
+ sequence.parse('foobar').should == 'foo' # returns ['foo']
170
+ sequence.parse('abc').should == 'abc'
171
+ lambda { sequence.parse('') }.should raise_error(ParseError)
172
+
173
+ # it makes little sense for the predicate to follows a choice operator so we don't test that
174
+
175
+ end
176
+
177
+ it 'should be able to follow a "not predicate" with other parslets or combinations' do
178
+
179
+ # this is equivalent to "foo" followed by any three characters other than "bar"
180
+ sequence = 'foo' & 'bar'.not! & /.../
181
+ sequence.parse('fooabc').should == ['foo', 'abc']
182
+ lambda { sequence.parse('foobar') }.should raise_error(ParseError)
183
+ lambda { sequence.parse('foo') }.should raise_error(ParseError)
184
+ lambda { sequence.parse('') }.should raise_error(ParseError)
185
+
186
+ end
187
+
188
+ it 'should be able to include a "not predicate" when using a repetition operator' do
189
+
190
+ # basic example
191
+ sequence = ('foo' & 'bar'.not!).one_or_more
192
+ sequence.parse('foo').should == 'foo'
193
+ sequence.parse('foofoobar').should == 'foo'
194
+ sequence.parse('foofoo').should == ['foo', 'foo']
195
+ lambda { sequence.parse('bar') }.should raise_error(ParseError)
196
+ lambda { sequence.parse('foobar') }.should raise_error(ParseError)
197
+
198
+ # variation: note that greedy matching alters the behaviour
199
+ sequence = ('foo' & 'bar').one_or_more & 'abc'.not!
200
+ sequence.parse('foobar').should == ['foo', 'bar']
201
+ sequence.parse('foobarfoobar').should == [['foo', 'bar'], ['foo', 'bar']]
202
+ lambda { sequence.parse('foobarabc') }.should raise_error(ParseError)
203
+
204
+ end
205
+
206
+ it 'should be able to use regular expression shortcuts in conjunction with predicates' do
207
+
208
+ # match "foo" as long as it's not followed by a digit
209
+ sequence = 'foo' & /\d/.not!
210
+ sequence.parse('foo').should == 'foo'
211
+ sequence.parse('foobar').should == 'foo'
212
+ lambda { sequence.parse('foo1') }.should raise_error(ParseError)
213
+
214
+ # match "word" characters as long as they're not followed by whitespace
215
+ sequence = /\w+/ & /\s/.not!
216
+ sequence.parse('foo').should == 'foo'
217
+ lambda { sequence.parse('foo ') }.should raise_error(ParseError)
218
+
219
+ end
220
+
221
+ end
222
+
223
+ describe 'omitting tokens from the output using the "skip" method' do
224
+
225
+ it 'should be able to skip quotation marks delimiting a string' do
226
+ sequence = '"'.skip & /[^"]+/ & '"'.skip
227
+ sequence.parse('"hello world"').should == 'hello world' # note this is returning a ParserState object
228
+ end
229
+
230
+ it 'should be able to skip within a repetition expression' do
231
+ sequence = ('foo'.skip & /\d+/).one_or_more
232
+ sequence.parse('foo1...').should == '1'
233
+ sequence.parse('foo1foo2...').should == ['1', '2'] # only returns 1
234
+ sequence.parse('foo1foo2foo3...').should == ['1', '2', '3'] # only returns 1
235
+ end
236
+
237
+ it 'should be able to skip commas separating a list' do
238
+
239
+ # closer to real-world use: a comma-separated list
240
+ sequence = /\w+/ & (/\s*,\s*/.skip & /\w+/).zero_or_more
241
+ sequence.parse('a').should == 'a'
242
+ sequence.parse('a, b').should == ['a', 'b']
243
+ sequence.parse('a, b, c').should == ['a', ['b', 'c']]
244
+ sequence.parse('a, b, c, d').should == ['a', ['b', 'c', 'd']]
245
+
246
+ # again, using the ">>" operator
247
+ sequence = /\w+/ >> (/\s*,\s*/.skip & /\w+/).zero_or_more
248
+ sequence.parse('a').should == 'a'
249
+ sequence.parse('a, b').should == ['a', 'b']
250
+ sequence.parse('a, b, c').should == ['a', 'b', 'c']
251
+ sequence.parse('a, b, c, d').should == ['a', 'b', 'c', 'd']
252
+
253
+ end
254
+
255
+ end
256
+
257
+ describe 'using the shorthand ">>" pseudo-operator' do
258
+
259
+ it 'should be able to chain the operator multiple times' do
260
+
261
+ # comma-separated words followed by comma-separated digits
262
+ sequence = /[a-zA-Z]+/ >> (/\s*,\s*/.skip & /[a-zA-Z]+/).zero_or_more >> (/\s*,\s*/.skip & /\d+/).one_or_more
263
+ sequence.parse('a, 1').should == ['a', '1']
264
+ sequence.parse('a, b, 1').should == ['a', 'b', '1']
265
+ sequence.parse('a, 1, 2').should == ['a', '1', '2']
266
+ sequence.parse('a, b, 1, 2').should == ['a', 'b', '1', '2']
267
+
268
+ # same, but enclosed in quotes
269
+ sequence = '"'.skip & /[a-zA-Z]+/ >> (/\s*,\s*/.skip & /[a-zA-Z]+/).zero_or_more >> (/\s*,\s*/.skip & /\d+/).one_or_more & '"'.skip
270
+ sequence.parse('"a, 1"').should == ['a', '1']
271
+ sequence.parse('"a, b, 1"').should == ['a', 'b', '1']
272
+ sequence.parse('"a, 1, 2"').should == ['a', '1', '2']
273
+ sequence.parse('"a, b, 1, 2"').should == ['a', 'b', '1', '2']
274
+
275
+ # alternative construction of same
276
+ sequence = /[a-zA-Z]+/ >> (/\s*,\s*/.skip & /[a-zA-Z]+/).zero_or_more & /\s*,\s*/.skip & /\d+/ >> (/\s*,\s*/.skip & /\d+/).zero_or_more
277
+ sequence.parse('a, 1').should == ['a', '1']
278
+ sequence.parse('a, b, 1').should == ['a', 'b', '1']
279
+ sequence.parse('a, 1, 2').should == ['a', '1', '2']
280
+ sequence.parse('a, b, 1, 2').should == ['a', 'b', '1', '2']
281
+
282
+ end
283
+
284
+ end
285
+
286
+ end # class Grammar
287
+ end # module Walrus
@@ -0,0 +1,33 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_merge_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using a Parslet Merge' do
15
+
16
+ it 'should be able to compare for equality' do
17
+ ParsletMerge.new('foo', 'bar').should eql(ParsletMerge.new('foo', 'bar'))
18
+ ParsletMerge.new('foo', 'bar').should_not eql(ParsletOmission.new('foo')) # wrong class
19
+ end
20
+
21
+ it 'ParsletMerge and ParsletSequence hashs should not match even if created using the same parseable instances' do
22
+ parseable1 = 'foo'.to_parseable
23
+ parseable2 = 'bar'.to_parseable
24
+ p1 = ParsletMerge.new(parseable1, parseable2)
25
+ p2 = ParsletSequence.new(parseable1, parseable2)
26
+ p1.hash.should_not == p2.hash
27
+ p1.should_not eql(p2)
28
+ end
29
+
30
+ end
31
+
32
+ end # class Grammar
33
+ end # module Walrus
@@ -0,0 +1,58 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_omission_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using a Parslet Omission' do
15
+
16
+ it 'should raise if "parseable" argument is nil' do
17
+ lambda { ParsletOmission.new(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should complain if pass nil string for parsing' do
21
+ lambda { ParsletOmission.new('foo'.to_parseable).parse(nil) }.should raise_error(ArgumentError)
22
+ end
23
+
24
+ it 'should let parse errors from lower levels fall through' do
25
+ lambda { ParsletOmission.new('foo'.to_parseable).parse('bar') }.should raise_error(ParseError)
26
+ end
27
+
28
+ it 'should indicate parse errors with a SubstringSkippedException' do
29
+ lambda { ParsletOmission.new('foo'.to_parseable).parse('foo') }.should raise_error(SkippedSubstringException)
30
+ end
31
+
32
+ it 'the raised SubstringSkippedException should include the parsed substring' do
33
+ begin
34
+ ParsletOmission.new('foo'.to_parseable).parse('foobar')
35
+ rescue SkippedSubstringException => e
36
+ substring = e.to_s
37
+ end
38
+ substring.should == 'foo'
39
+ end
40
+
41
+ it 'the parsed substring should be an empty string in the case of a zero-width parse success at a lower level' do
42
+ begin
43
+ ParsletOmission.new('foo'.optional).parse('bar') # a contrived example
44
+ rescue SkippedSubstringException => e
45
+ substring = e.to_s
46
+ end
47
+ substring.should == ''
48
+ end
49
+
50
+ it 'should be able to compare for equality' do
51
+ ParsletOmission.new('foo').should eql(ParsletOmission.new('foo'))
52
+ ParsletOmission.new('foo').should_not eql(ParsletOmission.new('bar'))
53
+ end
54
+
55
+ end
56
+
57
+ end # class Grammar
58
+ end # module Walrus
@@ -0,0 +1,77 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_repetition_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using a Parslet Repetition' do
15
+
16
+ it 'should raise if "parseable" argument is nil' do
17
+ lambda { ParsletRepetition.new(nil, 0) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should raise if "min" argument is nil' do
21
+ lambda { ParsletRepetition.new('foo'.to_parseable, nil) }.should raise_error(ArgumentError)
22
+ end
23
+
24
+ it 'should raise if pass nil string for parsing' do
25
+ lambda { ParsletRepetition.new('foo'.to_parseable, 0).parse(nil) }.should raise_error(ArgumentError)
26
+ end
27
+
28
+ it 'should be able to match "zero or more" times (like "*" in regular expressions)' do
29
+ parslet = ParsletRepetition.new('foo'.to_parseable, 0)
30
+ lambda { parslet.parse('bar') }.should throw_symbol(:ZeroWidthParseSuccess) # zero times
31
+ parslet.parse('foo').should == 'foo' # one time
32
+ parslet.parse('foofoo').should == ['foo', 'foo'] # two times
33
+ parslet.parse('foofoofoobar').should == ['foo', 'foo', 'foo'] # three times
34
+ end
35
+
36
+ it 'should be able to match "zero or one" times (like "?" in regular expressions)' do
37
+ parslet = ParsletRepetition.new('foo'.to_parseable, 0, 1)
38
+ lambda { parslet.parse('bar') }.should throw_symbol(:ZeroWidthParseSuccess) # zero times
39
+ parslet.parse('foo').should == 'foo' # one time
40
+ parslet.parse('foofoo').should == 'foo' # stop at one time
41
+ end
42
+
43
+ it 'should be able to match "one or more" times (like "+" in regular expressions)' do
44
+ parslet = ParsletRepetition.new('foo'.to_parseable, 1)
45
+ lambda { parslet.parse('bar') }.should raise_error(ParseError) # zero times (error)
46
+ parslet.parse('foo').should == 'foo' # one time
47
+ parslet.parse('foofoo').should == ['foo', 'foo'] # two times
48
+ parslet.parse('foofoofoobar').should == ['foo', 'foo', 'foo'] # three times
49
+ end
50
+
51
+ it 'should be able to match "between X and Y" times (like {X, Y} in regular expressions)' do
52
+ parslet = ParsletRepetition.new('foo'.to_parseable, 2, 3)
53
+ lambda { parslet.parse('bar') }.should raise_error(ParseError) # zero times (error)
54
+ lambda { parslet.parse('foo') }.should raise_error(ParseError) # one time (error)
55
+ parslet.parse('foofoo').should == ['foo', 'foo'] # two times
56
+ parslet.parse('foofoofoo').should == ['foo', 'foo', 'foo'] # three times
57
+ parslet.parse('foofoofoofoo').should == ['foo', 'foo', 'foo'] # stop at three times
58
+ end
59
+
60
+ it 'matches should be greedy' do
61
+
62
+ # here the ParsletRepetition should consume all the "foos", leaving nothing for the final parslet
63
+ parslet = ParsletRepetition.new('foo'.to_parseable, 1) & 'foo'
64
+ lambda { parslet.parse('foofoofoofoo') }.should raise_error(ParseError)
65
+
66
+ end
67
+
68
+ it 'should be able to compare for equality' do
69
+ ParsletRepetition.new('foo'.to_parseable, 1).should eql(ParsletRepetition.new('foo'.to_parseable, 1))
70
+ ParsletRepetition.new('foo'.to_parseable, 1).should_not eql(ParsletRepetition.new('bar'.to_parseable, 1))
71
+ ParsletRepetition.new('foo'.to_parseable, 1).should_not eql(ParsletRepetition.new('foo'.to_parseable, 2))
72
+ end
73
+
74
+ end
75
+
76
+ end # class Grammar
77
+ end # module Walrus
@@ -0,0 +1,49 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_sequence_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using a Parslet Sequence' do
15
+
16
+ setup do
17
+ @p1 = 'foo'.to_parseable
18
+ @p2 = 'bar'.to_parseable
19
+ end
20
+
21
+ it 'hashes should be the same if initialized with the same parseables' do
22
+ ParsletSequence.new(@p1, @p2).hash.should == ParsletSequence.new(@p1, @p2).hash
23
+ ParsletSequence.new(@p1, @p2).should eql(ParsletSequence.new(@p1, @p2))
24
+ end
25
+
26
+ it 'hashes should (ideally) be different if initialized with different parseables' do
27
+ ParsletSequence.new(@p1, @p2).hash.should_not == ParsletSequence.new('baz'.to_parseable, 'abc'.to_parseable).hash
28
+ ParsletSequence.new(@p1, @p2).should_not eql(ParsletSequence.new('baz'.to_parseable, 'abc'.to_parseable))
29
+ end
30
+
31
+ it 'hashes should be different compared to other similar classes even if initialized with the same parseables' do
32
+ ParsletSequence.new(@p1, @p2).hash.should_not == ParsletChoice.new(@p1, @p2).hash
33
+ ParsletSequence.new(@p1, @p2).should_not eql(ParsletChoice.new(@p1, @p2))
34
+ end
35
+
36
+ it 'should be able to use Parslet Choice instances as keys in a hash' do
37
+ hash = {}
38
+ key1 = ParsletSequence.new(@p1, @p2)
39
+ key2 = ParsletSequence.new('baz'.to_parseable, 'abc'.to_parseable)
40
+ hash[:key1] = 'foo'
41
+ hash[:key2] = 'bar'
42
+ hash[:key1].should == 'foo'
43
+ hash[:key2].should == 'bar'
44
+ end
45
+
46
+ end
47
+
48
+ end # class Grammar
49
+ end # module Walrus
@@ -0,0 +1,23 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/parslet_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ describe 'using a parslet' do
15
+
16
+ it 'should complain if sent "parse" message (Parslet is an abstract superclass, "parse" is the responsibility of the subclasses)' do
17
+ lambda { Parslet.new.parse('bar') }.should raise_error(NotImplementedError)
18
+ end
19
+
20
+ end
21
+
22
+ end # class Grammar
23
+ end # module Walrus
@@ -0,0 +1,53 @@
1
+ # Copyright 2007 Wincent Colaiuta
2
+ # This program is distributed in the hope that it will be useful, but WITHOUT
3
+ # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4
+ # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5
+ # in the accompanying file, "LICENSE.txt", for more details.
6
+ #
7
+ # $Id: /mirrors/Walrus/trunk/walrus/spec/grammar/predicate_spec.rb 6702 2007-04-09T15:04:40.448669Z wincent $
8
+
9
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
10
+
11
+ module Walrus
12
+ class Grammar
13
+
14
+ autoload(:AndPredicate, 'walrus/grammar/and_predicate')
15
+ autoload(:NotPredicate, 'walrus/grammar/not_predicate')
16
+
17
+ describe 'using a predicate' do
18
+
19
+ it 'should raise an ArgumentError if initialized with nil' do
20
+ lambda { Predicate.new(nil) }.should raise_error(ArgumentError)
21
+ end
22
+
23
+ it 'should complain if sent "parse" message (Predicate abstract superclass, "parse" is the responsibility of the subclasses)' do
24
+ lambda { Predicate.new('foo').parse('bar') }.should raise_error(NotImplementedError)
25
+ end
26
+
27
+ it 'should be able to compare predicates for equality' do
28
+ Predicate.new('foo').should eql(Predicate.new('foo'))
29
+ Predicate.new('foo').should_not eql(Predicate.new('bar'))
30
+ end
31
+
32
+ it '"and" and "not" predicates should yield different hashes even if initialized with the same "parseable"' do
33
+
34
+ parseable = 'foo'.to_parseable
35
+ p1 = Predicate.new(parseable)
36
+ p2 = AndPredicate.new(parseable)
37
+ p3 = NotPredicate.new(parseable)
38
+
39
+ p1.hash.should_not == p2.hash
40
+ p2.hash.should_not == p3.hash
41
+ p3.hash.should_not == p1.hash
42
+
43
+ p1.should_not eql(p2)
44
+ p2.should_not eql(p3)
45
+ p3.should_not eql(p1)
46
+
47
+ end
48
+
49
+
50
+ end
51
+
52
+ end # class Grammar
53
+ end # module Walrus