@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
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memoized stylesheet functions (XSLT 3.0 section 10.3.2, cache="yes" and
|
|
3
|
+
* new-each-time="no"): a call with the same arguments as an earlier one
|
|
4
|
+
* of the transformation returns the earlier result. Atomic values are
|
|
5
|
+
* compared by type and value, nodes and other items by identity.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/runtime/functionCache
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { isAtomic } from "../../xdm/atomic.js";
|
|
11
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The value of an atomic item as a string: canonical, except for QNames
|
|
15
|
+
* (their namespace, not their prefix).
|
|
16
|
+
* @param {import("../../xdm/atomic.js").AtomicValue} item
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
function valueKey(item) {
|
|
20
|
+
const { namespaceURI, localName } = item.value ?? {};
|
|
21
|
+
return localName === undefined
|
|
22
|
+
? canonicalString(item)
|
|
23
|
+
: `{${namespaceURI}}${localName}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The key of a list of arguments.
|
|
28
|
+
* @param {Array<Array>} args - Converted argument values
|
|
29
|
+
* @param {object} tx - The transformation (numbers the other items)
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
function argumentsKey(args, tx) {
|
|
33
|
+
tx.itemIds ??= { ids: new WeakMap(), next: 0 };
|
|
34
|
+
const { ids } = tx.itemIds;
|
|
35
|
+
const idOf = (item) => {
|
|
36
|
+
if (!ids.has(item)) ids.set(item, tx.itemIds.next++);
|
|
37
|
+
return ids.get(item);
|
|
38
|
+
};
|
|
39
|
+
return JSON.stringify(
|
|
40
|
+
args.map((value) =>
|
|
41
|
+
value.map((item) =>
|
|
42
|
+
isAtomic(item) ? [item.type.name, valueKey(item)] : idOf(item),
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Calls a function through its cache.
|
|
50
|
+
* @param {object} compiled - The compiled function (its cache key)
|
|
51
|
+
* @param {Array<Array>} args - Converted argument values
|
|
52
|
+
* @param {object} tx - The transformation
|
|
53
|
+
* @param {() => Array} call - Computes the result
|
|
54
|
+
* @returns {Array}
|
|
55
|
+
*/
|
|
56
|
+
export function cachedCall(compiled, args, tx, call) {
|
|
57
|
+
tx.functionCaches ??= new Map();
|
|
58
|
+
let cache = tx.functionCaches.get(compiled);
|
|
59
|
+
if (!cache) tx.functionCaches.set(compiled, (cache = new Map()));
|
|
60
|
+
const key = argumentsKey(args, tx);
|
|
61
|
+
if (cache.has(key)) return cache.get(key);
|
|
62
|
+
const result = call();
|
|
63
|
+
cache.set(key, result);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Calls of stylesheet functions (xsl:function) from XPath: the arguments
|
|
3
|
+
* converted to the parameter types and bound, the body run with an
|
|
4
|
+
* absent focus, the result converted to the declared type.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/runtime/functionCall
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { cachedCall } from "./functionCache.js";
|
|
10
|
+
import { SequenceReceiver } from "./sequenceReceiver.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Calls a compiled stylesheet function.
|
|
14
|
+
* @param {{params: object[], body: Array, convert: Function,
|
|
15
|
+
* memo?: boolean}} compiled
|
|
16
|
+
* @param {Array<Array>} args - Argument values
|
|
17
|
+
* @param {object} context - XPath dynamic context of the call
|
|
18
|
+
* @returns {Array} the result
|
|
19
|
+
*/
|
|
20
|
+
export function callFunction(compiled, args, context) {
|
|
21
|
+
const { tx } = context.xc;
|
|
22
|
+
const values = compiled.params.map((param, i) => param.convert(args[i]));
|
|
23
|
+
if (compiled.memo) {
|
|
24
|
+
return cachedCall(compiled, values, tx, () => run(compiled, values, tx));
|
|
25
|
+
}
|
|
26
|
+
return run(compiled, values, tx);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Runs the body of a stylesheet function.
|
|
31
|
+
* @param {object} compiled
|
|
32
|
+
* @param {Array<Array>} values - Converted argument values
|
|
33
|
+
* @param {object} tx - The transformation
|
|
34
|
+
* @returns {Array} the result
|
|
35
|
+
*/
|
|
36
|
+
function run(compiled, values, tx) {
|
|
37
|
+
let env = tx.globalEnv;
|
|
38
|
+
for (const value of values) env = { value, next: env };
|
|
39
|
+
const xc = {
|
|
40
|
+
tx,
|
|
41
|
+
item: undefined,
|
|
42
|
+
position: 0,
|
|
43
|
+
size: 0,
|
|
44
|
+
env,
|
|
45
|
+
mode: tx.defaultMode,
|
|
46
|
+
rule: null,
|
|
47
|
+
group: undefined,
|
|
48
|
+
groupKey: undefined,
|
|
49
|
+
regex: undefined,
|
|
50
|
+
tunnel: null,
|
|
51
|
+
temporary: true,
|
|
52
|
+
dyn: null,
|
|
53
|
+
};
|
|
54
|
+
const out = new SequenceReceiver(tx.scratch);
|
|
55
|
+
tx.machine.runBody(compiled.body, xc, out);
|
|
56
|
+
return compiled.convert(out.items);
|
|
57
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The functions XSLT adds to XPath (XSLT 3.0 section 20): current(),
|
|
3
|
+
* current-group(), current-grouping-key(), regex-group(), key(),
|
|
4
|
+
* document() and the unparsed entity functions, with the availability
|
|
5
|
+
* functions (availability.js) and copy-of()/snapshot() (copyFunctions.js).
|
|
6
|
+
* They read the XSLT context from the XPath dynamic context
|
|
7
|
+
* (`context.xc`, see context.js).
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/xslt/runtime/functions
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { stringItem } from "../../xpath/eval/atomics.js";
|
|
13
|
+
import { rootOf } from "../../xpath/eval/domNodes.js";
|
|
14
|
+
import { resolveUri } from "../../xpath/eval/uris.js";
|
|
15
|
+
import { AtomicValue, isNode } from "../../xdm/atomic.js";
|
|
16
|
+
import { types } from "../../xdm/types.js";
|
|
17
|
+
import { stringValue } from "../../xdm/nodes.js";
|
|
18
|
+
import { xsltError } from "../names.js";
|
|
19
|
+
import { accumulatorFunctions } from "./accumulatorFunctions.js";
|
|
20
|
+
import { availabilityFunctions, nameArgument } from "./availability.js";
|
|
21
|
+
import { copyFunctions } from "./copyFunctions.js";
|
|
22
|
+
import { keyLookup } from "./keys.js";
|
|
23
|
+
import { mergeFunctions } from "./merging.js";
|
|
24
|
+
import { streamAvailableFunctions } from "./streamAvailable.js";
|
|
25
|
+
import { transformFunctions } from "./transformFunction.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Whether a node is a node of the subtree rooted at another.
|
|
29
|
+
* @param {Node} node
|
|
30
|
+
* @param {Node} top
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
function within(node, top) {
|
|
34
|
+
for (let a = node; a; a = a.parentNode ?? a.ownerElement) {
|
|
35
|
+
if (a === top) return true;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* key().
|
|
42
|
+
* @param {Array} args
|
|
43
|
+
* @param {object} context
|
|
44
|
+
* @returns {Array}
|
|
45
|
+
*/
|
|
46
|
+
function key([[name], values, top], context) {
|
|
47
|
+
const { uri, local } = nameArgument(name.value, context, "XTDE1260");
|
|
48
|
+
let root = top?.[0];
|
|
49
|
+
if (!top) {
|
|
50
|
+
if (!isNode(context.contextItem)) {
|
|
51
|
+
throw xsltError("XTDE1270", "key() needs a context node");
|
|
52
|
+
}
|
|
53
|
+
root = rootOf(context.contextItem);
|
|
54
|
+
if (root.nodeType !== 9 && root.nodeType !== 11) {
|
|
55
|
+
throw xsltError("XTDE1270", "key() needs a node in a document");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const nodes = keyLookup(
|
|
59
|
+
context.xc,
|
|
60
|
+
`{${uri}}${local}`,
|
|
61
|
+
values,
|
|
62
|
+
rootOf(root),
|
|
63
|
+
context.sc?.owner,
|
|
64
|
+
);
|
|
65
|
+
return root === rootOf(root) ? nodes : nodes.filter((n) => within(n, root));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* document().
|
|
70
|
+
* @param {Array} args
|
|
71
|
+
* @param {object} context
|
|
72
|
+
* @returns {Array}
|
|
73
|
+
*/
|
|
74
|
+
function documentFunction([items, base], context) {
|
|
75
|
+
const results = [];
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
const fixedBase = base?.length
|
|
78
|
+
? (rootOf(base[0]).documentURI ?? context.staticBaseUri)
|
|
79
|
+
: null;
|
|
80
|
+
const baseUri =
|
|
81
|
+
fixedBase ??
|
|
82
|
+
(isNode(item)
|
|
83
|
+
? (rootOf(item).documentURI ?? context.staticBaseUri)
|
|
84
|
+
: context.staticBaseUri);
|
|
85
|
+
const uri = resolveUri(stringValue(item).replace(/#.*$/, ""), baseUri);
|
|
86
|
+
const own = context.xc.tx.stylesheet.moduleDocuments.get(uri);
|
|
87
|
+
results.push(own ?? context.loadDocument(uri));
|
|
88
|
+
}
|
|
89
|
+
return context.order.sort(results);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** XSLT functions, as function library definitions. */
|
|
93
|
+
export const xsltFunctions = [
|
|
94
|
+
...availabilityFunctions,
|
|
95
|
+
...copyFunctions,
|
|
96
|
+
{
|
|
97
|
+
local: "current",
|
|
98
|
+
params: [],
|
|
99
|
+
returns: "item()",
|
|
100
|
+
impl: (_, context) => {
|
|
101
|
+
if (context.xc.item === undefined) {
|
|
102
|
+
throw xsltError("XPDY0002", "current() has no current item");
|
|
103
|
+
}
|
|
104
|
+
return [context.xc.item];
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
local: "current-group",
|
|
109
|
+
params: [],
|
|
110
|
+
returns: "item()*",
|
|
111
|
+
impl: (_, context) => {
|
|
112
|
+
// absent in dynamic function calls (XSLT 3.0)
|
|
113
|
+
if (context.xc.group === undefined || context.dynamicCall) {
|
|
114
|
+
throw xsltError(
|
|
115
|
+
"XTDE1061",
|
|
116
|
+
"current-group(): there is no current group",
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return context.xc.group;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
local: "current-grouping-key",
|
|
124
|
+
params: [],
|
|
125
|
+
returns: "xs:anyAtomicType*",
|
|
126
|
+
impl: (_, context) => {
|
|
127
|
+
if (context.xc.groupKey === undefined || context.dynamicCall) {
|
|
128
|
+
throw xsltError("XTDE1071", "current-grouping-key(): there is no key");
|
|
129
|
+
}
|
|
130
|
+
return context.xc.groupKey;
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
local: "regex-group",
|
|
135
|
+
params: ["xs:integer"],
|
|
136
|
+
returns: "xs:string",
|
|
137
|
+
impl: ([[n]], context) => [
|
|
138
|
+
stringItem(context.xc.regex?.[Number(n.value)] ?? ""),
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
local: "key",
|
|
143
|
+
params: ["xs:string", "xs:anyAtomicType*"],
|
|
144
|
+
returns: "node()*",
|
|
145
|
+
focus: true,
|
|
146
|
+
impl: key,
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
local: "key",
|
|
150
|
+
params: ["xs:string", "xs:anyAtomicType*", "node()"],
|
|
151
|
+
returns: "node()*",
|
|
152
|
+
impl: key,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
local: "document",
|
|
156
|
+
params: ["item()*"],
|
|
157
|
+
returns: "node()*",
|
|
158
|
+
impl: documentFunction,
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
local: "document",
|
|
162
|
+
params: ["item()*", "node()"],
|
|
163
|
+
returns: "node()*",
|
|
164
|
+
impl: documentFunction,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
local: "current-output-uri",
|
|
168
|
+
params: [],
|
|
169
|
+
returns: "xs:anyURI?",
|
|
170
|
+
impl: (_, context) => {
|
|
171
|
+
// absent in temporary output state, patterns and dynamic calls
|
|
172
|
+
const { temporary, outputUri } = context.xc;
|
|
173
|
+
return temporary || context.dynamicCall || outputUri === undefined
|
|
174
|
+
? []
|
|
175
|
+
: [new AtomicValue(types.anyURI, outputUri)];
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
// the DOM gives no unparsed entities: their URI and public id are ""
|
|
179
|
+
...["unparsed-entity-uri", "unparsed-entity-public-id"].flatMap((local) =>
|
|
180
|
+
[["xs:string"], ["xs:string", "node()"]].map((params) => ({
|
|
181
|
+
local,
|
|
182
|
+
params,
|
|
183
|
+
returns: "xs:string",
|
|
184
|
+
impl: () => [stringItem("")],
|
|
185
|
+
})),
|
|
186
|
+
),
|
|
187
|
+
...accumulatorFunctions,
|
|
188
|
+
...mergeFunctions,
|
|
189
|
+
...streamAvailableFunctions,
|
|
190
|
+
...transformFunctions,
|
|
191
|
+
];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The groups of xsl:for-each-group (XSLT 3.0 section 14): by grouping
|
|
3
|
+
* keys (group-by, group-adjacent, single or composite keys) and by
|
|
4
|
+
* patterns (group-starting-with, group-ending-with).
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/runtime/groups
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { atomize } from "../../xdm/nodes.js";
|
|
10
|
+
import { xsltError } from "../names.js";
|
|
11
|
+
import { patternMatches } from "../patterns/compile.js";
|
|
12
|
+
import { comparisonKey } from "./atomicKeys.js";
|
|
13
|
+
import { withFocus } from "./context.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* A group in the making.
|
|
17
|
+
* @typedef {object} Group
|
|
18
|
+
* @property {Array} items - Its population, in order
|
|
19
|
+
* @property {Array|undefined} key - Its grouping key
|
|
20
|
+
* @property {string} [keyString] - The key compared
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The grouping keys of an item, each with the string it is compared by:
|
|
25
|
+
* one per value, or one for the whole sequence of a composite key.
|
|
26
|
+
* @param {Array} values - Atomized key values
|
|
27
|
+
* @param {object} collation
|
|
28
|
+
* @param {boolean} composite
|
|
29
|
+
* @returns {Array<{key: Array, keyString: string}>}
|
|
30
|
+
*/
|
|
31
|
+
function keysOf(values, collation, composite) {
|
|
32
|
+
const strings = values.map((value) => comparisonKey(value, collation));
|
|
33
|
+
if (composite) return [{ key: values, keyString: JSON.stringify(strings) }];
|
|
34
|
+
return values.map((value, i) => ({ key: [value], keyString: strings[i] }));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Groups by keys (group-by, group-adjacent).
|
|
39
|
+
* @param {Array} items
|
|
40
|
+
* @param {(xc: object) => Array} evaluateKey - Evaluates the key expression
|
|
41
|
+
* @param {object} xc
|
|
42
|
+
* @param {{adjacent: boolean, collation: object, composite: boolean}} how
|
|
43
|
+
* @returns {Group[]}
|
|
44
|
+
* @throws {import("../../errors.js").XPathError} XTTE1100 for an
|
|
45
|
+
* adjacent key that is not a single value
|
|
46
|
+
*/
|
|
47
|
+
export function groupByKeys(items, evaluateKey, xc, how) {
|
|
48
|
+
const { adjacent, collation, composite } = how;
|
|
49
|
+
const groups = [];
|
|
50
|
+
const byKey = new Map();
|
|
51
|
+
items.forEach((item, i) => {
|
|
52
|
+
const values = atomize(
|
|
53
|
+
evaluateKey(withFocus(xc, item, i + 1, items.length)),
|
|
54
|
+
);
|
|
55
|
+
if (adjacent && !composite && values.length !== 1) {
|
|
56
|
+
throw xsltError("XTTE1100", "A grouping key must be a single value");
|
|
57
|
+
}
|
|
58
|
+
const keys = keysOf(values, collation, composite);
|
|
59
|
+
if (adjacent) {
|
|
60
|
+
const [{ key, keyString }] = keys;
|
|
61
|
+
const last = groups.at(-1);
|
|
62
|
+
if (last && last.keyString === keyString) last.items.push(item);
|
|
63
|
+
else groups.push({ items: [item], key, keyString });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
for (const { key, keyString } of keys) {
|
|
68
|
+
if (seen.has(keyString)) continue;
|
|
69
|
+
seen.add(keyString);
|
|
70
|
+
let group = byKey.get(keyString);
|
|
71
|
+
if (!group) {
|
|
72
|
+
group = { items: [], key };
|
|
73
|
+
byKey.set(keyString, group);
|
|
74
|
+
groups.push(group);
|
|
75
|
+
}
|
|
76
|
+
group.items.push(item);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return groups;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Groups by patterns (group-starting-with, group-ending-with); in XSLT 3.0
|
|
84
|
+
* the population may hold any items.
|
|
85
|
+
* @param {Array} items
|
|
86
|
+
* @param {object} pattern - Compiled pattern
|
|
87
|
+
* @param {object} xc
|
|
88
|
+
* @param {boolean} starting - group-starting-with
|
|
89
|
+
* @returns {Group[]}
|
|
90
|
+
*/
|
|
91
|
+
export function groupByPattern(items, pattern, xc, starting) {
|
|
92
|
+
const groups = [];
|
|
93
|
+
let current = null;
|
|
94
|
+
items.forEach((item, i) => {
|
|
95
|
+
const matches = patternMatches(
|
|
96
|
+
pattern,
|
|
97
|
+
item,
|
|
98
|
+
withFocus(xc, item, i + 1, items.length),
|
|
99
|
+
);
|
|
100
|
+
if (!current || (starting && matches && current.items.length > 0)) {
|
|
101
|
+
current = { items: [], key: undefined };
|
|
102
|
+
groups.push(current);
|
|
103
|
+
}
|
|
104
|
+
current.items.push(item);
|
|
105
|
+
if (!starting && matches) current = null;
|
|
106
|
+
});
|
|
107
|
+
return groups;
|
|
108
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The invocation of a stylesheet (XSLT 3.0 section 2.3): an initial
|
|
3
|
+
* named template, an initial mode applied to the initial match selection,
|
|
4
|
+
* or an initial function; then the principal result as a document (a
|
|
5
|
+
* sequence when build-tree is "no", see rawResults.js).
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/runtime/invocation
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { toSequence } from "../../xpath/eval/values.js";
|
|
11
|
+
import { isEntryPoint } from "../compiler/packageChecks.js";
|
|
12
|
+
import { xsltError } from "../names.js";
|
|
13
|
+
import { applyTemplates, invokeTemplate, NO_ARGS } from "./apply.js";
|
|
14
|
+
import { callFunction } from "./functionCall.js";
|
|
15
|
+
import { clarkName, normalizeParams } from "./params.js";
|
|
16
|
+
import { resultReceiver } from "./rawResults.js";
|
|
17
|
+
|
|
18
|
+
/** The default initial template of XSLT 3.0. */
|
|
19
|
+
const INITIAL_TEMPLATE =
|
|
20
|
+
"{http://www.w3.org/1999/XSL/Transform}initial-template";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A result tree as a Document when it is well-formed (one element, no
|
|
24
|
+
* text at the top), else the document fragment that holds it.
|
|
25
|
+
* @param {DocumentFragment} fragment
|
|
26
|
+
* @param {() => Document} createDocument
|
|
27
|
+
* @returns {Document|DocumentFragment}
|
|
28
|
+
*/
|
|
29
|
+
export function finishTree(fragment, createDocument) {
|
|
30
|
+
const children = [...fragment.childNodes];
|
|
31
|
+
const elements = children.filter((child) => child.nodeType === 1).length;
|
|
32
|
+
if (elements !== 1 || children.some((child) => child.nodeType === 3)) {
|
|
33
|
+
return fragment;
|
|
34
|
+
}
|
|
35
|
+
const document = createDocument();
|
|
36
|
+
for (const child of children) {
|
|
37
|
+
document.appendChild(document.importNode(child, true));
|
|
38
|
+
}
|
|
39
|
+
return document;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The principal result tree: the document that owns the fragment when
|
|
44
|
+
* the tree is well-formed (one element, no text at the top), else the
|
|
45
|
+
* fragment.
|
|
46
|
+
* @param {DocumentFragment} fragment
|
|
47
|
+
* @param {Document} document - Its owner, still empty
|
|
48
|
+
* @returns {Document|DocumentFragment}
|
|
49
|
+
*/
|
|
50
|
+
function asDocument(fragment, document) {
|
|
51
|
+
const children = [...fragment.childNodes];
|
|
52
|
+
const wellFormed =
|
|
53
|
+
children.filter((child) => child.nodeType === 1).length === 1 &&
|
|
54
|
+
!children.some((child) => child.nodeType === 3);
|
|
55
|
+
if (!wellFormed) return fragment;
|
|
56
|
+
document.appendChild(fragment);
|
|
57
|
+
return document;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The first step of a transformation: the initial template or mode.
|
|
62
|
+
* @param {object} tx - The transformation
|
|
63
|
+
* @param {object} options - See CompiledStylesheet.transform
|
|
64
|
+
* @param {Node|undefined} source
|
|
65
|
+
* @returns {Function} a step (see machine.js)
|
|
66
|
+
*/
|
|
67
|
+
function initialStep(tx, options, source) {
|
|
68
|
+
const { stylesheet } = tx;
|
|
69
|
+
const args = {
|
|
70
|
+
params: normalizeParams(options.templateParams),
|
|
71
|
+
tunnel: normalizeParams(options.tunnelParams),
|
|
72
|
+
};
|
|
73
|
+
let name = options.initialTemplate;
|
|
74
|
+
const fromItems =
|
|
75
|
+
options.initialMode !== undefined ||
|
|
76
|
+
source !== undefined ||
|
|
77
|
+
options.initialMatchSelection !== undefined;
|
|
78
|
+
if (name === undefined && !fromItems) name = INITIAL_TEMPLATE;
|
|
79
|
+
if (name !== undefined) {
|
|
80
|
+
const key = clarkName(name);
|
|
81
|
+
const template = stylesheet.namedTemplates.get(key);
|
|
82
|
+
if (!template || !isEntryPoint(stylesheet, "template", key)) {
|
|
83
|
+
throw xsltError("XTDE0040", `No public template named ${name}`);
|
|
84
|
+
}
|
|
85
|
+
const supplied = args.params.size || args.tunnel.size ? args : NO_ARGS;
|
|
86
|
+
return (xc, out, machine) =>
|
|
87
|
+
invokeTemplate(template, xc, out, machine, supplied);
|
|
88
|
+
}
|
|
89
|
+
const selection =
|
|
90
|
+
options.initialMatchSelection ?? (source === undefined ? [] : [source]);
|
|
91
|
+
if (source === undefined && options.initialMatchSelection === undefined) {
|
|
92
|
+
throw xsltError("XTDE0044", "No source document and no initial template");
|
|
93
|
+
}
|
|
94
|
+
return (xc, out, machine) =>
|
|
95
|
+
applyTemplates(
|
|
96
|
+
toSequence(selection),
|
|
97
|
+
tx.defaultMode,
|
|
98
|
+
xc,
|
|
99
|
+
out,
|
|
100
|
+
machine,
|
|
101
|
+
args,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Calls the initial function.
|
|
107
|
+
* @param {object} tx
|
|
108
|
+
* @param {{name: string, args: Array}} entry
|
|
109
|
+
* @returns {Array} its result
|
|
110
|
+
*/
|
|
111
|
+
function callInitialFunction(tx, { name, args }) {
|
|
112
|
+
const key = `${clarkName(name)}#${args.length}`;
|
|
113
|
+
const compiled = tx.stylesheet.functionBodies.get(key);
|
|
114
|
+
if (!compiled || !isEntryPoint(tx.stylesheet, "function", key)) {
|
|
115
|
+
throw xsltError("XTDE0041", `No public function ${key}`);
|
|
116
|
+
}
|
|
117
|
+
return callFunction(compiled, args.map(toSequence), { xc: tx.globalContext });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Runs the initial template, mode or function.
|
|
122
|
+
* @param {object} tx
|
|
123
|
+
* @param {object} options
|
|
124
|
+
* @param {Node|undefined} source
|
|
125
|
+
* @param {() => Document} createDocument
|
|
126
|
+
* @returns {{principal: *, principalOutput: object|null, secondary: Map,
|
|
127
|
+
* messages: Array}} principalOutput: the parameters of an
|
|
128
|
+
* xsl:result-document that wrote the principal result
|
|
129
|
+
*/
|
|
130
|
+
export function invoke(tx, options, source, createDocument) {
|
|
131
|
+
const result = { secondary: tx.secondary, messages: tx.messages };
|
|
132
|
+
if (options.initialFunction) {
|
|
133
|
+
return {
|
|
134
|
+
principal: callInitialFunction(tx, options.initialFunction),
|
|
135
|
+
...result,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const step = initialStep(tx, options, source);
|
|
139
|
+
const document = createDocument();
|
|
140
|
+
const { receiver, value } = resultReceiver(
|
|
141
|
+
tx.stylesheet.outputFor(null),
|
|
142
|
+
() => document,
|
|
143
|
+
tx.options.buildTree,
|
|
144
|
+
);
|
|
145
|
+
tx.machine.runBody([step], tx.globalContext, receiver);
|
|
146
|
+
const raw = value();
|
|
147
|
+
const written = Array.isArray(raw) ? raw.length > 0 : raw.hasChildNodes();
|
|
148
|
+
if (tx.principalOverride && written) {
|
|
149
|
+
throw xsltError("XTDE1490", "The principal result is written twice");
|
|
150
|
+
}
|
|
151
|
+
let principal = tx.principalOverride ?? raw;
|
|
152
|
+
if (principal === raw && !Array.isArray(raw)) {
|
|
153
|
+
principal = asDocument(raw, document);
|
|
154
|
+
}
|
|
155
|
+
return { principal, principalOutput: tx.principalOutput, ...result };
|
|
156
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The frame that runs xsl:iterate on the work stack (XSLT 3.0 section
|
|
3
|
+
* 7.2): one body per item, pushed after the previous one is done, so a
|
|
4
|
+
* long input sequence costs no JavaScript recursion. Each body sees the
|
|
5
|
+
* iteration state in `xc.iteration`, where xsl:next-iteration leaves the
|
|
6
|
+
* new parameter values and xsl:break marks the end.
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xslt/runtime/iteration
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { derive } from "./context.js";
|
|
12
|
+
import { BodyFrame } from "./machine.js";
|
|
13
|
+
|
|
14
|
+
/** Runs the iterations of an xsl:iterate, then its xsl:on-completion. */
|
|
15
|
+
export class IterateFrame {
|
|
16
|
+
/**
|
|
17
|
+
* @param {object} spec
|
|
18
|
+
* @param {Array} spec.items - The input sequence
|
|
19
|
+
* @param {object[]} spec.params - Compiled xsl:param children
|
|
20
|
+
* @param {import("./machine.js").Body} spec.steps - The body
|
|
21
|
+
* @param {import("./machine.js").Body} spec.completion - xsl:on-completion
|
|
22
|
+
* @param {object} spec.xc - Context of the instruction
|
|
23
|
+
* @param {object} spec.out - Receiver
|
|
24
|
+
*/
|
|
25
|
+
constructor({ items, params, steps, completion, xc, out }) {
|
|
26
|
+
this.items = items;
|
|
27
|
+
this.params = params;
|
|
28
|
+
this.steps = steps;
|
|
29
|
+
this.completion = completion;
|
|
30
|
+
this.xc = xc;
|
|
31
|
+
this.out = out;
|
|
32
|
+
this.index = 0;
|
|
33
|
+
/** @type {Array[]|null} the parameter values, computed on the first advance */
|
|
34
|
+
this.values = null;
|
|
35
|
+
this.state = { next: null, broken: false };
|
|
36
|
+
this.done = false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The initial values of the parameters, each seeing the ones before.
|
|
41
|
+
* @param {import("./machine.js").Machine} machine
|
|
42
|
+
* @returns {Array[]}
|
|
43
|
+
*/
|
|
44
|
+
initialValues(machine) {
|
|
45
|
+
const values = [];
|
|
46
|
+
let ctx = this.xc;
|
|
47
|
+
for (const param of this.params) {
|
|
48
|
+
const value = param.value(ctx, machine);
|
|
49
|
+
values.push(value);
|
|
50
|
+
ctx = derive(ctx, { env: { value, next: ctx.env } });
|
|
51
|
+
}
|
|
52
|
+
return values;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Applies the values given by xsl:next-iteration. */
|
|
56
|
+
applyNext() {
|
|
57
|
+
const next = this.state.next;
|
|
58
|
+
if (!next) return;
|
|
59
|
+
this.state.next = null;
|
|
60
|
+
this.params.forEach((param, i) => {
|
|
61
|
+
if (next.has(param.key)) {
|
|
62
|
+
this.values[i] = param.convert(next.get(param.key));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The context of a body: the parameters bound, a focus or none.
|
|
69
|
+
* @param {object} focus - `{item, position, size}`
|
|
70
|
+
* @returns {object}
|
|
71
|
+
*/
|
|
72
|
+
context(focus) {
|
|
73
|
+
let env = this.xc.env;
|
|
74
|
+
for (const value of this.values) env = { value, next: env };
|
|
75
|
+
return derive(this.xc, {
|
|
76
|
+
...focus,
|
|
77
|
+
env,
|
|
78
|
+
rule: null,
|
|
79
|
+
iteration: this.state,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Pushes the next body, or the completion.
|
|
85
|
+
* @param {import("./machine.js").Machine} machine
|
|
86
|
+
* @returns {boolean} false when the iteration is over
|
|
87
|
+
*/
|
|
88
|
+
advance(machine) {
|
|
89
|
+
if (this.done || this.state.broken) return false;
|
|
90
|
+
if (this.values === null) this.values = this.initialValues(machine);
|
|
91
|
+
else this.applyNext();
|
|
92
|
+
const size = this.items.length;
|
|
93
|
+
if (this.index < size) {
|
|
94
|
+
const item = this.items[this.index++];
|
|
95
|
+
const xc = this.context({ item, position: this.index, size });
|
|
96
|
+
if (this.steps.length > 0) {
|
|
97
|
+
machine.push(new BodyFrame(this.steps, xc, this.out));
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
this.done = true;
|
|
102
|
+
if (this.completion.length === 0) return false;
|
|
103
|
+
const xc = this.context({ item: undefined, position: 0, size: 0 });
|
|
104
|
+
machine.push(new BodyFrame(this.completion, xc, this.out));
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
}
|