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
data/README.adoc ADDED
@@ -0,0 +1,454 @@
1
+ = Plurimath Parslet
2
+
3
+ This is a fork of the original Parslet gem, which is a parser library for Ruby.
4
+
5
+ This differs from the original Parslet gem, such that it is Opal (JS based Ruby)
6
+ compatible.
7
+
8
+ == Introduction
9
+
10
+ Parslet makes developing complex parsers easy. It does so by:
11
+
12
+ * providing the best error reporting possible
13
+ * not generating reams of code for you to debug
14
+
15
+ Parslet takes the long way around to make your job easier. It allows for
16
+ incremental language construction. Often, you start out small, implementing
17
+ the atoms of your language first; _parslet_ takes pride in making this
18
+ possible.
19
+
20
+ Eager to try this out? Please see the associated web site:
21
+ https://kschiess.github.io/parslet/
22
+
23
+ == Synopsis
24
+
25
+ [source,ruby]
26
+ ----
27
+ require 'parslet'
28
+ include Parslet
29
+
30
+ # parslet parses strings
31
+ str('foo').
32
+ parse('foo') # => "foo"@0
33
+
34
+ # it matches character sets
35
+ match['abc'].parse('a') # => "a"@0
36
+ match['abc'].parse('b') # => "b"@0
37
+ match['abc'].parse('c') # => "c"@0
38
+
39
+ # and it annotates its output
40
+ str('foo').as(:important_bit).
41
+ parse('foo') # => {:important_bit=>"foo"@0}
42
+
43
+ # you can construct parsers with just a few lines
44
+ quote = str('"')
45
+ simple_string = quote >> (quote.absent? >> any).repeat >> quote
46
+
47
+ simple_string.
48
+ parse('"Simple Simple Simple"') # => "\"Simple Simple Simple\""@0
49
+
50
+ # or by making a fuss about it
51
+ class Smalltalk < Parslet::Parser
52
+ root :smalltalk
53
+
54
+ rule(:smalltalk) { statements }
55
+ rule(:statements) {
56
+ # insert smalltalk parser here (outside of the scope of this readme)
57
+ }
58
+ end
59
+
60
+ # and then
61
+ Smalltalk.new.parse('smalltalk')
62
+ ----
63
+
64
+ == Features
65
+
66
+ * Tools for every part of the parser chain
67
+ * Transformers generate Abstract Syntax Trees
68
+ * Accelerators transform parsers, making them quite a bit faster
69
+ * Pluggable error reporters
70
+ * Graphviz export for your parser
71
+ * Rspec testing support rig
72
+ * Simply Ruby, composable and hackable
73
+
74
+ == Compatibility
75
+
76
+ This library is intended to work with Ruby variants >= 2.7. It has been tested on:
77
+
78
+ * MRI Ruby 2.7+
79
+ * JRuby
80
+ * Opal (works as Ruby 3.2, with the limitation of `StringScanner#charpos` not being
81
+ available, which is used in some examples)
82
+
83
+ Please report as a bug if you encounter issues.
84
+
85
+ == Status
86
+
87
+ Production worthy.
88
+
89
+ == Installation
90
+
91
+ Add this line to your application's Gemfile:
92
+
93
+ [source,ruby]
94
+ ----
95
+ gem 'plurimath-parslet'
96
+ ----
97
+
98
+ And then execute:
99
+
100
+ [source,bash]
101
+ ----
102
+ $ bundle install
103
+ ----
104
+
105
+ Or install it yourself as:
106
+
107
+ [source,bash]
108
+ ----
109
+ $ gem install plurimath-parslet
110
+ ----
111
+
112
+ == Usage
113
+
114
+ [source,ruby]
115
+ ----
116
+ require 'parslet'
117
+ ----
118
+
119
+ The gem maintains the original `parslet` namespace for compatibility.
120
+
121
+ == Examples
122
+
123
+ The `examples/` directory contains 25 interactive examples that demonstrate various parsing scenarios and techniques. These examples are designed to help you learn parslet by showing real-world parsing problems and their solutions.
124
+
125
+ === How to run examples
126
+
127
+ Each example can be run directly from the command line:
128
+
129
+ [source,bash]
130
+ ----
131
+ $ ruby examples/run_boolean_algebra.rb
132
+ $ ruby examples/run_json.rb
133
+ $ ruby examples/run_calc.rb
134
+ ----
135
+
136
+ === Available examples
137
+
138
+ [cols="1,3,2", options="header"]
139
+ |===
140
+ | Example | Description | Key Features
141
+
142
+ | `run_boolean_algebra.rb`
143
+ | Boolean expression parser with operator precedence
144
+ | AND/OR operators, parentheses, DNF transformation
145
+
146
+ | `run_calc.rb`
147
+ | Basic calculator with arithmetic operations
148
+ | Operator precedence, expression evaluation
149
+
150
+ | `run_capture.rb`
151
+ | Named capture groups and result extraction
152
+ | Capture syntax, result processing
153
+
154
+ | `run_comments.rb`
155
+ | Comment parsing (single-line and multi-line)
156
+ | Comment syntax, nested structures
157
+
158
+ | `run_deepest_errors.rb`
159
+ | Advanced error reporting and debugging
160
+ | Error handling, parse failure analysis
161
+
162
+ | `run_documentation.rb`
163
+ | Markdown-style documentation parser
164
+ | Headers, lists, formatting, code blocks
165
+
166
+ | `run_email_parser.rb`
167
+ | Email address validation and parsing
168
+ | Email format validation, domain parsing
169
+
170
+ | `run_empty.rb`
171
+ | Empty rule behavior and edge cases
172
+ | Empty matches, optional content
173
+
174
+ | `run_erb.rb`
175
+ | ERB template parsing
176
+ | Template syntax, embedded Ruby code
177
+
178
+ | `run_ip_address.rb`
179
+ | IPv4 address parsing and validation
180
+ | IP format validation, octet parsing
181
+
182
+ | `run_json.rb`
183
+ | Complete JSON parser with all data types
184
+ | Objects, arrays, strings, numbers, booleans, null
185
+
186
+ | `run_local.rb`
187
+ | Local variable scoping demonstration
188
+ | Variable declarations, scope management
189
+
190
+ | `run_mathn.rb`
191
+ | Mathematical expression parsing
192
+ | Math operations, Ruby mathn compatibility
193
+
194
+ | `run_minilisp.rb`
195
+ | Minimal Lisp interpreter
196
+ | S-expressions, nested structures, symbols
197
+
198
+ | `run_modularity.rb`
199
+ | Modular parser design patterns
200
+ | Parser composition, reusable components
201
+
202
+ | `run_nested_errors.rb`
203
+ | Nested error handling strategies
204
+ | Error propagation, context preservation
205
+
206
+ | `run_optimized_erb.rb`
207
+ | Performance-optimized ERB parsing
208
+ | Greedy parsing, performance comparison
209
+
210
+ | `run_parens.rb`
211
+ | Parentheses matching and balancing
212
+ | Balanced expressions, nesting validation
213
+
214
+ | `run_prec_calc.rb`
215
+ | Calculator with full operator precedence
216
+ | Complex precedence rules, associativity
217
+
218
+ | `run_readme.rb`
219
+ | README-style documentation parsing
220
+ | Document structure, sections, formatting
221
+
222
+ | `run_scopes.rb`
223
+ | Variable scope handling in parsers
224
+ | Block scoping, variable shadowing
225
+
226
+ | `run_seasons.rb`
227
+ | Transform chains and data processing
228
+ | Multi-stage transformations, data flow
229
+
230
+ | `run_sentence.rb`
231
+ | Natural language sentence parsing
232
+ | Grammar rules, sentence structure
233
+
234
+ | `run_simple_xml.rb`
235
+ | Basic XML parsing
236
+ | Tags, attributes, nested elements
237
+
238
+ | `run_string_parser.rb`
239
+ | String literal parsing with escaping
240
+ | Quote handling, escape sequences
241
+ |===
242
+
243
+ === Example structure
244
+
245
+ Each example follows a consistent structure:
246
+
247
+ * Educational comments explaining the parsing problem
248
+ * Sample input data demonstrating various test cases
249
+ * Parser demonstration showing both successful parsing and error handling
250
+ * Output explanation describing what the parser supports and how it works
251
+
252
+ === Learning path
253
+
254
+ For beginners, we recommend starting with these examples in order:
255
+
256
+ . `run_simple_xml.rb` - Basic parsing concepts
257
+ . `run_calc.rb` - Operator precedence and evaluation
258
+ . `run_json.rb` - Complex data structures
259
+ . `run_boolean_algebra.rb` - Transformations and logic
260
+ . `run_minilisp.rb` - Advanced parsing techniques
261
+
262
+ == Development
263
+
264
+ After checking out the repo, run:
265
+
266
+ [source,bash]
267
+ ----
268
+ $ bundle install
269
+ ----
270
+
271
+ === Available rake tasks
272
+
273
+ ==== Testing
274
+
275
+ * `rake spec` - Run all tests (438 examples covering all functionality)
276
+ * `rake spec:unit` - Run unit tests only
277
+ * `rake spec:opal` - Run Opal (JavaScript) tests (437 examples)
278
+
279
+ ===== Running Opal tests
280
+
281
+ The Opal test suite runs the same specs as the Ruby test suite but in a
282
+ JavaScript environment via Node.js. This ensures parslet works correctly when
283
+ compiled to JavaScript with Opal.
284
+
285
+ To run all Opal tests:
286
+
287
+ [source,bash]
288
+ ----
289
+ $ bundle exec rake spec:opal
290
+ ----
291
+
292
+ The Opal specs are located in the `spec-opal/` directory and mirror the
293
+ structure of the main `spec/` directory.
294
+
295
+ NOTE: Some Opal tests may fail due to environment differences between Ruby and
296
+ JavaScript execution, but the core parsing functionality is fully supported.
297
+
298
+ ==== Benchmarking
299
+
300
+ * `rake benchmark` - Run quick benchmarks (alias for benchmark:quick)
301
+ * `rake benchmark:quick` - Run example-focused benchmarks only
302
+ * `rake benchmark:examples` - Run example-focused benchmarks
303
+ * `rake benchmark:all` - Run comprehensive benchmark suite (all categories)
304
+ * `rake benchmark:export` - Run benchmarks and export results to JSON/YAML files
305
+
306
+ ===== What gets benchmarked
307
+
308
+ The benchmark suite measures parsing performance across different scenarios:
309
+
310
+ **Basic Parsing Operations**
311
+
312
+ * `str('hello')` - Simple string matching performance
313
+ * `match('[a-z]').repeat(1)` - Character class matching with repetition
314
+ * Email-like pattern matching - Complex regex-style parsing (`user@example.com`)
315
+
316
+ **Calculator Parser** (from `example/calc.rb`)
317
+
318
+ * Simple expressions: `1+2`
319
+ * Medium complexity: `1+2*3-4/2`
320
+ * Complex expressions: `123*456+789-321/3*2+1`
321
+ * Full pipeline (parse + transform + evaluate)
322
+
323
+ **JSON Parser** (from `example/json.rb`)
324
+
325
+ * Simple objects: `{"key": "value"}`
326
+ * Arrays: `[1, 2, 3, 4, 5]`
327
+ * Complex nested structures with multiple data types
328
+ * Parse vs. transform performance comparison
329
+
330
+ **String Parsing**
331
+
332
+ * Simple quoted strings: `"hello world"`
333
+ * Long strings (1000+ characters)
334
+ * Escaped strings with backslash sequences: `"hello \"world\" with escapes"`
335
+
336
+ **Repetition Patterns**
337
+
338
+ * `repeat(1)` with varying input lengths (short/medium/long)
339
+ * Bounded repetition `repeat(3,6)`
340
+ * Optional repetition `repeat` (zero or more)
341
+ * Performance scaling with input size
342
+
343
+ **Transform Operations**
344
+
345
+ * Simple AST transformations (number/string conversion)
346
+ * Medium complexity (multiple rules, arrays)
347
+ * Complex nested transformations with multiple rule types
348
+
349
+ ===== Sample benchmark output
350
+
351
+ [example]
352
+ ====
353
+ [source]
354
+ ----
355
+ Plurimath Parslet Performance Benchmarks
356
+ ==================================================
357
+
358
+ Basic Parsing Operations
359
+ ------------------------------
360
+ ruby 3.3.2 (2024-05-30 revision e5a195edf6) [arm64-darwin23]
361
+ Warming up --------------------------------------
362
+ str('hello') 17.235k i/100ms
363
+ match('[a-z]').repeat(1)
364
+ 3.502k i/100ms
365
+ email-like pattern 2.780k i/100ms
366
+ Calculating -------------------------------------
367
+ str('hello') 174.636k (± 2.1%) i/s (5.73 μs/i)
368
+ match('[a-z]').repeat(1)
369
+ 35.182k (± 2.6%) i/s (28.42 μs/i)
370
+ email-like pattern 27.874k (± 8.5%) i/s (35.88 μs/i)
371
+
372
+ Comparison:
373
+ str('hello'): 174636.1 i/s
374
+ match('[a-z]').repeat(1): 35182.1 i/s - 4.96x slower
375
+ email-like pattern: 27873.8 i/s - 6.27x slower
376
+
377
+ Calculator Parser Benchmarks
378
+ ------------------------------
379
+ parse simple: '1+2' 18.791k (± 3.2%) i/s (53.22 μs/i)
380
+ parse medium: '1+2*3-4/2'
381
+ 8.871k (± 6.4%) i/s (112.73 μs/i)
382
+ parse complex: '123*456+789-321/3*2+1'
383
+ 5.872k (± 4.3%) i/s (170.30 μs/i)
384
+ full calc simple 7.516k (± 8.5%) i/s (133.06 μs/i)
385
+ full calc complex 3.018k (± 1.9%) i/s (331.34 μs/i)
386
+ ----
387
+ ====
388
+
389
+ ===== Benchmark results export
390
+
391
+ Results are exported to multiple formats for analysis:
392
+
393
+ * `benchmark/results.json` - Detailed benchmark data with iterations/second, standard deviation, and microseconds per iteration
394
+ * `benchmark/results.yaml` - YAML format results for easy reading
395
+ * `benchmark/summary.json` - Performance summary with fastest/slowest operations and insights
396
+ * `benchmark/summary.yaml` - YAML format summary
397
+
398
+ The exported data includes:
399
+
400
+ * Ruby version and platform information
401
+ * Parslet version and benchmark tool versions
402
+ * Detailed performance metrics for each test case
403
+ * Statistical analysis (standard deviation, error percentages)
404
+ * Performance comparisons and insights
405
+ * Identification of performance bottlenecks and optimization opportunities
406
+
407
+ ==== Building and distribution
408
+
409
+ * `rake build` - Build plurimath-parslet-3.0.0.gem into the pkg directory
410
+ * `rake build:checksum` - Generate SHA512 checksum of the gem
411
+ * `rake install` - Build and install gem into system gems
412
+ * `rake install:local` - Build and install gem without network access
413
+ * `rake release[remote]` - Create tag and push gem to rubygems.org
414
+
415
+ ==== Documentation
416
+
417
+ * `rake rdoc` - Build RDoc HTML files
418
+ * `rake rdoc:coverage` - Print RDoc coverage report
419
+ * `rake rerdoc` - Rebuild RDoc HTML files
420
+
421
+ ==== Maintenance
422
+
423
+ * `rake clean` - Remove temporary products
424
+ * `rake clobber` - Remove generated files
425
+ * `rake clobber_rdoc` - Remove RDoc HTML files
426
+ * `rake stat` - Print lines of code statistics
427
+
428
+ === Example coverage
429
+
430
+ All 25 examples in the `examples/` directory are covered by specs and tested automatically:
431
+
432
+ * boolean_algebra.rb, calc.rb, capture.rb, comments.rb, deepest_errors.rb
433
+ * documentation.rb, email_parser.rb, empty.rb, erb.rb, ip_address.rb
434
+ * json.rb, local.rb, mathn.rb, minilisp.rb, modularity.rb
435
+ * nested_errors.rb, optimized_erb.rb, parens.rb, prec_calc.rb, readme.rb
436
+ * scopes.rb, seasons.rb, sentence.rb, simple_xml.rb, string_parser.rb
437
+
438
+ == Contributing
439
+
440
+ . Fork it
441
+ . Create your feature branch (`git checkout -b my-new-feature`)
442
+ . Commit your changes (`git commit -am 'Add some feature'`)
443
+ . Push to the branch (`git push origin my-new-feature`)
444
+ . Create a new Pull Request
445
+
446
+ == License
447
+
448
+ The gem is available as open source under the terms of the MIT License.
449
+
450
+ == Copyright
451
+
452
+ (c) 2010-2018 Kaspar Schiess.
453
+
454
+ 2025 Augmented by Ribose Inc.
data/Rakefile ADDED
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rdoc/task'
6
+
7
+ begin
8
+ require 'opal/rspec/rake_task'
9
+ rescue LoadError, NoMethodError
10
+ # Opal not available or incompatible with current Ruby version
11
+ end
12
+
13
+ desc 'Run all tests'
14
+ RSpec::Core::RakeTask.new(:spec)
15
+
16
+ namespace :spec do
17
+ desc 'Run unit tests only'
18
+ RSpec::Core::RakeTask.new(:unit) do |task|
19
+ task.pattern = 'spec/parslet/**/*_spec.rb'
20
+ end
21
+
22
+ if defined?(Opal::RSpec::RakeTask)
23
+ desc 'Run Opal (JavaScript) tests'
24
+ Opal::RSpec::RakeTask.new(:opal) do |task|
25
+ task.append_path 'lib'
26
+ end
27
+ end
28
+ end
29
+
30
+ RDoc::Task.new do |rdoc|
31
+ rdoc.rdoc_dir = 'rdoc'
32
+ rdoc.title = 'Plurimath Parslet'
33
+ rdoc.options << '--line-numbers'
34
+ rdoc.rdoc_files.include('README.adoc')
35
+ rdoc.rdoc_files.include('lib/**/*.rb')
36
+ end
37
+
38
+ desc 'Print LOC statistics'
39
+ task :stat do
40
+ %w[lib spec example].each do |dir|
41
+ next unless Dir.exist?(dir)
42
+
43
+ loc = `find #{dir} -name "*.rb" | xargs wc -l | grep 'total'`.split.first.to_i
44
+ printf("%20s %d\n", dir, loc)
45
+ end
46
+ end
47
+
48
+ namespace :benchmark do
49
+ desc 'Run comprehensive benchmark suite'
50
+ task :all do
51
+ ruby 'benchmark/benchmark_suite.rb'
52
+ end
53
+
54
+ desc 'Run example-focused benchmarks'
55
+ task :examples do
56
+ ruby 'benchmark/example_benchmarks.rb'
57
+ end
58
+
59
+ desc 'Run benchmarks and export results to JSON/YAML'
60
+ task :export do
61
+ ruby 'benchmark/benchmark_runner.rb'
62
+ end
63
+
64
+ desc 'Run quick benchmark (examples only)'
65
+ task quick: :examples
66
+ end
67
+
68
+ desc 'Run quick benchmarks'
69
+ task benchmark: 'benchmark:quick'
70
+
71
+ task default: :spec
@@ -0,0 +1,62 @@
1
+
2
+ # @api private
3
+ module Parslet::Accelerator
4
+ class Application
5
+ def initialize atom, rules
6
+ @atom = atom
7
+ @rules = rules
8
+ end
9
+
10
+ def call
11
+ @atom.accept(self)
12
+ end
13
+
14
+ def visit_parser(root)
15
+ transform root.accept(self)
16
+ end
17
+ def visit_entity(name, block)
18
+ transform Parslet::Atoms::Entity.new(name) { block.call.accept(self) }
19
+ end
20
+ def visit_named(name, atom)
21
+ transform Parslet::Atoms::Named.new(atom.accept(self), name)
22
+ end
23
+ def visit_repetition(tag, min, max, atom)
24
+ transform Parslet::Atoms::Repetition.new(atom.accept(self), min, max, tag)
25
+ end
26
+ def visit_alternative(alternatives)
27
+ transform Parslet::Atoms::Alternative.new(
28
+ *alternatives.map { |atom| atom.accept(self) })
29
+ end
30
+ def visit_sequence(sequence)
31
+ transform Parslet::Atoms::Sequence.new(
32
+ *sequence.map { |atom| atom.accept(self) })
33
+ end
34
+ def visit_lookahead(positive, atom)
35
+ transform Parslet::Atoms::Lookahead.new(atom, positive)
36
+ end
37
+ def visit_re(regexp)
38
+ transform Parslet::Atoms::Re.new(regexp)
39
+ end
40
+ def visit_str(str)
41
+ transform Parslet::Atoms::Str.new(str)
42
+ end
43
+
44
+ def transform atom
45
+ @rules.each do |expr, action|
46
+ # Try and match each rule in turn
47
+ binding = Parslet::Accelerator.match(atom, expr)
48
+ if binding
49
+ # On a successful match, allow the rule action to transform the
50
+ # parslet into something new.
51
+ ctx = Parslet::Context.new(binding)
52
+ return ctx.instance_eval(&action)
53
+ end
54
+ end # rules.each
55
+
56
+ # If no rule matches, this is the fallback - a clean new parslet atom.
57
+ return atom
58
+ end
59
+ end
60
+ end
61
+
62
+ require 'parslet/context'
@@ -0,0 +1,112 @@
1
+
2
+ require 'parslet/atoms/visitor'
3
+
4
+ module Parslet::Accelerator
5
+ # @api private
6
+ class Apply
7
+ def initialize(engine, expr)
8
+ @engine = engine
9
+ @expr = expr
10
+ end
11
+
12
+ def visit_parser(root)
13
+ false
14
+ end
15
+ def visit_entity(name, block)
16
+ false
17
+ end
18
+ def visit_named(name, atom)
19
+ match(:as) do |key|
20
+ @engine.try_bind(key, name)
21
+ end
22
+ end
23
+ def visit_repetition(tag, min, max, atom)
24
+ match(:rep) do |e_min, e_max, expr|
25
+ e_min == min && e_max == max && @engine.match(atom, expr)
26
+ end
27
+ end
28
+ def visit_alternative(alternatives)
29
+ match(:alt) do |*expressions|
30
+ return false if alternatives.size != expressions.size
31
+
32
+ alternatives.zip(expressions).all? do |atom, expr|
33
+ @engine.match(atom, expr)
34
+ end
35
+ end
36
+ end
37
+ def visit_sequence(sequence)
38
+ match(:seq) do |*expressions|
39
+ return false if sequence.size != expressions.size
40
+
41
+ sequence.zip(expressions).all? do |atom, expr|
42
+ @engine.match(atom, expr)
43
+ end
44
+ end
45
+ end
46
+ def visit_lookahead(positive, atom)
47
+ match(:absent) do |expr|
48
+ return positive == false && @engine.match(atom, expr)
49
+ end
50
+ match(:present) do |expr|
51
+ return positive == true && @engine.match(atom, expr)
52
+ end
53
+ end
54
+ def visit_re(regexp)
55
+ match(:re) do |*bind_conditions|
56
+ bind_conditions.all? { |bind_cond|
57
+ @engine.try_bind(bind_cond, regexp) }
58
+ end
59
+ end
60
+ def visit_str(str)
61
+ match(:str) do |*bind_conditions|
62
+ bind_conditions.all? { |bind_cond|
63
+ @engine.try_bind(bind_cond, str) }
64
+ end
65
+ end
66
+
67
+ def match(type_tag)
68
+ expr_tag = @expr.type
69
+ if expr_tag == type_tag
70
+ yield *@expr.args
71
+ end
72
+ end
73
+ end
74
+
75
+ # @api private
76
+ class Engine
77
+ attr_reader :bindings
78
+
79
+ def initialize
80
+ @bindings = {}
81
+ end
82
+
83
+ def match(atom, expr)
84
+ atom.accept(
85
+ Apply.new(self, expr))
86
+ end
87
+
88
+ def try_bind(variable, value)
89
+ if bound? variable
90
+ return value == lookup(variable)
91
+ else
92
+ case variable
93
+ when Symbol
94
+ bind(variable, value)
95
+ else
96
+ # This does not look like a variable - let's try matching it against
97
+ # the value:
98
+ variable === value
99
+ end
100
+ end
101
+ end
102
+ def bound? var
103
+ @bindings.has_key? var
104
+ end
105
+ def lookup var
106
+ @bindings[var]
107
+ end
108
+ def bind var, val
109
+ @bindings[var] = val
110
+ end
111
+ end
112
+ end