leptris 1.9.314.0 → 1.9.317.1
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/CHANGELOG.md +46 -0
- data/Rakefile +1 -1
- data/lib/leptris/vendor/aarch64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/aarch64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/arm-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/arm64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/ppc64le-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/s390x-linux-musl/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-darwin/libleptris.dylib +0 -0
- data/lib/leptris/vendor/x86_64-linux/libleptris.so +0 -0
- data/lib/leptris/vendor/x86_64-linux-musl/libleptris.so +0 -0
- data/lib/leptris/version.rb +1 -1
- data/lib/leptris/xml/descriptor.rb +10 -2
- data/lib/leptris/xml/ffi.rb +7 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +70 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/bindings/rust/descriptor/src/lib.rs +6 -0
- data/vendor-src/libleptris/bindings/rust/descriptor/tests/layout.rs +1 -0
- data/vendor-src/libleptris/cli/commands/xquery.c +16 -4
- data/vendor-src/libleptris/src/include/leptris/descriptor.h +8 -1
- data/vendor-src/libleptris/src/leptris/descriptor/plan.c +46 -5
- data/vendor-src/libleptris/src/leptris/xpath/evaluator_operators.c +106 -21
- data/vendor-src/libleptris/src/leptris/xpath/functions.c +20 -0
- data/vendor-src/libleptris/src/leptris/xpath/functions_ext31.c +145 -32
- data/vendor-src/libleptris/src/leptris/xpath/parser.c +48 -8
- data/vendor-src/libleptris/src/leptris/xpath/xpath_public.c +19 -0
- data/vendor-src/libleptris/src/leptris/xquery/xquery.c +16 -0
- data/vendor-src/libleptris/test/CMakeLists.txt +1 -0
- data/vendor-src/libleptris/test/abi/test_descriptor.cpp +141 -0
- data/vendor-src/libleptris/test/parser/test_whitespace_shape.cpp +153 -0
- data/vendor-src/libleptris/test/xquery/test_qt3.cpp +6 -6
- data/vendor-src/libleptris/test/xquery/test_xquery.cpp +64 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- 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: b745c01d0d7ec0b75d4fe3a0b994fc5f2a95cdd58b7e318eaee8c33ce8a005c6
|
|
4
|
+
data.tar.gz: 0e17a0313b9d7ff6fd20b4eed9df95398d35592f3f6c92ce49455439610f3ed2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 28a4c82d4ea33fbfc9844a3c663b269943972da95d9a2339b30aa8c0cd3bc6a40d71748610a4d63c9cb55e8c856e4375c744c40c0065b9d808b8627be5102924
|
|
7
|
+
data.tar.gz: 7ace6d1ab6118f7fe3308543b42e6ebe60f91b84ad84297aa88dd892d42e8f26ab1f98961bc785393386cd31d45a209a9e27b93b73042bc914fc4f809f0a58f3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,49 @@
|
|
|
1
|
+
## [1.9.317.1] - 2026-10-08
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- **The published 1.9.317.0 gem predates PR #395**: a parallel
|
|
6
|
+
release race published 1.9.317.0 from the carrier-folds ride
|
|
7
|
+
(bd81bdf) before the `:unqualified` ns form merged — this patch
|
|
8
|
+
release carries the #1560 `ns: :unqualified` DSL (element rows
|
|
9
|
+
bind the unwritten spelling under both namespace-less and
|
|
10
|
+
default-xmlns documents; the serialize→re-parse→re-walk
|
|
11
|
+
round-trip agrees) and the #1565 nested-capture serialization
|
|
12
|
+
pin to the published artifact line. Main already carried both
|
|
13
|
+
(PR #395); only the gem lagged.
|
|
14
|
+
|
|
15
|
+
## [1.9.317.0] - 2026-10-08
|
|
16
|
+
|
|
17
|
+
### Added — engine sync (libleptris 1.9.315-1.9.317)
|
|
18
|
+
|
|
19
|
+
- **Unqualified-only ns form for child rows (1.9.317, #1560)**:
|
|
20
|
+
`ns: :unqualified` matches the UNWRITTEN spelling — an
|
|
21
|
+
unprefixed child binds under both a namespace-less document and
|
|
22
|
+
a default-`xmlns` document, and prefixed spellings never bind
|
|
23
|
+
(the `NS_ANY` superset lutaml-model#932 had to settle for). The
|
|
24
|
+
form keys on the spelling, not the URI, so
|
|
25
|
+
serialize→re-parse→re-walk round-trips agree: a default-ns child
|
|
26
|
+
serializes bare and still re-binds. Element-row only (attributes
|
|
27
|
+
have no unprefixed namespace by XML rules). `PLAN_NS_UNQUALIFIED
|
|
28
|
+
= 3` attached; all three shapes + the round-trip spec-pinned.
|
|
29
|
+
|
|
30
|
+
### Fixed — engine sync (1.9.316)
|
|
31
|
+
|
|
32
|
+
- **Nested captures emit their own rows (#1565)**: a captured
|
|
33
|
+
child's rows resolve against the CAPTURING row's
|
|
34
|
+
`child_plan_index` plan, so a child bound by a nested row (the
|
|
35
|
+
`w:font`-in-`w:fonts` shape) serializes its own attribute and
|
|
36
|
+
child rows instead of silently vanishing — pinned through
|
|
37
|
+
`Descriptor#serialize`.
|
|
38
|
+
|
|
39
|
+
### Engine sync (1.9.315)
|
|
40
|
+
|
|
41
|
+
- Carrier-by-pointer node identity for the higher-order argument
|
|
42
|
+
channel (QT3 fold-left-009/016): element members keep their node
|
|
43
|
+
pointer, `intersect` dedups by node, single-element results ride
|
|
44
|
+
the carrier. Engine-internal to the XQuery engine — the binding
|
|
45
|
+
contract is unchanged (suite carries it).
|
|
46
|
+
|
|
1
47
|
## [1.9.314.0] - 2026-10-07
|
|
2
48
|
|
|
3
49
|
### Changed — engine sync (libleptris 1.9.314)
|
data/Rakefile
CHANGED
|
@@ -8,7 +8,7 @@ RSpec::Core::RakeTask.new(:spec)
|
|
|
8
8
|
# Pin for `rake compile` and the platform-gem builds. Keep in lockstep
|
|
9
9
|
# with .github/workflows/build.yml (which calls `rake compile`) and the
|
|
10
10
|
# CHANGELOG when libleptris releases.
|
|
11
|
-
LIBLEPTRIS_VERSION = "1.9.
|
|
11
|
+
LIBLEPTRIS_VERSION = "1.9.317"
|
|
12
12
|
# Vendored alongside libleptris for fn:normalize-unicode (TODO
|
|
13
13
|
# .restructure/20): built per platform with a RELOCATABLE @rpath
|
|
14
14
|
# install name, loaded by ffi.rb before libleptris so the
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
data/lib/leptris/version.rb
CHANGED
|
@@ -175,11 +175,15 @@ class Leptris::XML::Descriptor
|
|
|
175
175
|
case ns
|
|
176
176
|
when nil, :none then [Leptris::XML::FFI::PLAN_NS_NONE, nil]
|
|
177
177
|
when :any then [Leptris::XML::FFI::PLAN_NS_ANY, nil]
|
|
178
|
+
when :unqualified
|
|
179
|
+
# libleptris 1.9.317 (#1560): the UNWRITTEN spelling.
|
|
180
|
+
[Leptris::XML::FFI::PLAN_NS_UNQUALIFIED, nil]
|
|
178
181
|
when Hash
|
|
179
182
|
[Leptris::XML::FFI::PLAN_NS_EXACT,
|
|
180
183
|
anchor_string(anchors, ns.fetch(:exact))]
|
|
181
184
|
else
|
|
182
|
-
raise ArgumentError,
|
|
185
|
+
raise ArgumentError,
|
|
186
|
+
"ns must be :none, :any, :unqualified, or {exact: uri}"
|
|
183
187
|
end
|
|
184
188
|
ep[:ns_form] = ns_form
|
|
185
189
|
ep[:ns_uri] = ns_uri
|
|
@@ -230,10 +234,14 @@ class Leptris::XML::Descriptor
|
|
|
230
234
|
# defaults to NONE for backward compatibility. WILDCARD
|
|
231
235
|
# rows (#1552) read an explicit :ns through pad0 (an
|
|
232
236
|
# explicit :none filters no-namespace remainder alone;
|
|
233
|
-
# unset stays the catch-all ANY default).
|
|
237
|
+
# unset stays the catch-all ANY default). :unqualified
|
|
238
|
+
# (libleptris 1.9.317, #1560) matches the unwritten
|
|
239
|
+
# spelling on element rows — element-row only (attributes
|
|
240
|
+
# have no unprefixed namespace by XML rules).
|
|
234
241
|
cp[:ns_form] =
|
|
235
242
|
case row[:ns]
|
|
236
243
|
when :any then Leptris::XML::FFI::PLAN_NS_ANY
|
|
244
|
+
when :unqualified then Leptris::XML::FFI::PLAN_NS_UNQUALIFIED
|
|
237
245
|
when Hash then Leptris::XML::FFI::PLAN_NS_EXACT
|
|
238
246
|
else Leptris::XML::FFI::PLAN_NS_NONE
|
|
239
247
|
end
|
data/lib/leptris/xml/ffi.rb
CHANGED
|
@@ -380,6 +380,13 @@ module Leptris
|
|
|
380
380
|
PLAN_NS_NONE = 0
|
|
381
381
|
PLAN_NS_EXACT = 1
|
|
382
382
|
PLAN_NS_ANY = 2
|
|
383
|
+
# libleptris 1.9.317 (#1560): matches the UNWRITTEN spelling
|
|
384
|
+
# on element rows — no written prefix, regardless of the
|
|
385
|
+
# effective namespace URI (binds under both a namespace-less
|
|
386
|
+
# and a default-xmlns document; prefixed spellings never
|
|
387
|
+
# bind). Attributes have no unprefixed namespace by XML
|
|
388
|
+
# rules, so the form is element-row only.
|
|
389
|
+
PLAN_NS_UNQUALIFIED = 3
|
|
383
390
|
|
|
384
391
|
PLAN_VALUE_ELEMENT = 0
|
|
385
392
|
PLAN_VALUE_SCALAR = 1
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,75 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.317] - 2026-10-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Descriptor: unqualified-only ns form for child rows (#1560,
|
|
8
|
+
PR #1574).** `LEPTRIS_PLAN_NS_UNQUALIFIED = 3` matches the WRITTEN
|
|
9
|
+
spelling — a child row binds children with no written prefix
|
|
10
|
+
regardless of the effective namespace URI, and refuses prefixed
|
|
11
|
+
spellings. An unprefixed child binds under both a namespace-less
|
|
12
|
+
document and a default-`xmlns` document (`NS_NONE` drops the
|
|
13
|
+
latter); a `p:child` never binds (the `NS_ANY` superset
|
|
14
|
+
lutaml-model#932 had to settle for). The serialize -> re-parse ->
|
|
15
|
+
re-walk round-trip agrees on all three shapes because the form
|
|
16
|
+
keys on the spelling, not the URI: the serializer emits prefix
|
|
17
|
+
declarations only, so a default-ns child serializes bare and
|
|
18
|
+
still re-binds. Rust descriptor-ABI mirror shipped in the same
|
|
19
|
+
wave (leptris-rs#4).
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## [1.9.316] - 2026-10-07
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **Descriptor plan serializer: nested captures emit their own rows
|
|
28
|
+
(#1565, PR #1573).** A captured child's rows now resolve against the
|
|
29
|
+
CAPTURING row's `child_plan_index` plan instead of the plan that
|
|
30
|
+
walked it, so a child bound by a nested row (the `w:font`-in-
|
|
31
|
+
`w:fonts` shape) serializes its own attribute and child rows —
|
|
32
|
+
previously they resolved against the parent plan and silently
|
|
33
|
+
vanished. CONTENT rows inline their text at every nesting level.
|
|
34
|
+
The new nested-capture spec also exposed a 2 x 16B leak: the inner
|
|
35
|
+
walk's ns stamp was overwritten without freeing; fixed
|
|
36
|
+
free-before-overwrite (leaks gate clean).
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## [1.9.315] - 2026-10-07
|
|
41
|
+
|
|
42
|
+
### Added
|
|
43
|
+
|
|
44
|
+
- **Carrier-by-pointer node identity — QT3 fold-left-009/016
|
|
45
|
+
adopted (PR #1571).** Element members riding the higher-order
|
|
46
|
+
function argument channel keep their NODE POINTER (the \x03P
|
|
47
|
+
carrier), so node identity survives a string-typed channel:
|
|
48
|
+
`$foundSoFar intersect $this` dedups by node (fold-left-009's
|
|
49
|
+
distinct-nodes-stable), and fold results decode to real nodes so
|
|
50
|
+
`$result/text()` steps (fold-left-016's css-selector interpreter).
|
|
51
|
+
The fold suite runs 16/16 — both long-pinned cases adopted.
|
|
52
|
+
Pointers are eval-safe: constructor documents anchor on the
|
|
53
|
+
source tree (the #691 discipline). Along the way, three language
|
|
54
|
+
gaps closed: typeswitch arms accept `element(id)` /
|
|
55
|
+
`element(class)` NAME arguments with case-bound variables binding
|
|
56
|
+
node operands as nodes; keyword-named constructors parse
|
|
57
|
+
(`<div/>`, `<mod/>` — HTML tag names colliding with XQuery
|
|
58
|
+
operators); and single-element results ride the carrier instead
|
|
59
|
+
of collapsing to their string value.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **Pointer carriers only for real ELEMENT members.** The item
|
|
64
|
+
collector handed \x03P carriers to synthetic attribute and
|
|
65
|
+
namespace nodes too — heap-owned by the carrying nodeset, so the
|
|
66
|
+
carrier dangled once it was freed (AddressSanitizer
|
|
67
|
+
heap-use-after-free via fn:subsequence over an attribute step).
|
|
68
|
+
Real elements stay document-anchored for the evaluation;
|
|
69
|
+
attributes and namespaces keep the markup snapshot.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
3
73
|
## [1.9.314] - 2026-10-07
|
|
4
74
|
|
|
5
75
|
### Added
|
|
@@ -49,6 +49,12 @@ pub type PlanNsForm = u8;
|
|
|
49
49
|
pub const PLAN_NS_NONE: PlanNsForm = 0;
|
|
50
50
|
pub const PLAN_NS_EXACT: PlanNsForm = 1;
|
|
51
51
|
pub const PLAN_NS_ANY: PlanNsForm = 2;
|
|
52
|
+
/// #1560: matches the UNWRITTEN spelling — no written prefix,
|
|
53
|
+
/// regardless of the effective namespace URI. An unprefixed child
|
|
54
|
+
/// binds under both a namespace-less document and a default-xmlns
|
|
55
|
+
/// document; prefixed spellings never bind. Element rows only
|
|
56
|
+
/// (unprefixed attributes have no namespace by XML rules).
|
|
57
|
+
pub const PLAN_NS_UNQUALIFIED: PlanNsForm = 3;
|
|
52
58
|
|
|
53
59
|
/// Result node kinds (the value tree a walk returns).
|
|
54
60
|
pub type PlanValueKind = i32;
|
|
@@ -34,6 +34,7 @@ fn kind_and_flag_constants_match_engine_headers() {
|
|
|
34
34
|
assert_eq!(PLAN_NS_NONE, 0);
|
|
35
35
|
assert_eq!(PLAN_NS_EXACT, 1);
|
|
36
36
|
assert_eq!(PLAN_NS_ANY, 2);
|
|
37
|
+
assert_eq!(PLAN_NS_UNQUALIFIED, 3);
|
|
37
38
|
assert_eq!(PLAN_VALUE_ELEMENT, 0);
|
|
38
39
|
assert_eq!(PLAN_VALUE_SCALAR, 1);
|
|
39
40
|
assert_eq!(PLAN_VALUE_COLLECTION, 2);
|
|
@@ -119,16 +119,28 @@ static cli_result_t xquery_run(int argc, char** argv) {
|
|
|
119
119
|
cli_error("XQuery evaluation failed");
|
|
120
120
|
rc = CLI_ERROR_XPATH;
|
|
121
121
|
} else if (leptris_xpath_result_type(r) == LEPTRIS_XPATH_NODESET) {
|
|
122
|
+
/* carrier-by-pointer (fold-left-009/016): synth members may
|
|
123
|
+
* carry a node pointer — render its markup. */
|
|
124
|
+
extern void* xpath_carrier_node_of(const char* s);
|
|
122
125
|
size_t n = leptris_xpath_result_count(r);
|
|
123
126
|
for (size_t k = 0; k < n; k++) {
|
|
124
127
|
if (k) putchar(' ');
|
|
128
|
+
LeptrisElement el = NULL;
|
|
129
|
+
if (leptris_xpath_result_node_kind(r, k) ==
|
|
130
|
+
LEPTRIS_XPATH_NODE_ELEMENT) {
|
|
131
|
+
el = leptris_xpath_result_get(r, k);
|
|
132
|
+
} else {
|
|
133
|
+
const char* raw =
|
|
134
|
+
leptris_xpath_result_node_value(r, k);
|
|
135
|
+
if (raw && (unsigned char)raw[0] == 0x03 &&
|
|
136
|
+
raw[1] == 'P')
|
|
137
|
+
el = (LeptrisElement)xpath_carrier_node_of(raw);
|
|
138
|
+
}
|
|
125
139
|
/* Constructed elements (#181) are real nodes now — print
|
|
126
140
|
* their serialized markup, not the string-value (the
|
|
127
141
|
* pre-node ctor spelling printed markup too). */
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
char* m = leptris_element_serialize(
|
|
131
|
-
leptris_xpath_result_get(r, k), NULL);
|
|
142
|
+
if (el) {
|
|
143
|
+
char* m = leptris_element_serialize(el, NULL);
|
|
132
144
|
fputs(m ? m : "", stdout);
|
|
133
145
|
leptris_free_string(m);
|
|
134
146
|
} else {
|
|
@@ -96,7 +96,14 @@ typedef enum {
|
|
|
96
96
|
typedef enum {
|
|
97
97
|
LEPTRIS_PLAN_NS_NONE = 0, /* only elements with no namespace */
|
|
98
98
|
LEPTRIS_PLAN_NS_EXACT = 1, /* resolved namespace URI equals ns_uri */
|
|
99
|
-
LEPTRIS_PLAN_NS_ANY = 2
|
|
99
|
+
LEPTRIS_PLAN_NS_ANY = 2,
|
|
100
|
+
/* #1560: matches the UNWRITTEN spelling — children with no
|
|
101
|
+
* prefix, regardless of the effective namespace URI. An
|
|
102
|
+
* unprefixed child binds under both a namespace-less document
|
|
103
|
+
* and a default-xmlns document; prefixed spellings never bind.
|
|
104
|
+
* Unprefixed attributes have no namespace by XML rules, so
|
|
105
|
+
* this form only applies to element rows. */
|
|
106
|
+
LEPTRIS_PLAN_NS_UNQUALIFIED = 3
|
|
100
107
|
} LeptrisPlanNsForm;
|
|
101
108
|
|
|
102
109
|
typedef struct {
|
|
@@ -562,6 +562,11 @@ static int dp_ns_binds(LeptrisElement elem, uint8_t ns_form,
|
|
|
562
562
|
case LEPTRIS_PLAN_NS_ANY:
|
|
563
563
|
default:
|
|
564
564
|
return 1;
|
|
565
|
+
case LEPTRIS_PLAN_NS_UNQUALIFIED:
|
|
566
|
+
/* #1560: the WRITTEN spelling — no prefix, any
|
|
567
|
+
* effective URI (default-xmlns elements included);
|
|
568
|
+
* prefixed spellings never bind. */
|
|
569
|
+
return prefix == NULL;
|
|
565
570
|
}
|
|
566
571
|
}
|
|
567
572
|
|
|
@@ -852,6 +857,11 @@ static int dp_walk_children(LeptrisElement elem, const dp_plan* plan,
|
|
|
852
857
|
* back to the target plan's (NESTED matches with
|
|
853
858
|
* the target's form, #1115). */
|
|
854
859
|
if (row->ns_prefix) {
|
|
860
|
+
/* The inner walk's own #1551 stamp may already
|
|
861
|
+
* hold this pair — free before overwriting or
|
|
862
|
+
* the first strdup orphans. */
|
|
863
|
+
free(v->ns_prefix);
|
|
864
|
+
free(v->ns_uri);
|
|
855
865
|
v->ns_prefix = dp_strdup(row->ns_prefix);
|
|
856
866
|
v->ns_uri = dp_strdup(row->ns_uri ? row->ns_uri
|
|
857
867
|
: target->ns_uri);
|
|
@@ -1343,7 +1353,8 @@ static int dp_emit_value(const struct leptris_plan_result* v,
|
|
|
1343
1353
|
|
|
1344
1354
|
static int dp_emit_element(const struct leptris_plan_result* v,
|
|
1345
1355
|
const char* name, const LeptrisPlan pool,
|
|
1346
|
-
|
|
1356
|
+
const dp_plan* kids_container, dp_sb* b,
|
|
1357
|
+
const dp_decls* decls, int is_root) {
|
|
1347
1358
|
if (!dp_emit_element_open(v, name, b, decls, is_root)) return 0;
|
|
1348
1359
|
const struct leptris_plan_result** kids = NULL;
|
|
1349
1360
|
size_t nk = 0, cap = 0;
|
|
@@ -1360,11 +1371,14 @@ static int dp_emit_element(const struct leptris_plan_result* v,
|
|
|
1360
1371
|
return 0;
|
|
1361
1372
|
}
|
|
1362
1373
|
qsort((void*)kids, nk, sizeof(*kids), dp_kid_pos_cmp);
|
|
1363
|
-
|
|
1364
|
-
|
|
1374
|
+
/* #1565: kid ROWS live in the plan that WALKED this element —
|
|
1375
|
+
* container_plan is the plan whose row CAPTURED it (the
|
|
1376
|
+
* parent), so a nested member resolved its kids' rows against
|
|
1377
|
+
* the parent's rows and a CONTENT row serialized as an
|
|
1378
|
+
* empty-named wrapper. */
|
|
1365
1379
|
int ok = 1;
|
|
1366
1380
|
for (size_t i = 0; i < nk && ok; i++)
|
|
1367
|
-
ok = dp_emit_value(kids[i],
|
|
1381
|
+
ok = dp_emit_value(kids[i], kids_container, pool, b, NULL, 0);
|
|
1368
1382
|
free((void*)kids);
|
|
1369
1383
|
if (!ok) return 0;
|
|
1370
1384
|
return dp_sb_puts(b, "</") && dp_sb_puts(b, name) &&
|
|
@@ -1377,6 +1391,16 @@ static int dp_emit_value(const struct leptris_plan_result* v,
|
|
|
1377
1391
|
if (v->kind == LEPTRIS_PLAN_VALUE_RAW)
|
|
1378
1392
|
return dp_sb_puts(b, v->str ? v->str : "");
|
|
1379
1393
|
if (v->kind == LEPTRIS_PLAN_VALUE_ELEMENT) {
|
|
1394
|
+
/* #1565: a walk member captured by a CONTENT row is an
|
|
1395
|
+
* unnamed element — the row lookup must precede the name
|
|
1396
|
+
* resolution, or the empty wire name becomes the wrapper
|
|
1397
|
+
* (<w:item><>text</></w:item>). Content emits inline text
|
|
1398
|
+
* at every nesting level. */
|
|
1399
|
+
if (container && v->row_index != UINT32_MAX &&
|
|
1400
|
+
v->row_index < container->child_count &&
|
|
1401
|
+
container->child_plans[v->row_index].kind ==
|
|
1402
|
+
LEPTRIS_PLAN_KIND_CONTENT)
|
|
1403
|
+
return dp_sb_escape(b, v->str ? v->str : "", 0);
|
|
1380
1404
|
/* The element's own binding was stamped at walk time; a
|
|
1381
1405
|
* walk member without one falls back to its plan's. */
|
|
1382
1406
|
char* name = NULL;
|
|
@@ -1397,7 +1421,24 @@ static int dp_emit_value(const struct leptris_plan_result* v,
|
|
|
1397
1421
|
else
|
|
1398
1422
|
name = dp_strdup(local ? local : "");
|
|
1399
1423
|
if (!name) return 0;
|
|
1400
|
-
|
|
1424
|
+
/* Kids resolve their rows against the plan that WALKED this
|
|
1425
|
+
* element: the capturing row's child_plan_index when nested,
|
|
1426
|
+
* else container_plan (the walk root is its own plan). */
|
|
1427
|
+
const struct leptris_plan* poolp =
|
|
1428
|
+
(const struct leptris_plan*)pool;
|
|
1429
|
+
const dp_plan* kids_container =
|
|
1430
|
+
&poolp->plans[v->container_plan];
|
|
1431
|
+
if (container && v->row_index != UINT32_MAX &&
|
|
1432
|
+
v->row_index < container->child_count) {
|
|
1433
|
+
const leptris_child_plan* cap =
|
|
1434
|
+
&container->child_plans[v->row_index];
|
|
1435
|
+
if (cap->child_plan_index >= 0 &&
|
|
1436
|
+
(uint32_t)cap->child_plan_index < poolp->plan_count)
|
|
1437
|
+
kids_container =
|
|
1438
|
+
&poolp->plans[cap->child_plan_index];
|
|
1439
|
+
}
|
|
1440
|
+
int ok = dp_emit_element(v, name, pool, kids_container, b,
|
|
1441
|
+
decls, is_root);
|
|
1401
1442
|
free(name);
|
|
1402
1443
|
return ok;
|
|
1403
1444
|
}
|
|
@@ -147,6 +147,8 @@ extern char* get_node_text(void* node);
|
|
|
147
147
|
/* functions_ext31.c — #691 source-document anchoring (#181 ctor docs). */
|
|
148
148
|
extern int xq_anchor_on_source(XPathContext* ctx,
|
|
149
149
|
struct leptris_document* anchored);
|
|
150
|
+
/* carrier-by-pointer decode (fold-left-009/016). */
|
|
151
|
+
extern void* xpath_carrier_node_of(const char* s);
|
|
150
152
|
|
|
151
153
|
/* #181: a materialized ctor node splices its MARKUP into a
|
|
152
154
|
* parent's serialized content — get_node_text would yield only its
|
|
@@ -791,6 +793,7 @@ static int op_relational_cmp(XPathOperatorType op, double a, double b) {
|
|
|
791
793
|
* `instance of` and XQuery typeswitch. */
|
|
792
794
|
int xpath_result_matches_type(struct leptris_xpath_result* v,
|
|
793
795
|
const char* base) {
|
|
796
|
+
extern const char* leptris_element_get_name(LeptrisElement e);
|
|
794
797
|
int is_string_ty = strcmp(base, "xs:string") == 0 ||
|
|
795
798
|
strcmp(base, "xs:anyURI") == 0 ||
|
|
796
799
|
strncmp(base, "xs:date", 7) == 0 ||
|
|
@@ -801,7 +804,9 @@ int xpath_result_matches_type(struct leptris_xpath_result* v,
|
|
|
801
804
|
strcmp(base, "node()") != 0 &&
|
|
802
805
|
strcmp(base, "item()") != 0 &&
|
|
803
806
|
strcmp(base, "element()") != 0 &&
|
|
807
|
+
strncmp(base, "element(", 8) != 0 &&
|
|
804
808
|
strcmp(base, "attribute()") != 0 &&
|
|
809
|
+
strncmp(base, "attribute(", 10) != 0 &&
|
|
805
810
|
strcmp(base, "text()") != 0 &&
|
|
806
811
|
strcmp(base, "comment()") != 0 &&
|
|
807
812
|
strcmp(base, "processing-instruction()") != 0;
|
|
@@ -825,6 +830,22 @@ int xpath_result_matches_type(struct leptris_xpath_result* v,
|
|
|
825
830
|
if (!(tag >= 0 && tag <= 7)) return 0;
|
|
826
831
|
} else if (strcmp(base, "element()") == 0) {
|
|
827
832
|
if (tag != (int)LEPTRIS_NODE_ELEMENT) return 0;
|
|
833
|
+
} else if (strncmp(base, "element(", 8) == 0) {
|
|
834
|
+
/* ElementTest with a NAME: element(id) matches
|
|
835
|
+
* local-name "id" (fold-left-016 selector arms);
|
|
836
|
+
* element(*) is any element. */
|
|
837
|
+
size_t bl = strlen(base);
|
|
838
|
+
if (tag != (int)LEPTRIS_NODE_ELEMENT || bl < 10 ||
|
|
839
|
+
base[bl - 1] != ')')
|
|
840
|
+
return 0;
|
|
841
|
+
if (!(bl == 11 && base[9] == '*')) {
|
|
842
|
+
const char* en =
|
|
843
|
+
leptris_element_get_name((LeptrisElement)n);
|
|
844
|
+
size_t nl = bl - 9;
|
|
845
|
+
if (!en || strlen(en) != nl ||
|
|
846
|
+
strncmp(en, base + 8, nl) != 0)
|
|
847
|
+
return 0;
|
|
848
|
+
}
|
|
828
849
|
} else if (strcmp(base, "attribute()") == 0) {
|
|
829
850
|
if (tag != (int)LEPTRIS_NODE_ATTRIBUTE) return 0;
|
|
830
851
|
} else if (strcmp(base, "text()") == 0) {
|
|
@@ -1889,9 +1910,18 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
1889
1910
|
if (sv && sv[0] == '\x03' && sv[1] == 'M' &&
|
|
1890
1911
|
xpath_array_members_of(sv, &mem, &mn) && mn) {
|
|
1891
1912
|
for (size_t j = 0; j < mn; j++) {
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1913
|
+
/* carrier-by-pointer: element members
|
|
1914
|
+
* decode to their NODE (identity —
|
|
1915
|
+
* fold-left-009/016). */
|
|
1916
|
+
void* cn = xpath_carrier_node_of(mem[j]);
|
|
1917
|
+
if (cn) {
|
|
1918
|
+
xpath_nodeset_add(one,
|
|
1919
|
+
(LeptrisNode*)cn);
|
|
1920
|
+
} else {
|
|
1921
|
+
XPathTextNode* tn = synth_text(
|
|
1922
|
+
mem[j], strlen(mem[j]));
|
|
1923
|
+
if (tn) xpath_nodeset_add(one, tn);
|
|
1924
|
+
}
|
|
1895
1925
|
free(mem[j]);
|
|
1896
1926
|
}
|
|
1897
1927
|
free(mem);
|
|
@@ -1900,11 +1930,19 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
1900
1930
|
sv[1] == 'E') {
|
|
1901
1931
|
free(sv);
|
|
1902
1932
|
} else {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1933
|
+
void* cn =
|
|
1934
|
+
xpath_carrier_node_of(sv ? sv : "");
|
|
1935
|
+
if (cn) {
|
|
1936
|
+
xpath_nodeset_add(one, (LeptrisNode*)cn);
|
|
1937
|
+
} else {
|
|
1938
|
+
XPathTextNode* tn =
|
|
1939
|
+
synth_text(sv ? sv : "",
|
|
1940
|
+
sv ? strlen(sv) : 0);
|
|
1941
|
+
free(sv);
|
|
1942
|
+
if (tn) xpath_nodeset_add(one, tn);
|
|
1943
|
+
sv = NULL;
|
|
1944
|
+
}
|
|
1945
|
+
if (sv) free(sv);
|
|
1908
1946
|
}
|
|
1909
1947
|
}
|
|
1910
1948
|
XPathVariable* ex = xpath_variable_set_get(
|
|
@@ -2045,11 +2083,26 @@ struct leptris_xpath_result* evaluate_operator(XPathContext* ctx,
|
|
|
2045
2083
|
XPathNodeSet* one = xpath_nodeset_new();
|
|
2046
2084
|
if (one) {
|
|
2047
2085
|
one->owns_synthetic_text = 1;
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2086
|
+
if (v->type == XPATH_RESULT_NODESET &&
|
|
2087
|
+
v->value.nodeset_value &&
|
|
2088
|
+
v->value.nodeset_value->count > 0) {
|
|
2089
|
+
/* Node operands bind AS NODES — the arm
|
|
2090
|
+
* body steps axes on them
|
|
2091
|
+
* (fold-left-016: $a/text()); a
|
|
2092
|
+
* string-value synth has no children. */
|
|
2093
|
+
for (size_t xi = 0;
|
|
2094
|
+
xi < v->value.nodeset_value->count;
|
|
2095
|
+
xi++)
|
|
2096
|
+
xpath_nodeset_add(
|
|
2097
|
+
one,
|
|
2098
|
+
v->value.nodeset_value->nodes[xi]);
|
|
2099
|
+
} else {
|
|
2100
|
+
const char* bindv =
|
|
2101
|
+
marked ? marked : (vs_ ? vs_ : "");
|
|
2102
|
+
XPathTextNode* tn = xpath_synth_text(
|
|
2103
|
+
bindv, strlen(bindv));
|
|
2104
|
+
if (tn) xpath_nodeset_add(one, tn);
|
|
2105
|
+
}
|
|
2053
2106
|
XPathVariable* var = xpath_variable_set_add(
|
|
2054
2107
|
vs, vn, XPATH_VAR_TYPE_NODE_SET);
|
|
2055
2108
|
if (var)
|
|
@@ -4377,6 +4430,17 @@ struct leptris_xpath_result* xpath_call_function_item(
|
|
|
4377
4430
|
* typed/untyped carrier marks the M members
|
|
4378
4431
|
* now carry. */
|
|
4379
4432
|
const char* mv = mem[j];
|
|
4433
|
+
void* cn = xpath_carrier_node_of(mv);
|
|
4434
|
+
if (cn) {
|
|
4435
|
+
/* Node members atomize to their string
|
|
4436
|
+
* value (XDM function conversion). */
|
|
4437
|
+
const char* sv =
|
|
4438
|
+
leptris_element_text((LeptrisElement)cn);
|
|
4439
|
+
kids[j].value =
|
|
4440
|
+
(char*)(sv ? sv : "");
|
|
4441
|
+
karr[j] = &kids[j];
|
|
4442
|
+
continue;
|
|
4443
|
+
}
|
|
4380
4444
|
if (mv && mv[0] == '\x03' && mv[1] &&
|
|
4381
4445
|
mv[1] != 'A' && mv[1] != 'M')
|
|
4382
4446
|
mv += 2;
|
|
@@ -4408,8 +4472,16 @@ struct leptris_xpath_result* xpath_call_function_item(
|
|
|
4408
4472
|
else if (argv[i] && argv[i][0] == '\x03' &&
|
|
4409
4473
|
argv[i][1] == 'E')
|
|
4410
4474
|
argn[i].value = "";
|
|
4411
|
-
else
|
|
4412
|
-
|
|
4475
|
+
else {
|
|
4476
|
+
void* cn = xpath_carrier_node_of(argv[i]);
|
|
4477
|
+
if (cn) {
|
|
4478
|
+
const char* sv = leptris_element_text(
|
|
4479
|
+
(LeptrisElement)cn);
|
|
4480
|
+
argn[i].value = (char*)(sv ? sv : "");
|
|
4481
|
+
} else {
|
|
4482
|
+
argn[i].value = argv[i];
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4413
4485
|
}
|
|
4414
4486
|
child_arr[i] = &argn[i];
|
|
4415
4487
|
}
|
|
@@ -4476,17 +4548,30 @@ struct leptris_xpath_result* xpath_call_function_item(
|
|
|
4476
4548
|
if (argv[ai][0] == '\x03' && argv[ai][1] == 'M' &&
|
|
4477
4549
|
xpath_array_members_of(argv[ai], &mem, &mn) && mn) {
|
|
4478
4550
|
for (size_t j = 0; j < mn; j++) {
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4551
|
+
/* carrier-by-pointer: element members decode to
|
|
4552
|
+
* their NODE (identity — fold-left-009/016);
|
|
4553
|
+
* borrowed, not synth. */
|
|
4554
|
+
void* cn = xpath_carrier_node_of(mem[j]);
|
|
4555
|
+
if (cn) {
|
|
4556
|
+
xpath_nodeset_add(one, (LeptrisNode*)cn);
|
|
4557
|
+
} else {
|
|
4558
|
+
XPathTextNode* tn =
|
|
4559
|
+
synth_text(mem[j], strlen(mem[j]));
|
|
4560
|
+
if (tn) xpath_nodeset_add(one, tn);
|
|
4561
|
+
}
|
|
4482
4562
|
free(mem[j]);
|
|
4483
4563
|
}
|
|
4484
4564
|
free(mem);
|
|
4485
4565
|
} else if (!(argv[ai][0] == '\x03' &&
|
|
4486
4566
|
argv[ai][1] == 'E')) {
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4567
|
+
void* cn = xpath_carrier_node_of(argv[ai]);
|
|
4568
|
+
if (cn) {
|
|
4569
|
+
xpath_nodeset_add(one, (LeptrisNode*)cn);
|
|
4570
|
+
} else {
|
|
4571
|
+
XPathTextNode* tn =
|
|
4572
|
+
synth_text(argv[ai], strlen(argv[ai]));
|
|
4573
|
+
if (tn) xpath_nodeset_add(one, tn);
|
|
4574
|
+
}
|
|
4490
4575
|
}
|
|
4491
4576
|
/* "\x03E": the empty sequence binds NO nodes, so
|
|
4492
4577
|
* empty($p) is true (fold zero accumulators). */
|
|
@@ -1120,6 +1120,26 @@ static size_t de_collect(struct leptris_xpath_result* r, DeItem* out,
|
|
|
1120
1120
|
if (c[0] == '\x03' && c[1] == 'B') {
|
|
1121
1121
|
out[i].kind = 3;
|
|
1122
1122
|
out[i].num = c[2] == 't' ? 1 : 0;
|
|
1123
|
+
} else if (c[0] == '\x03' && c[1] == 'P') {
|
|
1124
|
+
/* carrier-by-pointer: decode to the NODE's
|
|
1125
|
+
* MARKUP so both sides compare as markup
|
|
1126
|
+
* strings (real elements collect as markup in
|
|
1127
|
+
* the element branch below); the markup compare
|
|
1128
|
+
* reparses into de_node_equal. */
|
|
1129
|
+
extern void* xpath_carrier_node_of(const char*);
|
|
1130
|
+
extern char* ctor_member_markup(void* node);
|
|
1131
|
+
void* cn = xpath_carrier_node_of(c);
|
|
1132
|
+
if (cn) {
|
|
1133
|
+
out[i].kind = 2;
|
|
1134
|
+
out[i].str = ctor_member_markup(cn);
|
|
1135
|
+
if (!out[i].str) {
|
|
1136
|
+
out[i].kind = 0;
|
|
1137
|
+
out[i].node = cn;
|
|
1138
|
+
}
|
|
1139
|
+
} else {
|
|
1140
|
+
out[i].kind = 2;
|
|
1141
|
+
out[i].borrow = c;
|
|
1142
|
+
}
|
|
1123
1143
|
} else if (c[0] == '\x03' && c[1] == 'A') {
|
|
1124
1144
|
/* attribute ctor carrier: kind 4 compares
|
|
1125
1145
|
* name + value (K2-SeqDeepEqualFunc-25/31/32) */
|