moxml 0.5.107 → 0.5.108

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: abbc78c5ee5efd00d9513dbe54fa844d69518c656f2eaec2c16984230ef86e09
4
- data.tar.gz: 2d45e9d308e8c5826d1dafdbeab32e1c0ef1f44dbbb51586283183436b6e42cf
3
+ metadata.gz: fff38acca654943d1500e6647de3b62fadbb6919e73a1a655c84718a47a08d20
4
+ data.tar.gz: cb163aac66d87cb7bb3140bfce8f682791b4cb8430bb435219d3d30141e5ada1
5
5
  SHA512:
6
- metadata.gz: 3074fb3116eab8bc30697dd0fb75221c10be71f2d2cc884d1954911c7b054ca314db8407a31637fa9c59bd53c00d7bf9b648d436d87cfaec3bffe1102c7af366
7
- data.tar.gz: 3a08135e70f29be37a8c97c75e01239386bd43a4d14c32837525b9395b68ff2778181a1a5499116884eb796bb6b08e30a40509c7d2a5ee25b4056d34ff188dcf
6
+ metadata.gz: c8ce340193f8f168f5fc06cc12191478dddd56e0debb1e960c43aee2380613350ae3d44e972ba3d1a5084319c3c26902c3ee5aa14ad61c94af57ca259045b1db
7
+ data.tar.gz: 771fdc938de78cb25a12830f1257f80c2387ee56916b29c9de46dfa3bb340a3179ed5876498c2c36b22ca4c870f3a70a14c495783aeca73ff68f0eaab59b42f1
data/README.adoc CHANGED
@@ -655,8 +655,48 @@ root.add_child(title)
655
655
  puts doc.to_xml(indent: 2)
656
656
  ----
657
657
 
658
+ === Performance versus nokogiri
659
+
660
+ CPU-time medians (1,000-record catalog, 20 iterations) through the
661
+ moxml adapter layer on both sides — moxml + leptris 1.9.307 vs
662
+ moxml + nokogiri:
663
+
664
+ [cols="2,1,4"]
665
+ |===
666
+ |Operation |leptris vs nokogiri |Notes
667
+
668
+ |parse
669
+ |3.27x
670
+ |Engine pool-carved parse
671
+
672
+ |serialize
673
+ |3.38x
674
+ |
675
+
676
+ |XPath
677
+ |50x
678
+ |Native compiled-eval seams
679
+
680
+ |walk + read (`.text`, `[]`)
681
+ |1.90x
682
+ |C klass-mint wrappers + wrapper read memos
683
+
684
+ |warm re-read
685
+ |2.10x
686
+ |The memo family — zero string allocation per node
687
+
688
+ |build (`add_element`)
689
+ |4.24x
690
+ |C-mint construction + bulk attributes
691
+
692
+ |attribute pairs
693
+ |1.73x
694
+ |One C crossing, no Attribute objects
695
+ |===
696
+
658
697
  === Fast paths and diagnostics
659
698
 
699
+
660
700
  Read-only attribute listing without Attribute node objects — the
661
701
  walk-hot shape when only names and values are needed (leptris
662
702
  answers in one C crossing; document order, duplicates included):
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.107"
4
+ VERSION = "0.5.108"
5
5
  end
@@ -155,6 +155,19 @@ module Moxml
155
155
 
156
156
  private
157
157
 
158
+ # After an E/e: a digit, or a sign followed by a digit. Anything
159
+ # else leaves the E as part of a name (1o vs 1e ambiguity guard).
160
+ def lookahead_after_e_is_digit_sign?
161
+ next_char = @expression[@position + 1]
162
+ return true if /\d/.match?(next_char)
163
+
164
+ if /[+-]/.match?(next_char)
165
+ return @expression[@position + 2]&.match?(/\d/) || false
166
+ end
167
+
168
+ false
169
+ end
170
+
158
171
  # Get current character
159
172
  #
160
173
  # @return [String, nil] Current character or nil if at end
@@ -259,6 +272,22 @@ module Moxml
259
272
  end
260
273
  end
261
274
 
275
+ # Exponent part (libxml2/nokogiri extension): digits [Ee] [+|-]
276
+ # digits. Without it, 1E+1 tokenized as 1E followed by + 1 and
277
+ # predicates raised "Expected ']' after predicate".
278
+ if @position < @length && current_char =~ /[Ee]/ &&
279
+ @position + 1 < @length && lookahead_after_e_is_digit_sign?
280
+ value += current_char
281
+ advance
282
+ value += current_char if /[+-]/.match?(current_char)
283
+ advance if /[+-]/.match?(current_char)
284
+
285
+ while @position < @length && current_char =~ /\d/
286
+ value += current_char
287
+ advance
288
+ end
289
+ end
290
+
262
291
  add_token(:number, value, start_pos)
263
292
  end
264
293
 
@@ -451,8 +451,10 @@ module Moxml
451
451
  elsif match?(:number)
452
452
  value = current_value
453
453
  advance
454
- # Convert string to actual numeric value
455
- numeric_value = value.include?(".") ? value.to_f : value.to_i
454
+ # Convert string to actual numeric value. Exponent
455
+ # literals must go through to_f — to_i truncates at the E
456
+ # ("1E+1" became 1).
457
+ numeric_value = value.match?(/[.Ee]/) ? value.to_f : value.to_i
456
458
  return AST::Node.number(numeric_value)
457
459
  elsif match?(:dollar)
458
460
  advance
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ # Signed exponent literals (libxml2/nokogiri extension): the lexer
6
+ # consumed exponent digits but not their sign, so 1E+1 tokenized as
7
+ # 1E + 1 and predicates raised "Expected ']'"; and the parser's
8
+ # numeric conversion ran to_i on dotless literals, so 2E3 evaluated
9
+ # as 2. Both fixed; the engine now matches nokogiri on every shape.
10
+ RSpec.describe "XPath exponent number literals" do
11
+ %i[nokogiri ox].each do |adapter|
12
+ it "evaluates signed exponents on #{adapter}" do
13
+ ctx = Moxml.new(adapter)
14
+ doc = ctx.parse("<r><x v='2000'/><x v='5'/></r>")
15
+ expect(doc.xpath("//x[@v > 1E+1]").size).to eq(1)
16
+ expect(doc.xpath("//x[@v = 2E3]").size).to eq(1)
17
+ expect(doc.xpath("//x[@v > 5.2E-3]").size).to eq(2)
18
+ expect(doc.xpath("//x[@v < 1e1]").size).to eq(1)
19
+ end
20
+ end
21
+
22
+ it "tokenizes exponent literals as one number" do
23
+ tokens = Moxml::XPath::Lexer.new("1E+1 2E3 5.2E-3 1e2").tokenize
24
+ numbers = tokens.select { |t| t.first == :number }.map { |t| t[1] }
25
+ expect(numbers).to eq(["1E+1", "2E3", "5.2E-3", "1e2"])
26
+ end
27
+
28
+ it "parses exponent literals at full precision" do
29
+ ast = Moxml::XPath::Parser.parse("1E+1")
30
+ literal = ast
31
+ literal = literal.children.first while literal.children.any? { |c| c.respond_to?(:type) }
32
+ expect(literal.value).to eq(10.0)
33
+ end
34
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.107
4
+ version: 0.5.108
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-05 00:00:00.000000000 Z
11
+ date: 2026-10-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Moxml is a unified XML manipulation library that provides a common API
@@ -557,6 +557,7 @@ files:
557
557
  - spec/moxml/xpath/functions/special_functions_spec.rb
558
558
  - spec/moxml/xpath/functions/string_functions_spec.rb
559
559
  - spec/moxml/xpath/lexer_spec.rb
560
+ - spec/moxml/xpath/number_literals_spec.rb
560
561
  - spec/moxml/xpath/parser_integration_spec.rb
561
562
  - spec/moxml/xpath/parser_spec.rb
562
563
  - spec/moxml/xpath/ruby/generator_spec.rb