@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.
Files changed (286) hide show
  1. package/LICENSE.md +28 -0
  2. package/README.md +98 -2
  3. package/package.json +43 -4
  4. package/src/errors.js +20 -0
  5. package/src/functions/aggregates.js +144 -0
  6. package/src/functions/arraySort.js +33 -0
  7. package/src/functions/arrays.js +181 -0
  8. package/src/functions/attributeTypes.js +58 -0
  9. package/src/functions/baseUris.js +82 -0
  10. package/src/functions/collations.js +127 -0
  11. package/src/functions/collections.js +84 -0
  12. package/src/functions/context.js +83 -0
  13. package/src/functions/core.js +167 -0
  14. package/src/functions/datetime.js +203 -0
  15. package/src/functions/deepEqual.js +132 -0
  16. package/src/functions/diagnostics.js +88 -0
  17. package/src/functions/focus.js +34 -0
  18. package/src/functions/format/dateComponents.js +176 -0
  19. package/src/functions/format/datePicture.js +115 -0
  20. package/src/functions/format/decimalFormat.js +65 -0
  21. package/src/functions/format/digitPattern.js +145 -0
  22. package/src/functions/format/formatDateTime.js +204 -0
  23. package/src/functions/format/formatInteger.js +109 -0
  24. package/src/functions/format/formatNumber.js +165 -0
  25. package/src/functions/format/fractionFormat.js +80 -0
  26. package/src/functions/format/germanWords.js +89 -0
  27. package/src/functions/format/numberLanguages.js +41 -0
  28. package/src/functions/format/numberPicture.js +202 -0
  29. package/src/functions/format/numberWords.js +155 -0
  30. package/src/functions/format/place.js +74 -0
  31. package/src/functions/format/timezoneFormat.js +89 -0
  32. package/src/functions/higherOrder.js +106 -0
  33. package/src/functions/ids.js +127 -0
  34. package/src/functions/json/index.js +17 -0
  35. package/src/functions/json/jsonChars.js +110 -0
  36. package/src/functions/json/jsonParser.js +203 -0
  37. package/src/functions/json/jsonStrings.js +116 -0
  38. package/src/functions/json/jsonToXml.js +116 -0
  39. package/src/functions/json/parseJson.js +119 -0
  40. package/src/functions/json/xmlRepresentation.js +103 -0
  41. package/src/functions/json/xmlToJson.js +147 -0
  42. package/src/functions/library.js +47 -0
  43. package/src/functions/mapArrayEqual.js +41 -0
  44. package/src/functions/maps.js +152 -0
  45. package/src/functions/math.js +73 -0
  46. package/src/functions/nodePaths.js +181 -0
  47. package/src/functions/nodes.js +199 -0
  48. package/src/functions/numeric.js +148 -0
  49. package/src/functions/options.js +58 -0
  50. package/src/functions/parseIetfDate.js +104 -0
  51. package/src/functions/parseXml.js +77 -0
  52. package/src/functions/qnames.js +68 -0
  53. package/src/functions/random.js +128 -0
  54. package/src/functions/regex/analyzeString.js +144 -0
  55. package/src/functions/regex/charClass.js +112 -0
  56. package/src/functions/regex/compile.js +81 -0
  57. package/src/functions/regex/escapes.js +109 -0
  58. package/src/functions/regex/parser.js +169 -0
  59. package/src/functions/regex/reader.js +37 -0
  60. package/src/functions/regex/regexFunctions.js +140 -0
  61. package/src/functions/regex/translate.js +102 -0
  62. package/src/functions/regex/unicodeBlocks.js +194 -0
  63. package/src/functions/registry.js +140 -0
  64. package/src/functions/sequences.js +173 -0
  65. package/src/functions/serializeFunction.js +53 -0
  66. package/src/functions/signatures.js +105 -0
  67. package/src/functions/sort.js +102 -0
  68. package/src/functions/stringMatch.js +45 -0
  69. package/src/functions/strings.js +194 -0
  70. package/src/functions/stringsCore.js +66 -0
  71. package/src/functions/support.js +106 -0
  72. package/src/functions/textDecoding.js +117 -0
  73. package/src/functions/ucaCollation.js +127 -0
  74. package/src/functions/unparsedText.js +71 -0
  75. package/src/functions/uri.js +166 -0
  76. package/src/functions/xqueryModule.js +24 -0
  77. package/src/index.js +38 -0
  78. package/src/items/array.js +84 -0
  79. package/src/items/function.js +45 -0
  80. package/src/items/map.js +120 -0
  81. package/src/items/mapKey.js +82 -0
  82. package/src/serialize/index.js +129 -0
  83. package/src/serialize/markup/content.js +74 -0
  84. package/src/serialize/markup/dialect.js +178 -0
  85. package/src/serialize/markup/htmlData.js +70 -0
  86. package/src/serialize/markup/namespaces.js +145 -0
  87. package/src/serialize/markup/prolog.js +91 -0
  88. package/src/serialize/markup/writer.js +183 -0
  89. package/src/serialize/methods/adaptive.js +131 -0
  90. package/src/serialize/methods/json.js +158 -0
  91. package/src/serialize/methods/text.js +43 -0
  92. package/src/serialize/output/buffer.js +48 -0
  93. package/src/serialize/output/encoding.js +146 -0
  94. package/src/serialize/output/expander.js +204 -0
  95. package/src/serialize/params/fromElement.js +141 -0
  96. package/src/serialize/params/fromMap.js +128 -0
  97. package/src/serialize/params/names.js +69 -0
  98. package/src/serialize/params/settings.js +153 -0
  99. package/src/serialize/params/values.js +163 -0
  100. package/src/serialize/sequence.js +111 -0
  101. package/src/xdm/arithmetic.js +155 -0
  102. package/src/xdm/atomic.js +142 -0
  103. package/src/xdm/binary.js +92 -0
  104. package/src/xdm/calendar.js +71 -0
  105. package/src/xdm/cast.js +176 -0
  106. package/src/xdm/compare.js +192 -0
  107. package/src/xdm/datetime.js +206 -0
  108. package/src/xdm/decimal.js +200 -0
  109. package/src/xdm/duration.js +128 -0
  110. package/src/xdm/generalCompare.js +152 -0
  111. package/src/xdm/index.js +47 -0
  112. package/src/xdm/lexical.js +126 -0
  113. package/src/xdm/nodes.js +170 -0
  114. package/src/xdm/numeric.js +122 -0
  115. package/src/xdm/qname.js +65 -0
  116. package/src/xdm/strings.js +52 -0
  117. package/src/xdm/temporalArithmetic.js +180 -0
  118. package/src/xdm/timeline.js +68 -0
  119. package/src/xdm/types.js +178 -0
  120. package/src/xpath/eval/atomics.js +45 -0
  121. package/src/xpath/eval/axes.js +212 -0
  122. package/src/xpath/eval/calls.js +168 -0
  123. package/src/xpath/eval/coercion.js +146 -0
  124. package/src/xpath/eval/compiler.js +71 -0
  125. package/src/xpath/eval/constructors.js +129 -0
  126. package/src/xpath/eval/decimalFormats.js +93 -0
  127. package/src/xpath/eval/descendantMemo.js +77 -0
  128. package/src/xpath/eval/documentOrder.js +142 -0
  129. package/src/xpath/eval/domNodes.js +174 -0
  130. package/src/xpath/eval/dynamicCall.js +27 -0
  131. package/src/xpath/eval/dynamicContext.js +161 -0
  132. package/src/xpath/eval/flow.js +115 -0
  133. package/src/xpath/eval/functionItems.js +194 -0
  134. package/src/xpath/eval/groupedChildren.js +88 -0
  135. package/src/xpath/eval/inline.js +71 -0
  136. package/src/xpath/eval/namespaceNodes.js +90 -0
  137. package/src/xpath/eval/nodeTests.js +164 -0
  138. package/src/xpath/eval/operators.js +190 -0
  139. package/src/xpath/eval/paths.js +163 -0
  140. package/src/xpath/eval/positional.js +81 -0
  141. package/src/xpath/eval/predicates.js +56 -0
  142. package/src/xpath/eval/primary.js +99 -0
  143. package/src/xpath/eval/resources.js +149 -0
  144. package/src/xpath/eval/scope.js +108 -0
  145. package/src/xpath/eval/sequenceType.js +193 -0
  146. package/src/xpath/eval/staticContext.js +142 -0
  147. package/src/xpath/eval/steps.js +98 -0
  148. package/src/xpath/eval/subtype.js +154 -0
  149. package/src/xpath/eval/typeExprs.js +147 -0
  150. package/src/xpath/eval/typeNames.js +101 -0
  151. package/src/xpath/eval/uris.js +21 -0
  152. package/src/xpath/eval/values.js +64 -0
  153. package/src/xpath/index.js +217 -0
  154. package/src/xpath/syntax/ast.js +130 -0
  155. package/src/xpath/syntax/chars.js +58 -0
  156. package/src/xpath/syntax/constructors.js +144 -0
  157. package/src/xpath/syntax/expressions.js +107 -0
  158. package/src/xpath/syntax/index.js +44 -0
  159. package/src/xpath/syntax/kindTests.js +134 -0
  160. package/src/xpath/syntax/lexer.js +220 -0
  161. package/src/xpath/syntax/operators.js +111 -0
  162. package/src/xpath/syntax/paths.js +102 -0
  163. package/src/xpath/syntax/postfix.js +91 -0
  164. package/src/xpath/syntax/primaries.js +149 -0
  165. package/src/xpath/syntax/steps.js +146 -0
  166. package/src/xpath/syntax/syntaxError.js +31 -0
  167. package/src/xpath/syntax/tokenStream.js +163 -0
  168. package/src/xpath/syntax/typeAst.js +76 -0
  169. package/src/xpath/syntax/typeOperators.js +127 -0
  170. package/src/xpath/syntax/types.js +158 -0
  171. package/src/xslt/api.js +153 -0
  172. package/src/xslt/compiler/acceptance.js +178 -0
  173. package/src/xslt/compiler/accumulatorDecl.js +146 -0
  174. package/src/xslt/compiler/attributeValues.js +174 -0
  175. package/src/xslt/compiler/attributes.js +211 -0
  176. package/src/xslt/compiler/avt.js +103 -0
  177. package/src/xslt/compiler/body.js +154 -0
  178. package/src/xslt/compiler/children.js +105 -0
  179. package/src/xslt/compiler/compatibility.js +130 -0
  180. package/src/xslt/compiler/components.js +179 -0
  181. package/src/xslt/compiler/contextItem.js +95 -0
  182. package/src/xslt/compiler/decimalFormats.js +132 -0
  183. package/src/xslt/compiler/declarationKinds.js +82 -0
  184. package/src/xslt/compiler/elementInfo.js +246 -0
  185. package/src/xslt/compiler/expressions.js +175 -0
  186. package/src/xslt/compiler/globalContextItem.js +92 -0
  187. package/src/xslt/compiler/globals.js +209 -0
  188. package/src/xslt/compiler/linker.js +134 -0
  189. package/src/xslt/compiler/miscDecl.js +171 -0
  190. package/src/xslt/compiler/modeDecl.js +88 -0
  191. package/src/xslt/compiler/moduleLoading.js +111 -0
  192. package/src/xslt/compiler/modules.js +158 -0
  193. package/src/xslt/compiler/outputDecl.js +209 -0
  194. package/src/xslt/compiler/overrideLinks.js +141 -0
  195. package/src/xslt/compiler/packageChecks.js +84 -0
  196. package/src/xslt/compiler/packageRegistry.js +95 -0
  197. package/src/xslt/compiler/packageTable.js +181 -0
  198. package/src/xslt/compiler/packageVersions.js +143 -0
  199. package/src/xslt/compiler/packages.js +138 -0
  200. package/src/xslt/compiler/registry.js +234 -0
  201. package/src/xslt/compiler/staticStage.js +197 -0
  202. package/src/xslt/compiler/staticVariables.js +93 -0
  203. package/src/xslt/compiler/stylesheet.js +205 -0
  204. package/src/xslt/compiler/tailPosition.js +74 -0
  205. package/src/xslt/compiler/templatesDecl.js +180 -0
  206. package/src/xslt/compiler/useAccumulators.js +76 -0
  207. package/src/xslt/compiler/useWhen.js +94 -0
  208. package/src/xslt/compiler/visibility.js +160 -0
  209. package/src/xslt/index.js +11 -0
  210. package/src/xslt/instructions/analyzeString.js +137 -0
  211. package/src/xslt/instructions/assert.js +72 -0
  212. package/src/xslt/instructions/attributeSets.js +115 -0
  213. package/src/xslt/instructions/conditional.js +99 -0
  214. package/src/xslt/instructions/control.js +123 -0
  215. package/src/xslt/instructions/copy.js +151 -0
  216. package/src/xslt/instructions/elements.js +161 -0
  217. package/src/xslt/instructions/evaluate.js +189 -0
  218. package/src/xslt/instructions/fork.js +35 -0
  219. package/src/xslt/instructions/grouping.js +106 -0
  220. package/src/xslt/instructions/index.js +94 -0
  221. package/src/xslt/instructions/iterate.js +182 -0
  222. package/src/xslt/instructions/literal.js +142 -0
  223. package/src/xslt/instructions/maps.js +72 -0
  224. package/src/xslt/instructions/merge.js +190 -0
  225. package/src/xslt/instructions/number.js +205 -0
  226. package/src/xslt/instructions/output.js +102 -0
  227. package/src/xslt/instructions/params.js +110 -0
  228. package/src/xslt/instructions/resultDocument.js +143 -0
  229. package/src/xslt/instructions/sort.js +114 -0
  230. package/src/xslt/instructions/sourceDocument.js +73 -0
  231. package/src/xslt/instructions/templates.js +153 -0
  232. package/src/xslt/instructions/text.js +151 -0
  233. package/src/xslt/instructions/try.js +186 -0
  234. package/src/xslt/instructions/variables.js +119 -0
  235. package/src/xslt/names.js +204 -0
  236. package/src/xslt/patterns/axisOrigins.js +121 -0
  237. package/src/xslt/patterns/compile.js +151 -0
  238. package/src/xslt/patterns/general.js +132 -0
  239. package/src/xslt/patterns/grammar.js +190 -0
  240. package/src/xslt/patterns/modes.js +153 -0
  241. package/src/xslt/patterns/paths.js +130 -0
  242. package/src/xslt/patterns/patternChecks.js +29 -0
  243. package/src/xslt/patterns/steps.js +162 -0
  244. package/src/xslt/processor.js +178 -0
  245. package/src/xslt/runtime/accumulatorFunctions.js +48 -0
  246. package/src/xslt/runtime/accumulators.js +174 -0
  247. package/src/xslt/runtime/apply.js +153 -0
  248. package/src/xslt/runtime/atomicKeys.js +36 -0
  249. package/src/xslt/runtime/availability.js +162 -0
  250. package/src/xslt/runtime/baseUri.js +64 -0
  251. package/src/xslt/runtime/compilerHook.js +27 -0
  252. package/src/xslt/runtime/conditional.js +110 -0
  253. package/src/xslt/runtime/context.js +106 -0
  254. package/src/xslt/runtime/copy.js +123 -0
  255. package/src/xslt/runtime/copyFunctions.js +71 -0
  256. package/src/xslt/runtime/copyOrigins.js +60 -0
  257. package/src/xslt/runtime/defaultMethod.js +42 -0
  258. package/src/xslt/runtime/documentContent.js +93 -0
  259. package/src/xslt/runtime/dynamicXPath.js +90 -0
  260. package/src/xslt/runtime/functionCache.js +65 -0
  261. package/src/xslt/runtime/functionCall.js +57 -0
  262. package/src/xslt/runtime/functions.js +191 -0
  263. package/src/xslt/runtime/groups.js +108 -0
  264. package/src/xslt/runtime/invocation.js +156 -0
  265. package/src/xslt/runtime/iteration.js +107 -0
  266. package/src/xslt/runtime/keys.js +141 -0
  267. package/src/xslt/runtime/lazy.js +97 -0
  268. package/src/xslt/runtime/locations.js +86 -0
  269. package/src/xslt/runtime/machine.js +137 -0
  270. package/src/xslt/runtime/merging.js +167 -0
  271. package/src/xslt/runtime/namespaceScope.js +141 -0
  272. package/src/xslt/runtime/numberFormat.js +89 -0
  273. package/src/xslt/runtime/numbering.js +168 -0
  274. package/src/xslt/runtime/params.js +59 -0
  275. package/src/xslt/runtime/properties.js +103 -0
  276. package/src/xslt/runtime/rawResults.js +82 -0
  277. package/src/xslt/runtime/sequenceReceiver.js +102 -0
  278. package/src/xslt/runtime/snapshot.js +66 -0
  279. package/src/xslt/runtime/sortKeys.js +119 -0
  280. package/src/xslt/runtime/streamAvailable.js +60 -0
  281. package/src/xslt/runtime/strip.js +102 -0
  282. package/src/xslt/runtime/transformFunction.js +169 -0
  283. package/src/xslt/runtime/transformation.js +168 -0
  284. package/src/xslt/runtime/transformationContext.js +85 -0
  285. package/src/xslt/runtime/treeReceiver.js +194 -0
  286. package/src/xslt/runtime/values.js +92 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Parser of XPath 3.1 postfix expressions: productions [49] PostfixExpr,
3
+ * [50] ArgumentList, [53] Lookup and [54] KeySpecifier of Appendix A.1
4
+ * (predicates, dynamic function calls and lookups after a primary).
5
+ *
6
+ * @module @tradik/xslt3/xpath/syntax/postfix
7
+ */
8
+
9
+ import { makeNode } from "./ast.js";
10
+ import { parseExprSingle } from "./expressions.js";
11
+ import { parseParenthesizedExpr, parsePrimaryExpr } from "./primaries.js";
12
+ import { parsePredicate } from "./steps.js";
13
+ import { isPlainName } from "./tokenStream.js";
14
+
15
+ /** @typedef {import("./tokenStream.js").TokenStream} TokenStream */
16
+ /** @typedef {import("./ast.js").Expr} Expr */
17
+
18
+ /**
19
+ * PostfixExpr ::= PrimaryExpr (Predicate | ArgumentList | Lookup)*
20
+ *
21
+ * @param {TokenStream} p - Tokens
22
+ * @returns {Expr} The expression
23
+ */
24
+ export function parsePostfixExpr(p) {
25
+ let expr = parsePrimaryExpr(p);
26
+ for (;;) {
27
+ if (p.isSymbol("[")) {
28
+ const { predicate, end } = parsePredicate(p);
29
+ expr = makeNode("FilterExpr", { base: expr, predicate }, expr.start, end);
30
+ } else if (p.isSymbol("(")) {
31
+ const { arguments: args, end } = parseArgumentList(p);
32
+ expr = makeNode(
33
+ "DynamicFunctionCall",
34
+ { functionExpr: expr, arguments: args },
35
+ expr.start,
36
+ end,
37
+ );
38
+ } else if (p.acceptSymbol("?")) {
39
+ const { keyKind, key, end } = parseKeySpecifier(p);
40
+ expr = makeNode("Lookup", { base: expr, keyKind, key }, expr.start, end);
41
+ } else {
42
+ return expr;
43
+ }
44
+ }
45
+ }
46
+
47
+ /**
48
+ * ArgumentList ::= "(" (Argument ("," Argument)*)? ")", an Argument being
49
+ * an ExprSingle or the placeholder "?" (partial function application).
50
+ *
51
+ * @param {TokenStream} p - Tokens, at "("
52
+ * @returns {{arguments: Array<Expr|import("./ast.js").ArgumentPlaceholder>, end: number}}
53
+ * The arguments and the offset after ")"
54
+ */
55
+ export function parseArgumentList(p) {
56
+ p.expectSymbol("(");
57
+ const args = [];
58
+ if (!p.isSymbol(")")) {
59
+ do {
60
+ if (p.isSymbol("?") && (p.isSymbol(",", 1) || p.isSymbol(")", 1))) {
61
+ const { start, end } = p.next();
62
+ args.push(makeNode("ArgumentPlaceholder", {}, start, end));
63
+ } else {
64
+ args.push(parseExprSingle(p));
65
+ }
66
+ } while (p.acceptSymbol(","));
67
+ }
68
+ return { arguments: args, end: p.expectSymbol(")").end };
69
+ }
70
+
71
+ /**
72
+ * KeySpecifier ::= NCName | IntegerLiteral | ParenthesizedExpr | "*", after
73
+ * the "?" of a lookup.
74
+ *
75
+ * @param {TokenStream} p - Tokens
76
+ * @returns {{keyKind: string, key: string|Expr|null, end: number}} The key
77
+ */
78
+ export function parseKeySpecifier(p) {
79
+ const token = p.peek();
80
+ if (isPlainName(token) || token.type === "integer") {
81
+ p.next();
82
+ const keyKind = token.type === "name" ? "name" : "integer";
83
+ return { keyKind, key: token.local ?? token.value, end: token.end };
84
+ }
85
+ if (p.acceptSymbol("*")) {
86
+ return { keyKind: "wildcard", key: null, end: token.end };
87
+ }
88
+ if (!p.isSymbol("(")) p.fail('Expected a key: NCName, integer, "(" or "*"');
89
+ const key = parseParenthesizedExpr(p);
90
+ return { keyKind: "expr", key, end: p.lastEnd };
91
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Parser of XPath 3.1 primary expressions: productions [56] PrimaryExpr to
3
+ * [68] NamedFunctionRef and [76] UnaryLookup of Appendix A.1. Postfixes are
4
+ * in postfix.js; function, map and array constructors in constructors.js.
5
+ *
6
+ * Reserved-function-names constraint (A.3): an unprefixed name from
7
+ * {@link RESERVED_FUNCTION_NAMES} cannot be called or referenced as a
8
+ * function, so `if(1)` or `node#0` never are function calls.
9
+ *
10
+ * @module @tradik/xslt3/xpath/syntax/primaries
11
+ */
12
+
13
+ import { makeNode } from "./ast.js";
14
+ import {
15
+ parseCurlyArrayConstructor,
16
+ parseInlineFunctionExpr,
17
+ parseMapConstructor,
18
+ parseSquareArrayConstructor,
19
+ } from "./constructors.js";
20
+ import { parseExpr } from "./expressions.js";
21
+ import { parseArgumentList, parseKeySpecifier } from "./postfix.js";
22
+ import { isPlainName, qName } from "./tokenStream.js";
23
+
24
+ /** @typedef {import("./tokenStream.js").TokenStream} TokenStream */
25
+ /** @typedef {import("./ast.js").Expr} Expr */
26
+
27
+ /** Unprefixed names that are not function names (XPath 3.1 A.3). */
28
+ export const RESERVED_FUNCTION_NAMES = new Set(
29
+ (
30
+ "array attribute comment document-node element empty-sequence function " +
31
+ "if item map namespace-node node processing-instruction schema-attribute " +
32
+ "schema-element switch text typeswitch"
33
+ ).split(" "),
34
+ );
35
+
36
+ /**
37
+ * PrimaryExpr: literals, variable references, parenthesized expressions,
38
+ * `.`, function calls and references, constructors and `?key`.
39
+ *
40
+ * @param {TokenStream} p - Tokens
41
+ * @returns {Expr} The expression
42
+ */
43
+ export function parsePrimaryExpr(p) {
44
+ const token = p.peek();
45
+ switch (token.type) {
46
+ case "integer":
47
+ case "decimal":
48
+ case "double":
49
+ p.next();
50
+ return makeNode(
51
+ "NumericLiteral",
52
+ { kind: token.type, value: token.value },
53
+ token.start,
54
+ token.end,
55
+ );
56
+ case "string":
57
+ p.next();
58
+ return makeNode(
59
+ "StringLiteral",
60
+ { value: token.value },
61
+ token.start,
62
+ token.end,
63
+ );
64
+ case "name":
65
+ return parseNamedPrimary(p);
66
+ }
67
+ if (p.isSymbol("$")) return parseVarRef(p);
68
+ if (p.isSymbol("(")) return parseParenthesizedExpr(p);
69
+ if (p.isSymbol("[")) return parseSquareArrayConstructor(p);
70
+ if (p.acceptSymbol(".")) {
71
+ return makeNode("ContextItemExpr", {}, token.start, token.end);
72
+ }
73
+ if (p.acceptSymbol("?")) {
74
+ const { keyKind, key, end } = parseKeySpecifier(p);
75
+ return makeNode("UnaryLookup", { keyKind, key }, token.start, end);
76
+ }
77
+ return p.fail("Expected an expression");
78
+ }
79
+
80
+ /**
81
+ * A primary expression that starts with a name: `f#2`, `function(...)`,
82
+ * `map {...}`, `array {...}` or a function call. Plain names never get
83
+ * here: the path parser makes name tests of them.
84
+ *
85
+ * @param {TokenStream} p - Tokens
86
+ * @returns {Expr} The expression
87
+ */
88
+ function parseNamedPrimary(p) {
89
+ const token = p.peek();
90
+ if (p.isKeyword("function") && p.isSymbol("(", 1)) {
91
+ return parseInlineFunctionExpr(p);
92
+ }
93
+ if (p.isSymbol("{", 1)) {
94
+ if (p.isKeyword("map")) return parseMapConstructor(p);
95
+ if (p.isKeyword("array")) return parseCurlyArrayConstructor(p);
96
+ }
97
+ if (isPlainName(token) && RESERVED_FUNCTION_NAMES.has(token.local)) {
98
+ p.fail("Reserved name used as a function name");
99
+ }
100
+ p.next();
101
+ if (p.acceptSymbol("#")) {
102
+ const arity = p.peek();
103
+ if (arity.type !== "integer") {
104
+ p.fail("Expected the arity, an integer literal");
105
+ }
106
+ p.next();
107
+ return makeNode(
108
+ "NamedFunctionRef",
109
+ { name: qName(token), arity: Number(arity.value) },
110
+ token.start,
111
+ arity.end,
112
+ );
113
+ }
114
+ const { arguments: args, end } = parseArgumentList(p);
115
+ return makeNode(
116
+ "FunctionCall",
117
+ { name: qName(token), arguments: args },
118
+ token.start,
119
+ end,
120
+ );
121
+ }
122
+
123
+ /**
124
+ * ParenthesizedExpr ::= "(" Expr? ")"; `()` gives an EmptySequence, `(E)`
125
+ * the node of E.
126
+ *
127
+ * @param {TokenStream} p - Tokens, at "("
128
+ * @returns {Expr} The expression
129
+ */
130
+ export function parseParenthesizedExpr(p) {
131
+ const open = p.expectSymbol("(");
132
+ const close = p.acceptSymbol(")");
133
+ if (close) return makeNode("EmptySequence", {}, open.start, close.end);
134
+ const expr = parseExpr(p);
135
+ p.expectSymbol(")");
136
+ return expr;
137
+ }
138
+
139
+ /**
140
+ * VarRef ::= "$" VarName
141
+ *
142
+ * @param {TokenStream} p - Tokens, at "$"
143
+ * @returns {import("./ast.js").VarRef} The reference
144
+ */
145
+ export function parseVarRef(p) {
146
+ const dollar = p.expectSymbol("$");
147
+ const name = p.expectName("a variable name");
148
+ return makeNode("VarRef", { name: qName(name) }, dollar.start, name.end);
149
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Parser of XPath 3.1 steps: productions [38] StepExpr to [52]
3
+ * PredicateList of Appendix A.1 (axes, abbreviations, node tests,
4
+ * predicates).
5
+ *
6
+ * @module @tradik/xslt3/xpath/syntax/steps
7
+ */
8
+
9
+ import { makeNode } from "./ast.js";
10
+ import { parseExpr } from "./expressions.js";
11
+ import { isKindTestName, parseKindTest } from "./kindTests.js";
12
+ import { parsePostfixExpr } from "./postfix.js";
13
+ import { isPlainName, qName } from "./tokenStream.js";
14
+
15
+ /** @typedef {import("./tokenStream.js").TokenStream} TokenStream */
16
+ /** @typedef {import("./ast.js").Expr} Expr */
17
+
18
+ /** The 13 axes of XPath 3.1, namespace included. */
19
+ const AXES = new Set(
20
+ (
21
+ "child descendant attribute self descendant-or-self following-sibling " +
22
+ "following namespace parent ancestor preceding-sibling preceding " +
23
+ "ancestor-or-self"
24
+ ).split(" "),
25
+ );
26
+
27
+ /**
28
+ * StepExpr ::= PostfixExpr | AxisStep
29
+ *
30
+ * @param {TokenStream} p - Tokens
31
+ * @returns {Expr} The step
32
+ */
33
+ export function parseStepExpr(p) {
34
+ const token = p.peek();
35
+ if (p.isSymbol("..")) {
36
+ p.next();
37
+ const nodeTest = makeNode("AnyKindTest", {}, token.start, token.end);
38
+ return finishStep(p, "parent", nodeTest, token.start);
39
+ }
40
+ if (p.isSymbol("@")) {
41
+ p.next();
42
+ return finishStep(p, "attribute", parseNodeTest(p), token.start);
43
+ }
44
+ if (token.type === "name" && p.isSymbol("::", 1)) {
45
+ if (!isPlainName(token) || !AXES.has(token.local)) p.fail("Unknown axis");
46
+ p.next();
47
+ p.next();
48
+ return finishStep(p, token.local, parseNodeTest(p), token.start);
49
+ }
50
+ if (startsNodeTest(p)) {
51
+ const nodeTest = parseNodeTest(p);
52
+ return finishStep(p, defaultAxis(nodeTest), nodeTest, token.start);
53
+ }
54
+ return parsePostfixExpr(p);
55
+ }
56
+
57
+ /**
58
+ * Whether an abbreviated step (no axis) starts here, rather than a primary
59
+ * expression such as a function call, `f#1`, `map {}` or `array {}`.
60
+ *
61
+ * @param {TokenStream} p - Tokens
62
+ * @returns {boolean} True for a name test, wildcard or kind test
63
+ */
64
+ function startsNodeTest(p) {
65
+ const token = p.peek();
66
+ if (token.type === "wildcard" || p.isSymbol("*")) return true;
67
+ if (token.type !== "name") return false;
68
+ if (p.isSymbol("(", 1)) return isKindTestName(token);
69
+ if (p.isSymbol("#", 1)) return false;
70
+ const constructor = p.isKeyword("map") || p.isKeyword("array");
71
+ return !(constructor && p.isSymbol("{", 1));
72
+ }
73
+
74
+ /**
75
+ * Default axis of an abbreviated step (XPath 3.1 section 3.3.5).
76
+ *
77
+ * @param {import("./typeAst.js").NodeTest} nodeTest - Test of the step
78
+ * @returns {string} "attribute", "namespace" or "child"
79
+ */
80
+ function defaultAxis(nodeTest) {
81
+ switch (nodeTest.type) {
82
+ case "AttributeTest":
83
+ case "SchemaAttributeTest":
84
+ return "attribute";
85
+ case "NamespaceNodeTest":
86
+ return "namespace";
87
+ default:
88
+ return "child";
89
+ }
90
+ }
91
+
92
+ /**
93
+ * NodeTest ::= KindTest | NameTest, NameTest being an EQName or a Wildcard.
94
+ *
95
+ * @param {TokenStream} p - Tokens
96
+ * @returns {import("./typeAst.js").NodeTest} The test
97
+ */
98
+ function parseNodeTest(p) {
99
+ const token = p.peek();
100
+ if (token.type === "name" && p.isSymbol("(", 1) && isKindTestName(token)) {
101
+ return parseKindTest(p);
102
+ }
103
+ if (token.type === "wildcard" || p.isSymbol("*")) {
104
+ p.next();
105
+ const fields =
106
+ token.type === "wildcard"
107
+ ? qName(token)
108
+ : { prefix: null, local: null, uri: null };
109
+ return makeNode("Wildcard", fields, token.start, token.end);
110
+ }
111
+ const name = p.expectName("a node test");
112
+ return makeNode("NameTest", { name: qName(name) }, name.start, name.end);
113
+ }
114
+
115
+ /**
116
+ * Reads the predicates of an axis step and builds the step.
117
+ *
118
+ * @param {TokenStream} p - Tokens
119
+ * @param {string} axis - Axis name
120
+ * @param {import("./typeAst.js").NodeTest} nodeTest - Node test
121
+ * @param {number} start - Offset of the step
122
+ * @returns {import("./ast.js").AxisStep} The step
123
+ */
124
+ function finishStep(p, axis, nodeTest, start) {
125
+ const predicates = [];
126
+ let end = nodeTest.end;
127
+ while (p.isSymbol("[")) {
128
+ const { predicate, end: predicateEnd } = parsePredicate(p);
129
+ predicates.push(predicate);
130
+ end = predicateEnd;
131
+ }
132
+ return makeNode("AxisStep", { axis, nodeTest, predicates }, start, end);
133
+ }
134
+
135
+ /**
136
+ * Predicate ::= "[" Expr "]"
137
+ *
138
+ * @param {TokenStream} p - Tokens, at "["
139
+ * @returns {{predicate: Expr, end: number}} The expression and the offset
140
+ * after "]"
141
+ */
142
+ export function parsePredicate(p) {
143
+ p.expectSymbol("[");
144
+ const predicate = parseExpr(p);
145
+ return { predicate, end: p.expectSymbol("]").end };
146
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Construction of XPath static errors that point into the source text.
3
+ *
4
+ * @module @tradik/xslt3/xpath/syntax/syntaxError
5
+ */
6
+
7
+ import { XPathError } from "../../errors.js";
8
+
9
+ /** Characters of context shown on each side of the error offset. */
10
+ const EXCERPT_RADIUS = 20;
11
+
12
+ /**
13
+ * Builds an error whose message names the offset and shows the source around
14
+ * it, with "^" marking the offset, e.g. `... at offset 4: "1 + ^)"`.
15
+ *
16
+ * @param {string} source - The whole expression
17
+ * @param {number} offset - Offset (UTF-16 code units) where the problem is
18
+ * @param {string} message - What is wrong
19
+ * @param {string} [code] - Error code, XPST0003 (syntax error) by default
20
+ * @returns {XPathError} The error, for the caller to throw
21
+ */
22
+ export function syntaxError(source, offset, message, code = "XPST0003") {
23
+ const from = Math.max(0, offset - EXCERPT_RADIUS);
24
+ const to = Math.min(source.length, offset + EXCERPT_RADIUS);
25
+ const before = (from > 0 ? "..." : "") + source.slice(from, offset);
26
+ const after = source.slice(offset, to) + (to < source.length ? "..." : "");
27
+ return new XPathError(
28
+ code,
29
+ `${message} at offset ${offset}: ${JSON.stringify(before + "^" + after)}`,
30
+ );
31
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Cursor over the tokens of one expression, shared by the parser modules.
3
+ *
4
+ * Keywords are recognised here by spelling only: a keyword is an unprefixed
5
+ * name token without a braced URI, so `fn:div` or `Q{}div` never are.
6
+ *
7
+ * @module @tradik/xslt3/xpath/syntax/tokenStream
8
+ */
9
+
10
+ import { tokenize } from "./lexer.js";
11
+ import { syntaxError } from "./syntaxError.js";
12
+
13
+ /**
14
+ * Describes a token for error messages.
15
+ *
16
+ * @param {import("./lexer.js").Token} token - Token
17
+ * @param {string} source - The expression
18
+ * @returns {string} e.g. `")"` or "end of expression"
19
+ */
20
+ function describe(token, source) {
21
+ if (token.type === "eof") return "end of expression";
22
+ return JSON.stringify(source.slice(token.start, token.end));
23
+ }
24
+
25
+ /** Tokens of an expression with a read position. */
26
+ export class TokenStream {
27
+ /**
28
+ * @param {string} source - XPath expression
29
+ * @param {object} [options] - Parser options (see parseXPath)
30
+ */
31
+ constructor(source, options = {}) {
32
+ this.source = source;
33
+ this.options = options;
34
+ this.tokens = tokenize(source);
35
+ this.index = 0;
36
+ /** Offset after the last consumed token. */
37
+ this.lastEnd = 0;
38
+ }
39
+
40
+ /**
41
+ * @param {number} [ahead] - 0 for the current token, 1 for the next...
42
+ * @returns {import("./lexer.js").Token} The token (eof past the end)
43
+ */
44
+ peek(ahead = 0) {
45
+ const last = this.tokens.length - 1;
46
+ return this.tokens[Math.min(this.index + ahead, last)];
47
+ }
48
+
49
+ /** @returns {import("./lexer.js").Token} The current token, consumed. */
50
+ next() {
51
+ const token = this.peek();
52
+ if (token.type !== "eof") this.index++;
53
+ this.lastEnd = token.end;
54
+ return token;
55
+ }
56
+
57
+ /**
58
+ * @param {string} value - Symbol, e.g. "("
59
+ * @param {number} [ahead] - Lookahead distance
60
+ * @returns {boolean} Whether that token is the symbol
61
+ */
62
+ isSymbol(value, ahead = 0) {
63
+ const token = this.peek(ahead);
64
+ return token.type === "symbol" && token.value === value;
65
+ }
66
+
67
+ /**
68
+ * @param {string} word - Keyword, e.g. "div"
69
+ * @param {number} [ahead] - Lookahead distance
70
+ * @returns {boolean} Whether that token is the keyword
71
+ */
72
+ isKeyword(word, ahead = 0) {
73
+ const token = this.peek(ahead);
74
+ return isPlainName(token) && token.local === word;
75
+ }
76
+
77
+ /**
78
+ * Consumes the current token when it is the symbol.
79
+ *
80
+ * @param {string} value - Symbol
81
+ * @returns {import("./lexer.js").Token|null} The token, or null
82
+ */
83
+ acceptSymbol(value) {
84
+ return this.isSymbol(value) ? this.next() : null;
85
+ }
86
+
87
+ /**
88
+ * Consumes the current token when it is the keyword.
89
+ *
90
+ * @param {string} word - Keyword
91
+ * @returns {import("./lexer.js").Token|null} The token, or null
92
+ */
93
+ acceptKeyword(word) {
94
+ return this.isKeyword(word) ? this.next() : null;
95
+ }
96
+
97
+ /**
98
+ * Consumes the symbol or fails.
99
+ *
100
+ * @param {string} value - Expected symbol
101
+ * @returns {import("./lexer.js").Token} The token
102
+ */
103
+ expectSymbol(value) {
104
+ return this.acceptSymbol(value) ?? this.fail(`Expected "${value}"`);
105
+ }
106
+
107
+ /**
108
+ * Consumes the keyword or fails.
109
+ *
110
+ * @param {string} word - Expected keyword
111
+ * @returns {import("./lexer.js").Token} The token
112
+ */
113
+ expectKeyword(word) {
114
+ return this.acceptKeyword(word) ?? this.fail(`Expected "${word}"`);
115
+ }
116
+
117
+ /**
118
+ * Consumes a name token (NCName, QName or EQName) or fails.
119
+ *
120
+ * @param {string} [what] - What the name is, for the message
121
+ * @returns {import("./lexer.js").Token} The token
122
+ */
123
+ expectName(what = "a name") {
124
+ if (this.peek().type !== "name") this.fail(`Expected ${what}`);
125
+ return this.next();
126
+ }
127
+
128
+ /**
129
+ * Throws XPST0003 (or another code) at the current token.
130
+ *
131
+ * @param {string} message - What was expected
132
+ * @param {import("./lexer.js").Token} [token] - Offending token
133
+ * @param {string} [code] - Error code
134
+ * @returns {never}
135
+ */
136
+ fail(message, token = this.peek(), code = undefined) {
137
+ const found = describe(token, this.source);
138
+ throw syntaxError(
139
+ this.source,
140
+ token.start,
141
+ `${message}, found ${found}`,
142
+ code,
143
+ );
144
+ }
145
+ }
146
+
147
+ /**
148
+ * @param {import("./lexer.js").Token} token - Token
149
+ * @returns {boolean} Whether it is a name without prefix and braced URI
150
+ */
151
+ export function isPlainName(token) {
152
+ return token.type === "name" && token.prefix === null && token.uri === null;
153
+ }
154
+
155
+ /**
156
+ * The QName fields of a name or wildcard token, as kept in the AST.
157
+ *
158
+ * @param {import("./lexer.js").Token} token - Name token
159
+ * @returns {import("./ast.js").QName} `{ prefix, local, uri }`
160
+ */
161
+ export function qName(token) {
162
+ return { prefix: token.prefix, local: token.local, uri: token.uri };
163
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The XPath 3.1 abstract syntax tree: node tests, item types and sequence
3
+ * types. Same conventions as ast.js (`{ type, ...fields, start, end }`).
4
+ * This module holds type definitions only.
5
+ *
6
+ * @module @tradik/xslt3/xpath/syntax/typeAst
7
+ */
8
+
9
+ /** @typedef {import("./ast.js").QName} QName */
10
+
11
+ /**
12
+ * `ItemType occurrence`; `itemType` null means `empty-sequence()`.
13
+ * `occurrence` is "" (exactly one), "?", "*" or "+".
14
+ *
15
+ * @typedef {{type: "SequenceType", itemType: ItemType|null, occurrence: ""|"?"|"*"|"+", start: number, end: number}} SequenceType
16
+ */
17
+
18
+ /**
19
+ * Any item type. A parenthesized item type `(T)` leaves no node.
20
+ *
21
+ * @typedef {KindTest|AnyItemTest|AtomicType|AnyFunctionTest|TypedFunctionTest|
22
+ * AnyMapTest|TypedMapTest|AnyArrayTest|TypedArrayTest} ItemType
23
+ */
24
+
25
+ /** @typedef {NameTest|Wildcard|KindTest} NodeTest Test of an axis step */
26
+
27
+ /**
28
+ * @typedef {AnyKindTest|DocumentTest|ElementTest|AttributeTest|
29
+ * SchemaElementTest|SchemaAttributeTest|PITest|CommentTest|TextTest|
30
+ * NamespaceNodeTest} KindTest
31
+ */
32
+
33
+ /** @typedef {{type: "NameTest", name: QName, start: number, end: number}} NameTest */
34
+
35
+ /**
36
+ * Wildcard name test: `*` (all fields null), `prefix:*` (prefix set),
37
+ * `*:local` (local set) or `Q{uri}*` (uri set, possibly "").
38
+ *
39
+ * @typedef {{type: "Wildcard", prefix: string|null, local: string|null, uri: string|null, start: number, end: number}} Wildcard
40
+ */
41
+
42
+ /** @typedef {{type: "AnyKindTest", start: number, end: number}} AnyKindTest `node()` */
43
+ /** @typedef {{type: "DocumentTest", elementTest: ElementTest|SchemaElementTest|null, start: number, end: number}} DocumentTest `document-node(...)` */
44
+
45
+ /**
46
+ * `element()`, `element(name)`, `element(*, type)`, `element(name, type?)`.
47
+ * `name` null stands for `*` (or no name), `typeName` null for no type;
48
+ * `nillable` is true for the trailing `?`.
49
+ *
50
+ * @typedef {{type: "ElementTest", name: QName|null, typeName: QName|null, nillable: boolean, start: number, end: number}} ElementTest
51
+ */
52
+
53
+ /** @typedef {{type: "AttributeTest", name: QName|null, typeName: QName|null, start: number, end: number}} AttributeTest `attribute(name-or-*, type)` */
54
+ /** @typedef {{type: "SchemaElementTest", name: QName, start: number, end: number}} SchemaElementTest */
55
+ /** @typedef {{type: "SchemaAttributeTest", name: QName, start: number, end: number}} SchemaAttributeTest */
56
+
57
+ /**
58
+ * `processing-instruction(target)`; a string literal target is stored
59
+ * whitespace-normalized; null when there is no target.
60
+ *
61
+ * @typedef {{type: "PITest", target: string|null, start: number, end: number}} PITest
62
+ */
63
+
64
+ /** @typedef {{type: "CommentTest", start: number, end: number}} CommentTest `comment()` */
65
+ /** @typedef {{type: "TextTest", start: number, end: number}} TextTest `text()` */
66
+ /** @typedef {{type: "NamespaceNodeTest", start: number, end: number}} NamespaceNodeTest `namespace-node()` */
67
+ /** @typedef {{type: "AnyItemTest", start: number, end: number}} AnyItemTest `item()` */
68
+ /** @typedef {{type: "AtomicType", name: QName, start: number, end: number}} AtomicType AtomicOrUnionType, e.g. `xs:integer` */
69
+ /** @typedef {{type: "AnyFunctionTest", start: number, end: number}} AnyFunctionTest `function(*)` */
70
+ /** @typedef {{type: "TypedFunctionTest", paramTypes: SequenceType[], returnType: SequenceType, start: number, end: number}} TypedFunctionTest `function(T1, T2) as R` */
71
+ /** @typedef {{type: "AnyMapTest", start: number, end: number}} AnyMapTest `map(*)` */
72
+ /** @typedef {{type: "TypedMapTest", keyType: AtomicType, valueType: SequenceType, start: number, end: number}} TypedMapTest `map(K, V)` */
73
+ /** @typedef {{type: "AnyArrayTest", start: number, end: number}} AnyArrayTest `array(*)` */
74
+ /** @typedef {{type: "TypedArrayTest", memberType: SequenceType, start: number, end: number}} TypedArrayTest `array(T)` */
75
+
76
+ export {};