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.
- checksums.yaml +7 -0
- data/HISTORY.txt +284 -0
- data/LICENSE +23 -0
- data/README.adoc +454 -0
- data/Rakefile +71 -0
- data/lib/parslet/accelerator/application.rb +62 -0
- data/lib/parslet/accelerator/engine.rb +112 -0
- data/lib/parslet/accelerator.rb +162 -0
- data/lib/parslet/atoms/alternative.rb +53 -0
- data/lib/parslet/atoms/base.rb +157 -0
- data/lib/parslet/atoms/can_flatten.rb +137 -0
- data/lib/parslet/atoms/capture.rb +38 -0
- data/lib/parslet/atoms/context.rb +103 -0
- data/lib/parslet/atoms/dsl.rb +112 -0
- data/lib/parslet/atoms/dynamic.rb +32 -0
- data/lib/parslet/atoms/entity.rb +45 -0
- data/lib/parslet/atoms/ignored.rb +26 -0
- data/lib/parslet/atoms/infix.rb +115 -0
- data/lib/parslet/atoms/lookahead.rb +52 -0
- data/lib/parslet/atoms/named.rb +32 -0
- data/lib/parslet/atoms/re.rb +41 -0
- data/lib/parslet/atoms/repetition.rb +87 -0
- data/lib/parslet/atoms/scope.rb +26 -0
- data/lib/parslet/atoms/sequence.rb +48 -0
- data/lib/parslet/atoms/str.rb +42 -0
- data/lib/parslet/atoms/visitor.rb +89 -0
- data/lib/parslet/atoms.rb +34 -0
- data/lib/parslet/cause.rb +101 -0
- data/lib/parslet/context.rb +21 -0
- data/lib/parslet/convenience.rb +33 -0
- data/lib/parslet/error_reporter/contextual.rb +120 -0
- data/lib/parslet/error_reporter/deepest.rb +100 -0
- data/lib/parslet/error_reporter/tree.rb +63 -0
- data/lib/parslet/error_reporter.rb +8 -0
- data/lib/parslet/export.rb +163 -0
- data/lib/parslet/expression/treetop.rb +92 -0
- data/lib/parslet/expression.rb +51 -0
- data/lib/parslet/graphviz.rb +97 -0
- data/lib/parslet/parser.rb +68 -0
- data/lib/parslet/pattern/binding.rb +49 -0
- data/lib/parslet/pattern.rb +113 -0
- data/lib/parslet/position.rb +21 -0
- data/lib/parslet/rig/rspec.rb +52 -0
- data/lib/parslet/scope.rb +42 -0
- data/lib/parslet/slice.rb +105 -0
- data/lib/parslet/source/line_cache.rb +99 -0
- data/lib/parslet/source.rb +96 -0
- data/lib/parslet/transform.rb +265 -0
- data/lib/parslet/version.rb +5 -0
- data/lib/parslet.rb +314 -0
- data/plurimath-parslet.gemspec +42 -0
- data/spec/acceptance/infix_parser_spec.rb +145 -0
- data/spec/acceptance/mixing_parsers_spec.rb +74 -0
- data/spec/acceptance/regression_spec.rb +329 -0
- data/spec/acceptance/repetition_and_maybe_spec.rb +44 -0
- data/spec/acceptance/unconsumed_input_spec.rb +21 -0
- data/spec/examples/boolean_algebra_spec.rb +257 -0
- data/spec/examples/calc_spec.rb +278 -0
- data/spec/examples/capture_spec.rb +137 -0
- data/spec/examples/comments_spec.rb +186 -0
- data/spec/examples/deepest_errors_spec.rb +420 -0
- data/spec/examples/documentation_spec.rb +205 -0
- data/spec/examples/email_parser_spec.rb +275 -0
- data/spec/examples/empty_spec.rb +37 -0
- data/spec/examples/erb_spec.rb +482 -0
- data/spec/examples/ip_address_spec.rb +153 -0
- data/spec/examples/json_spec.rb +413 -0
- data/spec/examples/local_spec.rb +302 -0
- data/spec/examples/mathn_spec.rb +151 -0
- data/spec/examples/minilisp_spec.rb +492 -0
- data/spec/examples/modularity_spec.rb +340 -0
- data/spec/examples/nested_errors_spec.rb +322 -0
- data/spec/examples/optimized_erb_spec.rb +299 -0
- data/spec/examples/parens_spec.rb +239 -0
- data/spec/examples/prec_calc_spec.rb +525 -0
- data/spec/examples/readme_spec.rb +228 -0
- data/spec/examples/scopes_spec.rb +187 -0
- data/spec/examples/seasons_spec.rb +196 -0
- data/spec/examples/sentence_spec.rb +119 -0
- data/spec/examples/simple_xml_spec.rb +250 -0
- data/spec/examples/string_parser_spec.rb +407 -0
- data/spec/fixtures/examples/boolean_algebra.rb +62 -0
- data/spec/fixtures/examples/calc.rb +86 -0
- data/spec/fixtures/examples/capture.rb +36 -0
- data/spec/fixtures/examples/comments.rb +22 -0
- data/spec/fixtures/examples/deepest_errors.rb +99 -0
- data/spec/fixtures/examples/documentation.rb +32 -0
- data/spec/fixtures/examples/email_parser.rb +42 -0
- data/spec/fixtures/examples/empty.rb +10 -0
- data/spec/fixtures/examples/erb.rb +39 -0
- data/spec/fixtures/examples/ip_address.rb +103 -0
- data/spec/fixtures/examples/json.rb +107 -0
- data/spec/fixtures/examples/local.rb +60 -0
- data/spec/fixtures/examples/mathn.rb +47 -0
- data/spec/fixtures/examples/minilisp.rb +75 -0
- data/spec/fixtures/examples/modularity.rb +60 -0
- data/spec/fixtures/examples/nested_errors.rb +95 -0
- data/spec/fixtures/examples/optimized_erb.rb +105 -0
- data/spec/fixtures/examples/parens.rb +25 -0
- data/spec/fixtures/examples/prec_calc.rb +71 -0
- data/spec/fixtures/examples/readme.rb +59 -0
- data/spec/fixtures/examples/scopes.rb +43 -0
- data/spec/fixtures/examples/seasons.rb +40 -0
- data/spec/fixtures/examples/sentence.rb +18 -0
- data/spec/fixtures/examples/simple_xml.rb +51 -0
- data/spec/fixtures/examples/string_parser.rb +77 -0
- data/spec/parslet/atom_results_spec.rb +39 -0
- data/spec/parslet/atoms/alternative_spec.rb +26 -0
- data/spec/parslet/atoms/base_spec.rb +127 -0
- data/spec/parslet/atoms/capture_spec.rb +21 -0
- data/spec/parslet/atoms/combinations_spec.rb +5 -0
- data/spec/parslet/atoms/dsl_spec.rb +7 -0
- data/spec/parslet/atoms/entity_spec.rb +77 -0
- data/spec/parslet/atoms/ignored_spec.rb +15 -0
- data/spec/parslet/atoms/infix_spec.rb +5 -0
- data/spec/parslet/atoms/lookahead_spec.rb +22 -0
- data/spec/parslet/atoms/named_spec.rb +4 -0
- data/spec/parslet/atoms/re_spec.rb +14 -0
- data/spec/parslet/atoms/repetition_spec.rb +24 -0
- data/spec/parslet/atoms/scope_spec.rb +26 -0
- data/spec/parslet/atoms/sequence_spec.rb +28 -0
- data/spec/parslet/atoms/str_spec.rb +15 -0
- data/spec/parslet/atoms/visitor_spec.rb +101 -0
- data/spec/parslet/atoms_spec.rb +488 -0
- data/spec/parslet/convenience_spec.rb +54 -0
- data/spec/parslet/error_reporter/contextual_spec.rb +118 -0
- data/spec/parslet/error_reporter/deepest_spec.rb +82 -0
- data/spec/parslet/error_reporter/tree_spec.rb +7 -0
- data/spec/parslet/export_spec.rb +40 -0
- data/spec/parslet/expression/treetop_spec.rb +74 -0
- data/spec/parslet/minilisp.citrus +29 -0
- data/spec/parslet/minilisp.tt +29 -0
- data/spec/parslet/parser_spec.rb +36 -0
- data/spec/parslet/parslet_spec.rb +38 -0
- data/spec/parslet/pattern_spec.rb +272 -0
- data/spec/parslet/position_spec.rb +14 -0
- data/spec/parslet/rig/rspec_spec.rb +54 -0
- data/spec/parslet/scope_spec.rb +45 -0
- data/spec/parslet/slice_spec.rb +186 -0
- data/spec/parslet/source/line_cache_spec.rb +74 -0
- data/spec/parslet/source_spec.rb +210 -0
- data/spec/parslet/transform/context_spec.rb +56 -0
- data/spec/parslet/transform_spec.rb +183 -0
- data/spec/spec_helper.rb +74 -0
- data/spec/support/opal.rb +8 -0
- data/spec/support/opal.rb.erb +14 -0
- data/spec/support/parslet_matchers.rb +96 -0
- 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
|