@elaraai/east 1.0.84 → 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 (239) 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 +5 -3
  81. package/dist/src/naming.d.ts.map +1 -1
  82. package/dist/src/naming.js.map +1 -1
  83. package/dist/src/portable.spec.d.ts +6 -0
  84. package/dist/src/portable.spec.d.ts.map +1 -0
  85. package/dist/src/portable.spec.js +181 -0
  86. package/dist/src/portable.spec.js.map +1 -0
  87. package/dist/src/query/evaluate.d.ts +98 -0
  88. package/dist/src/query/evaluate.d.ts.map +1 -0
  89. package/dist/src/query/evaluate.js +193 -0
  90. package/dist/src/query/evaluate.js.map +1 -0
  91. package/dist/src/query/index.d.ts +17 -0
  92. package/dist/src/query/index.d.ts.map +1 -0
  93. package/dist/src/query/index.js +17 -0
  94. package/dist/src/query/index.js.map +1 -0
  95. package/dist/src/query/jq/catalog.d.ts +174 -0
  96. package/dist/src/query/jq/catalog.d.ts.map +1 -0
  97. package/dist/src/query/jq/catalog.js +1740 -0
  98. package/dist/src/query/jq/catalog.js.map +1 -0
  99. package/dist/src/query/jq/check.d.ts +210 -0
  100. package/dist/src/query/jq/check.d.ts.map +1 -0
  101. package/dist/src/query/jq/check.js +2261 -0
  102. package/dist/src/query/jq/check.js.map +1 -0
  103. package/dist/src/query/jq/complete.d.ts +89 -0
  104. package/dist/src/query/jq/complete.d.ts.map +1 -0
  105. package/dist/src/query/jq/complete.js +320 -0
  106. package/dist/src/query/jq/complete.js.map +1 -0
  107. package/dist/src/query/jq/describe.d.ts +53 -0
  108. package/dist/src/query/jq/describe.d.ts.map +1 -0
  109. package/dist/src/query/jq/describe.js +149 -0
  110. package/dist/src/query/jq/describe.js.map +1 -0
  111. package/dist/src/query/jq/lex.d.ts +118 -0
  112. package/dist/src/query/jq/lex.d.ts.map +1 -0
  113. package/dist/src/query/jq/lex.js +371 -0
  114. package/dist/src/query/jq/lex.js.map +1 -0
  115. package/dist/src/query/jq/literals.d.ts +49 -0
  116. package/dist/src/query/jq/literals.d.ts.map +1 -0
  117. package/dist/src/query/jq/literals.js +78 -0
  118. package/dist/src/query/jq/literals.js.map +1 -0
  119. package/dist/src/query/jq/messages.d.ts +126 -0
  120. package/dist/src/query/jq/messages.d.ts.map +1 -0
  121. package/dist/src/query/jq/messages.js +172 -0
  122. package/dist/src/query/jq/messages.js.map +1 -0
  123. package/dist/src/query/jq/parse.d.ts +70 -0
  124. package/dist/src/query/jq/parse.d.ts.map +1 -0
  125. package/dist/src/query/jq/parse.js +1013 -0
  126. package/dist/src/query/jq/parse.js.map +1 -0
  127. package/dist/src/query/jq/print.d.ts +59 -0
  128. package/dist/src/query/jq/print.d.ts.map +1 -0
  129. package/dist/src/query/jq/print.js +587 -0
  130. package/dist/src/query/jq/print.js.map +1 -0
  131. package/dist/src/query/jq/shapes.d.ts +345 -0
  132. package/dist/src/query/jq/shapes.d.ts.map +1 -0
  133. package/dist/src/query/jq/shapes.js +584 -0
  134. package/dist/src/query/jq/shapes.js.map +1 -0
  135. package/dist/src/query/jq/spans.d.ts +133 -0
  136. package/dist/src/query/jq/spans.d.ts.map +1 -0
  137. package/dist/src/query/jq/spans.js +195 -0
  138. package/dist/src/query/jq/spans.js.map +1 -0
  139. package/dist/src/query/jq/split.d.ts +334 -0
  140. package/dist/src/query/jq/split.d.ts.map +1 -0
  141. package/dist/src/query/jq/split.js +2108 -0
  142. package/dist/src/query/jq/split.js.map +1 -0
  143. package/dist/src/query/jq/strftime.d.ts +20 -0
  144. package/dist/src/query/jq/strftime.d.ts.map +1 -0
  145. package/dist/src/query/jq/strftime.js +65 -0
  146. package/dist/src/query/jq/strftime.js.map +1 -0
  147. package/dist/src/query/jq/summary.d.ts +139 -0
  148. package/dist/src/query/jq/summary.d.ts.map +1 -0
  149. package/dist/src/query/jq/summary.js +222 -0
  150. package/dist/src/query/jq/summary.js.map +1 -0
  151. package/dist/src/query/jq/translate-builtins.d.ts +21 -0
  152. package/dist/src/query/jq/translate-builtins.d.ts.map +1 -0
  153. package/dist/src/query/jq/translate-builtins.js +1873 -0
  154. package/dist/src/query/jq/translate-builtins.js.map +1 -0
  155. package/dist/src/query/jq/translate.d.ts +627 -0
  156. package/dist/src/query/jq/translate.d.ts.map +1 -0
  157. package/dist/src/query/jq/translate.js +3364 -0
  158. package/dist/src/query/jq/translate.js.map +1 -0
  159. package/dist/src/query/types.d.ts +505 -0
  160. package/dist/src/query/types.d.ts.map +1 -0
  161. package/dist/src/query/types.js +231 -0
  162. package/dist/src/query/types.js.map +1 -0
  163. package/dist/src/runner_exec.d.ts +134 -0
  164. package/dist/src/runner_exec.d.ts.map +1 -0
  165. package/dist/src/runner_exec.js +371 -0
  166. package/dist/src/runner_exec.js.map +1 -0
  167. package/dist/src/runner_exec.spec.d.ts +6 -0
  168. package/dist/src/runner_exec.spec.d.ts.map +1 -0
  169. package/dist/src/runner_exec.spec.js +198 -0
  170. package/dist/src/runner_exec.spec.js.map +1 -0
  171. package/dist/src/runner_io.d.ts +370 -0
  172. package/dist/src/runner_io.d.ts.map +1 -0
  173. package/dist/src/runner_io.js +563 -0
  174. package/dist/src/runner_io.js.map +1 -0
  175. package/dist/src/runner_io.spec.d.ts +6 -0
  176. package/dist/src/runner_io.spec.d.ts.map +1 -0
  177. package/dist/src/runner_io.spec.js +224 -0
  178. package/dist/src/runner_io.spec.js.map +1 -0
  179. package/dist/src/runner_merge.d.ts +95 -0
  180. package/dist/src/runner_merge.d.ts.map +1 -0
  181. package/dist/src/runner_merge.js +238 -0
  182. package/dist/src/runner_merge.js.map +1 -0
  183. package/dist/src/runner_protocol.d.ts +11 -4
  184. package/dist/src/runner_protocol.d.ts.map +1 -1
  185. package/dist/src/runner_protocol.js +11 -4
  186. package/dist/src/runner_protocol.js.map +1 -1
  187. package/dist/src/serialization/beast2/index.d.ts +1 -1
  188. package/dist/src/serialization/beast2/index.d.ts.map +1 -1
  189. package/dist/src/serialization/beast2/index.js.map +1 -1
  190. package/dist/src/serialization/beast2/v5/frames.d.ts +10 -6
  191. package/dist/src/serialization/beast2/v5/frames.d.ts.map +1 -1
  192. package/dist/src/serialization/beast2/v5/frames.js +10 -6
  193. package/dist/src/serialization/beast2/v5/frames.js.map +1 -1
  194. package/dist/src/serialization/beast2/v5/frames.spec.d.ts +6 -0
  195. package/dist/src/serialization/beast2/v5/frames.spec.d.ts.map +1 -0
  196. package/dist/src/serialization/beast2/v5/frames.spec.js +42 -0
  197. package/dist/src/serialization/beast2/v5/frames.spec.js.map +1 -0
  198. package/dist/src/serialization/beast2/v5/index.spec.js +108 -0
  199. package/dist/src/serialization/beast2/v5/index.spec.js.map +1 -1
  200. package/dist/src/serialization/beast2/v5/lazy.d.ts +12 -7
  201. package/dist/src/serialization/beast2/v5/lazy.d.ts.map +1 -1
  202. package/dist/src/serialization/beast2/v5/lazy.js +16 -6
  203. package/dist/src/serialization/beast2/v5/lazy.js.map +1 -1
  204. package/dist/src/serialization/beast2/v5/lazy.spec.js +159 -18
  205. package/dist/src/serialization/beast2/v5/lazy.spec.js.map +1 -1
  206. package/dist/src/serialization/beast2/v5/stream.d.ts +110 -21
  207. package/dist/src/serialization/beast2/v5/stream.d.ts.map +1 -1
  208. package/dist/src/serialization/beast2/v5/stream.js +153 -42
  209. package/dist/src/serialization/beast2/v5/stream.js.map +1 -1
  210. package/dist/src/serialization/beast2/v5/weight.d.ts +36 -0
  211. package/dist/src/serialization/beast2/v5/weight.d.ts.map +1 -0
  212. package/dist/src/serialization/beast2/v5/weight.js +238 -0
  213. package/dist/src/serialization/beast2/v5/weight.js.map +1 -0
  214. package/dist/src/serialization/crc32.d.ts +38 -0
  215. package/dist/src/serialization/crc32.d.ts.map +1 -0
  216. package/dist/src/serialization/crc32.js +70 -0
  217. package/dist/src/serialization/crc32.js.map +1 -0
  218. package/dist/src/serialization/crc32.spec.d.ts +6 -0
  219. package/dist/src/serialization/crc32.spec.d.ts.map +1 -0
  220. package/dist/src/serialization/crc32.spec.js +92 -0
  221. package/dist/src/serialization/crc32.spec.js.map +1 -0
  222. package/dist/src/serialization/east-inferred.spec.js +4 -0
  223. package/dist/src/serialization/east-inferred.spec.js.map +1 -1
  224. package/dist/src/serialization/east.d.ts.map +1 -1
  225. package/dist/src/serialization/east.js +134 -110
  226. package/dist/src/serialization/east.js.map +1 -1
  227. package/dist/src/serialization/east.spec.js +185 -4
  228. package/dist/src/serialization/east.spec.js.map +1 -1
  229. package/dist/src/serialization/east_text.d.ts +25 -0
  230. package/dist/src/serialization/east_text.d.ts.map +1 -0
  231. package/dist/src/serialization/east_text.js +29 -0
  232. package/dist/src/serialization/east_text.js.map +1 -0
  233. package/dist/src/serialization/index.d.ts +1 -1
  234. package/dist/src/serialization/index.d.ts.map +1 -1
  235. package/dist/src/types.d.ts +1 -1
  236. package/dist/src/types.d.ts.map +1 -1
  237. package/dist/src/types.js +7 -6
  238. package/dist/src/types.js.map +1 -1
  239. package/package.json +6 -3
@@ -9,8 +9,9 @@ import { compareFor } from "../comparison.js";
9
9
  import { SortedMap } from "../containers/sortedmap.js";
10
10
  import { SortedSet } from "../containers/sortedset.js";
11
11
  import { isVariant, variant } from "../containers/variant.js";
12
- import { EastTypeValueType, toEastTypeValue } from "../type_of_type.js";
12
+ import { EastTypeValueType, isTypeValueEqual, toEastTypeValue } from "../type_of_type.js";
13
13
  import { ref } from "../containers/ref.js";
14
+ import { printEastString } from "./east_text.js";
14
15
  class ParseError extends Error {
15
16
  position;
16
17
  path;
@@ -55,65 +56,87 @@ function _encodeRelativeRef(currentPath, targetPath) {
55
56
  return `${upLevels}#${remainingStr}`;
56
57
  }
57
58
  /**
58
- * Decode a relative reference string and return the target path array.
59
- * Input like "2#.foo[0]" returns the target path array.
60
- * Input like "1#" returns the target path array.
59
+ * Resolve a relative reference string against the current path, to the
60
+ * punctuated path of its target: "2#.foo[0]" is the current path up two
61
+ * levels, then `.foo[0]`. The reference's components are taken as the printer
62
+ * wrote them, whole, so a quoted name or a Dict key's text holding a `.` or a
63
+ * `]` names what it named.
61
64
  */
62
65
  function _decodeRelativeRef(refStr, currentPath) {
63
66
  const hashIdx = refStr.indexOf('#');
64
67
  if (hashIdx === -1) {
65
68
  throw new Error(`Invalid relative reference: ${refStr}`);
66
69
  }
67
- const upLevelStr = refStr.substring(0, hashIdx);
68
- const remainingStr = refStr.substring(hashIdx + 1);
69
- let upLevels;
70
- try {
71
- upLevels = parseInt(upLevelStr);
72
- }
73
- catch {
74
- throw new Error(`Invalid relative reference: ${refStr}`);
75
- }
76
- if (upLevels < 0 || upLevels > currentPath.length) {
70
+ const upLevels = Number.parseInt(refStr.substring(0, hashIdx), 10);
71
+ if (Number.isNaN(upLevels) || upLevels > currentPath.length) {
77
72
  throw new Error(`Invalid relative reference: going up ${upLevels} levels from depth ${currentPath.length}`);
78
73
  }
79
- // Build target path
80
- const targetPath = currentPath.slice(0, currentPath.length - upLevels);
81
- // Add remaining components if any
82
- if (remainingStr.length > 0) {
83
- // Parse the remaining punctuated path
84
- // Format: .field[0][key] etc.
85
- let pos = 0;
86
- while (pos < remainingStr.length) {
87
- if (remainingStr[pos] === '.') {
88
- // Identifier follows
89
- pos++;
90
- let end = pos;
91
- while (end < remainingStr.length && /[a-zA-Z0-9_]/.test(remainingStr[end])) {
92
- end++;
93
- }
94
- targetPath.push(`.${remainingStr.substring(pos, end)}`);
95
- pos = end;
96
- }
97
- else if (remainingStr[pos] === '[') {
98
- // Bracket expression
99
- let end = pos + 1;
100
- let depth = 1;
101
- while (end < remainingStr.length && depth > 0) {
102
- if (remainingStr[end] === '[')
103
- depth++;
104
- else if (remainingStr[end] === ']')
105
- depth--;
106
- end++;
107
- }
108
- targetPath.push(remainingStr.substring(pos, end));
109
- pos = end;
74
+ return _pathToPunctuated(currentPath.slice(0, currentPath.length - upLevels)) + refStr.substring(hashIdx + 1);
75
+ }
76
+ /**
77
+ * Find where a reference's path ends, from `pos`: the run of components a
78
+ * printer writes — `.` and a field or case name, bare or quoted in backticks,
79
+ * and a bracketed array index, Dict key or a Ref's `[]`. A quoted name, and a
80
+ * string or quoted name in a key, is taken whole, so a `.` or `]` in one does
81
+ * not end the path.
82
+ */
83
+ function _scanReferencePath(input, pos) {
84
+ while (pos < input.length) {
85
+ const ch = input[pos];
86
+ if (ch === '.') {
87
+ pos += 1;
88
+ if (input[pos] === '`') {
89
+ pos = _skipQuoted(input, pos);
110
90
  }
111
91
  else {
112
- pos++;
92
+ while (pos < input.length && /[a-zA-Z0-9_]/.test(input[pos])) {
93
+ pos += 1;
94
+ }
113
95
  }
114
96
  }
97
+ else if (ch === '[') {
98
+ pos += 1;
99
+ let depth = 1;
100
+ while (pos < input.length && depth > 0) {
101
+ const c = input[pos];
102
+ if (c === '"' || c === '`') {
103
+ pos = _skipQuoted(input, pos);
104
+ continue;
105
+ }
106
+ if (c === '[')
107
+ depth++;
108
+ else if (c === ']')
109
+ depth--;
110
+ pos += 1;
111
+ }
112
+ }
113
+ else {
114
+ break;
115
+ }
116
+ }
117
+ return pos;
118
+ }
119
+ /**
120
+ * Find the position after the string or quoted name opening at `pos`: its
121
+ * closing quote is the first no `\` escapes, and with none it runs to the end
122
+ * of the input.
123
+ */
124
+ function _skipQuoted(input, pos) {
125
+ const quote = input[pos];
126
+ pos += 1;
127
+ while (pos < input.length) {
128
+ const c = input[pos];
129
+ if (c === '\\') {
130
+ pos += 2;
131
+ }
132
+ else if (c === quote) {
133
+ return pos + 1;
134
+ }
135
+ else {
136
+ pos += 1;
137
+ }
115
138
  }
116
- return targetPath;
139
+ return input.length;
117
140
  }
118
141
  export function encodeEastFor(type) {
119
142
  // Convert EastType to EastTypeValue if necessary
@@ -176,7 +199,9 @@ export function printFor(type, typeCtx = new Map()) {
176
199
  };
177
200
  }
178
201
  else if (type.type === "String") {
179
- return (x, _ctx) => JSON.stringify(x);
202
+ // Only the escapes the grammar reads, `\\` and `\"`: every other
203
+ // character, a newline or a tab among them, is written as itself.
204
+ return (x, _ctx) => printEastString(x);
180
205
  }
181
206
  else if (type.type === "DateTime") {
182
207
  return (x, _ctx) => x.toISOString().substring(0, 23);
@@ -298,14 +323,18 @@ export function printFor(type, typeCtx = new Map()) {
298
323
  }
299
324
  else if (type.type === "Variant") {
300
325
  const case_printers = {};
326
+ // The path component a back-reference through each case names it by: the
327
+ // case's name as the text spells it, quoted when it is no plain identifier
328
+ const case_paths = new Map();
301
329
  const ret = (x, ctx = { refs: new Map(), currentPath: [] }) => {
302
330
  // Add variant case to path
303
- ctx.currentPath.push(`.${x.type}`);
331
+ ctx.currentPath.push(case_paths.get(x.type));
304
332
  const result = case_printers[x.type](x.value, ctx);
305
333
  ctx.currentPath.pop();
306
334
  return result;
307
335
  };
308
336
  for (const { name: k, type: t } of type.value) {
337
+ case_paths.set(k, `.${printIdentifier(k)}`);
309
338
  if (t.type === "Null") {
310
339
  const prefix = `.${printIdentifier(k)}`;
311
340
  case_printers[k] = (_value, _ctx) => prefix;
@@ -449,13 +478,13 @@ const createParser = (type, frozen, typeCtx = new Map()) => {
449
478
  case "Blob":
450
479
  return parseBlob(frozen);
451
480
  case "Ref":
452
- return createRefParser(type.value, frozen, typeCtx);
481
+ return createRefParser(type.value, frozen, typeCtx, type);
453
482
  case "Array":
454
- return createArrayParser(type.value, frozen, typeCtx);
483
+ return createArrayParser(type.value, frozen, typeCtx, type);
455
484
  case "Set":
456
- return createSetParser(type.value, frozen, typeCtx);
485
+ return createSetParser(type.value, frozen, typeCtx, type);
457
486
  case "Dict":
458
- return createDictParser(type.value.key, type.value.value, frozen, typeCtx);
487
+ return createDictParser(type.value.key, type.value.value, frozen, typeCtx, type);
459
488
  case "Struct":
460
489
  return createStructParser(type.value, frozen, typeCtx);
461
490
  case "Variant":
@@ -591,9 +620,11 @@ const isTokenTerminator = (input, pos) => {
591
620
  /**
592
621
  * Parses a $.path reference and returns the referenced value.
593
622
  * Returns null if not at a reference (doesn't start with '$').
594
- * Throws ParseError if the reference is invalid or undefined.
623
+ * Throws ParseError if the reference is invalid or undefined, or names a
624
+ * container of another type than `type`, the one the text expects where the
625
+ * reference stands.
595
626
  */
596
- const parseReference = (input, pos, ctx) => {
627
+ const parseReference = (input, pos, ctx, type) => {
597
628
  // Check if this looks like a reference: either "#..." or "N#..." where N is a digit
598
629
  let refStart = pos;
599
630
  let isRelative = false;
@@ -628,41 +659,14 @@ const parseReference = (input, pos, ctx) => {
628
659
  else {
629
660
  refStart = pos + 1; // Start after '#'
630
661
  }
631
- let pathEnd = refStart;
632
- while (pathEnd < input.length) {
633
- const ch = input[pathEnd];
634
- if (ch === '.') {
635
- // Dot followed by identifier - keep going until we hit non-identifier char
636
- pathEnd++;
637
- while (pathEnd < input.length && /[a-zA-Z0-9_]/.test(input[pathEnd])) {
638
- pathEnd++;
639
- }
640
- }
641
- else if (ch === '[') {
642
- // Bracket - find the matching close bracket
643
- pathEnd++;
644
- let depth = 1;
645
- while (pathEnd < input.length && depth > 0) {
646
- if (input[pathEnd] === '[')
647
- depth++;
648
- else if (input[pathEnd] === ']')
649
- depth--;
650
- pathEnd++;
651
- }
652
- }
653
- else {
654
- // Hit something that's not part of the path
655
- break;
656
- }
657
- }
662
+ const pathEnd = _scanReferencePath(input, refStart);
658
663
  const refStr = input.substring(pos, pathEnd);
659
664
  // Resolve the target path
660
665
  let targetPathStr;
661
666
  try {
662
667
  if (isRelative) {
663
668
  // refStr is like "1#.a" - pass it directly to _decodeRelativeRef
664
- const targetPath = _decodeRelativeRef(refStr, ctx.currentPath);
665
- targetPathStr = _pathToPunctuated(targetPath);
669
+ targetPathStr = _decodeRelativeRef(refStr, ctx.currentPath);
666
670
  }
667
671
  else {
668
672
  // Absolute reference: just use the path part directly (without the leading #)
@@ -673,10 +677,16 @@ const parseReference = (input, pos, ctx) => {
673
677
  throw new ParseError(`invalid reference ${refStr}: ${e instanceof Error ? e.message : String(e)}`, pos);
674
678
  }
675
679
  // Look up in refs map
676
- if (!ctx.refs.has(targetPathStr)) {
680
+ const target = ctx.refs.get(targetPathStr);
681
+ if (target === undefined) {
677
682
  throw new ParseError(`undefined reference ${refStr}`, pos);
678
683
  }
679
- return { value: ctx.refs.get(targetPathStr), position: pathEnd };
684
+ // A container of the type the text expects here, as East's type equality
685
+ // has it: one of another type would be read as a value of the wrong type
686
+ if (!isTypeValueEqual(target.type, type)) {
687
+ throw new ParseError(`invalid reference ${refStr}: it names a value of another type`, pos);
688
+ }
689
+ return { value: target.value, position: pathEnd };
680
690
  };
681
691
  const parseBoolean = (input, pos, _ctx) => {
682
692
  pos = consumeWhitespace(input, pos);
@@ -720,7 +730,8 @@ const parseString = (input, pos, _ctx) => {
720
730
  rangeStart = pos;
721
731
  }
722
732
  else {
723
- // TODO we need to (a) deal with escape sequences at least for unprintable characters and (b) safely handle LTR-RTL transition characters
733
+ // The grammar has these two escapes and no others: a printer writes
734
+ // every other character, a control character included, as itself.
724
735
  throw new ParseError("unexpected escape sequence in string", pos);
725
736
  }
726
737
  }
@@ -785,7 +796,7 @@ const parseBlob = (frozen) => (input, pos, _ctx) => {
785
796
  }
786
797
  return { value: bytes, position: pos };
787
798
  };
788
- const createRefParser = (value_type, frozen, typeCtx) => {
799
+ const createRefParser = (value_type, frozen, typeCtx, self) => {
789
800
  let valueParser;
790
801
  const ret = (input, pos, ctx) => {
791
802
  pos = consumeWhitespace(input, pos);
@@ -795,7 +806,7 @@ const createRefParser = (value_type, frozen, typeCtx) => {
795
806
  ctx = { refs: new Map(), currentPath: [] };
796
807
  }
797
808
  // Check for reference first
798
- const reference = parseReference(input, pos, ctx);
809
+ const reference = parseReference(input, pos, ctx, self);
799
810
  if (reference) {
800
811
  return reference;
801
812
  }
@@ -807,7 +818,7 @@ const createRefParser = (value_type, frozen, typeCtx) => {
807
818
  // Pre-register this ref if we have context
808
819
  if (ctx) {
809
820
  const pathStr = _pathToPunctuated(ctx.currentPath);
810
- ctx.refs.set(pathStr, r);
821
+ ctx.refs.set(pathStr, { value: r, type: self });
811
822
  }
812
823
  if (ctx)
813
824
  ctx.currentPath.push(`[]`);
@@ -835,7 +846,7 @@ const createRefParser = (value_type, frozen, typeCtx) => {
835
846
  valueParser = createParser(value_type, frozen, typeCtx);
836
847
  return ret;
837
848
  };
838
- const createArrayParser = (value_type, frozen, typeCtx) => {
849
+ const createArrayParser = (value_type, frozen, typeCtx, self) => {
839
850
  let valueParser;
840
851
  const ret = (input, pos, ctx) => {
841
852
  pos = consumeWhitespace(input, pos);
@@ -845,7 +856,7 @@ const createArrayParser = (value_type, frozen, typeCtx) => {
845
856
  ctx = { refs: new Map(), currentPath: [] };
846
857
  }
847
858
  // Check for reference first
848
- const ref = parseReference(input, pos, ctx);
859
+ const ref = parseReference(input, pos, ctx, self);
849
860
  if (ref) {
850
861
  return ref;
851
862
  }
@@ -857,7 +868,7 @@ const createArrayParser = (value_type, frozen, typeCtx) => {
857
868
  // Pre-register this array if we have context
858
869
  if (ctx) {
859
870
  const pathStr = _pathToPunctuated(ctx.currentPath);
860
- ctx.refs.set(pathStr, values);
871
+ ctx.refs.set(pathStr, { value: values, type: self });
861
872
  }
862
873
  // Handle empty array
863
874
  if (input[pos] === ']') {
@@ -902,7 +913,7 @@ const createArrayParser = (value_type, frozen, typeCtx) => {
902
913
  valueParser = createParser(value_type, frozen, typeCtx);
903
914
  return ret;
904
915
  };
905
- const createSetParser = (key_type, frozen, typeCtx) => {
916
+ const createSetParser = (key_type, frozen, typeCtx, self) => {
906
917
  const keyParser = createParser(key_type, frozen, typeCtx);
907
918
  const keyCompare = compareFor(key_type);
908
919
  return (input, pos, ctx) => {
@@ -913,7 +924,7 @@ const createSetParser = (key_type, frozen, typeCtx) => {
913
924
  ctx = { refs: new Map(), currentPath: [] };
914
925
  }
915
926
  // Check for reference first
916
- const ref = parseReference(input, pos, ctx);
927
+ const ref = parseReference(input, pos, ctx, self);
917
928
  if (ref) {
918
929
  return ref;
919
930
  }
@@ -925,7 +936,7 @@ const createSetParser = (key_type, frozen, typeCtx) => {
925
936
  // Pre-register this set if we have context
926
937
  if (ctx) {
927
938
  const pathStr = _pathToPunctuated(ctx.currentPath);
928
- ctx.refs.set(pathStr, values);
939
+ ctx.refs.set(pathStr, { value: values, type: self });
929
940
  }
930
941
  // Handle empty set
931
942
  if (input[pos] === '}') {
@@ -962,7 +973,7 @@ const createSetParser = (key_type, frozen, typeCtx) => {
962
973
  }
963
974
  };
964
975
  };
965
- const createDictParser = (key_type, value_type, frozen, typeCtx) => {
976
+ const createDictParser = (key_type, value_type, frozen, typeCtx, self) => {
966
977
  const keyParser = createParser(key_type, frozen, typeCtx);
967
978
  let valueParser;
968
979
  const keyCompare = compareFor(key_type);
@@ -975,7 +986,7 @@ const createDictParser = (key_type, value_type, frozen, typeCtx) => {
975
986
  ctx = { refs: new Map(), currentPath: [] };
976
987
  }
977
988
  // Check for reference first
978
- const ref = parseReference(input, pos, ctx);
989
+ const ref = parseReference(input, pos, ctx, self);
979
990
  if (ref) {
980
991
  return ref;
981
992
  }
@@ -987,7 +998,7 @@ const createDictParser = (key_type, value_type, frozen, typeCtx) => {
987
998
  // Pre-register this dict if we have context
988
999
  if (ctx) {
989
1000
  const pathStr = _pathToPunctuated(ctx.currentPath);
990
- ctx.refs.set(pathStr, values);
1001
+ ctx.refs.set(pathStr, { value: values, type: self });
991
1002
  }
992
1003
  // Handle empty dict
993
1004
  if (input[pos] === ':') {
@@ -1073,7 +1084,9 @@ const parseQuotedIdentifier = (input, pos, _ctx) => {
1073
1084
  pos += 1;
1074
1085
  let rangeStart = pos;
1075
1086
  let result = "";
1076
- // search through for escape characters, copy string out in ranges
1087
+ // search through for escape characters, copy string out in ranges; an
1088
+ // escaped character is taken whole, so one right before the closing
1089
+ // backtick never hides it
1077
1090
  while (pos < input.length) {
1078
1091
  const char = input[pos];
1079
1092
  if (char === '\\') {
@@ -1082,11 +1095,11 @@ const parseQuotedIdentifier = (input, pos, _ctx) => {
1082
1095
  if (pos < input.length) {
1083
1096
  const char2 = input[pos];
1084
1097
  if (char2 === '\\' || char2 === '`') {
1085
- rangeStart = pos;
1098
+ result = result + char2;
1086
1099
  pos += 1;
1100
+ rangeStart = pos;
1087
1101
  }
1088
1102
  else {
1089
- // TODO we need to (a) deal with escape sequences at least for unprintable characters and (b) safely handle LTR-RTL transition characters
1090
1103
  throw new ParseError("unexpected escape sequence in identifier", pos);
1091
1104
  }
1092
1105
  }
@@ -1098,7 +1111,9 @@ const parseQuotedIdentifier = (input, pos, _ctx) => {
1098
1111
  result = result + input.substring(rangeStart, pos);
1099
1112
  return { value: result, position: pos + 1 };
1100
1113
  }
1101
- pos += 1;
1114
+ else {
1115
+ pos += 1;
1116
+ }
1102
1117
  }
1103
1118
  throw new ParseError("unterminated identifier (missing closing `)", pos);
1104
1119
  };
@@ -1120,6 +1135,10 @@ const parseIdentifier = (input, pos, _ctx) => {
1120
1135
  };
1121
1136
  const createStructParser = (fields, frozen, typeCtx) => {
1122
1137
  const fieldNames = fields.map(f => f.name);
1138
+ // The path component a back-reference through each field names it by, as
1139
+ // the printer writes it: `.` and the field's name as the text spells it,
1140
+ // quoted when it is no plain identifier
1141
+ const fieldPaths = fieldNames.map(name => `.${printIdentifier(name)}`);
1123
1142
  const fieldParsers = [];
1124
1143
  const ret = (input, pos, ctx) => {
1125
1144
  pos = consumeWhitespace(input, pos);
@@ -1168,7 +1187,7 @@ const createStructParser = (fields, frozen, typeCtx) => {
1168
1187
  pos += 1;
1169
1188
  const parser = fieldParsers[fieldIndex];
1170
1189
  // Track path for struct fields
1171
- const fieldPath = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(fieldName) ? `.${fieldName}` : `[${JSON.stringify(fieldName)}]`;
1190
+ const fieldPath = fieldPaths[fieldIndex];
1172
1191
  if (ctx)
1173
1192
  ctx.currentPath.push(fieldPath);
1174
1193
  try {
@@ -1240,6 +1259,9 @@ const createStructParser = (fields, frozen, typeCtx) => {
1240
1259
  const createVariantParser = (cases, frozen, typeCtx) => {
1241
1260
  const caseParsers = {};
1242
1261
  const caseNames = cases.map(c => c.name);
1262
+ // The path component a back-reference through each case names it by, as the
1263
+ // printer writes it
1264
+ const casePaths = new Map(caseNames.map(name => [name, `.${printIdentifier(name)}`]));
1243
1265
  const ret = (input, pos, ctx) => {
1244
1266
  pos = consumeWhitespace(input, pos);
1245
1267
  // Create context if we don't have one (for top-level parsing with aliases)
@@ -1257,15 +1279,17 @@ const createVariantParser = (cases, frozen, typeCtx) => {
1257
1279
  throw new ParseError("whitespace not allowed between '.' and case identifier", pos);
1258
1280
  }
1259
1281
  const { value: caseName, position: casePos } = parseIdentifier(input, pos, ctx);
1260
- const valueParser = caseParsers[caseName];
1261
- if (valueParser === undefined) {
1282
+ // One of the type's own cases, never a name an object inherits (`toString`)
1283
+ const casePath = casePaths.get(caseName);
1284
+ const valueParser = casePath === undefined ? undefined : caseParsers[caseName];
1285
+ if (valueParser === undefined || casePath === undefined) {
1262
1286
  throw new ParseError(`unknown variant case .${printIdentifier(caseName)}, expected one of: ${caseNames.map(c => `.${printIdentifier(c)}`).join(', ')}`, pos);
1263
1287
  }
1264
1288
  // Create variant with undefined placeholder
1265
1289
  const v = variant(caseName, undefined);
1266
1290
  // Parse the value with case name in path
1267
1291
  if (ctx)
1268
- ctx.currentPath.push(`.${caseName}`);
1292
+ ctx.currentPath.push(casePath);
1269
1293
  try {
1270
1294
  const { value: caseValue, position: valuePos } = valueParser(input, casePos, ctx);
1271
1295
  // Mutate the variant's value field
@@ -1277,7 +1301,7 @@ const createVariantParser = (cases, frozen, typeCtx) => {
1277
1301
  }
1278
1302
  catch (e) {
1279
1303
  if (e instanceof ParseError) {
1280
- const newPath = `.${caseName}` + (e.path ? e.path : '');
1304
+ const newPath = casePath + (e.path ? e.path : '');
1281
1305
  throw new ParseError(e.message, e.position, newPath);
1282
1306
  }
1283
1307
  throw e;
@@ -1886,7 +1910,7 @@ function parseInferredStruct(input, startPos, frozen) {
1886
1910
  }
1887
1911
  pos = consumeWhitespace(input, pos + 1);
1888
1912
  // Parse field value and infer type
1889
- const fieldPath = /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(fieldName) ? `.${fieldName}` : `[${JSON.stringify(fieldName)}]`;
1913
+ const fieldPath = `.${printIdentifier(fieldName)}`;
1890
1914
  try {
1891
1915
  const fieldValue = parseInferredValue(input, pos, frozen);
1892
1916
  fields[fieldName] = fieldValue.type;
@@ -1953,7 +1977,7 @@ function parseInferredVariant(input, startPos, frozen) {
1953
1977
  }
1954
1978
  catch (e) {
1955
1979
  if (e instanceof ParseError) {
1956
- const newPath = `.${caseName}` + (e.path ? e.path : '');
1980
+ const newPath = `.${printIdentifier(caseName)}` + (e.path ? e.path : '');
1957
1981
  throw new ParseError(e.message, e.position, newPath);
1958
1982
  }
1959
1983
  throw e;