aontu 0.68.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.
- package/dist/alias.d.ts +12 -1
- package/dist/alias.js +176 -6
- package/dist/alias.js.map +1 -1
- package/dist/aliasname.d.ts +19 -0
- package/dist/aliasname.js +65 -0
- package/dist/aliasname.js.map +1 -0
- package/dist/aontu.d.ts +1 -1
- package/dist/aontu.js +21 -8
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.js +1 -1
- package/dist/aontumodel.js.map +1 -1
- package/dist/ctx.d.ts +1 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +18 -1
- package/dist/err.js.map +1 -1
- package/dist/format.js +14 -0
- package/dist/format.js.map +1 -1
- package/dist/hints.js +36 -7
- package/dist/hints.js.map +1 -1
- package/dist/lang.js +289 -35
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +10 -4
- package/dist/lsp.js +116 -7
- package/dist/lsp.js.map +1 -1
- package/dist/site.d.ts +9 -0
- package/dist/site.js +2 -1
- package/dist/site.js.map +1 -1
- package/dist/subsume.js +6 -1
- package/dist/subsume.js.map +1 -1
- package/dist/val/BagVal.d.ts +1 -0
- package/dist/val/BagVal.js +3 -0
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/EmitFuncVal.js +2 -1
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +1 -1
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +2 -1
- package/dist/val/FuncBaseVal.js +25 -1
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/MapVal.js +10 -4
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +2 -1
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +3 -0
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/RecurseVal.d.ts +1 -0
- package/dist/val/RecurseVal.js +7 -3
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +1 -0
- package/dist/val/RefVal.js +29 -12
- package/dist/val/RefVal.js.map +1 -1
- package/package.json +1 -1
- package/src/alias.ts +214 -6
- package/src/aliasname.ts +95 -0
- package/src/aontu.ts +23 -9
- package/src/aontumodel.ts +1 -1
- package/src/ctx.ts +4 -1
- package/src/err.ts +19 -1
- package/src/format.ts +14 -0
- package/src/hints.ts +40 -7
- package/src/lang.ts +334 -30
- package/src/lsp.ts +136 -6
- package/src/site.ts +15 -1
- package/src/subsume.ts +9 -2
- package/src/val/BagVal.ts +4 -0
- package/src/val/EmitFuncVal.ts +3 -2
- package/src/val/FilterFuncVal.ts +2 -2
- package/src/val/FuncBaseVal.ts +30 -1
- package/src/val/MapVal.ts +10 -4
- package/src/val/MatchFuncVal.ts +3 -2
- package/src/val/NilVal.ts +3 -0
- package/src/val/RecurseVal.ts +8 -3
- package/src/val/RefVal.ts +31 -12
package/src/lang.ts
CHANGED
|
@@ -190,12 +190,34 @@ const CC_d = 100
|
|
|
190
190
|
const CC_D = 68
|
|
191
191
|
|
|
192
192
|
const CC_PCT = 37
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
import type { AliasBind } from './aliasname'
|
|
194
|
+
import {
|
|
195
|
+
ALIAS_RE,
|
|
196
|
+
ALIAS_SET,
|
|
197
|
+
ALIAS_SHORTHAND_RE,
|
|
198
|
+
EXPORT_DECL_NAME,
|
|
199
|
+
RESERVED_KEY_PREFIX,
|
|
200
|
+
exportHoldKey,
|
|
201
|
+
aliasBareName,
|
|
202
|
+
aliasScopedKey,
|
|
203
|
+
aliasSetItems,
|
|
204
|
+
} from './aliasname'
|
|
195
205
|
|
|
196
206
|
const CC_EQ = 61
|
|
197
207
|
const CC_SP = 32
|
|
198
208
|
const CC_TAB = 9
|
|
209
|
+
const CC_OB = 123
|
|
210
|
+
const WS_RE = /\s/
|
|
211
|
+
|
|
212
|
+
let SCOPE_SEQ = 0
|
|
213
|
+
const MERGE_KEY = RESERVED_KEY_PREFIX + 'merge'
|
|
214
|
+
const ALIAS_MARK_KEY = RESERVED_KEY_PREFIX + 'alias'
|
|
215
|
+
const OPTIONAL_MARK_KEY = RESERVED_KEY_PREFIX + 'optional'
|
|
216
|
+
|
|
217
|
+
// `{ %a } = @"f.aon"` is the pair `<head>: <include>`, so the head is
|
|
218
|
+
// one token and the grammar needs nothing new.
|
|
219
|
+
const IMPORT_HEAD_RE = new RegExp('^(' + ALIAS_SET + ')[ \\t]*=(?!=)')
|
|
220
|
+
const EXPORT_RE = /^export[ \t]*\([ \t]*([^()\s][^()]*?)[ \t]*\)/
|
|
199
221
|
|
|
200
222
|
const CC_9 = 57
|
|
201
223
|
const CC_A = 65
|
|
@@ -329,6 +351,72 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
329
351
|
},
|
|
330
352
|
})
|
|
331
353
|
|
|
354
|
+
// THE FIXED MATCHER'S CHECK RUNS AT EVERY POSITION: `{` opens a fixed
|
|
355
|
+
// token the text check never sees.
|
|
356
|
+
jsonic.options({
|
|
357
|
+
fixed: {
|
|
358
|
+
check: (lex: any) => {
|
|
359
|
+
const pnt = lex.pnt
|
|
360
|
+
const ex = lex.aontu_export
|
|
361
|
+
|
|
362
|
+
// The `(` is the pair's separator, the argument its value.
|
|
363
|
+
if (null != ex && pnt.sI === ex.cl) {
|
|
364
|
+
const cltkn = lex.token('#CL', undefined, '(', pnt)
|
|
365
|
+
pnt.cI += ex.arg - pnt.sI
|
|
366
|
+
pnt.sI = ex.arg
|
|
367
|
+
return { done: true, token: cltkn }
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (null != ex && pnt.sI === ex.arg) {
|
|
371
|
+
delete lex.aontu_export
|
|
372
|
+
const atkn = lex.token('#TX', ex.src, ex.src, pnt)
|
|
373
|
+
pnt.cI += ex.end - pnt.sI
|
|
374
|
+
pnt.sI = ex.end
|
|
375
|
+
return { done: true, token: atkn }
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// THE SHORTHAND IS READ OFF THE SOURCE: a name lexes as its pair.
|
|
379
|
+
const sh = lex.aontu_shorthand
|
|
380
|
+
if (null != sh && pnt.sI === sh.at) {
|
|
381
|
+
if (1 === sh.stage) {
|
|
382
|
+
sh.stage = 2
|
|
383
|
+
return { done: true, token: lex.token('#CL', undefined, ':', pnt) }
|
|
384
|
+
}
|
|
385
|
+
delete lex.aontu_shorthand
|
|
386
|
+
return {
|
|
387
|
+
done: true, token: lex.token(
|
|
388
|
+
'#VL',
|
|
389
|
+
(r: Rule, ctx: JsonicContext) =>
|
|
390
|
+
addsite(new RefVal(
|
|
391
|
+
{ peg: [aliasScopedKey(sh.name, srcUrl(ctx))], absolute: true }),
|
|
392
|
+
r, ctx),
|
|
393
|
+
sh.name, pnt)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (CC_OB === lex.src.charCodeAt(pnt.sI)) {
|
|
398
|
+
const sres = ALIAS_SHORTHAND_RE.exec(lex.refwd())
|
|
399
|
+
if (null != sres) {
|
|
400
|
+
lex.aontu_shorthand_end = pnt.sI + sres[0].length
|
|
401
|
+
}
|
|
402
|
+
const hres = IMPORT_HEAD_RE.exec(lex.refwd())
|
|
403
|
+
if (null != hres) {
|
|
404
|
+
const hsrc = hres[1]
|
|
405
|
+
lex.aontu_eq_at = pnt.sI + hres[0].length - 1
|
|
406
|
+
const htkn = lex.token('#TX', hsrc, hsrc, pnt, {
|
|
407
|
+
aontu_import: aliasSetItems(hsrc) as AliasBind[],
|
|
408
|
+
})
|
|
409
|
+
pnt.sI += hsrc.length
|
|
410
|
+
pnt.cI += hsrc.length
|
|
411
|
+
return { done: true, token: htkn }
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return undefined
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
})
|
|
419
|
+
|
|
332
420
|
jsonic.options({
|
|
333
421
|
text: {
|
|
334
422
|
check: (lex: any) => {
|
|
@@ -349,10 +437,29 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
349
437
|
lex.aontu_eq_at = j
|
|
350
438
|
}
|
|
351
439
|
|
|
440
|
+
// The name is the KEY; separator and reference follow it.
|
|
441
|
+
const shend = lex.aontu_shorthand_end
|
|
442
|
+
if (null != shend && pnt.sI < shend) {
|
|
443
|
+
let k = pnt.sI + asrc.length
|
|
444
|
+
while (k < src.length && WS_RE.test(src[k])) {
|
|
445
|
+
k++
|
|
446
|
+
}
|
|
447
|
+
const ktkn = lex.token('#TX', asrc.substring(1), asrc, pnt)
|
|
448
|
+
pnt.cI += k - pnt.sI
|
|
449
|
+
pnt.sI = k
|
|
450
|
+
lex.aontu_shorthand = { at: k, name: asrc, stage: 1 }
|
|
451
|
+
if (shend - 1 <= k) {
|
|
452
|
+
delete lex.aontu_shorthand_end
|
|
453
|
+
}
|
|
454
|
+
return { done: true, token: ktkn }
|
|
455
|
+
}
|
|
456
|
+
|
|
352
457
|
const atkn = lex.token(
|
|
353
458
|
'#VL',
|
|
354
459
|
(r: Rule, ctx: JsonicContext) =>
|
|
355
|
-
addsite(new RefVal(
|
|
460
|
+
addsite(new RefVal(
|
|
461
|
+
{ peg: [aliasScopedKey(asrc, srcUrl(ctx))], absolute: true }),
|
|
462
|
+
r, ctx),
|
|
356
463
|
asrc,
|
|
357
464
|
pnt)
|
|
358
465
|
pnt.sI += asrc.length
|
|
@@ -360,6 +467,27 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
360
467
|
return { done: true, token: atkn }
|
|
361
468
|
}
|
|
362
469
|
|
|
470
|
+
// `export(...)`: the word is the key, spelled unwritably.
|
|
471
|
+
if (CC_e === src.charCodeAt(pnt.sI)) {
|
|
472
|
+
const eres = EXPORT_RE.exec(lex.refwd())
|
|
473
|
+
if (null != eres) {
|
|
474
|
+
const open = eres[0].indexOf('(')
|
|
475
|
+
const arg = eres[0].indexOf(eres[1], open)
|
|
476
|
+
lex.aontu_export = {
|
|
477
|
+
cl: pnt.sI + open,
|
|
478
|
+
arg: pnt.sI + arg,
|
|
479
|
+
end: pnt.sI + eres[0].length,
|
|
480
|
+
src: eres[1],
|
|
481
|
+
}
|
|
482
|
+
const etkn = lex.token(
|
|
483
|
+
'#TX', exportHoldKey(), EXPORT_DECL_NAME, pnt,
|
|
484
|
+
{ aontu_export: true, aontu_export_items: aliasSetItems(eres[1]) })
|
|
485
|
+
pnt.sI += EXPORT_DECL_NAME.length
|
|
486
|
+
pnt.cI += EXPORT_DECL_NAME.length
|
|
487
|
+
return { done: true, token: etkn }
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
363
491
|
if (CC_EQ === src.charCodeAt(pnt.sI) && lex.aontu_eq_at === pnt.sI) {
|
|
364
492
|
delete lex.aontu_eq_at
|
|
365
493
|
const eqtkn = lex.token('#CL', undefined, '=', pnt, { aontu_eq: true })
|
|
@@ -437,6 +565,28 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
437
565
|
return v
|
|
438
566
|
}
|
|
439
567
|
|
|
568
|
+
// `export` publishes a name; it renames none, and `{%}` is theirs.
|
|
569
|
+
const publishedNames = (items?: AliasBind[]): string[] | undefined =>
|
|
570
|
+
undefined === items || 0 === items.length ? undefined :
|
|
571
|
+
items.every((i) => i.local === i.remote) ?
|
|
572
|
+
items.map((i) => i.local) : undefined
|
|
573
|
+
|
|
574
|
+
// A ROOT MAY BE WRAPPED: a file's names are its MAP'S, not open()'s.
|
|
575
|
+
const declaringMap = (val: any): any => {
|
|
576
|
+
let cur = val
|
|
577
|
+
while (true === cur?.isFunc && 1 === cur.peg?.length) {
|
|
578
|
+
cur = cur.peg[0]
|
|
579
|
+
}
|
|
580
|
+
return cur
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// The file a value was written in, and so any alias name's scope. A
|
|
584
|
+
// DOCUMENT IS ITS OWN SCOPE too, so a fileless parse is tagged.
|
|
585
|
+
const srcUrl = (ctx: JsonicContext): string => {
|
|
586
|
+
const url = ctx.meta.multisource && ctx.meta.multisource.path
|
|
587
|
+
return url ? url : ((ctx.meta as any).aontu_scope ??= '#' + (++SCOPE_SEQ))
|
|
588
|
+
}
|
|
589
|
+
|
|
440
590
|
let addsite = (v: Val, r: Rule, ctx: JsonicContext) => {
|
|
441
591
|
// The source text comes from the SAME token the row and column
|
|
442
592
|
// come from. jsonic has carried it all along; not reading it is
|
|
@@ -452,8 +602,16 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
452
602
|
const isAliasDecl = (ktkn: any, sep: any): boolean =>
|
|
453
603
|
null != ktkn && VL === ktkn.tin && ALIAS_RE.test('' + ktkn.src) &&
|
|
454
604
|
true === sep?.use?.aontu_eq
|
|
605
|
+
const keyName = (ktkn: any): string =>
|
|
606
|
+
'string' === typeof ktkn?.val ? ktkn.val : '' + ktkn?.src
|
|
607
|
+
|
|
455
608
|
const keyRefusalOf = (ktkn: any, sep: any):
|
|
456
609
|
{ why: string, details?: Record<string, any> } | undefined => {
|
|
610
|
+
// THE SENTINEL NAMESPACE IS THE ENGINE'S; it writes `export` here.
|
|
611
|
+
if (keyName(ktkn).startsWith(RESERVED_KEY_PREFIX) &&
|
|
612
|
+
true !== ktkn.use?.aontu_export) {
|
|
613
|
+
return { why: 'reserved_key' }
|
|
614
|
+
}
|
|
457
615
|
if (null == ktkn || VL !== ktkn.tin) {
|
|
458
616
|
return undefined
|
|
459
617
|
}
|
|
@@ -579,14 +737,14 @@ help isolate the syntax error.`,
|
|
|
579
737
|
((prev as any)[SPREAD] || { o: '&', v: [] })
|
|
580
738
|
;(prev as any)[SPREAD].v.push(lm.spread.cj)
|
|
581
739
|
}
|
|
582
|
-
prev
|
|
583
|
-
for (const k of lm.optionalKeys) { prev.
|
|
584
|
-
prev
|
|
585
|
-
for (const k of lm.aliasKeys) { prev.
|
|
740
|
+
prev[OPTIONAL_MARK_KEY] = (prev[OPTIONAL_MARK_KEY] || [])
|
|
741
|
+
for (const k of lm.optionalKeys) { prev[OPTIONAL_MARK_KEY].push(k) }
|
|
742
|
+
prev[ALIAS_MARK_KEY] = (prev[ALIAS_MARK_KEY] || [])
|
|
743
|
+
for (const k of lm.aliasKeys) { prev[ALIAS_MARK_KEY].push(k) }
|
|
586
744
|
return prev
|
|
587
745
|
}
|
|
588
|
-
prev
|
|
589
|
-
prev.
|
|
746
|
+
prev[MERGE_KEY] = (prev[MERGE_KEY] || [])
|
|
747
|
+
prev[MERGE_KEY].push(curr)
|
|
590
748
|
return prev
|
|
591
749
|
}
|
|
592
750
|
}
|
|
@@ -1033,7 +1191,8 @@ help isolate the syntax error.`,
|
|
|
1033
1191
|
const aname = r.u.aontu_alias_val
|
|
1034
1192
|
if (null != aname && null != valnode) {
|
|
1035
1193
|
const hoist = ((ctx as any).aontu_alias_hoist ||= [])
|
|
1036
|
-
hoist.push(
|
|
1194
|
+
hoist.push(
|
|
1195
|
+
{ name: aliasScopedKey('' + aname, srcUrl(ctx)), val: valnode })
|
|
1037
1196
|
}
|
|
1038
1197
|
|
|
1039
1198
|
return undefined
|
|
@@ -1055,13 +1214,27 @@ help isolate the syntax error.`,
|
|
|
1055
1214
|
|
|
1056
1215
|
.bc((r: Rule, ctx: JsonicContext) => {
|
|
1057
1216
|
const optionalKeys = r.u.aontu_optional_keys ?? []
|
|
1058
|
-
const
|
|
1217
|
+
const aliasDecls = r.u.aontu_alias_keys ?? []
|
|
1218
|
+
const aliasKeys: string[] = []
|
|
1219
|
+
const exportKeys: string[] = []
|
|
1059
1220
|
|
|
1060
1221
|
let mo = r.node
|
|
1061
1222
|
|
|
1223
|
+
// A REFUSED KEY TAKES NO KEY and is not OPTIONAL: its name is a
|
|
1224
|
+
// mark's, and the elision below would put it back.
|
|
1225
|
+
for (const kr of (r.u.aontu_key_refusals ?? []) as any[]) {
|
|
1226
|
+
if ('reserved_key' === kr.why) {
|
|
1227
|
+
delete mo[kr.key]
|
|
1228
|
+
const oi = optionalKeys.indexOf(kr.key)
|
|
1229
|
+
if (-1 !== oi) {
|
|
1230
|
+
optionalKeys.splice(oi, 1)
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1062
1235
|
for (const k in mo) {
|
|
1063
|
-
if (null == mo[k] &&
|
|
1064
|
-
|
|
1236
|
+
if (null == mo[k] && MERGE_KEY !== k &&
|
|
1237
|
+
OPTIONAL_MARK_KEY !== k && ALIAS_MARK_KEY !== k) {
|
|
1065
1238
|
// Pathed at the KEY, not at the enclosing map. addsite takes
|
|
1066
1239
|
// the rule's path, which here is the map's, so the error
|
|
1067
1240
|
// would otherwise name the container and leave the reader to
|
|
@@ -1089,27 +1262,129 @@ help isolate the syntax error.`,
|
|
|
1089
1262
|
return undefined
|
|
1090
1263
|
}
|
|
1091
1264
|
|
|
1092
|
-
|
|
1265
|
+
const reservedRefusals: Val[] = []
|
|
1266
|
+
for (const { key, tkn, why, details, url } of
|
|
1093
1267
|
(r.u.aontu_key_refusals ?? []) as any[]) {
|
|
1094
1268
|
const en: any = siteAt(addsite(new NilVal({ why }), r, ctx), tokenSite(tkn))
|
|
1095
1269
|
if (null != details) {
|
|
1096
1270
|
en.details = details
|
|
1097
1271
|
}
|
|
1098
1272
|
en.path = [...(r.k?.path ?? []), key]
|
|
1099
|
-
|
|
1273
|
+
// A COLON DECLARATION IS REFUSED AND STILL NAMES SOMETHING, so
|
|
1274
|
+
// the refusal is held under the key that name would have had
|
|
1275
|
+
// and a use of it reports why; not an alias key, so it
|
|
1276
|
+
// generates unused.
|
|
1277
|
+
if ('alias_colon' === why) {
|
|
1278
|
+
delete mo[key]
|
|
1279
|
+
mo[aliasScopedKey(key, url)] = en
|
|
1280
|
+
}
|
|
1281
|
+
// A RESERVED KEY WAITS: its name is where the marks are kept.
|
|
1282
|
+
else if ('reserved_key' === why) {
|
|
1283
|
+
reservedRefusals.push(en)
|
|
1284
|
+
}
|
|
1285
|
+
else {
|
|
1286
|
+
mo[key] = en
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
// A NAME BELONGS TO THE FILE THAT DECLARED IT. Renamed here and
|
|
1291
|
+
// not at the key, so an elided value is still reported by the
|
|
1292
|
+
// name the source spells; rebuilt, as key order is resolution.
|
|
1293
|
+
const renamed = new Map<string, string>()
|
|
1294
|
+
for (const { name, key } of aliasDecls as any[]) {
|
|
1295
|
+
renamed.set(name, key)
|
|
1296
|
+
if (!aliasKeys.includes(key)) {
|
|
1297
|
+
aliasKeys.push(key)
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
if (0 < renamed.size) {
|
|
1301
|
+
const entries = Object.entries(mo)
|
|
1302
|
+
for (const [k] of entries) {
|
|
1303
|
+
delete mo[k]
|
|
1304
|
+
}
|
|
1305
|
+
for (const [k, v] of entries) {
|
|
1306
|
+
const key = renamed.get(k)
|
|
1307
|
+
if (undefined === key) {
|
|
1308
|
+
mo[k] = v
|
|
1309
|
+
}
|
|
1310
|
+
else {
|
|
1311
|
+
mo[key] = v
|
|
1312
|
+
repathInstance(v, [...(r.k?.path ?? []), key])
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// `export` PUBLISHES NAMES AND NOTHING ELSE; a bare name, a key
|
|
1318
|
+
// and the wildcard are refused, and the argument is erased.
|
|
1319
|
+
for (const { items, tkn } of (r.u.aontu_export_decls ?? []) as any[]) {
|
|
1320
|
+
delete mo[tkn.val]
|
|
1321
|
+
const names = publishedNames(items)
|
|
1322
|
+
if (undefined === names) {
|
|
1323
|
+
const en: any =
|
|
1324
|
+
siteAt(addsite(new NilVal({ why: 'export_arg' }), r, ctx), tokenSite(tkn))
|
|
1325
|
+
en.path = [...(r.k?.path ?? []), EXPORT_DECL_NAME]
|
|
1326
|
+
mo[EXPORT_DECL_NAME] = en
|
|
1327
|
+
}
|
|
1328
|
+
else {
|
|
1329
|
+
exportKeys.push(...names.map((n) => aliasScopedKey(n, srcUrl(ctx))))
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// THE DESTRUCTURE IS ADDITIVE: the values land where the head
|
|
1334
|
+
// stands, each name binds in THIS file's scope, and a file
|
|
1335
|
+
// publishes only what IT declares. A DECLARATION IS THE
|
|
1336
|
+
// DOCUMENT'S, so the names go to the root and the values may
|
|
1337
|
+
// sit under a key.
|
|
1338
|
+
for (const im of (r.u.aontu_import_decls ?? []) as any[]) {
|
|
1339
|
+
const hoist = ((ctx as any).aontu_alias_hoist ||= [])
|
|
1340
|
+
const iv: any = mo[im.key]
|
|
1341
|
+
delete mo[im.key]
|
|
1342
|
+
const dv: any = declaringMap(iv)
|
|
1343
|
+
const ex: string[] = dv.exportKeys ?? []
|
|
1344
|
+
const ak: string[] = dv.aliasKeys ?? []
|
|
1345
|
+
for (const k of ak) {
|
|
1346
|
+
hoist.push({ name: k, val: dv.peg[k] })
|
|
1347
|
+
delete dv.peg[k]
|
|
1348
|
+
}
|
|
1349
|
+
if (0 < ak.length + ex.length) {
|
|
1350
|
+
dv.aliasKeys = []
|
|
1351
|
+
dv.exportKeys = []
|
|
1352
|
+
}
|
|
1353
|
+
repathInstance(iv, [...(r.k?.path ?? [])])
|
|
1354
|
+
; (mo[MERGE_KEY] = mo[MERGE_KEY] || []).push(iv)
|
|
1355
|
+
const binds: AliasBind[] = 0 === im.names.length ?
|
|
1356
|
+
ex.map((k: string) =>
|
|
1357
|
+
({ local: aliasBareName(k), remote: aliasBareName(k) })) : im.names
|
|
1358
|
+
for (const { local, remote } of binds) {
|
|
1359
|
+
const from = ex.find((k: string) => aliasBareName(k) === remote)
|
|
1360
|
+
let bind: Val
|
|
1361
|
+
if (undefined === from || !ak.includes(from)) {
|
|
1362
|
+
bind = siteAt(addsite(
|
|
1363
|
+
new NilVal({ why: 'import_not_exported' }), r, ctx), tokenSite(im.tkn))
|
|
1364
|
+
; (bind as any).details = { name: remote }
|
|
1365
|
+
const en: any = siteAt(addsite(
|
|
1366
|
+
new NilVal({ why: 'import_not_exported' }), r, ctx), tokenSite(im.tkn))
|
|
1367
|
+
en.details = { name: remote }
|
|
1368
|
+
mo[MERGE_KEY].push(en)
|
|
1369
|
+
}
|
|
1370
|
+
else {
|
|
1371
|
+
bind = addsite(new RefVal({ peg: [from], absolute: true }), r, ctx)
|
|
1372
|
+
}
|
|
1373
|
+
hoist.push({ name: aliasScopedKey(local, im.url), val: bind })
|
|
1374
|
+
}
|
|
1100
1375
|
}
|
|
1101
1376
|
|
|
1102
1377
|
// Marks carried over from a map include folded in the merge
|
|
1103
1378
|
// hook above, applied here where the MapVal is built.
|
|
1104
|
-
if (mo
|
|
1105
|
-
for (const k of (mo
|
|
1379
|
+
if (mo[OPTIONAL_MARK_KEY] || mo[ALIAS_MARK_KEY]) {
|
|
1380
|
+
for (const k of (mo[OPTIONAL_MARK_KEY] || [])) {
|
|
1106
1381
|
if (!optionalKeys.includes(k)) { optionalKeys.push(k) }
|
|
1107
1382
|
}
|
|
1108
|
-
for (const k of (mo
|
|
1383
|
+
for (const k of (mo[ALIAS_MARK_KEY] || [])) {
|
|
1109
1384
|
if (!aliasKeys.includes(k)) { aliasKeys.push(k) }
|
|
1110
1385
|
}
|
|
1111
|
-
delete mo
|
|
1112
|
-
delete mo
|
|
1386
|
+
delete mo[OPTIONAL_MARK_KEY]
|
|
1387
|
+
delete mo[ALIAS_MARK_KEY]
|
|
1113
1388
|
}
|
|
1114
1389
|
|
|
1115
1390
|
// A value-prefix declaration lands here, at the document root
|
|
@@ -1126,21 +1401,28 @@ help isolate the syntax error.`,
|
|
|
1126
1401
|
}
|
|
1127
1402
|
|
|
1128
1403
|
// Handle defered conjuncts, e.g. `{x:1 @"foo"}`
|
|
1129
|
-
|
|
1404
|
+
const deferred = mo[MERGE_KEY]
|
|
1405
|
+
delete mo[MERGE_KEY]
|
|
1406
|
+
|
|
1407
|
+
const merge = 0 < reservedRefusals.length ?
|
|
1408
|
+
[...(deferred ?? []), ...reservedRefusals] : deferred
|
|
1409
|
+
|
|
1410
|
+
if (merge) {
|
|
1130
1411
|
let mop = { ...mo }
|
|
1131
|
-
delete mop.___merge
|
|
1132
1412
|
|
|
1133
1413
|
let mopv = new MapVal({ peg: mop })
|
|
1134
1414
|
mopv.optionalKeys = optionalKeys
|
|
1135
1415
|
mopv.aliasKeys = aliasKeys
|
|
1416
|
+
mopv.exportKeys = exportKeys
|
|
1136
1417
|
|
|
1137
1418
|
r.node =
|
|
1138
|
-
addsite(new ConjunctVal({ peg: [mopv, ...
|
|
1419
|
+
addsite(new ConjunctVal({ peg: [mopv, ...merge] }), r, ctx)
|
|
1139
1420
|
}
|
|
1140
1421
|
else {
|
|
1141
1422
|
r.node = addsite(new MapVal({ peg: mo }), r, ctx)
|
|
1142
1423
|
r.node.optionalKeys = optionalKeys
|
|
1143
1424
|
r.node.aliasKeys = aliasKeys
|
|
1425
|
+
r.node.exportKeys = exportKeys
|
|
1144
1426
|
}
|
|
1145
1427
|
|
|
1146
1428
|
return undefined
|
|
@@ -1169,7 +1451,7 @@ help isolate the syntax error.`,
|
|
|
1169
1451
|
}
|
|
1170
1452
|
}
|
|
1171
1453
|
|
|
1172
|
-
// No
|
|
1454
|
+
// No merge-key arm here: the deferred map.merge that writes it
|
|
1173
1455
|
// only ever fires for a `pair` rule, whose parent is always a
|
|
1174
1456
|
// `map` rule with a plain-object node — never a list.
|
|
1175
1457
|
{
|
|
@@ -1263,19 +1545,39 @@ help isolate the syntax error.`,
|
|
|
1263
1545
|
}
|
|
1264
1546
|
})
|
|
1265
1547
|
|
|
1266
|
-
.bc((rule: Rule) => {
|
|
1548
|
+
.bc((rule: Rule, ctx: JsonicContext) => {
|
|
1267
1549
|
// TRAVERSE PARENTS TO GET PATH
|
|
1268
1550
|
|
|
1269
|
-
|
|
1551
|
+
// AN OPTIONAL PAIR OPENS ON ITS `?`: the rule before read the key.
|
|
1552
|
+
const ktkn: any = true === rule.prev?.u?.aontu_optional ?
|
|
1553
|
+
rule.prev.o0 : rule.o0
|
|
1270
1554
|
const holder: any = rule.parent
|
|
1271
1555
|
const kr = keyRefusalOf(ktkn, rule.o1)
|
|
1272
1556
|
if (null != kr) {
|
|
1273
1557
|
holder.u.aontu_key_refusals = (holder.u.aontu_key_refusals || [])
|
|
1274
|
-
holder.u.aontu_key_refusals.push(
|
|
1558
|
+
holder.u.aontu_key_refusals.push(
|
|
1559
|
+
{ key: keyName(ktkn), tkn: ktkn, url: srcUrl(ctx), ...kr })
|
|
1275
1560
|
}
|
|
1276
1561
|
else if (isAliasDecl(ktkn, rule.o1)) {
|
|
1277
1562
|
holder.u.aontu_alias_keys = (holder.u.aontu_alias_keys || [])
|
|
1278
|
-
holder.u.aontu_alias_keys.push(
|
|
1563
|
+
holder.u.aontu_alias_keys.push({
|
|
1564
|
+
name: '' + ktkn.src,
|
|
1565
|
+
key: aliasScopedKey('' + ktkn.src, srcUrl(ctx)),
|
|
1566
|
+
})
|
|
1567
|
+
}
|
|
1568
|
+
else if (true === ktkn?.use?.aontu_export) {
|
|
1569
|
+
holder.u.aontu_export_decls = (holder.u.aontu_export_decls || [])
|
|
1570
|
+
holder.u.aontu_export_decls.push(
|
|
1571
|
+
{ items: ktkn.use.aontu_export_items, tkn: ktkn })
|
|
1572
|
+
}
|
|
1573
|
+
else if (null != ktkn?.use?.aontu_import) {
|
|
1574
|
+
holder.u.aontu_import_decls = (holder.u.aontu_import_decls || [])
|
|
1575
|
+
holder.u.aontu_import_decls.push({
|
|
1576
|
+
key: '' + ktkn.src,
|
|
1577
|
+
names: ktkn.use.aontu_import,
|
|
1578
|
+
url: srcUrl(ctx),
|
|
1579
|
+
tkn: ktkn,
|
|
1580
|
+
})
|
|
1279
1581
|
}
|
|
1280
1582
|
|
|
1281
1583
|
if (rule.u.spread) {
|
|
@@ -1394,7 +1696,8 @@ help isolate the syntax error.`,
|
|
|
1394
1696
|
}
|
|
1395
1697
|
// An element is a value position, so the prefix form applies.
|
|
1396
1698
|
if (null == kr && isAliasDecl(ktkn, rule.o1)) {
|
|
1397
|
-
; ((ctx as any).aontu_alias_hoist ||= [])
|
|
1699
|
+
; ((ctx as any).aontu_alias_hoist ||= [])
|
|
1700
|
+
.push({ name: aliasScopedKey(key, srcUrl(ctx)), val: v })
|
|
1398
1701
|
rule.node.push(v)
|
|
1399
1702
|
return undefined
|
|
1400
1703
|
}
|
|
@@ -1406,7 +1709,8 @@ help isolate the syntax error.`,
|
|
|
1406
1709
|
// "is this the top level" test reads the path, so an element
|
|
1407
1710
|
// of a top-level list must not read as the root.
|
|
1408
1711
|
mv.path = [...(rule.k?.path ?? []), '' + rule.node.length]
|
|
1409
|
-
|
|
1712
|
+
// A REFUSED KEY IS NOT OPTIONAL: the refusal would be dropped.
|
|
1713
|
+
if (true === rule.u.aontu_optional_elem && null == kr) {
|
|
1410
1714
|
mv.optionalKeys = [key]
|
|
1411
1715
|
}
|
|
1412
1716
|
rule.node.push(mv)
|