@elaraai/east 1.0.83 → 1.0.85

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 (252) hide show
  1. package/dist/src/analyze.d.ts.map +1 -1
  2. package/dist/src/analyze.js +48 -0
  3. package/dist/src/analyze.js.map +1 -1
  4. package/dist/src/ast_to_ir.coerce_to.spec.js +4 -4
  5. package/dist/src/ast_to_ir.coerce_to.spec.js.map +1 -1
  6. package/dist/src/ast_to_ir.d.ts +6 -1
  7. package/dist/src/ast_to_ir.d.ts.map +1 -1
  8. package/dist/src/ast_to_ir.js +20 -7
  9. package/dist/src/ast_to_ir.js.map +1 -1
  10. package/dist/src/builtins.d.ts +1 -1
  11. package/dist/src/builtins.d.ts.map +1 -1
  12. package/dist/src/builtins.js +52 -0
  13. package/dist/src/builtins.js.map +1 -1
  14. package/dist/src/codegen/canonical.d.ts +37 -0
  15. package/dist/src/codegen/canonical.d.ts.map +1 -0
  16. package/dist/src/codegen/canonical.js +208 -0
  17. package/dist/src/codegen/canonical.js.map +1 -0
  18. package/dist/src/codegen/codegen.spec.js +132 -201
  19. package/dist/src/codegen/codegen.spec.js.map +1 -1
  20. package/dist/src/codegen/printer.d.ts.map +1 -1
  21. package/dist/src/codegen/printer.js +142 -24
  22. package/dist/src/codegen/printer.js.map +1 -1
  23. package/dist/src/codegen/spellings.d.ts +4 -1
  24. package/dist/src/codegen/spellings.d.ts.map +1 -1
  25. package/dist/src/codegen/spellings.js +5 -1
  26. package/dist/src/codegen/spellings.js.map +1 -1
  27. package/dist/src/compile/builtins/index.d.ts.map +1 -1
  28. package/dist/src/compile/builtins/index.js +2 -0
  29. package/dist/src/compile/builtins/index.js.map +1 -1
  30. package/dist/src/compile/builtins/primitives.d.ts.map +1 -1
  31. package/dist/src/compile/builtins/primitives.js +2 -1
  32. package/dist/src/compile/builtins/primitives.js.map +1 -1
  33. package/dist/src/compile/builtins/query.d.ts +10 -0
  34. package/dist/src/compile/builtins/query.d.ts.map +1 -0
  35. package/dist/src/compile/builtins/query.js +5 -0
  36. package/dist/src/compile/builtins/query.js.map +1 -0
  37. package/dist/src/expr/block.d.ts.map +1 -1
  38. package/dist/src/expr/block.js +2 -1
  39. package/dist/src/expr/block.js.map +1 -1
  40. package/dist/src/expr/index.d.ts +75 -47
  41. package/dist/src/expr/index.d.ts.map +1 -1
  42. package/dist/src/expr/index.js +31 -0
  43. package/dist/src/expr/index.js.map +1 -1
  44. package/dist/src/expr/libs/datetime.d.ts +20 -20
  45. package/dist/src/expr/libs/datetime.d.ts.map +1 -1
  46. package/dist/src/expr/libs/datetime.js +208 -167
  47. package/dist/src/expr/libs/datetime.js.map +1 -1
  48. package/dist/src/expr/libs/float.d.ts +15 -15
  49. package/dist/src/expr/libs/float.d.ts.map +1 -1
  50. package/dist/src/expr/libs/float.js +298 -267
  51. package/dist/src/expr/libs/float.js.map +1 -1
  52. package/dist/src/expr/libs/integer.d.ts +12 -12
  53. package/dist/src/expr/libs/integer.d.ts.map +1 -1
  54. package/dist/src/expr/libs/integer.js +219 -194
  55. package/dist/src/expr/libs/integer.js.map +1 -1
  56. package/dist/src/expr/libs/lazy.d.ts +28 -0
  57. package/dist/src/expr/libs/lazy.d.ts.map +1 -0
  58. package/dist/src/expr/libs/lazy.js +55 -0
  59. package/dist/src/expr/libs/lazy.js.map +1 -0
  60. package/dist/src/expr/libs/lazy.spec.d.ts +6 -0
  61. package/dist/src/expr/libs/lazy.spec.d.ts.map +1 -0
  62. package/dist/src/expr/libs/lazy.spec.js +102 -0
  63. package/dist/src/expr/libs/lazy.spec.js.map +1 -0
  64. package/dist/src/expr/query.d.ts +60 -0
  65. package/dist/src/expr/query.d.ts.map +1 -0
  66. package/dist/src/expr/query.js +84 -0
  67. package/dist/src/expr/query.js.map +1 -0
  68. package/dist/src/index.d.ts +3 -0
  69. package/dist/src/index.d.ts.map +1 -1
  70. package/dist/src/index.js +3 -0
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/internal.d.ts +4 -0
  73. package/dist/src/internal.d.ts.map +1 -1
  74. package/dist/src/internal.js +9 -0
  75. package/dist/src/internal.js.map +1 -1
  76. package/dist/src/location.d.ts +22 -0
  77. package/dist/src/location.d.ts.map +1 -1
  78. package/dist/src/location.js +31 -0
  79. package/dist/src/location.js.map +1 -1
  80. package/dist/src/naming.d.ts +12 -7
  81. package/dist/src/naming.d.ts.map +1 -1
  82. package/dist/src/naming.js +30 -13
  83. package/dist/src/naming.js.map +1 -1
  84. package/dist/src/naming.spec.js +49 -0
  85. package/dist/src/naming.spec.js.map +1 -1
  86. package/dist/src/portable.spec.d.ts +6 -0
  87. package/dist/src/portable.spec.d.ts.map +1 -0
  88. package/dist/src/portable.spec.js +181 -0
  89. package/dist/src/portable.spec.js.map +1 -0
  90. package/dist/src/query/evaluate.d.ts +98 -0
  91. package/dist/src/query/evaluate.d.ts.map +1 -0
  92. package/dist/src/query/evaluate.js +193 -0
  93. package/dist/src/query/evaluate.js.map +1 -0
  94. package/dist/src/query/index.d.ts +17 -0
  95. package/dist/src/query/index.d.ts.map +1 -0
  96. package/dist/src/query/index.js +17 -0
  97. package/dist/src/query/index.js.map +1 -0
  98. package/dist/src/query/jq/catalog.d.ts +174 -0
  99. package/dist/src/query/jq/catalog.d.ts.map +1 -0
  100. package/dist/src/query/jq/catalog.js +1740 -0
  101. package/dist/src/query/jq/catalog.js.map +1 -0
  102. package/dist/src/query/jq/check.d.ts +210 -0
  103. package/dist/src/query/jq/check.d.ts.map +1 -0
  104. package/dist/src/query/jq/check.js +2261 -0
  105. package/dist/src/query/jq/check.js.map +1 -0
  106. package/dist/src/query/jq/complete.d.ts +89 -0
  107. package/dist/src/query/jq/complete.d.ts.map +1 -0
  108. package/dist/src/query/jq/complete.js +320 -0
  109. package/dist/src/query/jq/complete.js.map +1 -0
  110. package/dist/src/query/jq/describe.d.ts +53 -0
  111. package/dist/src/query/jq/describe.d.ts.map +1 -0
  112. package/dist/src/query/jq/describe.js +149 -0
  113. package/dist/src/query/jq/describe.js.map +1 -0
  114. package/dist/src/query/jq/lex.d.ts +118 -0
  115. package/dist/src/query/jq/lex.d.ts.map +1 -0
  116. package/dist/src/query/jq/lex.js +371 -0
  117. package/dist/src/query/jq/lex.js.map +1 -0
  118. package/dist/src/query/jq/literals.d.ts +49 -0
  119. package/dist/src/query/jq/literals.d.ts.map +1 -0
  120. package/dist/src/query/jq/literals.js +78 -0
  121. package/dist/src/query/jq/literals.js.map +1 -0
  122. package/dist/src/query/jq/messages.d.ts +126 -0
  123. package/dist/src/query/jq/messages.d.ts.map +1 -0
  124. package/dist/src/query/jq/messages.js +172 -0
  125. package/dist/src/query/jq/messages.js.map +1 -0
  126. package/dist/src/query/jq/parse.d.ts +70 -0
  127. package/dist/src/query/jq/parse.d.ts.map +1 -0
  128. package/dist/src/query/jq/parse.js +1013 -0
  129. package/dist/src/query/jq/parse.js.map +1 -0
  130. package/dist/src/query/jq/print.d.ts +59 -0
  131. package/dist/src/query/jq/print.d.ts.map +1 -0
  132. package/dist/src/query/jq/print.js +587 -0
  133. package/dist/src/query/jq/print.js.map +1 -0
  134. package/dist/src/query/jq/shapes.d.ts +345 -0
  135. package/dist/src/query/jq/shapes.d.ts.map +1 -0
  136. package/dist/src/query/jq/shapes.js +584 -0
  137. package/dist/src/query/jq/shapes.js.map +1 -0
  138. package/dist/src/query/jq/spans.d.ts +133 -0
  139. package/dist/src/query/jq/spans.d.ts.map +1 -0
  140. package/dist/src/query/jq/spans.js +195 -0
  141. package/dist/src/query/jq/spans.js.map +1 -0
  142. package/dist/src/query/jq/split.d.ts +334 -0
  143. package/dist/src/query/jq/split.d.ts.map +1 -0
  144. package/dist/src/query/jq/split.js +2108 -0
  145. package/dist/src/query/jq/split.js.map +1 -0
  146. package/dist/src/query/jq/strftime.d.ts +20 -0
  147. package/dist/src/query/jq/strftime.d.ts.map +1 -0
  148. package/dist/src/query/jq/strftime.js +65 -0
  149. package/dist/src/query/jq/strftime.js.map +1 -0
  150. package/dist/src/query/jq/summary.d.ts +139 -0
  151. package/dist/src/query/jq/summary.d.ts.map +1 -0
  152. package/dist/src/query/jq/summary.js +222 -0
  153. package/dist/src/query/jq/summary.js.map +1 -0
  154. package/dist/src/query/jq/translate-builtins.d.ts +21 -0
  155. package/dist/src/query/jq/translate-builtins.d.ts.map +1 -0
  156. package/dist/src/query/jq/translate-builtins.js +1873 -0
  157. package/dist/src/query/jq/translate-builtins.js.map +1 -0
  158. package/dist/src/query/jq/translate.d.ts +627 -0
  159. package/dist/src/query/jq/translate.d.ts.map +1 -0
  160. package/dist/src/query/jq/translate.js +3364 -0
  161. package/dist/src/query/jq/translate.js.map +1 -0
  162. package/dist/src/query/types.d.ts +505 -0
  163. package/dist/src/query/types.d.ts.map +1 -0
  164. package/dist/src/query/types.js +231 -0
  165. package/dist/src/query/types.js.map +1 -0
  166. package/dist/src/runner_exec.d.ts +134 -0
  167. package/dist/src/runner_exec.d.ts.map +1 -0
  168. package/dist/src/runner_exec.js +371 -0
  169. package/dist/src/runner_exec.js.map +1 -0
  170. package/dist/src/runner_exec.spec.d.ts +6 -0
  171. package/dist/src/runner_exec.spec.d.ts.map +1 -0
  172. package/dist/src/runner_exec.spec.js +198 -0
  173. package/dist/src/runner_exec.spec.js.map +1 -0
  174. package/dist/src/runner_io.d.ts +370 -0
  175. package/dist/src/runner_io.d.ts.map +1 -0
  176. package/dist/src/runner_io.js +563 -0
  177. package/dist/src/runner_io.js.map +1 -0
  178. package/dist/src/runner_io.spec.d.ts +6 -0
  179. package/dist/src/runner_io.spec.d.ts.map +1 -0
  180. package/dist/src/runner_io.spec.js +224 -0
  181. package/dist/src/runner_io.spec.js.map +1 -0
  182. package/dist/src/runner_merge.d.ts +95 -0
  183. package/dist/src/runner_merge.d.ts.map +1 -0
  184. package/dist/src/runner_merge.js +238 -0
  185. package/dist/src/runner_merge.js.map +1 -0
  186. package/dist/src/runner_protocol.d.ts +60 -6
  187. package/dist/src/runner_protocol.d.ts.map +1 -1
  188. package/dist/src/runner_protocol.js +48 -6
  189. package/dist/src/runner_protocol.js.map +1 -1
  190. package/dist/src/serialization/beast2/index.d.ts +2 -2
  191. package/dist/src/serialization/beast2/index.d.ts.map +1 -1
  192. package/dist/src/serialization/beast2/index.js +1 -1
  193. package/dist/src/serialization/beast2/index.js.map +1 -1
  194. package/dist/src/serialization/beast2/v5/frames.d.ts +10 -6
  195. package/dist/src/serialization/beast2/v5/frames.d.ts.map +1 -1
  196. package/dist/src/serialization/beast2/v5/frames.js +10 -6
  197. package/dist/src/serialization/beast2/v5/frames.js.map +1 -1
  198. package/dist/src/serialization/beast2/v5/frames.spec.d.ts +6 -0
  199. package/dist/src/serialization/beast2/v5/frames.spec.d.ts.map +1 -0
  200. package/dist/src/serialization/beast2/v5/frames.spec.js +42 -0
  201. package/dist/src/serialization/beast2/v5/frames.spec.js.map +1 -0
  202. package/dist/src/serialization/beast2/v5/index.spec.js +108 -0
  203. package/dist/src/serialization/beast2/v5/index.spec.js.map +1 -1
  204. package/dist/src/serialization/beast2/v5/lazy.d.ts +74 -7
  205. package/dist/src/serialization/beast2/v5/lazy.d.ts.map +1 -1
  206. package/dist/src/serialization/beast2/v5/lazy.js +129 -27
  207. package/dist/src/serialization/beast2/v5/lazy.js.map +1 -1
  208. package/dist/src/serialization/beast2/v5/lazy.spec.js +251 -16
  209. package/dist/src/serialization/beast2/v5/lazy.spec.js.map +1 -1
  210. package/dist/src/serialization/beast2/v5/stream.d.ts +139 -23
  211. package/dist/src/serialization/beast2/v5/stream.d.ts.map +1 -1
  212. package/dist/src/serialization/beast2/v5/stream.js +196 -44
  213. package/dist/src/serialization/beast2/v5/stream.js.map +1 -1
  214. package/dist/src/serialization/beast2/v5/type-section.d.ts.map +1 -1
  215. package/dist/src/serialization/beast2/v5/type-section.js +25 -12
  216. package/dist/src/serialization/beast2/v5/type-section.js.map +1 -1
  217. package/dist/src/serialization/beast2/v5/type-section.spec.d.ts +6 -0
  218. package/dist/src/serialization/beast2/v5/type-section.spec.d.ts.map +1 -0
  219. package/dist/src/serialization/beast2/v5/type-section.spec.js +41 -0
  220. package/dist/src/serialization/beast2/v5/type-section.spec.js.map +1 -0
  221. package/dist/src/serialization/beast2/v5/weight.d.ts +36 -0
  222. package/dist/src/serialization/beast2/v5/weight.d.ts.map +1 -0
  223. package/dist/src/serialization/beast2/v5/weight.js +238 -0
  224. package/dist/src/serialization/beast2/v5/weight.js.map +1 -0
  225. package/dist/src/serialization/crc32.d.ts +38 -0
  226. package/dist/src/serialization/crc32.d.ts.map +1 -0
  227. package/dist/src/serialization/crc32.js +70 -0
  228. package/dist/src/serialization/crc32.js.map +1 -0
  229. package/dist/src/serialization/crc32.spec.d.ts +6 -0
  230. package/dist/src/serialization/crc32.spec.d.ts.map +1 -0
  231. package/dist/src/serialization/crc32.spec.js +92 -0
  232. package/dist/src/serialization/crc32.spec.js.map +1 -0
  233. package/dist/src/serialization/east-inferred.spec.js +4 -0
  234. package/dist/src/serialization/east-inferred.spec.js.map +1 -1
  235. package/dist/src/serialization/east.d.ts.map +1 -1
  236. package/dist/src/serialization/east.js +134 -110
  237. package/dist/src/serialization/east.js.map +1 -1
  238. package/dist/src/serialization/east.spec.js +185 -4
  239. package/dist/src/serialization/east.spec.js.map +1 -1
  240. package/dist/src/serialization/east_text.d.ts +25 -0
  241. package/dist/src/serialization/east_text.d.ts.map +1 -0
  242. package/dist/src/serialization/east_text.js +29 -0
  243. package/dist/src/serialization/east_text.js.map +1 -0
  244. package/dist/src/serialization/index.d.ts +1 -1
  245. package/dist/src/serialization/index.d.ts.map +1 -1
  246. package/dist/src/serialization/index.js +1 -1
  247. package/dist/src/serialization/index.js.map +1 -1
  248. package/dist/src/types.d.ts +1 -1
  249. package/dist/src/types.d.ts.map +1 -1
  250. package/dist/src/types.js +7 -6
  251. package/dist/src/types.js.map +1 -1
  252. package/package.json +7 -4
@@ -0,0 +1,1013 @@
1
+ /**
2
+ * Copyright (c) 2025 Elara AI Pty Ltd
3
+ * Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
4
+ */
5
+ /**
6
+ * The jq parser: jq 1.8's grammar (`src/parser.y`) into {@link JqType}
7
+ * values, with a span for every node and `syntax` diagnostics.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+ import { none, some, variant } from "../../containers/variant.js";
12
+ import { scanJq } from "./lex.js";
13
+ import { childPath, jqChildren, jqPatternChildren, toQuerySpan, } from "./spans.js";
14
+ const INTEGER_MAX = 9223372036854775807n;
15
+ const UPDATE_OPS = new Set(["=", "|=", "+=", "-=", "*=", "/=", "%=", "//="]);
16
+ const COMPARISON_OPS = new Set(["==", "!=", "<", "<=", ">", ">="]);
17
+ /** Keywords that open a filter in a query position. */
18
+ const FILTER_KEYWORDS = new Set([
19
+ "def", "label", "reduce", "foreach", "if", "try", "break", "import", "include", "module",
20
+ ]);
21
+ /** Keywords a query may not use: they load modules. */
22
+ const MODULE_KEYWORDS = new Set(["import", "include", "module"]);
23
+ /** Thrown to abandon a parse with its diagnostic. */
24
+ class JqSyntaxError extends Error {
25
+ diagnostic;
26
+ token;
27
+ constructor(diagnostic, token) {
28
+ super(diagnostic.message);
29
+ this.diagnostic = diagnostic;
30
+ this.token = token;
31
+ }
32
+ }
33
+ /**
34
+ * Builds a diagnostic.
35
+ *
36
+ * @param text - the parsed text
37
+ * @param code - `syntax`, or `unsupported` for a module keyword
38
+ * @param range - the text it is about, or `undefined` for none
39
+ * @param message - its sentence
40
+ * @param fixes - its one-click fixes
41
+ * @returns the diagnostic
42
+ */
43
+ function diagnostic(text, code, range, message, fixes = []) {
44
+ return {
45
+ code,
46
+ fixes,
47
+ message,
48
+ severity: variant("error", null),
49
+ span: range === undefined ? none : some(toQuerySpan(text, range.from, range.to)),
50
+ suggestions: [],
51
+ };
52
+ }
53
+ /**
54
+ * A one-click fix of one text edit.
55
+ *
56
+ * @param label - what the fix does
57
+ * @param offset - where the edit starts
58
+ * @param length - how many code units it replaces
59
+ * @param insert - what it inserts
60
+ * @returns the fix
61
+ */
62
+ function fix(label, offset, length, insert) {
63
+ return { edits: [{ insert, length: BigInt(length), offset: BigInt(offset) }], label };
64
+ }
65
+ /** The bracket a punctuation opens or closes, paired. */
66
+ const CLOSER = { "(": ")", "[": "]", "{": "}" };
67
+ /**
68
+ * Reports the brackets that do not pair: a closer with no opener, a closer of
69
+ * the wrong kind, and an opener never closed.
70
+ *
71
+ * @param text - the text
72
+ * @param lexemes - its lexemes
73
+ * @returns one diagnostic per bracket problem
74
+ */
75
+ function checkBrackets(text, lexemes) {
76
+ const problems = [];
77
+ const open = [];
78
+ for (const t of lexemes) {
79
+ if ((t.role === "punct" && CLOSER[t.value] !== undefined) || t.role === "interp_start") {
80
+ open.push(t);
81
+ }
82
+ else if (t.role === "punct" && (t.value === ")" || t.value === "]" || t.value === "}")) {
83
+ const top = open[open.length - 1];
84
+ if (top === undefined || top.role === "interp_start") {
85
+ problems.push(diagnostic(text, "syntax", t, `syntax: unexpected "${t.value}".`));
86
+ }
87
+ else if (CLOSER[top.value] === t.value) {
88
+ open.pop();
89
+ }
90
+ else {
91
+ const { column } = toQuerySpan(text, top.from, top.to);
92
+ problems.push(diagnostic(text, "syntax", t, `syntax: unexpected "${t.value}" — "${top.value}" at column ${column} is still open.`));
93
+ open.pop();
94
+ }
95
+ }
96
+ else if (t.role === "interp_end") {
97
+ while (open.length > 0 && open[open.length - 1].role !== "interp_start") {
98
+ const unclosed = open.pop();
99
+ problems.push(neverClosed(text, unclosed));
100
+ }
101
+ open.pop();
102
+ }
103
+ }
104
+ for (const unclosed of open) {
105
+ if (unclosed.role !== "interp_start")
106
+ problems.push(neverClosed(text, unclosed));
107
+ }
108
+ return problems;
109
+ }
110
+ /**
111
+ * The diagnostic for an opening bracket never closed, with its fix.
112
+ *
113
+ * @param text - the text
114
+ * @param opener - the bracket
115
+ * @returns the diagnostic
116
+ */
117
+ function neverClosed(text, opener) {
118
+ return diagnostic(text, "syntax", opener, `syntax: "${opener.value}" is never closed.`, [fix("Close it", text.length, 0, CLOSER[opener.value])]);
119
+ }
120
+ /**
121
+ * Describes a token for a message.
122
+ *
123
+ * @param t - the token, or `undefined` for the end of the text
124
+ * @returns `end of input`, `a string`, or the token's text in quotes
125
+ */
126
+ function describe(t) {
127
+ if (t === undefined)
128
+ return "end of input";
129
+ if (t.role === "string_start" || t.role === "unterminated")
130
+ return "a string";
131
+ return `"${t.text}"`;
132
+ }
133
+ /**
134
+ * A recursive-descent parser over the significant lexemes of a text, from a
135
+ * starting lexeme.
136
+ */
137
+ class Parser {
138
+ text;
139
+ toks;
140
+ /** Each node's and pattern's range, by the object. */
141
+ ranges = new Map();
142
+ /** Where each parenthesised node's text starts: its outermost `(`. */
143
+ opens = new Map();
144
+ at;
145
+ lastEnd = 0;
146
+ constructor(text, toks, start) {
147
+ this.text = text;
148
+ this.toks = toks;
149
+ this.at = start;
150
+ }
151
+ /** The index of the lexeme the parser is at. */
152
+ get position() {
153
+ return this.at;
154
+ }
155
+ peek(ahead = 0) {
156
+ return this.toks[this.at + ahead];
157
+ }
158
+ next() {
159
+ const t = this.toks[this.at];
160
+ if (t === undefined)
161
+ throw this.unexpected("a filter");
162
+ this.at += 1;
163
+ this.lastEnd = t.to;
164
+ return t;
165
+ }
166
+ isOp(value, ahead = 0) {
167
+ const t = this.peek(ahead);
168
+ return t?.role === "op" && t.value === value;
169
+ }
170
+ isPunct(value, ahead = 0) {
171
+ const t = this.peek(ahead);
172
+ return t?.role === "punct" && t.value === value;
173
+ }
174
+ isKeyword(value, ahead = 0) {
175
+ const t = this.peek(ahead);
176
+ return t?.role === "keyword" && t.value === value;
177
+ }
178
+ /** Records a node's range, from `from` to the last lexeme read. */
179
+ mk(node, from) {
180
+ this.ranges.set(node, { from, to: this.lastEnd });
181
+ return node;
182
+ }
183
+ from(node) {
184
+ return this.ranges.get(node).from;
185
+ }
186
+ /** Where a node's text starts with the parentheses around it: where a node
187
+ * built on it starts. */
188
+ start(node) {
189
+ return this.opens.get(node) ?? this.from(node);
190
+ }
191
+ /** A diagnostic for the lexeme the parser is at. */
192
+ unexpected(expected) {
193
+ const t = this.peek();
194
+ const range = t ?? { from: this.text.length, to: this.text.length };
195
+ return new JqSyntaxError(diagnostic(this.text, "syntax", range, `syntax: unexpected ${describe(t)}; expected ${expected}.`), this.at);
196
+ }
197
+ expectPunct(value, expected = `"${value}"`) {
198
+ if (!this.isPunct(value))
199
+ throw this.unexpected(expected);
200
+ return this.next();
201
+ }
202
+ expectOp(value, expected = `"${value}"`) {
203
+ if (!this.isOp(value))
204
+ throw this.unexpected(expected);
205
+ return this.next();
206
+ }
207
+ expectKeyword(value, expected = `"${value}"`) {
208
+ if (!this.isKeyword(value))
209
+ throw this.unexpected(expected);
210
+ return this.next();
211
+ }
212
+ expectVariable(expected) {
213
+ const t = this.peek();
214
+ if (t?.role !== "variable")
215
+ throw this.unexpected(expected);
216
+ this.next();
217
+ return t.value;
218
+ }
219
+ /** Whether a lexeme can begin a filter. */
220
+ startsFilter(t) {
221
+ if (t === undefined)
222
+ return false;
223
+ switch (t.role) {
224
+ case "field":
225
+ case "ident":
226
+ case "loc":
227
+ case "variable":
228
+ case "format":
229
+ case "number":
230
+ case "string_start":
231
+ return true;
232
+ case "punct":
233
+ return t.value === "." || t.value === ".." || t.value === "(" || t.value === "[" || t.value === "{";
234
+ case "op":
235
+ return t.value === "-";
236
+ case "keyword":
237
+ return FILTER_KEYWORDS.has(t.value);
238
+ default:
239
+ return false;
240
+ }
241
+ }
242
+ /** Reads a `|` that must be followed by a filter. */
243
+ pipeThenFilter() {
244
+ const bar = this.next();
245
+ if (!this.startsFilter(this.peek())) {
246
+ throw new JqSyntaxError(diagnostic(this.text, "syntax", bar, "syntax: expected a filter after \"|\".", [fix("Remove the |", bar.from, 1, "")]), this.at - 1);
247
+ }
248
+ }
249
+ /** Reads the whole program. */
250
+ parseProgram() {
251
+ const node = this.parseQuery();
252
+ if (this.peek() !== undefined)
253
+ throw this.unexpected("\"|\", \",\" or end of input");
254
+ return node;
255
+ }
256
+ /** Query: pipes, right-associative, over commas. */
257
+ parseQuery() {
258
+ const left = this.parseComma();
259
+ if (this.isOp("|")) {
260
+ this.pipeThenFilter();
261
+ const right = this.parseQuery();
262
+ return this.mk(variant("pipe", { left, right }), this.start(left));
263
+ }
264
+ return left;
265
+ }
266
+ parseComma() {
267
+ let left = this.parseBinding();
268
+ while (this.isOp(",")) {
269
+ this.next();
270
+ const right = this.parseBinding();
271
+ left = this.mk(variant("comma", { left, right }), this.start(left));
272
+ }
273
+ return left;
274
+ }
275
+ /** `def f: body; rest`, `label $name | body`, `source as $p | body`, or an expression. */
276
+ parseBinding() {
277
+ const t = this.peek();
278
+ if (t?.role === "keyword" && t.value === "def") {
279
+ this.next();
280
+ const nameToken = this.peek();
281
+ if (nameToken?.role !== "ident")
282
+ throw this.unexpected("a function name");
283
+ this.next();
284
+ const params = [];
285
+ if (this.isPunct("(")) {
286
+ this.next();
287
+ for (;;) {
288
+ const param = this.peek();
289
+ if (param?.role === "variable")
290
+ params.push(`$${param.value}`);
291
+ else if (param?.role === "ident")
292
+ params.push(param.value);
293
+ else
294
+ throw this.unexpected("a parameter (name or $name)");
295
+ this.next();
296
+ if (this.isPunct(";")) {
297
+ this.next();
298
+ continue;
299
+ }
300
+ this.expectPunct(")", "\";\" or \")\"");
301
+ break;
302
+ }
303
+ }
304
+ this.expectPunct(":");
305
+ const body = this.parseQuery();
306
+ this.expectPunct(";", "\";\" after the definition");
307
+ const rest = this.parseQuery();
308
+ return this.mk(variant("def", { body, name: nameToken.value, params, rest }), t.from);
309
+ }
310
+ if (t?.role === "keyword" && t.value === "label") {
311
+ this.next();
312
+ const name = this.expectVariable("a label ($name)");
313
+ if (!this.isOp("|"))
314
+ throw this.unexpected("\"|\"");
315
+ this.pipeThenFilter();
316
+ const body = this.parseQuery();
317
+ return this.mk(variant("label", { body, name }), t.from);
318
+ }
319
+ const source = this.parseExpr();
320
+ if (this.isKeyword("as")) {
321
+ this.next();
322
+ const patterns = [this.parsePattern()];
323
+ while (this.isOp("?//")) {
324
+ this.next();
325
+ patterns.push(this.parsePattern());
326
+ }
327
+ if (!this.isOp("|"))
328
+ throw this.unexpected("\"|\" or \"?//\"");
329
+ this.pipeThenFilter();
330
+ const body = this.parseQuery();
331
+ return this.mk(variant("bind", { body, patterns, source }), this.start(source));
332
+ }
333
+ return source;
334
+ }
335
+ /** An expression: `//` and below. */
336
+ parseExpr() {
337
+ const left = this.parseUpdate();
338
+ if (this.isOp("//")) {
339
+ this.next();
340
+ const right = this.parseExpr();
341
+ return this.mk(variant("alternative", { left, right }), this.start(left));
342
+ }
343
+ return left;
344
+ }
345
+ /** Update-assignment, non-associative. */
346
+ parseUpdate() {
347
+ const path = this.parseOr();
348
+ const t = this.peek();
349
+ if (t?.role === "op" && UPDATE_OPS.has(t.value)) {
350
+ this.next();
351
+ const value = this.parseOr();
352
+ const again = this.peek();
353
+ if (again?.role === "op" && UPDATE_OPS.has(again.value)) {
354
+ throw this.unexpected(`parentheses around one side of "${t.value}"`);
355
+ }
356
+ return this.mk(variant("update", { op: t.value, path, value }), this.start(path));
357
+ }
358
+ return path;
359
+ }
360
+ parseOr() {
361
+ let left = this.parseAnd();
362
+ while (this.isKeyword("or")) {
363
+ this.next();
364
+ const right = this.parseAnd();
365
+ left = this.mk(variant("binary", { left, op: "or", right }), this.start(left));
366
+ }
367
+ return left;
368
+ }
369
+ parseAnd() {
370
+ let left = this.parseComparison();
371
+ while (this.isKeyword("and")) {
372
+ this.next();
373
+ const right = this.parseComparison();
374
+ left = this.mk(variant("binary", { left, op: "and", right }), this.start(left));
375
+ }
376
+ return left;
377
+ }
378
+ /** Comparisons, non-associative. */
379
+ parseComparison() {
380
+ const left = this.parseAdditive();
381
+ const t = this.peek();
382
+ if (t?.role === "op" && COMPARISON_OPS.has(t.value)) {
383
+ this.next();
384
+ const right = this.parseAdditive();
385
+ const again = this.peek();
386
+ if (again?.role === "op" && COMPARISON_OPS.has(again.value)) {
387
+ throw this.unexpected(`parentheses around one side of "${t.value}"`);
388
+ }
389
+ return this.mk(variant("binary", { left, op: t.value, right }), this.start(left));
390
+ }
391
+ return left;
392
+ }
393
+ parseAdditive() {
394
+ let left = this.parseMultiplicative();
395
+ for (;;) {
396
+ const t = this.peek();
397
+ if (t?.role !== "op" || (t.value !== "+" && t.value !== "-"))
398
+ return left;
399
+ this.next();
400
+ const right = this.parseMultiplicative();
401
+ left = this.mk(variant("binary", { left, op: t.value, right }), this.start(left));
402
+ }
403
+ }
404
+ parseMultiplicative() {
405
+ let left = this.parseTerm();
406
+ for (;;) {
407
+ const t = this.peek();
408
+ if (t?.role !== "op" || (t.value !== "*" && t.value !== "/" && t.value !== "%"))
409
+ return left;
410
+ this.next();
411
+ const right = this.parseTerm();
412
+ left = this.mk(variant("binary", { left, op: t.value, right }), this.start(left));
413
+ }
414
+ }
415
+ /** A term: `-` over a term, or a postfix term. */
416
+ parseTerm() {
417
+ const t = this.peek();
418
+ if (t?.role === "op" && t.value === "-") {
419
+ this.next();
420
+ const operand = this.parseTerm();
421
+ return this.mk(variant("negate", operand), t.from);
422
+ }
423
+ return this.parsePostfix();
424
+ }
425
+ /** A primary term and its postfix operators. */
426
+ parsePostfix() {
427
+ const start = this.peek();
428
+ let node = this.parsePrimary();
429
+ // `try` takes every postfix operator into its body or handler.
430
+ if (start?.role === "keyword" && start.value === "try")
431
+ return node;
432
+ const from = this.start(node);
433
+ for (;;) {
434
+ const t = this.peek();
435
+ if (t?.role === "field") {
436
+ this.next();
437
+ const optional = this.eatOptional();
438
+ node = this.mk(variant("field", { name: t.value, optional, target: node }), from);
439
+ }
440
+ else if (this.isPunct(".") && (this.peek(1)?.role === "string_start" || this.peek(1)?.role === "format")) {
441
+ this.next();
442
+ node = this.fieldOrIndex(node, this.parseKeyString(), from);
443
+ }
444
+ else if (this.isPunct(".") && this.isPunct("[", 1)) {
445
+ this.next();
446
+ node = this.parseBracketSuffix(node, from);
447
+ }
448
+ else if (this.isPunct("[")) {
449
+ node = this.parseBracketSuffix(node, from);
450
+ }
451
+ else if (this.isOp("?")) {
452
+ this.next();
453
+ node = this.mk(variant("try", { body: node, catch: none }), from);
454
+ }
455
+ else {
456
+ return node;
457
+ }
458
+ }
459
+ }
460
+ eatOptional() {
461
+ if (!this.isOp("?"))
462
+ return false;
463
+ this.next();
464
+ return true;
465
+ }
466
+ /** `t."name"` is a field; `t."a\(f)"` indexes by the string. */
467
+ fieldOrIndex(target, key, from) {
468
+ const optional = this.eatOptional();
469
+ if (key.constant !== undefined)
470
+ return this.mk(variant("field", { name: key.constant, optional, target }), from);
471
+ return this.mk(variant("index", { index: key.node, optional, target }), from);
472
+ }
473
+ /** `t[]`, `t[e]`, `t[a:b]`, `t[a:]`, `t[:b]`, each with an optional `?`. */
474
+ parseBracketSuffix(target, from) {
475
+ this.expectPunct("[");
476
+ if (this.isPunct("]")) {
477
+ this.next();
478
+ const optional = this.eatOptional();
479
+ return this.mk(variant("iterate", { optional, target }), from);
480
+ }
481
+ if (this.isPunct(":")) {
482
+ this.next();
483
+ const to = this.parseQuery();
484
+ this.expectPunct("]");
485
+ const optional = this.eatOptional();
486
+ return this.mk(variant("slice", { from: none, optional, target, to: some(to) }), from);
487
+ }
488
+ const index = this.parseQuery();
489
+ if (this.isPunct(":")) {
490
+ this.next();
491
+ if (this.isPunct("]")) {
492
+ this.next();
493
+ const optional = this.eatOptional();
494
+ return this.mk(variant("slice", { from: some(index), optional, target, to: none }), from);
495
+ }
496
+ const to = this.parseQuery();
497
+ this.expectPunct("]");
498
+ const optional = this.eatOptional();
499
+ return this.mk(variant("slice", { from: some(index), optional, target, to: some(to) }), from);
500
+ }
501
+ this.expectPunct("]", "\"]\" or \":\"");
502
+ const optional = this.eatOptional();
503
+ return this.mk(variant("index", { index, optional, target }), from);
504
+ }
505
+ /** A primary term. */
506
+ parsePrimary() {
507
+ const t = this.peek();
508
+ if (t === undefined)
509
+ throw this.unexpected("a filter");
510
+ switch (t.role) {
511
+ case "field": {
512
+ this.next();
513
+ // `.name` reads the field of `.`, whose text is the dot.
514
+ const identity = variant("identity", null);
515
+ this.ranges.set(identity, { from: t.from, to: t.from + 1 });
516
+ const optional = this.eatOptional();
517
+ return this.mk(variant("field", { name: t.value, optional, target: identity }), t.from);
518
+ }
519
+ case "number":
520
+ this.next();
521
+ return this.mk(this.numberLiteral(t), t.from);
522
+ case "string_start":
523
+ case "format": {
524
+ const term = this.parseStringTerm();
525
+ return term.node;
526
+ }
527
+ case "variable":
528
+ this.next();
529
+ return this.mk(variant("variable", t.value), t.from);
530
+ case "loc":
531
+ this.next();
532
+ return this.mk(variant("variable", "__loc__"), t.from);
533
+ case "ident":
534
+ return this.parseIdentifier(t);
535
+ case "punct":
536
+ return this.parsePunctuation(t);
537
+ case "keyword":
538
+ return this.parseKeywordTerm(t);
539
+ default:
540
+ throw this.unexpected("a filter");
541
+ }
542
+ }
543
+ parsePunctuation(t) {
544
+ switch (t.value) {
545
+ case ".": {
546
+ this.next();
547
+ const identity = this.mk(variant("identity", null), t.from);
548
+ const after = this.peek();
549
+ if (after?.role === "string_start" || after?.role === "format") {
550
+ return this.fieldOrIndex(identity, this.parseKeyString(), t.from);
551
+ }
552
+ return identity;
553
+ }
554
+ case "..":
555
+ this.next();
556
+ return this.mk(variant("descend", null), t.from);
557
+ case "(": {
558
+ this.next();
559
+ const inner = this.parseQuery();
560
+ this.expectPunct(")");
561
+ // The node's own span leaves out its parentheses; a node built on it
562
+ // starts at the outermost `(`.
563
+ this.opens.set(inner, t.from);
564
+ return inner;
565
+ }
566
+ case "[": {
567
+ this.next();
568
+ if (this.isPunct("]")) {
569
+ this.next();
570
+ return this.mk(variant("array", none), t.from);
571
+ }
572
+ const body = this.parseQuery();
573
+ this.expectPunct("]");
574
+ return this.mk(variant("array", some(body)), t.from);
575
+ }
576
+ case "{":
577
+ return this.parseObject(t);
578
+ default:
579
+ throw this.unexpected("a filter");
580
+ }
581
+ }
582
+ parseIdentifier(t) {
583
+ this.next();
584
+ if (!this.isPunct("(")) {
585
+ if (t.value === "null")
586
+ return this.mk(variant("literal", variant("null", null)), t.from);
587
+ if (t.value === "true")
588
+ return this.mk(variant("literal", variant("boolean", true)), t.from);
589
+ if (t.value === "false")
590
+ return this.mk(variant("literal", variant("boolean", false)), t.from);
591
+ return this.mk(variant("call", { args: [], name: t.value }), t.from);
592
+ }
593
+ this.next();
594
+ const args = [this.parseQuery()];
595
+ while (this.isPunct(";")) {
596
+ this.next();
597
+ args.push(this.parseQuery());
598
+ }
599
+ this.expectPunct(")", "\";\" or \")\"");
600
+ return this.mk(variant("call", { args, name: t.value }), t.from);
601
+ }
602
+ parseKeywordTerm(t) {
603
+ switch (t.value) {
604
+ case "reduce": {
605
+ this.next();
606
+ const source = this.parseExpr();
607
+ this.expectKeyword("as");
608
+ const pattern = this.parsePattern();
609
+ this.expectPunct("(");
610
+ const init = this.parseQuery();
611
+ this.expectPunct(";");
612
+ const update = this.parseQuery();
613
+ this.expectPunct(")");
614
+ return this.mk(variant("reduce", { init, pattern, source, update }), t.from);
615
+ }
616
+ case "foreach": {
617
+ this.next();
618
+ const source = this.parseExpr();
619
+ this.expectKeyword("as");
620
+ const pattern = this.parsePattern();
621
+ this.expectPunct("(");
622
+ const init = this.parseQuery();
623
+ this.expectPunct(";");
624
+ const update = this.parseQuery();
625
+ let extract = none;
626
+ if (this.isPunct(";")) {
627
+ this.next();
628
+ extract = some(this.parseQuery());
629
+ }
630
+ this.expectPunct(")", "\";\" or \")\"");
631
+ return this.mk(variant("foreach", { extract, init, pattern, source, update }), t.from);
632
+ }
633
+ case "if": {
634
+ this.next();
635
+ const branches = [];
636
+ let condition = this.parseQuery();
637
+ this.expectKeyword("then");
638
+ branches.push({ condition, then: this.parseQuery() });
639
+ let otherwise = none;
640
+ for (;;) {
641
+ if (this.isKeyword("elif")) {
642
+ this.next();
643
+ condition = this.parseQuery();
644
+ this.expectKeyword("then");
645
+ branches.push({ condition, then: this.parseQuery() });
646
+ }
647
+ else if (this.isKeyword("else")) {
648
+ this.next();
649
+ otherwise = some(this.parseQuery());
650
+ this.expectKeyword("end");
651
+ break;
652
+ }
653
+ else if (this.isKeyword("end")) {
654
+ this.next();
655
+ break;
656
+ }
657
+ else {
658
+ throw this.unexpected("\"elif\", \"else\" or \"end\"");
659
+ }
660
+ }
661
+ return this.mk(variant("if", { branches, otherwise }), t.from);
662
+ }
663
+ case "try": {
664
+ this.next();
665
+ const body = this.parseTerm();
666
+ let handler = none;
667
+ if (this.isKeyword("catch")) {
668
+ this.next();
669
+ handler = some(this.parseTerm());
670
+ }
671
+ return this.mk(variant("try", { body, catch: handler }), t.from);
672
+ }
673
+ case "break": {
674
+ this.next();
675
+ const name = this.expectVariable("a label ($name)");
676
+ return this.mk(variant("break", name), t.from);
677
+ }
678
+ default:
679
+ if (MODULE_KEYWORDS.has(t.value)) {
680
+ throw new JqSyntaxError(diagnostic(this.text, "unsupported", t, `unsupported: ${t.value} is excluded — queries are deterministic and have no host access.`), this.at);
681
+ }
682
+ throw this.unexpected("a filter");
683
+ }
684
+ }
685
+ /** A number literal: an Integer when written without `.` or an exponent. */
686
+ numberLiteral(t) {
687
+ if (/^[0-9]+$/.test(t.value)) {
688
+ const value = BigInt(t.value);
689
+ if (value > INTEGER_MAX) {
690
+ throw new JqSyntaxError(diagnostic(this.text, "syntax", t, `syntax: ${t.value} is too large for an Integer; write ${t.value}.0 for a Float.`), this.at - 1);
691
+ }
692
+ return variant("literal", variant("integer", value));
693
+ }
694
+ const value = Number(t.value);
695
+ if (!Number.isFinite(value)) {
696
+ throw new JqSyntaxError(diagnostic(this.text, "syntax", t, `syntax: ${t.value} is too large for a Float.`), this.at - 1);
697
+ }
698
+ return variant("literal", variant("float", value));
699
+ }
700
+ /** A string naming a field or a key: `@format` must be followed by its
701
+ * string there, as in jq. */
702
+ parseKeyString() {
703
+ const t = this.peek();
704
+ if (t.role === "format" && this.peek(1)?.role !== "string_start") {
705
+ this.next();
706
+ throw this.unexpected(`a string after @${t.value}`);
707
+ }
708
+ return this.parseStringTerm();
709
+ }
710
+ /** A string, with an optional `@format` before it, or a bare `@format`. */
711
+ parseStringTerm() {
712
+ const t = this.peek();
713
+ if (t.role === "format") {
714
+ this.next();
715
+ if (this.peek()?.role !== "string_start") {
716
+ return { constant: undefined, node: this.mk(variant("format", { name: t.value, string: none }), t.from) };
717
+ }
718
+ const string = this.parseString();
719
+ return { constant: undefined, node: this.mk(variant("format", { name: t.value, string: some(string.node) }), t.from) };
720
+ }
721
+ return this.parseString();
722
+ }
723
+ /** A string: a constant, or a `string` node when it interpolates. */
724
+ parseString() {
725
+ const start = this.peek();
726
+ if (start?.role !== "string_start")
727
+ throw this.unexpected("a string");
728
+ this.next();
729
+ const parts = [];
730
+ let text = "";
731
+ for (;;) {
732
+ const t = this.next();
733
+ if (t.role === "string_text") {
734
+ text += t.value;
735
+ }
736
+ else if (t.role === "interp_start") {
737
+ if (text !== "")
738
+ parts.push(variant("text", text));
739
+ text = "";
740
+ const inner = this.parseQuery();
741
+ if (this.peek()?.role !== "interp_end")
742
+ throw this.unexpected("\")\" to close the interpolation");
743
+ this.next();
744
+ parts.push(variant("interpolate", inner));
745
+ }
746
+ else {
747
+ break; // string_end: the lexer and the bracket check guarantee it
748
+ }
749
+ }
750
+ if (parts.length === 0)
751
+ return { constant: text, node: this.mk(variant("literal", variant("string", text)), start.from) };
752
+ if (text !== "")
753
+ parts.push(variant("text", text));
754
+ return { constant: undefined, node: this.mk(variant("string", parts), start.from) };
755
+ }
756
+ /** `{…}`: literal keys build Structs, computed keys Dicts. */
757
+ parseObject(open) {
758
+ this.next();
759
+ const entries = [];
760
+ if (this.isPunct("}")) {
761
+ this.next();
762
+ return this.mk(variant("object", entries), open.from);
763
+ }
764
+ for (;;) {
765
+ entries.push(this.parseObjectEntry());
766
+ if (this.isOp(",")) {
767
+ this.next();
768
+ if (this.isPunct("}")) {
769
+ this.next();
770
+ break;
771
+ }
772
+ continue;
773
+ }
774
+ this.expectPunct("}", "\",\" or \"}\"");
775
+ break;
776
+ }
777
+ return this.mk(variant("object", entries), open.from);
778
+ }
779
+ parseObjectEntry() {
780
+ const t = this.peek();
781
+ let key;
782
+ let valueRequired = false;
783
+ if (t?.role === "ident" || t?.role === "keyword") {
784
+ this.next();
785
+ key = variant("name", t.value);
786
+ }
787
+ else if (t?.role === "string_start" || t?.role === "format") {
788
+ const term = this.parseKeyString();
789
+ key = term.constant !== undefined ? variant("name", term.constant) : variant("computed", term.node);
790
+ }
791
+ else if (t?.role === "variable") {
792
+ this.next();
793
+ key = variant("variable", t.value);
794
+ }
795
+ else if (t?.role === "loc") {
796
+ this.next();
797
+ return { key: variant("variable", "__loc__"), value: none };
798
+ }
799
+ else if (t?.role === "punct" && t.value === "(") {
800
+ this.next();
801
+ const computed = this.parseQuery();
802
+ this.expectPunct(")");
803
+ key = variant("computed", computed);
804
+ valueRequired = true;
805
+ }
806
+ else {
807
+ throw this.unexpected("a key");
808
+ }
809
+ if (!this.isPunct(":")) {
810
+ if (valueRequired)
811
+ throw this.unexpected("\":\"");
812
+ return { key, value: none };
813
+ }
814
+ this.next();
815
+ return { key, value: some(this.parseObjectValue()) };
816
+ }
817
+ /** An object's value: an expression, or expressions piped. */
818
+ parseObjectValue() {
819
+ const left = this.parseExpr();
820
+ if (this.isOp("|")) {
821
+ this.pipeThenFilter();
822
+ const right = this.parseObjectValue();
823
+ return this.mk(variant("pipe", { left, right }), this.start(left));
824
+ }
825
+ return left;
826
+ }
827
+ /** A destructuring pattern. */
828
+ parsePattern() {
829
+ const t = this.peek();
830
+ if (t?.role === "variable") {
831
+ this.next();
832
+ return this.mk(variant("variable", t.value), t.from);
833
+ }
834
+ if (t?.role === "punct" && t.value === "[") {
835
+ this.next();
836
+ const items = [this.parsePattern()];
837
+ while (this.isOp(",")) {
838
+ this.next();
839
+ items.push(this.parsePattern());
840
+ }
841
+ this.expectPunct("]", "\",\" or \"]\"");
842
+ return this.mk(variant("array", items), t.from);
843
+ }
844
+ if (t?.role === "punct" && t.value === "{") {
845
+ this.next();
846
+ const entries = [this.parseObjectPatternEntry()];
847
+ while (this.isOp(",")) {
848
+ this.next();
849
+ entries.push(this.parseObjectPatternEntry());
850
+ }
851
+ this.expectPunct("}", "\",\" or \"}\"");
852
+ return this.mk(variant("object", entries), t.from);
853
+ }
854
+ throw this.unexpected("a pattern ($name, [...] or {...})");
855
+ }
856
+ parseObjectPatternEntry() {
857
+ const t = this.peek();
858
+ if (t?.role === "variable") {
859
+ this.next();
860
+ if (this.isPunct(":")) {
861
+ throw new JqSyntaxError(diagnostic(this.text, "syntax", { from: t.from, to: this.peek().to }, `syntax: "$${t.value}: pattern" is not supported; write "${t.value}: pattern" and bind $${t.value} separately.`), this.at);
862
+ }
863
+ return { key: t.value, value: none };
864
+ }
865
+ let key;
866
+ if (t?.role === "ident" || t?.role === "keyword") {
867
+ this.next();
868
+ key = t.value;
869
+ }
870
+ else if (t?.role === "string_start" || t?.role === "format") {
871
+ const term = this.parseKeyString();
872
+ if (term.constant === undefined)
873
+ throw this.computedPatternKey(this.from(term.node));
874
+ key = term.constant;
875
+ }
876
+ else if (t?.role === "punct" && t.value === "(") {
877
+ throw this.computedPatternKey(t.from);
878
+ }
879
+ else {
880
+ throw this.unexpected("a key");
881
+ }
882
+ this.expectPunct(":");
883
+ return { key, value: some(this.parsePattern()) };
884
+ }
885
+ computedPatternKey(from) {
886
+ return new JqSyntaxError(diagnostic(this.text, "syntax", { from, to: Math.max(from + 1, this.lastEnd) }, "syntax: computed keys in patterns are not supported (#875 Defer)."), this.at);
887
+ }
888
+ }
889
+ /**
890
+ * The index after which parsing resumes following a problem at `failed`: the
891
+ * next `|` outside every bracket, `if … end` and `def … ;`.
892
+ *
893
+ * @param toks - the significant lexemes
894
+ * @param failed - the index the problem was found at
895
+ * @returns the index of that `|`, or `undefined` when there is none
896
+ */
897
+ function nextTopLevelPipe(toks, failed) {
898
+ const open = [];
899
+ for (let i = 0; i < toks.length; i++) {
900
+ const t = toks[i];
901
+ if (i > failed && open.length === 0 && t.role === "op" && t.value === "|")
902
+ return i;
903
+ if ((t.role === "punct" && CLOSER[t.value] !== undefined) || t.role === "interp_start")
904
+ open.push(t.value);
905
+ else if ((t.role === "punct" && (t.value === ")" || t.value === "]" || t.value === "}")) || t.role === "interp_end")
906
+ open.pop();
907
+ else if (t.role === "keyword" && (t.value === "if" || t.value === "def"))
908
+ open.push(t.value);
909
+ else if (t.role === "keyword" && t.value === "end" && open[open.length - 1] === "if")
910
+ open.pop();
911
+ else if (t.role === "punct" && t.value === ";" && open[open.length - 1] === "def")
912
+ open.pop();
913
+ }
914
+ return undefined;
915
+ }
916
+ /**
917
+ * The spans of a program's nodes, by path, from the parser's ranges.
918
+ *
919
+ * @param root - the program
920
+ * @param ranges - each node's and pattern's range
921
+ * @param length - the text's length: the root covers it all
922
+ * @returns the spans
923
+ */
924
+ function spansOf(root, ranges, length) {
925
+ const spans = new Map();
926
+ const visitPattern = (pattern, path) => {
927
+ spans.set(path, ranges.get(pattern));
928
+ for (const child of jqPatternChildren(pattern))
929
+ visitPattern(child.pattern, childPath(path, child.step));
930
+ };
931
+ const visit = (node, path) => {
932
+ spans.set(path, path === "" ? { from: 0, to: length } : ranges.get(node));
933
+ for (const child of jqChildren(node)) {
934
+ if (child.node !== undefined)
935
+ visit(child.node, childPath(path, child.step));
936
+ else
937
+ visitPattern(child.pattern, childPath(path, child.step));
938
+ }
939
+ };
940
+ visit(root, "");
941
+ return spans;
942
+ }
943
+ /**
944
+ * Parses jq text into a {@link JqType} program.
945
+ *
946
+ * @param text - jq 1.8 text
947
+ * @returns the program with the span of every node, or `none` with the
948
+ * problems that stopped it
949
+ *
950
+ * @remarks
951
+ * The parser keeps jq's sugar, so printing gives back what was written:
952
+ * `.a.b` is two `field` nodes, `.a?` sets `optional`, and `f?` is a `try`
953
+ * with no `catch`. A literal is the typed East value it writes
954
+ * (`JqLiteralType`): a number written without `.` or an exponent is an
955
+ * Integer (64-bit; larger is a problem), any other number a Float, and a
956
+ * string without interpolation a String. `$__loc__` is the variable
957
+ * `__loc__`; `import`, `include` and `module` are `unsupported`.
958
+ *
959
+ * A problem is a `syntax` diagnostic with its span, and a fix where one is
960
+ * obvious: an unclosed bracket or string is closed, a trailing `|` removed.
961
+ * After a problem the parser resumes at the next `|` outside every bracket,
962
+ * so one typo reports one problem.
963
+ *
964
+ * @example
965
+ * ```ts
966
+ * const parsed = parseJq(".orders | length");
967
+ * parsed.program.type; // "some"
968
+ * parsed.spans.get("pipe.left"); // { from: 0, to: 7 }
969
+ *
970
+ * parseJq(".orders |").diagnostics[0]!.message;
971
+ * // 'syntax: expected a filter after "|".'
972
+ * ```
973
+ */
974
+ export function parseJq(text) {
975
+ const lexemes = scanJq(text);
976
+ const diagnostics = [];
977
+ for (const t of lexemes) {
978
+ if (t.role === "unterminated") {
979
+ diagnostics.push(diagnostic(text, "syntax", t, "syntax: this string is never closed.", [fix("Close it", t.to, 0, "\"")]));
980
+ }
981
+ else if (t.error !== undefined) {
982
+ diagnostics.push(diagnostic(text, "syntax", t, `syntax: ${t.error}.`));
983
+ }
984
+ }
985
+ diagnostics.push(...checkBrackets(text, lexemes));
986
+ const significant = lexemes.filter(t => t.role !== "ws" && t.role !== "comment");
987
+ if (diagnostics.length === 0 && significant.length === 0) {
988
+ diagnostics.push(diagnostic(text, "syntax", undefined, "syntax: empty program."));
989
+ }
990
+ if (diagnostics.length > 0)
991
+ return { text, program: none, spans: new Map(), diagnostics };
992
+ let start = 0;
993
+ for (;;) {
994
+ const parser = new Parser(text, significant, start);
995
+ try {
996
+ const program = parser.parseProgram();
997
+ if (start === 0)
998
+ return { text, program: some(program), spans: spansOf(program, parser.ranges, text.length), diagnostics };
999
+ break;
1000
+ }
1001
+ catch (e) {
1002
+ if (!(e instanceof JqSyntaxError))
1003
+ throw e;
1004
+ diagnostics.push(e.diagnostic);
1005
+ const resume = nextTopLevelPipe(significant, Math.max(e.token, parser.position) - 1);
1006
+ if (resume === undefined || resume + 1 >= significant.length)
1007
+ break;
1008
+ start = resume + 1;
1009
+ }
1010
+ }
1011
+ return { text, program: none, spans: new Map(), diagnostics };
1012
+ }
1013
+ //# sourceMappingURL=parse.js.map