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,1418 @@
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/parser_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
+
13
+ describe 'parsing raw text, escape markers and comments' do
14
+
15
+ context_setup do
16
+ @parser = Parser.new()
17
+ end
18
+
19
+ it 'should be able to instantiate the parser' do
20
+ @parser.should_not be_nil
21
+ end
22
+
23
+ it 'should be able to parse a plaintext string' do
24
+
25
+ # a single word
26
+ result = @parser.parse('foo')
27
+ result.should be_kind_of(WalrusGrammar::RawText)
28
+ result.lexeme.should == 'foo'
29
+
30
+ # multiple words
31
+ result = @parser.parse('foo bar')
32
+ result.should be_kind_of(WalrusGrammar::RawText)
33
+ result.lexeme.should == 'foo bar'
34
+
35
+ # multiple lines
36
+ result = @parser.parse("hello\nworld")
37
+ result.should be_kind_of(WalrusGrammar::RawText)
38
+ result.lexeme.should == "hello\nworld"
39
+
40
+ end
41
+
42
+ it 'should be able to parse a comment' do
43
+
44
+ # comment with content
45
+ result = @parser.parse('## hello world')
46
+ result.should be_kind_of(WalrusGrammar::Comment)
47
+ result.lexeme.should == ' hello world'
48
+
49
+ # comment with no content
50
+ result = @parser.parse('##')
51
+ result.should be_kind_of(WalrusGrammar::Comment)
52
+ result.lexeme.should == ''
53
+
54
+ # multi-line comment (empty)
55
+ result = @parser.parse('#**#')
56
+ result.should be_kind_of(WalrusGrammar::Comment)
57
+ result.should be_kind_of(WalrusGrammar::MultilineComment)
58
+ result.content.should == ''
59
+
60
+ # multi-line comment (with content)
61
+ result = @parser.parse('#* hello world *#')
62
+ result.should be_kind_of(WalrusGrammar::MultilineComment)
63
+ result.content.should == ' hello world '
64
+
65
+ # multi-line comment (spanning multiple lines)
66
+ result = @parser.parse("#* hello\nworld *#")
67
+ result.should be_kind_of(WalrusGrammar::MultilineComment)
68
+ result.content.should == " hello\nworld "
69
+
70
+ # multi-line comment (with nested comment)
71
+ result = @parser.parse('#* hello #*world*# *#')
72
+ result.should be_kind_of(WalrusGrammar::MultilineComment)
73
+ result.content[0].should == ' hello '
74
+ result.content[1].should be_kind_of(WalrusGrammar::MultilineComment)
75
+ result.content[1].content.should == 'world'
76
+ result.content[2].should == ' '
77
+
78
+ # multi-line comment (with nested comment, spanning multiple lines)
79
+ result = @parser.parse("#* hello\n#* world\n... *# *#")
80
+ result.should be_kind_of(WalrusGrammar::MultilineComment)
81
+ result.content[0].should == " hello\n"
82
+ result.content[1].should be_kind_of(WalrusGrammar::MultilineComment)
83
+ result.content[1].content.should == " world\n... "
84
+ result.content[2].should == ' '
85
+
86
+ # multi-line comment (with nested single-line comment)
87
+ result = @parser.parse("#* ##hello\n*#")
88
+ result.should be_kind_of(WalrusGrammar::MultilineComment)
89
+ result.content[0].should == ' '
90
+ result.content[1].should be_kind_of(WalrusGrammar::Comment)
91
+ result.content[1].lexeme.should == 'hello' # here the newline gets eaten
92
+
93
+ end
94
+
95
+ it 'should be able to parse an escape marker' do
96
+
97
+ # directive marker
98
+ result = @parser.parse('\\#')
99
+ result.should be_kind_of(WalrusGrammar::EscapeSequence)
100
+ result.lexeme.should == '#'
101
+
102
+ # placeholder marker
103
+ result = @parser.parse('\\$')
104
+ result.should be_kind_of(WalrusGrammar::EscapeSequence)
105
+ result.lexeme.should == '$'
106
+
107
+ # escape marker
108
+ result = @parser.parse('\\\\')
109
+ result.should be_kind_of(WalrusGrammar::EscapeSequence)
110
+ result.lexeme.should == '\\'
111
+
112
+ # multiple escape markers
113
+ result = @parser.parse('\\#\\$\\\\')
114
+ result[0].should be_kind_of(WalrusGrammar::EscapeSequence)
115
+ result[0].lexeme.should == '#'
116
+ result[1].should be_kind_of(WalrusGrammar::EscapeSequence)
117
+ result[1].lexeme.should == '$'
118
+ result[2].should be_kind_of(WalrusGrammar::EscapeSequence)
119
+ result[2].lexeme.should == '\\'
120
+
121
+ end
122
+
123
+ it 'should complain on finding an illegal escape marker' do
124
+
125
+ # invalid character
126
+ lambda { @parser.parse('\\x') }.should raise_error(Grammar::ParseError)
127
+
128
+ # no character
129
+ lambda { @parser.parse('\\') }.should raise_error(Grammar::ParseError)
130
+
131
+ end
132
+
133
+ it 'should be able to mix comments and plain text' do
134
+
135
+ # plain text followed by comment
136
+ result = @parser.parse('foobar ## hello world')
137
+ result[0].should be_kind_of(WalrusGrammar::RawText)
138
+ result[0].lexeme.should == 'foobar '
139
+ result[1].should be_kind_of(WalrusGrammar::Comment)
140
+ result[1].lexeme.should == ' hello world'
141
+
142
+ # comment should only extend up until the next newline
143
+ result = @parser.parse("## hello world\nfoobar")
144
+ result[0].should be_kind_of(WalrusGrammar::Comment)
145
+ result[0].lexeme.should == ' hello world'
146
+
147
+ end
148
+
149
+ it 'should be able to mix escape markers and plain text' do
150
+
151
+ # plain text followed by an escape marker
152
+ result = @parser.parse('hello \\#')
153
+ result[0].should be_kind_of(WalrusGrammar::RawText)
154
+ result[0].lexeme.should == 'hello '
155
+ result[1].should be_kind_of(WalrusGrammar::EscapeSequence)
156
+ result[1].lexeme.should == '#'
157
+
158
+ # an escape marker followed by plain text
159
+ result = @parser.parse('\\$hello')
160
+ result[0].should be_kind_of(WalrusGrammar::EscapeSequence)
161
+ result[0].lexeme.should == '$'
162
+ result[1].should be_kind_of(WalrusGrammar::RawText)
163
+ result[1].lexeme.should == 'hello'
164
+
165
+ # alternation
166
+ result = @parser.parse('hello \\\\ world')
167
+ result[0].should be_kind_of(WalrusGrammar::RawText)
168
+ result[0].lexeme.should == 'hello '
169
+ result[1].should be_kind_of(WalrusGrammar::EscapeSequence)
170
+ result[1].lexeme.should == '\\'
171
+ result[2].should be_kind_of(WalrusGrammar::RawText)
172
+ result[2].lexeme.should == ' world'
173
+
174
+ # with newlines thrown into the mix
175
+ result = @parser.parse("hello\n\\#")
176
+ result[0].should be_kind_of(WalrusGrammar::RawText)
177
+ result[0].lexeme.should == "hello\n"
178
+ result[1].should be_kind_of(WalrusGrammar::EscapeSequence)
179
+ result[1].lexeme.should == '#'
180
+
181
+ end
182
+ end
183
+
184
+ describe 'parsing directives' do
185
+
186
+ context_setup do
187
+ @parser = Parser.new()
188
+ end
189
+
190
+ it 'should complain on encountering an unknown or invalid directive name' do
191
+ lambda { @parser.parse('#glindenburgen') }.should raise_error(Grammar::ParseError)
192
+ lambda { @parser.parse('#') }.should raise_error(Grammar::ParseError)
193
+ end
194
+
195
+ it 'should complain if there is whitespace between the directive marker (#) and the directive name' do
196
+ lambda { @parser.parse('# extends "other_template"') }.should raise_error(Grammar::ParseError)
197
+ end
198
+
199
+ it 'should be able to parse a directive that takes a single parameter' do
200
+ result = @parser.parse('#extends "other_template"')
201
+ result.should be_kind_of(WalrusGrammar::Directive)
202
+ result.should be_kind_of(WalrusGrammar::ExtendsDirective)
203
+ result.class_name.lexeme.should == 'other_template'
204
+ end
205
+
206
+ it 'should be able to follow a directive by a comment on the same line, only if the directive has an explicit termination marker' do
207
+
208
+ # no intervening whitespace ("extends" directive, takes one parameter)
209
+ result = @parser.parse('#extends "other_template"### comment')
210
+ result[0].should be_kind_of(WalrusGrammar::ExtendsDirective)
211
+ result[0].class_name.lexeme.should == 'other_template'
212
+ result[1].should be_kind_of(WalrusGrammar::Comment)
213
+ result[1].lexeme.should == ' comment'
214
+
215
+ # counter-example
216
+ lambda { @parser.parse('#extends "other_template"## comment') }.should raise_error(Grammar::ParseError)
217
+
218
+ # intervening whitespace (between parameter and trailing comment)
219
+ result = @parser.parse('#extends "other_template" ### comment')
220
+ result[0].should be_kind_of(WalrusGrammar::ExtendsDirective)
221
+ result[0].class_name.lexeme.should == 'other_template'
222
+ result[1].should be_kind_of(WalrusGrammar::Comment)
223
+ result[1].lexeme.should == ' comment'
224
+
225
+ # counter-example
226
+ lambda { @parser.parse('#extends "other_template" ## comment') }.should raise_error(Grammar::ParseError)
227
+
228
+ # intervening whitespace (between directive and parameter)
229
+ result = @parser.parse('#extends "other_template" ### comment')
230
+ result[0].should be_kind_of(WalrusGrammar::ExtendsDirective)
231
+ result[0].class_name.lexeme.should == 'other_template'
232
+ result[1].should be_kind_of(WalrusGrammar::Comment)
233
+ result[1].lexeme.should == ' comment'
234
+
235
+ # counter-example
236
+ lambda { @parser.parse('#extends "other_template" ## comment') }.should raise_error(Grammar::ParseError)
237
+
238
+ end
239
+
240
+ it 'should be able to span directives across lines by using a line continuation backslash' do
241
+
242
+ # basic case
243
+ result = @parser.parse("#extends \\\n'other_template'")
244
+ result.should be_kind_of(WalrusGrammar::ExtendsDirective)
245
+ result.class_name.lexeme.should == 'other_template'
246
+
247
+ # should fail if backslash is not the last character on the line
248
+ lambda { @parser.parse("#extends \\ \n'other_template'") }.should raise_error(Grammar::ParseError)
249
+
250
+ end
251
+
252
+ it 'should be able to parse an "import" directive' do
253
+
254
+ # followed by a newline
255
+ result = @parser.parse("#import 'other_template'\nhello")
256
+ result[0].should be_kind_of(WalrusGrammar::ImportDirective)
257
+ result[0].class_name.lexeme.should == 'other_template'
258
+ result[1].should be_kind_of(WalrusGrammar::RawText)
259
+ result[1].lexeme.should == 'hello' # newline gets eaten
260
+
261
+ # followed by whitespace
262
+ result = @parser.parse('#import "other_template" ')
263
+ result.should be_kind_of(WalrusGrammar::ImportDirective)
264
+ result.class_name.lexeme.should == 'other_template'
265
+
266
+ # followed by the end of the input
267
+ result = @parser.parse('#import "other_template"')
268
+ result.should be_kind_of(WalrusGrammar::ImportDirective)
269
+ result.class_name.lexeme.should == 'other_template'
270
+
271
+ # comment with no intervening whitespace
272
+ result = @parser.parse('#import "other_template"### comment')
273
+ result[0].should be_kind_of(WalrusGrammar::ImportDirective)
274
+ result[0].class_name.lexeme.should == 'other_template'
275
+ result[1].should be_kind_of(WalrusGrammar::Comment)
276
+ result[1].lexeme.should == ' comment'
277
+
278
+ # counter-example
279
+ lambda { @parser.parse('#import "other_template"## comment') }.should raise_error(Grammar::ParseError)
280
+
281
+ # intervening whitespace (between parameter and trailing comment)
282
+ result = @parser.parse('#import "other_template" ### comment')
283
+ result[0].should be_kind_of(WalrusGrammar::ImportDirective)
284
+ result[0].class_name.lexeme.should == 'other_template'
285
+ result[1].should be_kind_of(WalrusGrammar::Comment)
286
+ result[1].lexeme.should == ' comment'
287
+
288
+ # counter-example
289
+ lambda { @parser.parse('#import "other_template" ## comment') }.should raise_error(Grammar::ParseError)
290
+
291
+ # intervening whitespace (between directive and parameter)
292
+ result = @parser.parse('#import "other_template" ### comment')
293
+ result[0].should be_kind_of(WalrusGrammar::ImportDirective)
294
+ result[0].class_name.lexeme.should == 'other_template'
295
+ result[1].should be_kind_of(WalrusGrammar::Comment)
296
+ result[1].lexeme.should == ' comment'
297
+
298
+ # counter-example
299
+ lambda { @parser.parse('#import "other_template" ## comment') }.should raise_error(Grammar::ParseError)
300
+
301
+ end
302
+
303
+ it 'should be able to parse single quoted string literals' do
304
+
305
+ # string literals have no special meaning when part of raw text
306
+ result = @parser.parse("'hello'")
307
+ result.should be_kind_of(WalrusGrammar::RawText)
308
+ result.lexeme.should == "'hello'"
309
+
310
+ # empty string
311
+ result = @parser.parse("#import ''")
312
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
313
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
314
+ result.class_name.lexeme.to_s.should == '' # actually just returns []; I might need to add a "flatten" or "to_string" method to my Grammar specification system
315
+
316
+ # with escaped single quotes inside
317
+ result = @parser.parse("#import 'hello \\'world\\''")
318
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
319
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
320
+ result.class_name.lexeme.should == "hello \\'world\\'"
321
+
322
+ # with other escapes inside
323
+ result = @parser.parse("#import 'hello\\nworld'")
324
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
325
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
326
+ result.class_name.lexeme.should == 'hello\nworld'
327
+
328
+ # with double quotes inside
329
+ result = @parser.parse("#import 'hello \"world\"'")
330
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
331
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
332
+ result.class_name.lexeme.should == 'hello "world"'
333
+
334
+ # with Walrus comments inside (ignored)
335
+ result = @parser.parse("#import 'hello ##world'")
336
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
337
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
338
+ result.class_name.lexeme.should == 'hello ##world'
339
+
340
+ # with Walrus placeholders inside (no interpolation)
341
+ result = @parser.parse("#import 'hello $world'")
342
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
343
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
344
+ result.class_name.lexeme.should == 'hello $world'
345
+
346
+ # with Walrus directives inside (no interpolation)
347
+ result = @parser.parse("#import 'hello #end'")
348
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
349
+ result.class_name.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
350
+ result.class_name.lexeme.should == 'hello #end'
351
+
352
+ end
353
+
354
+ it 'should be able to parse double quoted string literals' do
355
+
356
+ # string literals have no special meaning when part of raw text
357
+ result = @parser.parse('"hello"')
358
+ result.should be_kind_of(WalrusGrammar::RawText)
359
+ result.lexeme.should == '"hello"'
360
+
361
+ # empty string
362
+ result = @parser.parse('#import ""')
363
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
364
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
365
+ result.class_name.lexeme.to_s.should == '' # actually just returns []; I might need to add a "flatten" or "to_string" method to my Grammar specification system
366
+
367
+ # with escaped double quotes inside
368
+ result = @parser.parse('#import "hello \\"world\\""')
369
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
370
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
371
+ result.class_name.lexeme.should == 'hello \\"world\\"'
372
+
373
+ # with other escapes inside
374
+ result = @parser.parse('#import "hello\\nworld"')
375
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
376
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
377
+ result.class_name.lexeme.should == 'hello\\nworld'
378
+
379
+ # with single quotes inside
380
+ result = @parser.parse('#import "hello \'world\'"')
381
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
382
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
383
+ result.class_name.lexeme.should == "hello 'world'"
384
+
385
+ # with Walrus comments inside (ignored)
386
+ result = @parser.parse('#import "hello ##world"')
387
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
388
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
389
+ result.class_name.lexeme.should == 'hello ##world'
390
+
391
+ # with Walrus placeholders inside (no interpolation)
392
+ result = @parser.parse('#import "hello $world"')
393
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
394
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
395
+ result.class_name.lexeme.should == 'hello $world'
396
+
397
+ # with Walrus directives inside (no interpolation)
398
+ result = @parser.parse('#import "hello #end"')
399
+ result.class_name.should be_kind_of(WalrusGrammar::StringLiteral)
400
+ result.class_name.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
401
+ result.class_name.lexeme.should == 'hello #end'
402
+
403
+ end
404
+
405
+ # will use the #silent directive here because it's an easy way to make the parser look for a ruby expression
406
+ it 'should be able to parse basic Ruby expressions' do
407
+
408
+ # a numeric literal
409
+ result = @parser.parse('#silent 1')
410
+ result.should be_kind_of(WalrusGrammar::Directive)
411
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
412
+ result.expression.should be_kind_of(WalrusGrammar::NumericLiteral)
413
+ result.expression.lexeme.should == '1'
414
+
415
+ # a single-quoted string literal
416
+ result = @parser.parse("#silent 'foo'")
417
+ result.should be_kind_of(WalrusGrammar::Directive)
418
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
419
+ result.expression.should be_kind_of(WalrusGrammar::SingleQuotedStringLiteral)
420
+ result.expression.lexeme.should == 'foo'
421
+
422
+ # a double-quoted string literal
423
+ result = @parser.parse('#silent "foo"')
424
+ result.should be_kind_of(WalrusGrammar::Directive)
425
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
426
+ result.expression.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
427
+ result.expression.lexeme.should == 'foo'
428
+
429
+ # an identifier
430
+ result = @parser.parse('#silent foo')
431
+ result.should be_kind_of(WalrusGrammar::Directive)
432
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
433
+ result.expression.should be_kind_of(WalrusGrammar::Identifier)
434
+ result.expression.lexeme.should == 'foo'
435
+
436
+ result = @parser.parse('#silent foo_bar')
437
+ result.should be_kind_of(WalrusGrammar::Directive)
438
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
439
+ result.expression.should be_kind_of(WalrusGrammar::Identifier)
440
+ result.expression.lexeme.should == 'foo_bar'
441
+
442
+ # a constant
443
+ result = @parser.parse('#silent Foo')
444
+ result.should be_kind_of(WalrusGrammar::Directive)
445
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
446
+ result.expression.should be_kind_of(WalrusGrammar::Constant)
447
+ result.expression.lexeme.should == 'Foo'
448
+
449
+ result = @parser.parse('#silent FooBar')
450
+ result.should be_kind_of(WalrusGrammar::Directive)
451
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
452
+ result.expression.should be_kind_of(WalrusGrammar::Constant)
453
+ result.expression.lexeme.should == 'FooBar'
454
+
455
+ # a symbol
456
+ result = @parser.parse('#silent :foo')
457
+ result.should be_kind_of(WalrusGrammar::Directive)
458
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
459
+ result.expression.should be_kind_of(WalrusGrammar::SymbolLiteral)
460
+ result.expression.lexeme.should == ':foo'
461
+
462
+ result = @parser.parse('#silent :Foo')
463
+ result.should be_kind_of(WalrusGrammar::Directive)
464
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
465
+ result.expression.should be_kind_of(WalrusGrammar::SymbolLiteral)
466
+ result.expression.lexeme.should == ':Foo'
467
+
468
+ # an array literal
469
+ result = @parser.parse('#silent [1]')
470
+ result.should be_kind_of(WalrusGrammar::Directive)
471
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
472
+ result.expression.should be_kind_of(WalrusGrammar::RubyExpression)
473
+ result.expression.should be_kind_of(WalrusGrammar::ArrayLiteral)
474
+ result.expression.elements.should be_kind_of(WalrusGrammar::NumericLiteral)
475
+ result.expression.elements.lexeme.should == '1'
476
+
477
+ result = @parser.parse('#silent [1, 2, 3]')
478
+ result.should be_kind_of(WalrusGrammar::Directive)
479
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
480
+ result.expression.should be_kind_of(WalrusGrammar::RubyExpression)
481
+ result.expression.should be_kind_of(WalrusGrammar::ArrayLiteral)
482
+ result.expression.elements.should be_kind_of(Array)
483
+ result.expression.elements[0].should be_kind_of(WalrusGrammar::NumericLiteral)
484
+ result.expression.elements[0].lexeme.should == '1'
485
+ result.expression.elements[1].should be_kind_of(WalrusGrammar::NumericLiteral)
486
+ result.expression.elements[1].lexeme.should == '2'
487
+ result.expression.elements[2].should be_kind_of(WalrusGrammar::NumericLiteral)
488
+ result.expression.elements[2].lexeme.should == '3'
489
+
490
+ # a hash literal
491
+ result = @parser.parse('#silent { :foo => "bar" }')
492
+ result.should be_kind_of(WalrusGrammar::Directive)
493
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
494
+ result.expression.should be_kind_of(WalrusGrammar::RubyExpression)
495
+ result.expression.should be_kind_of(WalrusGrammar::HashLiteral)
496
+ result.expression.pairs.should be_kind_of(WalrusGrammar::RubyExpression)
497
+ result.expression.pairs.should be_kind_of(WalrusGrammar::HashAssignment)
498
+ result.expression.pairs.lvalue.should be_kind_of(WalrusGrammar::SymbolLiteral)
499
+ result.expression.pairs.lvalue.lexeme.should == ':foo'
500
+ result.expression.pairs.expression.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
501
+ result.expression.pairs.expression.lexeme.should == 'bar'
502
+
503
+ result = @parser.parse('#silent { :foo => "bar", :baz => "xyz" }')
504
+ result.expression.should be_kind_of(WalrusGrammar::HashLiteral)
505
+ result.expression.pairs.should be_kind_of(Array)
506
+ result.expression.pairs[0].should be_kind_of(WalrusGrammar::HashAssignment)
507
+ result.expression.pairs[0].lvalue.should be_kind_of(WalrusGrammar::SymbolLiteral)
508
+ result.expression.pairs[0].lvalue.lexeme.should == ':foo'
509
+ result.expression.pairs[0].expression.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
510
+ result.expression.pairs[0].expression.lexeme.should == 'bar'
511
+ result.expression.pairs[1].should be_kind_of(WalrusGrammar::HashAssignment)
512
+ result.expression.pairs[1].lvalue.should be_kind_of(WalrusGrammar::SymbolLiteral)
513
+ result.expression.pairs[1].lvalue.lexeme.should == ':baz'
514
+ result.expression.pairs[1].expression.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
515
+ result.expression.pairs[1].expression.lexeme.should == 'xyz'
516
+
517
+ # an addition expression
518
+ result = @parser.parse('#silent 1 + 2')
519
+ result.should be_kind_of(WalrusGrammar::Directive)
520
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
521
+ result.expression.should be_kind_of(WalrusGrammar::RubyExpression)
522
+ result.expression.should be_kind_of(WalrusGrammar::AdditionExpression)
523
+ result.expression.left.should be_kind_of(WalrusGrammar::NumericLiteral)
524
+ result.expression.left.lexeme.should == '1'
525
+ result.expression.right.should be_kind_of(WalrusGrammar::NumericLiteral)
526
+ result.expression.right.lexeme.should == '2'
527
+
528
+ # an assignment expression
529
+ result = @parser.parse('#silent foo = 1')
530
+ result.should be_kind_of(WalrusGrammar::Directive)
531
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
532
+ result.expression.should be_kind_of(WalrusGrammar::RubyExpression)
533
+ result.expression.should be_kind_of(WalrusGrammar::AssignmentExpression)
534
+ result.expression.lvalue.should be_kind_of(WalrusGrammar::Identifier)
535
+ result.expression.lvalue.lexeme.should == 'foo'
536
+ result.expression.expression.should be_kind_of(WalrusGrammar::NumericLiteral)
537
+ result.expression.expression.lexeme.should == '1'
538
+
539
+ # a method invocation
540
+ result = @parser.parse('#silent foo.delete')
541
+ result.should be_kind_of(WalrusGrammar::Directive)
542
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
543
+ result.expression.should be_kind_of(WalrusGrammar::RubyExpression)
544
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
545
+
546
+ result = @parser.parse('#silent foo.delete()')
547
+ result.should be_kind_of(WalrusGrammar::Directive)
548
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
549
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
550
+ result.expression.target.should be_kind_of(WalrusGrammar::Identifier)
551
+ result.expression.target.lexeme.should == 'foo'
552
+ result.expression.message.should be_kind_of(WalrusGrammar::RubyExpression)
553
+ result.expression.message.should be_kind_of(WalrusGrammar::MethodExpression)
554
+ result.expression.message.name.should be_kind_of(WalrusGrammar::Identifier)
555
+ result.expression.message.name.lexeme.should == 'delete'
556
+ result.expression.message.params.should be_kind_of(Array)
557
+ result.expression.message.params.should == []
558
+
559
+ result = @parser.parse('#silent foo.delete(1)')
560
+ result.should be_kind_of(WalrusGrammar::Directive)
561
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
562
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
563
+ result.expression.target.should be_kind_of(WalrusGrammar::Identifier)
564
+ result.expression.target.lexeme.should == 'foo'
565
+ result.expression.message.should be_kind_of(WalrusGrammar::MethodExpression)
566
+ result.expression.message.name.should be_kind_of(WalrusGrammar::Identifier)
567
+ result.expression.message.name.lexeme.should == 'delete'
568
+ result.expression.message.params.should be_kind_of(WalrusGrammar::NumericLiteral)
569
+ result.expression.message.params.lexeme.should == '1'
570
+
571
+ result = @parser.parse('#silent foo.delete(bar, baz)')
572
+ result.should be_kind_of(WalrusGrammar::Directive)
573
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
574
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
575
+ result.expression.target.should be_kind_of(WalrusGrammar::Identifier)
576
+ result.expression.target.lexeme.should == 'foo'
577
+ result.expression.message.should be_kind_of(WalrusGrammar::MethodExpression)
578
+ result.expression.message.name.should be_kind_of(WalrusGrammar::Identifier)
579
+ result.expression.message.name.lexeme.should == 'delete'
580
+ result.expression.message.params.should be_kind_of(Array)
581
+ result.expression.message.params[0].should be_kind_of(WalrusGrammar::Identifier)
582
+ result.expression.message.params[0].lexeme.should == 'bar'
583
+ result.expression.message.params[1].should be_kind_of(WalrusGrammar::Identifier)
584
+ result.expression.message.params[1].lexeme.should == 'baz'
585
+
586
+ # chained method invocation
587
+ result = @parser.parse('#silent foo.bar.baz')
588
+ result.should be_kind_of(WalrusGrammar::Directive)
589
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
590
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
591
+ result.expression.target.should be_kind_of(WalrusGrammar::MessageExpression)
592
+ result.expression.target.target.should be_kind_of(WalrusGrammar::Identifier)
593
+ result.expression.target.target.lexeme.to_s.should == 'foo'
594
+ result.expression.target.message.should be_kind_of(WalrusGrammar::MethodExpression)
595
+ result.expression.target.message.name.should be_kind_of(WalrusGrammar::Identifier)
596
+ result.expression.target.message.name.lexeme.to_s.should == 'bar'
597
+ result.expression.target.message.params.should == []
598
+ result.expression.message.should be_kind_of(WalrusGrammar::MethodExpression)
599
+ result.expression.message.name.should be_kind_of(WalrusGrammar::Identifier)
600
+ result.expression.message.name.lexeme.to_s.should == 'baz'
601
+ result.expression.message.params.should == []
602
+
603
+ # chained method invocation with arguments
604
+ result = @parser.parse('#silent foo.bar(1).baz')
605
+ result.should be_kind_of(WalrusGrammar::Directive)
606
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
607
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
608
+
609
+ result = @parser.parse('#silent foo.bar.baz(2)')
610
+ result.should be_kind_of(WalrusGrammar::Directive)
611
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
612
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
613
+
614
+ result = @parser.parse('#silent foo.bar(1).baz(2)')
615
+ result.should be_kind_of(WalrusGrammar::Directive)
616
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
617
+ result.expression.should be_kind_of(WalrusGrammar::MessageExpression)
618
+
619
+ # nested arrays
620
+ result = @parser.parse('#silent [1, 2, [foo, bar]]')
621
+ result.should be_kind_of(WalrusGrammar::Directive)
622
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
623
+ result.expression.should be_kind_of(WalrusGrammar::ArrayLiteral)
624
+ result.expression.elements.should be_kind_of(Array)
625
+ result.expression.elements[0].should be_kind_of(WalrusGrammar::NumericLiteral)
626
+ result.expression.elements[0].lexeme.to_s.should == '1'
627
+ result.expression.elements[1].should be_kind_of(WalrusGrammar::NumericLiteral)
628
+ result.expression.elements[1].lexeme.to_s.should == '2'
629
+ result.expression.elements[2].should be_kind_of(WalrusGrammar::ArrayLiteral)
630
+ result.expression.elements[2].elements.should be_kind_of(Array)
631
+ result.expression.elements[2].elements[0].should be_kind_of(WalrusGrammar::Identifier)
632
+ result.expression.elements[2].elements[0].lexeme.to_s.should == 'foo'
633
+ result.expression.elements[2].elements[1].should be_kind_of(WalrusGrammar::Identifier)
634
+ result.expression.elements[2].elements[1].lexeme.to_s.should == 'bar'
635
+
636
+ # nesting in a hash
637
+ result = @parser.parse('#silent { :foo => [1, 2] }')
638
+ result.should be_kind_of(WalrusGrammar::Directive)
639
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
640
+ result.expression.should be_kind_of(WalrusGrammar::HashLiteral)
641
+
642
+ # multiple addition expressions
643
+ result = @parser.parse('#silent 1 + 2 + 3')
644
+ result.should be_kind_of(WalrusGrammar::Directive)
645
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
646
+ result.expression.should be_kind_of(WalrusGrammar::AdditionExpression)
647
+ result.expression.left.should be_kind_of(WalrusGrammar::AdditionExpression)
648
+ result.expression.left.left.should be_kind_of(WalrusGrammar::NumericLiteral)
649
+ result.expression.left.left.lexeme.to_s.should == '1'
650
+ result.expression.left.right.should be_kind_of(WalrusGrammar::NumericLiteral)
651
+ result.expression.left.right.lexeme.to_s.should == '2'
652
+ result.expression.right.should be_kind_of(WalrusGrammar::NumericLiteral)
653
+ result.expression.right.lexeme.to_s.should == '3'
654
+
655
+ # addition and assignment
656
+ result = @parser.parse('#silent foo = bar + 1')
657
+ result.should be_kind_of(WalrusGrammar::Directive)
658
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
659
+ result.expression.should be_kind_of(WalrusGrammar::AssignmentExpression)
660
+
661
+ end
662
+
663
+ it 'should be able go from AST representation of a Ruby expression to an evaluable string form' do
664
+
665
+ result = @parser.parse('#silent 1 + 2 + 3')
666
+
667
+ # given that ruby expressions might be able to contain placeholders, i am not sure if a simple "reverse to original string" method will be enough...
668
+
669
+ end
670
+
671
+ it 'should be able to parse the "block" directive' do
672
+
673
+ # simple case: no parameters, no content
674
+ result = @parser.parse("#block foo\n#end")
675
+ result.should be_kind_of(WalrusGrammar::Directive)
676
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
677
+ result.identifier.to_s.should == 'foo'
678
+ result.params.should == []
679
+ result.content.should == []
680
+
681
+ # pathologically short case
682
+ result = @parser.parse('#block foo##end')
683
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
684
+ result.identifier.to_s.should == 'foo'
685
+ result.params.should == []
686
+ result.content.should == []
687
+
688
+ # some content
689
+ result = @parser.parse('#block foo#hello#end')
690
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
691
+ result.identifier.to_s.should == 'foo'
692
+ result.params.should == []
693
+ result.content.should be_kind_of(WalrusGrammar::RawText)
694
+ result.content.lexeme.should == 'hello'
695
+
696
+ result = @parser.parse("#block foo\nhello\n#end")
697
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
698
+ result.identifier.to_s.should == 'foo'
699
+ result.params.should == []
700
+ result.content.should be_kind_of(WalrusGrammar::RawText)
701
+ result.content.lexeme.should == "hello\n"
702
+
703
+ # empty params list
704
+ result = @parser.parse("#block foo()\n#end")
705
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
706
+ result.identifier.to_s.should == 'foo'
707
+ result.params.should == []
708
+ result.content.should == []
709
+
710
+ # one param
711
+ result = @parser.parse("#block foo(bar)\n#end")
712
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
713
+ result.identifier.to_s.should == 'foo'
714
+ result.params.should be_kind_of(WalrusGrammar::Identifier)
715
+ result.params.lexeme.should == 'bar'
716
+ result.content.should == []
717
+
718
+ # one param with blockault value
719
+ result = @parser.parse("#block foo(bar = 1)\n#end")
720
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
721
+ result.identifier.to_s.should == 'foo'
722
+ result.params.should be_kind_of(WalrusGrammar::AssignmentExpression)
723
+ result.params.lvalue.should be_kind_of(WalrusGrammar::Identifier)
724
+ result.params.lvalue.lexeme.should == 'bar'
725
+ result.params.expression.should be_kind_of(WalrusGrammar::NumericLiteral)
726
+ result.params.expression.lexeme.should == '1'
727
+ result.content.should == []
728
+
729
+ result = @parser.parse("#block foo(bar = nil)\n#end")
730
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
731
+ result.identifier.to_s.should == 'foo'
732
+ result.params.should be_kind_of(WalrusGrammar::AssignmentExpression)
733
+ result.params.lvalue.should be_kind_of(WalrusGrammar::Identifier)
734
+ result.params.lvalue.lexeme.should == 'bar'
735
+ result.params.expression.should be_kind_of(WalrusGrammar::Identifier)
736
+ result.params.expression.lexeme.should == 'nil'
737
+ result.content.should == []
738
+
739
+ # two params
740
+ result = @parser.parse("#block foo(bar, baz)\n#end")
741
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
742
+ result.identifier.to_s.should == 'foo'
743
+ result.params[0].should be_kind_of(WalrusGrammar::Identifier)
744
+ result.params[0].lexeme.should == 'bar'
745
+ result.params[1].should be_kind_of(WalrusGrammar::Identifier)
746
+ result.params[1].lexeme.should == 'baz'
747
+ result.content.should == []
748
+
749
+ # nested block block
750
+ result = @parser.parse(%Q{#block outer
751
+ hello
752
+ #block inner
753
+ world
754
+ #end
755
+ ...
756
+ #end})
757
+ result.should be_kind_of(WalrusGrammar::BlockDirective)
758
+ result.identifier.to_s.should == 'outer'
759
+ result.params.should == []
760
+ result.content[0].should be_kind_of(WalrusGrammar::RawText)
761
+ result.content[0].lexeme.should == "hello\n"
762
+ result.content[1].should be_kind_of(WalrusGrammar::BlockDirective)
763
+ result.content[1].identifier.to_s.should == 'inner'
764
+ result.content[1].params.should == []
765
+ result.content[1].content.should be_kind_of(WalrusGrammar::RawText)
766
+ result.content[1].content.lexeme.should == "world\n"
767
+ result.content[2].should be_kind_of(WalrusGrammar::RawText)
768
+ result.content[2].lexeme.should == "...\n"
769
+
770
+ # missing identifier
771
+ lambda { @parser.parse("#block\n#end") }.should raise_error(Grammar::ParseError)
772
+ lambda { @parser.parse("#block ()\n#end") }.should raise_error(Grammar::ParseError)
773
+
774
+ # non-terminated parameter list
775
+ lambda { @parser.parse("#block foo(bar\n#end") }.should raise_error(Grammar::ParseError)
776
+ lambda { @parser.parse("#block foo(bar,)\n#end") }.should raise_error(Grammar::ParseError)
777
+
778
+ # illegal parameter type
779
+ lambda { @parser.parse("#block foo(1)\n#end") }.should raise_error(Grammar::ParseError)
780
+ lambda { @parser.parse("#block foo($bar)\n#end") }.should raise_error(Grammar::ParseError)
781
+
782
+ end
783
+
784
+ it 'should be able to parse the "def" directive' do
785
+
786
+ # simple case: no parameters, no content
787
+ result = @parser.parse("#def foo\n#end")
788
+ result.should be_kind_of(WalrusGrammar::Directive)
789
+ result.should be_kind_of(WalrusGrammar::DefDirective)
790
+ result.identifier.to_s.should == 'foo'
791
+ result.params.should == []
792
+ result.content.should == []
793
+
794
+ # pathologically short case
795
+ result = @parser.parse('#def foo##end')
796
+ result.should be_kind_of(WalrusGrammar::DefDirective)
797
+ result.identifier.to_s.should == 'foo'
798
+ result.params.should == []
799
+ result.content.should == []
800
+
801
+ # some content
802
+ result = @parser.parse('#def foo#hello#end')
803
+ result.should be_kind_of(WalrusGrammar::DefDirective)
804
+ result.identifier.to_s.should == 'foo'
805
+ result.params.should == []
806
+ result.content.should be_kind_of(WalrusGrammar::RawText)
807
+ result.content.lexeme.should == 'hello'
808
+
809
+ result = @parser.parse("#def foo\nhello\n#end")
810
+ result.should be_kind_of(WalrusGrammar::DefDirective)
811
+ result.identifier.to_s.should == 'foo'
812
+ result.params.should == []
813
+ result.content.should be_kind_of(WalrusGrammar::RawText)
814
+ result.content.lexeme.should == "hello\n"
815
+
816
+ # empty params list
817
+ result = @parser.parse("#def foo()\n#end")
818
+ result.should be_kind_of(WalrusGrammar::DefDirective)
819
+ result.identifier.to_s.should == 'foo'
820
+ result.params.should == []
821
+ result.content.should == []
822
+
823
+ # one param
824
+ result = @parser.parse("#def foo(bar)\n#end")
825
+ result.should be_kind_of(WalrusGrammar::DefDirective)
826
+ result.identifier.to_s.should == 'foo'
827
+ result.params.should be_kind_of(WalrusGrammar::Identifier)
828
+ result.params.lexeme.should == 'bar'
829
+ result.content.should == []
830
+
831
+ # one param with default value
832
+ result = @parser.parse("#def foo(bar = 1)\n#end")
833
+ result.should be_kind_of(WalrusGrammar::DefDirective)
834
+ result.identifier.to_s.should == 'foo'
835
+ result.params.should be_kind_of(WalrusGrammar::AssignmentExpression)
836
+ result.params.lvalue.should be_kind_of(WalrusGrammar::Identifier)
837
+ result.params.lvalue.lexeme.should == 'bar'
838
+ result.params.expression.should be_kind_of(WalrusGrammar::NumericLiteral)
839
+ result.params.expression.lexeme.should == '1'
840
+ result.content.should == []
841
+
842
+ result = @parser.parse("#def foo(bar = nil)\n#end")
843
+ result.should be_kind_of(WalrusGrammar::DefDirective)
844
+ result.identifier.to_s.should == 'foo'
845
+ result.params.should be_kind_of(WalrusGrammar::AssignmentExpression)
846
+ result.params.lvalue.should be_kind_of(WalrusGrammar::Identifier)
847
+ result.params.lvalue.lexeme.should == 'bar'
848
+ result.params.expression.should be_kind_of(WalrusGrammar::Identifier)
849
+ result.params.expression.lexeme.should == 'nil'
850
+ result.content.should == []
851
+
852
+ # two params
853
+ result = @parser.parse("#def foo(bar, baz)\n#end")
854
+ result.should be_kind_of(WalrusGrammar::DefDirective)
855
+ result.identifier.to_s.should == 'foo'
856
+ result.params[0].should be_kind_of(WalrusGrammar::Identifier)
857
+ result.params[0].lexeme.should == 'bar'
858
+ result.params[1].should be_kind_of(WalrusGrammar::Identifier)
859
+ result.params[1].lexeme.should == 'baz'
860
+ result.content.should == []
861
+
862
+ # nested def block
863
+ result = @parser.parse(%Q{#def outer
864
+ hello
865
+ #def inner
866
+ world
867
+ #end
868
+ ...
869
+ #end})
870
+ result.should be_kind_of(WalrusGrammar::DefDirective)
871
+ result.identifier.to_s.should == 'outer'
872
+ result.params.should == []
873
+ result.content[0].should be_kind_of(WalrusGrammar::RawText)
874
+ result.content[0].lexeme.should == "hello\n"
875
+ result.content[1].should be_kind_of(WalrusGrammar::DefDirective)
876
+ result.content[1].identifier.to_s.should == 'inner'
877
+ result.content[1].params.should == []
878
+ result.content[1].content.should be_kind_of(WalrusGrammar::RawText)
879
+ result.content[1].content.lexeme.should == "world\n"
880
+ result.content[2].should be_kind_of(WalrusGrammar::RawText)
881
+ result.content[2].lexeme.should == "...\n"
882
+
883
+ # missing identifier
884
+ lambda { @parser.parse("#def\n#end") }.should raise_error(Grammar::ParseError)
885
+ lambda { @parser.parse("#def ()\n#end") }.should raise_error(Grammar::ParseError)
886
+
887
+ # non-terminated parameter list
888
+ lambda { @parser.parse("#def foo(bar\n#end") }.should raise_error(Grammar::ParseError)
889
+ lambda { @parser.parse("#def foo(bar,)\n#end") }.should raise_error(Grammar::ParseError)
890
+
891
+ # illegal parameter type
892
+ lambda { @parser.parse("#def foo(1)\n#end") }.should raise_error(Grammar::ParseError)
893
+ lambda { @parser.parse("#def foo($bar)\n#end") }.should raise_error(Grammar::ParseError)
894
+
895
+ end
896
+
897
+ it 'should be able to parse the "echo" directive' do
898
+
899
+ # success case
900
+ result = @parser.parse('#echo foo')
901
+ result.should be_kind_of(WalrusGrammar::Directive)
902
+ result.should be_kind_of(WalrusGrammar::EchoDirective)
903
+ result.expression.should be_kind_of(WalrusGrammar::Identifier)
904
+ result.expression.lexeme.should == 'foo'
905
+
906
+ # failing case
907
+ lambda { @parser.parse('#echo') }.should raise_error(Grammar::ParseError)
908
+
909
+ # allow multiple expressions separated by semicolons
910
+ result = @parser.parse('#echo foo; bar')
911
+ result.should be_kind_of(WalrusGrammar::EchoDirective)
912
+ result.expression.should be_kind_of(Array)
913
+ result.expression[0].should be_kind_of(WalrusGrammar::Identifier)
914
+ result.expression[0].lexeme.should == 'foo'
915
+ result.expression[1].should be_kind_of(WalrusGrammar::Identifier)
916
+ result.expression[1].lexeme.should == 'bar'
917
+
918
+ end
919
+
920
+ it 'should be able to parse "echo" directive, short notation' do
921
+
922
+ # single expression
923
+ result = @parser.parse('#= 1 #')
924
+ result.should be_kind_of(WalrusGrammar::Directive)
925
+ result.should be_kind_of(WalrusGrammar::EchoDirective)
926
+ result.expression.should be_kind_of(WalrusGrammar::NumericLiteral)
927
+ result.expression.lexeme.should == '1'
928
+
929
+ # expression list
930
+ result = @parser.parse('#= foo; bar #')
931
+ result.should be_kind_of(WalrusGrammar::EchoDirective)
932
+ result.expression.should be_kind_of(Array)
933
+ result.expression[0].should be_kind_of(WalrusGrammar::Identifier)
934
+ result.expression[0].lexeme.should == 'foo'
935
+ result.expression[1].should be_kind_of(WalrusGrammar::Identifier)
936
+ result.expression[1].lexeme.should == 'bar'
937
+
938
+ # explicit end marker is required
939
+ lambda { @parser.parse('#= 1') }.should raise_error(Grammar::ParseError)
940
+ lambda { @parser.parse('#= foo; bar') }.should raise_error(Grammar::ParseError)
941
+
942
+ end
943
+
944
+ it 'should be able to parse the "raw" directive' do
945
+
946
+ # shortest example possible
947
+ result = @parser.parse('#raw##end')
948
+ result.should be_kind_of(WalrusGrammar::Directive)
949
+ result.should be_kind_of(WalrusGrammar::RawDirective)
950
+ result.content.should == ''
951
+
952
+ # one character longer
953
+ result = @parser.parse('#raw##end#')
954
+ result.should be_kind_of(WalrusGrammar::Directive)
955
+ result.should be_kind_of(WalrusGrammar::RawDirective)
956
+ result.content.should == ''
957
+
958
+ # same but with trailing newline instead
959
+ result = @parser.parse("#raw##end\n")
960
+ result.should be_kind_of(WalrusGrammar::Directive)
961
+ result.should be_kind_of(WalrusGrammar::RawDirective)
962
+ result.content.should == ''
963
+
964
+ # only slightly longer (still on one line)
965
+ result = @parser.parse('#raw#hello world#end')
966
+ result.should be_kind_of(WalrusGrammar::Directive)
967
+ result.should be_kind_of(WalrusGrammar::RawDirective)
968
+ result.content.should == 'hello world'
969
+
970
+ result = @parser.parse('#raw#hello world#end#')
971
+ result.should be_kind_of(WalrusGrammar::Directive)
972
+ result.should be_kind_of(WalrusGrammar::RawDirective)
973
+ result.content.should == 'hello world'
974
+
975
+ result = @parser.parse("#raw#hello world#end\n")
976
+ result.should be_kind_of(WalrusGrammar::Directive)
977
+ result.should be_kind_of(WalrusGrammar::RawDirective)
978
+ result.content.should == 'hello world'
979
+
980
+ result = @parser.parse("#raw\nhello world\n#end")
981
+ result.should be_kind_of(WalrusGrammar::Directive)
982
+ result.should be_kind_of(WalrusGrammar::RawDirective)
983
+ result.content.should == "hello world\n"
984
+
985
+ result = @parser.parse("#raw\nhello world\n#end#")
986
+ result.should be_kind_of(WalrusGrammar::Directive)
987
+ result.should be_kind_of(WalrusGrammar::RawDirective)
988
+ result.content.should == "hello world\n"
989
+
990
+ # with embedded directives (should be ignored)
991
+ result = @parser.parse('#raw##def example#end')
992
+ result.should be_kind_of(WalrusGrammar::Directive)
993
+ result.should be_kind_of(WalrusGrammar::RawDirective)
994
+ result.content.should == '#def example'
995
+
996
+ # with embedded placeholders (should be ignored)
997
+ result = @parser.parse('#raw#$foobar#end')
998
+ result.should be_kind_of(WalrusGrammar::Directive)
999
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1000
+ result.content.should == '$foobar'
1001
+
1002
+ # with embedded escapes (should be ignored)
1003
+ result = @parser.parse('#raw#\\$placeholder#end')
1004
+ result.should be_kind_of(WalrusGrammar::Directive)
1005
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1006
+ result.content.should == '\\$placeholder'
1007
+
1008
+ # note that you can't include a literal "#end" in the raw block
1009
+ lambda { @parser.parse('#raw# here is my #end! #end') }.should raise_error(Grammar::ParseError)
1010
+
1011
+ # must use a "here doc" in order to do that
1012
+ result = @parser.parse('#raw <<HERE_DOCUMENT
1013
+ #end
1014
+ HERE_DOCUMENT')
1015
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1016
+ result.content.should == "#end\n"
1017
+
1018
+ # optionally indented end marker
1019
+ result = @parser.parse('#raw <<-HERE_DOCUMENT
1020
+ #end
1021
+ HERE_DOCUMENT')
1022
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1023
+ result.content.should == "#end\n"
1024
+
1025
+ # actually indented end marker
1026
+ result = @parser.parse('#raw <<-HERE_DOCUMENT
1027
+ #end
1028
+ HERE_DOCUMENT')
1029
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1030
+ result.content.should == "#end\n"
1031
+
1032
+ # empty here document
1033
+ result = @parser.parse('#raw <<HERE_DOCUMENT
1034
+ HERE_DOCUMENT')
1035
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1036
+ result.content.should == ''
1037
+
1038
+ result = @parser.parse('#raw <<-HERE_DOCUMENT
1039
+ HERE_DOCUMENT')
1040
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1041
+ result.content.should == ''
1042
+
1043
+ # whitespace after end marker
1044
+ result = @parser.parse('#raw <<HERE_DOCUMENT
1045
+ #end
1046
+ HERE_DOCUMENT ')
1047
+ result.should be_kind_of(WalrusGrammar::RawDirective)
1048
+ result.content.should == "#end\n"
1049
+
1050
+ # invalid here document (whitespace before end marker)
1051
+ lambda { @parser.parse('#raw <<HERE_DOCUMENT
1052
+ #end
1053
+ HERE_DOCUMENT') }.should raise_error(Grammar::ParseError)
1054
+
1055
+ # invalid here document (non-matching end marker)
1056
+ lambda { @parser.parse('#raw <<HERE_DOCUMENT
1057
+ #end
1058
+ THERE_DOCUMENT') }.should raise_error(Grammar::ParseError)
1059
+
1060
+ end
1061
+
1062
+ it 'should be able to parse the "ruby" directive' do
1063
+
1064
+ # the end marker is required
1065
+ lambda { @parser.parse('#ruby') }.should raise_error(Grammar::ParseError)
1066
+ lambda { @parser.parse('#ruby#foo') }.should raise_error(Grammar::ParseError)
1067
+
1068
+ # shortest possible version
1069
+ result = @parser.parse('#ruby##end')
1070
+ result.should be_kind_of(WalrusGrammar::Directive)
1071
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1072
+ result.content.should == ''
1073
+
1074
+ # two line version, also short
1075
+ result = @parser.parse("#ruby\n#end")
1076
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1077
+ result.content.should == ''
1078
+
1079
+ # simple examples with content
1080
+ result = @parser.parse('#ruby#hello world#end')
1081
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1082
+ result.content.should == 'hello world'
1083
+
1084
+ result = @parser.parse("#ruby\nfoobar#end")
1085
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1086
+ result.content.should == 'foobar'
1087
+
1088
+ # can include anything at all in the block, escape sequences, placeholders, directives etc and all will be ignored
1089
+ result = @parser.parse("#ruby\n#ignored,$ignored,\\#ignored,\\$ignored#end")
1090
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1091
+ result.content.should == '#ignored,$ignored,\\#ignored,\\$ignored'
1092
+
1093
+ # to include a literal "#end" you must use a here document
1094
+ result = @parser.parse('#ruby <<HERE_DOCUMENT
1095
+ #end
1096
+ HERE_DOCUMENT')
1097
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1098
+ result.content.should == "#end\n"
1099
+
1100
+ # optionally indented end marker
1101
+ result = @parser.parse('#ruby <<-HERE_DOCUMENT
1102
+ #end
1103
+ HERE_DOCUMENT')
1104
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1105
+ result.content.should == "#end\n"
1106
+
1107
+ # actually indented end marker
1108
+ result = @parser.parse('#ruby <<-HERE_DOCUMENT
1109
+ #end
1110
+ HERE_DOCUMENT')
1111
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1112
+ result.content.should == "#end\n"
1113
+
1114
+ # empty here document
1115
+ result = @parser.parse('#ruby <<HERE_DOCUMENT
1116
+ HERE_DOCUMENT')
1117
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1118
+ result.content.should == ''
1119
+
1120
+ result = @parser.parse('#ruby <<-HERE_DOCUMENT
1121
+ HERE_DOCUMENT')
1122
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1123
+ result.content.should == ''
1124
+
1125
+ # whitespace after end marker
1126
+ result = @parser.parse('#ruby <<HERE_DOCUMENT
1127
+ #end
1128
+ HERE_DOCUMENT ')
1129
+ result.should be_kind_of(WalrusGrammar::RubyDirective)
1130
+ result.content.should == "#end\n"
1131
+
1132
+ # invalid here document (whitespace before end marker)
1133
+ lambda { @parser.parse('#ruby <<HERE_DOCUMENT
1134
+ #end
1135
+ HERE_DOCUMENT') }.should raise_error(Grammar::ParseError)
1136
+
1137
+ # invalid here document (non-matching end marker)
1138
+ lambda { @parser.parse('#ruby <<HERE_DOCUMENT
1139
+ #end
1140
+ THERE_DOCUMENT') }.should raise_error(Grammar::ParseError)
1141
+
1142
+ end
1143
+
1144
+ it 'should be able to parse the "set" directive' do
1145
+
1146
+ # assign a string literal
1147
+ result = @parser.parse('#set $foo = "bar"')
1148
+ result.should be_kind_of(WalrusGrammar::Directive)
1149
+ result.should be_kind_of(WalrusGrammar::SetDirective)
1150
+ result.placeholder.to_s.should == 'foo'
1151
+ result.expression.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1152
+ result.expression.lexeme.should == 'bar'
1153
+
1154
+ # assign a local variable
1155
+ result = @parser.parse('#set $foo = bar')
1156
+ result.should be_kind_of(WalrusGrammar::SetDirective)
1157
+ result.placeholder.to_s.should == 'foo'
1158
+ result.expression.should be_kind_of(WalrusGrammar::Identifier)
1159
+ result.expression.lexeme.should == 'bar'
1160
+
1161
+ # no whitespace allowed between "$" and placeholder name
1162
+ lambda { @parser.parse('#set $ foo = bar') }.should raise_error(Grammar::ParseError)
1163
+
1164
+ # "long form" not allowed in #set directives
1165
+ lambda { @parser.parse('#set ${foo} = bar') }.should raise_error(Grammar::ParseError)
1166
+
1167
+ # explicitly close directive
1168
+ result = @parser.parse('#set $foo = "bar"#')
1169
+ result.should be_kind_of(WalrusGrammar::SetDirective)
1170
+ result.placeholder.to_s.should == 'foo'
1171
+ result.expression.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1172
+ result.expression.lexeme.should == 'bar'
1173
+
1174
+ end
1175
+
1176
+ it 'should be able to parse the "silent" directive' do
1177
+
1178
+ # for more detailed tests see "should be able to parse basic Ruby expressions above"
1179
+ lambda { @parser.parse('#silent') }.should raise_error(Grammar::ParseError)
1180
+
1181
+ # allow multiple expressions separated by semicolons
1182
+ result = @parser.parse('#silent foo; bar')
1183
+ result.should be_kind_of(WalrusGrammar::Directive)
1184
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
1185
+ result.expression.should be_kind_of(Array)
1186
+ result.expression[0].should be_kind_of(WalrusGrammar::Identifier)
1187
+ result.expression[0].lexeme.should == 'foo'
1188
+ result.expression[1].should be_kind_of(WalrusGrammar::Identifier)
1189
+ result.expression[1].lexeme.should == 'bar'
1190
+
1191
+ end
1192
+
1193
+ it 'should be able to parse "silent" directive, short notation' do
1194
+
1195
+ # single expression
1196
+ result = @parser.parse('# 1 #')
1197
+ result.should be_kind_of(WalrusGrammar::Directive)
1198
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
1199
+ result.expression.should be_kind_of(WalrusGrammar::NumericLiteral)
1200
+ result.expression.lexeme.should == '1'
1201
+
1202
+ # expression list
1203
+ result = @parser.parse('# foo; bar #')
1204
+ result.should be_kind_of(WalrusGrammar::SilentDirective)
1205
+ result.expression.should be_kind_of(Array)
1206
+ result.expression[0].should be_kind_of(WalrusGrammar::Identifier)
1207
+ result.expression[0].lexeme.should == 'foo'
1208
+ result.expression[1].should be_kind_of(WalrusGrammar::Identifier)
1209
+ result.expression[1].lexeme.should == 'bar'
1210
+
1211
+ # more complex expression
1212
+ result = @parser.parse("# @secret_ivar = 'foo' #")
1213
+ result = @parser.parse("# foo + bar #")
1214
+ result = @parser.parse("# foo.bar #")
1215
+ result = @parser.parse("# [foo, bar]#")
1216
+ result = @parser.parse("# { :foo => bar }#")
1217
+
1218
+ # leading whitespace is obligatory
1219
+ lambda { @parser.parse('#1 #') }.should raise_error(Grammar::ParseError)
1220
+ lambda { @parser.parse('#foo; bar #') }.should raise_error(Grammar::ParseError)
1221
+
1222
+ # explicit end marker is required
1223
+ lambda { @parser.parse('# 1') }.should raise_error(Grammar::ParseError)
1224
+ lambda { @parser.parse('# foo; bar') }.should raise_error(Grammar::ParseError)
1225
+
1226
+ end
1227
+
1228
+ it 'should be able to parse the "slurp" directive' do
1229
+
1230
+ # basic case
1231
+ result = @parser.parse("hello #slurp\nworld")
1232
+ result[0].should be_kind_of(WalrusGrammar::RawText)
1233
+ result[0].lexeme.should == 'hello '
1234
+ result[1].should be_kind_of(WalrusGrammar::SlurpDirective)
1235
+ result[2].should be_kind_of(WalrusGrammar::RawText)
1236
+ result[2].lexeme.should == 'world'
1237
+
1238
+ # must be the last thing on the line (no comments)
1239
+ lambda { @parser.parse("hello #slurp ## my comment...\nworld") }.should raise_error(Grammar::ParseError)
1240
+
1241
+ # but intervening whitespace is ok
1242
+ result = @parser.parse("hello #slurp \nworld")
1243
+ result[0].should be_kind_of(WalrusGrammar::RawText)
1244
+ result[0].lexeme.should == 'hello '
1245
+ result[1].should be_kind_of(WalrusGrammar::SlurpDirective)
1246
+ result[2].should be_kind_of(WalrusGrammar::RawText)
1247
+ result[2].lexeme.should == 'world'
1248
+
1249
+ # should only slurp one newline, not multiple newlines
1250
+ result = @parser.parse("hello #slurp\n\n\nworld") # three newlines
1251
+ result[0].should be_kind_of(WalrusGrammar::RawText)
1252
+ result[0].lexeme.should == 'hello '
1253
+ result[1].should be_kind_of(WalrusGrammar::SlurpDirective)
1254
+ result[2].should be_kind_of(WalrusGrammar::RawText)
1255
+ result[2].lexeme.should == "\n\nworld" # one newline slurped, two left
1256
+
1257
+ end
1258
+
1259
+ it 'should be able to parse the "super" directive with parentheses' do
1260
+
1261
+ # super with empty params
1262
+ result = @parser.parse('#super()')
1263
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1264
+ result.params.should == []
1265
+
1266
+ # same with intervening whitespace
1267
+ result = @parser.parse('#super ()')
1268
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1269
+ result.params.should == []
1270
+
1271
+ # super with one param
1272
+ result = @parser.parse('#super("foo")')
1273
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1274
+ result.params.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1275
+ result.params.lexeme.should == 'foo'
1276
+ result.params.to_s.should == 'foo'
1277
+
1278
+ # same with intervening whitespace
1279
+ result = @parser.parse('#super ("foo")')
1280
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1281
+ result.params.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1282
+ result.params.lexeme.should == 'foo'
1283
+ result.params.to_s.should == 'foo'
1284
+
1285
+ # super with two params
1286
+ result = @parser.parse('#super("foo", "bar")')
1287
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1288
+ result.params.should be_kind_of(Array)
1289
+ result.params[0].should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1290
+ result.params[0].lexeme.should == 'foo'
1291
+ result.params[0].to_s.should == 'foo'
1292
+ result.params[1].should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1293
+ result.params[1].lexeme.should == 'bar'
1294
+ result.params[1].to_s.should == 'bar'
1295
+
1296
+ # same with intervening whitespace
1297
+ result = @parser.parse('#super ("foo", "bar")')
1298
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1299
+ result.params.should be_kind_of(Array)
1300
+ result.params[0].should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1301
+ result.params[0].lexeme.should == 'foo'
1302
+ result.params[0].to_s.should == 'foo'
1303
+ result.params[1].should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1304
+ result.params[1].lexeme.should == 'bar'
1305
+ result.params[1].to_s.should == 'bar'
1306
+
1307
+ end
1308
+
1309
+ it 'should be able to parse the "super" directive without parentheses' do
1310
+
1311
+ # super with no params
1312
+ result = @parser.parse('#super')
1313
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1314
+ result.params.should == []
1315
+
1316
+ # super with one param
1317
+ result = @parser.parse('#super "foo"')
1318
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1319
+ result.params.should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1320
+ result.params.lexeme.should == 'foo'
1321
+ result.params.to_s.should == 'foo'
1322
+
1323
+ # super with two params
1324
+ result = @parser.parse('#super "foo", "bar"')
1325
+ result.should be_kind_of(WalrusGrammar::SuperDirective)
1326
+ result.params.should be_kind_of(Array)
1327
+ result.params[0].should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1328
+ result.params[0].lexeme.should == 'foo'
1329
+ result.params[0].to_s.should == 'foo'
1330
+ result.params[1].should be_kind_of(WalrusGrammar::DoubleQuotedStringLiteral)
1331
+ result.params[1].lexeme.should == 'bar'
1332
+ result.params[1].to_s.should == 'bar'
1333
+
1334
+ end
1335
+
1336
+ it 'parse results should contain information about their location in the original source (line and column start/end)' do
1337
+
1338
+ # simple raw text
1339
+ result = @parser.parse('hello world')
1340
+ result.line_start.should == 0 # where the node starts
1341
+ result.column_start.should == 0 # where the node starts
1342
+ result.line_end.should == 0 # how far the parser got
1343
+ result.column_end.should == 11 # how far the parser got
1344
+
1345
+ # super with two params
1346
+ result = @parser.parse('#super "foo", "bar"')
1347
+ result.line_start.should == 0
1348
+ result.column_start.should == 0
1349
+ result.line_end.should == 0
1350
+ result.column_end.should == 19
1351
+ result.params.line_start.should == 0
1352
+ # result.params.column_start.should == 7 # get 0
1353
+ result.params.line_end.should == 0
1354
+ result.params.column_end.should == 19
1355
+ result.params[0].line_start.should == 0
1356
+ result.params[0].column_start.should == 7
1357
+ result.params[0].line_end.should == 0
1358
+ result.params[0].column_end.should == 12
1359
+ result.params[1].line_start.should == 0
1360
+ # result.params[1].column_start.should == 14 # get 12
1361
+ result.params[1].line_end.should == 0
1362
+ result.params[1].column_end.should == 19
1363
+
1364
+ end
1365
+
1366
+ it 'ParseErrors should contain information about the location of the problem' do
1367
+
1368
+ # error at beginning of string (unknown directive)
1369
+ begin
1370
+ @parser.parse('#sooper')
1371
+ rescue Grammar::ParseError => e
1372
+ exception = e
1373
+ end
1374
+ exception.line_start.should == 0
1375
+ exception.column_start.should == 0
1376
+ exception.line_end.should == 0
1377
+ exception.column_end.should == 0
1378
+
1379
+ # error on second line (unknown directive)
1380
+ begin
1381
+ @parser.parse("## a comment\n#sooper")
1382
+ rescue Grammar::ParseError => e
1383
+ exception = e
1384
+ end
1385
+ exception.line_start.should == 0
1386
+ exception.column_start.should == 0
1387
+ exception.line_end.should == 1
1388
+ exception.column_end.should == 0
1389
+
1390
+ # error at end of second line (missing closing bracket)
1391
+ begin
1392
+ @parser.parse("## a comment\n#super (1, 2")
1393
+ rescue Grammar::ParseError => e
1394
+ exception = e
1395
+ end
1396
+ exception.line_start.should == 0
1397
+ exception.column_start.should == 0
1398
+ exception.line_end.should == 1
1399
+ # exception.column_end.should == 12 # returns 0, which is almost right... but we want the rightmost coordinate, not the beginning of the busted directive
1400
+
1401
+ # here the error was returned at line 1, column 0 (the very beginning of the #super directive)
1402
+ # but we really would have preferred it to be reported at column 12 (the missing closing bracket)
1403
+ # to get to the rightmost point the parser will have had to follow this path:
1404
+ # - try to scan a directive
1405
+ # - try to scan a super directive
1406
+ # - try to scan a parameter list
1407
+ # - try to scan a paremeter etc
1408
+
1409
+ end
1410
+
1411
+ it 'produced AST nodes should contain information about their location in the source file' do
1412
+
1413
+ end
1414
+
1415
+ end
1416
+
1417
+ end # module Walrus
1418
+