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,52 @@
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/proc_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 ProcParslet' do
15
+
16
+ setup do
17
+ @parslet = lambda do |string, options|
18
+ if string == 'foobar' : string
19
+ else raise ParseError.new('expected foobar by got "%s"' + string.to_s)
20
+ end
21
+ end.to_parseable
22
+ end
23
+
24
+ it 'should raise an ArgumentError if initialized with nil' do
25
+ lambda { ProcParslet.new(nil) }.should raise_error(ArgumentError)
26
+ end
27
+
28
+ it 'should complain if asked to parse nil' do
29
+ lambda { @parslet.parse(nil) }.should raise_error(ArgumentError)
30
+ end
31
+
32
+ it 'should raise ParseError if unable to parse' do
33
+ lambda { @parslet.parse('bar') }.should raise_error(ParseError)
34
+ end
35
+
36
+ it 'should return a parsed value if able to parse' do
37
+ @parslet.parse('foobar').should == 'foobar'
38
+ end
39
+
40
+ it 'should be able to compare parslets for equality' do
41
+
42
+ # in practice only parslets created with the exact same Proc instance will be eql because Proc returns different hashes for each
43
+ @parslet.should eql(@parslet.clone)
44
+ @parslet.should eql(@parslet.dup)
45
+ @parslet.should_not eql(lambda { nil }.to_parseable)
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end # class Grammar
52
+ end # module Walrus
@@ -0,0 +1,347 @@
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/regexp_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 regexp parslet' do
15
+
16
+ setup do
17
+ @parslet = RegexpParslet.new(/[a-zA-Z_][a-zA-Z0-9_]*/)
18
+ end
19
+
20
+ it 'should raise an ArgumentError if initialized with nil' do
21
+ lambda { RegexpParslet.new(nil) }.should raise_error(ArgumentError)
22
+ end
23
+
24
+ it 'parse should succeed if the input string matches' do
25
+ lambda { @parslet.parse('an_identifier') }.should_not raise_error
26
+ lambda { @parslet.parse('An_Identifier') }.should_not raise_error
27
+ lambda { @parslet.parse('AN_IDENTIFIER') }.should_not raise_error
28
+ lambda { @parslet.parse('an_identifier1') }.should_not raise_error
29
+ lambda { @parslet.parse('An_Identifier1') }.should_not raise_error
30
+ lambda { @parslet.parse('AN_IDENTIFIER1') }.should_not raise_error
31
+ lambda { @parslet.parse('a') }.should_not raise_error
32
+ lambda { @parslet.parse('A') }.should_not raise_error
33
+ lambda { @parslet.parse('a9') }.should_not raise_error
34
+ lambda { @parslet.parse('A9') }.should_not raise_error
35
+ lambda { @parslet.parse('_identifier') }.should_not raise_error
36
+ lambda { @parslet.parse('_Identifier') }.should_not raise_error
37
+ lambda { @parslet.parse('_IDENTIFIER') }.should_not raise_error
38
+ lambda { @parslet.parse('_9Identifier') }.should_not raise_error
39
+ lambda { @parslet.parse('_') }.should_not raise_error
40
+ end
41
+
42
+ it 'parse should succeed if the input string matches, even if it continues after the match' do
43
+ lambda { @parslet.parse('an_identifier, more') }.should_not raise_error
44
+ lambda { @parslet.parse('An_Identifier, more') }.should_not raise_error
45
+ lambda { @parslet.parse('AN_IDENTIFIER, more') }.should_not raise_error
46
+ lambda { @parslet.parse('an_identifier1, more') }.should_not raise_error
47
+ lambda { @parslet.parse('An_Identifier1, more') }.should_not raise_error
48
+ lambda { @parslet.parse('AN_IDENTIFIER1, more') }.should_not raise_error
49
+ lambda { @parslet.parse('a, more') }.should_not raise_error
50
+ lambda { @parslet.parse('A, more') }.should_not raise_error
51
+ lambda { @parslet.parse('a9, more') }.should_not raise_error
52
+ lambda { @parslet.parse('A9, more') }.should_not raise_error
53
+ lambda { @parslet.parse('_identifier, more') }.should_not raise_error
54
+ lambda { @parslet.parse('_Identifier, more') }.should_not raise_error
55
+ lambda { @parslet.parse('_IDENTIFIER, more') }.should_not raise_error
56
+ lambda { @parslet.parse('_9Identifier, more') }.should_not raise_error
57
+ lambda { @parslet.parse('_, more') }.should_not raise_error
58
+ end
59
+
60
+ it 'parse should return a MatchDataWrapper object' do
61
+ @parslet.parse('an_identifier').should == 'an_identifier'
62
+ @parslet.parse('an_identifier, more').should == 'an_identifier'
63
+ end
64
+
65
+ it 'parse should raise an ArgumentError if passed nil' do
66
+ lambda { @parslet.parse(nil) }.should raise_error(ArgumentError)
67
+ end
68
+
69
+ it 'parse should raise a ParseError if the input string does not match' do
70
+ lambda { @parslet.parse('9') }.should raise_error(ParseError) # a number is not a valid identifier
71
+ lambda { @parslet.parse('9fff') }.should raise_error(ParseError) # identifiers must not start with numbers
72
+ lambda { @parslet.parse(' identifier') }.should raise_error(ParseError) # note the leading whitespace
73
+ lambda { @parslet.parse('') }.should raise_error(ParseError) # empty strings can't match
74
+ end
75
+
76
+ it 'should be able to compare parslets for equality' do
77
+ /foo/.to_parseable.should eql(/foo/.to_parseable) # equal
78
+ /foo/.to_parseable.should_not eql(/bar/.to_parseable) # different
79
+ /foo/.to_parseable.should_not eql(/Foo/.to_parseable) # differing only in case
80
+ /foo/.to_parseable.should_not eql('foo') # totally different classes
81
+ end
82
+
83
+ it 'should accurately pack line and column ends into whatever gets returned from "parse"' do
84
+
85
+ # single word
86
+ parslet = /.+/m.to_parseable
87
+ result = parslet.parse('hello')
88
+ result.line_end.should == 0
89
+ result.column_end.should == 5
90
+
91
+ # single word with newline at end (UNIX style)
92
+ result = parslet.parse("hello\n")
93
+ result.line_end.should == 1
94
+ result.column_end.should == 0
95
+
96
+ # single word with newline at end (Classic Mac style)
97
+ result = parslet.parse("hello\r")
98
+ result.line_end.should == 1
99
+ result.column_end.should == 0
100
+
101
+ # single word with newline at end (Windows style)
102
+ result = parslet.parse("hello\r\n")
103
+ result.line_end.should == 1
104
+ result.column_end.should == 0
105
+
106
+ # two lines (UNIX style)
107
+ result = parslet.parse("hello\nworld")
108
+ result.line_end.should == 1
109
+ result.column_end.should == 5
110
+
111
+ # two lines (Classic Mac style)
112
+ result = parslet.parse("hello\rworld")
113
+ result.line_end.should == 1
114
+ result.column_end.should == 5
115
+
116
+ # two lines (Windows style)
117
+ result = parslet.parse("hello\r\nworld")
118
+ result.line_end.should == 1
119
+ result.column_end.should == 5
120
+
121
+ end
122
+
123
+ # in the case of RegexpParslets, the "last successfully scanned position" is always 0, 0
124
+ it 'line and column end should reflect last succesfully scanned position prior to failure' do
125
+
126
+ # fail right at start
127
+ parslet = /hello\r\nworld/.to_parseable
128
+ begin
129
+ parslet.parse('foobar')
130
+ rescue ParseError => e
131
+ exception = e
132
+ end
133
+ exception.line_end.should == 0
134
+ exception.column_end.should == 0
135
+
136
+ # fail after 1 character
137
+ begin
138
+ parslet.parse('hfoobar')
139
+ rescue ParseError => e
140
+ exception = e
141
+ end
142
+ exception.line_end.should == 0
143
+ exception.column_end.should == 0
144
+
145
+ # fail after end-of-line
146
+ begin
147
+ parslet.parse("hello\r\nfoobar")
148
+ rescue ParseError => e
149
+ exception = e
150
+ end
151
+ exception.line_end.should == 0
152
+ exception.column_end.should == 0
153
+
154
+ end
155
+
156
+ end
157
+
158
+ describe 'chaining two regexp parslets together' do
159
+
160
+ it 'parslets should work in specified order' do
161
+ parslet = RegexpParslet.new(/foo.\d/) & RegexpParslet.new(/bar.\d/)
162
+ parslet.parse('foo_1bar_2').should == ['foo_1', 'bar_2']
163
+ end
164
+
165
+ # Parser Expression Grammars match greedily
166
+ it 'parslets should match greedily' do
167
+
168
+ # the first parslet should gobble up the entire string, preventing the second parslet from succeeding
169
+ parslet = RegexpParslet.new(/foo.+\d/) & RegexpParslet.new(/bar.+\d/)
170
+ lambda { parslet.parse('foo_1bar_2') }.should raise_error(ParseError)
171
+
172
+ end
173
+
174
+ end
175
+
176
+ describe 'alternating two regexp parslets' do
177
+
178
+ it 'either parslet should apply to generate a match' do
179
+ parslet = RegexpParslet.new(/\d+/) | RegexpParslet.new(/[A-Z]+/)
180
+ parslet.parse('ABC').should == 'ABC'
181
+ parslet.parse('123').should == '123'
182
+ end
183
+
184
+ it 'should fail if no parslet generates a match' do
185
+ parslet = RegexpParslet.new(/\d+/) | RegexpParslet.new(/[A-Z]+/)
186
+ lambda { parslet.parse('abc') }.should raise_error(ParseError)
187
+ end
188
+
189
+ it 'parslets should be tried in left-to-right order' do
190
+
191
+ # in this case the first parslet should win even though the second one is also a valid match
192
+ parslet = RegexpParslet.new(/(.)(..)/) | RegexpParslet.new(/(..)(.)/)
193
+ match_data = parslet.parse('abc').match_data
194
+ match_data[1].should == 'a'
195
+ match_data[2].should == 'bc'
196
+
197
+ # here we swap the order; again the first parslet should win
198
+ parslet = RegexpParslet.new(/(..)(.)/) | RegexpParslet.new(/(.)(..)/)
199
+ match_data = parslet.parse('abc').match_data
200
+ match_data[1].should == 'ab'
201
+ match_data[2].should == 'c'
202
+
203
+ end
204
+
205
+ end
206
+
207
+ describe 'chaining three regexp parslets' do
208
+
209
+ it 'parslets should work in specified order' do
210
+ parslet = RegexpParslet.new(/foo.\d/) & RegexpParslet.new(/bar.\d/) & RegexpParslet.new(/.../)
211
+ parslet.parse('foo_1bar_2ABC').should == ['foo_1', 'bar_2', 'ABC']
212
+ end
213
+
214
+ end
215
+
216
+ describe 'alternating three regexp parslets' do
217
+
218
+ it 'any parslet should apply to generate a match' do
219
+ parslet = RegexpParslet.new(/\d+/) | RegexpParslet.new(/[A-Z]+/) | RegexpParslet.new(/[a-z]+/)
220
+ parslet.parse('ABC').should == 'ABC'
221
+ parslet.parse('123').should == '123'
222
+ parslet.parse('abc').should == 'abc'
223
+ end
224
+
225
+ it 'should fail if no parslet generates a match' do
226
+ parslet = RegexpParslet.new(/\d+/) | RegexpParslet.new(/[A-Z]+/) | RegexpParslet.new(/[a-z]+/)
227
+ lambda { parslet.parse(':::') }.should raise_error(ParseError)
228
+ end
229
+
230
+ it 'parslets should be tried in left-to-right order' do
231
+
232
+ # in this case the first parslet should win even though the others also produce valid matches
233
+ parslet = RegexpParslet.new(/(.)(..)/) | RegexpParslet.new(/(..)(.)/) | RegexpParslet.new(/(...)/)
234
+ match_data = parslet.parse('abc').match_data
235
+ match_data[1].should == 'a'
236
+ match_data[2].should == 'bc'
237
+
238
+ # here we swap the order; again the first parslet should win
239
+ parslet = RegexpParslet.new(/(..)(.)/) | RegexpParslet.new(/(.)(..)/) | RegexpParslet.new(/(...)/)
240
+ match_data = parslet.parse('abc').match_data
241
+ match_data[1].should == 'ab'
242
+ match_data[2].should == 'c'
243
+
244
+ # similar test but this time the first parslet can't win (doesn't match)
245
+ parslet = RegexpParslet.new(/foo/) | RegexpParslet.new(/(...)/) | RegexpParslet.new(/(.)(..)/)
246
+ match_data = parslet.parse('abc').match_data
247
+ match_data[1].should == 'abc'
248
+
249
+ end
250
+
251
+ end
252
+
253
+ describe 'combining chaining and alternation' do
254
+
255
+ it 'chaining should having higher precedence than alternation' do
256
+
257
+ # equivalent to /foo/ | ( /bar/ & /abc/ )
258
+ parslet = RegexpParslet.new(/foo/) | RegexpParslet.new(/bar/) & RegexpParslet.new(/abc/)
259
+ parslet.parse('foo').should == 'foo' # succeed on first choice
260
+ parslet.parse('barabc').should == ['bar', 'abc'] # succeed on alternate path
261
+ lambda { parslet.parse('bar...') }.should raise_error(ParseError) # fail half-way down alternate path
262
+ lambda { parslet.parse('lemon') }.should raise_error(ParseError) # fail immediately
263
+
264
+ # swap the order, now equivalent to: ( /bar/ & /abc/ ) | /foo/
265
+ parslet = RegexpParslet.new(/bar/) & RegexpParslet.new(/abc/) | RegexpParslet.new(/foo/)
266
+ parslet.parse('barabc').should == ['bar', 'abc'] # succeed on first choice
267
+ parslet.parse('foo').should == 'foo' # succeed on alternate path
268
+ lambda { parslet.parse('bar...') }.should raise_error(ParseError) # fail half-way down first path
269
+ lambda { parslet.parse('lemon') }.should raise_error(ParseError) # fail immediately
270
+
271
+ end
272
+
273
+ it 'should be able to override precedence using parentheses' do
274
+
275
+ # take first example above and make it ( /foo/ | /bar/ ) & /abc/
276
+ parslet = (RegexpParslet.new(/foo/) | RegexpParslet.new(/bar/)) & RegexpParslet.new(/abc/)
277
+ parslet.parse('fooabc').should == ['foo', 'abc'] # first choice
278
+ parslet.parse('barabc').should == ['bar', 'abc'] # second choice
279
+ lambda { parslet.parse('foo...') }.should raise_error(ParseError) # fail in second half
280
+ lambda { parslet.parse('bar...') }.should raise_error(ParseError) # another way of failing in second half
281
+ lambda { parslet.parse('foo') }.should raise_error(ParseError) # another way of failing in second half
282
+ lambda { parslet.parse('bar') }.should raise_error(ParseError) # another way of failing in second half
283
+ lambda { parslet.parse('lemon') }.should raise_error(ParseError) # fail immediately
284
+ lambda { parslet.parse('abcfoo') }.should raise_error(ParseError) # order matters
285
+
286
+ # take second example above and make it /bar/ & ( /abc/ | /foo/ )
287
+ parslet = RegexpParslet.new(/bar/) & (RegexpParslet.new(/abc/) | RegexpParslet.new(/foo/))
288
+ parslet.parse('barabc').should == ['bar', 'abc'] # succeed on first choice
289
+ parslet.parse('barfoo').should == ['bar', 'foo'] # second choice
290
+ lambda { parslet.parse('bar...') }.should raise_error(ParseError) # fail in second part
291
+ lambda { parslet.parse('bar') }.should raise_error(ParseError) # another way to fail in second part
292
+ lambda { parslet.parse('lemon') }.should raise_error(ParseError) # fail immediately
293
+ lambda { parslet.parse('abcbar') }.should raise_error(ParseError) # order matters
294
+
295
+ end
296
+
297
+ it 'should be able to include long runs of sequences' do
298
+
299
+ # A & B & C & D | E
300
+ parslet = RegexpParslet.new(/a/) & RegexpParslet.new(/b/) & RegexpParslet.new(/c/) & RegexpParslet.new(/d/) | RegexpParslet.new(/e/)
301
+ parslet.parse('abcd').should == ['a', 'b', 'c', 'd']
302
+ parslet.parse('e').should == 'e'
303
+ lambda { parslet.parse('f') }.should raise_error(ParseError)
304
+
305
+ end
306
+
307
+ it 'should be able to include long runs of options' do
308
+
309
+ # A | B | C | D & E
310
+ parslet = RegexpParslet.new(/a/) | RegexpParslet.new(/b/) | RegexpParslet.new(/c/) | RegexpParslet.new(/d/) & RegexpParslet.new(/e/)
311
+ parslet.parse('a').should == 'a'
312
+ parslet.parse('b').should == 'b'
313
+ parslet.parse('c').should == 'c'
314
+ parslet.parse('de').should == ['d', 'e']
315
+ lambda { parslet.parse('f') }.should raise_error(ParseError)
316
+
317
+ end
318
+
319
+ it 'should be able to alternate repeatedly between sequences and choices' do
320
+
321
+ # A & B | C & D | E
322
+ parslet = RegexpParslet.new(/a/) & RegexpParslet.new(/b/) | RegexpParslet.new(/c/) & RegexpParslet.new(/d/) | RegexpParslet.new(/e/)
323
+ parslet.parse('ab').should == ['a', 'b']
324
+ parslet.parse('cd').should == ['c', 'd']
325
+ parslet.parse('e').should == 'e'
326
+ lambda { parslet.parse('f') }.should raise_error(ParseError)
327
+
328
+ end
329
+
330
+ it 'should be able to combine long runs with alternation' do
331
+
332
+ # A & B & C | D | E | F & G & H
333
+ parslet = RegexpParslet.new(/a/) & RegexpParslet.new(/b/) & RegexpParslet.new(/c/) |
334
+ RegexpParslet.new(/d/) | RegexpParslet.new(/e/) | RegexpParslet.new(/f/) &
335
+ RegexpParslet.new(/g/) & RegexpParslet.new(/h/)
336
+ parslet.parse('abc').should == ['a', 'b', 'c']
337
+ parslet.parse('d').should == 'd'
338
+ parslet.parse('e').should == 'e'
339
+ parslet.parse('fgh').should == ['f', 'g', 'h']
340
+ lambda { parslet.parse('i') }.should raise_error(ParseError)
341
+
342
+ end
343
+
344
+ end
345
+
346
+ end # class Grammar
347
+ end # module Walrus
@@ -0,0 +1,94 @@
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/string_enumerator_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 string enumerator' do
15
+
16
+ it 'should raise an ArgumentError if initialized with nil' do
17
+ lambda { StringEnumerator.new(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should return characters one by one until end of string, then return nil' do
21
+ enumerator = StringEnumerator.new('hello')
22
+ enumerator.next.should == 'h'
23
+ enumerator.next.should == 'e'
24
+ enumerator.next.should == 'l'
25
+ enumerator.next.should == 'l'
26
+ enumerator.next.should == 'o'
27
+ enumerator.next.should be_nil
28
+ end
29
+
30
+ it 'enumerators should be Unicode-aware (UTF-8)' do
31
+ enumerator = StringEnumerator.new('€ cañon')
32
+ enumerator.next.should == '€'
33
+ enumerator.next.should == ' '
34
+ enumerator.next.should == 'c'
35
+ enumerator.next.should == 'a'
36
+ enumerator.next.should == 'ñ'
37
+ enumerator.next.should == 'o'
38
+ enumerator.next.should == 'n'
39
+ enumerator.next.should be_nil
40
+ end
41
+
42
+ # this was a bug
43
+ it 'enumerators should continue past newlines' do
44
+ enumerator = StringEnumerator.new("hello\nworld")
45
+ enumerator.next.should == 'h'
46
+ enumerator.next.should == 'e'
47
+ enumerator.next.should == 'l'
48
+ enumerator.next.should == 'l'
49
+ enumerator.next.should == 'o'
50
+ enumerator.next.should == "\n" # was returning nil here
51
+ enumerator.next.should == 'w'
52
+ enumerator.next.should == 'o'
53
+ enumerator.next.should == 'r'
54
+ enumerator.next.should == 'l'
55
+ enumerator.next.should == 'd'
56
+ end
57
+
58
+ it 'should be able to peek at the next character without actually enumerating' do
59
+ enumerator = StringEnumerator.new('h€llo')
60
+ enumerator.peek.should == 'h' # peek but don't advance
61
+ enumerator.next.should == 'h' # advance
62
+ enumerator.peek.should == '€' # peek a multi-byte character
63
+ enumerator.next.should == '€' # advance a multi-byte character
64
+ enumerator.peek.should == 'l' # peek
65
+ enumerator.peek.should == 'l' # peek the same character again
66
+ enumerator.next.should == 'l' # advance
67
+ enumerator.next.should == 'l' # advance
68
+ enumerator.next.should == 'o' # advance
69
+ enumerator.peek.should == nil # at end should return nil
70
+ enumerator.next.should == nil # nothing left to scan
71
+ end
72
+
73
+ it 'should be able to recall the last character using the "last" method' do
74
+ enumerator = StringEnumerator.new('h€llo')
75
+ enumerator.last.should == nil # nothing scanned yet
76
+ enumerator.next.should == 'h' # advance
77
+ enumerator.last.should == nil # still no previous character
78
+ enumerator.next.should == '€' # advance
79
+ enumerator.last.should == 'h'
80
+ enumerator.next.should == 'l' # advance
81
+ enumerator.last.should == '€'
82
+ enumerator.next.should == 'l' # advance
83
+ enumerator.last.should == 'l'
84
+ enumerator.next.should == 'o' # advance
85
+ enumerator.last.should == 'l'
86
+ enumerator.next.should == nil # nothing left to scan
87
+ enumerator.last.should == 'o'
88
+ enumerator.last.should == 'o' # didn't advance, so should return the same as last time
89
+ end
90
+
91
+ end
92
+
93
+ end # class Grammar
94
+ end # module Walrus
@@ -0,0 +1,143 @@
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/string_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 string parslet' do
15
+
16
+ setup do
17
+ @parslet = StringParslet.new('HELLO')
18
+ end
19
+
20
+ it 'should raise an ArgumentError if initialized with nil' do
21
+ lambda { StringParslet.new(nil) }.should raise_error(ArgumentError)
22
+ end
23
+
24
+ it 'parse should succeed if the input string matches' do
25
+ lambda { @parslet.parse('HELLO') }.should_not raise_error
26
+ end
27
+
28
+ it 'parse should succeed if the input string matches, even if it continues after the match' do
29
+ lambda { @parslet.parse('HELLO...') }.should_not raise_error
30
+ end
31
+
32
+ it 'parse should return parsed string' do
33
+ @parslet.parse('HELLO').should == 'HELLO'
34
+ @parslet.parse('HELLO...').should == 'HELLO'
35
+ end
36
+
37
+ it 'parse should raise an ArgumentError if passed nil' do
38
+ lambda { @parslet.parse(nil) }.should raise_error(ArgumentError)
39
+ end
40
+
41
+ it 'parse should raise a ParseError if the input string does not match' do
42
+ lambda { @parslet.parse('GOODBYE') }.should raise_error(ParseError) # total mismatch
43
+ lambda { @parslet.parse('GOODBYE, HELLO') }.should raise_error(ParseError) # eventually would match, but too late
44
+ lambda { @parslet.parse('HELL...') }.should raise_error(ParseError) # starts well, but fails
45
+ lambda { @parslet.parse(' HELLO') }.should raise_error(ParseError) # note the leading whitespace
46
+ lambda { @parslet.parse('') }.should raise_error(ParseError) # empty strings can't match
47
+ end
48
+
49
+ it 'parse exceptions should include a detailed error message' do
50
+ # TODO: catch the raised exception and compare the message
51
+ lambda { @parslet.parse('HELL...') }.should raise_error(ParseError)
52
+ lambda { @parslet.parse('HELL') }.should raise_error(ParseError)
53
+ end
54
+
55
+ it 'should be able to compare string parslets for equality' do
56
+ 'foo'.to_parseable.should eql('foo'.to_parseable) # equal
57
+ 'foo'.to_parseable.should_not eql('bar'.to_parseable) # different
58
+ 'foo'.to_parseable.should_not eql('Foo'.to_parseable) # differing only in case
59
+ 'foo'.to_parseable.should_not eql(/foo/) # totally different classes
60
+ end
61
+
62
+ it 'should accurately pack line and column ends into whatever is returned by "parse"' do
63
+
64
+ # single word
65
+ parslet = 'hello'.to_parseable
66
+ result = parslet.parse('hello')
67
+ result.line_end.should == 0
68
+ result.column_end.should == 5
69
+
70
+ # single word with newline at end (UNIX style)
71
+ parslet = "hello\n".to_parseable
72
+ result = parslet.parse("hello\n")
73
+ result.line_end.should == 1
74
+ result.column_end.should == 0
75
+
76
+ # single word with newline at end (Classic Mac style)
77
+ parslet = "hello\r".to_parseable
78
+ result = parslet.parse("hello\r")
79
+ result.line_end.should == 1
80
+ result.column_end.should == 0
81
+
82
+ # single word with newline at end (Windows style)
83
+ parslet = "hello\r\n".to_parseable
84
+ result = parslet.parse("hello\r\n")
85
+ result.line_end.should == 1
86
+ result.column_end.should == 0
87
+
88
+ # two lines (UNIX style)
89
+ parslet = "hello\nworld".to_parseable
90
+ result = parslet.parse("hello\nworld")
91
+ result.line_end.should == 1
92
+ result.column_end.should == 5
93
+
94
+ # two lines (Classic Mac style)
95
+ parslet = "hello\rworld".to_parseable
96
+ result = parslet.parse("hello\rworld")
97
+ result.line_end.should == 1
98
+ result.column_end.should == 5
99
+
100
+ # two lines (Windows style)
101
+ parslet = "hello\r\nworld".to_parseable
102
+ result = parslet.parse("hello\r\nworld")
103
+ result.line_end.should == 1
104
+ result.column_end.should == 5
105
+
106
+ end
107
+
108
+ it 'line and column end should reflect last succesfully scanned position prior to failure' do
109
+
110
+ # fail right at start
111
+ parslet = "hello\r\nworld".to_parseable
112
+ begin
113
+ parslet.parse('foobar')
114
+ rescue ParseError => e
115
+ exception = e
116
+ end
117
+ exception.line_end.should == 0
118
+ exception.column_end.should == 0
119
+
120
+ # fail after 1 character
121
+ begin
122
+ parslet.parse('hfoobar')
123
+ rescue ParseError => e
124
+ exception = e
125
+ end
126
+ exception.line_end.should == 0
127
+ exception.column_end.should == 1
128
+
129
+ # fail after end-of-line
130
+ begin
131
+ parslet.parse("hello\r\nfoobar")
132
+ rescue ParseError => e
133
+ exception = e
134
+ end
135
+ exception.line_end.should == 1
136
+ exception.column_end.should == 0
137
+
138
+ end
139
+
140
+ end
141
+
142
+ end # class Grammar
143
+ end # module Walrus
@@ -0,0 +1,30 @@
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/symbol_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 symbol parslet' do
15
+
16
+ it 'should raise an ArgumentError if initialized with nil' do
17
+ lambda { SymbolParslet.new(nil) }.should raise_error(ArgumentError)
18
+ end
19
+
20
+ it 'should be able to compare symbol parslets for equality' do
21
+ :foo.to_parseable.should eql(:foo.to_parseable) # equal
22
+ :foo.to_parseable.should_not eql(:bar.to_parseable) # different
23
+ :foo.to_parseable.should_not eql(:Foo.to_parseable) # differing only in case
24
+ :foo.to_parseable.should_not eql(/foo/) # totally different classes
25
+ end
26
+
27
+ end
28
+
29
+ end # class Grammar
30
+ end # module Walrus