aontu 0.67.0 → 0.69.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 (86) hide show
  1. package/dist/alias.d.ts +12 -1
  2. package/dist/alias.js +176 -6
  3. package/dist/alias.js.map +1 -1
  4. package/dist/aliasname.d.ts +19 -0
  5. package/dist/aliasname.js +65 -0
  6. package/dist/aliasname.js.map +1 -0
  7. package/dist/aontu.d.ts +1 -1
  8. package/dist/aontu.js +21 -8
  9. package/dist/aontu.js.map +1 -1
  10. package/dist/aontumodel.js +1 -1
  11. package/dist/aontumodel.js.map +1 -1
  12. package/dist/cli.d.ts +4 -1
  13. package/dist/cli.js +29 -13
  14. package/dist/cli.js.map +1 -1
  15. package/dist/ctx.d.ts +1 -0
  16. package/dist/ctx.js +1 -0
  17. package/dist/ctx.js.map +1 -1
  18. package/dist/err.js +18 -1
  19. package/dist/err.js.map +1 -1
  20. package/dist/format.js +14 -0
  21. package/dist/format.js.map +1 -1
  22. package/dist/helpdoc.js +1 -1
  23. package/dist/helpdoc.js.map +1 -1
  24. package/dist/hints.js +171 -0
  25. package/dist/hints.js.map +1 -1
  26. package/dist/lang.js +325 -38
  27. package/dist/lang.js.map +1 -1
  28. package/dist/lsp.d.ts +10 -4
  29. package/dist/lsp.js +116 -7
  30. package/dist/lsp.js.map +1 -1
  31. package/dist/site.d.ts +9 -0
  32. package/dist/site.js +2 -1
  33. package/dist/site.js.map +1 -1
  34. package/dist/subsume.js +6 -1
  35. package/dist/subsume.js.map +1 -1
  36. package/dist/val/BagVal.d.ts +1 -0
  37. package/dist/val/BagVal.js +3 -0
  38. package/dist/val/BagVal.js.map +1 -1
  39. package/dist/val/EmitFuncVal.js +2 -1
  40. package/dist/val/EmitFuncVal.js.map +1 -1
  41. package/dist/val/FilterFuncVal.js +1 -1
  42. package/dist/val/FilterFuncVal.js.map +1 -1
  43. package/dist/val/FuncBaseVal.d.ts +2 -1
  44. package/dist/val/FuncBaseVal.js +25 -1
  45. package/dist/val/FuncBaseVal.js.map +1 -1
  46. package/dist/val/MapVal.js +10 -4
  47. package/dist/val/MapVal.js.map +1 -1
  48. package/dist/val/MatchFuncVal.js +2 -1
  49. package/dist/val/MatchFuncVal.js.map +1 -1
  50. package/dist/val/NilVal.js +3 -0
  51. package/dist/val/NilVal.js.map +1 -1
  52. package/dist/val/RecurseVal.d.ts +1 -0
  53. package/dist/val/RecurseVal.js +7 -3
  54. package/dist/val/RecurseVal.js.map +1 -1
  55. package/dist/val/RefVal.d.ts +1 -0
  56. package/dist/val/RefVal.js +47 -18
  57. package/dist/val/RefVal.js.map +1 -1
  58. package/dist/vet.d.ts +2 -1
  59. package/dist/vet.js +51 -6
  60. package/dist/vet.js.map +1 -1
  61. package/package.json +1 -1
  62. package/skill/error-codes.md +25 -1
  63. package/src/alias.ts +214 -6
  64. package/src/aliasname.ts +95 -0
  65. package/src/aontu.ts +23 -9
  66. package/src/aontumodel.ts +1 -1
  67. package/src/cli.ts +35 -13
  68. package/src/ctx.ts +4 -1
  69. package/src/err.ts +19 -1
  70. package/src/format.ts +14 -0
  71. package/src/helpdoc.ts +1 -1
  72. package/src/hints.ts +228 -0
  73. package/src/lang.ts +370 -33
  74. package/src/lsp.ts +136 -6
  75. package/src/site.ts +15 -1
  76. package/src/subsume.ts +9 -2
  77. package/src/val/BagVal.ts +4 -0
  78. package/src/val/EmitFuncVal.ts +3 -2
  79. package/src/val/FilterFuncVal.ts +2 -2
  80. package/src/val/FuncBaseVal.ts +30 -1
  81. package/src/val/MapVal.ts +10 -4
  82. package/src/val/MatchFuncVal.ts +3 -2
  83. package/src/val/NilVal.ts +3 -0
  84. package/src/val/RecurseVal.ts +8 -3
  85. package/src/val/RefVal.ts +49 -19
  86. package/src/vet.ts +55 -6
package/dist/lang.js CHANGED
@@ -37,6 +37,8 @@ const DisjunctVal_1 = require("./val/DisjunctVal");
37
37
  const IntegerVal_1 = require("./val/IntegerVal");
38
38
  const ListVal_1 = require("./val/ListVal");
39
39
  const MapVal_1 = require("./val/MapVal");
40
+ const Val_1 = require("./val/Val");
41
+ const ctx_1 = require("./ctx");
40
42
  const NilVal_1 = require("./val/NilVal");
41
43
  const NullVal_1 = require("./val/NullVal");
42
44
  const NumberVal_1 = require("./val/NumberVal");
@@ -93,10 +95,20 @@ const CC_0 = 48;
93
95
  const CC_d = 100;
94
96
  const CC_D = 68;
95
97
  const CC_PCT = 37;
96
- const ALIAS_RE = /^%[A-Za-z_][A-Za-z0-9_]*/;
98
+ const aliasname_1 = require("./aliasname");
97
99
  const CC_EQ = 61;
98
100
  const CC_SP = 32;
99
101
  const CC_TAB = 9;
102
+ const CC_OB = 123;
103
+ const WS_RE = /\s/;
104
+ let SCOPE_SEQ = 0;
105
+ const MERGE_KEY = aliasname_1.RESERVED_KEY_PREFIX + 'merge';
106
+ const ALIAS_MARK_KEY = aliasname_1.RESERVED_KEY_PREFIX + 'alias';
107
+ const OPTIONAL_MARK_KEY = aliasname_1.RESERVED_KEY_PREFIX + 'optional';
108
+ // `{ %a } = @"f.aon"` is the pair `<head>: <include>`, so the head is
109
+ // one token and the grammar needs nothing new.
110
+ const IMPORT_HEAD_RE = new RegExp('^(' + aliasname_1.ALIAS_SET + ')[ \\t]*=(?!=)');
111
+ const EXPORT_RE = /^export[ \t]*\([ \t]*([^()\s][^()]*?)[ \t]*\)/;
100
112
  const CC_9 = 57;
101
113
  const CC_A = 65;
102
114
  const CC_Z = 90;
@@ -175,7 +187,7 @@ let AontuJsonic = function AontuLang(jsonic) {
175
187
  const segs = Array.isArray(t.peg) ? t.peg :
176
188
  ('string' === typeof t.peg ? [t.peg] : []);
177
189
  for (const seg of segs) {
178
- if ('string' === typeof seg && ALIAS_RE.test(seg)) {
190
+ if ('string' === typeof seg && aliasname_1.ALIAS_RE.test(seg)) {
179
191
  return addsite(new NilVal_1.NilVal({ why: 'alias_in_path' }), r, ctx);
180
192
  }
181
193
  }
@@ -209,13 +221,67 @@ let AontuJsonic = function AontuLang(jsonic) {
209
221
  },
210
222
  },
211
223
  });
224
+ // THE FIXED MATCHER'S CHECK RUNS AT EVERY POSITION: `{` opens a fixed
225
+ // token the text check never sees.
226
+ jsonic.options({
227
+ fixed: {
228
+ check: (lex) => {
229
+ const pnt = lex.pnt;
230
+ const ex = lex.aontu_export;
231
+ // The `(` is the pair's separator, the argument its value.
232
+ if (null != ex && pnt.sI === ex.cl) {
233
+ const cltkn = lex.token('#CL', undefined, '(', pnt);
234
+ pnt.cI += ex.arg - pnt.sI;
235
+ pnt.sI = ex.arg;
236
+ return { done: true, token: cltkn };
237
+ }
238
+ if (null != ex && pnt.sI === ex.arg) {
239
+ delete lex.aontu_export;
240
+ const atkn = lex.token('#TX', ex.src, ex.src, pnt);
241
+ pnt.cI += ex.end - pnt.sI;
242
+ pnt.sI = ex.end;
243
+ return { done: true, token: atkn };
244
+ }
245
+ // THE SHORTHAND IS READ OFF THE SOURCE: a name lexes as its pair.
246
+ const sh = lex.aontu_shorthand;
247
+ if (null != sh && pnt.sI === sh.at) {
248
+ if (1 === sh.stage) {
249
+ sh.stage = 2;
250
+ return { done: true, token: lex.token('#CL', undefined, ':', pnt) };
251
+ }
252
+ delete lex.aontu_shorthand;
253
+ return {
254
+ done: true, token: lex.token('#VL', (r, ctx) => addsite(new RefVal_1.RefVal({ peg: [(0, aliasname_1.aliasScopedKey)(sh.name, srcUrl(ctx))], absolute: true }), r, ctx), sh.name, pnt)
255
+ };
256
+ }
257
+ if (CC_OB === lex.src.charCodeAt(pnt.sI)) {
258
+ const sres = aliasname_1.ALIAS_SHORTHAND_RE.exec(lex.refwd());
259
+ if (null != sres) {
260
+ lex.aontu_shorthand_end = pnt.sI + sres[0].length;
261
+ }
262
+ const hres = IMPORT_HEAD_RE.exec(lex.refwd());
263
+ if (null != hres) {
264
+ const hsrc = hres[1];
265
+ lex.aontu_eq_at = pnt.sI + hres[0].length - 1;
266
+ const htkn = lex.token('#TX', hsrc, hsrc, pnt, {
267
+ aontu_import: (0, aliasname_1.aliasSetItems)(hsrc),
268
+ });
269
+ pnt.sI += hsrc.length;
270
+ pnt.cI += hsrc.length;
271
+ return { done: true, token: htkn };
272
+ }
273
+ }
274
+ return undefined;
275
+ },
276
+ },
277
+ });
212
278
  jsonic.options({
213
279
  text: {
214
280
  check: (lex) => {
215
281
  const pnt = lex.pnt;
216
282
  const src = lex.src;
217
283
  const ares = CC_PCT === src.charCodeAt(pnt.sI) ?
218
- ALIAS_RE.exec(lex.refwd()) : null;
284
+ aliasname_1.ALIAS_RE.exec(lex.refwd()) : null;
219
285
  if (null != ares) {
220
286
  const asrc = ares[0];
221
287
  let j = pnt.sI + asrc.length;
@@ -226,11 +292,45 @@ let AontuJsonic = function AontuLang(jsonic) {
226
292
  if (CC_EQ === src.charCodeAt(j) && CC_EQ !== src.charCodeAt(j + 1)) {
227
293
  lex.aontu_eq_at = j;
228
294
  }
229
- const atkn = lex.token('#VL', (r, ctx) => addsite(new RefVal_1.RefVal({ peg: [asrc], absolute: true }), r, ctx), asrc, pnt);
295
+ // The name is the KEY; separator and reference follow it.
296
+ const shend = lex.aontu_shorthand_end;
297
+ if (null != shend && pnt.sI < shend) {
298
+ let k = pnt.sI + asrc.length;
299
+ while (k < src.length && WS_RE.test(src[k])) {
300
+ k++;
301
+ }
302
+ const ktkn = lex.token('#TX', asrc.substring(1), asrc, pnt);
303
+ pnt.cI += k - pnt.sI;
304
+ pnt.sI = k;
305
+ lex.aontu_shorthand = { at: k, name: asrc, stage: 1 };
306
+ if (shend - 1 <= k) {
307
+ delete lex.aontu_shorthand_end;
308
+ }
309
+ return { done: true, token: ktkn };
310
+ }
311
+ const atkn = lex.token('#VL', (r, ctx) => addsite(new RefVal_1.RefVal({ peg: [(0, aliasname_1.aliasScopedKey)(asrc, srcUrl(ctx))], absolute: true }), r, ctx), asrc, pnt);
230
312
  pnt.sI += asrc.length;
231
313
  pnt.cI += asrc.length;
232
314
  return { done: true, token: atkn };
233
315
  }
316
+ // `export(...)`: the word is the key, spelled unwritably.
317
+ if (CC_e === src.charCodeAt(pnt.sI)) {
318
+ const eres = EXPORT_RE.exec(lex.refwd());
319
+ if (null != eres) {
320
+ const open = eres[0].indexOf('(');
321
+ const arg = eres[0].indexOf(eres[1], open);
322
+ lex.aontu_export = {
323
+ cl: pnt.sI + open,
324
+ arg: pnt.sI + arg,
325
+ end: pnt.sI + eres[0].length,
326
+ src: eres[1],
327
+ };
328
+ const etkn = lex.token('#TX', (0, aliasname_1.exportHoldKey)(), aliasname_1.EXPORT_DECL_NAME, pnt, { aontu_export: true, aontu_export_items: (0, aliasname_1.aliasSetItems)(eres[1]) });
329
+ pnt.sI += aliasname_1.EXPORT_DECL_NAME.length;
330
+ pnt.cI += aliasname_1.EXPORT_DECL_NAME.length;
331
+ return { done: true, token: etkn };
332
+ }
333
+ }
234
334
  if (CC_EQ === src.charCodeAt(pnt.sI) && lex.aontu_eq_at === pnt.sI) {
235
335
  delete lex.aontu_eq_at;
236
336
  const eqtkn = lex.token('#CL', undefined, '=', pnt, { aontu_eq: true });
@@ -291,6 +391,24 @@ let AontuJsonic = function AontuLang(jsonic) {
291
391
  v.site.len = ts.len;
292
392
  return v;
293
393
  };
394
+ // `export` publishes a name; it renames none, and `{%}` is theirs.
395
+ const publishedNames = (items) => undefined === items || 0 === items.length ? undefined :
396
+ items.every((i) => i.local === i.remote) ?
397
+ items.map((i) => i.local) : undefined;
398
+ // A ROOT MAY BE WRAPPED: a file's names are its MAP'S, not open()'s.
399
+ const declaringMap = (val) => {
400
+ let cur = val;
401
+ while (true === cur?.isFunc && 1 === cur.peg?.length) {
402
+ cur = cur.peg[0];
403
+ }
404
+ return cur;
405
+ };
406
+ // The file a value was written in, and so any alias name's scope. A
407
+ // DOCUMENT IS ITS OWN SCOPE too, so a fileless parse is tagged.
408
+ const srcUrl = (ctx) => {
409
+ const url = ctx.meta.multisource && ctx.meta.multisource.path;
410
+ return url ? url : (ctx.meta.aontu_scope ??= '#' + (++SCOPE_SEQ));
411
+ };
294
412
  let addsite = (v, r, ctx) => {
295
413
  // The source text comes from the SAME token the row and column
296
414
  // come from. jsonic has carried it all along; not reading it is
@@ -301,14 +419,20 @@ let AontuJsonic = function AontuLang(jsonic) {
301
419
  v.path = r.k ? [...r.k.path] : [];
302
420
  return v;
303
421
  };
304
- const isAliasDecl = (ktkn, sep) => null != ktkn && VL === ktkn.tin && ALIAS_RE.test('' + ktkn.src) &&
422
+ const isAliasDecl = (ktkn, sep) => null != ktkn && VL === ktkn.tin && aliasname_1.ALIAS_RE.test('' + ktkn.src) &&
305
423
  true === sep?.use?.aontu_eq;
424
+ const keyName = (ktkn) => 'string' === typeof ktkn?.val ? ktkn.val : '' + ktkn?.src;
306
425
  const keyRefusalOf = (ktkn, sep) => {
426
+ // THE SENTINEL NAMESPACE IS THE ENGINE'S; it writes `export` here.
427
+ if (keyName(ktkn).startsWith(aliasname_1.RESERVED_KEY_PREFIX) &&
428
+ true !== ktkn.use?.aontu_export) {
429
+ return { why: 'reserved_key' };
430
+ }
307
431
  if (null == ktkn || VL !== ktkn.tin) {
308
432
  return undefined;
309
433
  }
310
434
  const kname = '' + ktkn.src;
311
- if (ALIAS_RE.test(kname)) {
435
+ if (aliasname_1.ALIAS_RE.test(kname)) {
312
436
  return isAliasDecl(ktkn, sep) ? undefined : { why: 'alias_colon' };
313
437
  }
314
438
  const bad = ktkn.use?.aontu_bad;
@@ -410,18 +534,18 @@ help isolate the syntax error.`,
410
534
  (prev[type_1.SPREAD] || { o: '&', v: [] });
411
535
  prev[type_1.SPREAD].v.push(lm.spread.cj);
412
536
  }
413
- prev.___optional = (prev.___optional || []);
537
+ prev[OPTIONAL_MARK_KEY] = (prev[OPTIONAL_MARK_KEY] || []);
414
538
  for (const k of lm.optionalKeys) {
415
- prev.___optional.push(k);
539
+ prev[OPTIONAL_MARK_KEY].push(k);
416
540
  }
417
- prev.___alias = (prev.___alias || []);
541
+ prev[ALIAS_MARK_KEY] = (prev[ALIAS_MARK_KEY] || []);
418
542
  for (const k of lm.aliasKeys) {
419
- prev.___alias.push(k);
543
+ prev[ALIAS_MARK_KEY].push(k);
420
544
  }
421
545
  return prev;
422
546
  }
423
- prev.___merge = (prev.___merge || []);
424
- prev.___merge.push(curr);
547
+ prev[MERGE_KEY] = (prev[MERGE_KEY] || []);
548
+ prev[MERGE_KEY].push(curr);
425
549
  return prev;
426
550
  }
427
551
  }
@@ -554,7 +678,7 @@ help isolate the syntax error.`,
554
678
  // as the alias reference rather than through the dot rule, and
555
679
  // is refused for the same reason.
556
680
  if (terms[0] instanceof RefVal_1.RefVal &&
557
- terms[0].peg.some((seg) => 'string' === typeof seg && ALIAS_RE.test(seg))) {
681
+ terms[0].peg.some((seg) => 'string' === typeof seg && aliasname_1.ALIAS_RE.test(seg))) {
558
682
  return addsite(new NilVal_1.NilVal({ why: 'alias_in_path' }), r, ctx);
559
683
  }
560
684
  if (terms[0] instanceof RefVal_1.RefVal) {
@@ -702,6 +826,16 @@ help isolate the syntax error.`,
702
826
  });
703
827
  jsonic.rule('val', (rs) => {
704
828
  rs
829
+ // Before the core implicit-map alts: a value prefix, not a key.
830
+ .open([
831
+ {
832
+ s: [VL, CL],
833
+ c: (r) => 0 !== r.d && isAliasDecl(r.o0, r.o1),
834
+ p: 'val',
835
+ a: (r) => { r.u.aontu_alias_val = '' + r.o0.src; },
836
+ g: 'aontu-alias-val',
837
+ },
838
+ ], { append: false })
705
839
  .open([
706
840
  {
707
841
  s: [CJ, CL], p: 'map', b: 2, n: { pk: 1 },
@@ -770,6 +904,11 @@ help isolate the syntax error.`,
770
904
  }
771
905
  // else { ERROR? }
772
906
  r.node = valnode;
907
+ const aname = r.u.aontu_alias_val;
908
+ if (null != aname && null != valnode) {
909
+ const hoist = (ctx.aontu_alias_hoist ||= []);
910
+ hoist.push({ name: (0, aliasname_1.aliasScopedKey)('' + aname, srcUrl(ctx)), val: valnode });
911
+ }
773
912
  return undefined;
774
913
  })
775
914
  .close([{ s: [CJ, CL], b: 2, g: 'spread,json,more' }]);
@@ -783,11 +922,24 @@ help isolate the syntax error.`,
783
922
  ])
784
923
  .bc((r, ctx) => {
785
924
  const optionalKeys = r.u.aontu_optional_keys ?? [];
786
- const aliasKeys = r.u.aontu_alias_keys ?? [];
925
+ const aliasDecls = r.u.aontu_alias_keys ?? [];
926
+ const aliasKeys = [];
927
+ const exportKeys = [];
787
928
  let mo = r.node;
929
+ // A REFUSED KEY TAKES NO KEY and is not OPTIONAL: its name is a
930
+ // mark's, and the elision below would put it back.
931
+ for (const kr of (r.u.aontu_key_refusals ?? [])) {
932
+ if ('reserved_key' === kr.why) {
933
+ delete mo[kr.key];
934
+ const oi = optionalKeys.indexOf(kr.key);
935
+ if (-1 !== oi) {
936
+ optionalKeys.splice(oi, 1);
937
+ }
938
+ }
939
+ }
788
940
  for (const k in mo) {
789
- if (null == mo[k] && '___merge' !== k &&
790
- '___optional' !== k && '___alias' !== k) {
941
+ if (null == mo[k] && MERGE_KEY !== k &&
942
+ OPTIONAL_MARK_KEY !== k && ALIAS_MARK_KEY !== k) {
791
943
  // Pathed at the KEY, not at the enclosing map. addsite takes
792
944
  // the rule's path, which here is the map's, so the error
793
945
  // would otherwise name the container and leave the reader to
@@ -811,44 +963,158 @@ help isolate the syntax error.`,
811
963
  r.node = addsite(new NilVal_1.NilVal({ why: 'elided_value' }), r, ctx);
812
964
  return undefined;
813
965
  }
814
- for (const { key, tkn, why, details } of (r.u.aontu_key_refusals ?? [])) {
966
+ const reservedRefusals = [];
967
+ for (const { key, tkn, why, details, url } of (r.u.aontu_key_refusals ?? [])) {
815
968
  const en = siteAt(addsite(new NilVal_1.NilVal({ why }), r, ctx), tokenSite(tkn));
816
969
  if (null != details) {
817
970
  en.details = details;
818
971
  }
819
972
  en.path = [...(r.k?.path ?? []), key];
820
- mo[key] = en;
973
+ // A COLON DECLARATION IS REFUSED AND STILL NAMES SOMETHING, so
974
+ // the refusal is held under the key that name would have had
975
+ // and a use of it reports why; not an alias key, so it
976
+ // generates unused.
977
+ if ('alias_colon' === why) {
978
+ delete mo[key];
979
+ mo[(0, aliasname_1.aliasScopedKey)(key, url)] = en;
980
+ }
981
+ // A RESERVED KEY WAITS: its name is where the marks are kept.
982
+ else if ('reserved_key' === why) {
983
+ reservedRefusals.push(en);
984
+ }
985
+ else {
986
+ mo[key] = en;
987
+ }
988
+ }
989
+ // A NAME BELONGS TO THE FILE THAT DECLARED IT. Renamed here and
990
+ // not at the key, so an elided value is still reported by the
991
+ // name the source spells; rebuilt, as key order is resolution.
992
+ const renamed = new Map();
993
+ for (const { name, key } of aliasDecls) {
994
+ renamed.set(name, key);
995
+ if (!aliasKeys.includes(key)) {
996
+ aliasKeys.push(key);
997
+ }
998
+ }
999
+ if (0 < renamed.size) {
1000
+ const entries = Object.entries(mo);
1001
+ for (const [k] of entries) {
1002
+ delete mo[k];
1003
+ }
1004
+ for (const [k, v] of entries) {
1005
+ const key = renamed.get(k);
1006
+ if (undefined === key) {
1007
+ mo[k] = v;
1008
+ }
1009
+ else {
1010
+ mo[key] = v;
1011
+ (0, Val_1.repathInstance)(v, [...(r.k?.path ?? []), key]);
1012
+ }
1013
+ }
1014
+ }
1015
+ // `export` PUBLISHES NAMES AND NOTHING ELSE; a bare name, a key
1016
+ // and the wildcard are refused, and the argument is erased.
1017
+ for (const { items, tkn } of (r.u.aontu_export_decls ?? [])) {
1018
+ delete mo[tkn.val];
1019
+ const names = publishedNames(items);
1020
+ if (undefined === names) {
1021
+ const en = siteAt(addsite(new NilVal_1.NilVal({ why: 'export_arg' }), r, ctx), tokenSite(tkn));
1022
+ en.path = [...(r.k?.path ?? []), aliasname_1.EXPORT_DECL_NAME];
1023
+ mo[aliasname_1.EXPORT_DECL_NAME] = en;
1024
+ }
1025
+ else {
1026
+ exportKeys.push(...names.map((n) => (0, aliasname_1.aliasScopedKey)(n, srcUrl(ctx))));
1027
+ }
1028
+ }
1029
+ // THE DESTRUCTURE IS ADDITIVE: the values land where the head
1030
+ // stands, each name binds in THIS file's scope, and a file
1031
+ // publishes only what IT declares. A DECLARATION IS THE
1032
+ // DOCUMENT'S, so the names go to the root and the values may
1033
+ // sit under a key.
1034
+ for (const im of (r.u.aontu_import_decls ?? [])) {
1035
+ const hoist = (ctx.aontu_alias_hoist ||= []);
1036
+ const iv = mo[im.key];
1037
+ delete mo[im.key];
1038
+ const dv = declaringMap(iv);
1039
+ const ex = dv.exportKeys ?? [];
1040
+ const ak = dv.aliasKeys ?? [];
1041
+ for (const k of ak) {
1042
+ hoist.push({ name: k, val: dv.peg[k] });
1043
+ delete dv.peg[k];
1044
+ }
1045
+ if (0 < ak.length + ex.length) {
1046
+ dv.aliasKeys = [];
1047
+ dv.exportKeys = [];
1048
+ }
1049
+ (0, Val_1.repathInstance)(iv, [...(r.k?.path ?? [])]);
1050
+ (mo[MERGE_KEY] = mo[MERGE_KEY] || []).push(iv);
1051
+ const binds = 0 === im.names.length ?
1052
+ ex.map((k) => ({ local: (0, aliasname_1.aliasBareName)(k), remote: (0, aliasname_1.aliasBareName)(k) })) : im.names;
1053
+ for (const { local, remote } of binds) {
1054
+ const from = ex.find((k) => (0, aliasname_1.aliasBareName)(k) === remote);
1055
+ let bind;
1056
+ if (undefined === from || !ak.includes(from)) {
1057
+ bind = siteAt(addsite(new NilVal_1.NilVal({ why: 'import_not_exported' }), r, ctx), tokenSite(im.tkn));
1058
+ bind.details = { name: remote };
1059
+ const en = siteAt(addsite(new NilVal_1.NilVal({ why: 'import_not_exported' }), r, ctx), tokenSite(im.tkn));
1060
+ en.details = { name: remote };
1061
+ mo[MERGE_KEY].push(en);
1062
+ }
1063
+ else {
1064
+ bind = addsite(new RefVal_1.RefVal({ peg: [from], absolute: true }), r, ctx);
1065
+ }
1066
+ hoist.push({ name: (0, aliasname_1.aliasScopedKey)(local, im.url), val: bind });
1067
+ }
821
1068
  }
822
1069
  // Marks carried over from a map include folded in the merge
823
1070
  // hook above, applied here where the MapVal is built.
824
- if (mo.___optional || mo.___alias) {
825
- for (const k of (mo.___optional || [])) {
1071
+ if (mo[OPTIONAL_MARK_KEY] || mo[ALIAS_MARK_KEY]) {
1072
+ for (const k of (mo[OPTIONAL_MARK_KEY] || [])) {
826
1073
  if (!optionalKeys.includes(k)) {
827
1074
  optionalKeys.push(k);
828
1075
  }
829
1076
  }
830
- for (const k of (mo.___alias || [])) {
1077
+ for (const k of (mo[ALIAS_MARK_KEY] || [])) {
831
1078
  if (!aliasKeys.includes(k)) {
832
1079
  aliasKeys.push(k);
833
1080
  }
834
1081
  }
835
- delete mo.___optional;
836
- delete mo.___alias;
1082
+ delete mo[OPTIONAL_MARK_KEY];
1083
+ delete mo[ALIAS_MARK_KEY];
1084
+ }
1085
+ // A value-prefix declaration lands here, at the document root
1086
+ // (ALIASES.0.md), as a copy pathed at its name.
1087
+ if (1 === r.d) {
1088
+ for (const { name, val } of (ctx.aontu_alias_hoist ?? [])) {
1089
+ const acx = new ctx_1.AontuContext({ path: [name] });
1090
+ const copy = val.clone(acx, { path: [name] });
1091
+ (0, Val_1.repathInstance)(copy, [name]);
1092
+ mo[name] = null == mo[name] ? copy :
1093
+ addsite(new ConjunctVal_1.ConjunctVal({ peg: [mo[name], copy] }), r, ctx);
1094
+ if (!aliasKeys.includes(name)) {
1095
+ aliasKeys.push(name);
1096
+ }
1097
+ }
837
1098
  }
838
1099
  // Handle defered conjuncts, e.g. `{x:1 @"foo"}`
839
- if (mo.___merge) {
1100
+ const deferred = mo[MERGE_KEY];
1101
+ delete mo[MERGE_KEY];
1102
+ const merge = 0 < reservedRefusals.length ?
1103
+ [...(deferred ?? []), ...reservedRefusals] : deferred;
1104
+ if (merge) {
840
1105
  let mop = { ...mo };
841
- delete mop.___merge;
842
1106
  let mopv = new MapVal_1.MapVal({ peg: mop });
843
1107
  mopv.optionalKeys = optionalKeys;
844
1108
  mopv.aliasKeys = aliasKeys;
1109
+ mopv.exportKeys = exportKeys;
845
1110
  r.node =
846
- addsite(new ConjunctVal_1.ConjunctVal({ peg: [mopv, ...mo.___merge] }), r, ctx);
1111
+ addsite(new ConjunctVal_1.ConjunctVal({ peg: [mopv, ...merge] }), r, ctx);
847
1112
  }
848
1113
  else {
849
1114
  r.node = addsite(new MapVal_1.MapVal({ peg: mo }), r, ctx);
850
1115
  r.node.optionalKeys = optionalKeys;
851
1116
  r.node.aliasKeys = aliasKeys;
1117
+ r.node.exportKeys = exportKeys;
852
1118
  }
853
1119
  return undefined;
854
1120
  })
@@ -868,7 +1134,7 @@ help isolate the syntax error.`,
868
1134
  ao[i] = en;
869
1135
  }
870
1136
  }
871
- // No ___merge arm here: the deferred map.merge that writes it
1137
+ // No merge-key arm here: the deferred map.merge that writes it
872
1138
  // only ever fires for a `pair` rule, whose parent is always a
873
1139
  // `map` rule with a plain-object node — never a list.
874
1140
  {
@@ -944,18 +1210,36 @@ help isolate the syntax error.`,
944
1210
  r.child.k.key = '&';
945
1211
  }
946
1212
  })
947
- .bc((rule) => {
1213
+ .bc((rule, ctx) => {
948
1214
  // TRAVERSE PARENTS TO GET PATH
949
- const ktkn = rule.o0;
1215
+ // AN OPTIONAL PAIR OPENS ON ITS `?`: the rule before read the key.
1216
+ const ktkn = true === rule.prev?.u?.aontu_optional ?
1217
+ rule.prev.o0 : rule.o0;
950
1218
  const holder = rule.parent;
951
1219
  const kr = keyRefusalOf(ktkn, rule.o1);
952
1220
  if (null != kr) {
953
1221
  holder.u.aontu_key_refusals = (holder.u.aontu_key_refusals || []);
954
- holder.u.aontu_key_refusals.push({ key: '' + ktkn.src, tkn: ktkn, ...kr });
1222
+ holder.u.aontu_key_refusals.push({ key: keyName(ktkn), tkn: ktkn, url: srcUrl(ctx), ...kr });
955
1223
  }
956
1224
  else if (isAliasDecl(ktkn, rule.o1)) {
957
1225
  holder.u.aontu_alias_keys = (holder.u.aontu_alias_keys || []);
958
- holder.u.aontu_alias_keys.push('' + ktkn.src);
1226
+ holder.u.aontu_alias_keys.push({
1227
+ name: '' + ktkn.src,
1228
+ key: (0, aliasname_1.aliasScopedKey)('' + ktkn.src, srcUrl(ctx)),
1229
+ });
1230
+ }
1231
+ else if (true === ktkn?.use?.aontu_export) {
1232
+ holder.u.aontu_export_decls = (holder.u.aontu_export_decls || []);
1233
+ holder.u.aontu_export_decls.push({ items: ktkn.use.aontu_export_items, tkn: ktkn });
1234
+ }
1235
+ else if (null != ktkn?.use?.aontu_import) {
1236
+ holder.u.aontu_import_decls = (holder.u.aontu_import_decls || []);
1237
+ holder.u.aontu_import_decls.push({
1238
+ key: '' + ktkn.src,
1239
+ names: ktkn.use.aontu_import,
1240
+ url: srcUrl(ctx),
1241
+ tkn: ktkn,
1242
+ });
959
1243
  }
960
1244
  if (rule.u.spread) {
961
1245
  rule.node[type_1.SPREAD] =
@@ -1054,21 +1338,24 @@ help isolate the syntax error.`,
1054
1338
  v.path = [...(rule.k?.path ?? []),
1055
1339
  '' + rule.node.length, key];
1056
1340
  }
1341
+ // An element is a value position, so the prefix form applies.
1342
+ if (null == kr && isAliasDecl(ktkn, rule.o1)) {
1343
+ ;
1344
+ (ctx.aontu_alias_hoist ||= [])
1345
+ .push({ name: (0, aliasname_1.aliasScopedKey)(key, srcUrl(ctx)), val: v });
1346
+ rule.node.push(v);
1347
+ return undefined;
1348
+ }
1057
1349
  const mv = addsite(new MapVal_1.MapVal({ peg: { [key]: v } }), rule, ctx);
1058
1350
  // The element's path is the list's plus its index, as any
1059
1351
  // element's is (and as the Go port paths it): the map rule's
1060
1352
  // "is this the top level" test reads the path, so an element
1061
1353
  // of a top-level list must not read as the root.
1062
1354
  mv.path = [...(rule.k?.path ?? []), '' + rule.node.length];
1063
- if (true === rule.u.aontu_optional_elem) {
1355
+ // A REFUSED KEY IS NOT OPTIONAL: the refusal would be dropped.
1356
+ if (true === rule.u.aontu_optional_elem && null == kr) {
1064
1357
  mv.optionalKeys = [key];
1065
1358
  }
1066
- // ... and a declaration is a declaration IN the element, which
1067
- // is where MapVal.unify refuses it: a list element is not the
1068
- // top level.
1069
- if (isAliasDecl(ktkn, rule.o1)) {
1070
- mv.aliasKeys = [key];
1071
- }
1072
1359
  rule.node.push(mv);
1073
1360
  }
1074
1361
  return undefined;