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
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# JSON Parser Example - Ruby Transform: Ruby Transform (Parslet-Compatible)
|
|
2
|
+
#
|
|
3
|
+
# This example demonstrates Ruby Transform for parsing JSON:
|
|
4
|
+
# 1. Rust parser (parsanol-rs) does the fast parsing
|
|
5
|
+
# 2. Returns a generic tree (hash/array/string structure)
|
|
6
|
+
# 3. Ruby transform converts tree to Ruby objects
|
|
7
|
+
#
|
|
8
|
+
# This is the most flexible option and is 100% Parslet API compatible.
|
|
9
|
+
|
|
10
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
11
|
+
|
|
12
|
+
require 'parsanol'
|
|
13
|
+
|
|
14
|
+
# Step 1: Define the JSON parser grammar
|
|
15
|
+
class JsonParser < Parsanol::Parser
|
|
16
|
+
root :json
|
|
17
|
+
|
|
18
|
+
rule(:json) { space? >> value >> space? }
|
|
19
|
+
|
|
20
|
+
rule(:value) {
|
|
21
|
+
object |
|
|
22
|
+
array |
|
|
23
|
+
string |
|
|
24
|
+
number |
|
|
25
|
+
true_value |
|
|
26
|
+
false_value |
|
|
27
|
+
null_value
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Object: { "key": value, ... }
|
|
31
|
+
rule(:object) {
|
|
32
|
+
str('{') >> space? >>
|
|
33
|
+
(entry >> (comma >> entry).repeat).maybe.as(:object) >>
|
|
34
|
+
space? >> str('}')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
rule(:entry) {
|
|
38
|
+
(string.as(:key) >> space? >> colon >> space? >> value.as(:val)).as(:entry)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Array: [ value, ... ]
|
|
42
|
+
rule(:array) {
|
|
43
|
+
str('[') >> space? >>
|
|
44
|
+
(value >> (comma >> value).repeat).maybe.as(:array) >>
|
|
45
|
+
space? >> str(']')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# String: "..."
|
|
49
|
+
rule(:string) {
|
|
50
|
+
str('"') >> (
|
|
51
|
+
str('\\') >> any | str('"').absent? >> any
|
|
52
|
+
).repeat.as(:string) >> str('"')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Number: -?[0-9]+(\.[0-9]+)?([eE][+-]?[0-9]+)?
|
|
56
|
+
rule(:number) {
|
|
57
|
+
(
|
|
58
|
+
str('-').maybe >>
|
|
59
|
+
(str('0') | (match('[1-9]') >> digit.repeat)) >>
|
|
60
|
+
(str('.') >> digit.repeat(1)).maybe >>
|
|
61
|
+
(match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1)).maybe
|
|
62
|
+
).as(:number)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Literals
|
|
66
|
+
rule(:true_value) { str('true').as(:true) }
|
|
67
|
+
rule(:false_value) { str('false').as(:false) }
|
|
68
|
+
rule(:null_value) { str('null').as(:null) }
|
|
69
|
+
|
|
70
|
+
# Helpers
|
|
71
|
+
rule(:digit) { match('[0-9]') }
|
|
72
|
+
rule(:space) { match('\s').repeat(1) }
|
|
73
|
+
rule(:space?) { space.maybe }
|
|
74
|
+
rule(:comma) { space? >> str(',') >> space? }
|
|
75
|
+
rule(:colon) { str(':') }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Step 2: Define the transform (Parslet-style)
|
|
79
|
+
class JsonTransform < Parsanol::Transform
|
|
80
|
+
# Entry helper class
|
|
81
|
+
class Entry < Struct.new(:key, :val); end
|
|
82
|
+
|
|
83
|
+
# Transform arrays
|
|
84
|
+
rule(array: subtree(:ar)) {
|
|
85
|
+
ar.is_a?(Array) ? ar : [ar]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Transform objects
|
|
89
|
+
rule(object: subtree(:ob)) {
|
|
90
|
+
(ob.is_a?(Array) ? ob : [ob]).each_with_object({}) { |e, h| h[e.key] = e.val }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Transform entries
|
|
94
|
+
rule(entry: { key: simple(:ke), val: simple(:va) }) {
|
|
95
|
+
Entry.new(ke, va)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
# Transform strings
|
|
99
|
+
rule(string: simple(:st)) {
|
|
100
|
+
st.to_s
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# Transform numbers
|
|
104
|
+
rule(number: simple(:nb)) {
|
|
105
|
+
str = nb.to_s
|
|
106
|
+
str.match?(/[eE.]/) ? Float(str) : Integer(str)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
# Transform literals
|
|
110
|
+
rule(null: simple(:_nu)) { nil }
|
|
111
|
+
rule(true: simple(:_tr)) { true }
|
|
112
|
+
rule(false: simple(:_fa)) { false }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Step 3: Parse and transform
|
|
116
|
+
def parse_json(input)
|
|
117
|
+
parser = JsonParser.new
|
|
118
|
+
transform = JsonTransform.new
|
|
119
|
+
|
|
120
|
+
# Ruby Transform: Parse in Rust, transform in Ruby
|
|
121
|
+
tree = parser.parse(input)
|
|
122
|
+
puts "Parse tree (first 500 chars): #{tree.inspect[0..500]}..."
|
|
123
|
+
|
|
124
|
+
result = transform.apply(tree)
|
|
125
|
+
puts "Result: #{result.inspect[0..200]}..."
|
|
126
|
+
|
|
127
|
+
result
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Example usage
|
|
131
|
+
if __FILE__ == $0
|
|
132
|
+
puts "=" * 60
|
|
133
|
+
puts "JSON Parser Example - Ruby Transform: Ruby Transform"
|
|
134
|
+
puts "=" * 60
|
|
135
|
+
|
|
136
|
+
test_cases = [
|
|
137
|
+
['"hello"', "hello"],
|
|
138
|
+
['42', 42],
|
|
139
|
+
['3.14', 3.14],
|
|
140
|
+
['true', true],
|
|
141
|
+
['false', false],
|
|
142
|
+
['null', nil],
|
|
143
|
+
['[1, 2, 3]', [1, 2, 3]],
|
|
144
|
+
['{"a": 1}', { "a" => 1 }],
|
|
145
|
+
['{"name": "test", "value": 42}', { "name" => "test", "value" => 42 }],
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
test_cases.each do |input, expected|
|
|
149
|
+
puts
|
|
150
|
+
puts "-" * 40
|
|
151
|
+
puts "Input: #{input}"
|
|
152
|
+
begin
|
|
153
|
+
result = parse_json(input)
|
|
154
|
+
status = result == expected ? "✓ PASS" : "✗ FAIL"
|
|
155
|
+
puts "Expected: #{expected.inspect}, Got: #{result.inspect} - #{status}"
|
|
156
|
+
rescue => e
|
|
157
|
+
puts "Error: #{e.message}"
|
|
158
|
+
puts e.backtrace.first(3).join("\n")
|
|
159
|
+
puts "✗ FAIL"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Complex example
|
|
164
|
+
puts
|
|
165
|
+
puts "-" * 40
|
|
166
|
+
puts "Complex JSON example:"
|
|
167
|
+
complex_json = <<~JSON
|
|
168
|
+
{
|
|
169
|
+
"users": [
|
|
170
|
+
{"name": "Alice", "age": 30, "active": true},
|
|
171
|
+
{"name": "Bob", "age": 25, "active": false}
|
|
172
|
+
],
|
|
173
|
+
"count": 2,
|
|
174
|
+
"metadata": {
|
|
175
|
+
"version": "1.0",
|
|
176
|
+
"tags": ["admin", "test"]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
JSON
|
|
180
|
+
|
|
181
|
+
begin
|
|
182
|
+
result = parse_json(complex_json)
|
|
183
|
+
puts "Parsed successfully!"
|
|
184
|
+
puts "Users: #{result['users'].map { |u| u['name'] }.join(', ')}"
|
|
185
|
+
puts "Count: #{result['count']}"
|
|
186
|
+
puts "Version: #{result['metadata']['version']}"
|
|
187
|
+
puts "✓ PASS"
|
|
188
|
+
rescue => e
|
|
189
|
+
puts "Error: #{e.message}"
|
|
190
|
+
puts "✗ FAIL"
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
puts
|
|
194
|
+
puts "=" * 60
|
|
195
|
+
puts "Ruby Transform Benefits for JSON:"
|
|
196
|
+
puts "- Flexible: Can add custom transform logic"
|
|
197
|
+
puts "- Debuggable: Inspect tree before transform"
|
|
198
|
+
puts "- Compatible: Works with existing Parslet code"
|
|
199
|
+
puts "=" * 60
|
|
200
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# JSON (Ruby Transform - Option A)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This implementation demonstrates Parslet-compatible JSON parsing: Rust parses,
|
|
6
|
+
Ruby transforms into domain objects.
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- Migrating from Parslet
|
|
11
|
+
- Custom JSON processing logic
|
|
12
|
+
- Flexible transformation needs
|
|
13
|
+
|
|
14
|
+
## Key Concepts
|
|
15
|
+
|
|
16
|
+
1. **Rust Parsing**: Fast native parsing engine
|
|
17
|
+
2. **Ruby Transform**: Familiar transformation API
|
|
18
|
+
3. **Custom Output**: Any Ruby object structure
|
|
19
|
+
|
|
20
|
+
## Running
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ruby example/json/ruby_transform.rb
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Output
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Input: {"key": "value"}
|
|
30
|
+
Parse tree: {object: [{string: "key", value: {string: "value"}}]}
|
|
31
|
+
Result: {"key" => "value"}
|
|
32
|
+
```
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# JSON Parser Example - Serialized: JSON Serialization
|
|
2
|
+
#
|
|
3
|
+
# This example demonstrates Serialized for parsing JSON:
|
|
4
|
+
# 1. Rust parser (parsanol-rs) does the parsing
|
|
5
|
+
# 2. Rust transform converts to typed structs
|
|
6
|
+
# 3. Result is serialized to JSON (meta!)
|
|
7
|
+
# 4. Ruby deserializes JSON to Ruby objects
|
|
8
|
+
#
|
|
9
|
+
# Note: Since JSON output is already JSON, Serialized essentially
|
|
10
|
+
# validates and normalizes the input JSON.
|
|
11
|
+
|
|
12
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
13
|
+
|
|
14
|
+
require 'parsanol'
|
|
15
|
+
require 'json'
|
|
16
|
+
|
|
17
|
+
# NOTE: This example requires the native extension to support parse_to_json
|
|
18
|
+
# which is planned but not yet implemented. This serves as an API preview.
|
|
19
|
+
|
|
20
|
+
# Step 1: Define the JSON parser grammar (same as Option A)
|
|
21
|
+
class JsonParser < Parsanol::Parser
|
|
22
|
+
root :json
|
|
23
|
+
|
|
24
|
+
rule(:json) { space? >> value >> space? }
|
|
25
|
+
|
|
26
|
+
rule(:value) {
|
|
27
|
+
object |
|
|
28
|
+
array |
|
|
29
|
+
string |
|
|
30
|
+
number |
|
|
31
|
+
true_value |
|
|
32
|
+
false_value |
|
|
33
|
+
null_value
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
rule(:object) {
|
|
37
|
+
str('{') >> space? >>
|
|
38
|
+
(entry >> (comma >> entry).repeat).maybe.as(:object) >>
|
|
39
|
+
space? >> str('}')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
rule(:entry) {
|
|
43
|
+
(string.as(:key) >> space? >> colon >> space? >> value.as(:val)).as(:entry)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
rule(:array) {
|
|
47
|
+
str('[') >> space? >>
|
|
48
|
+
(value >> (comma >> value).repeat).maybe.as(:array) >>
|
|
49
|
+
space? >> str(']')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
rule(:string) {
|
|
53
|
+
str('"') >> (
|
|
54
|
+
str('\\') >> any | str('"').absent? >> any
|
|
55
|
+
).repeat.as(:string) >> str('"')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
rule(:number) {
|
|
59
|
+
(
|
|
60
|
+
str('-').maybe >>
|
|
61
|
+
(str('0') | (match('[1-9]') >> digit.repeat)) >>
|
|
62
|
+
(str('.') >> digit.repeat(1)).maybe >>
|
|
63
|
+
(match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1)).maybe
|
|
64
|
+
).as(:number)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
rule(:true_value) { str('true').as(:true) }
|
|
68
|
+
rule(:false_value) { str('false').as(:false) }
|
|
69
|
+
rule(:null_value) { str('null').as(:null) }
|
|
70
|
+
|
|
71
|
+
rule(:digit) { match('[0-9]') }
|
|
72
|
+
rule(:space) { match('\s').repeat(1) }
|
|
73
|
+
rule(:space?) { space.maybe }
|
|
74
|
+
rule(:comma) { space? >> str(',') >> space? }
|
|
75
|
+
rule(:colon) { str(':') }
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Step 2: Define Ruby classes for typed output (optional, for structured access)
|
|
79
|
+
class JsonValue; end
|
|
80
|
+
|
|
81
|
+
class JsonString < JsonValue
|
|
82
|
+
attr_reader :value
|
|
83
|
+
def initialize(value:) @value = value end
|
|
84
|
+
def to_ruby = @value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
class JsonNumber < JsonValue
|
|
88
|
+
attr_reader :value
|
|
89
|
+
def initialize(value:) @value = value end
|
|
90
|
+
def to_ruby = @value
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class JsonBool < JsonValue
|
|
94
|
+
attr_reader :value
|
|
95
|
+
def initialize(value:) @value = value end
|
|
96
|
+
def to_ruby = @value
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class JsonNull < JsonValue
|
|
100
|
+
def to_ruby = nil
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class JsonArray < JsonValue
|
|
104
|
+
attr_reader :elements
|
|
105
|
+
def initialize(elements:) @elements = elements end
|
|
106
|
+
def to_ruby = @elements.map(&:to_ruby)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
class JsonObject < JsonValue
|
|
110
|
+
attr_reader :members
|
|
111
|
+
def initialize(members:) @members = members end
|
|
112
|
+
def to_ruby = @members.transform_values(&:to_ruby)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Step 3: Deserializer
|
|
116
|
+
class JsonValueDeserializer
|
|
117
|
+
def self.from_json(json_string)
|
|
118
|
+
data = JSON.parse(json_string)
|
|
119
|
+
from_ruby(data)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def self.from_ruby(data)
|
|
123
|
+
case data
|
|
124
|
+
when String
|
|
125
|
+
JsonString.new(value: data)
|
|
126
|
+
when Integer
|
|
127
|
+
JsonNumber.new(value: data)
|
|
128
|
+
when Float
|
|
129
|
+
JsonNumber.new(value: data)
|
|
130
|
+
when true
|
|
131
|
+
JsonBool.new(value: true)
|
|
132
|
+
when false
|
|
133
|
+
JsonBool.new(value: false)
|
|
134
|
+
when nil
|
|
135
|
+
JsonNull.new
|
|
136
|
+
when Array
|
|
137
|
+
JsonArray.new(elements: data.map { |e| from_ruby(e) })
|
|
138
|
+
when Hash
|
|
139
|
+
JsonObject.new(members: data.transform_values { |v| from_ruby(v) })
|
|
140
|
+
else
|
|
141
|
+
raise "Unknown type: #{data.class}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Step 4: Parse with JSON output
|
|
147
|
+
def parse_json(input)
|
|
148
|
+
parser = JsonParser.new
|
|
149
|
+
|
|
150
|
+
# Serialized: Parse and get JSON from Rust
|
|
151
|
+
# NOTE: This requires native extension support
|
|
152
|
+
# output_json = parser.parse_to_json(input)
|
|
153
|
+
|
|
154
|
+
# For now, simulate by using Option A then serializing
|
|
155
|
+
# Real implementation would call:
|
|
156
|
+
# Native.parse_to_json(grammar_json, input)
|
|
157
|
+
|
|
158
|
+
# Use the parser defined in this file
|
|
159
|
+
tree = parser.parse(input)
|
|
160
|
+
transform = JsonTransform.new
|
|
161
|
+
result = transform.apply(tree)
|
|
162
|
+
|
|
163
|
+
# This would come from Rust in Serialized
|
|
164
|
+
output_json = result.to_json
|
|
165
|
+
puts "Output JSON: #{output_json[0..100]}..."
|
|
166
|
+
|
|
167
|
+
# Deserialize to typed objects
|
|
168
|
+
typed = JsonValueDeserializer.from_json(output_json)
|
|
169
|
+
puts "Typed: #{typed.class}"
|
|
170
|
+
|
|
171
|
+
# Convert to Ruby native types
|
|
172
|
+
typed.to_ruby
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Transform class (needed for simulation)
|
|
176
|
+
class JsonTransform < Parsanol::Transform
|
|
177
|
+
class Entry < Struct.new(:key, :val); end
|
|
178
|
+
rule(array: subtree(:ar)) { ar.is_a?(Array) ? ar : [ar] }
|
|
179
|
+
rule(object: subtree(:ob)) { (ob.is_a?(Array) ? ob : [ob]).each_with_object({}) { |e, h| h[e.key] = e.val } }
|
|
180
|
+
rule(entry: { key: simple(:ke), val: simple(:va) }) { Entry.new(ke, va) }
|
|
181
|
+
rule(string: simple(:st)) { st.to_s }
|
|
182
|
+
rule(number: simple(:nb)) {
|
|
183
|
+
s = nb.to_s
|
|
184
|
+
s.match?(/[eE.]/) ? Float(s) : Integer(s)
|
|
185
|
+
}
|
|
186
|
+
rule(null: simple(:_nu)) { nil }
|
|
187
|
+
rule(true: simple(:_tr)) { true }
|
|
188
|
+
rule(false: simple(:_fa)) { false }
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Example usage
|
|
192
|
+
if __FILE__ == $0
|
|
193
|
+
puts "=" * 60
|
|
194
|
+
puts "JSON Parser Example - Serialized: JSON Serialization"
|
|
195
|
+
puts "=" * 60
|
|
196
|
+
puts
|
|
197
|
+
puts "NOTE: This example shows the planned API for Serialized."
|
|
198
|
+
puts "The native extension support for parse_to_json is coming soon."
|
|
199
|
+
puts
|
|
200
|
+
|
|
201
|
+
test_cases = [
|
|
202
|
+
['"hello"', "hello"],
|
|
203
|
+
['42', 42],
|
|
204
|
+
['[1, 2, 3]', [1, 2, 3]],
|
|
205
|
+
['{"a": 1}', { "a" => 1 }],
|
|
206
|
+
]
|
|
207
|
+
|
|
208
|
+
test_cases.each do |input, expected|
|
|
209
|
+
puts
|
|
210
|
+
puts "-" * 40
|
|
211
|
+
puts "Input: #{input}"
|
|
212
|
+
begin
|
|
213
|
+
result = parse_json(input)
|
|
214
|
+
status = result == expected ? "✓ PASS" : "✗ FAIL"
|
|
215
|
+
puts "Expected: #{expected.inspect}, Got: #{result.inspect} - #{status}"
|
|
216
|
+
rescue => e
|
|
217
|
+
puts "Error: #{e.message}"
|
|
218
|
+
puts "✗ FAIL"
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
puts
|
|
223
|
+
puts "=" * 60
|
|
224
|
+
puts "Serialized Benefits for JSON:"
|
|
225
|
+
puts "- Validates JSON structure"
|
|
226
|
+
puts "- Normalizes formatting"
|
|
227
|
+
puts "- Type-safe output (with typed classes)"
|
|
228
|
+
puts "- Easy to cache serialized results"
|
|
229
|
+
puts
|
|
230
|
+
puts "Note: For simple JSON parsing, Serialized adds validation but"
|
|
231
|
+
puts "the output is essentially the same as the input."
|
|
232
|
+
puts "=" * 60
|
|
233
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# JSON (Serialized - Option B)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This implementation demonstrates full Rust processing with JSON output
|
|
6
|
+
for JSON parsing.
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
|
|
10
|
+
- Cross-language compatibility
|
|
11
|
+
- Structured output required
|
|
12
|
+
- Performance-critical applications
|
|
13
|
+
|
|
14
|
+
## Key Concepts
|
|
15
|
+
|
|
16
|
+
1. **Rust Parsing + Transform**: All processing in Rust
|
|
17
|
+
2. **JSON Serialization**: Language-agnostic output
|
|
18
|
+
3. **Type Safety**: Schema-driven structure
|
|
19
|
+
|
|
20
|
+
## Running
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ruby example/json/serialized.rb
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Output
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Input: {"name": "test", "value": 42}
|
|
30
|
+
JSON: {"name":"test","value":42}
|
|
31
|
+
```
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + "/../lib"
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# MIT License - (c) 2011 John Mettraux
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'parsanol/parslet' # gem install parslet
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module MyJson
|
|
12
|
+
|
|
13
|
+
class Parser < Parsanol::Parser
|
|
14
|
+
|
|
15
|
+
rule(:spaces) { match('\s').repeat(1) }
|
|
16
|
+
rule(:spaces?) { spaces.maybe }
|
|
17
|
+
|
|
18
|
+
rule(:comma) { spaces? >> str(',') >> spaces? }
|
|
19
|
+
rule(:digit) { match('[0-9]') }
|
|
20
|
+
|
|
21
|
+
rule(:number) {
|
|
22
|
+
(
|
|
23
|
+
str('-').maybe >> (
|
|
24
|
+
str('0') | (match('[1-9]') >> digit.repeat)
|
|
25
|
+
) >> (
|
|
26
|
+
str('.') >> digit.repeat(1)
|
|
27
|
+
).maybe >> (
|
|
28
|
+
match('[eE]') >> (str('+') | str('-')).maybe >> digit.repeat(1)
|
|
29
|
+
).maybe
|
|
30
|
+
).as(:number)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
rule(:string) {
|
|
34
|
+
str('"') >> (
|
|
35
|
+
str('\\') >> any | str('"').absent? >> any
|
|
36
|
+
).repeat.as(:string) >> str('"')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
rule(:array) {
|
|
40
|
+
str('[') >> spaces? >>
|
|
41
|
+
(value >> (comma >> value).repeat).maybe.as(:array) >>
|
|
42
|
+
spaces? >> str(']')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
rule(:object) {
|
|
46
|
+
str('{') >> spaces? >>
|
|
47
|
+
(entry >> (comma >> entry).repeat).maybe.as(:object) >>
|
|
48
|
+
spaces? >> str('}')
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
rule(:value) {
|
|
52
|
+
string | number |
|
|
53
|
+
object | array |
|
|
54
|
+
str('true').as(:true) | str('false').as(:false) |
|
|
55
|
+
str('null').as(:null)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
rule(:entry) {
|
|
59
|
+
(
|
|
60
|
+
string.as(:key) >> spaces? >>
|
|
61
|
+
str(':') >> spaces? >>
|
|
62
|
+
value.as(:val)
|
|
63
|
+
).as(:entry)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
rule(:attribute) { (entry | value).as(:attribute) }
|
|
67
|
+
|
|
68
|
+
rule(:top) { spaces? >> value >> spaces? }
|
|
69
|
+
|
|
70
|
+
root(:top)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
class Transformer < Parsanol::Transform
|
|
74
|
+
|
|
75
|
+
class Entry < Struct.new(:key, :val); end
|
|
76
|
+
|
|
77
|
+
rule(:array => subtree(:ar)) {
|
|
78
|
+
ar.is_a?(Array) ? ar : [ ar ]
|
|
79
|
+
}
|
|
80
|
+
rule(:object => subtree(:ob)) {
|
|
81
|
+
(ob.is_a?(Array) ? ob : [ ob ]).inject({}) { |h, e| h[e.key] = e.val; h }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
rule(:entry => { :key => simple(:ke), :val => simple(:va) }) {
|
|
85
|
+
Entry.new(ke, va)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
rule(:string => simple(:st)) {
|
|
89
|
+
st.to_s
|
|
90
|
+
}
|
|
91
|
+
rule(:number => simple(:nb)) {
|
|
92
|
+
nb.match(/[eE\.]/) ? Float(nb) : Integer(nb)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
rule(:null => simple(:nu)) { nil }
|
|
96
|
+
rule(:true => simple(:tr)) { true }
|
|
97
|
+
rule(:false => simple(:fa)) { false }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.parse(s)
|
|
101
|
+
|
|
102
|
+
parser = Parser.new
|
|
103
|
+
transformer = Transformer.new
|
|
104
|
+
|
|
105
|
+
tree = parser.parse(s)
|
|
106
|
+
puts; p tree; puts
|
|
107
|
+
out = transformer.apply(tree)
|
|
108
|
+
|
|
109
|
+
out
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
s = %{
|
|
115
|
+
[ 1, 2, 3, null,
|
|
116
|
+
"asdfasdf asdfds", { "a": -1.2 }, { "b": true, "c": false },
|
|
117
|
+
0.1e24, true, false, [ 1 ] ]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
out = MyJson.parse(s)
|
|
121
|
+
|
|
122
|
+
p out; puts
|
|
123
|
+
|
|
124
|
+
out == [
|
|
125
|
+
1, 2, 3, nil,
|
|
126
|
+
"asdfasdf asdfds", { "a" => -1.2 }, { "b" => true, "c" => false },
|
|
127
|
+
0.1e24, true, false, [ 1 ]
|
|
128
|
+
] || raise("MyJson is a failure")
|