parsanol 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.
Potentially problematic release.
This version of parsanol might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/HISTORY.txt +25 -0
- data/LICENSE +23 -0
- data/README.adoc +643 -0
- data/Rakefile +189 -0
- data/example/balanced-parens/basic.rb +42 -0
- data/example/balanced-parens/basic.rb.md +86 -0
- data/example/balanced-parens/parens.rb +42 -0
- data/example/balanced-parens/ruby_transform.rb +162 -0
- data/example/big.erb +73 -0
- data/example/boolean-algebra/basic.rb +70 -0
- data/example/boolean-algebra/basic.rb.md +108 -0
- data/example/boolean-algebra/ruby_transform.rb +263 -0
- data/example/calculator/basic.rb +153 -0
- data/example/calculator/basic.rb.md +120 -0
- data/example/calculator/pattern.rb +153 -0
- data/example/calculator/ruby_transform.rb +156 -0
- data/example/calculator/ruby_transform.rb.md +32 -0
- data/example/calculator/serialized.rb +257 -0
- data/example/calculator/serialized.rb.md +32 -0
- data/example/calculator/transform.rb +153 -0
- data/example/calculator/zero_copy.rb +269 -0
- data/example/calculator/zero_copy.rb.md +36 -0
- data/example/capture/basic.rb +49 -0
- data/example/capture/basic.rb.md +106 -0
- data/example/capture/example.json +39 -0
- data/example/comments/basic.rb +35 -0
- data/example/comments/basic.rb.md +110 -0
- data/example/csv/ruby_transform.rb +148 -0
- data/example/csv/ruby_transform.rb.md +131 -0
- data/example/csv/serialized.rb +201 -0
- data/example/csv/serialized.rb.md +31 -0
- data/example/csv/zero_copy.rb +276 -0
- data/example/csv/zero_copy.rb.md +36 -0
- data/example/custom_atoms/indent_atom.rb +79 -0
- data/example/deepest-errors/basic.rb +131 -0
- data/example/deepest-errors/basic.rb.md +152 -0
- data/example/documentation/basic.rb +18 -0
- data/example/documentation/basic.rb.md +97 -0
- data/example/email/basic.rb +55 -0
- data/example/email/basic.rb.md +102 -0
- data/example/email/ruby_transform.rb +106 -0
- data/example/empty/basic.rb +13 -0
- data/example/empty/basic.rb.md +73 -0
- data/example/empty/example.json +38 -0
- data/example/erb/basic.rb +47 -0
- data/example/erb/basic.rb.md +103 -0
- data/example/erb/optimized.rb +42 -0
- data/example/error-reporting/basic.rb +132 -0
- data/example/error-reporting/basic.rb.md +122 -0
- data/example/expression-evaluator/basic.rb +284 -0
- data/example/expression-evaluator/basic.rb.md +138 -0
- data/example/ini/basic.rb +154 -0
- data/example/ini/basic.rb.md +129 -0
- data/example/ini/ruby_transform.rb +154 -0
- data/example/ip-address/basic.rb +125 -0
- data/example/ip-address/basic.rb.md +139 -0
- data/example/iso-6709/basic.rb +231 -0
- data/example/iso-6709/basic.rb.md +143 -0
- data/example/iso-8601/basic.rb +275 -0
- data/example/iso-8601/basic.rb.md +149 -0
- data/example/json/basic.rb +128 -0
- data/example/json/basic.rb.md +121 -0
- data/example/json/pattern.rb +128 -0
- data/example/json/ruby_transform.rb +200 -0
- data/example/json/ruby_transform.rb.md +32 -0
- data/example/json/serialized.rb +233 -0
- data/example/json/serialized.rb.md +31 -0
- data/example/json/transform.rb +128 -0
- data/example/json/zero_copy.rb +316 -0
- data/example/json/zero_copy.rb.md +36 -0
- data/example/local/basic.rb +34 -0
- data/example/local/basic.rb.md +91 -0
- data/example/local/example.json +38 -0
- data/example/markdown/basic.rb +287 -0
- data/example/markdown/basic.rb.md +160 -0
- data/example/markup/basic.rb +173 -0
- data/example/markup/basic.rb.md +118 -0
- data/example/mathn/basic.rb +47 -0
- data/example/mathn/basic.rb.md +96 -0
- data/example/mathn/example.json +39 -0
- data/example/minilisp/basic.rb +94 -0
- data/example/minilisp/basic.rb.md +133 -0
- data/example/modularity/basic.rb +47 -0
- data/example/modularity/basic.rb.md +152 -0
- data/example/nested-errors/basic.rb +132 -0
- data/example/nested-errors/basic.rb.md +157 -0
- data/example/output/boolean_algebra.out +4 -0
- data/example/output/calc.out +1 -0
- data/example/output/capture.out +3 -0
- data/example/output/comments.out +8 -0
- data/example/output/deepest_errors.out +54 -0
- data/example/output/documentation.err +4 -0
- data/example/output/documentation.out +1 -0
- data/example/output/email_parser.out +2 -0
- data/example/output/empty.err +1 -0
- data/example/output/erb.out +7 -0
- data/example/output/ignore.out +1 -0
- data/example/output/ignore_whitespace.out +1 -0
- data/example/output/ip_address.out +9 -0
- data/example/output/json.out +5 -0
- data/example/output/local.out +3 -0
- data/example/output/mathn.out +4 -0
- data/example/output/minilisp.out +5 -0
- data/example/output/modularity.out +0 -0
- data/example/output/nested_errors.out +54 -0
- data/example/output/optimized_erb.out +1 -0
- data/example/output/parens.out +8 -0
- data/example/output/prec_calc.out +5 -0
- data/example/output/readme.out +1 -0
- data/example/output/scopes.out +1 -0
- data/example/output/seasons.out +28 -0
- data/example/output/sentence.out +1 -0
- data/example/output/simple_xml.out +2 -0
- data/example/output/string_parser.out +3 -0
- data/example/prec-calc/basic.rb +71 -0
- data/example/prec-calc/basic.rb.md +114 -0
- data/example/readme/basic.rb +30 -0
- data/example/readme/basic.rb.md +80 -0
- data/example/scopes/basic.rb +15 -0
- data/example/scopes/basic.rb.md +73 -0
- data/example/scopes/example.json +38 -0
- data/example/seasons/basic.rb +46 -0
- data/example/seasons/basic.rb.md +117 -0
- data/example/seasons/example.json +40 -0
- data/example/sentence/basic.rb +36 -0
- data/example/sentence/basic.rb.md +81 -0
- data/example/sexp/ruby_transform.rb +180 -0
- data/example/sexp/ruby_transform.rb.md +143 -0
- data/example/simple-xml/basic.rb +54 -0
- data/example/simple-xml/basic.rb.md +125 -0
- data/example/simple.lit +3 -0
- data/example/string-literal/basic.rb +77 -0
- data/example/string-literal/basic.rb.md +128 -0
- data/example/test.lit +4 -0
- data/example/toml/basic.rb +226 -0
- data/example/toml/basic.rb.md +173 -0
- data/example/url/basic.rb +219 -0
- data/example/url/basic.rb.md +142 -0
- data/example/url/ruby_transform.rb +219 -0
- data/example/yaml/basic.rb +216 -0
- data/example/yaml/basic.rb.md +148 -0
- data/ext/parsanol_native/extconf.rb +4 -0
- data/lib/parsanol/accelerator/application.rb +62 -0
- data/lib/parsanol/accelerator/engine.rb +112 -0
- data/lib/parsanol/accelerator.rb +162 -0
- data/lib/parsanol/ast_visitor.rb +122 -0
- data/lib/parsanol/atoms/alternative.rb +97 -0
- data/lib/parsanol/atoms/base.rb +214 -0
- data/lib/parsanol/atoms/can_flatten.rb +192 -0
- data/lib/parsanol/atoms/capture.rb +41 -0
- data/lib/parsanol/atoms/context.rb +351 -0
- data/lib/parsanol/atoms/context_optimized.rb +42 -0
- data/lib/parsanol/atoms/custom.rb +110 -0
- data/lib/parsanol/atoms/cut.rb +62 -0
- data/lib/parsanol/atoms/dsl.rb +130 -0
- data/lib/parsanol/atoms/dynamic.rb +33 -0
- data/lib/parsanol/atoms/entity.rb +55 -0
- data/lib/parsanol/atoms/ignored.rb +28 -0
- data/lib/parsanol/atoms/infix.rb +121 -0
- data/lib/parsanol/atoms/lookahead.rb +64 -0
- data/lib/parsanol/atoms/named.rb +50 -0
- data/lib/parsanol/atoms/re.rb +61 -0
- data/lib/parsanol/atoms/repetition.rb +241 -0
- data/lib/parsanol/atoms/scope.rb +28 -0
- data/lib/parsanol/atoms/sequence.rb +157 -0
- data/lib/parsanol/atoms/str.rb +90 -0
- data/lib/parsanol/atoms/visitor.rb +91 -0
- data/lib/parsanol/atoms.rb +36 -0
- data/lib/parsanol/buffer.rb +130 -0
- data/lib/parsanol/builder_callbacks.rb +353 -0
- data/lib/parsanol/cause.rb +101 -0
- data/lib/parsanol/context.rb +23 -0
- data/lib/parsanol/convenience.rb +35 -0
- data/lib/parsanol/edit_tracker.rb +107 -0
- data/lib/parsanol/error_reporter/contextual.rb +122 -0
- data/lib/parsanol/error_reporter/deepest.rb +106 -0
- data/lib/parsanol/error_reporter/tree.rb +68 -0
- data/lib/parsanol/error_reporter.rb +98 -0
- data/lib/parsanol/export.rb +163 -0
- data/lib/parsanol/expression/treetop.rb +94 -0
- data/lib/parsanol/expression.rb +51 -0
- data/lib/parsanol/fast_mode.rb +145 -0
- data/lib/parsanol/first_set.rb +75 -0
- data/lib/parsanol/grammar_builder.rb +177 -0
- data/lib/parsanol/graphviz.rb +97 -0
- data/lib/parsanol/incremental_parser.rb +179 -0
- data/lib/parsanol/interval_tree.rb +215 -0
- data/lib/parsanol/lazy_result.rb +178 -0
- data/lib/parsanol/lexer.rb +146 -0
- data/lib/parsanol/native/parser.rb +630 -0
- data/lib/parsanol/native/serializer.rb +245 -0
- data/lib/parsanol/native/transformer.rb +438 -0
- data/lib/parsanol/native/types.rb +41 -0
- data/lib/parsanol/native.rb +217 -0
- data/lib/parsanol/optimizer.rb +86 -0
- data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
- data/lib/parsanol/optimizers/cut_inserter.rb +175 -0
- data/lib/parsanol/optimizers/lookahead_optimizer.rb +58 -0
- data/lib/parsanol/optimizers/quantifier_optimizer.rb +62 -0
- data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
- data/lib/parsanol/options/ruby_transform.rb +109 -0
- data/lib/parsanol/options/serialized.rb +94 -0
- data/lib/parsanol/options/zero_copy.rb +130 -0
- data/lib/parsanol/options.rb +20 -0
- data/lib/parsanol/parallel.rb +133 -0
- data/lib/parsanol/parsanol_native.bundle +0 -0
- data/lib/parsanol/parser.rb +151 -0
- data/lib/parsanol/parslet.rb +148 -0
- data/lib/parsanol/parslet_native.bundle +0 -0
- data/lib/parsanol/pattern/binding.rb +49 -0
- data/lib/parsanol/pattern.rb +115 -0
- data/lib/parsanol/pool.rb +220 -0
- data/lib/parsanol/pools/array_pool.rb +75 -0
- data/lib/parsanol/pools/buffer_pool.rb +173 -0
- data/lib/parsanol/pools/position_pool.rb +92 -0
- data/lib/parsanol/pools/slice_pool.rb +64 -0
- data/lib/parsanol/position.rb +89 -0
- data/lib/parsanol/result.rb +44 -0
- data/lib/parsanol/result_builder.rb +208 -0
- data/lib/parsanol/result_stream.rb +262 -0
- data/lib/parsanol/rig/rspec.rb +52 -0
- data/lib/parsanol/rope.rb +78 -0
- data/lib/parsanol/scope.rb +42 -0
- data/lib/parsanol/slice.rb +172 -0
- data/lib/parsanol/source/line_cache.rb +99 -0
- data/lib/parsanol/source.rb +171 -0
- data/lib/parsanol/source_location.rb +164 -0
- data/lib/parsanol/streaming_parser.rb +124 -0
- data/lib/parsanol/string_view.rb +192 -0
- data/lib/parsanol/transform.rb +267 -0
- data/lib/parsanol/version.rb +5 -0
- data/lib/parsanol/wasm/README.md +80 -0
- data/lib/parsanol/wasm/package.json +51 -0
- data/lib/parsanol/wasm/parsanol.js +252 -0
- data/lib/parsanol/wasm/parslet.d.ts +129 -0
- data/lib/parsanol/wasm_parser.rb +239 -0
- data/lib/parsanol.rb +408 -0
- data/parsanol-ruby.gemspec +56 -0
- data/spec/acceptance/examples_spec.rb +96 -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/benchmark/comparative/runner_spec.rb +105 -0
- data/spec/integration/array_pooling_spec.rb +193 -0
- data/spec/integration/buffer_allocation_spec.rb +324 -0
- data/spec/integration/position_pooling_spec.rb +184 -0
- data/spec/integration/result_builder_spec.rb +282 -0
- data/spec/integration/rope_stringview_integration_spec.rb +188 -0
- data/spec/integration/slice_pooling_spec.rb +63 -0
- data/spec/integration/string_view_integration_spec.rb +125 -0
- data/spec/lexer_spec.rb +231 -0
- data/spec/parsanol/atom_results_spec.rb +39 -0
- data/spec/parsanol/atoms/alternative_spec.rb +26 -0
- data/spec/parsanol/atoms/base_spec.rb +127 -0
- data/spec/parsanol/atoms/capture_spec.rb +21 -0
- data/spec/parsanol/atoms/combinations_spec.rb +5 -0
- data/spec/parsanol/atoms/custom_spec.rb +79 -0
- data/spec/parsanol/atoms/dsl_spec.rb +7 -0
- data/spec/parsanol/atoms/entity_spec.rb +77 -0
- data/spec/parsanol/atoms/ignored_spec.rb +15 -0
- data/spec/parsanol/atoms/infix_spec.rb +5 -0
- data/spec/parsanol/atoms/lookahead_spec.rb +22 -0
- data/spec/parsanol/atoms/named_spec.rb +4 -0
- data/spec/parsanol/atoms/re_spec.rb +14 -0
- data/spec/parsanol/atoms/repetition_spec.rb +24 -0
- data/spec/parsanol/atoms/scope_spec.rb +26 -0
- data/spec/parsanol/atoms/sequence_spec.rb +28 -0
- data/spec/parsanol/atoms/str_spec.rb +15 -0
- data/spec/parsanol/atoms/visitor_spec.rb +101 -0
- data/spec/parsanol/atoms_spec.rb +488 -0
- data/spec/parsanol/auto_optimize_spec.rb +334 -0
- data/spec/parsanol/buffer_spec.rb +219 -0
- data/spec/parsanol/builder_callbacks_spec.rb +377 -0
- data/spec/parsanol/choice_optimizer_spec.rb +231 -0
- data/spec/parsanol/convenience_spec.rb +54 -0
- data/spec/parsanol/cut_inserter_spec.rb +248 -0
- data/spec/parsanol/cut_spec.rb +66 -0
- data/spec/parsanol/edit_tracker_spec.rb +218 -0
- data/spec/parsanol/error_reporter/contextual_spec.rb +122 -0
- data/spec/parsanol/error_reporter/deepest_spec.rb +82 -0
- data/spec/parsanol/error_reporter/tree_spec.rb +7 -0
- data/spec/parsanol/export_spec.rb +67 -0
- data/spec/parsanol/expression/treetop_spec.rb +75 -0
- data/spec/parsanol/first_set_spec.rb +298 -0
- data/spec/parsanol/interval_tree_spec.rb +205 -0
- data/spec/parsanol/lazy_result_spec.rb +288 -0
- data/spec/parsanol/lookahead_optimizer_spec.rb +252 -0
- data/spec/parsanol/minilisp.citrus +29 -0
- data/spec/parsanol/minilisp.tt +29 -0
- data/spec/parsanol/optimizer_spec.rb +459 -0
- data/spec/parsanol/options/parslet_compat_spec.rb +166 -0
- data/spec/parsanol/options/ruby_transform_spec.rb +70 -0
- data/spec/parsanol/options/serialized_spec.rb +69 -0
- data/spec/parsanol/options/zero_copy_spec.rb +230 -0
- data/spec/parsanol/parser_spec.rb +36 -0
- data/spec/parsanol/parslet_spec.rb +38 -0
- data/spec/parsanol/pattern_spec.rb +272 -0
- data/spec/parsanol/pool_spec.rb +392 -0
- data/spec/parsanol/pools/array_pool_spec.rb +356 -0
- data/spec/parsanol/pools/buffer_pool_spec.rb +365 -0
- data/spec/parsanol/pools/position_pool_spec.rb +118 -0
- data/spec/parsanol/pools/slice_pool_spec.rb +262 -0
- data/spec/parsanol/position_spec.rb +14 -0
- data/spec/parsanol/result_builder_spec.rb +391 -0
- data/spec/parsanol/rig/rspec_spec.rb +54 -0
- data/spec/parsanol/rope_spec.rb +207 -0
- data/spec/parsanol/scope_spec.rb +45 -0
- data/spec/parsanol/slice_spec.rb +249 -0
- data/spec/parsanol/source/line_cache_spec.rb +74 -0
- data/spec/parsanol/source_spec.rb +207 -0
- data/spec/parsanol/string_view_spec.rb +345 -0
- data/spec/parsanol/transform/context_spec.rb +56 -0
- data/spec/parsanol/transform_spec.rb +183 -0
- data/spec/parsanol/tree_memoization_spec.rb +149 -0
- data/spec/parslet_compatibility/expressir_edge_cases_spec.rb +153 -0
- data/spec/parslet_compatibility/minimal_reproduction.rb +199 -0
- data/spec/parslet_compatibility_spec.rb +399 -0
- data/spec/parslet_imported/atom_spec.rb +93 -0
- data/spec/parslet_imported/combinator_spec.rb +161 -0
- data/spec/parslet_imported/spec_helper.rb +73 -0
- data/spec/performance/batch_parsing_benchmark.rb +129 -0
- data/spec/performance/complete_optimization_summary.rb +143 -0
- data/spec/performance/grammar_caching_analysis.rb +121 -0
- data/spec/performance/grammar_caching_benchmark.rb +80 -0
- data/spec/performance/native_benchmark_spec.rb +230 -0
- data/spec/performance/phase5_benchmark.rb +144 -0
- data/spec/performance/profiling_benchmark.rb +131 -0
- data/spec/performance/ruby_improvements_benchmark.rb +171 -0
- data/spec/performance_spec.rb +374 -0
- data/spec/spec_helper.rb +79 -0
- data/spec/support/opal.rb +8 -0
- data/spec/support/opal.rb.erb +14 -0
- metadata +485 -0
data/Rakefile
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler/gem_tasks'
|
|
4
|
+
require 'rspec/core/rake_task'
|
|
5
|
+
require 'rdoc/task'
|
|
6
|
+
require 'rubygems/package_task'
|
|
7
|
+
|
|
8
|
+
begin
|
|
9
|
+
require 'opal/rspec/rake_task'
|
|
10
|
+
rescue LoadError, NoMethodError
|
|
11
|
+
# Opal not available or incompatible with current Ruby version
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Native extension compilation using rake-compiler
|
|
15
|
+
begin
|
|
16
|
+
require 'rake/extensiontask'
|
|
17
|
+
Rake::ExtensionTask.new('parsanol_native') do |ext|
|
|
18
|
+
ext.lib_dir = 'lib/parsanol'
|
|
19
|
+
end
|
|
20
|
+
rescue LoadError
|
|
21
|
+
# rake-compiler not available
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'Run all tests'
|
|
25
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
26
|
+
|
|
27
|
+
namespace :spec do
|
|
28
|
+
desc 'Run unit tests only'
|
|
29
|
+
RSpec::Core::RakeTask.new(:unit) do |task|
|
|
30
|
+
task.pattern = 'spec/parsanol/**/*_spec.rb'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if defined?(Opal::RSpec::RakeTask)
|
|
34
|
+
desc 'Run Opal (JavaScript) tests'
|
|
35
|
+
Opal::RSpec::RakeTask.new(:opal) do |task|
|
|
36
|
+
task.append_path 'lib'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
RDoc::Task.new do |rdoc|
|
|
42
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
43
|
+
rdoc.title = 'Parsanol'
|
|
44
|
+
rdoc.options << '--line-numbers'
|
|
45
|
+
rdoc.rdoc_files.include('README.adoc')
|
|
46
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc 'Print LOC statistics'
|
|
50
|
+
task :stat do
|
|
51
|
+
%w[lib spec example].each do |dir|
|
|
52
|
+
next unless Dir.exist?(dir)
|
|
53
|
+
|
|
54
|
+
loc = `find #{dir} -name "*.rb" | xargs wc -l | grep 'total'`.split.first.to_i
|
|
55
|
+
printf("%20s %d\n", dir, loc)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# ===== Native Gem Building =====
|
|
60
|
+
# Platform definitions for precompiled gems
|
|
61
|
+
PLATFORMS = [
|
|
62
|
+
['x64-mingw32', 'x86_64-w64-mingw32'],
|
|
63
|
+
['x64-mingw-ucrt', 'x86_64-w64-mingw32'],
|
|
64
|
+
['arm64-mingw-ucrt', 'aarch64-w64-mingw32'],
|
|
65
|
+
['x86_64-linux', 'x86_64-linux-gnu'],
|
|
66
|
+
['x86_64-linux-gnu', 'x86_64-linux-gnu'],
|
|
67
|
+
['x86_64-linux-musl', 'x86_64-linux-musl'],
|
|
68
|
+
['aarch64-linux', 'aarch64-linux-gnu'],
|
|
69
|
+
['aarch64-linux-gnu', 'aarch64-linux-gnu'],
|
|
70
|
+
['aarch64-linux-musl', 'aarch64-linux-musl'],
|
|
71
|
+
['x86_64-darwin', 'x86_64-apple-darwin'],
|
|
72
|
+
['arm64-darwin', 'arm64-apple-darwin'],
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
namespace :gem do
|
|
76
|
+
desc 'Build install-compilation gem (platform: any)'
|
|
77
|
+
task 'native:any' do
|
|
78
|
+
sh 'rake platform:any gem'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
desc 'Define the gem task to build on any platform (compile on install)'
|
|
82
|
+
task 'platform:any' do
|
|
83
|
+
spec = Gem::Specification.load('parsanol-ruby.gemspec').dup
|
|
84
|
+
task = Gem::PackageTask.new(spec)
|
|
85
|
+
task.define
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Generate tasks for each platform
|
|
89
|
+
PLATFORMS.each do |platform, _host|
|
|
90
|
+
desc "Build pre-compiled gem for the #{platform} platform"
|
|
91
|
+
task "native:#{platform}" do
|
|
92
|
+
sh "rake compile platform:#{platform} gem"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
desc "Define the gem task to build on the #{platform} platform (binary gem)"
|
|
96
|
+
task "platform:#{platform}" do
|
|
97
|
+
spec = Gem::Specification.load('parsanol-ruby.gemspec').dup
|
|
98
|
+
spec.platform = Gem::Platform.new(platform)
|
|
99
|
+
|
|
100
|
+
# Include pre-compiled native extension
|
|
101
|
+
spec.files += Dir.glob('lib/parsanol/*.{so,dylib,dll,bundle}')
|
|
102
|
+
|
|
103
|
+
# Remove extension build for binary gems (already compiled)
|
|
104
|
+
spec.extensions = []
|
|
105
|
+
|
|
106
|
+
# Remove build-time dependencies
|
|
107
|
+
spec.dependencies.reject! { |d| d.name == 'rb_sys' }
|
|
108
|
+
spec.dependencies.reject! { |d| d.name == 'rake-compiler' }
|
|
109
|
+
|
|
110
|
+
task = Gem::PackageTask.new(spec)
|
|
111
|
+
task.define
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
desc 'Build all platform gems (requires cross-compilation setup)'
|
|
116
|
+
task :native do
|
|
117
|
+
puts 'Building all platform gems...'
|
|
118
|
+
puts 'Run individual tasks like: rake gem:native:x86_64-linux'
|
|
119
|
+
puts 'Or use the CI workflow for cross-compilation.'
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
namespace :benchmark do
|
|
124
|
+
desc 'Run comprehensive benchmark suite'
|
|
125
|
+
task :all do
|
|
126
|
+
ruby 'benchmark/benchmark_suite.rb'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
desc 'Run example-focused benchmarks'
|
|
130
|
+
task :examples do
|
|
131
|
+
ruby 'benchmark/example_benchmarks.rb'
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
desc 'Run benchmarks and export results to JSON/YAML'
|
|
135
|
+
task :export do
|
|
136
|
+
ruby 'benchmark/benchmark_runner.rb'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
desc 'Run quick benchmark (examples only)'
|
|
140
|
+
task quick: :examples
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Load comparative benchmark tasks
|
|
144
|
+
Dir.glob('benchmark/tasks/*.rake').each { |r| load r }
|
|
145
|
+
|
|
146
|
+
desc 'Run quick benchmarks'
|
|
147
|
+
task benchmark: 'benchmark:quick'
|
|
148
|
+
|
|
149
|
+
# ===== Parslet Compatibility Tests =====
|
|
150
|
+
namespace :compat do
|
|
151
|
+
desc 'Run imported Parslet tests with original Parslet (baseline)'
|
|
152
|
+
task :parslet do
|
|
153
|
+
ENV['PARSANOL_BACKEND'] = 'parslet'
|
|
154
|
+
sh 'bundle exec rspec spec/parslet_imported/ --format documentation'
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
desc 'Run imported Parslet tests with Parsanol compatibility layer'
|
|
158
|
+
task :parsanol do
|
|
159
|
+
ENV['PARSANOL_BACKEND'] = 'parsanol'
|
|
160
|
+
sh 'bundle exec rspec spec/parslet_imported/ --format documentation'
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
desc 'Run both and save results for comparison'
|
|
164
|
+
task :compare do
|
|
165
|
+
require 'fileutils'
|
|
166
|
+
|
|
167
|
+
results_dir = 'tmp/compat_results'
|
|
168
|
+
FileUtils.mkdir_p(results_dir)
|
|
169
|
+
|
|
170
|
+
puts '=== Running with original Parslet ==='
|
|
171
|
+
ENV['PARSANOL_BACKEND'] = 'parslet'
|
|
172
|
+
sh "bundle exec rspec spec/parslet_imported/ --format documentation > #{results_dir}/parslet.txt 2>&1"
|
|
173
|
+
|
|
174
|
+
puts "\n=== Running with Parsanol::Parslet ==="
|
|
175
|
+
ENV['PARSANOL_BACKEND'] = 'parsanol'
|
|
176
|
+
sh "bundle exec rspec spec/parslet_imported/ --format documentation > #{results_dir}/parsanol.txt 2>&1"
|
|
177
|
+
|
|
178
|
+
puts "\n=== Comparing results ==="
|
|
179
|
+
puts "Results saved to:"
|
|
180
|
+
puts " - #{results_dir}/parslet.txt"
|
|
181
|
+
puts " - #{results_dir}/parsanol.txt"
|
|
182
|
+
puts "\nTo compare: diff #{results_dir}/parslet.txt #{results_dir}/parsanol.txt"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
desc 'Run imported Parslet tests (default: with Parsanol)'
|
|
186
|
+
task run: :parsanol
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
task default: :spec
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# A small example that demonstrates the power of tree pattern matching. Also
|
|
2
|
+
# uses '.as(:name)' to construct a tree that can reliably be matched
|
|
3
|
+
# afterwards.
|
|
4
|
+
|
|
5
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
6
|
+
|
|
7
|
+
require 'pp'
|
|
8
|
+
require 'parsanol/parslet'
|
|
9
|
+
|
|
10
|
+
module LISP # as in 'lots of insipid and stupid parenthesis'
|
|
11
|
+
class Parser < Parsanol::Parser
|
|
12
|
+
rule(:balanced) {
|
|
13
|
+
str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
root(:balanced)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Transform < Parsanol::Transform
|
|
20
|
+
rule(:l => '(', :m => simple(:x), :r => ')') {
|
|
21
|
+
# innermost :m will contain nil
|
|
22
|
+
x.nil? ? 1 : x+1
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
parser = LISP::Parser.new
|
|
28
|
+
transform = LISP::Transform.new
|
|
29
|
+
%w!
|
|
30
|
+
()
|
|
31
|
+
(())
|
|
32
|
+
((((()))))
|
|
33
|
+
((())
|
|
34
|
+
!.each do |pexp|
|
|
35
|
+
begin
|
|
36
|
+
result = parser.parse(pexp)
|
|
37
|
+
puts "#{"%20s"%pexp}: #{result.inspect} (#{transform.apply(result)} parens)"
|
|
38
|
+
rescue Parsanol::ParseFailed => m
|
|
39
|
+
puts "#{"%20s"%pexp}: #{m}"
|
|
40
|
+
end
|
|
41
|
+
puts
|
|
42
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Balanced Parentheses - Ruby Implementation
|
|
2
|
+
|
|
3
|
+
## How to Run
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
cd parsanol-ruby/example/balanced-parens
|
|
7
|
+
ruby basic.rb
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Code Walkthrough
|
|
11
|
+
|
|
12
|
+
### Recursive Balanced Rule
|
|
13
|
+
|
|
14
|
+
Parentheses are defined recursively:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
rule(:balanced) {
|
|
18
|
+
str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each level contains opening paren, optional nested content, and closing paren.
|
|
23
|
+
|
|
24
|
+
### Labeled Components
|
|
25
|
+
|
|
26
|
+
Each part is labeled for pattern matching:
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
str('(').as(:l) # Opening paren labeled :l
|
|
30
|
+
balanced.maybe.as(:m) # Middle content labeled :m
|
|
31
|
+
str(')').as(:r) # Closing paren labeled :r
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Labels enable precise pattern matching in transforms.
|
|
35
|
+
|
|
36
|
+
### Transform for Counting
|
|
37
|
+
|
|
38
|
+
The transform counts nesting depth:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
class Transform < Parsanol::Transform
|
|
42
|
+
rule(:l => '(', :m => simple(:x), :r => ')') {
|
|
43
|
+
x.nil? ? 1 : x+1
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Pattern matches the structure; nil indicates innermost level.
|
|
49
|
+
|
|
50
|
+
### Recursive Tree Structure
|
|
51
|
+
|
|
52
|
+
Deep nesting creates nested parse trees:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
# Input: ((()))
|
|
56
|
+
# Tree: {:l=>"(", :m=>{:l=>"(", :m=>{:l=>"(", :m=>nil, :r=>")"}, :r=>")"}, :r=>")"}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The innermost `:m` is nil when nothing is inside.
|
|
60
|
+
|
|
61
|
+
## Output Types
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
# Parse tree for "(())":
|
|
65
|
+
{:l=>"(", :m=>{:l=>"(", :m=>nil, :r=>")"}, :r=>")"}
|
|
66
|
+
|
|
67
|
+
# After transform:
|
|
68
|
+
2 # Depth of nesting
|
|
69
|
+
|
|
70
|
+
# Invalid input:
|
|
71
|
+
# Raises Parsanol::ParseFailed
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Design Decisions
|
|
75
|
+
|
|
76
|
+
### Why Use Labels for Literals?
|
|
77
|
+
|
|
78
|
+
Labeling constant values (`:l => '('`) allows pattern matching on structure, not just values.
|
|
79
|
+
|
|
80
|
+
### Why Maybe for Middle Content?
|
|
81
|
+
|
|
82
|
+
The innermost parentheses have nothing inside. `.maybe` handles this base case.
|
|
83
|
+
|
|
84
|
+
### Why Count Depth in Transform?
|
|
85
|
+
|
|
86
|
+
Counting demonstrates tree traversal. Real applications might validate matching pairs or build AST structures.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# A small example that demonstrates the power of tree pattern matching. Also
|
|
2
|
+
# uses '.as(:name)' to construct a tree that can reliably be matched
|
|
3
|
+
# afterwards.
|
|
4
|
+
|
|
5
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
6
|
+
|
|
7
|
+
require 'pp'
|
|
8
|
+
require 'parsanol/parslet'
|
|
9
|
+
|
|
10
|
+
module LISP # as in 'lots of insipid and stupid parenthesis'
|
|
11
|
+
class Parser < Parsanol::Parser
|
|
12
|
+
rule(:balanced) {
|
|
13
|
+
str('(').as(:l) >> balanced.maybe.as(:m) >> str(')').as(:r)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
root(:balanced)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Transform < Parsanol::Transform
|
|
20
|
+
rule(:l => '(', :m => simple(:x), :r => ')') {
|
|
21
|
+
# innermost :m will contain nil
|
|
22
|
+
x.nil? ? 1 : x+1
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
parser = LISP::Parser.new
|
|
28
|
+
transform = LISP::Transform.new
|
|
29
|
+
%w!
|
|
30
|
+
()
|
|
31
|
+
(())
|
|
32
|
+
((((()))))
|
|
33
|
+
((())
|
|
34
|
+
!.each do |pexp|
|
|
35
|
+
begin
|
|
36
|
+
result = parser.parse(pexp)
|
|
37
|
+
puts "#{"%20s"%pexp}: #{result.inspect} (#{transform.apply(result)} parens)"
|
|
38
|
+
rescue Parsanol::ParseFailed => m
|
|
39
|
+
puts "#{"%20s"%pexp}: #{m}"
|
|
40
|
+
end
|
|
41
|
+
puts
|
|
42
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Balanced Parentheses Parser Example - RubyTransform
|
|
2
|
+
#
|
|
3
|
+
# This example demonstrates parsing balanced parentheses expressions.
|
|
4
|
+
# Shows recursive grammar rules and validation.
|
|
5
|
+
#
|
|
6
|
+
# Run with: ruby -Ilib example/balanced_parens_ruby_transform.rb
|
|
7
|
+
|
|
8
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
9
|
+
|
|
10
|
+
require 'parsanol'
|
|
11
|
+
|
|
12
|
+
# Step 1: Define the balanced parentheses grammar
|
|
13
|
+
# Using a PEG-friendly approach: parse multiple balanced groups
|
|
14
|
+
class BalancedParensParser < Parsanol::Parser
|
|
15
|
+
root :content
|
|
16
|
+
|
|
17
|
+
# Content: zero or more balanced groups
|
|
18
|
+
rule(:content) {
|
|
19
|
+
balanced.repeat
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Balanced: a parenthesized group that may contain more groups
|
|
23
|
+
rule(:balanced) {
|
|
24
|
+
str('(') >> content >> str(')')
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Step 2: Node classes
|
|
29
|
+
class ParenExpr
|
|
30
|
+
attr_reader :inner
|
|
31
|
+
|
|
32
|
+
def initialize(inner)
|
|
33
|
+
@inner = inner
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def balanced?
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def to_s
|
|
41
|
+
"(#{@inner})"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def depth
|
|
45
|
+
1 + (@inner.respond_to?(:depth) ? @inner.depth : 0)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
class EmptyExpr
|
|
50
|
+
def balanced?
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def to_s
|
|
55
|
+
""
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def depth
|
|
59
|
+
0
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class SequenceExpr
|
|
64
|
+
attr_reader :exprs
|
|
65
|
+
|
|
66
|
+
def initialize(exprs)
|
|
67
|
+
@exprs = exprs
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def balanced?
|
|
71
|
+
@exprs.all?(&:balanced?)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def to_s
|
|
75
|
+
@exprs.map(&:to_s).join
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def depth
|
|
79
|
+
@exprs.map(&:depth).max || 0
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Step 3: Parse and build AST
|
|
84
|
+
def parse_balanced(input)
|
|
85
|
+
parser = BalancedParensParser.new
|
|
86
|
+
|
|
87
|
+
tree = parser.parse(input)
|
|
88
|
+
puts "Parse tree: #{tree.inspect}"
|
|
89
|
+
|
|
90
|
+
# Build AST from tree
|
|
91
|
+
ast = build_ast(tree)
|
|
92
|
+
puts "AST: #{ast.to_s}"
|
|
93
|
+
puts "Balanced: #{ast.balanced?}"
|
|
94
|
+
puts "Max depth: #{ast.depth}"
|
|
95
|
+
|
|
96
|
+
ast
|
|
97
|
+
rescue Parsanol::ParseFailed => e
|
|
98
|
+
puts "Parse failed: #{e.message}"
|
|
99
|
+
nil
|
|
100
|
+
rescue SystemStackError => e
|
|
101
|
+
puts "Stack overflow - grammar too complex for input"
|
|
102
|
+
nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def build_ast(tree)
|
|
106
|
+
# Handle nil and empty
|
|
107
|
+
return EmptyExpr.new if tree.nil?
|
|
108
|
+
return EmptyExpr.new if tree.to_s.empty?
|
|
109
|
+
|
|
110
|
+
if tree.is_a?(Array)
|
|
111
|
+
exprs = tree.map { |t| build_ast(t) }.reject { |e| e.is_a?(EmptyExpr) }
|
|
112
|
+
return EmptyExpr.new if exprs.empty?
|
|
113
|
+
return exprs.first if exprs.length == 1
|
|
114
|
+
SequenceExpr.new(exprs)
|
|
115
|
+
elsif tree.is_a?(Hash)
|
|
116
|
+
if tree[:balanced]
|
|
117
|
+
inner = build_ast(tree[:balanced])
|
|
118
|
+
ParenExpr.new(inner)
|
|
119
|
+
elsif tree[:content]
|
|
120
|
+
build_ast(tree[:content])
|
|
121
|
+
else
|
|
122
|
+
EmptyExpr.new
|
|
123
|
+
end
|
|
124
|
+
else
|
|
125
|
+
EmptyExpr.new
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Example usage
|
|
130
|
+
if __FILE__ == $0
|
|
131
|
+
puts "=" * 60
|
|
132
|
+
puts "Balanced Parentheses Parser - RubyTransform"
|
|
133
|
+
puts "=" * 60
|
|
134
|
+
puts
|
|
135
|
+
|
|
136
|
+
test_cases = [
|
|
137
|
+
"", # Empty - balanced
|
|
138
|
+
"()", # Simple - balanced
|
|
139
|
+
"(())", # Nested - balanced
|
|
140
|
+
"(()())", # Multiple - balanced
|
|
141
|
+
"((()))", # Deeply nested - balanced
|
|
142
|
+
"(()())()", # Multiple groups - balanced
|
|
143
|
+
"(())(())", # Two groups - balanced
|
|
144
|
+
"(", # Unbalanced - should fail
|
|
145
|
+
")", # Unbalanced - should fail
|
|
146
|
+
"(()", # Unbalanced - should fail
|
|
147
|
+
"())", # Unbalanced - should fail
|
|
148
|
+
"((())", # Unbalanced - should fail
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
test_cases.each do |input|
|
|
152
|
+
puts "-" * 40
|
|
153
|
+
puts "Input: '#{input}'"
|
|
154
|
+
ast = parse_balanced(input)
|
|
155
|
+
if ast
|
|
156
|
+
puts "Result: #{ast.balanced? ? '✓ BALANCED' : '✗ UNBALANCED'}"
|
|
157
|
+
else
|
|
158
|
+
puts "Result: ✗ PARSE FAILED"
|
|
159
|
+
end
|
|
160
|
+
puts
|
|
161
|
+
end
|
|
162
|
+
end
|
data/example/big.erb
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
2
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
3
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
4
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
5
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
6
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
7
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
8
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
9
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
10
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
11
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
12
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
13
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
14
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
15
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
16
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
17
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
18
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
19
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
20
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
21
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
22
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
23
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
24
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
25
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
26
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
27
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
28
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
29
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
30
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
31
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
32
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
33
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
34
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
35
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
36
|
+
|
|
37
|
+
<%= erb tag %>
|
|
38
|
+
|
|
39
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
40
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
41
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
42
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
43
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
44
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
45
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
46
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
47
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
48
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
49
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
50
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
51
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
52
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
53
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
54
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
55
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
56
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
57
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
58
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
59
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
60
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
61
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
62
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
63
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
64
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
65
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
66
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
67
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
68
|
+
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
69
|
+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
70
|
+
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
71
|
+
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
72
|
+
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
73
|
+
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
2
|
+
|
|
3
|
+
require "parsanol/parslet"
|
|
4
|
+
require "pp"
|
|
5
|
+
|
|
6
|
+
# Parses strings like "var1 and (var2 or var3)" respecting operator precedence
|
|
7
|
+
# and parentheses. After that transforms the parse tree into an array of
|
|
8
|
+
# arrays like this:
|
|
9
|
+
#
|
|
10
|
+
# [["1", "2"], ["1", "3"]]
|
|
11
|
+
#
|
|
12
|
+
# The array represents a DNF (disjunctive normal form). Elements of outer
|
|
13
|
+
# array are connected with "or" operator, while elements of inner arrays are
|
|
14
|
+
# joined with "and".
|
|
15
|
+
#
|
|
16
|
+
class MyParser < Parsanol::Parser
|
|
17
|
+
rule(:space) { match[" "].repeat(1) }
|
|
18
|
+
rule(:space?) { space.maybe }
|
|
19
|
+
|
|
20
|
+
rule(:lparen) { str("(") >> space? }
|
|
21
|
+
rule(:rparen) { str(")") >> space? }
|
|
22
|
+
|
|
23
|
+
rule(:and_operator) { str("and") >> space? }
|
|
24
|
+
rule(:or_operator) { str("or") >> space? }
|
|
25
|
+
|
|
26
|
+
rule(:var) { str("var") >> match["0-9"].repeat(1).as(:var) >> space? }
|
|
27
|
+
|
|
28
|
+
# The primary rule deals with parentheses.
|
|
29
|
+
rule(:primary) { lparen >> or_operation >> rparen | var }
|
|
30
|
+
|
|
31
|
+
# Note that following rules are both right-recursive.
|
|
32
|
+
rule(:and_operation) {
|
|
33
|
+
(primary.as(:left) >> and_operator >>
|
|
34
|
+
and_operation.as(:right)).as(:and) |
|
|
35
|
+
primary }
|
|
36
|
+
|
|
37
|
+
rule(:or_operation) {
|
|
38
|
+
(and_operation.as(:left) >> or_operator >>
|
|
39
|
+
or_operation.as(:right)).as(:or) |
|
|
40
|
+
and_operation }
|
|
41
|
+
|
|
42
|
+
# We start at the lowest precedence rule.
|
|
43
|
+
root(:or_operation)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class Transformer < Parsanol::Transform
|
|
47
|
+
rule(:var => simple(:var)) { [[String(var)]] }
|
|
48
|
+
|
|
49
|
+
rule(:or => { :left => subtree(:left), :right => subtree(:right) }) do
|
|
50
|
+
(left + right)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
rule(:and => { :left => subtree(:left), :right => subtree(:right) }) do
|
|
54
|
+
res = []
|
|
55
|
+
left.each do |l|
|
|
56
|
+
right.each do |r|
|
|
57
|
+
res << (l + r)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
res
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
pp tree = MyParser.new.parse("var1 and (var2 or var3)")
|
|
65
|
+
# {:and=>
|
|
66
|
+
# {:left=>{:var=>"1"@3},
|
|
67
|
+
# :right=>{:or=>{:left=>{:var=>"2"@13}, :right=>{:var=>"3"@21}}}}}
|
|
68
|
+
pp Transformer.new.apply(tree)
|
|
69
|
+
# [["1", "2"], ["1", "3"]]
|
|
70
|
+
|