aontu 0.68.0 → 0.70.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 (74) 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/ctx.d.ts +1 -0
  13. package/dist/ctx.js +1 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/err.js +18 -1
  16. package/dist/err.js.map +1 -1
  17. package/dist/format.js +14 -0
  18. package/dist/format.js.map +1 -1
  19. package/dist/hints.js +36 -7
  20. package/dist/hints.js.map +1 -1
  21. package/dist/lang.js +289 -35
  22. package/dist/lang.js.map +1 -1
  23. package/dist/lsp.d.ts +10 -4
  24. package/dist/lsp.js +116 -7
  25. package/dist/lsp.js.map +1 -1
  26. package/dist/site.d.ts +9 -0
  27. package/dist/site.js +2 -1
  28. package/dist/site.js.map +1 -1
  29. package/dist/subsume.js +6 -1
  30. package/dist/subsume.js.map +1 -1
  31. package/dist/val/BagVal.d.ts +1 -0
  32. package/dist/val/BagVal.js +3 -0
  33. package/dist/val/BagVal.js.map +1 -1
  34. package/dist/val/EmitFuncVal.js +2 -1
  35. package/dist/val/EmitFuncVal.js.map +1 -1
  36. package/dist/val/FilterFuncVal.js +1 -1
  37. package/dist/val/FilterFuncVal.js.map +1 -1
  38. package/dist/val/FuncBaseVal.d.ts +2 -1
  39. package/dist/val/FuncBaseVal.js +25 -1
  40. package/dist/val/FuncBaseVal.js.map +1 -1
  41. package/dist/val/MapVal.js +10 -4
  42. package/dist/val/MapVal.js.map +1 -1
  43. package/dist/val/MatchFuncVal.js +2 -1
  44. package/dist/val/MatchFuncVal.js.map +1 -1
  45. package/dist/val/NilVal.js +3 -0
  46. package/dist/val/NilVal.js.map +1 -1
  47. package/dist/val/RecurseVal.d.ts +1 -0
  48. package/dist/val/RecurseVal.js +7 -3
  49. package/dist/val/RecurseVal.js.map +1 -1
  50. package/dist/val/RefVal.d.ts +1 -0
  51. package/dist/val/RefVal.js +29 -12
  52. package/dist/val/RefVal.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/alias.ts +214 -6
  55. package/src/aliasname.ts +95 -0
  56. package/src/aontu.ts +23 -9
  57. package/src/aontumodel.ts +1 -1
  58. package/src/ctx.ts +4 -1
  59. package/src/err.ts +19 -1
  60. package/src/format.ts +14 -0
  61. package/src/hints.ts +40 -7
  62. package/src/lang.ts +334 -30
  63. package/src/lsp.ts +136 -6
  64. package/src/site.ts +15 -1
  65. package/src/subsume.ts +9 -2
  66. package/src/val/BagVal.ts +4 -0
  67. package/src/val/EmitFuncVal.ts +3 -2
  68. package/src/val/FilterFuncVal.ts +2 -2
  69. package/src/val/FuncBaseVal.ts +30 -1
  70. package/src/val/MapVal.ts +10 -4
  71. package/src/val/MatchFuncVal.ts +3 -2
  72. package/src/val/NilVal.ts +3 -0
  73. package/src/val/RecurseVal.ts +8 -3
  74. package/src/val/RefVal.ts +31 -12
package/dist/lang.js CHANGED
@@ -95,11 +95,20 @@ const CC_0 = 48;
95
95
  const CC_d = 100;
96
96
  const CC_D = 68;
97
97
  const CC_PCT = 37;
98
- // Hyphen separates segments; not leading or trailing, as `-` prefixes negation.
99
- const ALIAS_RE = /^%[A-Za-z_][A-Za-z0-9_]*(?:-[A-Za-z0-9_]+)*/;
98
+ const aliasname_1 = require("./aliasname");
100
99
  const CC_EQ = 61;
101
100
  const CC_SP = 32;
102
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]*\)/;
103
112
  const CC_9 = 57;
104
113
  const CC_A = 65;
105
114
  const CC_Z = 90;
@@ -178,7 +187,7 @@ let AontuJsonic = function AontuLang(jsonic) {
178
187
  const segs = Array.isArray(t.peg) ? t.peg :
179
188
  ('string' === typeof t.peg ? [t.peg] : []);
180
189
  for (const seg of segs) {
181
- if ('string' === typeof seg && ALIAS_RE.test(seg)) {
190
+ if ('string' === typeof seg && aliasname_1.ALIAS_RE.test(seg)) {
182
191
  return addsite(new NilVal_1.NilVal({ why: 'alias_in_path' }), r, ctx);
183
192
  }
184
193
  }
@@ -212,13 +221,67 @@ let AontuJsonic = function AontuLang(jsonic) {
212
221
  },
213
222
  },
214
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
+ });
215
278
  jsonic.options({
216
279
  text: {
217
280
  check: (lex) => {
218
281
  const pnt = lex.pnt;
219
282
  const src = lex.src;
220
283
  const ares = CC_PCT === src.charCodeAt(pnt.sI) ?
221
- ALIAS_RE.exec(lex.refwd()) : null;
284
+ aliasname_1.ALIAS_RE.exec(lex.refwd()) : null;
222
285
  if (null != ares) {
223
286
  const asrc = ares[0];
224
287
  let j = pnt.sI + asrc.length;
@@ -229,11 +292,45 @@ let AontuJsonic = function AontuLang(jsonic) {
229
292
  if (CC_EQ === src.charCodeAt(j) && CC_EQ !== src.charCodeAt(j + 1)) {
230
293
  lex.aontu_eq_at = j;
231
294
  }
232
- 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);
233
312
  pnt.sI += asrc.length;
234
313
  pnt.cI += asrc.length;
235
314
  return { done: true, token: atkn };
236
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
+ }
237
334
  if (CC_EQ === src.charCodeAt(pnt.sI) && lex.aontu_eq_at === pnt.sI) {
238
335
  delete lex.aontu_eq_at;
239
336
  const eqtkn = lex.token('#CL', undefined, '=', pnt, { aontu_eq: true });
@@ -294,6 +391,24 @@ let AontuJsonic = function AontuLang(jsonic) {
294
391
  v.site.len = ts.len;
295
392
  return v;
296
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
+ };
297
412
  let addsite = (v, r, ctx) => {
298
413
  // The source text comes from the SAME token the row and column
299
414
  // come from. jsonic has carried it all along; not reading it is
@@ -304,14 +419,20 @@ let AontuJsonic = function AontuLang(jsonic) {
304
419
  v.path = r.k ? [...r.k.path] : [];
305
420
  return v;
306
421
  };
307
- 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) &&
308
423
  true === sep?.use?.aontu_eq;
424
+ const keyName = (ktkn) => 'string' === typeof ktkn?.val ? ktkn.val : '' + ktkn?.src;
309
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
+ }
310
431
  if (null == ktkn || VL !== ktkn.tin) {
311
432
  return undefined;
312
433
  }
313
434
  const kname = '' + ktkn.src;
314
- if (ALIAS_RE.test(kname)) {
435
+ if (aliasname_1.ALIAS_RE.test(kname)) {
315
436
  return isAliasDecl(ktkn, sep) ? undefined : { why: 'alias_colon' };
316
437
  }
317
438
  const bad = ktkn.use?.aontu_bad;
@@ -413,18 +534,18 @@ help isolate the syntax error.`,
413
534
  (prev[type_1.SPREAD] || { o: '&', v: [] });
414
535
  prev[type_1.SPREAD].v.push(lm.spread.cj);
415
536
  }
416
- prev.___optional = (prev.___optional || []);
537
+ prev[OPTIONAL_MARK_KEY] = (prev[OPTIONAL_MARK_KEY] || []);
417
538
  for (const k of lm.optionalKeys) {
418
- prev.___optional.push(k);
539
+ prev[OPTIONAL_MARK_KEY].push(k);
419
540
  }
420
- prev.___alias = (prev.___alias || []);
541
+ prev[ALIAS_MARK_KEY] = (prev[ALIAS_MARK_KEY] || []);
421
542
  for (const k of lm.aliasKeys) {
422
- prev.___alias.push(k);
543
+ prev[ALIAS_MARK_KEY].push(k);
423
544
  }
424
545
  return prev;
425
546
  }
426
- prev.___merge = (prev.___merge || []);
427
- prev.___merge.push(curr);
547
+ prev[MERGE_KEY] = (prev[MERGE_KEY] || []);
548
+ prev[MERGE_KEY].push(curr);
428
549
  return prev;
429
550
  }
430
551
  }
@@ -557,7 +678,7 @@ help isolate the syntax error.`,
557
678
  // as the alias reference rather than through the dot rule, and
558
679
  // is refused for the same reason.
559
680
  if (terms[0] instanceof RefVal_1.RefVal &&
560
- 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))) {
561
682
  return addsite(new NilVal_1.NilVal({ why: 'alias_in_path' }), r, ctx);
562
683
  }
563
684
  if (terms[0] instanceof RefVal_1.RefVal) {
@@ -786,7 +907,7 @@ help isolate the syntax error.`,
786
907
  const aname = r.u.aontu_alias_val;
787
908
  if (null != aname && null != valnode) {
788
909
  const hoist = (ctx.aontu_alias_hoist ||= []);
789
- hoist.push({ name: '' + aname, val: valnode });
910
+ hoist.push({ name: (0, aliasname_1.aliasScopedKey)('' + aname, srcUrl(ctx)), val: valnode });
790
911
  }
791
912
  return undefined;
792
913
  })
@@ -801,11 +922,24 @@ help isolate the syntax error.`,
801
922
  ])
802
923
  .bc((r, ctx) => {
803
924
  const optionalKeys = r.u.aontu_optional_keys ?? [];
804
- const aliasKeys = r.u.aontu_alias_keys ?? [];
925
+ const aliasDecls = r.u.aontu_alias_keys ?? [];
926
+ const aliasKeys = [];
927
+ const exportKeys = [];
805
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
+ }
806
940
  for (const k in mo) {
807
- if (null == mo[k] && '___merge' !== k &&
808
- '___optional' !== k && '___alias' !== k) {
941
+ if (null == mo[k] && MERGE_KEY !== k &&
942
+ OPTIONAL_MARK_KEY !== k && ALIAS_MARK_KEY !== k) {
809
943
  // Pathed at the KEY, not at the enclosing map. addsite takes
810
944
  // the rule's path, which here is the map's, so the error
811
945
  // would otherwise name the container and leave the reader to
@@ -829,29 +963,124 @@ help isolate the syntax error.`,
829
963
  r.node = addsite(new NilVal_1.NilVal({ why: 'elided_value' }), r, ctx);
830
964
  return undefined;
831
965
  }
832
- 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 ?? [])) {
833
968
  const en = siteAt(addsite(new NilVal_1.NilVal({ why }), r, ctx), tokenSite(tkn));
834
969
  if (null != details) {
835
970
  en.details = details;
836
971
  }
837
972
  en.path = [...(r.k?.path ?? []), key];
838
- 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
+ }
839
1068
  }
840
1069
  // Marks carried over from a map include folded in the merge
841
1070
  // hook above, applied here where the MapVal is built.
842
- if (mo.___optional || mo.___alias) {
843
- 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] || [])) {
844
1073
  if (!optionalKeys.includes(k)) {
845
1074
  optionalKeys.push(k);
846
1075
  }
847
1076
  }
848
- for (const k of (mo.___alias || [])) {
1077
+ for (const k of (mo[ALIAS_MARK_KEY] || [])) {
849
1078
  if (!aliasKeys.includes(k)) {
850
1079
  aliasKeys.push(k);
851
1080
  }
852
1081
  }
853
- delete mo.___optional;
854
- delete mo.___alias;
1082
+ delete mo[OPTIONAL_MARK_KEY];
1083
+ delete mo[ALIAS_MARK_KEY];
855
1084
  }
856
1085
  // A value-prefix declaration lands here, at the document root
857
1086
  // (ALIASES.0.md), as a copy pathed at its name.
@@ -868,19 +1097,24 @@ help isolate the syntax error.`,
868
1097
  }
869
1098
  }
870
1099
  // Handle defered conjuncts, e.g. `{x:1 @"foo"}`
871
- 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) {
872
1105
  let mop = { ...mo };
873
- delete mop.___merge;
874
1106
  let mopv = new MapVal_1.MapVal({ peg: mop });
875
1107
  mopv.optionalKeys = optionalKeys;
876
1108
  mopv.aliasKeys = aliasKeys;
1109
+ mopv.exportKeys = exportKeys;
877
1110
  r.node =
878
- addsite(new ConjunctVal_1.ConjunctVal({ peg: [mopv, ...mo.___merge] }), r, ctx);
1111
+ addsite(new ConjunctVal_1.ConjunctVal({ peg: [mopv, ...merge] }), r, ctx);
879
1112
  }
880
1113
  else {
881
1114
  r.node = addsite(new MapVal_1.MapVal({ peg: mo }), r, ctx);
882
1115
  r.node.optionalKeys = optionalKeys;
883
1116
  r.node.aliasKeys = aliasKeys;
1117
+ r.node.exportKeys = exportKeys;
884
1118
  }
885
1119
  return undefined;
886
1120
  })
@@ -900,7 +1134,7 @@ help isolate the syntax error.`,
900
1134
  ao[i] = en;
901
1135
  }
902
1136
  }
903
- // No ___merge arm here: the deferred map.merge that writes it
1137
+ // No merge-key arm here: the deferred map.merge that writes it
904
1138
  // only ever fires for a `pair` rule, whose parent is always a
905
1139
  // `map` rule with a plain-object node — never a list.
906
1140
  {
@@ -976,18 +1210,36 @@ help isolate the syntax error.`,
976
1210
  r.child.k.key = '&';
977
1211
  }
978
1212
  })
979
- .bc((rule) => {
1213
+ .bc((rule, ctx) => {
980
1214
  // TRAVERSE PARENTS TO GET PATH
981
- 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;
982
1218
  const holder = rule.parent;
983
1219
  const kr = keyRefusalOf(ktkn, rule.o1);
984
1220
  if (null != kr) {
985
1221
  holder.u.aontu_key_refusals = (holder.u.aontu_key_refusals || []);
986
- 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 });
987
1223
  }
988
1224
  else if (isAliasDecl(ktkn, rule.o1)) {
989
1225
  holder.u.aontu_alias_keys = (holder.u.aontu_alias_keys || []);
990
- 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
+ });
991
1243
  }
992
1244
  if (rule.u.spread) {
993
1245
  rule.node[type_1.SPREAD] =
@@ -1089,7 +1341,8 @@ help isolate the syntax error.`,
1089
1341
  // An element is a value position, so the prefix form applies.
1090
1342
  if (null == kr && isAliasDecl(ktkn, rule.o1)) {
1091
1343
  ;
1092
- (ctx.aontu_alias_hoist ||= []).push({ name: key, val: v });
1344
+ (ctx.aontu_alias_hoist ||= [])
1345
+ .push({ name: (0, aliasname_1.aliasScopedKey)(key, srcUrl(ctx)), val: v });
1093
1346
  rule.node.push(v);
1094
1347
  return undefined;
1095
1348
  }
@@ -1099,7 +1352,8 @@ help isolate the syntax error.`,
1099
1352
  // "is this the top level" test reads the path, so an element
1100
1353
  // of a top-level list must not read as the root.
1101
1354
  mv.path = [...(rule.k?.path ?? []), '' + rule.node.length];
1102
- 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) {
1103
1357
  mv.optionalKeys = [key];
1104
1358
  }
1105
1359
  rule.node.push(mv);