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.
- 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/cli.d.ts +4 -1
- package/dist/cli.js +29 -13
- package/dist/cli.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/helpdoc.js +1 -1
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.js +171 -0
- package/dist/hints.js.map +1 -1
- package/dist/lang.js +325 -38
- 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 +47 -18
- package/dist/val/RefVal.js.map +1 -1
- package/dist/vet.d.ts +2 -1
- package/dist/vet.js +51 -6
- package/dist/vet.js.map +1 -1
- package/package.json +1 -1
- package/skill/error-codes.md +25 -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/cli.ts +35 -13
- package/src/ctx.ts +4 -1
- package/src/err.ts +19 -1
- package/src/format.ts +14 -0
- package/src/helpdoc.ts +1 -1
- package/src/hints.ts +228 -0
- package/src/lang.ts +370 -33
- 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 +49 -19
- package/src/vet.ts +55 -6
package/src/lang.ts
CHANGED
|
@@ -106,6 +106,8 @@ import { DisjunctVal } from './val/DisjunctVal'
|
|
|
106
106
|
import { IntegerVal } from './val/IntegerVal'
|
|
107
107
|
import { ListVal } from './val/ListVal'
|
|
108
108
|
import { MapVal } from './val/MapVal'
|
|
109
|
+
import { repathInstance } from './val/Val'
|
|
110
|
+
import { AontuContext } from './ctx'
|
|
109
111
|
import { NilVal } from './val/NilVal'
|
|
110
112
|
import { NullVal } from './val/NullVal'
|
|
111
113
|
import { NumberVal } from './val/NumberVal'
|
|
@@ -188,11 +190,34 @@ const CC_d = 100
|
|
|
188
190
|
const CC_D = 68
|
|
189
191
|
|
|
190
192
|
const CC_PCT = 37
|
|
191
|
-
|
|
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'
|
|
192
205
|
|
|
193
206
|
const CC_EQ = 61
|
|
194
207
|
const CC_SP = 32
|
|
195
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]*\)/
|
|
196
221
|
|
|
197
222
|
const CC_9 = 57
|
|
198
223
|
const CC_A = 65
|
|
@@ -326,6 +351,72 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
326
351
|
},
|
|
327
352
|
})
|
|
328
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
|
+
|
|
329
420
|
jsonic.options({
|
|
330
421
|
text: {
|
|
331
422
|
check: (lex: any) => {
|
|
@@ -346,10 +437,29 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
346
437
|
lex.aontu_eq_at = j
|
|
347
438
|
}
|
|
348
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
|
+
|
|
349
457
|
const atkn = lex.token(
|
|
350
458
|
'#VL',
|
|
351
459
|
(r: Rule, ctx: JsonicContext) =>
|
|
352
|
-
addsite(new RefVal(
|
|
460
|
+
addsite(new RefVal(
|
|
461
|
+
{ peg: [aliasScopedKey(asrc, srcUrl(ctx))], absolute: true }),
|
|
462
|
+
r, ctx),
|
|
353
463
|
asrc,
|
|
354
464
|
pnt)
|
|
355
465
|
pnt.sI += asrc.length
|
|
@@ -357,6 +467,27 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
357
467
|
return { done: true, token: atkn }
|
|
358
468
|
}
|
|
359
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
|
+
|
|
360
491
|
if (CC_EQ === src.charCodeAt(pnt.sI) && lex.aontu_eq_at === pnt.sI) {
|
|
361
492
|
delete lex.aontu_eq_at
|
|
362
493
|
const eqtkn = lex.token('#CL', undefined, '=', pnt, { aontu_eq: true })
|
|
@@ -434,6 +565,28 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
434
565
|
return v
|
|
435
566
|
}
|
|
436
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
|
+
|
|
437
590
|
let addsite = (v: Val, r: Rule, ctx: JsonicContext) => {
|
|
438
591
|
// The source text comes from the SAME token the row and column
|
|
439
592
|
// come from. jsonic has carried it all along; not reading it is
|
|
@@ -449,8 +602,16 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
449
602
|
const isAliasDecl = (ktkn: any, sep: any): boolean =>
|
|
450
603
|
null != ktkn && VL === ktkn.tin && ALIAS_RE.test('' + ktkn.src) &&
|
|
451
604
|
true === sep?.use?.aontu_eq
|
|
605
|
+
const keyName = (ktkn: any): string =>
|
|
606
|
+
'string' === typeof ktkn?.val ? ktkn.val : '' + ktkn?.src
|
|
607
|
+
|
|
452
608
|
const keyRefusalOf = (ktkn: any, sep: any):
|
|
453
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
|
+
}
|
|
454
615
|
if (null == ktkn || VL !== ktkn.tin) {
|
|
455
616
|
return undefined
|
|
456
617
|
}
|
|
@@ -576,14 +737,14 @@ help isolate the syntax error.`,
|
|
|
576
737
|
((prev as any)[SPREAD] || { o: '&', v: [] })
|
|
577
738
|
;(prev as any)[SPREAD].v.push(lm.spread.cj)
|
|
578
739
|
}
|
|
579
|
-
prev
|
|
580
|
-
for (const k of lm.optionalKeys) { prev.
|
|
581
|
-
prev
|
|
582
|
-
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) }
|
|
583
744
|
return prev
|
|
584
745
|
}
|
|
585
|
-
prev
|
|
586
|
-
prev.
|
|
746
|
+
prev[MERGE_KEY] = (prev[MERGE_KEY] || [])
|
|
747
|
+
prev[MERGE_KEY].push(curr)
|
|
587
748
|
return prev
|
|
588
749
|
}
|
|
589
750
|
}
|
|
@@ -940,6 +1101,16 @@ help isolate the syntax error.`,
|
|
|
940
1101
|
jsonic.rule('val', (rs: RuleSpec) => {
|
|
941
1102
|
|
|
942
1103
|
rs
|
|
1104
|
+
// Before the core implicit-map alts: a value prefix, not a key.
|
|
1105
|
+
.open([
|
|
1106
|
+
{
|
|
1107
|
+
s: [VL, CL],
|
|
1108
|
+
c: (r: Rule) => 0 !== r.d && isAliasDecl(r.o0, r.o1),
|
|
1109
|
+
p: 'val',
|
|
1110
|
+
a: (r: Rule) => { r.u.aontu_alias_val = '' + r.o0.src },
|
|
1111
|
+
g: 'aontu-alias-val',
|
|
1112
|
+
},
|
|
1113
|
+
], { append: false })
|
|
943
1114
|
.open([
|
|
944
1115
|
{
|
|
945
1116
|
s: [CJ, CL], p: 'map', b: 2, n: { pk: 1 },
|
|
@@ -1017,6 +1188,13 @@ help isolate the syntax error.`,
|
|
|
1017
1188
|
|
|
1018
1189
|
r.node = valnode
|
|
1019
1190
|
|
|
1191
|
+
const aname = r.u.aontu_alias_val
|
|
1192
|
+
if (null != aname && null != valnode) {
|
|
1193
|
+
const hoist = ((ctx as any).aontu_alias_hoist ||= [])
|
|
1194
|
+
hoist.push(
|
|
1195
|
+
{ name: aliasScopedKey('' + aname, srcUrl(ctx)), val: valnode })
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1020
1198
|
return undefined
|
|
1021
1199
|
})
|
|
1022
1200
|
|
|
@@ -1036,13 +1214,27 @@ help isolate the syntax error.`,
|
|
|
1036
1214
|
|
|
1037
1215
|
.bc((r: Rule, ctx: JsonicContext) => {
|
|
1038
1216
|
const optionalKeys = r.u.aontu_optional_keys ?? []
|
|
1039
|
-
const
|
|
1217
|
+
const aliasDecls = r.u.aontu_alias_keys ?? []
|
|
1218
|
+
const aliasKeys: string[] = []
|
|
1219
|
+
const exportKeys: string[] = []
|
|
1040
1220
|
|
|
1041
1221
|
let mo = r.node
|
|
1042
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
|
+
|
|
1043
1235
|
for (const k in mo) {
|
|
1044
|
-
if (null == mo[k] &&
|
|
1045
|
-
|
|
1236
|
+
if (null == mo[k] && MERGE_KEY !== k &&
|
|
1237
|
+
OPTIONAL_MARK_KEY !== k && ALIAS_MARK_KEY !== k) {
|
|
1046
1238
|
// Pathed at the KEY, not at the enclosing map. addsite takes
|
|
1047
1239
|
// the rule's path, which here is the map's, so the error
|
|
1048
1240
|
// would otherwise name the container and leave the reader to
|
|
@@ -1070,45 +1262,167 @@ help isolate the syntax error.`,
|
|
|
1070
1262
|
return undefined
|
|
1071
1263
|
}
|
|
1072
1264
|
|
|
1073
|
-
|
|
1265
|
+
const reservedRefusals: Val[] = []
|
|
1266
|
+
for (const { key, tkn, why, details, url } of
|
|
1074
1267
|
(r.u.aontu_key_refusals ?? []) as any[]) {
|
|
1075
1268
|
const en: any = siteAt(addsite(new NilVal({ why }), r, ctx), tokenSite(tkn))
|
|
1076
1269
|
if (null != details) {
|
|
1077
1270
|
en.details = details
|
|
1078
1271
|
}
|
|
1079
1272
|
en.path = [...(r.k?.path ?? []), key]
|
|
1080
|
-
|
|
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
|
+
}
|
|
1081
1375
|
}
|
|
1082
1376
|
|
|
1083
1377
|
// Marks carried over from a map include folded in the merge
|
|
1084
1378
|
// hook above, applied here where the MapVal is built.
|
|
1085
|
-
if (mo
|
|
1086
|
-
for (const k of (mo
|
|
1379
|
+
if (mo[OPTIONAL_MARK_KEY] || mo[ALIAS_MARK_KEY]) {
|
|
1380
|
+
for (const k of (mo[OPTIONAL_MARK_KEY] || [])) {
|
|
1087
1381
|
if (!optionalKeys.includes(k)) { optionalKeys.push(k) }
|
|
1088
1382
|
}
|
|
1089
|
-
for (const k of (mo
|
|
1383
|
+
for (const k of (mo[ALIAS_MARK_KEY] || [])) {
|
|
1090
1384
|
if (!aliasKeys.includes(k)) { aliasKeys.push(k) }
|
|
1091
1385
|
}
|
|
1092
|
-
delete mo
|
|
1093
|
-
delete mo
|
|
1386
|
+
delete mo[OPTIONAL_MARK_KEY]
|
|
1387
|
+
delete mo[ALIAS_MARK_KEY]
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// A value-prefix declaration lands here, at the document root
|
|
1391
|
+
// (ALIASES.0.md), as a copy pathed at its name.
|
|
1392
|
+
if (1 === r.d) {
|
|
1393
|
+
for (const { name, val } of ((ctx as any).aontu_alias_hoist ?? [])) {
|
|
1394
|
+
const acx = new AontuContext({ path: [name] })
|
|
1395
|
+
const copy: any = (val as any).clone(acx, { path: [name] })
|
|
1396
|
+
repathInstance(copy, [name])
|
|
1397
|
+
mo[name] = null == mo[name] ? copy :
|
|
1398
|
+
addsite(new ConjunctVal({ peg: [mo[name], copy] }), r, ctx)
|
|
1399
|
+
if (!aliasKeys.includes(name)) { aliasKeys.push(name) }
|
|
1400
|
+
}
|
|
1094
1401
|
}
|
|
1095
1402
|
|
|
1096
1403
|
// Handle defered conjuncts, e.g. `{x:1 @"foo"}`
|
|
1097
|
-
|
|
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) {
|
|
1098
1411
|
let mop = { ...mo }
|
|
1099
|
-
delete mop.___merge
|
|
1100
1412
|
|
|
1101
1413
|
let mopv = new MapVal({ peg: mop })
|
|
1102
1414
|
mopv.optionalKeys = optionalKeys
|
|
1103
1415
|
mopv.aliasKeys = aliasKeys
|
|
1416
|
+
mopv.exportKeys = exportKeys
|
|
1104
1417
|
|
|
1105
1418
|
r.node =
|
|
1106
|
-
addsite(new ConjunctVal({ peg: [mopv, ...
|
|
1419
|
+
addsite(new ConjunctVal({ peg: [mopv, ...merge] }), r, ctx)
|
|
1107
1420
|
}
|
|
1108
1421
|
else {
|
|
1109
1422
|
r.node = addsite(new MapVal({ peg: mo }), r, ctx)
|
|
1110
1423
|
r.node.optionalKeys = optionalKeys
|
|
1111
1424
|
r.node.aliasKeys = aliasKeys
|
|
1425
|
+
r.node.exportKeys = exportKeys
|
|
1112
1426
|
}
|
|
1113
1427
|
|
|
1114
1428
|
return undefined
|
|
@@ -1137,7 +1451,7 @@ help isolate the syntax error.`,
|
|
|
1137
1451
|
}
|
|
1138
1452
|
}
|
|
1139
1453
|
|
|
1140
|
-
// No
|
|
1454
|
+
// No merge-key arm here: the deferred map.merge that writes it
|
|
1141
1455
|
// only ever fires for a `pair` rule, whose parent is always a
|
|
1142
1456
|
// `map` rule with a plain-object node — never a list.
|
|
1143
1457
|
{
|
|
@@ -1231,19 +1545,39 @@ help isolate the syntax error.`,
|
|
|
1231
1545
|
}
|
|
1232
1546
|
})
|
|
1233
1547
|
|
|
1234
|
-
.bc((rule: Rule) => {
|
|
1548
|
+
.bc((rule: Rule, ctx: JsonicContext) => {
|
|
1235
1549
|
// TRAVERSE PARENTS TO GET PATH
|
|
1236
1550
|
|
|
1237
|
-
|
|
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
|
|
1238
1554
|
const holder: any = rule.parent
|
|
1239
1555
|
const kr = keyRefusalOf(ktkn, rule.o1)
|
|
1240
1556
|
if (null != kr) {
|
|
1241
1557
|
holder.u.aontu_key_refusals = (holder.u.aontu_key_refusals || [])
|
|
1242
|
-
holder.u.aontu_key_refusals.push(
|
|
1558
|
+
holder.u.aontu_key_refusals.push(
|
|
1559
|
+
{ key: keyName(ktkn), tkn: ktkn, url: srcUrl(ctx), ...kr })
|
|
1243
1560
|
}
|
|
1244
1561
|
else if (isAliasDecl(ktkn, rule.o1)) {
|
|
1245
1562
|
holder.u.aontu_alias_keys = (holder.u.aontu_alias_keys || [])
|
|
1246
|
-
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
|
+
})
|
|
1247
1581
|
}
|
|
1248
1582
|
|
|
1249
1583
|
if (rule.u.spread) {
|
|
@@ -1360,6 +1694,14 @@ help isolate the syntax error.`,
|
|
|
1360
1694
|
v.path = [...(rule.k?.path ?? []),
|
|
1361
1695
|
'' + rule.node.length, key]
|
|
1362
1696
|
}
|
|
1697
|
+
// An element is a value position, so the prefix form applies.
|
|
1698
|
+
if (null == kr && isAliasDecl(ktkn, rule.o1)) {
|
|
1699
|
+
; ((ctx as any).aontu_alias_hoist ||= [])
|
|
1700
|
+
.push({ name: aliasScopedKey(key, srcUrl(ctx)), val: v })
|
|
1701
|
+
rule.node.push(v)
|
|
1702
|
+
return undefined
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1363
1705
|
const mv: any = addsite(
|
|
1364
1706
|
new MapVal({ peg: { [key]: v } }), rule, ctx)
|
|
1365
1707
|
// The element's path is the list's plus its index, as any
|
|
@@ -1367,15 +1709,10 @@ help isolate the syntax error.`,
|
|
|
1367
1709
|
// "is this the top level" test reads the path, so an element
|
|
1368
1710
|
// of a top-level list must not read as the root.
|
|
1369
1711
|
mv.path = [...(rule.k?.path ?? []), '' + rule.node.length]
|
|
1370
|
-
|
|
1712
|
+
// A REFUSED KEY IS NOT OPTIONAL: the refusal would be dropped.
|
|
1713
|
+
if (true === rule.u.aontu_optional_elem && null == kr) {
|
|
1371
1714
|
mv.optionalKeys = [key]
|
|
1372
1715
|
}
|
|
1373
|
-
// ... and a declaration is a declaration IN the element, which
|
|
1374
|
-
// is where MapVal.unify refuses it: a list element is not the
|
|
1375
|
-
// top level.
|
|
1376
|
-
if (isAliasDecl(ktkn, rule.o1)) {
|
|
1377
|
-
mv.aliasKeys = [key]
|
|
1378
|
-
}
|
|
1379
1716
|
rule.node.push(mv)
|
|
1380
1717
|
}
|
|
1381
1718
|
|