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,340 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fixtures/examples/modularity'
|
3
|
+
|
4
|
+
RSpec.describe 'Modularity Example' do
|
5
|
+
include ModularityExample
|
6
|
+
|
7
|
+
describe 'ModularityExample::ALanguage module' do
|
8
|
+
let(:test_class) do
|
9
|
+
Class.new do
|
10
|
+
include ModularityExample::ALanguage
|
11
|
+
end.new
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#a_language rule' do
|
15
|
+
it 'parses "aaa" correctly' do
|
16
|
+
result = test_class.a_language.parse('aaa')
|
17
|
+
expect(result).to be_a(Parslet::Slice)
|
18
|
+
expect(result.to_s).to eq('aaa')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'fails on incorrect input' do
|
22
|
+
expect { test_class.a_language.parse('bbb') }.to raise_error(Parslet::ParseFailed)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'fails on partial match' do
|
26
|
+
expect { test_class.a_language.parse('aa') }.to raise_error(Parslet::ParseFailed)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'fails on longer input' do
|
30
|
+
expect { test_class.a_language.parse('aaaa') }.to raise_error(Parslet::ParseFailed)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'ModularityExample::BLanguage parser' do
|
36
|
+
let(:parser) { ModularityExample::BLanguage.new }
|
37
|
+
|
38
|
+
describe '#blang rule' do
|
39
|
+
it 'parses "bbb" correctly' do
|
40
|
+
result = parser.blang.parse('bbb')
|
41
|
+
expect(result).to be_a(Parslet::Slice)
|
42
|
+
expect(result.to_s).to eq('bbb')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'fails on incorrect input' do
|
46
|
+
expect { parser.blang.parse('aaa') }.to raise_error(Parslet::ParseFailed)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'fails on partial match' do
|
50
|
+
expect { parser.blang.parse('bb') }.to raise_error(Parslet::ParseFailed)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'fails on longer input' do
|
54
|
+
expect { parser.blang.parse('bbbb') }.to raise_error(Parslet::ParseFailed)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#parse method (root)' do
|
59
|
+
it 'parses "bbb" correctly' do
|
60
|
+
result = parser.parse('bbb')
|
61
|
+
expect(result).to be_a(Parslet::Slice)
|
62
|
+
expect(result.to_s).to eq('bbb')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'fails on incorrect input' do
|
66
|
+
expect { parser.parse('ccc') }.to raise_error(Parslet::ParseFailed)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe 'ModularityExample.c_language' do
|
72
|
+
let(:c_lang) { ModularityExample.c_language }
|
73
|
+
|
74
|
+
it 'returns a parslet atom' do
|
75
|
+
expect(c_lang).to respond_to(:parse)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'parses "ccc" correctly' do
|
79
|
+
result = c_lang.parse('ccc')
|
80
|
+
expect(result).to be_a(Parslet::Slice)
|
81
|
+
expect(result.to_s).to eq('ccc')
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'fails on incorrect input' do
|
85
|
+
expect { c_lang.parse('aaa') }.to raise_error(Parslet::ParseFailed)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'fails on partial match' do
|
89
|
+
expect { c_lang.parse('cc') }.to raise_error(Parslet::ParseFailed)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'fails on longer input' do
|
93
|
+
expect { c_lang.parse('cccc') }.to raise_error(Parslet::ParseFailed)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe 'ModularityExample::Language parser' do
|
98
|
+
let(:parser) { ModularityExample::Language.new(ModularityExample.c_language) }
|
99
|
+
|
100
|
+
describe 'initialization' do
|
101
|
+
it 'accepts a c_language parameter' do
|
102
|
+
expect { ModularityExample::Language.new(ModularityExample.c_language) }.not_to raise_error
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'includes ALanguage module' do
|
106
|
+
expect(parser).to respond_to(:a_language)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#root rule' do
|
111
|
+
it 'parses a-language syntax "a(aaa)"' do
|
112
|
+
result = parser.root.parse('a(aaa)')
|
113
|
+
expect(result).to be_a(Parslet::Slice)
|
114
|
+
expect(result.to_s).to eq('a(aaa)')
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'parses a-language syntax with space "a(aaa) "' do
|
118
|
+
result = parser.root.parse('a(aaa) ')
|
119
|
+
expect(result).to be_a(Parslet::Slice)
|
120
|
+
expect(result.to_s).to eq('a(aaa) ')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'parses b-language syntax "b(bbb)"' do
|
124
|
+
result = parser.root.parse('b(bbb)')
|
125
|
+
expect(result).to be_a(Parslet::Slice)
|
126
|
+
expect(result.to_s).to eq('b(bbb)')
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'parses b-language syntax with space "b(bbb) "' do
|
130
|
+
result = parser.root.parse('b(bbb) ')
|
131
|
+
expect(result).to be_a(Parslet::Slice)
|
132
|
+
expect(result.to_s).to eq('b(bbb) ')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'parses c-language syntax "c(ccc)"' do
|
136
|
+
result = parser.root.parse('c(ccc)')
|
137
|
+
expect(result).to be_a(Parslet::Slice)
|
138
|
+
expect(result.to_s).to eq('c(ccc)')
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'parses c-language syntax with space "c(ccc) "' do
|
142
|
+
result = parser.root.parse('c(ccc) ')
|
143
|
+
expect(result).to be_a(Parslet::Slice)
|
144
|
+
expect(result.to_s).to eq('c(ccc) ')
|
145
|
+
end
|
146
|
+
|
147
|
+
it 'fails on malformed a-language syntax' do
|
148
|
+
expect { parser.root.parse('a(bbb)') }.to raise_error(Parslet::ParseFailed)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'fails on malformed b-language syntax' do
|
152
|
+
expect { parser.root.parse('b(aaa)') }.to raise_error(Parslet::ParseFailed)
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'fails on malformed c-language syntax' do
|
156
|
+
expect { parser.root.parse('c(aaa)') }.to raise_error(Parslet::ParseFailed)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'fails on unknown language prefix' do
|
160
|
+
expect { parser.root.parse('d(ddd)') }.to raise_error(Parslet::ParseFailed)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe '#parse method (root)' do
|
165
|
+
it 'parses a-language correctly' do
|
166
|
+
result = parser.parse('a(aaa)')
|
167
|
+
expect(result).to be_a(Parslet::Slice)
|
168
|
+
expect(result.to_s).to eq('a(aaa)')
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'parses b-language correctly' do
|
172
|
+
result = parser.parse('b(bbb)')
|
173
|
+
expect(result).to be_a(Parslet::Slice)
|
174
|
+
expect(result.to_s).to eq('b(bbb)')
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'parses c-language correctly' do
|
178
|
+
result = parser.parse('c(ccc)')
|
179
|
+
expect(result).to be_a(Parslet::Slice)
|
180
|
+
expect(result.to_s).to eq('c(ccc)')
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe '#space rule' do
|
185
|
+
it 'parses single space' do
|
186
|
+
result = parser.space.parse(' ')
|
187
|
+
expect(result).to be_a(Parslet::Slice)
|
188
|
+
expect(result.to_s).to eq(' ')
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'parses empty string (maybe)' do
|
192
|
+
result = parser.space.parse('')
|
193
|
+
expect(result.to_s).to eq('')
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'fails on multiple spaces' do
|
197
|
+
expect { parser.space.parse(' ') }.to raise_error(Parslet::ParseFailed)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe 'module helper methods' do
|
203
|
+
describe '.parse_a_language' do
|
204
|
+
it 'parses default a-language input' do
|
205
|
+
result = ModularityExample.parse_a_language
|
206
|
+
expect(result).to be_a(Parslet::Slice)
|
207
|
+
expect(result.to_s).to eq('a(aaa)')
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'parses custom a-language input' do
|
211
|
+
result = ModularityExample.parse_a_language('a(aaa) ')
|
212
|
+
expect(result).to be_a(Parslet::Slice)
|
213
|
+
expect(result.to_s).to eq('a(aaa) ')
|
214
|
+
end
|
215
|
+
|
216
|
+
it 'fails on invalid input' do
|
217
|
+
expect { ModularityExample.parse_a_language('invalid') }.to raise_error(Parslet::ParseFailed)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe '.parse_b_language' do
|
222
|
+
it 'parses default b-language input' do
|
223
|
+
result = ModularityExample.parse_b_language
|
224
|
+
expect(result).to be_a(Parslet::Slice)
|
225
|
+
expect(result.to_s).to eq('b(bbb)')
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'parses custom b-language input' do
|
229
|
+
result = ModularityExample.parse_b_language('b(bbb) ')
|
230
|
+
expect(result).to be_a(Parslet::Slice)
|
231
|
+
expect(result.to_s).to eq('b(bbb) ')
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'fails on invalid input' do
|
235
|
+
expect { ModularityExample.parse_b_language('invalid') }.to raise_error(Parslet::ParseFailed)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
describe '.parse_c_language' do
|
240
|
+
it 'parses default c-language input' do
|
241
|
+
result = ModularityExample.parse_c_language
|
242
|
+
expect(result).to be_a(Parslet::Slice)
|
243
|
+
expect(result.to_s).to eq('c(ccc)')
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'parses custom c-language input' do
|
247
|
+
result = ModularityExample.parse_c_language('c(ccc) ')
|
248
|
+
expect(result).to be_a(Parslet::Slice)
|
249
|
+
expect(result.to_s).to eq('c(ccc) ')
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'fails on invalid input' do
|
253
|
+
expect { ModularityExample.parse_c_language('invalid') }.to raise_error(Parslet::ParseFailed)
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
describe '.create_parser' do
|
258
|
+
it 'returns a Language parser instance' do
|
259
|
+
parser = ModularityExample.create_parser
|
260
|
+
expect(parser).to be_a(ModularityExample::Language)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'creates a functional parser' do
|
264
|
+
parser = ModularityExample.create_parser
|
265
|
+
result = parser.parse('a(aaa)')
|
266
|
+
expect(result.to_s).to eq('a(aaa)')
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
describe '.c_language' do
|
271
|
+
it 'returns a parslet atom' do
|
272
|
+
c_lang = ModularityExample.c_language
|
273
|
+
expect(c_lang).to respond_to(:parse)
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'can be used to create parsers' do
|
277
|
+
parser = ModularityExample::Language.new(ModularityExample.c_language)
|
278
|
+
result = parser.parse('c(ccc)')
|
279
|
+
expect(result.to_s).to eq('c(ccc)')
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
describe 'modular design demonstration' do
|
285
|
+
it 'shows how to mix parslet rules into classes' do
|
286
|
+
# ALanguage module demonstrates mixing rules into classes
|
287
|
+
test_class = Class.new { include ModularityExample::ALanguage }.new
|
288
|
+
expect(test_class).to respond_to(:a_language)
|
289
|
+
expect(test_class.a_language.parse('aaa').to_s).to eq('aaa')
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'shows how to use parsers as atoms' do
|
293
|
+
# BLanguage parser used as an atom in Language parser
|
294
|
+
parser = ModularityExample.create_parser
|
295
|
+
result = parser.parse('b(bbb)')
|
296
|
+
expect(result.to_s).to eq('b(bbb)')
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'shows how to pass parslet atoms around' do
|
300
|
+
# c_language atom passed to Language constructor
|
301
|
+
c_atom = ModularityExample.c_language
|
302
|
+
parser = ModularityExample::Language.new(c_atom)
|
303
|
+
result = parser.parse('c(ccc)')
|
304
|
+
expect(result.to_s).to eq('c(ccc)')
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'demonstrates all three modular approaches working together' do
|
308
|
+
parser = ModularityExample.create_parser
|
309
|
+
|
310
|
+
# All three approaches should work in the same parser
|
311
|
+
expect(parser.parse('a(aaa)').to_s).to eq('a(aaa)')
|
312
|
+
expect(parser.parse('b(bbb)').to_s).to eq('b(bbb)')
|
313
|
+
expect(parser.parse('c(ccc)').to_s).to eq('c(ccc)')
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
describe 'error handling' do
|
318
|
+
let(:parser) { ModularityExample.create_parser }
|
319
|
+
|
320
|
+
it 'provides meaningful error messages for malformed input' do
|
321
|
+
begin
|
322
|
+
parser.parse('invalid')
|
323
|
+
fail 'Expected ParseFailed to be raised'
|
324
|
+
rescue Parslet::ParseFailed => e
|
325
|
+
expect(e.message).to include('Expected one of')
|
326
|
+
expect(e.message).to include('at line 1 char 1')
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'handles empty input' do
|
331
|
+
expect { parser.parse('') }.to raise_error(Parslet::ParseFailed)
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'handles partial matches' do
|
335
|
+
expect { parser.parse('a(aa)') }.to raise_error(Parslet::ParseFailed)
|
336
|
+
expect { parser.parse('b(bb)') }.to raise_error(Parslet::ParseFailed)
|
337
|
+
expect { parser.parse('c(cc)') }.to raise_error(Parslet::ParseFailed)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
@@ -0,0 +1,322 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fixtures/examples/nested_errors'
|
3
|
+
|
4
|
+
RSpec.describe 'Nested Errors Example' do
|
5
|
+
include NestedErrorsExample
|
6
|
+
|
7
|
+
describe 'NestedErrorsExample::Parser' do
|
8
|
+
let(:parser) { NestedErrorsExample::Parser.new }
|
9
|
+
|
10
|
+
describe 'basic parsing components' do
|
11
|
+
describe '#space' do
|
12
|
+
it 'parses single space' do
|
13
|
+
result = parser.space.parse(' ')
|
14
|
+
expect(result).to be_a(Parslet::Slice)
|
15
|
+
expect(result.to_s).to eq(' ')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'parses multiple spaces' do
|
19
|
+
result = parser.space.parse(' ')
|
20
|
+
expect(result).to be_a(Parslet::Slice)
|
21
|
+
expect(result.to_s).to eq(' ')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'parses tabs' do
|
25
|
+
result = parser.space.parse("\t")
|
26
|
+
expect(result).to be_a(Parslet::Slice)
|
27
|
+
expect(result.to_s).to eq("\t")
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'fails on empty string' do
|
31
|
+
expect { parser.space.parse('') }.to raise_error(Parslet::ParseFailed)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#space?' do
|
36
|
+
it 'parses optional space' do
|
37
|
+
result = parser.space?.parse(' ')
|
38
|
+
expect(result).to be_a(Parslet::Slice)
|
39
|
+
expect(result.to_s).to eq(' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'parses empty string' do
|
43
|
+
result = parser.space?.parse('')
|
44
|
+
expect(result).to eq('')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#newline' do
|
49
|
+
it 'parses line feed' do
|
50
|
+
result = parser.newline.parse("\n")
|
51
|
+
expect(result).to be_a(Parslet::Slice)
|
52
|
+
expect(result.to_s).to eq("\n")
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'parses carriage return' do
|
56
|
+
result = parser.newline.parse("\r")
|
57
|
+
expect(result).to be_a(Parslet::Slice)
|
58
|
+
expect(result.to_s).to eq("\r")
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'fails on other characters' do
|
62
|
+
expect { parser.newline.parse('a') }.to raise_error(Parslet::ParseFailed)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#comment' do
|
67
|
+
it 'parses simple comment' do
|
68
|
+
result = parser.comment.parse('# this is a comment')
|
69
|
+
expect(result).to be_a(Parslet::Slice)
|
70
|
+
expect(result.to_s).to eq('# this is a comment')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'parses empty comment' do
|
74
|
+
result = parser.comment.parse('#')
|
75
|
+
expect(result).to be_a(Parslet::Slice)
|
76
|
+
expect(result.to_s).to eq('#')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'stops at newline' do
|
80
|
+
result = parser.comment.parse('# comment')
|
81
|
+
expect(result.to_s).to eq('# comment')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#identifier' do
|
86
|
+
it 'parses simple identifier' do
|
87
|
+
result = parser.identifier.parse('test')
|
88
|
+
expect(result).to be_a(Parslet::Slice)
|
89
|
+
expect(result.to_s).to eq('test')
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'parses identifier with numbers' do
|
93
|
+
result = parser.identifier.parse('test123')
|
94
|
+
expect(result).to be_a(Parslet::Slice)
|
95
|
+
expect(result.to_s).to eq('test123')
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'parses identifier with underscores' do
|
99
|
+
result = parser.identifier.parse('test_name')
|
100
|
+
expect(result).to be_a(Parslet::Slice)
|
101
|
+
expect(result.to_s).to eq('test_name')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'fails on empty string' do
|
105
|
+
expect { parser.identifier.parse('') }.to raise_error(Parslet::ParseFailed)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'resource statement components' do
|
111
|
+
describe '#reference' do
|
112
|
+
it 'parses single @ reference' do
|
113
|
+
result = parser.reference.parse('@resource')
|
114
|
+
expect(result).to eq({ reference: '@resource' })
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'parses double @ reference' do
|
118
|
+
result = parser.reference.parse('@@resource')
|
119
|
+
expect(result).to eq({ reference: '@@resource' })
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'fails without @' do
|
123
|
+
expect { parser.reference.parse('resource') }.to raise_error(Parslet::ParseFailed)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#res_action_or_link' do
|
128
|
+
it 'parses method call without question mark' do
|
129
|
+
result = parser.res_action_or_link.parse('.method()')
|
130
|
+
expect(result).to eq({ dot: '.', name: 'method' })
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'parses method call with question mark' do
|
134
|
+
result = parser.res_action_or_link.parse('.method?()')
|
135
|
+
expect(result).to eq({ dot: '.', name: 'method?' })
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#res_actions' do
|
140
|
+
it 'parses reference only' do
|
141
|
+
result = parser.res_actions.parse('@res')
|
142
|
+
expect(result).to eq({
|
143
|
+
resources: { reference: '@res' },
|
144
|
+
res_actions: []
|
145
|
+
})
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'parses reference with single action' do
|
149
|
+
result = parser.res_actions.parse('@res.action()')
|
150
|
+
expect(result[:resources][:reference].to_s).to eq('@res')
|
151
|
+
expect(result[:res_actions]).to be_an(Array)
|
152
|
+
expect(result[:res_actions].length).to eq(1)
|
153
|
+
expect(result[:res_actions][0][:res_action][:dot].to_s).to eq('.')
|
154
|
+
expect(result[:res_actions][0][:res_action][:name].to_s).to eq('action')
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'parses reference with multiple actions' do
|
158
|
+
result = parser.res_actions.parse('@res.action1().action2()')
|
159
|
+
expect(result[:resources][:reference].to_s).to eq('@res')
|
160
|
+
expect(result[:res_actions]).to be_an(Array)
|
161
|
+
expect(result[:res_actions].length).to eq(2)
|
162
|
+
expect(result[:res_actions][0][:res_action][:name].to_s).to eq('action1')
|
163
|
+
expect(result[:res_actions][1][:res_action][:name].to_s).to eq('action2')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#res_statement' do
|
168
|
+
it 'parses simple resource without actions or field' do
|
169
|
+
result = parser.res_statement.parse('@res')
|
170
|
+
expect(result[:resources][:reference].to_s).to eq('@res')
|
171
|
+
expect(result[:res_actions]).to eq([])
|
172
|
+
expect(result[:res_field]).to be_nil
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'parses resource statement without field' do
|
176
|
+
result = parser.res_statement.parse('@res.action()')
|
177
|
+
expect(result[:resources][:reference].to_s).to eq('@res')
|
178
|
+
expect(result[:res_actions]).to be_an(Array)
|
179
|
+
expect(result[:res_actions].length).to eq(1)
|
180
|
+
expect(result[:res_field]).to be_nil
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'parses resource statement with field' do
|
184
|
+
result = parser.res_statement.parse('@res.action():field')
|
185
|
+
expect(result[:resources][:reference].to_s).to eq('@res')
|
186
|
+
expect(result[:res_actions]).to be_an(Array)
|
187
|
+
expect(result[:res_actions].length).to eq(1)
|
188
|
+
expect(result[:res_field][:name].to_s).to eq('field')
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe 'block parsing' do
|
194
|
+
describe '#define_block' do
|
195
|
+
it 'parses simple define block with proper formatting' do
|
196
|
+
input = "define test()\n @res.action()\nend"
|
197
|
+
result = parser.define_block.parse(input)
|
198
|
+
expect(result[:define]).to eq('define')
|
199
|
+
expect(result[:name]).to eq('test')
|
200
|
+
expect(result[:body]).to be_an(Array)
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'fails on malformed define block (demonstrating error reporting)' do
|
204
|
+
input = "define test()\n @res"
|
205
|
+
expect { parser.define_block.parse(input) }.to raise_error(Parslet::ParseFailed)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe '#begin_block' do
|
210
|
+
it 'parses simple begin block with proper formatting' do
|
211
|
+
input = "begin\n @res.action()\nend"
|
212
|
+
result = parser.begin_block.parse(input)
|
213
|
+
expect(result[:begin]).to eq('begin')
|
214
|
+
expect(result[:body]).to be_an(Array)
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'parses concurrent begin block with proper formatting' do
|
218
|
+
input = "concurrent begin\n @res.action()\nend"
|
219
|
+
result = parser.begin_block.parse(input)
|
220
|
+
expect(result[:pre][:type]).to eq('concurrent')
|
221
|
+
expect(result[:begin]).to eq('begin')
|
222
|
+
expect(result[:body]).to be_an(Array)
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'fails on malformed begin block (demonstrating error reporting)' do
|
226
|
+
input = "begin\n @res"
|
227
|
+
expect { parser.begin_block.parse(input) }.to raise_error(Parslet::ParseFailed)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe 'root parser (radix)' do
|
233
|
+
it 'parses valid syntax correctly' do
|
234
|
+
input = "define test()\n @res.action()\nend"
|
235
|
+
result = parser.parse(input)
|
236
|
+
expect(result).to be_a(Hash)
|
237
|
+
expect(result[:define]).to eq('define')
|
238
|
+
expect(result[:name]).to eq('test')
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'fails to parse the first example (demonstrating error reporting)' do
|
242
|
+
input = "define f()\n @res.name\nend"
|
243
|
+
expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'fails to parse the second example (demonstrating error reporting)' do
|
247
|
+
input = "define f()\n begin\n @res.name\n end\nend"
|
248
|
+
expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe 'error handling' do
|
253
|
+
it 'handles incomplete define blocks' do
|
254
|
+
input = "define test()\n @res"
|
255
|
+
expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'handles incomplete begin blocks' do
|
259
|
+
input = "begin\n @res"
|
260
|
+
expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'provides meaningful error messages for malformed input' do
|
264
|
+
input = "invalid syntax here"
|
265
|
+
expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
describe 'nested error reporting' do
|
270
|
+
it 'demonstrates the purpose of the example - showing nested errors' do
|
271
|
+
input = "define f()\n @res.name\nend"
|
272
|
+
|
273
|
+
begin
|
274
|
+
parser.parse(input)
|
275
|
+
fail "Expected ParseFailed to be raised"
|
276
|
+
rescue Parslet::ParseFailed => e
|
277
|
+
# The error should contain information about the nested structure
|
278
|
+
expect(e.message).to include('Failed to match')
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
it 'works with parse_with_debug and nested reporter on valid input' do
|
283
|
+
input = "define test()\n @res.action()\nend"
|
284
|
+
|
285
|
+
expect { parser.parse_with_debug(input) }.not_to raise_error
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'demonstrates nested error reporting with malformed input' do
|
289
|
+
input = "define f()\n @res.name\nend"
|
290
|
+
|
291
|
+
# parse_with_debug may not always raise an error, so let's test the regular parse method
|
292
|
+
expect { parser.parse(input) }.to raise_error(Parslet::ParseFailed)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
describe 'prettify helper function' do
|
298
|
+
it 'formats strings with line numbers' do
|
299
|
+
input = "line 1\nline 2\nline 3"
|
300
|
+
result = NestedErrorsExample.prettify(input)
|
301
|
+
|
302
|
+
expect(result).to include('01 line 1')
|
303
|
+
expect(result).to include('02 line 2')
|
304
|
+
expect(result).to include('03 line 3')
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'handles single line input' do
|
308
|
+
input = "single line"
|
309
|
+
result = NestedErrorsExample.prettify(input)
|
310
|
+
|
311
|
+
expect(result).to include('01 single line')
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'handles empty input' do
|
315
|
+
input = ""
|
316
|
+
result = NestedErrorsExample.prettify(input)
|
317
|
+
|
318
|
+
expect(result).to include('10')
|
319
|
+
expect(result).to include('20')
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|