leptris 1.9.382.0-ppc64le-linux → 1.9.384.0-ppc64le-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 +4 -4
- data/Rakefile +1 -1
- data/lib/leptris/version.rb +1 -1
- data/lib/libleptris.so +0 -0
- data/vendor-src/README.md +1 -1
- data/vendor-src/libleptris/CHANGELOG.md +39 -0
- data/vendor-src/libleptris/CMakeLists.txt +1 -1
- data/vendor-src/libleptris/src/leptris/rng/rng_regex.c +193 -39
- data/vendor-src/libleptris/src/leptris/xpath/vm.c +8 -0
- data/vendor-src/libleptris/src/leptris/xsd/compile.c +27 -10
- data/vendor-src/libleptris/src/leptris/xsd/content_model.c +5 -0
- data/vendor-src/libleptris/src/leptris/xsd/validate.c +16 -8
- data/vendor-src/libleptris/test/CMakeLists.txt +11 -0
- data/vendor-src/libleptris/test/rng/test_rng.cpp +90 -0
- data/vendor-src/libleptris/test/xpath/test_bytecode_vm.cpp +36 -0
- data/vendor-src/libleptris/test/xsd/test_xsd_compile.cpp +166 -0
- data/vendor-src/libleptris/vcpkg.json +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9fa69448585e5eb841693914222c0c5b3d74552b4abb005df923aa9e1978c503
|
|
4
|
+
data.tar.gz: 95e3c776c79d0854549f0dc125e8ba1bc3ba39e4a1d381773cbb923263b91e75
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1196cae8b8550125559c1f0a97aff3d3ce96131f92bfc3c1371ebb2ab1a32fe81865ba1e7f586b181543b5aa9313e17e770ca1dadb2ebe7a3980885a1acfa434
|
|
7
|
+
data.tar.gz: 897aa60336bf4f2d5d4b1fd1c3da85c6a150c70357524d6995266c1a5c1b471a2f9399aa261835dc33becb2a0c3ce5db09bb771fba56f0d67b47edf0f8c9714c
|
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.384"
|
|
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
|
data/lib/leptris/version.rb
CHANGED
data/lib/libleptris.so
CHANGED
|
Binary file
|
data/vendor-src/README.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.9.384] - 2026-10-27
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- XPath: a parent step after an attribute step (`./@id/..`) yields
|
|
8
|
+
the OWNING element (XPath 1.0 section 2.2) — the VM parent axis
|
|
9
|
+
handled namespace/element/text contexts but attributes fell
|
|
10
|
+
through to the child-tree parent link (NULL for attributes), so
|
|
11
|
+
the step came back empty. Metanorma-standoc's SVG id
|
|
12
|
+
disambiguation drives exactly this shape and consumed the wrong
|
|
13
|
+
results downstream (#1867; the wrong-answer half of #1866).
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [1.9.383] - 2026-10-26
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- XSD: an empty instance no longer bypasses the content model —
|
|
22
|
+
`<note/>` validated clean against a required-particle sequence
|
|
23
|
+
(#1860; found by the moxml#460 facade tests).
|
|
24
|
+
- XSD: substitution-group members on type-less globals are captured
|
|
25
|
+
(particlesDc001) and a complexContent RESTRICTION no longer
|
|
26
|
+
defers its base — the fold prepended the base's model and
|
|
27
|
+
demanded the base content next to the derived content (#1861;
|
|
28
|
+
moxml#466). modelGroups sweep 145/145 expected-valid.
|
|
29
|
+
- XSD: an `xs:all` with `minOccurs="0"` may be absent — an empty
|
|
30
|
+
instance validates even when the particles carry min=1 (#1863;
|
|
31
|
+
mgAa003/mgD013).
|
|
32
|
+
- XSD regex (moxml#464): `charClassSub` is codepoint-exact (ranges,
|
|
33
|
+
blocks and categories in the subtrahend now subtract); `\d` is
|
|
34
|
+
the Unicode Nd category; complement escapes (`\D \W \S \I \C`)
|
|
35
|
+
are exact over categories; `\P` works inside classes; an empty
|
|
36
|
+
pattern branch matches only the empty string; class members and
|
|
37
|
+
range endpoints accept escaped `]`/`}` and astral characters.
|
|
38
|
+
Regex_w3c expected-valid sweep: 98 -> 17 rejections.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
3
42
|
## [1.9.382] - 2026-10-26
|
|
4
43
|
|
|
5
44
|
### Added
|
|
@@ -55,6 +55,21 @@ typedef struct RreNode {
|
|
|
55
55
|
* LEPTRIS_HAS_UTF8PROC (without it the escapes fail loudly). */
|
|
56
56
|
unsigned cat_set;
|
|
57
57
|
unsigned cat_mask;
|
|
58
|
+
/* cat_strict (moxml#464): a class-internal COMPLEMENT builtin
|
|
59
|
+
* ([\I], [\D]) needs bitmap AND cats to agree — the normal
|
|
60
|
+
* union can't express "not in the set" once the bitmap holds
|
|
61
|
+
* the complement's high bytes (U+00AA is in ~ASCII-bits but
|
|
62
|
+
* excluded by the category). */
|
|
63
|
+
unsigned cat_strict;
|
|
64
|
+
/* XSD charClassSub: the bracketed subtrahend (moxml#464). The
|
|
65
|
+
* node is consulted at match time so its ranges, blocks and
|
|
66
|
+
* categories all subtract — the old byte-bitmap merge ignored
|
|
67
|
+
* them. */
|
|
68
|
+
struct RreNode* subtract;
|
|
69
|
+
/* in-class \P member (moxml#464): the class matches the
|
|
70
|
+
* positive members UNIONED WITH the complement of this set —
|
|
71
|
+
* [\P{L}*] = not-L OR '*'. Class negation flips the union. */
|
|
72
|
+
struct RreNode* negm;
|
|
58
73
|
int neg;
|
|
59
74
|
int lo, hi; /* RRE_REP bounds; hi < 0 = unbounded */
|
|
60
75
|
} RreNode;
|
|
@@ -77,6 +92,8 @@ static void node_free(RreNode* n) {
|
|
|
77
92
|
if (!n) return;
|
|
78
93
|
node_free(n->l);
|
|
79
94
|
node_free(n->r);
|
|
95
|
+
node_free(n->subtract);
|
|
96
|
+
node_free(n->negm);
|
|
80
97
|
free(n);
|
|
81
98
|
}
|
|
82
99
|
|
|
@@ -88,6 +105,13 @@ static void cls_add_builtin(RreNode* n, char c) {
|
|
|
88
105
|
switch (c) {
|
|
89
106
|
case 'd':
|
|
90
107
|
for (int i = '0'; i <= '9'; i++) cls_add(n, (unsigned char)i);
|
|
108
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
109
|
+
/* XSD \d = XML decimal = Nd: Arabic-Indic and the
|
|
110
|
+
* other unicode digit families are \\d too
|
|
111
|
+
* (moxml#464, the reZ/reC \d rows) */
|
|
112
|
+
n->cat_set = 1;
|
|
113
|
+
n->cat_mask |= 1u << UTF8PROC_CATEGORY_ND;
|
|
114
|
+
#endif
|
|
91
115
|
break;
|
|
92
116
|
case 'w':
|
|
93
117
|
for (int i = '0'; i <= '9'; i++) cls_add(n, (unsigned char)i);
|
|
@@ -108,9 +132,21 @@ static void cls_add_builtin(RreNode* n, char c) {
|
|
|
108
132
|
for (int i = 'A'; i <= 'Z'; i++) cls_add(n, (unsigned char)i);
|
|
109
133
|
cls_add(n, '_');
|
|
110
134
|
cls_add(n, ':');
|
|
135
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
136
|
+
/* letters above the bitmap: the old 0x80-0xFF blanket
|
|
137
|
+
* also accepted symbols like U+00D7 (moxml#464) */
|
|
138
|
+
n->cat_set = 1;
|
|
139
|
+
n->cat_mask = (1u << UTF8PROC_CATEGORY_LU) |
|
|
140
|
+
(1u << UTF8PROC_CATEGORY_LL) |
|
|
141
|
+
(1u << UTF8PROC_CATEGORY_LT) |
|
|
142
|
+
(1u << UTF8PROC_CATEGORY_LM) |
|
|
143
|
+
(1u << UTF8PROC_CATEGORY_LO) |
|
|
144
|
+
(1u << UTF8PROC_CATEGORY_NL);
|
|
145
|
+
#else
|
|
111
146
|
for (int i = 0x80; i < 0x100; i++) cls_add(n, (unsigned char)i);
|
|
147
|
+
#endif
|
|
112
148
|
break;
|
|
113
|
-
case 'c': /* XML NameChar = \i + digits . - */
|
|
149
|
+
case 'c': /* XML NameChar = \i + digits . - + combining */
|
|
114
150
|
for (int i = 'a'; i <= 'z'; i++) cls_add(n, (unsigned char)i);
|
|
115
151
|
for (int i = 'A'; i <= 'Z'; i++) cls_add(n, (unsigned char)i);
|
|
116
152
|
for (int i = '0'; i <= '9'; i++) cls_add(n, (unsigned char)i);
|
|
@@ -118,18 +154,76 @@ static void cls_add_builtin(RreNode* n, char c) {
|
|
|
118
154
|
cls_add(n, ':');
|
|
119
155
|
cls_add(n, '.');
|
|
120
156
|
cls_add(n, '-');
|
|
157
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
158
|
+
n->cat_set = 1;
|
|
159
|
+
n->cat_mask = (1u << UTF8PROC_CATEGORY_LU) |
|
|
160
|
+
(1u << UTF8PROC_CATEGORY_LL) |
|
|
161
|
+
(1u << UTF8PROC_CATEGORY_LT) |
|
|
162
|
+
(1u << UTF8PROC_CATEGORY_LM) |
|
|
163
|
+
(1u << UTF8PROC_CATEGORY_LO) |
|
|
164
|
+
(1u << UTF8PROC_CATEGORY_NL) |
|
|
165
|
+
(1u << UTF8PROC_CATEGORY_ND) |
|
|
166
|
+
(1u << UTF8PROC_CATEGORY_MN) |
|
|
167
|
+
(1u << UTF8PROC_CATEGORY_MC);
|
|
168
|
+
#else
|
|
121
169
|
for (int i = 0x80; i < 0x100; i++) cls_add(n, (unsigned char)i);
|
|
170
|
+
#endif
|
|
122
171
|
break;
|
|
123
172
|
default:
|
|
124
173
|
break;
|
|
125
174
|
}
|
|
126
175
|
}
|
|
127
176
|
|
|
177
|
+
static unsigned rre_decode_cp(const char* s, size_t avail, int* len);
|
|
178
|
+
|
|
128
179
|
static int cls_has(const RreNode* n, unsigned char c) {
|
|
129
180
|
int hit = (n->cls[c >> 3] >> (c & 7)) & 1;
|
|
130
181
|
return n->neg ? !hit : hit;
|
|
131
182
|
}
|
|
132
183
|
|
|
184
|
+
/* One class MEMBER as a codepoint: singleCharEsc punctuation and
|
|
185
|
+
* control escapes, otherwise the raw (multi-byte) character
|
|
186
|
+
* (moxml#464: [\]} ranges and astral char-ref members). */
|
|
187
|
+
static int cls_read_cp(RreParser* ps, unsigned* out) {
|
|
188
|
+
if (*ps->p == '\\') {
|
|
189
|
+
char e = ps->p[1];
|
|
190
|
+
if (!e) return 0;
|
|
191
|
+
ps->p += 2;
|
|
192
|
+
switch (e) {
|
|
193
|
+
case 't': *out = '\t'; return 1;
|
|
194
|
+
case 'n': *out = '\n'; return 1;
|
|
195
|
+
case 'r': *out = '\r'; return 1;
|
|
196
|
+
case '\\': case '.': case '-': case '^':
|
|
197
|
+
case '?': case '*': case '+': case '(': case ')':
|
|
198
|
+
case '[': case ']': case '{': case '}': case '|':
|
|
199
|
+
*out = (unsigned char)e; return 1;
|
|
200
|
+
default: return 0; /* classes (\d...) are not members */
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (((unsigned char)*ps->p) < 0x80) {
|
|
204
|
+
*out = (unsigned char)*ps->p++;
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
int len = 0;
|
|
208
|
+
unsigned cp = rre_decode_cp(ps->p, strlen(ps->p), &len);
|
|
209
|
+
if (!len) return 0;
|
|
210
|
+
ps->p += len;
|
|
211
|
+
*out = cp;
|
|
212
|
+
return 1;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static void cls_add_cp(RreNode* n, unsigned cp) {
|
|
216
|
+
if (cp < 0x100) {
|
|
217
|
+
cls_add(n, (unsigned char)cp);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (n->uni_n < 4) {
|
|
221
|
+
n->uni_lo[n->uni_n] = cp;
|
|
222
|
+
n->uni_hi[n->uni_n] = cp;
|
|
223
|
+
n->uni_n++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
133
227
|
/* Codepoint form (moxml#464): bytes 0x00-0xFF check the bitmap,
|
|
134
228
|
* higher codepoints check the unicode ranges; neg flips the
|
|
135
229
|
* combined result. */
|
|
@@ -147,14 +241,19 @@ static int cls_has_cp(const RreNode* n, unsigned cp) {
|
|
|
147
241
|
}
|
|
148
242
|
}
|
|
149
243
|
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
150
|
-
if (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
244
|
+
if (n->cat_set) {
|
|
245
|
+
int c = (n->cat_mask &
|
|
246
|
+
(1u << utf8proc_get_property((utf8proc_int32_t)cp)
|
|
247
|
+
->category)) != 0;
|
|
248
|
+
hit = n->cat_strict ? (hit && c) : (hit || c);
|
|
154
249
|
}
|
|
155
250
|
#else
|
|
156
251
|
(void)0;
|
|
157
252
|
#endif
|
|
253
|
+
if (n->negm)
|
|
254
|
+
hit = hit || !cls_has_cp(n->negm, cp);
|
|
255
|
+
if (hit && n->subtract)
|
|
256
|
+
hit = !cls_has_cp(n->subtract, cp);
|
|
158
257
|
return n->neg ? !hit : hit;
|
|
159
258
|
}
|
|
160
259
|
|
|
@@ -443,7 +542,8 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
443
542
|
int first = 1;
|
|
444
543
|
while (*ps->p && (*ps->p != ']' || first)) {
|
|
445
544
|
first = 0;
|
|
446
|
-
if (*ps->p == '\\' && ps->p[1] == 'p'
|
|
545
|
+
if (*ps->p == '\\' && (ps->p[1] == 'p' || ps->p[1] == 'P') &&
|
|
546
|
+
ps->p[2] == '{') {
|
|
447
547
|
/* XSD property classes for the Latin-1 byte world —
|
|
448
548
|
* the byte-class matcher covers these exactly
|
|
449
549
|
* (leptris-ruby#434, ST_ContentType). Other blocks
|
|
@@ -456,6 +556,12 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
456
556
|
return NULL;
|
|
457
557
|
}
|
|
458
558
|
size_t len = (size_t)(close - ps->p);
|
|
559
|
+
char pc = ps->p[-2]; /* 'p' or 'P' */
|
|
560
|
+
RreNode* tmp = node_new(RRE_CLASS);
|
|
561
|
+
if (!tmp) {
|
|
562
|
+
node_free(n);
|
|
563
|
+
return NULL;
|
|
564
|
+
}
|
|
459
565
|
unsigned lo = 0, hi = 0;
|
|
460
566
|
int hit = rre_block_range(ps->p, len, &lo, &hi);
|
|
461
567
|
if (!hit && len == 2 && ps->p[0] == 'C' && ps->p[1] == 'c') {
|
|
@@ -463,7 +569,8 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
463
569
|
hit = 1;
|
|
464
570
|
}
|
|
465
571
|
if (hit) {
|
|
466
|
-
if (!cls_add_block(
|
|
572
|
+
if (!cls_add_block(tmp, lo, hi)) {
|
|
573
|
+
node_free(tmp);
|
|
467
574
|
node_free(n);
|
|
468
575
|
ps->bad = 1;
|
|
469
576
|
return NULL;
|
|
@@ -474,24 +581,47 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
474
581
|
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
475
582
|
unsigned mask = 0;
|
|
476
583
|
if (!rre_category_mask(ps->p, len, &mask)) {
|
|
584
|
+
node_free(tmp);
|
|
477
585
|
node_free(n);
|
|
478
586
|
ps->bad = 1;
|
|
479
587
|
return NULL;
|
|
480
588
|
}
|
|
481
|
-
|
|
482
|
-
|
|
589
|
+
tmp->cat_set = 1;
|
|
590
|
+
tmp->cat_mask |= mask;
|
|
483
591
|
#else
|
|
592
|
+
node_free(tmp);
|
|
484
593
|
node_free(n);
|
|
485
594
|
ps->bad = 1;
|
|
486
595
|
return NULL;
|
|
487
596
|
#endif
|
|
488
597
|
}
|
|
489
598
|
if (len == 2 && ps->p[0] == 'C' && ps->p[1] == 'c') {
|
|
490
|
-
cls_add(
|
|
599
|
+
cls_add(tmp, 0x7F);
|
|
491
600
|
for (int i = 0x80; i <= 0x9F; i++)
|
|
492
|
-
cls_add(
|
|
601
|
+
cls_add(tmp, (unsigned char)i);
|
|
493
602
|
}
|
|
494
603
|
ps->p = close + 1;
|
|
604
|
+
/* in-class \P (moxml#464, reF43/reK4): the member is
|
|
605
|
+
* the complement of the parsed set — stored as negm and
|
|
606
|
+
* unioned at match time. A second \P member degrades to
|
|
607
|
+
* the positive spelling (single negm slot). */
|
|
608
|
+
if (pc == 'P' && !n->negm) {
|
|
609
|
+
n->negm = tmp;
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
for (int i = 0; i < 256; i++)
|
|
613
|
+
if ((tmp->cls[i >> 3] >> (i & 7)) & 1)
|
|
614
|
+
cls_add(n, (unsigned char)i);
|
|
615
|
+
for (int i = 0; i < tmp->uni_n && n->uni_n < 4; i++) {
|
|
616
|
+
n->uni_lo[n->uni_n] = tmp->uni_lo[i];
|
|
617
|
+
n->uni_hi[n->uni_n] = tmp->uni_hi[i];
|
|
618
|
+
n->uni_n++;
|
|
619
|
+
}
|
|
620
|
+
if (tmp->cat_set) {
|
|
621
|
+
n->cat_set = 1;
|
|
622
|
+
n->cat_mask |= tmp->cat_mask;
|
|
623
|
+
}
|
|
624
|
+
node_free(tmp);
|
|
495
625
|
continue;
|
|
496
626
|
}
|
|
497
627
|
if (*ps->p == '\\' && ps->p[1]) {
|
|
@@ -508,6 +638,16 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
508
638
|
cls_add_builtin(tmp, e);
|
|
509
639
|
int neg_b = e == 'D' || e == 'W' || e == 'S' ||
|
|
510
640
|
e == 'I' || e == 'C';
|
|
641
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
642
|
+
if (!neg_b && tmp->cat_set) {
|
|
643
|
+
n->cat_set = 1;
|
|
644
|
+
n->cat_mask |= tmp->cat_mask;
|
|
645
|
+
} else if (neg_b && tmp->cat_set && !n->cat_set) {
|
|
646
|
+
n->cat_set = 1;
|
|
647
|
+
n->cat_strict = 1;
|
|
648
|
+
n->cat_mask = (~tmp->cat_mask) & 0x7FFFFFFFu;
|
|
649
|
+
}
|
|
650
|
+
#endif
|
|
511
651
|
for (int i = 0; i < 256; i++)
|
|
512
652
|
if (((tmp->cls[i >> 3] >> (i & 7)) & 1) ==
|
|
513
653
|
(neg_b ? 0 : 1))
|
|
@@ -543,10 +683,9 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
543
683
|
node_free(n);
|
|
544
684
|
return NULL;
|
|
545
685
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
node_free(sub);
|
|
686
|
+
/* owned: the matcher consults it codepoint-wide, so
|
|
687
|
+
* ranges/blocks/categories subtract exactly */
|
|
688
|
+
n->subtract = sub;
|
|
550
689
|
if (*ps->p != ']') {
|
|
551
690
|
node_free(n);
|
|
552
691
|
ps->bad = 1;
|
|
@@ -554,13 +693,30 @@ static RreNode* parse_class(RreParser* ps) {
|
|
|
554
693
|
}
|
|
555
694
|
break;
|
|
556
695
|
}
|
|
557
|
-
unsigned
|
|
696
|
+
unsigned lo = 0;
|
|
697
|
+
if (!cls_read_cp(ps, &lo)) {
|
|
698
|
+
node_free(n);
|
|
699
|
+
ps->bad = 1;
|
|
700
|
+
return NULL;
|
|
701
|
+
}
|
|
558
702
|
if (*ps->p == '-' && ps->p[1] && ps->p[1] != ']') {
|
|
559
703
|
ps->p++;
|
|
560
|
-
unsigned
|
|
561
|
-
|
|
704
|
+
unsigned hi = 0;
|
|
705
|
+
if (!cls_read_cp(ps, &hi) || hi < lo) {
|
|
706
|
+
node_free(n);
|
|
707
|
+
ps->bad = 1;
|
|
708
|
+
return NULL;
|
|
709
|
+
}
|
|
710
|
+
if (lo < 0x100 && hi < 0x100) {
|
|
711
|
+
for (unsigned int i = lo; i <= hi; i++)
|
|
712
|
+
cls_add(n, (unsigned char)i);
|
|
713
|
+
} else if (n->uni_n < 4) {
|
|
714
|
+
n->uni_lo[n->uni_n] = lo;
|
|
715
|
+
n->uni_hi[n->uni_n] = hi;
|
|
716
|
+
n->uni_n++;
|
|
717
|
+
}
|
|
562
718
|
} else {
|
|
563
|
-
|
|
719
|
+
cls_add_cp(n, lo);
|
|
564
720
|
}
|
|
565
721
|
}
|
|
566
722
|
if (*ps->p != ']') {
|
|
@@ -651,25 +807,18 @@ static RreNode* parse_atom(RreParser* ps) {
|
|
|
651
807
|
ps->p += 2;
|
|
652
808
|
RreNode* n = node_new(RRE_CLASS);
|
|
653
809
|
if (!n) return NULL;
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
: e == 'S' ? 's'
|
|
667
|
-
: e == 'I' ? 'i' : 'c');
|
|
668
|
-
for (int i = 0; i < 256; i++)
|
|
669
|
-
if ((tmp->cls[i >> 3] >> (i & 7)) & 1)
|
|
670
|
-
n->cls[i >> 3] &= (unsigned char)~(1u << (i & 7));
|
|
671
|
-
node_free(tmp);
|
|
672
|
-
}
|
|
810
|
+
/* complement atoms are the builtin with neg: cls_has_cp
|
|
811
|
+
* flips the combined bitmap/range/categorie result, so
|
|
812
|
+
* the class members stay exact (the old bitmap memset
|
|
813
|
+
* could not represent the category complement) */
|
|
814
|
+
cls_add_builtin(n, e == 'D' ? 'd'
|
|
815
|
+
: e == 'W' ? 'w'
|
|
816
|
+
: e == 'S' ? 's'
|
|
817
|
+
: e == 'I' ? 'i'
|
|
818
|
+
: e == 'C' ? 'c' : e);
|
|
819
|
+
if (e == 'D' || e == 'W' || e == 'S' || e == 'I' ||
|
|
820
|
+
e == 'C')
|
|
821
|
+
n->neg = 1;
|
|
673
822
|
return n;
|
|
674
823
|
}
|
|
675
824
|
ps->p += 2;
|
|
@@ -830,7 +979,8 @@ static const char* rre_match(RreNode* n, const char* s,
|
|
|
830
979
|
return NULL;
|
|
831
980
|
case RRE_CLASS: {
|
|
832
981
|
if (!*s) return NULL;
|
|
833
|
-
if ((unsigned char)*s < 0x80 && !n->cat_set
|
|
982
|
+
if ((unsigned char)*s < 0x80 && !n->cat_set &&
|
|
983
|
+
!n->subtract && !n->negm) {
|
|
834
984
|
if (cls_has(n, (unsigned char)*s))
|
|
835
985
|
return rre_run(k, s + 1);
|
|
836
986
|
return NULL;
|
|
@@ -900,6 +1050,10 @@ int rng_regex_supported(const char* pat) {
|
|
|
900
1050
|
|
|
901
1051
|
int rng_regex_matches(const char* pat, const char* text) {
|
|
902
1052
|
if (!pat || !text) return 0;
|
|
1053
|
+
/* XSD Appendix F.1: an EMPTY pattern branch matches only the
|
|
1054
|
+
* empty string (the reA1 corpus shape: pattern="" with
|
|
1055
|
+
* value="") */
|
|
1056
|
+
if (!*pat) return !*text;
|
|
903
1057
|
RreParser ps;
|
|
904
1058
|
ps.p = pat;
|
|
905
1059
|
ps.depth = 0;
|
|
@@ -768,6 +768,14 @@ struct leptris_xpath_result* vm_apply_axis_parent(XPathContext* ctx, XPathVM* vm
|
|
|
768
768
|
if (in && (int)in->type == (int)LEPTRIS_NODE_NAMESPACE) {
|
|
769
769
|
/* Namespace nodes' parent is the OWNER element (63). */
|
|
770
770
|
parent = ((LeptrisNamespaceNode*)in)->owner;
|
|
771
|
+
} else if (in && (int)in->type == (int)LEPTRIS_NODE_ATTRIBUTE) {
|
|
772
|
+
/* Attribute nodes' parent is the OWNER element (XPath
|
|
773
|
+
* 1.0 §2.2: `../` after an attribute step names the
|
|
774
|
+
* element carrying the attribute). Attributes are not
|
|
775
|
+
* in the child tree, so the any-kind parent link below
|
|
776
|
+
* is NULL for them (leptris#1866, standoc svg cleanup:
|
|
777
|
+
* `./@id/..`). */
|
|
778
|
+
parent = ((LeptrisAttributeNode*)in)->owner;
|
|
771
779
|
} else if (node_is_element(in)) {
|
|
772
780
|
parent = leptris_element_get_parent((LeptrisElement)in);
|
|
773
781
|
} else {
|
|
@@ -2294,16 +2294,29 @@ static void xsd_capture_root(struct leptris_xsd_schema* s,
|
|
|
2294
2294
|
* synthesized slot and point the declaration at it. */
|
|
2295
2295
|
char synth[512];
|
|
2296
2296
|
snprintf(synth, sizeof(synth), "element:%s", nm);
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2297
|
+
/* moxml#466: the declaration is captured even when
|
|
2298
|
+
* the element carries no inline type (a bare
|
|
2299
|
+
* <xs:element name=... substitutionGroup=.../> member
|
|
2300
|
+
* has none) — the strict root walk and the head
|
|
2301
|
+
* chain both need the row to exist. */
|
|
2302
|
+
int has_inline =
|
|
2303
|
+
xsd_capture_inline(s, (LeptrisElement)n, synth);
|
|
2304
|
+
XsdElementDecl* d =
|
|
2305
|
+
(XsdElementDecl*)calloc(1, sizeof(*d));
|
|
2306
|
+
if (d) {
|
|
2307
|
+
d->name = xsd_strdup(nm);
|
|
2308
|
+
if (s->target_ns) d->ns = xsd_strdup(s->target_ns);
|
|
2309
|
+
if (has_inline)
|
|
2303
2310
|
d->type = xsd_strdup(synth);
|
|
2304
|
-
|
|
2305
|
-
|
|
2311
|
+
const char* sg = leptris_element_attribute(
|
|
2312
|
+
(LeptrisElement)n, "substitutionGroup");
|
|
2313
|
+
if (sg) {
|
|
2314
|
+
const char* colon = strchr(sg, ':');
|
|
2315
|
+
d->sub_head = xsd_strdup(
|
|
2316
|
+
colon ? colon + 1 : sg);
|
|
2306
2317
|
}
|
|
2318
|
+
d->next = s->elements;
|
|
2319
|
+
s->elements = d;
|
|
2307
2320
|
}
|
|
2308
2321
|
}
|
|
2309
2322
|
} else if (strcmp(local, "complexType") == 0 && uri &&
|
|
@@ -2746,12 +2759,16 @@ static XsdCm* xsd_capture_complex_model(struct leptris_xsd_schema* s,
|
|
|
2746
2759
|
* half-captured base silently truncates the model.
|
|
2747
2760
|
* The visited memo means each file merges once, so
|
|
2748
2761
|
* the double capture that used to mask this is
|
|
2749
|
-
* gone.
|
|
2762
|
+
* gone. EXTENSION ONLY (moxml#466): a restriction's
|
|
2763
|
+
* model REPLACES the base's — folding it in prepends
|
|
2764
|
+
* a duplicate particle sequence and demands the base
|
|
2765
|
+
* content next to the derived content. */
|
|
2750
2766
|
int defer_base =
|
|
2751
2767
|
base_root
|
|
2752
2768
|
? base_root->ext_base != NULL
|
|
2753
2769
|
: (base && strncmp(base, "xs:", 3) != 0);
|
|
2754
|
-
if (defer_base && base
|
|
2770
|
+
if (defer_base && base &&
|
|
2771
|
+
strcmp(dl, "extension") == 0) {
|
|
2755
2772
|
free(root->ext_base);
|
|
2756
2773
|
root->ext_base = xsd_strdup(base);
|
|
2757
2774
|
}
|
|
@@ -255,6 +255,11 @@ static void ss_close(StateSet* ss, CmNfa* n) {
|
|
|
255
255
|
static int all_root_valid(XsdCm* all, const char* target_ns,
|
|
256
256
|
const char* const* names,
|
|
257
257
|
const char* const* ns_uris, size_t count) {
|
|
258
|
+
/* the whole all is ABSENT (moxml#466, mgAa003/mgD013): with
|
|
259
|
+
* minOccurs="0" an empty instance validates even when the
|
|
260
|
+
* particles carry min=1 — their bounds apply only when the all
|
|
261
|
+
* is present */
|
|
262
|
+
if (count == 0 && all->min == 0) return 1;
|
|
258
263
|
for (XsdCm* p = all->first_child; p; p = p->next) {
|
|
259
264
|
size_t hits = 0;
|
|
260
265
|
for (size_t i = 0; i < count; i++) {
|
|
@@ -325,15 +325,18 @@ static void validate_children(struct xsd_validator* v,
|
|
|
325
325
|
leptris_node_first_child(leptris_element_as_node(elem));
|
|
326
326
|
c; c = leptris_node_next_sibling(c))
|
|
327
327
|
if (leptris_node_get_type(c) == LEPTRIS_NODE_TYPE_ELEMENT) n++;
|
|
328
|
-
|
|
329
|
-
const char**
|
|
330
|
-
const char** uris = (const char**)calloc(n, sizeof(char*));
|
|
331
|
-
if (!names || !uris) {
|
|
332
|
-
free(names);
|
|
333
|
-
free(uris);
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
328
|
+
const char** names = NULL;
|
|
329
|
+
const char** uris = NULL;
|
|
336
330
|
size_t i = 0;
|
|
331
|
+
if (n > 0) {
|
|
332
|
+
names = (const char**)malloc(n * sizeof(char*));
|
|
333
|
+
uris = (const char**)calloc(n, sizeof(char*));
|
|
334
|
+
if (!names || !uris) {
|
|
335
|
+
free(names);
|
|
336
|
+
free(uris);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
337
340
|
for (LeptrisNodeRef c =
|
|
338
341
|
leptris_node_first_child(leptris_element_as_node(elem));
|
|
339
342
|
c; c = leptris_node_next_sibling(c)) {
|
|
@@ -346,6 +349,11 @@ static void validate_children(struct xsd_validator* v,
|
|
|
346
349
|
uris[i] = uri;
|
|
347
350
|
i++;
|
|
348
351
|
}
|
|
352
|
+
/* moxml#464-adjacent false-valid: the early return on !n let an
|
|
353
|
+
* empty instance pass ANY content model — required particles
|
|
354
|
+
* never failed. Zero children still walks: the NFA accepts the
|
|
355
|
+
* empty string only when every particle reaches accept with
|
|
356
|
+
* min == 0. names/uris stay NULL (never dereferenced at n==0). */
|
|
349
357
|
int ok = xsd_content_valid(s, model, xsd_target_ns(s), names, uris,
|
|
350
358
|
n);
|
|
351
359
|
if (ok) {
|
|
@@ -143,6 +143,17 @@ target_compile_definitions(test_rng_corpus PRIVATE
|
|
|
143
143
|
leptris_add_test(test_rng_corpus_msg rng/test_rng_corpus_msg.cpp)
|
|
144
144
|
target_compile_definitions(test_rng_corpus_msg PRIVATE
|
|
145
145
|
LEPTRIS_RNG_CASES_DIR="${CMAKE_CURRENT_SOURCE_DIR}/rng/jing-cases")
|
|
146
|
+
if(LEPTRIS_ENABLE_UTF8PROC)
|
|
147
|
+
# the regex suites gate their unicode-escape assertions on the
|
|
148
|
+
# same macro the ENGINE was built with — a mismatch silently
|
|
149
|
+
# compiles out the assertions (the gtest then "passes" while
|
|
150
|
+
# the engine behavior goes unverified).
|
|
151
|
+
target_compile_definitions(test_rng PRIVATE LEPTRIS_HAS_UTF8PROC=1)
|
|
152
|
+
target_compile_definitions(test_rng_corpus
|
|
153
|
+
PRIVATE LEPTRIS_HAS_UTF8PROC=1)
|
|
154
|
+
target_compile_definitions(test_rng_corpus_msg
|
|
155
|
+
PRIVATE LEPTRIS_HAS_UTF8PROC=1)
|
|
156
|
+
endif()
|
|
146
157
|
leptris_add_test(test_simd_text common/test_simd_text.cpp)
|
|
147
158
|
leptris_add_test(test_simd_spans common/test_simd_spans.cpp)
|
|
148
159
|
leptris_add_test(test_pool memory/test_pool.cpp)
|
|
@@ -1491,6 +1491,96 @@ TEST(RngRegex, BlockEscapesUnicode) {
|
|
|
1491
1491
|
"\xE4\xB8\x80"), 1);
|
|
1492
1492
|
}
|
|
1493
1493
|
|
|
1494
|
+
/* moxml#464 subtraction edges: the subtrahend carries RANGES and
|
|
1495
|
+
* categories, not just byte bits — the byte-bitmap merge ignored
|
|
1496
|
+
* them, so subtracting a range subtracted nothing. */
|
|
1497
|
+
TEST(RngRegex, SubtractionWithRangesAndCats) {
|
|
1498
|
+
/* byte-only subtraction still exact */
|
|
1499
|
+
EXPECT_EQ(rng_regex_matches("[a-z-[aeiou]]+", "bcd"), 1);
|
|
1500
|
+
EXPECT_EQ(rng_regex_matches("[a-z-[aeiou]]+", "axe"), 0);
|
|
1501
|
+
/* range in the subtrahend: subtract ALL lowercase from the
|
|
1502
|
+
* printable-ASCII range */
|
|
1503
|
+
EXPECT_EQ(rng_regex_matches("[!-~-[a-z]]+", "AB1!"), 1);
|
|
1504
|
+
EXPECT_EQ(rng_regex_matches("[!-~-[a-z]]+", "ab"), 0);
|
|
1505
|
+
EXPECT_EQ(rng_regex_matches("[!-~-[a-z]]+", "\xCE\xB1"), 0);
|
|
1506
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
1507
|
+
/* category in the subtrahend: letters minus uppercase */
|
|
1508
|
+
EXPECT_EQ(rng_regex_matches("[\\p{L}-[\\p{Lu}]]+", "abc"), 1);
|
|
1509
|
+
EXPECT_EQ(rng_regex_matches("[\\p{L}-[\\p{Lu}]]+", "ABC"), 0);
|
|
1510
|
+
EXPECT_EQ(rng_regex_matches("[\\p{L}-[\\p{Lu}]]+", "\xCE\xB1"), 1);
|
|
1511
|
+
EXPECT_EQ(rng_regex_matches("[\\p{L}-[\\p{Lu}]]+", "\xCE\x91"), 0);
|
|
1512
|
+
/* block in the subtrahend */
|
|
1513
|
+
EXPECT_EQ(rng_regex_matches("[\\p{L}-[\\p{IsGreek}]]+", "abc"), 1);
|
|
1514
|
+
EXPECT_EQ(rng_regex_matches("[\\p{L}-[\\p{IsGreek}]]+",
|
|
1515
|
+
"\xCE\xB1"), 0);
|
|
1516
|
+
#endif
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
/* moxml#464 \d / \D: XML decimal = Nd category — Arabic-Indic
|
|
1520
|
+
* and the other unicode digit families are \\d too. */
|
|
1521
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
1522
|
+
TEST(RngRegex, DigitEscapesAreUnicode) {
|
|
1523
|
+
/* U+0660 Arabic-Indic zero = D9 A0 */
|
|
1524
|
+
EXPECT_EQ(rng_regex_matches("\\d", "\xD9\xA0"), 1);
|
|
1525
|
+
EXPECT_EQ(rng_regex_matches("\\d+", "\xD9\xA0\xD9\xA1"), 1);
|
|
1526
|
+
EXPECT_EQ(rng_regex_matches("\\d", "a"), 0);
|
|
1527
|
+
EXPECT_EQ(rng_regex_matches("\\D", "\xD9\xA0"), 0);
|
|
1528
|
+
EXPECT_EQ(rng_regex_matches("\\D", "a"), 1);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
TEST(RngRegex, EmptyPatternMatchesEmptyOnly) {
|
|
1532
|
+
EXPECT_EQ(rng_regex_matches("", ""), 1);
|
|
1533
|
+
EXPECT_EQ(rng_regex_matches("", "a"), 0);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
TEST(RngRegex, EscapedBracketInRange) {
|
|
1537
|
+
EXPECT_EQ(rng_regex_matches("[a-\\}]+", "abc}"), 1);
|
|
1538
|
+
EXPECT_EQ(rng_regex_matches("[a-\\}]+", "!"), 0);
|
|
1539
|
+
EXPECT_EQ(rng_regex_matches("[1-\\]]+", "12]"), 1);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
1543
|
+
/* moxml#464 in-class \P: complement member (reF43/reK4). Exact as
|
|
1544
|
+
* the class's only member; the class negation composes. */
|
|
1545
|
+
TEST(RngRegex, NegatedPropertyInsideClass) {
|
|
1546
|
+
EXPECT_EQ(rng_regex_matches("[^\\P{IsBasicLatin}]", "a"), 1);
|
|
1547
|
+
EXPECT_EQ(rng_regex_matches("[^\\P{IsBasicLatin}]", "\xC3"), 0);
|
|
1548
|
+
EXPECT_EQ(rng_regex_matches("[\\P{L}*]", "*"), 1);
|
|
1549
|
+
EXPECT_EQ(rng_regex_matches("[\\P{L}*]", "a"), 0);
|
|
1550
|
+
EXPECT_EQ(rng_regex_matches("[\\P{L}]", "\xCE\xB1"), 0);
|
|
1551
|
+
EXPECT_EQ(rng_regex_matches("[\\P{L}]{0,2}", "*!"), 1);
|
|
1552
|
+
}
|
|
1553
|
+
#endif
|
|
1554
|
+
|
|
1555
|
+
TEST(RngRegex, AstralCharClassMember) {
|
|
1556
|
+
/* U+10000 = F0 90 80 80 */
|
|
1557
|
+
EXPECT_EQ(rng_regex_matches("[\xF0\x90\x80\x80]",
|
|
1558
|
+
"\xF0\x90\x80\x80"), 1);
|
|
1559
|
+
EXPECT_EQ(rng_regex_matches("[\xF0\x90\x80\x80]", "a"), 0);
|
|
1560
|
+
}
|
|
1561
|
+
#endif
|
|
1562
|
+
|
|
1563
|
+
/* moxml#464 \i / \c: XML NameStartChar / NameChar escapes. */
|
|
1564
|
+
TEST(RngRegex, NameCharEscapes) {
|
|
1565
|
+
EXPECT_EQ(rng_regex_matches("\\i+", "abc_:def"), 1);
|
|
1566
|
+
EXPECT_EQ(rng_regex_matches("\\i", "9"), 0);
|
|
1567
|
+
EXPECT_EQ(rng_regex_matches("\\c+", "a-b.c9_d:e"), 1);
|
|
1568
|
+
EXPECT_EQ(rng_regex_matches("\\c", "*"), 0);
|
|
1569
|
+
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
1570
|
+
/* non-ASCII letters are NameStartChars */
|
|
1571
|
+
EXPECT_EQ(rng_regex_matches("\\i", "\xCE\xB1"), 1);
|
|
1572
|
+
EXPECT_EQ(rng_regex_matches("\\c", "\xCE\xB1"), 1);
|
|
1573
|
+
/* Latin-1 symbols are NOT NameStartChars (the old 0x80-0xFF
|
|
1574
|
+
* blanket accepted them): U+00D7 multiplication sign is Sm */
|
|
1575
|
+
EXPECT_EQ(rng_regex_matches("\\i", "\xC3\x97"), 0);
|
|
1576
|
+
EXPECT_EQ(rng_regex_matches("\\c", "\xC3\x97"), 0);
|
|
1577
|
+
/* combining marks are NameChars but not NameStartChars:
|
|
1578
|
+
* U+0300 combining grave is Mn */
|
|
1579
|
+
EXPECT_EQ(rng_regex_matches("\\i", "\xCC\x80"), 0);
|
|
1580
|
+
EXPECT_EQ(rng_regex_matches("\\c", "\xCC\x80"), 1);
|
|
1581
|
+
#endif
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1494
1584
|
/* moxml#464 category escapes: \p{L} family masks over the
|
|
1495
1585
|
* utf8proc categories (LEPTRIS_HAS_UTF8PROC builds). */
|
|
1496
1586
|
#if defined(LEPTRIS_HAS_UTF8PROC)
|
|
@@ -114,6 +114,42 @@ TEST(XPathBytecodeSpecializedAxes, AttributeNameMatches) {
|
|
|
114
114
|
leptris_document_free(doc);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
TEST(XPathBytecodeSpecializedAxes, ParentAfterAttributeStep) {
|
|
118
|
+
const char xml[] =
|
|
119
|
+
"<root><child id=\"a\"><leaf/></child></root>";
|
|
120
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
121
|
+
LeptrisDocument doc = leptris_parse_string(xml, std::strlen(xml), &st);
|
|
122
|
+
ASSERT_NE(doc, nullptr);
|
|
123
|
+
|
|
124
|
+
/* leptris#1866: `..` after an attribute step yields the OWNING
|
|
125
|
+
* element (XPath 1.0 §2.2). The VM parent axis handled
|
|
126
|
+
* namespace/element/text contexts but attributes fell through
|
|
127
|
+
* to the child-tree parent link (NULL for attrs) — the step
|
|
128
|
+
* came back empty (standoc svg cleanup: `./@id/..`). */
|
|
129
|
+
LeptrisXPathResult r1 = leptris_xpath_eval(
|
|
130
|
+
doc, nullptr, "count(//child/@id/..)");
|
|
131
|
+
ASSERT_NE(r1, nullptr);
|
|
132
|
+
EXPECT_DOUBLE_EQ(leptris_xpath_result_number(r1), 1.0);
|
|
133
|
+
leptris_xpath_result_free(r1);
|
|
134
|
+
|
|
135
|
+
LeptrisXPathResult r2 = leptris_xpath_eval(
|
|
136
|
+
doc, nullptr, "name(//child/@id/..)");
|
|
137
|
+
ASSERT_NE(r2, nullptr);
|
|
138
|
+
char* s = leptris_xpath_result_string(r2);
|
|
139
|
+
EXPECT_STREQ(s, "child");
|
|
140
|
+
leptris_free_string(s);
|
|
141
|
+
leptris_xpath_result_free(r2);
|
|
142
|
+
|
|
143
|
+
/* a bare parent step on the element itself is unchanged */
|
|
144
|
+
LeptrisXPathResult r3 = leptris_xpath_eval(
|
|
145
|
+
doc, nullptr, "count(//leaf/..)");
|
|
146
|
+
ASSERT_NE(r3, nullptr);
|
|
147
|
+
EXPECT_DOUBLE_EQ(leptris_xpath_result_number(r3), 1.0);
|
|
148
|
+
leptris_xpath_result_free(r3);
|
|
149
|
+
|
|
150
|
+
leptris_document_free(doc);
|
|
151
|
+
}
|
|
152
|
+
|
|
117
153
|
TEST(XPathBytecodeSpecializedAxes, SelfAndParentAxes) {
|
|
118
154
|
const char xml[] =
|
|
119
155
|
"<root><child><leaf/></child></root>";
|
|
@@ -723,6 +723,47 @@ TEST(XsdValidate, ValidDocumentPasses) {
|
|
|
723
723
|
EXPECT_EQ(validate_doc(k_v_schema, "<count>3</count>"), 1);
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
+
// moxml#466: an xs:all with minOccurs="0" may be ABSENT — an empty
|
|
727
|
+
// instance validates even when its particles carry min=1 (the
|
|
728
|
+
// mgAa003/mgD013 corpus shapes).
|
|
729
|
+
TEST(XsdValidate, AllMinOccursZeroMayBeAbsent) {
|
|
730
|
+
const char* sch =
|
|
731
|
+
"<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
|
|
732
|
+
"<xsd:element name=\"doc\" type=\"foo\"/>"
|
|
733
|
+
"<xsd:complexType name=\"foo\">"
|
|
734
|
+
"<xsd:all minOccurs=\"0\">"
|
|
735
|
+
"<xsd:element name=\"t1\"/>"
|
|
736
|
+
"</xsd:all></xsd:complexType>"
|
|
737
|
+
"</xsd:schema>";
|
|
738
|
+
EXPECT_EQ(validate_doc(sch, "<doc/>"), 1);
|
|
739
|
+
EXPECT_EQ(validate_doc(sch, "<doc><t1/></doc>"), 1);
|
|
740
|
+
EXPECT_EQ(validate_doc(sch, "<doc><t1/><t1/></doc>"), 0);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
TEST(XsdValidate, AllMinOccursZeroAbsentWithRequiredParticles) {
|
|
744
|
+
const char* sch =
|
|
745
|
+
"<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
|
|
746
|
+
"<xsd:element name=\"root\">"
|
|
747
|
+
"<xsd:complexType><xsd:choice minOccurs=\"1\""
|
|
748
|
+
" maxOccurs=\"unbounded\">"
|
|
749
|
+
"<xsd:element name=\"elt1\">"
|
|
750
|
+
"<xsd:complexType>"
|
|
751
|
+
"<xsd:all minOccurs=\"0\">"
|
|
752
|
+
"<xsd:element name=\"elt2\"/>"
|
|
753
|
+
"<xsd:element name=\"elt3\"/>"
|
|
754
|
+
"<xsd:element name=\"elt4\"/>"
|
|
755
|
+
"</xsd:all></xsd:complexType>"
|
|
756
|
+
"</xsd:element>"
|
|
757
|
+
"</xsd:choice></xsd:complexType>"
|
|
758
|
+
"</xsd:element></xsd:schema>";
|
|
759
|
+
EXPECT_EQ(validate_doc(sch, "<root><elt1/></root>"), 1);
|
|
760
|
+
EXPECT_EQ(validate_doc(
|
|
761
|
+
sch, "<root><elt1><elt2/><elt3/><elt4/></elt1>"
|
|
762
|
+
"</root>"), 1);
|
|
763
|
+
EXPECT_EQ(validate_doc(
|
|
764
|
+
sch, "<root><elt1><elt2/></elt1></root>"), 0);
|
|
765
|
+
}
|
|
766
|
+
|
|
726
767
|
TEST(XsdValidate, ContentModelViolationReports) {
|
|
727
768
|
EXPECT_EQ(validate_doc(k_v_schema,
|
|
728
769
|
"<rows version=\"1\">"
|
|
@@ -1314,6 +1355,98 @@ TEST(XsdValidate, SubstitutionGroupMembersBind) {
|
|
|
1314
1355
|
"<box><other>z</other></box>"), 0);
|
|
1315
1356
|
}
|
|
1316
1357
|
|
|
1358
|
+
// moxml#466 class 1: a member of a substitution group binds a
|
|
1359
|
+
// ref-particle in a NAMESPACED schema (particlesDc001 family) —
|
|
1360
|
+
// elementFormDefault="qualified" + targetNamespace, head reached
|
|
1361
|
+
// through x:elem1.
|
|
1362
|
+
const char* k_subst_ns_schema =
|
|
1363
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\""
|
|
1364
|
+
" xmlns=\"http://xsdtesting\""
|
|
1365
|
+
" xmlns:x=\"http://xsdtesting\""
|
|
1366
|
+
" targetNamespace=\"http://xsdtesting\""
|
|
1367
|
+
" elementFormDefault=\"qualified\">"
|
|
1368
|
+
"<xs:element name=\"doc\">"
|
|
1369
|
+
"<xs:complexType><xs:sequence>"
|
|
1370
|
+
"<xs:element ref=\"x:elem1\"/>"
|
|
1371
|
+
"</xs:sequence></xs:complexType>"
|
|
1372
|
+
"</xs:element>"
|
|
1373
|
+
"<xs:element name=\"elem1\" abstract=\"false\"/>"
|
|
1374
|
+
"<xs:element name=\"elem2\" substitutionGroup=\"x:elem1\"/>"
|
|
1375
|
+
"</xs:schema>";
|
|
1376
|
+
|
|
1377
|
+
TEST(XsdValidate, SubstitutionGroupMemberBindsRefParticleQualified) {
|
|
1378
|
+
EXPECT_EQ(validate_doc(
|
|
1379
|
+
k_subst_ns_schema,
|
|
1380
|
+
"<x:doc xmlns:x=\"http://xsdtesting\">"
|
|
1381
|
+
"<x:elem1/></x:doc>"), 1);
|
|
1382
|
+
EXPECT_EQ(validate_doc(
|
|
1383
|
+
k_subst_ns_schema,
|
|
1384
|
+
"<x:doc xmlns:x=\"http://xsdtesting\">"
|
|
1385
|
+
"<x:elem2/></x:doc>"), 1);
|
|
1386
|
+
EXPECT_EQ(validate_doc(
|
|
1387
|
+
k_subst_ns_schema,
|
|
1388
|
+
"<x:doc xmlns:x=\"http://xsdtesting\">"
|
|
1389
|
+
"<x:other/></x:doc>"), 0);
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
// moxml#466 class 2: a complexContent RESTRICTION whose base is a
|
|
1393
|
+
// forward reference must not defer-fold — the derived model
|
|
1394
|
+
// REPLACES the base's. The fold prepended the base's particles and
|
|
1395
|
+
// demanded the base content next to the derived content
|
|
1396
|
+
// (instance <e1/> rejected against SEQ(e1) restriction of
|
|
1397
|
+
// SEQ(e1) base because the fold produced SEQ(SEQ(e1), SEQ(e1))).
|
|
1398
|
+
TEST(XsdValidate, RestrictionReplacesForwardBaseModel) {
|
|
1399
|
+
const char* fwd =
|
|
1400
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1401
|
+
"<xs:element name=\"doc\" type=\"foo\"/>"
|
|
1402
|
+
"<xs:complexType name=\"foo\"><xs:complexContent>"
|
|
1403
|
+
"<xs:restriction base=\"bar\">"
|
|
1404
|
+
"<xs:sequence><xs:element name=\"e1\"/></xs:sequence>"
|
|
1405
|
+
"</xs:restriction></xs:complexContent></xs:complexType>"
|
|
1406
|
+
"<xs:complexType name=\"bar\"><xs:sequence>"
|
|
1407
|
+
"<xs:element name=\"e1\"/><xs:element name=\"e2\"/>"
|
|
1408
|
+
"</xs:sequence></xs:complexType>"
|
|
1409
|
+
"</xs:schema>";
|
|
1410
|
+
EXPECT_EQ(validate_doc(fwd, "<doc><e1/></doc>"), 1);
|
|
1411
|
+
/* the base's model must NOT ride along */
|
|
1412
|
+
EXPECT_EQ(validate_doc(fwd, "<doc><e1/><e2/></doc>"), 0);
|
|
1413
|
+
/* resolved (non-forward) base: same replace semantics */
|
|
1414
|
+
const char* resolved =
|
|
1415
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1416
|
+
"<xs:element name=\"doc\" type=\"foo\"/>"
|
|
1417
|
+
"<xs:complexType name=\"bar\"><xs:sequence>"
|
|
1418
|
+
"<xs:element name=\"e1\"/><xs:element name=\"e2\"/>"
|
|
1419
|
+
"</xs:sequence></xs:complexType>"
|
|
1420
|
+
"<xs:complexType name=\"foo\"><xs:complexContent>"
|
|
1421
|
+
"<xs:restriction base=\"bar\">"
|
|
1422
|
+
"<xs:sequence><xs:element name=\"e1\"/></xs:sequence>"
|
|
1423
|
+
"</xs:restriction></xs:complexContent></xs:complexType>"
|
|
1424
|
+
"</xs:schema>";
|
|
1425
|
+
EXPECT_EQ(validate_doc(resolved, "<doc><e1/></doc>"), 1);
|
|
1426
|
+
EXPECT_EQ(validate_doc(resolved, "<doc><e1/><e2/></doc>"), 0);
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/* mgO008 family: restriction through a group-ref particle — the
|
|
1430
|
+
* derived model (the group's all) is the whole content model. */
|
|
1431
|
+
TEST(XsdValidate, RestrictionGroupRefParticleReplaces) {
|
|
1432
|
+
const char* grp =
|
|
1433
|
+
"<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"
|
|
1434
|
+
"<xsd:element name=\"doc\" type=\"foo\"/>"
|
|
1435
|
+
"<xsd:complexType name=\"foo\"><xsd:complexContent>"
|
|
1436
|
+
"<xsd:restriction base=\"bar\">"
|
|
1437
|
+
"<xsd:group ref=\"group\"/>"
|
|
1438
|
+
"</xsd:restriction></xsd:complexContent></xsd:complexType>"
|
|
1439
|
+
"<xsd:complexType name=\"bar\">"
|
|
1440
|
+
"<xsd:all minOccurs=\"1\" maxOccurs=\"1\">"
|
|
1441
|
+
"<xsd:element name=\"e1\"/><xsd:element name=\"e2\"/>"
|
|
1442
|
+
"</xsd:all></xsd:complexType>"
|
|
1443
|
+
"<xsd:group name=\"group\"><xsd:all>"
|
|
1444
|
+
"<xsd:element name=\"e1\"/></xsd:all></xsd:group>"
|
|
1445
|
+
"</xsd:schema>";
|
|
1446
|
+
EXPECT_EQ(validate_doc(grp, "<doc><e1/></doc>"), 1);
|
|
1447
|
+
EXPECT_EQ(validate_doc(grp, "<doc><e1/><e2/></doc>"), 0);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1317
1450
|
} // namespace
|
|
1318
1451
|
|
|
1319
1452
|
// ---- tier-1 charter: include/import via compile_file ---------------
|
|
@@ -3166,3 +3299,36 @@ TEST(XsdNegativeWildcard, AnyAttributeNotQNameDefined) {
|
|
|
3166
3299
|
leptris_document_free(b);
|
|
3167
3300
|
leptris_xsd_free(s);
|
|
3168
3301
|
}
|
|
3302
|
+
|
|
3303
|
+
// moxml#464-adjacent false-valid: an EMPTY instance against a
|
|
3304
|
+
// required-particle content model must fail — the walk's `!n`
|
|
3305
|
+
// early-return let it pass (the NFA never saw the instance).
|
|
3306
|
+
TEST(XsdContentModel, EmptyInstanceAgainstRequiredSequenceFails) {
|
|
3307
|
+
LeptrisStatus st = LEPTRIS_OK;
|
|
3308
|
+
const char xsd[] =
|
|
3309
|
+
"<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
|
|
3310
|
+
"<xs:element name=\"note\">"
|
|
3311
|
+
"<xs:complexType>"
|
|
3312
|
+
"<xs:sequence>"
|
|
3313
|
+
"<xs:element name=\"to\" type=\"xs:string\"/>"
|
|
3314
|
+
"</xs:sequence>"
|
|
3315
|
+
"</xs:complexType>"
|
|
3316
|
+
"</xs:element>"
|
|
3317
|
+
"</xs:schema>";
|
|
3318
|
+
LeptrisXsdSchema s = leptris_xsd_compile(xsd, strlen(xsd), &st);
|
|
3319
|
+
ASSERT_NE(s, (LeptrisXsdSchema)0) << "status " << (int)st;
|
|
3320
|
+
|
|
3321
|
+
const char empty[] = "<note/>";
|
|
3322
|
+
LeptrisDocument d = leptris_parse_string(empty, strlen(empty), &st);
|
|
3323
|
+
ASSERT_NE(d, nullptr);
|
|
3324
|
+
EXPECT_EQ(leptris_xsd_validate(s, d), 0);
|
|
3325
|
+
leptris_document_free(d);
|
|
3326
|
+
|
|
3327
|
+
// the satisfied model stays valid
|
|
3328
|
+
const char full[] = "<note><to>x</to></note>";
|
|
3329
|
+
LeptrisDocument f = leptris_parse_string(full, strlen(full), &st);
|
|
3330
|
+
ASSERT_NE(f, nullptr);
|
|
3331
|
+
EXPECT_EQ(leptris_xsd_validate(s, f), 1);
|
|
3332
|
+
leptris_document_free(f);
|
|
3333
|
+
leptris_xsd_free(s);
|
|
3334
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg/master/scripts/vcpkg.schema.json",
|
|
3
3
|
"name": "leptris",
|
|
4
|
-
"version": "1.9.
|
|
4
|
+
"version": "1.9.384",
|
|
5
5
|
"description": "High-performance XML parser and XPath 1.0 engine in C (libleptris)",
|
|
6
6
|
"homepage": "https://github.com/leptris/leptris",
|
|
7
7
|
"license": "MIT",
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: leptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.9.
|
|
4
|
+
version: 1.9.384.0
|
|
5
5
|
platform: ppc64le-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|