plurimath-parslet 3.0.0

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 (148) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.txt +284 -0
  3. data/LICENSE +23 -0
  4. data/README.adoc +454 -0
  5. data/Rakefile +71 -0
  6. data/lib/parslet/accelerator/application.rb +62 -0
  7. data/lib/parslet/accelerator/engine.rb +112 -0
  8. data/lib/parslet/accelerator.rb +162 -0
  9. data/lib/parslet/atoms/alternative.rb +53 -0
  10. data/lib/parslet/atoms/base.rb +157 -0
  11. data/lib/parslet/atoms/can_flatten.rb +137 -0
  12. data/lib/parslet/atoms/capture.rb +38 -0
  13. data/lib/parslet/atoms/context.rb +103 -0
  14. data/lib/parslet/atoms/dsl.rb +112 -0
  15. data/lib/parslet/atoms/dynamic.rb +32 -0
  16. data/lib/parslet/atoms/entity.rb +45 -0
  17. data/lib/parslet/atoms/ignored.rb +26 -0
  18. data/lib/parslet/atoms/infix.rb +115 -0
  19. data/lib/parslet/atoms/lookahead.rb +52 -0
  20. data/lib/parslet/atoms/named.rb +32 -0
  21. data/lib/parslet/atoms/re.rb +41 -0
  22. data/lib/parslet/atoms/repetition.rb +87 -0
  23. data/lib/parslet/atoms/scope.rb +26 -0
  24. data/lib/parslet/atoms/sequence.rb +48 -0
  25. data/lib/parslet/atoms/str.rb +42 -0
  26. data/lib/parslet/atoms/visitor.rb +89 -0
  27. data/lib/parslet/atoms.rb +34 -0
  28. data/lib/parslet/cause.rb +101 -0
  29. data/lib/parslet/context.rb +21 -0
  30. data/lib/parslet/convenience.rb +33 -0
  31. data/lib/parslet/error_reporter/contextual.rb +120 -0
  32. data/lib/parslet/error_reporter/deepest.rb +100 -0
  33. data/lib/parslet/error_reporter/tree.rb +63 -0
  34. data/lib/parslet/error_reporter.rb +8 -0
  35. data/lib/parslet/export.rb +163 -0
  36. data/lib/parslet/expression/treetop.rb +92 -0
  37. data/lib/parslet/expression.rb +51 -0
  38. data/lib/parslet/graphviz.rb +97 -0
  39. data/lib/parslet/parser.rb +68 -0
  40. data/lib/parslet/pattern/binding.rb +49 -0
  41. data/lib/parslet/pattern.rb +113 -0
  42. data/lib/parslet/position.rb +21 -0
  43. data/lib/parslet/rig/rspec.rb +52 -0
  44. data/lib/parslet/scope.rb +42 -0
  45. data/lib/parslet/slice.rb +105 -0
  46. data/lib/parslet/source/line_cache.rb +99 -0
  47. data/lib/parslet/source.rb +96 -0
  48. data/lib/parslet/transform.rb +265 -0
  49. data/lib/parslet/version.rb +5 -0
  50. data/lib/parslet.rb +314 -0
  51. data/plurimath-parslet.gemspec +42 -0
  52. data/spec/acceptance/infix_parser_spec.rb +145 -0
  53. data/spec/acceptance/mixing_parsers_spec.rb +74 -0
  54. data/spec/acceptance/regression_spec.rb +329 -0
  55. data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
  56. data/spec/acceptance/unconsumed_input_spec.rb +21 -0
  57. data/spec/examples/boolean_algebra_spec.rb +257 -0
  58. data/spec/examples/calc_spec.rb +278 -0
  59. data/spec/examples/capture_spec.rb +137 -0
  60. data/spec/examples/comments_spec.rb +186 -0
  61. data/spec/examples/deepest_errors_spec.rb +420 -0
  62. data/spec/examples/documentation_spec.rb +205 -0
  63. data/spec/examples/email_parser_spec.rb +275 -0
  64. data/spec/examples/empty_spec.rb +37 -0
  65. data/spec/examples/erb_spec.rb +482 -0
  66. data/spec/examples/ip_address_spec.rb +153 -0
  67. data/spec/examples/json_spec.rb +413 -0
  68. data/spec/examples/local_spec.rb +302 -0
  69. data/spec/examples/mathn_spec.rb +151 -0
  70. data/spec/examples/minilisp_spec.rb +492 -0
  71. data/spec/examples/modularity_spec.rb +340 -0
  72. data/spec/examples/nested_errors_spec.rb +322 -0
  73. data/spec/examples/optimized_erb_spec.rb +299 -0
  74. data/spec/examples/parens_spec.rb +239 -0
  75. data/spec/examples/prec_calc_spec.rb +525 -0
  76. data/spec/examples/readme_spec.rb +228 -0
  77. data/spec/examples/scopes_spec.rb +187 -0
  78. data/spec/examples/seasons_spec.rb +196 -0
  79. data/spec/examples/sentence_spec.rb +119 -0
  80. data/spec/examples/simple_xml_spec.rb +250 -0
  81. data/spec/examples/string_parser_spec.rb +407 -0
  82. data/spec/fixtures/examples/boolean_algebra.rb +62 -0
  83. data/spec/fixtures/examples/calc.rb +86 -0
  84. data/spec/fixtures/examples/capture.rb +36 -0
  85. data/spec/fixtures/examples/comments.rb +22 -0
  86. data/spec/fixtures/examples/deepest_errors.rb +99 -0
  87. data/spec/fixtures/examples/documentation.rb +32 -0
  88. data/spec/fixtures/examples/email_parser.rb +42 -0
  89. data/spec/fixtures/examples/empty.rb +10 -0
  90. data/spec/fixtures/examples/erb.rb +39 -0
  91. data/spec/fixtures/examples/ip_address.rb +103 -0
  92. data/spec/fixtures/examples/json.rb +107 -0
  93. data/spec/fixtures/examples/local.rb +60 -0
  94. data/spec/fixtures/examples/mathn.rb +47 -0
  95. data/spec/fixtures/examples/minilisp.rb +75 -0
  96. data/spec/fixtures/examples/modularity.rb +60 -0
  97. data/spec/fixtures/examples/nested_errors.rb +95 -0
  98. data/spec/fixtures/examples/optimized_erb.rb +105 -0
  99. data/spec/fixtures/examples/parens.rb +25 -0
  100. data/spec/fixtures/examples/prec_calc.rb +71 -0
  101. data/spec/fixtures/examples/readme.rb +59 -0
  102. data/spec/fixtures/examples/scopes.rb +43 -0
  103. data/spec/fixtures/examples/seasons.rb +40 -0
  104. data/spec/fixtures/examples/sentence.rb +18 -0
  105. data/spec/fixtures/examples/simple_xml.rb +51 -0
  106. data/spec/fixtures/examples/string_parser.rb +77 -0
  107. data/spec/parslet/atom_results_spec.rb +39 -0
  108. data/spec/parslet/atoms/alternative_spec.rb +26 -0
  109. data/spec/parslet/atoms/base_spec.rb +127 -0
  110. data/spec/parslet/atoms/capture_spec.rb +21 -0
  111. data/spec/parslet/atoms/combinations_spec.rb +5 -0
  112. data/spec/parslet/atoms/dsl_spec.rb +7 -0
  113. data/spec/parslet/atoms/entity_spec.rb +77 -0
  114. data/spec/parslet/atoms/ignored_spec.rb +15 -0
  115. data/spec/parslet/atoms/infix_spec.rb +5 -0
  116. data/spec/parslet/atoms/lookahead_spec.rb +22 -0
  117. data/spec/parslet/atoms/named_spec.rb +4 -0
  118. data/spec/parslet/atoms/re_spec.rb +14 -0
  119. data/spec/parslet/atoms/repetition_spec.rb +24 -0
  120. data/spec/parslet/atoms/scope_spec.rb +26 -0
  121. data/spec/parslet/atoms/sequence_spec.rb +28 -0
  122. data/spec/parslet/atoms/str_spec.rb +15 -0
  123. data/spec/parslet/atoms/visitor_spec.rb +101 -0
  124. data/spec/parslet/atoms_spec.rb +488 -0
  125. data/spec/parslet/convenience_spec.rb +54 -0
  126. data/spec/parslet/error_reporter/contextual_spec.rb +118 -0
  127. data/spec/parslet/error_reporter/deepest_spec.rb +82 -0
  128. data/spec/parslet/error_reporter/tree_spec.rb +7 -0
  129. data/spec/parslet/export_spec.rb +40 -0
  130. data/spec/parslet/expression/treetop_spec.rb +74 -0
  131. data/spec/parslet/minilisp.citrus +29 -0
  132. data/spec/parslet/minilisp.tt +29 -0
  133. data/spec/parslet/parser_spec.rb +36 -0
  134. data/spec/parslet/parslet_spec.rb +38 -0
  135. data/spec/parslet/pattern_spec.rb +272 -0
  136. data/spec/parslet/position_spec.rb +14 -0
  137. data/spec/parslet/rig/rspec_spec.rb +54 -0
  138. data/spec/parslet/scope_spec.rb +45 -0
  139. data/spec/parslet/slice_spec.rb +186 -0
  140. data/spec/parslet/source/line_cache_spec.rb +74 -0
  141. data/spec/parslet/source_spec.rb +210 -0
  142. data/spec/parslet/transform/context_spec.rb +56 -0
  143. data/spec/parslet/transform_spec.rb +183 -0
  144. data/spec/spec_helper.rb +74 -0
  145. data/spec/support/opal.rb +8 -0
  146. data/spec/support/opal.rb.erb +14 -0
  147. data/spec/support/parslet_matchers.rb +96 -0
  148. metadata +240 -0
@@ -0,0 +1,420 @@
1
+ require 'spec_helper'
2
+ require_relative '../fixtures/examples/deepest_errors'
3
+
4
+ RSpec.describe 'Deepest Errors Parser Example' do
5
+ let(:parser) { DeepestErrorsParser.new }
6
+
7
+ describe DeepestErrorsParser do
8
+ describe '#space' do
9
+ it 'parses single space' do
10
+ result = parser.space.parse(' ')
11
+ expect(result).to eq(' ')
12
+ end
13
+
14
+ it 'parses multiple spaces' do
15
+ result = parser.space.parse(' ')
16
+ expect(result).to eq(' ')
17
+ end
18
+
19
+ it 'parses tabs' do
20
+ result = parser.space.parse("\t")
21
+ expect(result).to eq("\t")
22
+ end
23
+
24
+ it 'parses mixed spaces and tabs' do
25
+ result = parser.space.parse(" \t ")
26
+ expect(result).to eq(" \t ")
27
+ end
28
+
29
+ it 'fails on empty string' do
30
+ expect { parser.space.parse('') }.to raise_error(Parslet::ParseFailed)
31
+ end
32
+ end
33
+
34
+ describe '#space?' do
35
+ it 'parses optional space' do
36
+ result = parser.space?.parse(' ')
37
+ expect(result).to eq(' ')
38
+ end
39
+
40
+ it 'parses empty string' do
41
+ result = parser.space?.parse('')
42
+ expect(result).to eq('')
43
+ end
44
+ end
45
+
46
+ describe '#newline' do
47
+ it 'parses carriage return' do
48
+ result = parser.newline.parse("\r")
49
+ expect(result).to eq("\r")
50
+ end
51
+
52
+ it 'parses line feed' do
53
+ result = parser.newline.parse("\n")
54
+ expect(result).to eq("\n")
55
+ end
56
+
57
+ it 'fails on other characters' do
58
+ expect { parser.newline.parse('a') }.to raise_error(Parslet::ParseFailed)
59
+ end
60
+ end
61
+
62
+ describe '#comment' do
63
+ it 'parses simple comment' do
64
+ result = parser.comment.parse('# this is a comment')
65
+ expect(result).to eq('# this is a comment')
66
+ end
67
+
68
+ it 'parses empty comment' do
69
+ result = parser.comment.parse('#')
70
+ expect(result).to eq('#')
71
+ end
72
+
73
+ it 'stops at newline' do
74
+ result = parser.comment.parse('# comment')
75
+ expect(result).to eq('# comment')
76
+ end
77
+ end
78
+
79
+ describe '#identifier' do
80
+ it 'parses simple identifier' do
81
+ result = parser.identifier.parse('test')
82
+ expect(result).to eq('test')
83
+ end
84
+
85
+ it 'parses identifier with numbers' do
86
+ result = parser.identifier.parse('test123')
87
+ expect(result).to eq('test123')
88
+ end
89
+
90
+ it 'parses identifier with underscores' do
91
+ result = parser.identifier.parse('test_name')
92
+ expect(result).to eq('test_name')
93
+ end
94
+
95
+ it 'fails on empty string' do
96
+ expect { parser.identifier.parse('') }.to raise_error(Parslet::ParseFailed)
97
+ end
98
+ end
99
+
100
+ describe '#reference' do
101
+ it 'parses single @ reference' do
102
+ result = parser.reference.parse('@res')
103
+ expected = { reference: '@res' }
104
+ expect(result).to parse_as(expected)
105
+ end
106
+
107
+ it 'parses double @ reference' do
108
+ result = parser.reference.parse('@@global')
109
+ expected = { reference: '@@global' }
110
+ expect(result).to parse_as(expected)
111
+ end
112
+
113
+ it 'fails without @' do
114
+ expect { parser.reference.parse('res') }.to raise_error(Parslet::ParseFailed)
115
+ end
116
+ end
117
+
118
+ describe '#res_action_or_link' do
119
+ it 'parses method call without question mark' do
120
+ result = parser.res_action_or_link.parse('.name()')
121
+ expected = {
122
+ dot: '.',
123
+ name: 'name'
124
+ }
125
+ expect(result).to parse_as(expected)
126
+ end
127
+
128
+ it 'parses method call with question mark' do
129
+ result = parser.res_action_or_link.parse('.valid?()')
130
+ expected = {
131
+ dot: '.',
132
+ name: 'valid?'
133
+ }
134
+ expect(result).to parse_as(expected)
135
+ end
136
+ end
137
+
138
+ describe '#res_actions' do
139
+ it 'parses reference only' do
140
+ result = parser.res_actions.parse('@res')
141
+ expected = {
142
+ resources: { reference: '@res' },
143
+ res_actions: []
144
+ }
145
+ expect(result).to parse_as(expected)
146
+ end
147
+
148
+ it 'parses reference with single action' do
149
+ result = parser.res_actions.parse('@res.name()')
150
+ expected = {
151
+ resources: { reference: '@res' },
152
+ res_actions: [
153
+ {
154
+ res_action: {
155
+ dot: '.',
156
+ name: 'name'
157
+ }
158
+ }
159
+ ]
160
+ }
161
+ expect(result).to parse_as(expected)
162
+ end
163
+
164
+ it 'parses reference with multiple actions' do
165
+ result = parser.res_actions.parse('@res.name().valid?()')
166
+ expected = {
167
+ resources: { reference: '@res' },
168
+ res_actions: [
169
+ {
170
+ res_action: {
171
+ dot: '.',
172
+ name: 'name'
173
+ }
174
+ },
175
+ {
176
+ res_action: {
177
+ dot: '.',
178
+ name: 'valid?'
179
+ }
180
+ }
181
+ ]
182
+ }
183
+ expect(result).to parse_as(expected)
184
+ end
185
+ end
186
+
187
+ describe '#res_statement' do
188
+ it 'parses resource statement without field' do
189
+ result = parser.res_statement.parse('@res.name()')
190
+ expected = {
191
+ resources: { reference: '@res' },
192
+ res_actions: [
193
+ {
194
+ res_action: {
195
+ dot: '.',
196
+ name: 'name'
197
+ }
198
+ }
199
+ ],
200
+ res_field: nil
201
+ }
202
+ expect(result).to parse_as(expected)
203
+ end
204
+
205
+ it 'parses resource statement with field' do
206
+ result = parser.res_statement.parse('@res.name():field')
207
+ expected = {
208
+ resources: { reference: '@res' },
209
+ res_actions: [
210
+ {
211
+ res_action: {
212
+ dot: '.',
213
+ name: 'name'
214
+ }
215
+ }
216
+ ],
217
+ res_field: { name: 'field' }
218
+ }
219
+ expect(result).to parse_as(expected)
220
+ end
221
+
222
+ it 'parses simple resource without actions or field' do
223
+ result = parser.res_statement.parse('@res')
224
+ expected = {
225
+ resources: { reference: '@res' },
226
+ res_actions: [],
227
+ res_field: nil
228
+ }
229
+ expect(result).to parse_as(expected)
230
+ end
231
+ end
232
+
233
+ describe '#define_block' do
234
+ it 'parses simple define block with proper formatting' do
235
+ input = "define f()\n@res.name()\nend"
236
+
237
+ result = parser.define_block.parse(input)
238
+ expect(result[:define]).to eq('define')
239
+ expect(result[:name]).to eq('f')
240
+ expect(result[:body]).to be_an(Array)
241
+ expect(result[:body].length).to eq(1)
242
+ end
243
+
244
+ it 'fails on malformed define block (demonstrating error reporting)' do
245
+ input = <<~CODE
246
+ define f()
247
+ @res.name
248
+ end
249
+ CODE
250
+
251
+ # This should fail due to improper syntax (@res.name should be @res.name())
252
+ expect { parser.define_block.parse(input) }.to raise_error(Parslet::ParseFailed)
253
+ end
254
+ end
255
+
256
+ describe '#begin_block' do
257
+ it 'parses simple begin block with proper formatting' do
258
+ input = "begin\n@res.name()\nend"
259
+
260
+ result = parser.begin_block.parse(input)
261
+ expect(result[:pre]).to be_nil
262
+ expect(result[:begin]).to eq('begin')
263
+ expect(result[:body]).to be_an(Array)
264
+ end
265
+
266
+ it 'parses concurrent begin block with proper formatting' do
267
+ input = "concurrent begin\n@res.name()\nend"
268
+
269
+ result = parser.begin_block.parse(input)
270
+ expect(result[:pre][:type]).to eq('concurrent')
271
+ expect(result[:begin]).to eq('begin')
272
+ end
273
+
274
+ it 'fails on malformed begin block (demonstrating error reporting)' do
275
+ input = <<~CODE
276
+ begin
277
+ @res.name
278
+ end
279
+ CODE
280
+
281
+ # This should fail due to improper syntax (@res.name should be @res.name())
282
+ expect { parser.begin_block.parse(input) }.to raise_error(Parslet::ParseFailed)
283
+ end
284
+ end
285
+
286
+ describe 'root parser (radix)' do
287
+ it 'fails to parse the first example (demonstrating error reporting)' do
288
+ input = <<~CODE
289
+ define f()
290
+ @res.name
291
+ end
292
+ CODE
293
+
294
+ # This should fail - the example is designed to show error reporting
295
+ expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
296
+ end
297
+
298
+ it 'fails to parse the second example (demonstrating error reporting)' do
299
+ input = <<~CODE
300
+ define f()
301
+ begin
302
+ @res.name
303
+ end
304
+ end
305
+ CODE
306
+
307
+ # This should also fail - the example is designed to show error reporting
308
+ expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
309
+ end
310
+
311
+ it 'parses valid syntax correctly' do
312
+ # Create a properly formatted input that should parse
313
+ input = "define f()\n@res.name()\nend\n"
314
+
315
+ result = parser.parse(input)
316
+ expect(result[:define]).to eq('define')
317
+ expect(result[:name]).to eq('f')
318
+ expect(result[:body]).to be_an(Array)
319
+ end
320
+ end
321
+
322
+ describe 'error handling' do
323
+ it 'provides meaningful error messages for malformed input' do
324
+ expect { parser.parse('invalid syntax') }.to raise_error(Parslet::ParseFailed)
325
+ end
326
+
327
+ it 'handles incomplete define blocks' do
328
+ expect { parser.parse('define f()') }.to raise_error(Parslet::ParseFailed)
329
+ end
330
+
331
+ it 'handles incomplete begin blocks' do
332
+ expect { parser.parse('begin') }.to raise_error(Parslet::ParseFailed)
333
+ end
334
+ end
335
+
336
+ describe 'deepest error reporting' do
337
+ it 'demonstrates deepest error reporting with malformed input' do
338
+ input = <<~CODE
339
+ define f()
340
+ @res.name
341
+ end
342
+ CODE
343
+
344
+ # This should fail with regular parse
345
+ expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
346
+
347
+ # parse_with_debug returns nil on failure but prints detailed error info
348
+ result = parser.parse_with_debug(input,
349
+ :reporter => Parslet::ErrorReporter::Deepest.new)
350
+ expect(result).to be_nil
351
+ end
352
+
353
+ it 'works with parse_with_debug and deepest reporter on valid input' do
354
+ input = "define f()\n@res.name()\nend\n"
355
+
356
+ # Test that parse_with_debug works with valid input
357
+ result = parser.parse_with_debug(input,
358
+ :reporter => Parslet::ErrorReporter::Deepest.new)
359
+ expect(result).not_to be_nil
360
+ expect(result[:define]).to eq('define')
361
+ end
362
+
363
+ it 'demonstrates the purpose of the example - showing deepest errors' do
364
+ # The example is specifically designed to show how deepest error reporting
365
+ # provides better error messages than standard parsing
366
+ input = <<~CODE
367
+ define f()
368
+ @res.name
369
+ end
370
+ CODE
371
+
372
+ # Regular parse should fail with exception
373
+ expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
374
+
375
+ # parse_with_debug returns nil but provides detailed error info
376
+ result = parser.parse_with_debug(input, :reporter => Parslet::ErrorReporter::Deepest.new)
377
+ expect(result).to be_nil
378
+ end
379
+ end
380
+ end
381
+
382
+ describe 'prettify helper function' do
383
+ it 'formats strings with line numbers' do
384
+ input = "line 1\nline 2\nline 3"
385
+
386
+ # Capture the output
387
+ output = capture_stdout { prettify(input) }
388
+
389
+ expect(output).to include('01 line 1')
390
+ expect(output).to include('02 line 2')
391
+ expect(output).to include('03 line 3')
392
+ end
393
+
394
+ it 'handles single line input' do
395
+ input = "single line"
396
+
397
+ output = capture_stdout { prettify(input) }
398
+ expect(output).to include('01 single line')
399
+ end
400
+
401
+ it 'handles empty input' do
402
+ input = ""
403
+
404
+ output = capture_stdout { prettify(input) }
405
+ # Should still show the header line
406
+ expect(output).to include('10')
407
+ expect(output).to include('20')
408
+ end
409
+ end
410
+
411
+ # Helper method to capture stdout
412
+ def capture_stdout
413
+ original_stdout = $stdout
414
+ $stdout = StringIO.new
415
+ yield
416
+ $stdout.string
417
+ ensure
418
+ $stdout = original_stdout
419
+ end
420
+ end
@@ -0,0 +1,205 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/examples/documentation'
3
+
4
+ RSpec.describe 'Documentation Example' do
5
+ include DocumentationExample
6
+
7
+ describe 'DocumentationExample::MyParser' do
8
+ let(:parser) { DocumentationExample::MyParser.new }
9
+
10
+ describe 'basic parsing functionality' do
11
+ describe '#a rule' do
12
+ it 'parses empty string (zero repetitions)' do
13
+ result = parser.a.parse('')
14
+ expect(result.to_s).to eq('')
15
+ end
16
+
17
+ it 'parses single "a"' do
18
+ result = parser.a.parse('a')
19
+ expect(result.to_s).to eq('a')
20
+ end
21
+
22
+ it 'parses multiple "a" characters' do
23
+ result = parser.a.parse('aaaa')
24
+ expect(result.to_s).to eq('aaaa')
25
+ end
26
+
27
+ it 'parses long sequence of "a" characters' do
28
+ input = 'a' * 10
29
+ result = parser.a.parse(input)
30
+ expect(result.to_s).to eq(input)
31
+ end
32
+
33
+ it 'fails on non-"a" characters' do
34
+ expect { parser.a.parse('b') }.to raise_error(Parslet::ParseFailed)
35
+ end
36
+
37
+ it 'fails on mixed characters' do
38
+ expect { parser.a.parse('aab') }.to raise_error(Parslet::ParseFailed)
39
+ end
40
+ end
41
+
42
+ describe '#parse method' do
43
+ it 'parses valid input successfully' do
44
+ result = parser.parse('aaaa')
45
+ expect(result.to_s).to eq('aaaa')
46
+ end
47
+
48
+ it 'handles empty input' do
49
+ result = parser.parse('')
50
+ expect(result.to_s).to eq('')
51
+ end
52
+
53
+ it 'raises ParseFailed for invalid input' do
54
+ expect { parser.parse('bbbb') }.to raise_error(Parslet::ParseFailed)
55
+ end
56
+
57
+ it 'raises ParseFailed for partially valid input' do
58
+ expect { parser.parse('aaab') }.to raise_error(Parslet::ParseFailed)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe 'error handling and reporting' do
64
+ it 'provides meaningful error messages' do
65
+ begin
66
+ parser.parse('bbbb')
67
+ fail 'Expected ParseFailed to be raised'
68
+ rescue Parslet::ParseFailed => e
69
+ expect(e.message).to include('Extra input after last repetition')
70
+ expect(e.message).to include('line 1')
71
+ expect(e.message).to include('char 1')
72
+ end
73
+ end
74
+
75
+ it 'reports correct position for errors' do
76
+ begin
77
+ parser.parse('aaab')
78
+ fail 'Expected ParseFailed to be raised'
79
+ rescue Parslet::ParseFailed => e
80
+ expect(e.message).to include('char 4')
81
+ end
82
+ end
83
+
84
+ it 'handles different types of invalid input' do
85
+ invalid_inputs = ['x', '123', 'aax', 'xa']
86
+
87
+ invalid_inputs.each do |input|
88
+ expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
89
+ end
90
+ end
91
+ end
92
+
93
+ describe 'edge cases' do
94
+ it 'handles very long valid input' do
95
+ long_input = 'a' * 1000
96
+ result = parser.parse(long_input)
97
+ expect(result.to_s).to eq(long_input)
98
+ end
99
+
100
+ it 'handles single character invalid input' do
101
+ expect { parser.parse('z') }.to raise_error(Parslet::ParseFailed)
102
+ end
103
+
104
+ it 'handles whitespace input' do
105
+ expect { parser.parse(' ') }.to raise_error(Parslet::ParseFailed)
106
+ end
107
+
108
+ it 'handles special characters' do
109
+ special_chars = ['!', '@', '#', '$', '%', '^', '&', '*']
110
+
111
+ special_chars.each do |char|
112
+ expect { parser.parse(char) }.to raise_error(Parslet::ParseFailed)
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ describe 'module helper methods' do
119
+ describe '.parse_a_sequence' do
120
+ it 'parses valid sequences' do
121
+ result = DocumentationExample.parse_a_sequence('aaa')
122
+ expect(result.to_s).to eq('aaa')
123
+ end
124
+
125
+ it 'handles empty input' do
126
+ result = DocumentationExample.parse_a_sequence('')
127
+ expect(result.to_s).to eq('')
128
+ end
129
+
130
+ it 'raises error for invalid input' do
131
+ expect { DocumentationExample.parse_a_sequence('bbb') }.to raise_error(Parslet::ParseFailed)
132
+ end
133
+ end
134
+
135
+ describe '.demonstrate_success' do
136
+ it 'returns successful parse result' do
137
+ result = DocumentationExample.demonstrate_success
138
+ expect(result.to_s).to eq('aaaa')
139
+ end
140
+
141
+ it 'demonstrates successful parsing without raising errors' do
142
+ expect { DocumentationExample.demonstrate_success }.not_to raise_error
143
+ end
144
+ end
145
+
146
+ describe '.demonstrate_failure' do
147
+ it 'returns ParseFailed exception instead of raising it' do
148
+ result = DocumentationExample.demonstrate_failure
149
+ expect(result).to be_a(Parslet::ParseFailed)
150
+ end
151
+
152
+ it 'captures the error message' do
153
+ error = DocumentationExample.demonstrate_failure
154
+ expect(error.message).to include('Extra input after last repetition')
155
+ end
156
+
157
+ it 'does not raise an exception' do
158
+ expect { DocumentationExample.demonstrate_failure }.not_to raise_error
159
+ end
160
+ end
161
+ end
162
+
163
+ describe 'documentation and demonstration purposes' do
164
+ it 'shows basic parslet functionality' do
165
+ # This test demonstrates the basic use case shown in documentation
166
+ parser = DocumentationExample::MyParser.new
167
+
168
+ # Successful case
169
+ success_result = parser.parse('aaaa')
170
+ expect(success_result.to_s).to eq('aaaa')
171
+
172
+ # Failure case
173
+ expect { parser.parse('bbbb') }.to raise_error(Parslet::ParseFailed)
174
+ end
175
+
176
+ it 'demonstrates error reporting capabilities' do
177
+ # This test shows how parslet reports errors for documentation
178
+ parser = DocumentationExample::MyParser.new
179
+
180
+ begin
181
+ parser.parse('invalid')
182
+ fail 'Should have raised ParseFailed'
183
+ rescue Parslet::ParseFailed => e
184
+ # Error should contain useful information
185
+ expect(e.message).to be_a(String)
186
+ expect(e.message.length).to be > 0
187
+ expect(e.message).to include('Extra input after last repetition')
188
+ end
189
+ end
190
+
191
+ it 'shows the difference between success and failure' do
192
+ # Demonstrates the contrast for documentation purposes
193
+ valid_inputs = ['', 'a', 'aa', 'aaa', 'aaaa', 'aaaaa']
194
+ invalid_inputs = ['b', 'ab', 'ba', 'aab', 'baa', 'xyz']
195
+
196
+ valid_inputs.each do |input|
197
+ expect { DocumentationExample.parse_a_sequence(input) }.not_to raise_error
198
+ end
199
+
200
+ invalid_inputs.each do |input|
201
+ expect { DocumentationExample.parse_a_sequence(input) }.to raise_error(Parslet::ParseFailed)
202
+ end
203
+ end
204
+ end
205
+ end