@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,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Patterns (XSLT 3.0 section 5.5): parsed as XPath expressions and
|
|
3
|
+
* compiled to matchers. A union is split into alternatives, each with its
|
|
4
|
+
* default priority (section 6.5); intersect and except combine patterns,
|
|
5
|
+
* and predicate patterns `.[P]` match any item.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/patterns/compile
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { parseXPath } from "../../xpath/syntax/index.js";
|
|
11
|
+
import { xsltError } from "../names.js";
|
|
12
|
+
import { parentOf } from "../../xpath/eval/domNodes.js";
|
|
13
|
+
import { derive } from "../runtime/context.js";
|
|
14
|
+
import { compileGeneralPath } from "./general.js";
|
|
15
|
+
import { checkPattern, isPredicatePattern } from "./grammar.js";
|
|
16
|
+
import { checkGroupingCalls } from "./patternChecks.js";
|
|
17
|
+
import { compilePathPattern } from "./paths.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {object} PatternAlternative
|
|
21
|
+
* @property {(item: *, xc: object) => boolean} matches
|
|
22
|
+
* @property {number} priority - Default priority
|
|
23
|
+
* @property {string} key - Index key: "e{uri}local", "a{uri}local", "k"
|
|
24
|
+
* followed by a nodeType, or "*" for any item
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Splits a pattern into its union alternatives.
|
|
29
|
+
* @param {object} ast
|
|
30
|
+
* @returns {object[]}
|
|
31
|
+
*/
|
|
32
|
+
function alternativesOf(ast) {
|
|
33
|
+
if (ast.type === "SetExpr" && ast.operator === "union") {
|
|
34
|
+
return [...alternativesOf(ast.left), ...alternativesOf(ast.right)];
|
|
35
|
+
}
|
|
36
|
+
return [ast];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Compiles a predicate pattern `.` or `.[P]` (XSLT 3.0), which matches
|
|
41
|
+
* any item.
|
|
42
|
+
* @param {object} ast
|
|
43
|
+
* @param {object} env
|
|
44
|
+
* @returns {PatternAlternative}
|
|
45
|
+
*/
|
|
46
|
+
function compilePredicatePattern(ast, env) {
|
|
47
|
+
const run = env.cx.exprs.compileAst(ast, env.sc, env.vars);
|
|
48
|
+
return {
|
|
49
|
+
matches: (item, xc) =>
|
|
50
|
+
run(env.cx.patternContext(item, xc, env.local)).length > 0,
|
|
51
|
+
priority: ast.type === "ContextItemExpr" ? -1 : 1,
|
|
52
|
+
key: "*",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Compiles `A intersect B` or `A except B` (XSLT 3.0).
|
|
58
|
+
* @param {object} ast
|
|
59
|
+
* @param {object} env
|
|
60
|
+
* @returns {PatternAlternative}
|
|
61
|
+
*/
|
|
62
|
+
function compileSetPattern(ast, env) {
|
|
63
|
+
const side = (part) =>
|
|
64
|
+
alternativesOf(part).map((alternative) =>
|
|
65
|
+
compileAlternative(alternative, env),
|
|
66
|
+
);
|
|
67
|
+
const left = side(ast.left);
|
|
68
|
+
const right = side(ast.right);
|
|
69
|
+
const any = (list, item, xc) => list.some((a) => a.matches(item, xc));
|
|
70
|
+
const intersect = ast.operator === "intersect";
|
|
71
|
+
// a node with a parent: both sides are evaluated from the same context
|
|
72
|
+
const formal = compileGeneralPath(ast, env);
|
|
73
|
+
return {
|
|
74
|
+
matches: (item, xc) =>
|
|
75
|
+
parentOf(item)
|
|
76
|
+
? formal(item, xc)
|
|
77
|
+
: any(left, item, xc) && any(right, item, xc) === intersect,
|
|
78
|
+
priority: 0.5,
|
|
79
|
+
key: left.length === 1 ? left[0].key : "*",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Compiles one alternative; while it is tested, current() is the item
|
|
85
|
+
* being matched, and a dynamic error is a non-match (XSLT 3.0 5.5.4).
|
|
86
|
+
* @param {object} ast
|
|
87
|
+
* @param {object} env - `{cx, sc, vars, local, text}`
|
|
88
|
+
* @returns {PatternAlternative}
|
|
89
|
+
*/
|
|
90
|
+
function compileAlternative(ast, env) {
|
|
91
|
+
let alternative;
|
|
92
|
+
if (ast.type === "SetExpr") alternative = compileSetPattern(ast, env);
|
|
93
|
+
else if (isPredicatePattern(ast)) {
|
|
94
|
+
alternative = compilePredicatePattern(ast, env);
|
|
95
|
+
} else alternative = compilePathPattern(ast, env);
|
|
96
|
+
const { matches } = alternative;
|
|
97
|
+
alternative.matches = (item, xc) => {
|
|
98
|
+
try {
|
|
99
|
+
return matches(item, xc.item === item ? xc : derive(xc, { item }));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (!error.code || error.code === "XTDE0640") throw error;
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
return alternative;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Compiles a pattern written on a stylesheet element.
|
|
110
|
+
* @param {string} text
|
|
111
|
+
* @param {Element} element
|
|
112
|
+
* @param {object} cx - Stylesheet compiler
|
|
113
|
+
* @param {object|null} [vars] - Local variables in scope (patterns of
|
|
114
|
+
* instructions); template and key patterns see the global ones only
|
|
115
|
+
* @returns {{text: string, alternatives: PatternAlternative[]}}
|
|
116
|
+
* @throws {import("../../errors.js").XPathError} XTSE0340 for an
|
|
117
|
+
* expression that is not a pattern
|
|
118
|
+
*/
|
|
119
|
+
export function compilePattern(text, element, cx, vars) {
|
|
120
|
+
let ast;
|
|
121
|
+
try {
|
|
122
|
+
ast = parseXPath(text);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throw error.code === "XPST0003"
|
|
125
|
+
? xsltError("XTSE0340", `Invalid pattern "${text}": ${error.message}`)
|
|
126
|
+
: error;
|
|
127
|
+
}
|
|
128
|
+
checkPattern(ast, text);
|
|
129
|
+
checkGroupingCalls(ast);
|
|
130
|
+
const env = {
|
|
131
|
+
cx,
|
|
132
|
+
sc: cx.exprs.staticContext(element),
|
|
133
|
+
vars: vars === undefined ? cx.globalScope().vars : vars,
|
|
134
|
+
local: vars !== undefined,
|
|
135
|
+
text,
|
|
136
|
+
};
|
|
137
|
+
const alternatives = alternativesOf(ast).map((alternative) =>
|
|
138
|
+
compileAlternative(alternative, env),
|
|
139
|
+
);
|
|
140
|
+
return { text, alternatives };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Whether an item matches any alternative of a pattern.
|
|
145
|
+
* @param {{alternatives: PatternAlternative[]}} pattern
|
|
146
|
+
* @param {*} item
|
|
147
|
+
* @param {object} xc
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
150
|
+
export const patternMatches = (pattern, item, xc) =>
|
|
151
|
+
pattern.alternatives.some((alternative) => alternative.matches(item, xc));
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path patterns of any form (XSLT 3.0 section 5.5.3), matched by their
|
|
3
|
+
* formal meaning: a node N matches when it is selected by the equivalent
|
|
4
|
+
* expression evaluated from some node of its tree. The steps are read
|
|
5
|
+
* from the last one: for each step, the context nodes from which the
|
|
6
|
+
* step can select N (the parent for child::, the ancestors for
|
|
7
|
+
* descendant::...) are evaluated, and those that do select N carry on
|
|
8
|
+
* to the step before. Used for the patterns the fast matcher of paths.js
|
|
9
|
+
* does not handle: descendant axes, parenthesized steps, filtered rooted
|
|
10
|
+
* steps, positional predicates over whole paths.
|
|
11
|
+
*
|
|
12
|
+
* @module @tradik/xslt3/xslt/patterns/general
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { parentOf } from "../../xpath/eval/domNodes.js";
|
|
16
|
+
import { compileAxisOrigins, filterAlone } from "./axisOrigins.js";
|
|
17
|
+
import { isRootedStep, unfiltered } from "./grammar.js";
|
|
18
|
+
|
|
19
|
+
/** @param {*} node @returns {boolean} whether a node is a document node */
|
|
20
|
+
const isDocument = (node) => node?.nodeType === 9 || node?.nodeType === 11;
|
|
21
|
+
|
|
22
|
+
/** @param {*} item @returns {boolean} */
|
|
23
|
+
const isNode = (item) => typeof item?.nodeType === "number";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Compiles one step: `origins(node, xc)` returns the context nodes from
|
|
27
|
+
* which the step selects the node.
|
|
28
|
+
* @param {object} step
|
|
29
|
+
* @param {object} env - See compile.js
|
|
30
|
+
* @param {boolean} first - First step of a relative pattern
|
|
31
|
+
* @returns {{origins: (node: Node, xc: object) => Node[]}}
|
|
32
|
+
*/
|
|
33
|
+
function compileStep(step, env, first) {
|
|
34
|
+
const run = env.cx.exprs.compileAst(step, env.sc, env.vars);
|
|
35
|
+
const selects = (context, node, xc) =>
|
|
36
|
+
run(env.cx.patternContext(context, xc, env.local)).includes(node);
|
|
37
|
+
if (step.type === "AxisStep") {
|
|
38
|
+
return compileAxisOrigins(step, env, first, run);
|
|
39
|
+
}
|
|
40
|
+
if (step.type === "FilterExpr") {
|
|
41
|
+
const inner = compileStep(unfiltered(step), env, first);
|
|
42
|
+
const predicates = [];
|
|
43
|
+
for (let s = step; s.type === "FilterExpr"; s = s.base) {
|
|
44
|
+
predicates.unshift(s.predicate);
|
|
45
|
+
}
|
|
46
|
+
const alone = filterAlone(predicates, env);
|
|
47
|
+
return {
|
|
48
|
+
origins: (node, xc) =>
|
|
49
|
+
inner.origins(node, xc).flatMap((c) => {
|
|
50
|
+
// a parentless node selected by itself (child-or-top)
|
|
51
|
+
if (c === node && !parentOf(node)) return alone(node, xc);
|
|
52
|
+
return selects(c, node, xc) ? [c] : [];
|
|
53
|
+
}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (step.type === "SetExpr") {
|
|
57
|
+
// each side is a path of its own (its first step adjusted)
|
|
58
|
+
const sides = [step.left, step.right].map((s) => compileStep(s, env, true));
|
|
59
|
+
const union = step.operator === "union";
|
|
60
|
+
return {
|
|
61
|
+
origins: (node, xc) => {
|
|
62
|
+
const candidates = new Set(sides[0].origins(node, xc));
|
|
63
|
+
if (union) {
|
|
64
|
+
for (const c of sides[1].origins(node, xc)) candidates.add(c);
|
|
65
|
+
}
|
|
66
|
+
return [...candidates].filter((c) => selects(c, node, xc));
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (step.type === "PathExpr" && !step.absolute) {
|
|
71
|
+
return compilePath(step, env, true);
|
|
72
|
+
}
|
|
73
|
+
// an absolute path or a rooted step: any node of the tree is a context
|
|
74
|
+
return { origins: (node, xc) => (selects(node, node, xc) ? [node] : []) };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Compiles the steps of a relative path.
|
|
79
|
+
* @param {{steps: object[]}} path
|
|
80
|
+
* @param {object} env
|
|
81
|
+
* @param {boolean} first - The path starts a relative pattern
|
|
82
|
+
* @returns {{origins: (node: Node, xc: object) => Node[]}}
|
|
83
|
+
*/
|
|
84
|
+
function compilePath(path, env, first) {
|
|
85
|
+
const steps = path.steps.map((step, i) =>
|
|
86
|
+
compileStep(step, env, first && i === 0),
|
|
87
|
+
);
|
|
88
|
+
return {
|
|
89
|
+
origins(node, xc) {
|
|
90
|
+
let current = [node];
|
|
91
|
+
for (let k = steps.length - 1; k >= 0 && current.length > 0; k--) {
|
|
92
|
+
const next = new Set();
|
|
93
|
+
for (const n of current) {
|
|
94
|
+
for (const c of steps[k].origins(n, xc)) next.add(c);
|
|
95
|
+
}
|
|
96
|
+
current = [...next];
|
|
97
|
+
}
|
|
98
|
+
return current;
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Compiles a path pattern of any form.
|
|
105
|
+
* @param {object} ast - PathExpr or a single step
|
|
106
|
+
* @param {object} env - See compile.js
|
|
107
|
+
* @returns {(item: *, xc: object) => boolean}
|
|
108
|
+
*/
|
|
109
|
+
export function compileGeneralPath(ast, env) {
|
|
110
|
+
const steps = ast.type === "PathExpr" ? ast.steps : [ast];
|
|
111
|
+
const absolute = ast.type === "PathExpr" && ast.absolute;
|
|
112
|
+
const rootStep = !absolute && isRootedStep(steps[0]) ? steps[0] : null;
|
|
113
|
+
const rest = compilePath(
|
|
114
|
+
{ steps: rootStep ? steps.slice(1) : steps },
|
|
115
|
+
env,
|
|
116
|
+
!absolute && !rootStep,
|
|
117
|
+
);
|
|
118
|
+
const root = rootStep
|
|
119
|
+
? env.cx.exprs.compileAst(rootStep, env.sc, env.vars)
|
|
120
|
+
: null;
|
|
121
|
+
return (item, xc) => {
|
|
122
|
+
if (!isNode(item)) return false;
|
|
123
|
+
const origins = rest.origins(item, xc);
|
|
124
|
+
if (absolute) {
|
|
125
|
+
return origins.some((c) => isDocument(c) && !parentOf(c));
|
|
126
|
+
}
|
|
127
|
+
if (!root) return origins.length > 0;
|
|
128
|
+
if (origins.length === 0) return false;
|
|
129
|
+
const roots = root(env.cx.patternContext(item, xc, env.local));
|
|
130
|
+
return origins.some((c) => roots.includes(c));
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The grammar of patterns (XSLT 3.0 section 5.5.2): a pattern is parsed
|
|
3
|
+
* as an XPath expression, then checked against the production Pattern30:
|
|
4
|
+
* a predicate pattern `.[P]...`, or unions, intersections and
|
|
5
|
+
* differences of path patterns whose steps are forward axis steps,
|
|
6
|
+
* parenthesized patterns with predicates, and rooted first steps
|
|
7
|
+
* (`$var`, doc(), id(), element-with-id(), key(), root()) whose arguments
|
|
8
|
+
* are variable references or literals.
|
|
9
|
+
*
|
|
10
|
+
* @module @tradik/xslt3/xslt/patterns/grammar
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { NS } from "../../functions/signatures.js";
|
|
14
|
+
import { withoutComments } from "../compiler/avt.js";
|
|
15
|
+
import { xsltError } from "../names.js";
|
|
16
|
+
|
|
17
|
+
/** Axes allowed in patterns. */
|
|
18
|
+
const FORWARD_AXES = new Set([
|
|
19
|
+
"child",
|
|
20
|
+
"descendant",
|
|
21
|
+
"attribute",
|
|
22
|
+
"self",
|
|
23
|
+
"descendant-or-self",
|
|
24
|
+
"namespace",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
/** Functions allowed as the root of a path pattern, by local name. */
|
|
28
|
+
const ROOT_FUNCTIONS = new Set(["doc", "id", "element-with-id", "key", "root"]);
|
|
29
|
+
|
|
30
|
+
/** Argument kinds allowed in those calls. */
|
|
31
|
+
const ARGUMENTS = new Set(["VarRef", "StringLiteral", "NumericLiteral"]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} message
|
|
35
|
+
* @returns {Error} XTSE0340
|
|
36
|
+
*/
|
|
37
|
+
const invalid = (message) => xsltError("XTSE0340", message);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Removes the predicates of a filter expression.
|
|
41
|
+
* @param {object} ast
|
|
42
|
+
* @returns {object} the filtered expression
|
|
43
|
+
*/
|
|
44
|
+
export function unfiltered(ast) {
|
|
45
|
+
let base = ast;
|
|
46
|
+
while (base.type === "FilterExpr") base = base.base;
|
|
47
|
+
return base;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Whether an expression is a predicate pattern `.[P]...`.
|
|
52
|
+
* @param {object} ast
|
|
53
|
+
* @returns {boolean}
|
|
54
|
+
*/
|
|
55
|
+
export const isPredicatePattern = (ast) =>
|
|
56
|
+
unfiltered(ast).type === "ContextItemExpr";
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Whether a call is a rooted step: one of the allowed functions in the
|
|
60
|
+
* standard function namespace.
|
|
61
|
+
* @param {object} ast - A FunctionCall
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function isRootFunction(ast) {
|
|
65
|
+
const { prefix, uri, local } = ast.name;
|
|
66
|
+
const standard =
|
|
67
|
+
uri === NS.fn || (uri === null && (prefix === null || prefix === "fn"));
|
|
68
|
+
return standard && ROOT_FUNCTIONS.has(local);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Checks a rooted step `$v[...]` or `f(...)[...]`.
|
|
73
|
+
* @param {object} ast
|
|
74
|
+
*/
|
|
75
|
+
function checkRooted(ast) {
|
|
76
|
+
const base = unfiltered(ast);
|
|
77
|
+
if (base.type === "VarRef") return;
|
|
78
|
+
if (!isRootFunction(base)) {
|
|
79
|
+
throw invalid(`${base.name.local}() cannot start a pattern`);
|
|
80
|
+
}
|
|
81
|
+
if (base.name.local === "root" && base.arguments.length > 0) {
|
|
82
|
+
throw invalid("root() in a pattern takes no argument");
|
|
83
|
+
}
|
|
84
|
+
for (const argument of base.arguments) {
|
|
85
|
+
if (!ARGUMENTS.has(argument.type)) {
|
|
86
|
+
throw invalid("Arguments in a pattern must be variables or literals");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Whether a step is rooted (a variable or a function call, filtered).
|
|
93
|
+
* @param {object} ast
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
export const isRootedStep = (ast) => {
|
|
97
|
+
const type = unfiltered(ast).type;
|
|
98
|
+
return type === "VarRef" || type === "FunctionCall";
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Checks one step of a path pattern.
|
|
103
|
+
* @param {object} ast
|
|
104
|
+
* @param {boolean} first - The first step of a relative path
|
|
105
|
+
*/
|
|
106
|
+
function checkStep(ast, first) {
|
|
107
|
+
if (ast.type === "AxisStep") {
|
|
108
|
+
if (!FORWARD_AXES.has(ast.axis)) {
|
|
109
|
+
throw invalid(`The ${ast.axis} axis is not allowed in a pattern`);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (isRootedStep(ast)) {
|
|
114
|
+
if (!first) throw invalid("A rooted step must come first in a pattern");
|
|
115
|
+
checkRooted(ast);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const base = unfiltered(ast);
|
|
119
|
+
if (
|
|
120
|
+
base.type === "SetExpr" ||
|
|
121
|
+
base.type === "PathExpr" ||
|
|
122
|
+
(base.type === "AxisStep" && base !== ast)
|
|
123
|
+
) {
|
|
124
|
+
checkUnion(base);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
throw invalid("Invalid step in a pattern");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Checks a UnionExprP (no predicate pattern inside).
|
|
132
|
+
* @param {object} ast
|
|
133
|
+
*/
|
|
134
|
+
function checkUnion(ast) {
|
|
135
|
+
if (ast.type === "SetExpr") {
|
|
136
|
+
checkUnion(ast.left);
|
|
137
|
+
checkUnion(ast.right);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (ast.type === "PathExpr") {
|
|
141
|
+
ast.steps.forEach((step, i) => checkStep(step, i === 0 && !ast.absolute));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
checkStep(ast, true);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Functions a pattern cannot call, with their error codes. */
|
|
148
|
+
const FORBIDDEN = {
|
|
149
|
+
"current-merge-group": "XTSE3470",
|
|
150
|
+
"current-merge-key": "XTSE3500",
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Checks that a pattern calls none of the forbidden functions.
|
|
155
|
+
* @param {*} node - A node of the syntax tree (or any value in it)
|
|
156
|
+
*/
|
|
157
|
+
function checkFunctions(node) {
|
|
158
|
+
if (Array.isArray(node)) {
|
|
159
|
+
node.forEach(checkFunctions);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (typeof node !== "object" || node === null) return;
|
|
163
|
+
if (node.type === "FunctionCall" || node.type === "NamedFunctionRef") {
|
|
164
|
+
const code = FORBIDDEN[node.name.local];
|
|
165
|
+
const { uri, prefix } = node.name;
|
|
166
|
+
const standard =
|
|
167
|
+
uri === NS.fn || (uri === null && (prefix ?? "fn") === "fn");
|
|
168
|
+
if (code && standard) {
|
|
169
|
+
throw xsltError(code, `${node.name.local}() cannot be used in a pattern`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const value of Object.values(node)) checkFunctions(value);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Checks a pattern.
|
|
177
|
+
* @param {object} ast - The parsed pattern
|
|
178
|
+
* @param {string} text - Its source text
|
|
179
|
+
* @throws {import("../../errors.js").XPathError} XTSE0340
|
|
180
|
+
*/
|
|
181
|
+
export function checkPattern(ast, text) {
|
|
182
|
+
checkFunctions(ast);
|
|
183
|
+
if (isPredicatePattern(ast)) {
|
|
184
|
+
if (/\S/.test(withoutComments(text.slice(0, unfiltered(ast).start)))) {
|
|
185
|
+
throw invalid("A predicate pattern cannot be parenthesized");
|
|
186
|
+
}
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
checkUnion(ast);
|
|
190
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modes and their template rules (XSLT 3.0 sections 6.4 to 6.6): rules
|
|
3
|
+
* ranked by import precedence, priority and declaration order, indexed by
|
|
4
|
+
* the node kind and name their patterns can match, so finding the rule
|
|
5
|
+
* for a node tests only the candidates for that node.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/patterns/modes
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { xsltError } from "../names.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A template rule: one alternative of the pattern of a template.
|
|
14
|
+
* @typedef {object} Rule
|
|
15
|
+
* @property {object} template - The compiled template
|
|
16
|
+
* @property {import("./compile.js").PatternAlternative} alternative
|
|
17
|
+
* @property {number} priority
|
|
18
|
+
* @property {number} precedence - Import precedence
|
|
19
|
+
* @property {number} position - Declaration order
|
|
20
|
+
* @property {number} [rank] - Position in the ranking (0 is best)
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The index keys of an item: specific (by name) and by kind.
|
|
25
|
+
* @param {*} item
|
|
26
|
+
* @returns {string[]}
|
|
27
|
+
*/
|
|
28
|
+
function itemKeys(item) {
|
|
29
|
+
switch (item?.nodeType) {
|
|
30
|
+
case 1:
|
|
31
|
+
return [`e{${item.namespaceURI ?? ""}}${item.localName}`, "k1"];
|
|
32
|
+
case 2:
|
|
33
|
+
return [`a{${item.namespaceURI ?? ""}}${item.localName}`, "k2"];
|
|
34
|
+
case 3:
|
|
35
|
+
case 4:
|
|
36
|
+
return ["k3"];
|
|
37
|
+
case 11:
|
|
38
|
+
return ["k9"];
|
|
39
|
+
case undefined:
|
|
40
|
+
return [];
|
|
41
|
+
default:
|
|
42
|
+
return [`k${item.nodeType}`];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** A mode: its rules and how unmatched items are processed. */
|
|
47
|
+
export class Mode {
|
|
48
|
+
/** @param {string} name - Clark name, "#default" for the unnamed mode */
|
|
49
|
+
constructor(name) {
|
|
50
|
+
this.name = name;
|
|
51
|
+
/** @type {Rule[]} */
|
|
52
|
+
this.rules = [];
|
|
53
|
+
this.onNoMatch = "text-only-copy";
|
|
54
|
+
this.onMultipleMatch = "use-last";
|
|
55
|
+
this.index = null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** @param {Rule} rule */
|
|
59
|
+
add(rule) {
|
|
60
|
+
this.rules.push(rule);
|
|
61
|
+
this.index = null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Ranks the rules and builds the index. */
|
|
65
|
+
build() {
|
|
66
|
+
this.rules.sort(
|
|
67
|
+
(a, b) =>
|
|
68
|
+
b.precedence - a.precedence ||
|
|
69
|
+
b.priority - a.priority ||
|
|
70
|
+
b.position - a.position,
|
|
71
|
+
);
|
|
72
|
+
const index = new Map([["*", []]]);
|
|
73
|
+
this.rules.forEach((rule, rank) => {
|
|
74
|
+
rule.rank = rank;
|
|
75
|
+
const key = rule.alternative.key;
|
|
76
|
+
if (!index.has(key)) index.set(key, []);
|
|
77
|
+
index.get(key).push(rule);
|
|
78
|
+
});
|
|
79
|
+
this.index = index;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The candidate rules of an item, best first.
|
|
84
|
+
* @param {*} item
|
|
85
|
+
* @returns {Rule[]}
|
|
86
|
+
*/
|
|
87
|
+
candidates(item) {
|
|
88
|
+
if (!this.index) {
|
|
89
|
+
this.build();
|
|
90
|
+
this.memo = new Map();
|
|
91
|
+
}
|
|
92
|
+
const keys = itemKeys(item);
|
|
93
|
+
const memoKey = keys[0] ?? "";
|
|
94
|
+
let result = this.memo.get(memoKey);
|
|
95
|
+
if (!result) {
|
|
96
|
+
const lists = [this.index.get("*")];
|
|
97
|
+
for (const key of keys) {
|
|
98
|
+
const list = this.index.get(key);
|
|
99
|
+
if (list) lists.push(list);
|
|
100
|
+
}
|
|
101
|
+
result = lists.flat().sort((a, b) => a.rank - b.rank);
|
|
102
|
+
this.memo.set(memoKey, result);
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Finds the best matching rule.
|
|
109
|
+
* @param {*} item
|
|
110
|
+
* @param {object} xc - Context whose item is `item`
|
|
111
|
+
* @param {(rule: Rule) => boolean} [accept] - Restricts the rules
|
|
112
|
+
* (xsl:apply-imports, xsl:next-match)
|
|
113
|
+
* @returns {Rule|null}
|
|
114
|
+
*/
|
|
115
|
+
find(item, xc, accept) {
|
|
116
|
+
const candidates = this.candidates(item);
|
|
117
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
118
|
+
const rule = candidates[i];
|
|
119
|
+
if (accept && !accept(rule)) continue;
|
|
120
|
+
if (!rule.alternative.matches(item, xc)) continue;
|
|
121
|
+
if (this.onMultipleMatch === "fail") {
|
|
122
|
+
this.checkAmbiguity(candidates, i, item, xc, accept);
|
|
123
|
+
}
|
|
124
|
+
return rule;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Raises XTDE0540 when another rule of the same rank matches.
|
|
131
|
+
* @param {Rule[]} candidates
|
|
132
|
+
* @param {number} i - Index of the chosen rule
|
|
133
|
+
* @param {*} item
|
|
134
|
+
* @param {object} xc
|
|
135
|
+
* @param {Function} [accept]
|
|
136
|
+
*/
|
|
137
|
+
checkAmbiguity(candidates, i, item, xc, accept) {
|
|
138
|
+
const chosen = candidates[i];
|
|
139
|
+
for (let j = i + 1; j < candidates.length; j++) {
|
|
140
|
+
const other = candidates[j];
|
|
141
|
+
if (
|
|
142
|
+
other.precedence !== chosen.precedence ||
|
|
143
|
+
other.priority !== chosen.priority
|
|
144
|
+
) {
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
if (other.template === chosen.template) continue;
|
|
148
|
+
if ((!accept || accept(other)) && other.alternative.matches(item, xc)) {
|
|
149
|
+
throw xsltError("XTDE0540", "Several template rules match the item");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|