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,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