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 +4 -4
- data/README.adoc +40 -0
- data/lib/moxml/version.rb +1 -1
- data/lib/moxml/xpath/lexer.rb +29 -0
- data/lib/moxml/xpath/parser.rb +4 -2
- data/spec/moxml/xpath/number_literals_spec.rb +34 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fff38acca654943d1500e6647de3b62fadbb6919e73a1a655c84718a47a08d20
|
|
4
|
+
data.tar.gz: cb163aac66d87cb7bb3140bfce8f682791b4cb8430bb435219d3d30141e5ada1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
data/lib/moxml/xpath/lexer.rb
CHANGED
|
@@ -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
|
|
data/lib/moxml/xpath/parser.rb
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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-
|
|
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
|