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,106 @@
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/additions/string_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 'iterating over a string' do
15
+
16
+ # formerly a bug: the StringScanner used under the covers was returnin nil (stopping) on hitting a newline
17
+ it 'should be able to iterate over strings containing newlines' do
18
+ chars = []
19
+ "hello\nworld".each_char { |c| chars << c }
20
+ chars.length.should == 11
21
+ chars[0].should == 'h'
22
+ chars[1].should == 'e'
23
+ chars[2].should == 'l'
24
+ chars[3].should == 'l'
25
+ chars[4].should == 'o'
26
+ chars[5].should == "\n"
27
+ chars[6].should == 'w'
28
+ chars[7].should == 'o'
29
+ chars[8].should == 'r'
30
+ chars[9].should == 'l'
31
+ chars[10].should == 'd'
32
+ end
33
+
34
+ end
35
+
36
+ describe 'working with Unicode strings' do
37
+
38
+ setup do
39
+ @string = 'Unicode €!' # € (Euro) is a three-byte UTF-8 glyph: "\342\202\254"
40
+ end
41
+
42
+ it 'the "each_char" method should work with multibyte characters' do
43
+ chars = []
44
+ @string.each_char { |c| chars << c }
45
+ chars.length.should == 10
46
+ chars[0].should == 'U'
47
+ chars[1].should == 'n'
48
+ chars[2].should == 'i'
49
+ chars[3].should == 'c'
50
+ chars[4].should == 'o'
51
+ chars[5].should == 'd'
52
+ chars[6].should == 'e'
53
+ chars[7].should == ' '
54
+ chars[8].should == '€'
55
+ chars[9].should == '!'
56
+ end
57
+
58
+ it 'the "chars" method should work with multibyte characters' do
59
+ @string.chars.should == ['U', 'n', 'i', 'c', 'o', 'd', 'e', ' ', '€', '!']
60
+ end
61
+
62
+ it 'should be able to use "enumerator" convenience method to get a string enumerator' do
63
+ enumerator = 'hello€'.enumerator
64
+ enumerator.next.should == 'h'
65
+ enumerator.next.should == 'e'
66
+ enumerator.next.should == 'l'
67
+ enumerator.next.should == 'l'
68
+ enumerator.next.should == 'o'
69
+ enumerator.next.should == '€'
70
+ enumerator.next.should be_nil
71
+ end
72
+
73
+ it 'the "jlength" method should correctly report the number of characters in a string' do
74
+ @string.jlength.should == 10
75
+ "€".jlength.should == 1 # three bytes long, but one character
76
+ end
77
+
78
+ end
79
+
80
+ # For more detailed specification of the StringParslet behaviour see string_parslet_spec.rb.
81
+ describe 'using shorthand to get StringParslets from String instances' do
82
+
83
+ it 'chaining two Strings with the "&" operator should yield a two-element sequence' do
84
+ sequence = 'foo' & 'bar'
85
+ sequence.parse('foobar').should == ['foo', 'bar']
86
+ lambda { sequence.parse('no match') }.should raise_error(ParseError)
87
+ end
88
+
89
+ it 'chaining three Strings with the "&" operator should yield a three-element sequence' do
90
+ sequence = 'foo' & 'bar' & '...'
91
+ sequence.parse('foobar...').should == ['foo', 'bar', '...']
92
+ lambda { sequence.parse('no match') }.should raise_error(ParseError)
93
+ end
94
+
95
+ it 'alternating two Strings with the "|" operator should yield a single string' do
96
+ sequence = 'foo' | 'bar'
97
+ sequence.parse('foo').should == 'foo'
98
+ sequence.parse('foobar').should == 'foo'
99
+ sequence.parse('bar').should == 'bar'
100
+ lambda { sequence.parse('no match') }.should raise_error(ParseError)
101
+ end
102
+
103
+ end
104
+
105
+ end # class Grammar
106
+ end # module Walrus
@@ -0,0 +1,29 @@
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/and_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
+ describe 'using an "and predicate"' do
15
+
16
+ it 'should complain on trying to parse a nil string' do
17
+ lambda { AndPredicate.new('irrelevant').parse(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should be able to compare for equality' do
21
+ AndPredicate.new('foo').should eql(AndPredicate.new('foo')) # same
22
+ AndPredicate.new('foo').should_not eql(AndPredicate.new('bar')) # different
23
+ AndPredicate.new('foo').should_not eql(Predicate.new('foo')) # same, but different classes
24
+ end
25
+
26
+ end
27
+
28
+ end # class Grammar
29
+ 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/continuation_wrapper_exception_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 'creating a continuation wrapper exception' do
15
+
16
+ it 'should complain if initialized with nil' do
17
+ lambda { ContinuationWrapperException.new(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ end
21
+
22
+ end # class Grammar
23
+ end # module Walrus
@@ -0,0 +1,41 @@
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/match_data_wrapper_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 match data object' do
15
+
16
+ setup do
17
+ 'hello agent' =~ /(\w+)(\s+)(\w+)/
18
+ @match = MatchDataWrapper.new($~)
19
+ end
20
+
21
+ it 'should raise if initialized with nil' do
22
+ lambda { MatchDataWrapper.new(nil) }.should raise_error(ArgumentError)
23
+ end
24
+
25
+ it 'stored match data should persist after multiple matches are executed' do
26
+ original = @match.match_data # store original value
27
+ 'foo' =~ /foo/ # clobber $~
28
+ @match.match_data.should == original # confirm that stored value is still intact
29
+ end
30
+
31
+ it 'comparisons with Strings should work automatically without having to call the "to_s" method' do
32
+ @match.should == 'hello agent' # normal order
33
+ 'hello agent'.should == @match # reverse order
34
+ @match.should_not == 'foobar' # inverse test sense (not equal)
35
+ 'foobar'.should_not == @match
36
+ end
37
+
38
+ end
39
+
40
+ end # class Grammar
41
+ end # module Walrus
@@ -0,0 +1,112 @@
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/memoizing_cache_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
+ class MemoizingCache
15
+
16
+ describe 'using the NoValueForKey class' do
17
+
18
+ it 'NoValueForKey should be a singleton' do
19
+ lambda { NoValueForKey.new }.should raise_error
20
+ NoValueForKey.instance.object_id.should == NoValueForKey.instance.object_id
21
+ end
22
+
23
+ it 'should be able to use NoValueForKey as the default value for a hash' do
24
+ hash = Hash.new(NoValueForKey.instance)
25
+ hash.default.should == NoValueForKey.instance
26
+ hash[:foo].should == NoValueForKey.instance
27
+ hash[:foo] = 'bar'
28
+ hash[:foo].should == 'bar'
29
+ hash[:bar].should == NoValueForKey.instance
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ describe 'using a memoizing cache' do
37
+
38
+ it 'should be able to parse with memoizing turned on' do
39
+
40
+ end
41
+
42
+ it 'should be able to parse with memoizing turned on' do
43
+
44
+ end
45
+
46
+ it 'parsing with memoization turned on should be faster' do
47
+
48
+ end
49
+
50
+ end
51
+
52
+ # left-recursion is enabled by code in the memoizer and elsewhere; keep the specs here for want of a better place
53
+ describe 'working with left-recursive rules' do
54
+
55
+ it 'circular rules should cause a short-circuit' do
56
+ grammar = Grammar.subclass('InfiniteLoop') do
57
+ starting_symbol :a
58
+ rule :a, :a # a bone-headed rule
59
+ end
60
+ lambda { grammar.parse('anything') }.should raise_error(LeftRecursionException)
61
+ end
62
+
63
+ it 'shortcuiting should not be fatal if a valid alternative is present' do
64
+ grammar = Grammar.subclass('AlmostInfinite') do
65
+ starting_symbol :a
66
+ rule :a, :a | :b # slightly less bone-headed
67
+ rule :b, 'foo'
68
+ end
69
+ grammar.parse('foo').should == 'foo'
70
+ end
71
+
72
+ it 'should retry after short-circuiting if valid continuation point' do
73
+
74
+ grammar = Grammar.subclass('MuchMoreRealisticExample') do
75
+ starting_symbol :a
76
+ rule :a, :a & :b | :b
77
+ rule :b, 'foo'
78
+ end
79
+
80
+ # note the right associativity
81
+ grammar.parse('foo').should == 'foo'
82
+ grammar.parse('foofoo').should == ['foo', 'foo']
83
+ grammar.parse('foofoofoo').should == [['foo', 'foo'], 'foo']
84
+ grammar.parse('foofoofoofoo').should == [[['foo', 'foo'], 'foo'], 'foo']
85
+ grammar.parse('foofoofoofoofoo').should == [[[['foo', 'foo'], 'foo'], 'foo'], 'foo']
86
+
87
+ end
88
+
89
+ it 'right associativity should work when building AST nodes' do
90
+
91
+ grammar = Grammar.subclass('RightAssociativeAdditionExample') do
92
+ starting_symbol :addition_expression
93
+ rule :term, /\d+/
94
+ rule :addition_expression, :addition_expression & '+'.skip & :term | :term
95
+ production :addition_expression.build(:node, :left, :right)
96
+
97
+ # TODO: syntax for expressing alternate production?
98
+
99
+ end
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+ end
108
+
109
+ end
110
+
111
+ end # class Grammar
112
+ end # module Walrus
@@ -0,0 +1,126 @@
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/node_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 'working in the default namespace' do
15
+
16
+ it 'should complain if passed nil as subclass name' do
17
+ lambda { Node.subclass(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should be able to create a Node subclass (default namespace)' do
21
+
22
+ # passing a string
23
+ Node.subclass('FooNode').should_not be_nil
24
+ FooNode.class.should == Class # meta class
25
+ FooNode.new('blah').class.should == FooNode
26
+
27
+ # passing a symbol
28
+ Node.subclass(:BarNode).should_not be_nil
29
+ BarNode.class.should == Class # meta class
30
+ BarNode.new('blah').class.should == BarNode
31
+
32
+ end
33
+
34
+ it 'should be able to create a Node subclass (explicit namespace, Walrus::Grammar)' do
35
+
36
+ # default accessor, lexeme
37
+ Node.subclass('ExplicitNamespaceNode', Walrus::Grammar).should_not be_nil
38
+ ExplicitNamespaceNode.new('hi').lexeme.should == 'hi'
39
+
40
+ # override default accessor
41
+ Node.subclass('ExplicitNamespaceNode2', Walrus::Grammar, :foo).should_not be_nil
42
+ ExplicitNamespaceNode2.new('hi').foo.should == 'hi'
43
+
44
+ # multiple accessors
45
+ Node.subclass('ExplicitNamespaceNode3', Walrus::Grammar, :foo, :bar).should_not be_nil
46
+ n = ExplicitNamespaceNode3.new('hi', 'world')
47
+ n.foo.should == 'hi'
48
+ n.bar.should == 'world'
49
+
50
+ end
51
+
52
+ it 'should be able to create a Node subclass (totally custom namespace)' do
53
+
54
+ # default accessor, lexeme
55
+ Node.subclass('CustomNamespaceNode', Walrus).should_not be_nil
56
+ CustomNamespaceNode.new('hi').lexeme.should == 'hi'
57
+
58
+ # override default accessor
59
+ Node.subclass('CustomNamespaceNode2', Walrus, :foo).should_not be_nil
60
+ CustomNamespaceNode2.new('hi').foo.should == 'hi'
61
+
62
+ # multiple accessors
63
+ Node.subclass('CustomNamespaceNode3', Walrus, :foo, :bar).should_not be_nil
64
+ n = CustomNamespaceNode3.new('hi', 'world')
65
+ n.foo.should == 'hi'
66
+ n.bar.should == 'world'
67
+
68
+ end
69
+
70
+ it 'read accessors should work on a Node subclasses' do
71
+
72
+ # default accessor, lexeme
73
+ Node.subclass('AmazingNode')
74
+ AmazingNode.new('xyz').lexeme.should == 'xyz'
75
+
76
+ # single custom accessor
77
+ Node.subclass('AmazingerNode', Walrus::Grammar, :foo)
78
+ AmazingerNode.new('bar').foo.should == 'bar'
79
+
80
+ # two custom accessors
81
+ Node.subclass('AmazingestNode', Walrus::Grammar, :bar, :baz)
82
+ node = AmazingestNode.new('hello', 'world')
83
+ node.bar.should == 'hello'
84
+ node.baz.should == 'world'
85
+
86
+ end
87
+
88
+ it 'should complain if initialize called with missing arguments' do
89
+
90
+ # default accessor, lexeme
91
+ Node.subclass('AmazingNode2')
92
+ lambda { AmazingNode2.new }.should raise_error(ArgumentError)
93
+
94
+ # single custom accessor
95
+ Node.subclass('AmazingerNode2', Walrus::Grammar, :foo)
96
+ lambda { AmazingerNode2.new }.should raise_error(ArgumentError)
97
+
98
+ # two custom accessors
99
+ Node.subclass('AmazingestNode2', Walrus::Grammar, :bar, :baz)
100
+ lambda { AmazingestNode2.new }.should raise_error(ArgumentError) # missing both arguments
101
+ lambda { AmazingestNode2.new('hello') }.should raise_error(ArgumentError) # missing one argument
102
+
103
+ end
104
+
105
+ it 'should be able to subclass a Node subclass' do
106
+
107
+ Node.subclass('FirstSubclass')
108
+ FirstSubclass.subclass('Grandchild').should_not be_nil
109
+ Grandchild.new('foo').class.should == Grandchild
110
+ Grandchild.new('hello').lexeme.should == 'hello'
111
+
112
+ end
113
+
114
+ it 'read accessors should work on a subclass of a Node subclass' do
115
+ Node.subclass('MySubclass')
116
+ MySubclass.subclass('OtherSubclass', Walrus::Grammar, :from, :to).should_not be_nil
117
+ s = OtherSubclass.new('Bob', 'Alice')
118
+ s.from.should == 'Bob'
119
+ s.to.should == 'Alice'
120
+ end
121
+
122
+ end
123
+
124
+ end # class Grammar
125
+ end # module Walrus
126
+
@@ -0,0 +1,29 @@
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/not_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
+ describe 'using a "not predicate"' do
15
+
16
+ it 'should complain on trying to parse a nil string' do
17
+ lambda { NotPredicate.new('irrelevant').parse(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should be able to compare for equality' do
21
+ NotPredicate.new('foo').should eql(NotPredicate.new('foo')) # same
22
+ NotPredicate.new('foo').should_not eql(NotPredicate.new('bar')) # different
23
+ NotPredicate.new('foo').should_not eql(Predicate.new('foo')) # same, but different classes
24
+ end
25
+
26
+ end
27
+
28
+ end # class Grammar
29
+ end # module Walrus
@@ -0,0 +1,172 @@
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/parser_state_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 parser state object' do
15
+
16
+ setup do
17
+ @base_string = 'this is the string to be parsed'
18
+ @state = ParserState.new(@base_string)
19
+ end
20
+
21
+ it 'should raise an ArgumentError if initialized with nil' do
22
+ lambda { ParserState.new(nil) }.should raise_error(ArgumentError)
23
+ end
24
+
25
+ it 'before parsing has started "remainder" should equal the entire string' do
26
+ @state.remainder.should == @base_string
27
+ end
28
+
29
+ it 'before parsing has started "remainder" should equal the entire string (when string is an empty string)' do
30
+ ParserState.new('').remainder.should == ''
31
+ end
32
+
33
+ it 'before parsing has started "results" should be empty' do
34
+ @state.results.should be_empty
35
+ end
36
+
37
+ it '"parsed" should complain if passed nil' do
38
+ lambda { @state.parsed(nil) }.should raise_error(ArgumentError)
39
+ end
40
+
41
+ it '"skipped" should complain if passed nil' do
42
+ lambda { @state.skipped(nil) }.should raise_error(ArgumentError)
43
+ end
44
+
45
+ it '"parsed" should return the remainder of the string' do
46
+ @state.parsed('this is the ').should == 'string to be parsed'
47
+ @state.parsed('string ').should == 'to be parsed'
48
+ @state.parsed('to be parsed').should == ''
49
+ end
50
+
51
+ it '"skipped" should return the remainder of the string' do
52
+ @state.skipped('this is the ').should == 'string to be parsed'
53
+ @state.skipped('string ').should == 'to be parsed'
54
+ @state.skipped('to be parsed').should == ''
55
+ end
56
+
57
+ it '"results" should return an unwrapped parsed result (for single results)' do
58
+ @state.parsed('this')
59
+ @state.results.should == 'this'
60
+ end
61
+
62
+ it 'skipped substrings should not appear in "results"' do
63
+ @state.skipped('this')
64
+ @state.results.should be_empty
65
+ end
66
+
67
+ it 'should return an array of the parsed results (for multiple results)' do
68
+ @state.parsed('this ')
69
+ @state.parsed('is ')
70
+ @state.results.should == ['this ', 'is ']
71
+ end
72
+
73
+ it 'should work when the entire string is consumed in a single operation (using "parsed")' do
74
+ @state.parsed(@base_string).should == ''
75
+ @state.results.should == @base_string
76
+ end
77
+
78
+ it 'should work when the entire string is consumed in a single operation (using "skipped")' do
79
+ @state.skipped(@base_string).should == ''
80
+ @state.results.should be_empty
81
+ end
82
+
83
+ it '"parsed" should complain if passed something that doesn\'t respond to the "line_end" and "column_end" messages' do
84
+
85
+ # line_end
86
+ my_mock = mock('mock_which_does_not_implement_line_end', :null_object => true)
87
+ my_mock.should_receive(:line_end).and_raise(NoMethodError)
88
+ lambda { @state.parsed(my_mock) }.should raise_error(NoMethodError)
89
+
90
+ # column_end
91
+ my_mock = mock('mock_which_does_not_implement_column_end', :null_object => true)
92
+ my_mock.should_receive(:column_end).and_raise(NoMethodError)
93
+ lambda { @state.parsed(my_mock) }.should raise_error(NoMethodError)
94
+
95
+ end
96
+
97
+ it '"skipped" should complain if passed something that doesn\'t respond to the "line_end" and "column_end" messages' do
98
+
99
+ # line_end
100
+ my_mock = mock('mock_which_does_not_implement_line_end', :null_object => true)
101
+ my_mock.should_receive(:line_end).and_raise(NoMethodError)
102
+ lambda { @state.skipped(my_mock) }.should raise_error(NoMethodError)
103
+
104
+ # column_end
105
+ my_mock = mock('mock_which_does_not_implement_column_end', :null_object => true)
106
+ my_mock.should_receive(:column_end).and_raise(NoMethodError)
107
+ lambda { @state.skipped(my_mock) }.should raise_error(NoMethodError)
108
+
109
+ end
110
+
111
+ it 'should be able to mix use of "parsed" and "skipped" methods' do
112
+
113
+ # first example
114
+ @state.skipped('this is the ').should == 'string to be parsed'
115
+ @state.results.should be_empty
116
+ @state.parsed('string ').should == 'to be parsed'
117
+ @state.results.should == 'string '
118
+ @state.skipped('to be parsed').should == ''
119
+ @state.results.should == 'string '
120
+
121
+ # second example (add this test to isolate a bug in another specification)
122
+ state = ParserState.new('foo1...')
123
+ state.skipped('foo').should == '1...'
124
+ state.remainder.should == '1...'
125
+ state.results.should be_empty
126
+ state.parsed('1').should == '...'
127
+ state.remainder.should == '...'
128
+ state.results.should == '1'
129
+
130
+ end
131
+
132
+ it '"parsed" and "results" methods should work with multi-byte Unicode strings' do
133
+
134
+ # basic test
135
+ state = ParserState.new('400€, foo')
136
+ state.remainder.should == '400€, foo'
137
+ state.parsed('40').should == '0€, foo'
138
+ state.results.should == '40'
139
+ state.parsed('0€, ').should == 'foo'
140
+ state.results.should == ['40', '0€, ']
141
+ state.parsed('foo').should == ''
142
+ state.results.should == ['40', '0€, ', 'foo']
143
+
144
+ # test with newlines before and after multi-byte chars
145
+ state = ParserState.new("400\n or more €...\nfoo")
146
+ state.remainder.should == "400\n or more €...\nfoo"
147
+ state.parsed("400\n or more").should == " €...\nfoo"
148
+ state.results.should == "400\n or more"
149
+ state.parsed(' €..').should == ".\nfoo"
150
+ state.results.should == ["400\n or more", ' €..']
151
+ state.parsed(".\nfoo").should == ''
152
+ state.results.should == ["400\n or more", ' €..', ".\nfoo"]
153
+
154
+ end
155
+
156
+ it '"skipped" and "results" methods should work with multi-byte Unicode strings' do
157
+ state = ParserState.new('400€, foo')
158
+ state.remainder.should == '400€, foo'
159
+ state.skipped('4').should == '00€, foo'
160
+ state.results.should be_empty
161
+ state.parsed('0').should == '0€, foo'
162
+ state.results.should == '0'
163
+ state.skipped('0€, ').should == 'foo'
164
+ state.results.should == '0'
165
+ state.parsed('foo').should == ''
166
+ state.results.should == ['0', 'foo']
167
+ end
168
+
169
+ end
170
+
171
+ end # class Grammar
172
+ end # module Walrus