parsanol 1.3.16-aarch64-linux → 1.3.18-aarch64-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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 47f7be12e8a16ae18e07d621fc699bd49d55367c4e17a6f43c3ba1a00f12bfc7
4
- data.tar.gz: 3877f2d6c8d48495efd798abf72bd96bbc7e6fc643cf6de78ca0a6b589445b37
3
+ metadata.gz: ec79b496ea0dd5cf94aeb9601343133f685c31782ad573ede40947f4c9ded214
4
+ data.tar.gz: 747502b04cf7510313812bde6e4606a741582a2489f2ad745482cf1f47b36a4e
5
5
  SHA512:
6
- metadata.gz: d6e19f01bc6b7fdb3168fbe5ad272182e590e685c1a12d3fa97c7064791f825efdd72cca971aa385af6ecb443a6e28e3052168d7bf6aaccf458ae4cfaac37cd1
7
- data.tar.gz: 66e2a1c85f1667ac98451fc943b25e9697ed7c227854ccf0e31ce214a68b56918a6f68cc1a21688a29b4bb360cafcc2711e12e493313c63c9e96908baff07e3e
6
+ metadata.gz: 5bb8a32f322259c12432d610bb1f526db17caf038be37819b089db8e9f9aad991fa2bac63141fe35edacb34841f24a609a0837152dfc1e6427baf53e13a0413c
7
+ data.tar.gz: e1feaa6b11461e52844d7d3152ca00a28e9ee939da21575536560bed7049500a70bb664062bc61416c0d86531735f9aac994e8f4fc8d004b4e20876d12263fc7
Binary file
Binary file
Binary file
Binary file
@@ -148,6 +148,13 @@ module Parsanol
148
148
  to_s(TOP)
149
149
  end
150
150
 
151
+ # Finalizes result by flattening. Public: the native error
152
+ # fallback (both extension and ffi tiers) calls it cross-object to
153
+ # finalize a recovered tree.
154
+ def finalize_result(value)
155
+ flatten(value)
156
+ end
157
+
151
158
  protected
152
159
 
153
160
  # Pre-allocated constant result tuples
@@ -180,11 +187,6 @@ module Parsanol
180
187
  # Alias for ok (legacy compatibility)
181
188
  alias succ ok
182
189
 
183
- # Finalizes result by flattening.
184
- def finalize_result(value)
185
- flatten(value)
186
- end
187
-
188
190
  private
189
191
 
190
192
  # Converts raw input to Source if needed.
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Parsanol
4
+ module Native
5
+ # Rust engine for runtimes that cannot load C-API extensions
6
+ # (JRuby, TruffleRuby, or an MRI whose platform gem is absent and
7
+ # cannot build one). Binds the parsanol cdylib's C ABI through the
8
+ # `ffi` gem: grammars register once into Rust-side handles keyed by
9
+ # serialized structure; results cross the boundary as flat-u64 batch
10
+ # arrays decoded by the same BatchDecoder used elsewhere.
11
+ module Ffi
12
+ class Error < StandardError
13
+ end
14
+
15
+ class << self
16
+ @available = nil
17
+
18
+ def available?
19
+ return @available unless @available.nil?
20
+
21
+ @available = begin
22
+ require "ffi"
23
+ lib = locate_library
24
+ lib && bind_library(lib)
25
+ rescue LoadError, StandardError
26
+ false
27
+ end
28
+ end
29
+
30
+ # Registers (once, cached) and parses. +grammar+ is a grammar
31
+ # atom or a pre-serialized JSON string. Mirrors Native.parse's
32
+ # contract, including the single reporter-pass fallback to the
33
+ # pure-Ruby engine for parslet-compatible failure errors.
34
+ def parse(grammar, input)
35
+ unless available? && @binding
36
+ raise Error, "parsanol FFI library not available"
37
+ end
38
+
39
+ if grammar.is_a?(String)
40
+ json = grammar
41
+ atom = nil
42
+ else
43
+ json = ::Parsanol::Native::Parser.serialize_grammar(grammar)
44
+ atom = grammar
45
+ end
46
+ handle = ((@handles ||= {})[json] ||= @binding.c_register(json))
47
+ raise Error, "grammar registration failed" if handle.zero?
48
+
49
+ written = parse_into(handle, input)
50
+ if written.positive?
51
+ return ::Parsanol::Native::BatchDecoder.decode_and_flatten(
52
+ @buffer.read_array_of_uint64(written), input, ::Parsanol::Slice
53
+ )
54
+ end
55
+
56
+ message = @binding.c_last_error.to_s
57
+ if atom
58
+ return ::Parsanol::Native.raise_native_parse_error(
59
+ RuntimeError.new(message), atom, input
60
+ )
61
+ end
62
+
63
+ raise Error, "parse failed: #{message}"
64
+ end
65
+
66
+ private
67
+
68
+ # Search order: explicit override, gem-vendored cdylib next to
69
+ # this file, then the system linker path.
70
+ def locate_library
71
+ candidates = []
72
+ env = ENV.fetch("PARSANOL_FFI_LIB", nil)
73
+ candidates << env if env && !env.empty?
74
+ here = File.dirname(__FILE__)
75
+ %w[libparsanol.dylib libparsanol.so parsanol.dll].each do |name|
76
+ path = File.expand_path(name, here)
77
+ candidates << path if File.file?(path)
78
+ end
79
+ candidates << "parsanol"
80
+ candidates.each do |cand|
81
+ return cand if File.file?(cand)
82
+
83
+ begin
84
+ FFI::DynamicLibrary.open(cand, FFI::DynamicLibrary::RTLD_NOW)
85
+ return cand
86
+ rescue StandardError
87
+ next
88
+ end
89
+ end
90
+ nil
91
+ end
92
+
93
+ def bind_library(path) # rubocop:disable Naming/PredicateMethod -- binds and reports success
94
+ binding_mod = Module.new do
95
+ extend FFI::Library
96
+
97
+ ffi_lib path
98
+ attach_function :c_register, :parsanol_c_register,
99
+ %i[string], :uint64
100
+ attach_function :c_parse, :parsanol_c_parse,
101
+ %i[uint64 string pointer size_t], :long_long
102
+ attach_function :c_last_error, :parsanol_c_last_error,
103
+ [], :string
104
+ attach_function :c_release, :parsanol_c_release,
105
+ %i[uint64], :void
106
+ end
107
+ # Probe the symbols now so a mismatched library fails loudly
108
+ # at availability time, not at first parse.
109
+ probe = binding_mod.c_last_error
110
+ @binding = binding_mod
111
+ @buffer = nil
112
+ !probe.nil? || true
113
+ end
114
+
115
+ # Two-call buffer protocol: cap=0 asks for the needed size, then
116
+ # one allocation carries the whole batch.
117
+ def parse_into(handle, input)
118
+ needed = @binding.c_parse(handle, input, nil, 0)
119
+ return needed unless needed.negative?
120
+
121
+ @buffer = FFI::MemoryPointer.new(:uint64, -needed)
122
+ @binding.c_parse(handle, input, @buffer, -needed)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -18,7 +18,7 @@ module Parsanol
18
18
  def available?
19
19
  return @cached_available unless @cached_available.nil?
20
20
 
21
- @cached_available = begin
21
+ @ext_loaded = begin
22
22
  # Try versioned path first (released gem), then non-versioned (local dev)
23
23
  ruby_version = RUBY_VERSION.split(".").take(2).join(".")
24
24
  begin
@@ -30,6 +30,13 @@ module Parsanol
30
30
  rescue LoadError
31
31
  false
32
32
  end
33
+ @cached_available = @ext_loaded || Ffi.available?
34
+ end
35
+
36
+ # True when the MRI C-API extension is loaded; false when the
37
+ # engine runs through the ffi-gem cdylib tier instead.
38
+ def extension_loaded?
39
+ @ext_loaded ? true : false
33
40
  end
34
41
 
35
42
  # Parse input with a Ruby grammar, returning clean AST.
@@ -10,6 +10,8 @@ require "parsanol/native/batch_decoder"
10
10
 
11
11
  module Parsanol
12
12
  module Native
13
+ # ffi-gem cdylib tier: lazy — only loaded when the MRI extension is absent.
14
+ autoload :Ffi, "parsanol/native/ffi"
13
15
  class << self
14
16
  # Check if native extension is available
15
17
  def available?
@@ -38,6 +40,10 @@ module Parsanol
38
40
  def parse(grammar, input)
39
41
  raise LoadError, "Native parser not available" unless available?
40
42
 
43
+ # ffi-gem tier (JRuby/TruffleRuby/no-binary MRI): same contract,
44
+ # batch-decoded results, single reporter-pass error fallback.
45
+ return Ffi.parse(grammar, input) unless Parser.extension_loaded?
46
+
41
47
  # Both sub-methods return the final decoded tree; on native failure
42
48
  # they fall back to the pure-Ruby parser, whose result is already
43
49
  # final and must not be transformed again.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parsanol
4
- VERSION = "1.3.16"
4
+ VERSION = "1.3.18"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parsanol
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.16
4
+ version: 1.3.18
5
5
  platform: aarch64-linux
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-15 00:00:00.000000000 Z
11
+ date: 2026-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -200,6 +200,7 @@ files:
200
200
  - lib/parsanol/native.rb
201
201
  - lib/parsanol/native/batch_decoder.rb
202
202
  - lib/parsanol/native/dynamic.rb
203
+ - lib/parsanol/native/ffi.rb
203
204
  - lib/parsanol/native/parser.rb
204
205
  - lib/parsanol/native/serializer.rb
205
206
  - lib/parsanol/native/transformer.rb