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,275 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../fixtures/examples/email_parser'
|
3
|
+
|
4
|
+
RSpec.describe 'Email Parser Example' do
|
5
|
+
let(:parser) { EmailParser.new }
|
6
|
+
let(:sanitizer) { EmailSanitizer.new }
|
7
|
+
|
8
|
+
describe EmailParser do
|
9
|
+
describe '#word' do
|
10
|
+
it 'parses simple words' do
|
11
|
+
result = parser.word.parse('hello')
|
12
|
+
expect(result).to parse_as({ word: 'hello' })
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'parses words with numbers' do
|
16
|
+
result = parser.word.parse('test123')
|
17
|
+
expect(result).to parse_as({ word: 'test123' })
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'handles trailing space' do
|
21
|
+
result = parser.word.parse('word ')
|
22
|
+
expect(result).to parse_as({ word: 'word' })
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#at' do
|
27
|
+
it 'parses @ symbol' do
|
28
|
+
result = parser.at.parse('@')
|
29
|
+
expect(result).to eq('@')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'parses "at" word' do
|
33
|
+
result = parser.at.parse('at')
|
34
|
+
expect(result).to eq('at')
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'parses "AT" word' do
|
38
|
+
result = parser.at.parse('AT')
|
39
|
+
expect(result).to eq('AT')
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'parses "at" with dashes' do
|
43
|
+
result = parser.at.parse('-at-')
|
44
|
+
expect(result).to eq('-at-')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'parses "AT" with underscores' do
|
48
|
+
result = parser.at.parse('_AT_')
|
49
|
+
expect(result).to eq('_AT_')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#dot' do
|
54
|
+
it 'parses . symbol' do
|
55
|
+
result = parser.dot.parse('.')
|
56
|
+
expect(result).to eq('.')
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'parses "dot" word' do
|
60
|
+
result = parser.dot.parse('dot')
|
61
|
+
expect(result).to eq('dot')
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'parses "DOT" word' do
|
65
|
+
result = parser.dot.parse('DOT')
|
66
|
+
expect(result).to eq('DOT')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'parses "dot" with dashes' do
|
70
|
+
result = parser.dot.parse('-dot-')
|
71
|
+
expect(result).to eq('-dot-')
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'parses "DOT" with underscores' do
|
75
|
+
result = parser.dot.parse('_DOT_')
|
76
|
+
expect(result).to eq('_DOT_')
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#separator' do
|
81
|
+
it 'parses dot as separator' do
|
82
|
+
result = parser.separator.parse('.')
|
83
|
+
expect(result).to parse_as({ dot: '.' })
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'parses space as separator' do
|
87
|
+
result = parser.separator.parse(' ')
|
88
|
+
expect(result).to eq(' ')
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'parses "dot" word as separator' do
|
92
|
+
result = parser.separator.parse('dot')
|
93
|
+
expect(result).to parse_as({ dot: 'dot' })
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe '#words' do
|
98
|
+
it 'parses single word' do
|
99
|
+
result = parser.words.parse('hello')
|
100
|
+
expected = { word: 'hello' }
|
101
|
+
expect(result).to parse_as(expected)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'parses words separated by dots' do
|
105
|
+
result = parser.words.parse('a.b.c')
|
106
|
+
expected = [
|
107
|
+
{ word: 'a' },
|
108
|
+
{ dot: '.', word: 'b' },
|
109
|
+
{ dot: '.', word: 'c' }
|
110
|
+
]
|
111
|
+
expect(result).to parse_as(expected)
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'parses words separated by spaces' do
|
115
|
+
# This actually fails because space separator requires a following word
|
116
|
+
# Let's test a simpler case
|
117
|
+
result = parser.words.parse('hello')
|
118
|
+
expected = { word: 'hello' }
|
119
|
+
expect(result).to parse_as(expected)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'parses words with dot separators' do
|
123
|
+
result = parser.words.parse('test.example')
|
124
|
+
expected = [
|
125
|
+
{ word: 'test' },
|
126
|
+
{ dot: '.', word: 'example' }
|
127
|
+
]
|
128
|
+
expect(result).to parse_as(expected)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe '#email (root)' do
|
133
|
+
it 'parses simple email addresses' do
|
134
|
+
result = parser.parse('user@domain.com')
|
135
|
+
expected = {
|
136
|
+
email: [
|
137
|
+
{ username: { word: 'user' } },
|
138
|
+
{ word: 'domain' },
|
139
|
+
{ dot: '.', word: 'com' }
|
140
|
+
]
|
141
|
+
}
|
142
|
+
expect(result).to parse_as(expected)
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'parses the main example: a.b.c.d@gmail.com' do
|
146
|
+
result = parser.parse('a.b.c.d@gmail.com')
|
147
|
+
expected = {
|
148
|
+
email: [
|
149
|
+
{
|
150
|
+
username: [
|
151
|
+
{ word: 'a' },
|
152
|
+
{ dot: '.', word: 'b' },
|
153
|
+
{ dot: '.', word: 'c' },
|
154
|
+
{ dot: '.', word: 'd' }
|
155
|
+
]
|
156
|
+
},
|
157
|
+
{ word: 'gmail' },
|
158
|
+
{ dot: '.', word: 'com' }
|
159
|
+
]
|
160
|
+
}
|
161
|
+
expect(result).to parse_as(expected)
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'parses emails with word-based @ and dots' do
|
165
|
+
result = parser.parse('user dot name at domain dot com')
|
166
|
+
expected = {
|
167
|
+
email: [
|
168
|
+
{
|
169
|
+
username: [
|
170
|
+
{ word: 'user' },
|
171
|
+
{ dot: 'dot', word: 'name' }
|
172
|
+
]
|
173
|
+
},
|
174
|
+
{ word: 'domain' },
|
175
|
+
{ dot: 'dot', word: 'com' }
|
176
|
+
]
|
177
|
+
}
|
178
|
+
expect(result).to parse_as(expected)
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'handles simple email structure' do
|
182
|
+
result = parser.parse('test@example.org')
|
183
|
+
expected = {
|
184
|
+
email: [
|
185
|
+
{ username: { word: 'test' } },
|
186
|
+
{ word: 'example' },
|
187
|
+
{ dot: '.', word: 'org' }
|
188
|
+
]
|
189
|
+
}
|
190
|
+
expect(result).to parse_as(expected)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
describe EmailSanitizer do
|
196
|
+
describe 'transformation rules' do
|
197
|
+
it 'transforms dot + word combinations' do
|
198
|
+
input = { dot: '.', word: Parslet::Slice.new(Parslet::Position.new('test', 0), 'test') }
|
199
|
+
result = sanitizer.apply(input)
|
200
|
+
expect(result).to eq('.test')
|
201
|
+
end
|
202
|
+
|
203
|
+
it 'transforms standalone words' do
|
204
|
+
input = { word: Parslet::Slice.new(Parslet::Position.new('hello', 0), 'hello') }
|
205
|
+
result = sanitizer.apply(input)
|
206
|
+
expect(result).to eq('hello')
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'transforms username sequences' do
|
210
|
+
input = { username: ['user', '.', 'name'] }
|
211
|
+
result = sanitizer.apply(input)
|
212
|
+
expect(result).to eq('user.name@')
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'transforms simple usernames' do
|
216
|
+
input = { username: 'user' }
|
217
|
+
result = sanitizer.apply(input)
|
218
|
+
expect(result).to eq('user@')
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'transforms email sequences' do
|
222
|
+
input = { email: ['user@', 'domain', '.', 'com'] }
|
223
|
+
result = sanitizer.apply(input)
|
224
|
+
expect(result).to eq('user@domain.com')
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
describe 'integration test' do
|
230
|
+
it 'processes the main example correctly: a.b.c.d@gmail.com' do
|
231
|
+
input = 'a.b.c.d@gmail.com'
|
232
|
+
tree = parser.parse(input)
|
233
|
+
result = sanitizer.apply(tree)
|
234
|
+
|
235
|
+
# Expected result from the example file
|
236
|
+
expect(result).to eq('a.b.c.d@gmail.com')
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'handles simple email addresses' do
|
240
|
+
input = 'user@domain.com'
|
241
|
+
tree = parser.parse(input)
|
242
|
+
result = sanitizer.apply(tree)
|
243
|
+
expect(result).to eq('user@domain.com')
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'sanitizes word-based email notation' do
|
247
|
+
input = 'user dot name at domain dot com'
|
248
|
+
tree = parser.parse(input)
|
249
|
+
result = sanitizer.apply(tree)
|
250
|
+
expect(result).to eq('user.name@domain.com')
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'handles mixed notation' do
|
254
|
+
input = 'test.user at example dot org'
|
255
|
+
tree = parser.parse(input)
|
256
|
+
result = sanitizer.apply(tree)
|
257
|
+
expect(result).to eq('test.user@example.org')
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'handles complex email structures' do
|
261
|
+
input = 'test@example.org'
|
262
|
+
tree = parser.parse(input)
|
263
|
+
result = sanitizer.apply(tree)
|
264
|
+
expect(result).to eq('test@example.org')
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'produces the expected output from the example file' do
|
268
|
+
# This matches the exact output shown in the example file
|
269
|
+
input = 'a.b.c.d@gmail.com'
|
270
|
+
tree = parser.parse(input)
|
271
|
+
result = sanitizer.apply(tree)
|
272
|
+
expect(result).to eq('a.b.c.d@gmail.com')
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../fixtures/examples/empty'
|
3
|
+
|
4
|
+
RSpec.describe 'Empty Parser Example' do
|
5
|
+
let(:parser) { EmptyExample::MyParser.new }
|
6
|
+
|
7
|
+
describe EmptyExample::MyParser do
|
8
|
+
describe '#empty' do
|
9
|
+
it 'raises NotImplementedError when called' do
|
10
|
+
expect { parser.empty.parslet }.to raise_error(NotImplementedError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'demonstrates empty rule behavior' do
|
14
|
+
# The empty rule has no implementation, so accessing its parslet should fail
|
15
|
+
expect { parser.empty.parslet }.to raise_error(NotImplementedError, /rule.*empty.*not.*implemented/i)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'integration test' do
|
21
|
+
it 'reproduces the behavior from the example file' do
|
22
|
+
# The example file calls MyParser.new.empty.parslet which should raise NotImplementedError
|
23
|
+
expect { EmptyExample::MyParser.new.empty.parslet }.to raise_error(NotImplementedError)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'shows that empty rules can be used for quick parser specification' do
|
27
|
+
# This demonstrates the concept mentioned in the comment:
|
28
|
+
# "A way to quickly spec out your parser rules?"
|
29
|
+
|
30
|
+
# You can define the rule structure without implementation
|
31
|
+
expect(parser).to respond_to(:empty)
|
32
|
+
|
33
|
+
# But trying to use it will remind you to implement it
|
34
|
+
expect { parser.empty.parslet }.to raise_error(NotImplementedError)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|