@tradik/xslt3 0.0.0-stage → 1.0.0
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.
- package/LICENSE.md +28 -0
- package/README.md +98 -2
- package/package.json +43 -4
- package/src/errors.js +20 -0
- package/src/functions/aggregates.js +144 -0
- package/src/functions/arraySort.js +33 -0
- package/src/functions/arrays.js +181 -0
- package/src/functions/attributeTypes.js +58 -0
- package/src/functions/baseUris.js +82 -0
- package/src/functions/collations.js +127 -0
- package/src/functions/collections.js +84 -0
- package/src/functions/context.js +83 -0
- package/src/functions/core.js +167 -0
- package/src/functions/datetime.js +203 -0
- package/src/functions/deepEqual.js +132 -0
- package/src/functions/diagnostics.js +88 -0
- package/src/functions/focus.js +34 -0
- package/src/functions/format/dateComponents.js +176 -0
- package/src/functions/format/datePicture.js +115 -0
- package/src/functions/format/decimalFormat.js +65 -0
- package/src/functions/format/digitPattern.js +145 -0
- package/src/functions/format/formatDateTime.js +204 -0
- package/src/functions/format/formatInteger.js +109 -0
- package/src/functions/format/formatNumber.js +165 -0
- package/src/functions/format/fractionFormat.js +80 -0
- package/src/functions/format/germanWords.js +89 -0
- package/src/functions/format/numberLanguages.js +41 -0
- package/src/functions/format/numberPicture.js +202 -0
- package/src/functions/format/numberWords.js +155 -0
- package/src/functions/format/place.js +74 -0
- package/src/functions/format/timezoneFormat.js +89 -0
- package/src/functions/higherOrder.js +106 -0
- package/src/functions/ids.js +127 -0
- package/src/functions/json/index.js +17 -0
- package/src/functions/json/jsonChars.js +110 -0
- package/src/functions/json/jsonParser.js +203 -0
- package/src/functions/json/jsonStrings.js +116 -0
- package/src/functions/json/jsonToXml.js +116 -0
- package/src/functions/json/parseJson.js +119 -0
- package/src/functions/json/xmlRepresentation.js +103 -0
- package/src/functions/json/xmlToJson.js +147 -0
- package/src/functions/library.js +47 -0
- package/src/functions/mapArrayEqual.js +41 -0
- package/src/functions/maps.js +152 -0
- package/src/functions/math.js +73 -0
- package/src/functions/nodePaths.js +181 -0
- package/src/functions/nodes.js +199 -0
- package/src/functions/numeric.js +148 -0
- package/src/functions/options.js +58 -0
- package/src/functions/parseIetfDate.js +104 -0
- package/src/functions/parseXml.js +77 -0
- package/src/functions/qnames.js +68 -0
- package/src/functions/random.js +128 -0
- package/src/functions/regex/analyzeString.js +144 -0
- package/src/functions/regex/charClass.js +112 -0
- package/src/functions/regex/compile.js +81 -0
- package/src/functions/regex/escapes.js +109 -0
- package/src/functions/regex/parser.js +169 -0
- package/src/functions/regex/reader.js +37 -0
- package/src/functions/regex/regexFunctions.js +140 -0
- package/src/functions/regex/translate.js +102 -0
- package/src/functions/regex/unicodeBlocks.js +194 -0
- package/src/functions/registry.js +140 -0
- package/src/functions/sequences.js +173 -0
- package/src/functions/serializeFunction.js +53 -0
- package/src/functions/signatures.js +105 -0
- package/src/functions/sort.js +102 -0
- package/src/functions/stringMatch.js +45 -0
- package/src/functions/strings.js +194 -0
- package/src/functions/stringsCore.js +66 -0
- package/src/functions/support.js +106 -0
- package/src/functions/textDecoding.js +117 -0
- package/src/functions/ucaCollation.js +127 -0
- package/src/functions/unparsedText.js +71 -0
- package/src/functions/uri.js +166 -0
- package/src/functions/xqueryModule.js +24 -0
- package/src/index.js +38 -0
- package/src/items/array.js +84 -0
- package/src/items/function.js +45 -0
- package/src/items/map.js +120 -0
- package/src/items/mapKey.js +82 -0
- package/src/serialize/index.js +129 -0
- package/src/serialize/markup/content.js +74 -0
- package/src/serialize/markup/dialect.js +178 -0
- package/src/serialize/markup/htmlData.js +70 -0
- package/src/serialize/markup/namespaces.js +145 -0
- package/src/serialize/markup/prolog.js +91 -0
- package/src/serialize/markup/writer.js +183 -0
- package/src/serialize/methods/adaptive.js +131 -0
- package/src/serialize/methods/json.js +158 -0
- package/src/serialize/methods/text.js +43 -0
- package/src/serialize/output/buffer.js +48 -0
- package/src/serialize/output/encoding.js +146 -0
- package/src/serialize/output/expander.js +204 -0
- package/src/serialize/params/fromElement.js +141 -0
- package/src/serialize/params/fromMap.js +128 -0
- package/src/serialize/params/names.js +69 -0
- package/src/serialize/params/settings.js +153 -0
- package/src/serialize/params/values.js +163 -0
- package/src/serialize/sequence.js +111 -0
- package/src/xdm/arithmetic.js +155 -0
- package/src/xdm/atomic.js +142 -0
- package/src/xdm/binary.js +92 -0
- package/src/xdm/calendar.js +71 -0
- package/src/xdm/cast.js +176 -0
- package/src/xdm/compare.js +192 -0
- package/src/xdm/datetime.js +206 -0
- package/src/xdm/decimal.js +200 -0
- package/src/xdm/duration.js +128 -0
- package/src/xdm/generalCompare.js +152 -0
- package/src/xdm/index.js +47 -0
- package/src/xdm/lexical.js +126 -0
- package/src/xdm/nodes.js +170 -0
- package/src/xdm/numeric.js +122 -0
- package/src/xdm/qname.js +65 -0
- package/src/xdm/strings.js +52 -0
- package/src/xdm/temporalArithmetic.js +180 -0
- package/src/xdm/timeline.js +68 -0
- package/src/xdm/types.js +178 -0
- package/src/xpath/eval/atomics.js +45 -0
- package/src/xpath/eval/axes.js +212 -0
- package/src/xpath/eval/calls.js +168 -0
- package/src/xpath/eval/coercion.js +146 -0
- package/src/xpath/eval/compiler.js +71 -0
- package/src/xpath/eval/constructors.js +129 -0
- package/src/xpath/eval/decimalFormats.js +93 -0
- package/src/xpath/eval/descendantMemo.js +77 -0
- package/src/xpath/eval/documentOrder.js +142 -0
- package/src/xpath/eval/domNodes.js +174 -0
- package/src/xpath/eval/dynamicCall.js +27 -0
- package/src/xpath/eval/dynamicContext.js +161 -0
- package/src/xpath/eval/flow.js +115 -0
- package/src/xpath/eval/functionItems.js +194 -0
- package/src/xpath/eval/groupedChildren.js +88 -0
- package/src/xpath/eval/inline.js +71 -0
- package/src/xpath/eval/namespaceNodes.js +90 -0
- package/src/xpath/eval/nodeTests.js +164 -0
- package/src/xpath/eval/operators.js +190 -0
- package/src/xpath/eval/paths.js +163 -0
- package/src/xpath/eval/positional.js +81 -0
- package/src/xpath/eval/predicates.js +56 -0
- package/src/xpath/eval/primary.js +99 -0
- package/src/xpath/eval/resources.js +149 -0
- package/src/xpath/eval/scope.js +108 -0
- package/src/xpath/eval/sequenceType.js +193 -0
- package/src/xpath/eval/staticContext.js +142 -0
- package/src/xpath/eval/steps.js +98 -0
- package/src/xpath/eval/subtype.js +154 -0
- package/src/xpath/eval/typeExprs.js +147 -0
- package/src/xpath/eval/typeNames.js +101 -0
- package/src/xpath/eval/uris.js +21 -0
- package/src/xpath/eval/values.js +64 -0
- package/src/xpath/index.js +217 -0
- package/src/xpath/syntax/ast.js +130 -0
- package/src/xpath/syntax/chars.js +58 -0
- package/src/xpath/syntax/constructors.js +144 -0
- package/src/xpath/syntax/expressions.js +107 -0
- package/src/xpath/syntax/index.js +44 -0
- package/src/xpath/syntax/kindTests.js +134 -0
- package/src/xpath/syntax/lexer.js +220 -0
- package/src/xpath/syntax/operators.js +111 -0
- package/src/xpath/syntax/paths.js +102 -0
- package/src/xpath/syntax/postfix.js +91 -0
- package/src/xpath/syntax/primaries.js +149 -0
- package/src/xpath/syntax/steps.js +146 -0
- package/src/xpath/syntax/syntaxError.js +31 -0
- package/src/xpath/syntax/tokenStream.js +163 -0
- package/src/xpath/syntax/typeAst.js +76 -0
- package/src/xpath/syntax/typeOperators.js +127 -0
- package/src/xpath/syntax/types.js +158 -0
- package/src/xslt/api.js +153 -0
- package/src/xslt/compiler/acceptance.js +178 -0
- package/src/xslt/compiler/accumulatorDecl.js +146 -0
- package/src/xslt/compiler/attributeValues.js +174 -0
- package/src/xslt/compiler/attributes.js +211 -0
- package/src/xslt/compiler/avt.js +103 -0
- package/src/xslt/compiler/body.js +154 -0
- package/src/xslt/compiler/children.js +105 -0
- package/src/xslt/compiler/compatibility.js +130 -0
- package/src/xslt/compiler/components.js +179 -0
- package/src/xslt/compiler/contextItem.js +95 -0
- package/src/xslt/compiler/decimalFormats.js +132 -0
- package/src/xslt/compiler/declarationKinds.js +82 -0
- package/src/xslt/compiler/elementInfo.js +246 -0
- package/src/xslt/compiler/expressions.js +175 -0
- package/src/xslt/compiler/globalContextItem.js +92 -0
- package/src/xslt/compiler/globals.js +209 -0
- package/src/xslt/compiler/linker.js +134 -0
- package/src/xslt/compiler/miscDecl.js +171 -0
- package/src/xslt/compiler/modeDecl.js +88 -0
- package/src/xslt/compiler/moduleLoading.js +111 -0
- package/src/xslt/compiler/modules.js +158 -0
- package/src/xslt/compiler/outputDecl.js +209 -0
- package/src/xslt/compiler/overrideLinks.js +141 -0
- package/src/xslt/compiler/packageChecks.js +84 -0
- package/src/xslt/compiler/packageRegistry.js +95 -0
- package/src/xslt/compiler/packageTable.js +181 -0
- package/src/xslt/compiler/packageVersions.js +143 -0
- package/src/xslt/compiler/packages.js +138 -0
- package/src/xslt/compiler/registry.js +234 -0
- package/src/xslt/compiler/staticStage.js +197 -0
- package/src/xslt/compiler/staticVariables.js +93 -0
- package/src/xslt/compiler/stylesheet.js +205 -0
- package/src/xslt/compiler/tailPosition.js +74 -0
- package/src/xslt/compiler/templatesDecl.js +180 -0
- package/src/xslt/compiler/useAccumulators.js +76 -0
- package/src/xslt/compiler/useWhen.js +94 -0
- package/src/xslt/compiler/visibility.js +160 -0
- package/src/xslt/index.js +11 -0
- package/src/xslt/instructions/analyzeString.js +137 -0
- package/src/xslt/instructions/assert.js +72 -0
- package/src/xslt/instructions/attributeSets.js +115 -0
- package/src/xslt/instructions/conditional.js +99 -0
- package/src/xslt/instructions/control.js +123 -0
- package/src/xslt/instructions/copy.js +151 -0
- package/src/xslt/instructions/elements.js +161 -0
- package/src/xslt/instructions/evaluate.js +189 -0
- package/src/xslt/instructions/fork.js +35 -0
- package/src/xslt/instructions/grouping.js +106 -0
- package/src/xslt/instructions/index.js +94 -0
- package/src/xslt/instructions/iterate.js +182 -0
- package/src/xslt/instructions/literal.js +142 -0
- package/src/xslt/instructions/maps.js +72 -0
- package/src/xslt/instructions/merge.js +190 -0
- package/src/xslt/instructions/number.js +205 -0
- package/src/xslt/instructions/output.js +102 -0
- package/src/xslt/instructions/params.js +110 -0
- package/src/xslt/instructions/resultDocument.js +143 -0
- package/src/xslt/instructions/sort.js +114 -0
- package/src/xslt/instructions/sourceDocument.js +73 -0
- package/src/xslt/instructions/templates.js +153 -0
- package/src/xslt/instructions/text.js +151 -0
- package/src/xslt/instructions/try.js +186 -0
- package/src/xslt/instructions/variables.js +119 -0
- package/src/xslt/names.js +204 -0
- package/src/xslt/patterns/axisOrigins.js +121 -0
- package/src/xslt/patterns/compile.js +151 -0
- package/src/xslt/patterns/general.js +132 -0
- package/src/xslt/patterns/grammar.js +190 -0
- package/src/xslt/patterns/modes.js +153 -0
- package/src/xslt/patterns/paths.js +130 -0
- package/src/xslt/patterns/patternChecks.js +29 -0
- package/src/xslt/patterns/steps.js +162 -0
- package/src/xslt/processor.js +178 -0
- package/src/xslt/runtime/accumulatorFunctions.js +48 -0
- package/src/xslt/runtime/accumulators.js +174 -0
- package/src/xslt/runtime/apply.js +153 -0
- package/src/xslt/runtime/atomicKeys.js +36 -0
- package/src/xslt/runtime/availability.js +162 -0
- package/src/xslt/runtime/baseUri.js +64 -0
- package/src/xslt/runtime/compilerHook.js +27 -0
- package/src/xslt/runtime/conditional.js +110 -0
- package/src/xslt/runtime/context.js +106 -0
- package/src/xslt/runtime/copy.js +123 -0
- package/src/xslt/runtime/copyFunctions.js +71 -0
- package/src/xslt/runtime/copyOrigins.js +60 -0
- package/src/xslt/runtime/defaultMethod.js +42 -0
- package/src/xslt/runtime/documentContent.js +93 -0
- package/src/xslt/runtime/dynamicXPath.js +90 -0
- package/src/xslt/runtime/functionCache.js +65 -0
- package/src/xslt/runtime/functionCall.js +57 -0
- package/src/xslt/runtime/functions.js +191 -0
- package/src/xslt/runtime/groups.js +108 -0
- package/src/xslt/runtime/invocation.js +156 -0
- package/src/xslt/runtime/iteration.js +107 -0
- package/src/xslt/runtime/keys.js +141 -0
- package/src/xslt/runtime/lazy.js +97 -0
- package/src/xslt/runtime/locations.js +86 -0
- package/src/xslt/runtime/machine.js +137 -0
- package/src/xslt/runtime/merging.js +167 -0
- package/src/xslt/runtime/namespaceScope.js +141 -0
- package/src/xslt/runtime/numberFormat.js +89 -0
- package/src/xslt/runtime/numbering.js +168 -0
- package/src/xslt/runtime/params.js +59 -0
- package/src/xslt/runtime/properties.js +103 -0
- package/src/xslt/runtime/rawResults.js +82 -0
- package/src/xslt/runtime/sequenceReceiver.js +102 -0
- package/src/xslt/runtime/snapshot.js +66 -0
- package/src/xslt/runtime/sortKeys.js +119 -0
- package/src/xslt/runtime/streamAvailable.js +60 -0
- package/src/xslt/runtime/strip.js +102 -0
- package/src/xslt/runtime/transformFunction.js +169 -0
- package/src/xslt/runtime/transformation.js +168 -0
- package/src/xslt/runtime/transformationContext.js +85 -0
- package/src/xslt/runtime/treeReceiver.js +194 -0
- package/src/xslt/runtime/values.js +92 -0
package/src/xdm/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XPath and XQuery Data Model 3.1 for a basic (non-schema-aware)
|
|
3
|
+
* processor: atomic types and values, casting, comparisons, arithmetic,
|
|
4
|
+
* atomization and effective boolean value.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xdm
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { XPathError } from "../errors.js";
|
|
10
|
+
export {
|
|
11
|
+
allTypes,
|
|
12
|
+
derivesFrom,
|
|
13
|
+
getType,
|
|
14
|
+
isNumericType,
|
|
15
|
+
namePatterns,
|
|
16
|
+
types,
|
|
17
|
+
XS_NAMESPACE,
|
|
18
|
+
} from "./types.js";
|
|
19
|
+
export {
|
|
20
|
+
atomic,
|
|
21
|
+
AtomicValue,
|
|
22
|
+
checkFacets,
|
|
23
|
+
isArray,
|
|
24
|
+
isAtomic,
|
|
25
|
+
isFunctionItem,
|
|
26
|
+
isMap,
|
|
27
|
+
isNode,
|
|
28
|
+
ITEM_KIND,
|
|
29
|
+
itemKind,
|
|
30
|
+
} from "./atomic.js";
|
|
31
|
+
export { Decimal, DIVISION_SCALE } from "./decimal.js";
|
|
32
|
+
export { DateTimeValue, MAX_YEAR } from "./datetime.js";
|
|
33
|
+
export { DurationValue } from "./duration.js";
|
|
34
|
+
export { QNameValue } from "./qname.js";
|
|
35
|
+
export { canonicalString, fromLexical } from "./lexical.js";
|
|
36
|
+
export { cast, castable } from "./cast.js";
|
|
37
|
+
export { compareAtomic, deepEqualAtomic, valueCompare } from "./compare.js";
|
|
38
|
+
export { generalCompare, toNumber } from "./generalCompare.js";
|
|
39
|
+
export { arithmetic, unaryArithmetic } from "./arithmetic.js";
|
|
40
|
+
export {
|
|
41
|
+
atomize,
|
|
42
|
+
effectiveBooleanValue,
|
|
43
|
+
nodeStringValue,
|
|
44
|
+
stringValue,
|
|
45
|
+
typedValue,
|
|
46
|
+
} from "./nodes.js";
|
|
47
|
+
export { compareCodepoints, normalizeWhitespace } from "./strings.js";
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexical spaces of all built-in atomic types: parsing a string into a
|
|
3
|
+
* typed value (F&O 3.1 19.2, casting from xs:string) and the canonical
|
|
4
|
+
* string of a value (F&O 3.1 19.1.1, casting to xs:string).
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xdm/lexical
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { XPathError } from "../errors.js";
|
|
10
|
+
import { AtomicValue, checkFacets } from "./atomic.js";
|
|
11
|
+
import {
|
|
12
|
+
formatBase64Binary,
|
|
13
|
+
formatHexBinary,
|
|
14
|
+
parseBase64Binary,
|
|
15
|
+
parseHexBinary,
|
|
16
|
+
} from "./binary.js";
|
|
17
|
+
import { formatDateTime, parseDateTime } from "./datetime.js";
|
|
18
|
+
import { formatDuration, parseDuration } from "./duration.js";
|
|
19
|
+
import {
|
|
20
|
+
formatDouble,
|
|
21
|
+
parseDecimal,
|
|
22
|
+
parseDouble,
|
|
23
|
+
parseFloat32,
|
|
24
|
+
parseInteger,
|
|
25
|
+
} from "./numeric.js";
|
|
26
|
+
import { parseQName } from "./qname.js";
|
|
27
|
+
import { normalizeWhitespace, parseBoolean } from "./strings.js";
|
|
28
|
+
import { getType } from "./types.js";
|
|
29
|
+
|
|
30
|
+
const identity = (text) => text;
|
|
31
|
+
const durationKinds = ["duration", "yearMonthDuration", "dayTimeDuration"];
|
|
32
|
+
const dateTimeKinds = [
|
|
33
|
+
"dateTime",
|
|
34
|
+
"date",
|
|
35
|
+
"time",
|
|
36
|
+
"gYearMonth",
|
|
37
|
+
"gYear",
|
|
38
|
+
"gMonthDay",
|
|
39
|
+
"gDay",
|
|
40
|
+
"gMonth",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/** Parsers by cast-primitive local name: (text, kind, options) → value | null. */
|
|
44
|
+
const parsers = {
|
|
45
|
+
untypedAtomic: identity,
|
|
46
|
+
string: identity,
|
|
47
|
+
anyURI: identity,
|
|
48
|
+
boolean: parseBoolean,
|
|
49
|
+
decimal: parseDecimal,
|
|
50
|
+
integer: parseInteger,
|
|
51
|
+
double: parseDouble,
|
|
52
|
+
float: parseFloat32,
|
|
53
|
+
hexBinary: parseHexBinary,
|
|
54
|
+
base64Binary: parseBase64Binary,
|
|
55
|
+
QName: (text, _kind, options) => parseQName(text, options.resolveNamespace),
|
|
56
|
+
};
|
|
57
|
+
for (const kind of durationKinds) {
|
|
58
|
+
parsers[kind] = (text) => parseDuration(kind, text);
|
|
59
|
+
}
|
|
60
|
+
for (const kind of dateTimeKinds) {
|
|
61
|
+
parsers[kind] = (text) => parseDateTime(kind, text);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Formatters by cast-primitive local name: value → canonical string. */
|
|
65
|
+
const formatters = {
|
|
66
|
+
untypedAtomic: identity,
|
|
67
|
+
string: identity,
|
|
68
|
+
anyURI: identity,
|
|
69
|
+
boolean: String,
|
|
70
|
+
decimal: String,
|
|
71
|
+
integer: String,
|
|
72
|
+
double: (value) => formatDouble(value),
|
|
73
|
+
float: (value) => formatDouble(value, true),
|
|
74
|
+
hexBinary: formatHexBinary,
|
|
75
|
+
base64Binary: formatBase64Binary,
|
|
76
|
+
QName: String,
|
|
77
|
+
NOTATION: String,
|
|
78
|
+
};
|
|
79
|
+
for (const kind of durationKinds) {
|
|
80
|
+
formatters[kind] = (value) => formatDuration(kind, value);
|
|
81
|
+
}
|
|
82
|
+
for (const kind of dateTimeKinds) {
|
|
83
|
+
formatters[kind] = (value) => formatDateTime(kind, value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Converts a string to a value of an atomic type by the rules of XSD
|
|
88
|
+
* validation: whiteSpace facet, lexical space, then the other facets.
|
|
89
|
+
* @param {string|object} typeName - Target type
|
|
90
|
+
* @param {string} text
|
|
91
|
+
* @param {object} [options]
|
|
92
|
+
* @param {import("./qname.js").NamespaceResolver} [options.resolveNamespace]
|
|
93
|
+
* - Prefix resolver for xs:QName
|
|
94
|
+
* @returns {AtomicValue}
|
|
95
|
+
* @throws {XPathError} XPST0080 for abstract types, FORG0001 for invalid
|
|
96
|
+
* text, FODT0001/FODT0002 for out-of-range dates/durations, FONS0004 for
|
|
97
|
+
* unbound QName prefixes
|
|
98
|
+
*/
|
|
99
|
+
export function fromLexical(typeName, text, options = {}) {
|
|
100
|
+
const type = getType(typeName);
|
|
101
|
+
if (type.abstract) {
|
|
102
|
+
throw new XPathError("XPST0080", `Cannot cast to ${type.prefixedName}`);
|
|
103
|
+
}
|
|
104
|
+
const parse = parsers[type.castPrimitive.localName];
|
|
105
|
+
const normalized = normalizeWhitespace(text, type.whiteSpace);
|
|
106
|
+
const value = parse
|
|
107
|
+
? parse(normalized, type.castPrimitive.localName, options)
|
|
108
|
+
: null;
|
|
109
|
+
if (value === null) {
|
|
110
|
+
throw new XPathError(
|
|
111
|
+
"FORG0001",
|
|
112
|
+
`"${text}" is not a valid ${type.prefixedName}`,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return new AtomicValue(type, checkFacets(type, value));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Canonical string of an atomic value (the result of casting it to
|
|
120
|
+
* xs:string).
|
|
121
|
+
* @param {AtomicValue} item
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
124
|
+
export function canonicalString(item) {
|
|
125
|
+
return formatters[item.type.castPrimitive.localName](item.value);
|
|
126
|
+
}
|
package/src/xdm/nodes.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Accessors of the data model for any item: string value, typed value
|
|
3
|
+
* (atomization) and effective boolean value.
|
|
4
|
+
*
|
|
5
|
+
* Nodes are DOM nodes of any implementation; only `nodeType`, `nodeValue`
|
|
6
|
+
* and `firstChild`/`nextSibling` (else `childNodes`) are read. Without a schema, elements, attributes, text
|
|
7
|
+
* and document nodes have an xs:untypedAtomic typed value; comments,
|
|
8
|
+
* processing instructions and namespace nodes an xs:string one.
|
|
9
|
+
*
|
|
10
|
+
* @module @tradik/xslt3/xdm/nodes
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { XPathError } from "../errors.js";
|
|
14
|
+
import { AtomicValue, itemKind } from "./atomic.js";
|
|
15
|
+
import { canonicalString } from "./lexical.js";
|
|
16
|
+
import { types } from "./types.js";
|
|
17
|
+
|
|
18
|
+
const TEXT_NODE = 3;
|
|
19
|
+
const CDATA_SECTION_NODE = 4;
|
|
20
|
+
const PROCESSING_INSTRUCTION_NODE = 7;
|
|
21
|
+
const COMMENT_NODE = 8;
|
|
22
|
+
/** nodeType used for XPath namespace nodes by DOM-based XPath engines. */
|
|
23
|
+
const NAMESPACE_NODE = 13;
|
|
24
|
+
const ELEMENT_NODE = 1;
|
|
25
|
+
const DOCUMENT_NODE = 9;
|
|
26
|
+
const DOCUMENT_FRAGMENT_NODE = 11;
|
|
27
|
+
const containers = new Set([
|
|
28
|
+
ELEMENT_NODE,
|
|
29
|
+
DOCUMENT_NODE,
|
|
30
|
+
DOCUMENT_FRAGMENT_NODE,
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Appends the text of the descendant text nodes of a node.
|
|
35
|
+
* @param {Node} node
|
|
36
|
+
* @param {string[]} parts
|
|
37
|
+
*/
|
|
38
|
+
function collectText(node, parts) {
|
|
39
|
+
// firstChild/nextSibling: childNodes is a live list behind a Proxy in
|
|
40
|
+
// jsdom, several times slower to iterate
|
|
41
|
+
if (node.firstChild !== undefined) {
|
|
42
|
+
for (let child = node.firstChild; child; child = child.nextSibling) {
|
|
43
|
+
const type = child.nodeType;
|
|
44
|
+
if (type === TEXT_NODE || type === CDATA_SECTION_NODE) {
|
|
45
|
+
parts.push(child.nodeValue);
|
|
46
|
+
} else if (type === ELEMENT_NODE) {
|
|
47
|
+
collectText(child, parts);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const child of node.childNodes) {
|
|
53
|
+
if (child.nodeType === TEXT_NODE || child.nodeType === CDATA_SECTION_NODE) {
|
|
54
|
+
parts.push(child.nodeValue);
|
|
55
|
+
} else if (child.nodeType === ELEMENT_NODE) {
|
|
56
|
+
collectText(child, parts);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* String value of a node (dm:string-value): the concatenated descendant
|
|
63
|
+
* text of elements and documents, the value of other nodes.
|
|
64
|
+
* @param {Node} node
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
export function nodeStringValue(node) {
|
|
68
|
+
return stringValueOf(node, node.nodeType);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* nodeStringValue() of a node whose nodeType was read already.
|
|
73
|
+
* @param {Node} node
|
|
74
|
+
* @param {number} type - Its nodeType
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/
|
|
77
|
+
function stringValueOf(node, type) {
|
|
78
|
+
if (!containers.has(type)) return node.nodeValue ?? "";
|
|
79
|
+
const first = node.firstChild;
|
|
80
|
+
if (first && first.nextSibling === null) {
|
|
81
|
+
// A single child, typically the text of a leaf element
|
|
82
|
+
const type = first.nodeType;
|
|
83
|
+
if (type === TEXT_NODE || type === CDATA_SECTION_NODE) {
|
|
84
|
+
return first.nodeValue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const parts = [];
|
|
88
|
+
collectText(node, parts);
|
|
89
|
+
return parts.join("");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Node types whose typed value is an xs:string. */
|
|
93
|
+
const stringTyped = new Set([
|
|
94
|
+
PROCESSING_INSTRUCTION_NODE,
|
|
95
|
+
COMMENT_NODE,
|
|
96
|
+
NAMESPACE_NODE,
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Typed value of a node (dm:typed-value) in a non-schema-aware processor.
|
|
101
|
+
* @param {Node} node
|
|
102
|
+
* @returns {AtomicValue}
|
|
103
|
+
*/
|
|
104
|
+
export function typedValue(node) {
|
|
105
|
+
const nodeType = node.nodeType;
|
|
106
|
+
const type = stringTyped.has(nodeType) ? types.string : types.untypedAtomic;
|
|
107
|
+
return new AtomicValue(type, stringValueOf(node, nodeType));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Atomizes a sequence (fn:data): atomic values are kept, nodes give their
|
|
112
|
+
* typed value, arrays are flattened recursively.
|
|
113
|
+
* @param {Array} sequence
|
|
114
|
+
* @returns {AtomicValue[]}
|
|
115
|
+
* @throws {XPathError} FOTY0013 for maps and function items
|
|
116
|
+
*/
|
|
117
|
+
export function atomize(sequence) {
|
|
118
|
+
const result = [];
|
|
119
|
+
for (const item of sequence) {
|
|
120
|
+
const kind = itemKind(item);
|
|
121
|
+
if (kind === "atomic") result.push(item);
|
|
122
|
+
else if (kind === "node") result.push(typedValue(item));
|
|
123
|
+
else if (kind === "array") result.push(...atomize(item.members.flat()));
|
|
124
|
+
else throw new XPathError("FOTY0013", `A ${kind} cannot be atomized`);
|
|
125
|
+
}
|
|
126
|
+
return result;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* String value of an item (fn:string).
|
|
131
|
+
* @param {*} item - Atomic value or node
|
|
132
|
+
* @returns {string}
|
|
133
|
+
* @throws {XPathError} FOTY0014 for maps, arrays and function items
|
|
134
|
+
*/
|
|
135
|
+
export function stringValue(item) {
|
|
136
|
+
const kind = itemKind(item);
|
|
137
|
+
if (kind === "atomic") return canonicalString(item);
|
|
138
|
+
if (kind === "node") return nodeStringValue(item);
|
|
139
|
+
throw new XPathError("FOTY0014", `A ${kind} has no string value`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const stringPrimitives = new Set(["string", "anyURI", "untypedAtomic"]);
|
|
143
|
+
const numericPrimitives = new Set(["decimal", "float", "double"]);
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Effective boolean value of a sequence (fn:boolean, XPath 3.1 2.4.3).
|
|
147
|
+
* @param {Array} sequence
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
* @throws {XPathError} FORG0006 when the sequence has no EBV
|
|
150
|
+
*/
|
|
151
|
+
export function effectiveBooleanValue(sequence) {
|
|
152
|
+
if (sequence.length === 0) return false;
|
|
153
|
+
const first = sequence[0];
|
|
154
|
+
const kind = itemKind(first);
|
|
155
|
+
if (kind === "node") return true;
|
|
156
|
+
if (kind === "atomic" && sequence.length === 1) {
|
|
157
|
+
const { type, value } = first;
|
|
158
|
+
const primitive = type.primitive.localName;
|
|
159
|
+
if (primitive === "boolean") return value;
|
|
160
|
+
if (stringPrimitives.has(primitive)) return value.length > 0;
|
|
161
|
+
if (numericPrimitives.has(primitive)) {
|
|
162
|
+
if (typeof value === "object") return value.sign() !== 0;
|
|
163
|
+
return !(Number.isNaN(value) || value === 0 || value === 0n);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
throw new XPathError(
|
|
167
|
+
"FORG0006",
|
|
168
|
+
"The sequence has no effective boolean value",
|
|
169
|
+
);
|
|
170
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lexical forms of the numeric types (XSD 1.1 Part 2) and their canonical
|
|
3
|
+
* string representations for casting to xs:string (F&O 3.1 section 19.1.2).
|
|
4
|
+
*
|
|
5
|
+
* Value representations: xs:decimal is a {@link Decimal}, xs:integer and
|
|
6
|
+
* its subtypes are BigInts, xs:double and xs:float are JS numbers (floats
|
|
7
|
+
* rounded with Math.fround; -0, NaN and the infinities are kept).
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/xdm/numeric
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { Decimal } from "./decimal.js";
|
|
13
|
+
|
|
14
|
+
const integerPattern = /^[+-]?\d+$/;
|
|
15
|
+
const doublePattern = /^[+-]?(\d+(\.\d*)?|\.\d+)([Ee][+-]?\d+)?$/;
|
|
16
|
+
const specialDoubles = new Map([
|
|
17
|
+
["INF", Infinity],
|
|
18
|
+
["+INF", Infinity],
|
|
19
|
+
["-INF", -Infinity],
|
|
20
|
+
["NaN", NaN],
|
|
21
|
+
]);
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parses an xs:integer lexical form (already whitespace-collapsed).
|
|
25
|
+
* @param {string} text
|
|
26
|
+
* @returns {bigint|null} null when invalid
|
|
27
|
+
*/
|
|
28
|
+
export function parseInteger(text) {
|
|
29
|
+
return integerPattern.test(text) ? BigInt(text) : null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parses an xs:decimal lexical form.
|
|
34
|
+
* @param {string} text
|
|
35
|
+
* @returns {Decimal|null} null when invalid
|
|
36
|
+
*/
|
|
37
|
+
export function parseDecimal(text) {
|
|
38
|
+
return Decimal.parse(text);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parses an xs:double lexical form, including "INF", "+INF", "-INF",
|
|
43
|
+
* "NaN" and negative zero.
|
|
44
|
+
* @param {string} text
|
|
45
|
+
* @returns {number|null} null when invalid
|
|
46
|
+
*/
|
|
47
|
+
export function parseDouble(text) {
|
|
48
|
+
if (specialDoubles.has(text)) return specialDoubles.get(text);
|
|
49
|
+
return doublePattern.test(text) ? Number(text) : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parses an xs:float lexical form. The decimal text is rounded to a double
|
|
54
|
+
* first and then to a float, which can differ from direct rounding in the
|
|
55
|
+
* last bit for rare inputs.
|
|
56
|
+
* @param {string} text
|
|
57
|
+
* @returns {number|null} null when invalid
|
|
58
|
+
*/
|
|
59
|
+
export function parseFloat32(text) {
|
|
60
|
+
const value = parseDouble(text);
|
|
61
|
+
return value === null ? null : Math.fround(value);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Shortest decimal digits that read back as the same double, or as the
|
|
66
|
+
* same float when `isFloat` is set.
|
|
67
|
+
* @param {number} n - Finite, non-zero
|
|
68
|
+
* @param {boolean} isFloat
|
|
69
|
+
* @returns {{digits: string, exponent: number}} significant digits
|
|
70
|
+
* d1d2d3… without trailing zeros; the value is d1.d2d3… × 10^exponent
|
|
71
|
+
*/
|
|
72
|
+
function shortestDigits(n, isFloat) {
|
|
73
|
+
let text = n.toExponential();
|
|
74
|
+
for (let precision = 0; isFloat && precision < 9; precision++) {
|
|
75
|
+
const candidate = n.toExponential(precision);
|
|
76
|
+
if (Math.fround(Number(candidate)) === n) {
|
|
77
|
+
text = candidate;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const [mantissa, exponent] = text.split("e");
|
|
82
|
+
const digits = mantissa.replace(/^-/, "").replace(".", "").replace(/0+$/, "");
|
|
83
|
+
return { digits, exponent: Number(exponent) };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Canonical string of an xs:double or xs:float value (F&O 3.1 19.1.2):
|
|
88
|
+
* plain decimal notation for magnitudes in [1e-6, 1e6), otherwise
|
|
89
|
+
* scientific "1.0E10" / "1.5E-7"; "INF", "-INF", "NaN", "0", "-0".
|
|
90
|
+
* @param {number} n
|
|
91
|
+
* @param {boolean} [isFloat=false] - Use the shortest float digits
|
|
92
|
+
* @returns {string}
|
|
93
|
+
*/
|
|
94
|
+
export function formatDouble(n, isFloat = false) {
|
|
95
|
+
if (Number.isNaN(n)) return "NaN";
|
|
96
|
+
if (n === Infinity) return "INF";
|
|
97
|
+
if (n === -Infinity) return "-INF";
|
|
98
|
+
if (n === 0) return Object.is(n, -0) ? "-0" : "0";
|
|
99
|
+
const { digits, exponent } = shortestDigits(n, isFloat);
|
|
100
|
+
const sign = n < 0 ? "-" : "";
|
|
101
|
+
const magnitude = Math.abs(n);
|
|
102
|
+
if (magnitude >= 1e-6 && magnitude < 1e6) {
|
|
103
|
+
return (
|
|
104
|
+
sign + Decimal.of(BigInt(digits), digits.length - 1 - exponent).toString()
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return `${sign}${digits[0]}.${digits.slice(1) || "0"}E${exponent}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Converts a finite double or float to the decimal it prints as (the
|
|
112
|
+
* shortest round-trip digits), as used for casting to xs:decimal.
|
|
113
|
+
* @param {number} n - Finite number
|
|
114
|
+
* @param {boolean} [isFloat=false]
|
|
115
|
+
* @returns {Decimal}
|
|
116
|
+
*/
|
|
117
|
+
export function numberToDecimal(n, isFloat = false) {
|
|
118
|
+
if (n === 0) return Decimal.ZERO;
|
|
119
|
+
const { digits, exponent } = shortestDigits(n, isFloat);
|
|
120
|
+
const unscaled = BigInt(digits);
|
|
121
|
+
return Decimal.of(n < 0 ? -unscaled : unscaled, digits.length - 1 - exponent);
|
|
122
|
+
}
|
package/src/xdm/qname.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xs:QName and xs:NOTATION values: `{ prefix, namespaceURI, localName }`,
|
|
3
|
+
* with "" for no prefix and no namespace. Casting from a string needs a
|
|
4
|
+
* namespace resolver for the prefix.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xdm/qname
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { XPathError } from "../errors.js";
|
|
10
|
+
import { namePatterns } from "./types.js";
|
|
11
|
+
|
|
12
|
+
/** A qualified name. */
|
|
13
|
+
export class QNameValue {
|
|
14
|
+
/**
|
|
15
|
+
* @param {string} namespaceURI - "" for no namespace
|
|
16
|
+
* @param {string} localName
|
|
17
|
+
* @param {string} [prefix=""]
|
|
18
|
+
*/
|
|
19
|
+
constructor(namespaceURI, localName, prefix = "") {
|
|
20
|
+
this.namespaceURI = namespaceURI;
|
|
21
|
+
this.localName = localName;
|
|
22
|
+
this.prefix = prefix;
|
|
23
|
+
Object.freeze(this);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @returns {string} "prefix:local" or "local" */
|
|
27
|
+
toString() {
|
|
28
|
+
return this.prefix ? `${this.prefix}:${this.localName}` : this.localName;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolves a namespace prefix ("" for the default namespace used for
|
|
34
|
+
* unprefixed names) to a URI, or returns null/undefined when unbound.
|
|
35
|
+
* @callback NamespaceResolver
|
|
36
|
+
* @param {string} prefix
|
|
37
|
+
* @returns {string|null|undefined}
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parses a lexical QName (whitespace already collapsed).
|
|
42
|
+
* @param {string} text
|
|
43
|
+
* @param {NamespaceResolver} [resolveNamespace] - Without it only
|
|
44
|
+
* unprefixed names can be cast, and they get no namespace
|
|
45
|
+
* @returns {QNameValue|null} null when the text is not a lexical QName
|
|
46
|
+
* @throws {XPathError} FONS0004 when the prefix is not bound
|
|
47
|
+
*/
|
|
48
|
+
export function parseQName(text, resolveNamespace) {
|
|
49
|
+
const parts = text.split(":");
|
|
50
|
+
if (
|
|
51
|
+
parts.length > 2 ||
|
|
52
|
+
!parts.every((part) => namePatterns.ncName.test(part))
|
|
53
|
+
) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const [prefix, localName] = parts.length === 2 ? parts : ["", parts[0]];
|
|
57
|
+
const namespaceURI = resolveNamespace?.(prefix) ?? (prefix ? null : "");
|
|
58
|
+
if (namespaceURI === null) {
|
|
59
|
+
throw new XPathError(
|
|
60
|
+
"FONS0004",
|
|
61
|
+
`No namespace is bound to the prefix ${prefix}`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return new QNameValue(namespaceURI, localName, prefix);
|
|
65
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String helpers of the data model: the whiteSpace facet, the xs:boolean
|
|
3
|
+
* lexical space and codepoint string comparison.
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/xdm/strings
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Applies a whiteSpace facet value (XSD 1.1 Part 2 section 4.3.6). Only the
|
|
10
|
+
* XML whitespace characters (space, tab, CR, LF) are affected.
|
|
11
|
+
* @param {string} text
|
|
12
|
+
* @param {"preserve"|"replace"|"collapse"} mode
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeWhitespace(text, mode) {
|
|
16
|
+
if (mode === "preserve") return text;
|
|
17
|
+
const replaced = text.replace(/[\t\n\r]/g, " ");
|
|
18
|
+
// not String#trim, which also strips non-XML whitespace such as U+00A0
|
|
19
|
+
return mode === "replace"
|
|
20
|
+
? replaced
|
|
21
|
+
: replaced.replace(/ {2,}/g, " ").replace(/^ | $/g, "");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parses the xs:boolean lexical forms "true", "false", "1", "0".
|
|
26
|
+
* @param {string} text - Whitespace-collapsed
|
|
27
|
+
* @returns {boolean|null} null when invalid
|
|
28
|
+
*/
|
|
29
|
+
export function parseBoolean(text) {
|
|
30
|
+
if (text === "true" || text === "1") return true;
|
|
31
|
+
if (text === "false" || text === "0") return false;
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Compares strings by Unicode codepoints (the default collation), unlike
|
|
37
|
+
* the JS operators, which compare UTF-16 code units.
|
|
38
|
+
* @param {string} a
|
|
39
|
+
* @param {string} b
|
|
40
|
+
* @returns {number} -1, 0 or 1
|
|
41
|
+
*/
|
|
42
|
+
export function compareCodepoints(a, b) {
|
|
43
|
+
if (a === b) return 0;
|
|
44
|
+
const length = Math.min(a.length, b.length);
|
|
45
|
+
for (let i = 0; i < length; i++) {
|
|
46
|
+
const x = a.codePointAt(i);
|
|
47
|
+
const y = b.codePointAt(i);
|
|
48
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
49
|
+
if (x > 0xffff) i++;
|
|
50
|
+
}
|
|
51
|
+
return a.length < b.length ? -1 : 1;
|
|
52
|
+
}
|