parsanol 1.3.13-arm-linux

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 (100) hide show
  1. checksums.yaml +7 -0
  2. data/HISTORY.txt +98 -0
  3. data/LICENSE +23 -0
  4. data/README.adoc +905 -0
  5. data/Rakefile +141 -0
  6. data/lib/parsanol/3.2/parsanol_native.so +0 -0
  7. data/lib/parsanol/3.3/parsanol_native.so +0 -0
  8. data/lib/parsanol/3.4/parsanol_native.so +0 -0
  9. data/lib/parsanol/4.0/parsanol_native.so +0 -0
  10. data/lib/parsanol/ast_visitor.rb +122 -0
  11. data/lib/parsanol/atoms/alternative.rb +123 -0
  12. data/lib/parsanol/atoms/base.rb +208 -0
  13. data/lib/parsanol/atoms/can_flatten.rb +194 -0
  14. data/lib/parsanol/atoms/capture.rb +38 -0
  15. data/lib/parsanol/atoms/context.rb +357 -0
  16. data/lib/parsanol/atoms/context_optimized.rb +38 -0
  17. data/lib/parsanol/atoms/custom.rb +110 -0
  18. data/lib/parsanol/atoms/cut.rb +66 -0
  19. data/lib/parsanol/atoms/dsl.rb +96 -0
  20. data/lib/parsanol/atoms/dynamic.rb +39 -0
  21. data/lib/parsanol/atoms/entity.rb +75 -0
  22. data/lib/parsanol/atoms/ignored.rb +37 -0
  23. data/lib/parsanol/atoms/infix.rb +167 -0
  24. data/lib/parsanol/atoms/lookahead.rb +85 -0
  25. data/lib/parsanol/atoms/named.rb +74 -0
  26. data/lib/parsanol/atoms/re.rb +83 -0
  27. data/lib/parsanol/atoms/repetition.rb +277 -0
  28. data/lib/parsanol/atoms/scope.rb +35 -0
  29. data/lib/parsanol/atoms/sequence.rb +195 -0
  30. data/lib/parsanol/atoms/str.rb +109 -0
  31. data/lib/parsanol/atoms/visitor.rb +91 -0
  32. data/lib/parsanol/atoms.rb +46 -0
  33. data/lib/parsanol/buffer.rb +133 -0
  34. data/lib/parsanol/builder_callbacks.rb +353 -0
  35. data/lib/parsanol/cause.rb +122 -0
  36. data/lib/parsanol/context.rb +39 -0
  37. data/lib/parsanol/convenience.rb +36 -0
  38. data/lib/parsanol/edit_tracker.rb +111 -0
  39. data/lib/parsanol/error_reporter/contextual.rb +99 -0
  40. data/lib/parsanol/error_reporter/deepest.rb +120 -0
  41. data/lib/parsanol/error_reporter/tree.rb +63 -0
  42. data/lib/parsanol/error_reporter.rb +100 -0
  43. data/lib/parsanol/expression/treetop.rb +154 -0
  44. data/lib/parsanol/expression.rb +106 -0
  45. data/lib/parsanol/fast_mode.rb +187 -0
  46. data/lib/parsanol/first_set.rb +79 -0
  47. data/lib/parsanol/grammar_builder.rb +179 -0
  48. data/lib/parsanol/incremental_parser.rb +182 -0
  49. data/lib/parsanol/interval_tree.rb +226 -0
  50. data/lib/parsanol/lazy_result.rb +179 -0
  51. data/lib/parsanol/mermaid.rb +142 -0
  52. data/lib/parsanol/native/batch_decoder.rb +255 -0
  53. data/lib/parsanol/native/dynamic.rb +238 -0
  54. data/lib/parsanol/native/parser.rb +102 -0
  55. data/lib/parsanol/native/serializer.rb +252 -0
  56. data/lib/parsanol/native/transformer.rb +604 -0
  57. data/lib/parsanol/native/types.rb +29 -0
  58. data/lib/parsanol/native.rb +223 -0
  59. data/lib/parsanol/optimizer.rb +85 -0
  60. data/lib/parsanol/optimizers/choice_optimizer.rb +78 -0
  61. data/lib/parsanol/optimizers/cut_inserter.rb +182 -0
  62. data/lib/parsanol/optimizers/lookahead_optimizer.rb +56 -0
  63. data/lib/parsanol/optimizers/quantifier_optimizer.rb +60 -0
  64. data/lib/parsanol/optimizers/sequence_optimizer.rb +97 -0
  65. data/lib/parsanol/options/zero_copy.rb +127 -0
  66. data/lib/parsanol/options.rb +21 -0
  67. data/lib/parsanol/parallel.rb +128 -0
  68. data/lib/parsanol/parser.rb +242 -0
  69. data/lib/parsanol/parslet.rb +151 -0
  70. data/lib/parsanol/pattern/binding.rb +91 -0
  71. data/lib/parsanol/pattern.rb +162 -0
  72. data/lib/parsanol/pool.rb +219 -0
  73. data/lib/parsanol/pools/array_pool.rb +75 -0
  74. data/lib/parsanol/pools/buffer_pool.rb +182 -0
  75. data/lib/parsanol/pools/position_pool.rb +92 -0
  76. data/lib/parsanol/pools/slice_pool.rb +64 -0
  77. data/lib/parsanol/position.rb +94 -0
  78. data/lib/parsanol/resettable.rb +29 -0
  79. data/lib/parsanol/result.rb +46 -0
  80. data/lib/parsanol/result_builder.rb +208 -0
  81. data/lib/parsanol/result_stream.rb +266 -0
  82. data/lib/parsanol/rig/rspec.rb +71 -0
  83. data/lib/parsanol/rope.rb +81 -0
  84. data/lib/parsanol/scope.rb +104 -0
  85. data/lib/parsanol/slice.rb +160 -0
  86. data/lib/parsanol/source/line_cache.rb +102 -0
  87. data/lib/parsanol/source.rb +185 -0
  88. data/lib/parsanol/source_location.rb +167 -0
  89. data/lib/parsanol/streaming_parser.rb +124 -0
  90. data/lib/parsanol/string_view.rb +198 -0
  91. data/lib/parsanol/transform.rb +226 -0
  92. data/lib/parsanol/version.rb +5 -0
  93. data/lib/parsanol/wasm/README.md +80 -0
  94. data/lib/parsanol/wasm/package.json +51 -0
  95. data/lib/parsanol/wasm/parsanol.js +252 -0
  96. data/lib/parsanol/wasm/parslet.d.ts +129 -0
  97. data/lib/parsanol/wasm_parser.rb +240 -0
  98. data/lib/parsanol.rb +278 -0
  99. data/parsanol.gemspec +67 -0
  100. metadata +279 -0
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parsanol
4
+ # Zero-copy string view that references original input.
5
+ #
6
+ # StringView avoids string copies by maintaining a reference to the
7
+ # original string with offset and length. Strings are only materialized
8
+ # when explicitly requested via #to_s.
9
+ #
10
+ # == Usage
11
+ #
12
+ # view = StringView.new(input_string, offset: 10, length: 5)
13
+ # view.to_s # Materializes string only when needed
14
+ # view[0] # Direct character access without copying
15
+ #
16
+ # == Performance
17
+ #
18
+ # - No string allocation until to_s called
19
+ # - Direct character access without copying
20
+ # - Reduced GC pressure from intermediate strings
21
+ # - Caches materialized strings for reuse
22
+ #
23
+ # == Design Principles
24
+ #
25
+ # 1. Zero-Copy: Reference original string, don't copy
26
+ # 2. Lazy Materialization: Create strings only when to_s called
27
+ # 3. Caching: Cache materialized strings for reuse
28
+ # 4. Compatibility: Acts like String where needed
29
+ # 5. Extensibility: Foundation for Rope (Phase 3.2)
30
+ #
31
+ class StringView
32
+ include Resettable
33
+
34
+ # @return [String] Original input string
35
+ attr_reader :string
36
+
37
+ # @return [Integer] Byte offset into string
38
+ attr_reader :offset
39
+
40
+ # @return [Integer] Length in bytes
41
+ attr_reader :length
42
+
43
+ # Initialize a new StringView.
44
+ #
45
+ # @param string [String] Original input string
46
+ # @param offset [Integer] Byte offset (default: 0)
47
+ # @param length [Integer] Length in bytes (default: string.bytesize)
48
+ #
49
+ def initialize(string, offset: 0, length: nil)
50
+ @string = string
51
+ @offset = offset
52
+ @length = length || (string.bytesize - offset)
53
+ @materialized = nil
54
+ end
55
+
56
+ # Materialize to string (with caching).
57
+ #
58
+ # First call creates string slice, subsequent calls return cached.
59
+ # This implements lazy evaluation - strings are only created when
60
+ # explicitly needed, not during parsing.
61
+ #
62
+ # @return [String] Materialized string
63
+ #
64
+ def to_s
65
+ @materialized ||= @string.byteslice(@offset, @length)
66
+ end
67
+
68
+ # Get character at index (zero-copy).
69
+ #
70
+ # Direct access to character in original string without creating
71
+ # intermediate string objects.
72
+ #
73
+ # @param index [Integer] Zero-based index
74
+ # @return [String, nil] Character at index or nil
75
+ #
76
+ def [](index)
77
+ return nil if index.negative? || index >= @length
78
+
79
+ @string.byteslice(@offset + index, 1)
80
+ end
81
+
82
+ # Get byte size.
83
+ #
84
+ # @return [Integer] Length in bytes
85
+ #
86
+ def bytesize
87
+ @length
88
+ end
89
+
90
+ alias size bytesize
91
+ alias length bytesize
92
+
93
+ # Check if empty.
94
+ #
95
+ # @return [Boolean] true if length is 0
96
+ #
97
+ def empty?
98
+ @length.zero?
99
+ end
100
+
101
+ # Compare with another object.
102
+ #
103
+ # StringViews are only equal if they reference the exact same string object
104
+ # (by object_id) and have the same offset/length. This is consistent with
105
+ # the view pattern - they're views of a specific string instance.
106
+ #
107
+ # When comparing with a String, content is compared.
108
+ #
109
+ # @param other [Object] Object to compare with
110
+ # @return [Boolean] true if equal
111
+ #
112
+ def ==(other)
113
+ case other
114
+ when String
115
+ to_s == other
116
+ when StringView
117
+ # Only equal if viewing the exact same string object with same range
118
+ @string.equal?(other.string) &&
119
+ @offset == other.offset &&
120
+ @length == other.length
121
+ else
122
+ super
123
+ end
124
+ end
125
+
126
+ alias eql? ==
127
+
128
+ # Hash code for hashing.
129
+ #
130
+ # Uses object_id of string to avoid materializing the view.
131
+ #
132
+ # @return [Integer] Hash code
133
+ #
134
+ def hash
135
+ [@string.object_id, @offset, @length].hash
136
+ end
137
+
138
+ # Create substring view (zero-copy).
139
+ #
140
+ # Returns a new StringView referencing a substring of this view.
141
+ # No string allocation occurs - just a new view with adjusted offset.
142
+ #
143
+ # @param start [Integer] Start offset (relative to view)
144
+ # @param len [Integer] Length
145
+ # @return [StringView] New view of substring
146
+ #
147
+ def slice(start, len)
148
+ # Handle edge cases
149
+ if len <= 0 || start >= @length
150
+ return self.class.new(@string, offset: @offset,
151
+ length: 0)
152
+ end
153
+
154
+ # Clamp start to valid range [0, @length)
155
+ clamped_start = [[start, 0].max, @length].min
156
+
157
+ # Calculate actual offset in original string
158
+ actual_offset = @offset + clamped_start
159
+
160
+ # Calculate actual length (min of requested and available)
161
+ available = @length - clamped_start
162
+ actual_length = [len, available].min
163
+
164
+ self.class.new(@string, offset: actual_offset, length: actual_length)
165
+ end
166
+
167
+ # Inspect for debugging.
168
+ #
169
+ # Shows whether string has been materialized.
170
+ #
171
+ # @return [String] Inspection string
172
+ #
173
+ def inspect
174
+ if @materialized
175
+ "#<StringView:#{object_id} @offset=#{@offset} @length=#{@length} cached=#{@materialized.inspect}>"
176
+ else
177
+ "#<StringView:#{object_id} @offset=#{@offset} @length=#{@length}>"
178
+ end
179
+ end
180
+
181
+ # Reset for pooling (if needed in future phases).
182
+ #
183
+ # Allows StringView objects to be reused from a pool.
184
+ #
185
+ # @param string [String] New string
186
+ # @param offset [Integer] New offset
187
+ # @param length [Integer] New length
188
+ # @return [self]
189
+ #
190
+ def reset!(string, offset, length)
191
+ @string = string
192
+ @offset = offset
193
+ @length = length
194
+ @materialized = nil
195
+ self
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parsanol/pattern"
4
+
5
+ # Tree transformation engine for converting parse trees into abstract syntax trees.
6
+ #
7
+ # Transforms expression trees through depth-first post-order traversal.
8
+ # When a rule pattern matches a node, that node is replaced by the result
9
+ # of the rule's transformation block. Unmatched nodes pass through unchanged.
10
+ #
11
+ # @example Basic transformation class
12
+ # class NumberTransform < Parsanol::Transform
13
+ # rule(int: simple(:value)) { Integer(value) }
14
+ # rule(float: simple(:value)) { Float(value) }
15
+ # end
16
+ #
17
+ # transform = NumberTransform.new
18
+ # transform.apply({ int: '42' }) # => 42
19
+ #
20
+ # @example Inline transformation definition
21
+ # transform = Parsanol::Transform.new do
22
+ # rule(a: simple(:x)) { x.upcase }
23
+ # end
24
+ # transform.apply({ a: 'hello' }) # => 'HELLO'
25
+ #
26
+ # @example Using context for external dependencies
27
+ # builder = AstBuilder.new
28
+ # transform = Parsanol::Transform.new do
29
+ # rule(expr: simple(:e)) { builder.build_node(e) }
30
+ # rule(expr: simple(:e)) { |ctx| ctx[:builder].build_node(e) }
31
+ # end
32
+ # transform.apply(tree, builder: builder)
33
+ #
34
+ # Rule blocks can have two forms:
35
+ # - Zero-arity: executed in a context where pattern bindings are local variables
36
+ # - Arity-1: receives a hash of bindings as the argument
37
+ #
38
+ # Inspired by tree transformation patterns in parser combinators.
39
+ #
40
+ module Parsanol
41
+ class Transform
42
+ include Parsanol
43
+
44
+ # Class-level rule definition for subclass inheritance.
45
+ class << self
46
+ include Parsanol
47
+
48
+ # Defines a transformation rule at the class level.
49
+ # Rules are inherited by subclasses and evaluated in reverse order
50
+ # (most recently defined rules have highest precedence).
51
+ #
52
+ # @param expression [Object] pattern to match against tree nodes
53
+ # @yield block to execute when pattern matches, receives bindings
54
+ # @return [Array] the updated rules list
55
+ #
56
+ def rule(expression, &transformer)
57
+ class_rules.unshift([Parsanol::Pattern.new(expression), transformer])
58
+ end
59
+
60
+ # Returns all class-level rules defined for this transform.
61
+ #
62
+ # @return [Array<Array>] array of [pattern, block] pairs
63
+ #
64
+ def class_rules
65
+ @class_rules ||= []
66
+ end
67
+
68
+ # Ensures subclasses inherit parent rules.
69
+ def inherited(subclass)
70
+ super
71
+ subclass.instance_variable_set(:@class_rules, class_rules.dup)
72
+ end
73
+ end
74
+
75
+ # Creates a new transform instance.
76
+ #
77
+ # @param strict [Boolean] if true, raises on unmatched hash nodes
78
+ # @yield optional block for inline rule definition
79
+ #
80
+ def initialize(strict = false, &definition)
81
+ @strict_mode = strict
82
+ @instance_rules = []
83
+
84
+ instance_eval(&definition) if definition
85
+ end
86
+
87
+ # Defines an instance-level transformation rule.
88
+ # Instance rules are checked before class rules.
89
+ #
90
+ # @param expression [Object] pattern to match
91
+ # @yield transformation block
92
+ #
93
+ def rule(expression, &transformer)
94
+ @instance_rules.unshift([Parsanol::Pattern.new(expression), transformer])
95
+ end
96
+
97
+ # Applies transformation to a parse tree.
98
+ #
99
+ # Performs depth-first post-order traversal, transforming nodes
100
+ # from leaves to root. Context values are available in rule blocks.
101
+ #
102
+ # @param tree [Object] parse tree to transform
103
+ # @param context [Hash, nil] optional context bindings
104
+ # @return [Object] transformed tree
105
+ #
106
+ def apply(tree, context = nil)
107
+ # First, recursively transform children (depth-first)
108
+ transformed = transform_children(tree, context)
109
+
110
+ # Then, try to match and transform this node
111
+ attempt_transformation(transformed, context)
112
+ end
113
+
114
+ # Returns combined class and instance rules.
115
+ # Instance rules take precedence over class rules.
116
+ #
117
+ # @return [Array<Array>] all applicable rules
118
+ #
119
+ def all_rules
120
+ @instance_rules + self.class.class_rules
121
+ end
122
+
123
+ # Executes a transformation block with the given bindings.
124
+ # Public API for testing and advanced usage.
125
+ #
126
+ # @param bindings [Hash] pattern bindings
127
+ # @param block [Proc] transformation block
128
+ # @return [Object] block result
129
+ #
130
+ def call_on_match(bindings, block)
131
+ return nil unless block
132
+
133
+ if block.arity == 1
134
+ block.call(bindings)
135
+ else
136
+ Context.new(bindings).instance_eval(&block)
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ # Recursively transforms child nodes based on tree type.
143
+ #
144
+ # @param node [Object] current tree node
145
+ # @param ctx [Hash, nil] context bindings
146
+ # @return [Object] node with transformed children
147
+ #
148
+ def transform_children(node, ctx)
149
+ case node
150
+ when Hash
151
+ transform_hash_children(node, ctx)
152
+ when Array
153
+ transform_array_children(node, ctx)
154
+ else
155
+ node
156
+ end
157
+ end
158
+
159
+ # Transforms all values in a hash.
160
+ #
161
+ def transform_hash_children(hash, ctx)
162
+ result = {}
163
+ hash.each { |key, val| result[key] = apply(val, ctx) }
164
+ result
165
+ end
166
+
167
+ # Transforms all elements in an array.
168
+ #
169
+ def transform_array_children(array, ctx)
170
+ array.map { |element| apply(element, ctx) }
171
+ end
172
+
173
+ # Attempts to match a node against all rules and transform if matched.
174
+ #
175
+ # @param node [Object] node to potentially transform
176
+ # @param ctx [Hash, nil] context bindings
177
+ # @return [Object] transformed node or original if no match
178
+ #
179
+ def attempt_transformation(node, ctx)
180
+ all_rules.each do |pattern, block|
181
+ bindings = pattern.match(node, ctx)
182
+ next unless bindings
183
+
184
+ return execute_block(block, bindings)
185
+ end
186
+
187
+ # No rule matched
188
+ handle_unmatched(node)
189
+ end
190
+
191
+ # Executes a transformation block with proper binding context.
192
+ #
193
+ # @param block [Proc] transformation block
194
+ # @param bindings [Hash] matched pattern bindings
195
+ # @return [Object] block result
196
+ #
197
+ def execute_block(block, bindings)
198
+ return nil unless block
199
+
200
+ if block.arity == 1
201
+ # Block expects bindings as argument
202
+ block.call(bindings)
203
+ else
204
+ # Block executes in context with bindings as local variables
205
+ Context.new(bindings).instance_eval(&block)
206
+ end
207
+ end
208
+
209
+ # Handles nodes that didn't match any rule.
210
+ #
211
+ # @param node [Object] unmatched node
212
+ # @return [Object] the node (or raises in strict mode)
213
+ # @raise [NotImplementedError] if strict mode and node is a Hash
214
+ #
215
+ def handle_unmatched(node)
216
+ return node unless @strict_mode
217
+ return node unless node.is_a?(Hash)
218
+
219
+ # In strict mode, provide helpful error about what wasn't matched
220
+ signature = node.transform_values(&:class)
221
+ raise NotImplementedError, "Failed to match #{signature.inspect}"
222
+ end
223
+ end
224
+ end
225
+
226
+ require "parsanol/context"
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parsanol
4
+ VERSION = "1.3.13"
5
+ end
@@ -0,0 +1,80 @@
1
+ # @parsanol/wasm
2
+
3
+ High-performance PEG parser using WebAssembly. Designed for use with Opal (Ruby in JavaScript) and general JavaScript applications.
4
+
5
+ ## Features
6
+
7
+ - **18-44x faster** than pure Ruby parser
8
+ - **99.5% fewer allocations**
9
+ - Works in browsers and Node.js
10
+ - Full TypeScript support
11
+ - Compatible with Opal
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @parsanol/wasm
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Browser/ESM
22
+
23
+ ```html
24
+ <script type="module">
25
+ import { initParsanol, ParsanolParser } from '@parsanol/wasm';
26
+
27
+ // Initialize WASM (call once)
28
+ await initParsanol();
29
+
30
+ // Create parser from grammar JSON
31
+ const grammar = {
32
+ atoms: [
33
+ { Str: { pattern: "hello" } }
34
+ ],
35
+ root: 0
36
+ };
37
+
38
+ const parser = new ParsanolParser(grammar);
39
+
40
+ // Parse input
41
+ const result = parser.parse('hello');
42
+ console.log(result); // "hello"
43
+ </script>
44
+ ```
45
+
46
+ ### Node.js
47
+
48
+ ```javascript
49
+ const { initParsanol, ParsanolParser } = require('@parsanol/wasm');
50
+
51
+ async function main() {
52
+ await initParsanol();
53
+
54
+ const parser = new ParsanolParser(grammarJson);
55
+ const result = parser.parse('input text');
56
+ console.log(result);
57
+ }
58
+
59
+ main();
60
+ ```
61
+
62
+ ### Opal (Ruby in Browser)
63
+
64
+ ```ruby
65
+ # First initialize WASM in JavaScript:
66
+ # Parsanol::WasmParser.init.then { puts "ready" }
67
+
68
+ require 'parsanol/wasm_parser'
69
+
70
+ grammar_json = {
71
+ atoms: [
72
+ { Str: { pattern: "hello" } }
73
+ ],
74
+ root: 0
75
+ }.to_json
76
+
77
+ parser = Parsanol::WasmParser.new(grammar_json)
78
+ result = parser.parse('hello')
79
+ puts result # => "hello"
80
+ ```
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@parsanol/wasm",
3
+ "version": "0.1.0",
4
+ "description": "High-performance PEG parser using WebAssembly for Opal and JavaScript",
5
+ "main": "parsanol.js",
6
+ "module": "parsanol.js",
7
+ "types": "parsanol.d.ts",
8
+ "browser": "parsanol.js",
9
+ "files": [
10
+ "parsanol.js",
11
+ "parsanol.d.ts",
12
+ "parsanol_native.js",
13
+ "parsanol_native_bg.js",
14
+ "parsanol_native_bg.wasm",
15
+ "parsanol_native.d.ts"
16
+ ],
17
+ "scripts": {
18
+ "build": "cd ../../ext/parsanol_native && wasm-pack build --features wasm --target web --out-dir ../../lib/parsanol/wasm",
19
+ "build:node": "cd ../../ext/parsanol_native && wasm-pack build --features wasm --target nodejs --out-dir ../../lib/parsanol/wasm-node",
20
+ "test": "node test/parsanol.test.js",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "parser",
25
+ "peg",
26
+ "parsing-expression-grammar",
27
+ "wasm",
28
+ "webassembly",
29
+ "opal",
30
+ "ruby",
31
+ "performance"
32
+ ],
33
+ "author": "Ribose Inc. <open.source@ribose.com>",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/parsanol/parsanol-rs.git",
38
+ "directory": "lib/parsanol/wasm"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/parsanol/parsanol-ruby/issues"
42
+ },
43
+ "homepage": "https://github.com/parsanol/parsanol-ruby#readme",
44
+ "engines": {
45
+ "node": ">=14.0.0"
46
+ },
47
+ "sideEffects": [
48
+ "parsanol_native.js",
49
+ "parsanol_native_bg.js"
50
+ ]
51
+ }