aontu 0.53.0 → 0.55.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/aontu.d.ts +3 -2
- package/dist/aontu.js +36 -7
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.js +500 -8
- package/dist/cli.js.map +1 -1
- package/dist/ctx.d.ts +5 -0
- package/dist/ctx.js +1 -0
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js.map +1 -1
- package/dist/err.js +7 -1
- package/dist/err.js.map +1 -1
- package/dist/graph.d.ts +2 -5
- package/dist/graph.js +83 -46
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +9 -10
- package/dist/hcanon.js.map +1 -1
- package/dist/hints.js +94 -26
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +34 -0
- package/dist/jsonschema.js.map +1 -1
- package/dist/lang.js +593 -191
- package/dist/lang.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +86 -6
- package/dist/lsp.js.map +1 -1
- package/dist/mcp.js +153 -6
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +42 -8
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.d.ts +4 -0
- package/dist/mod.js +97 -2
- package/dist/mod.js.map +1 -1
- package/dist/patch.d.ts +5 -0
- package/dist/patch.js +25 -25
- package/dist/patch.js.map +1 -1
- package/dist/provenance.d.ts +1 -0
- package/dist/provenance.js +2 -1
- package/dist/provenance.js.map +1 -1
- package/dist/query.js.map +1 -1
- package/dist/reach.d.ts +1 -0
- package/dist/reach.js +49 -21
- package/dist/reach.js.map +1 -1
- package/dist/relation.d.ts +4 -0
- package/dist/relation.js +125 -200
- package/dist/relation.js.map +1 -1
- package/dist/sig.d.ts +25 -0
- package/dist/sig.js +277 -0
- package/dist/sig.js.map +1 -0
- package/dist/sigdecl.d.ts +2 -0
- package/dist/sigdecl.js +11 -0
- package/dist/sigdecl.js.map +1 -0
- package/dist/siggate.d.ts +4 -0
- package/dist/siggate.js +90 -0
- package/dist/siggate.js.map +1 -0
- package/dist/std.js +75 -16
- package/dist/std.js.map +1 -1
- package/dist/subsume.js +57 -10
- package/dist/subsume.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.d.ts +2 -2
- package/dist/unify.js +93 -114
- package/dist/unify.js.map +1 -1
- package/dist/utility.d.ts +1 -2
- package/dist/utility.js +7 -61
- package/dist/utility.js.map +1 -1
- package/dist/val/AggFuncVal.d.ts +12 -1
- package/dist/val/AggFuncVal.js +165 -3
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/BagVal.d.ts +2 -0
- package/dist/val/BagVal.js +56 -8
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +33 -5
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.d.ts +35 -0
- package/dist/val/ContainerKindVal.js +99 -0
- package/dist/val/ContainerKindVal.js.map +1 -0
- package/dist/val/CopyFuncVal.js +0 -7
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.d.ts +1 -2
- package/dist/val/DisjunctVal.js +146 -39
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +37 -2
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +19 -8
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.d.ts +39 -0
- package/dist/val/GraphAtomVal.js +184 -0
- package/dist/val/GraphAtomVal.js.map +1 -0
- package/dist/val/JunctionVal.js +22 -5
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/ListVal.js +27 -34
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/MapVal.d.ts +1 -0
- package/dist/val/MapVal.js +82 -35
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/PathFuncVal.d.ts +2 -2
- package/dist/val/PathFuncVal.js +75 -16
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.d.ts +25 -0
- package/dist/val/PathVal.js +150 -0
- package/dist/val/PathVal.js.map +1 -0
- package/dist/val/PlusOpVal.d.ts +2 -1
- package/dist/val/PlusOpVal.js +50 -35
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -0
- package/dist/val/PrefVal.js +159 -32
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.d.ts +19 -0
- package/dist/val/RecurseVal.js +217 -0
- package/dist/val/RecurseVal.js.map +1 -0
- package/dist/val/RefVal.d.ts +2 -1
- package/dist/val/RefVal.js +105 -72
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.d.ts +30 -7
- package/dist/val/ReferFuncVal.js +395 -94
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.d.ts +4 -2
- package/dist/val/ScalarKindVal.js +12 -1
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +4 -2
- package/dist/val/SuperFuncVal.js +118 -14
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.d.ts +1 -1
- package/dist/val/Val.d.ts +2 -3
- package/dist/val/Val.js +39 -20
- package/dist/val/Val.js.map +1 -1
- package/dist/val/arith.js +4 -1
- package/dist/val/arith.js.map +1 -1
- package/dist/vet.d.ts +1 -0
- package/dist/vet.js +32 -1
- package/dist/vet.js.map +1 -1
- package/dist/view.d.ts +90 -0
- package/dist/view.js +2168 -0
- package/dist/view.js.map +1 -0
- package/grammar/aontu.gbnf +18 -10
- package/grammar/aontu.lark +15 -10
- package/grammar/aontu.tmLanguage.json +184 -0
- package/package.json +10 -3
- package/skill/grammar-card.md +1 -2
- package/src/aontu.ts +37 -7
- package/src/cli.ts +553 -8
- package/src/ctx.ts +20 -0
- package/src/diff.ts +4 -2
- package/src/err.ts +8 -1
- package/src/graph.ts +125 -75
- package/src/hcanon.ts +9 -11
- package/src/hints.ts +112 -29
- package/src/jsonschema.ts +41 -0
- package/src/lang.ts +642 -203
- package/src/lsp.ts +75 -6
- package/src/mcp.ts +164 -6
- package/src/mod-tool.ts +48 -9
- package/src/mod.ts +110 -1
- package/src/patch.ts +31 -27
- package/src/provenance.ts +8 -1
- package/src/query.ts +4 -2
- package/src/reach.ts +52 -23
- package/src/relation.ts +139 -236
- package/src/sig.ts +345 -0
- package/src/sigdecl.ts +11 -0
- package/src/siggate.ts +144 -0
- package/src/std.ts +77 -16
- package/src/subsume.ts +59 -10
- package/src/unify.ts +102 -125
- package/src/utility.ts +7 -67
- package/src/val/AggFuncVal.ts +231 -4
- package/src/val/BagVal.ts +58 -9
- package/src/val/ConstraintVal.ts +34 -5
- package/src/val/ContainerKindVal.ts +158 -0
- package/src/val/CopyFuncVal.ts +0 -7
- package/src/val/DisjunctVal.ts +152 -40
- package/src/val/ExpectVal.ts +39 -4
- package/src/val/FuncBaseVal.ts +21 -8
- package/src/val/GraphAtomVal.ts +264 -0
- package/src/val/JunctionVal.ts +23 -6
- package/src/val/ListVal.ts +30 -37
- package/src/val/MapVal.ts +87 -39
- package/src/val/PathFuncVal.ts +107 -19
- package/src/val/PathVal.ts +221 -0
- package/src/val/PlusOpVal.ts +56 -37
- package/src/val/PrefVal.ts +186 -38
- package/src/val/RecurseVal.ts +285 -0
- package/src/val/RefVal.ts +105 -83
- package/src/val/ReferFuncVal.ts +445 -100
- package/src/val/ScalarKindVal.ts +12 -0
- package/src/val/SuperFuncVal.ts +137 -13
- package/src/val/TopVal.ts +1 -1
- package/src/val/Val.ts +44 -35
- package/src/val/arith.ts +4 -1
- package/src/vet.ts +41 -4
- package/src/view.ts +2882 -0
- package/dist/val/IdFuncVal.d.ts +0 -13
- package/dist/val/IdFuncVal.js +0 -54
- package/dist/val/IdFuncVal.js.map +0 -1
- package/src/val/IdFuncVal.ts +0 -91
package/dist/lang.js
CHANGED
|
@@ -10,12 +10,30 @@ exports.Site = exports.Lang = void 0;
|
|
|
10
10
|
const node_fs_1 = require("node:fs");
|
|
11
11
|
const node_path_1 = require("node:path");
|
|
12
12
|
const jsonic_1 = require("@tabnas/jsonic");
|
|
13
|
+
// THE CONFIG-FORMAT READERS (ADR-012). Each is a jsonic plugin for one
|
|
14
|
+
// format, so an included `.toml` or `.yaml` is parsed by a real parser
|
|
15
|
+
// for that format rather than guessed at by this one. `@tabnas/json` is
|
|
16
|
+
// the strict RFC 8259 reader, used for `.json` and `.jsonld`.
|
|
17
|
+
const sig_1 = require("./sig");
|
|
18
|
+
const json_1 = require("@tabnas/json");
|
|
19
|
+
const toml_1 = require("@tabnas/toml");
|
|
20
|
+
const jsonc_1 = require("@tabnas/jsonc");
|
|
21
|
+
const json5_1 = require("@tabnas/json5");
|
|
22
|
+
const yaml_1 = require("@tabnas/yaml");
|
|
23
|
+
const ini_1 = require("@tabnas/ini");
|
|
13
24
|
const debug_1 = require("@tabnas/debug");
|
|
14
25
|
const multisource_1 = require("@tabnas/multisource");
|
|
15
26
|
// TODO: @tabnas/multisource should support virtual fs
|
|
16
27
|
const file_1 = require("@tabnas/multisource/resolver/file");
|
|
17
28
|
const pkg_1 = require("@tabnas/multisource/resolver/pkg");
|
|
18
29
|
const mem_1 = require("@tabnas/multisource/resolver/mem");
|
|
30
|
+
// The Aontu-source processor, TAKEN RATHER THAN ALIASED. The obvious
|
|
31
|
+
// spelling is the alias `aon: 'jsonic'`, which multisource resolves
|
|
32
|
+
// through its own processor map -- but `jsonic` is a FORMAT NAME in
|
|
33
|
+
// the include table now, so that alias resolved to the plain-jsonic
|
|
34
|
+
// DATA reader and every `.aon` include was suddenly parsed without the
|
|
35
|
+
// language in it. Naming the function leaves nothing to collide with.
|
|
36
|
+
const jsonic_2 = require("@tabnas/multisource/processor/jsonic");
|
|
19
37
|
const std_1 = require("./std");
|
|
20
38
|
const mod_1 = require("./mod");
|
|
21
39
|
const expr_1 = require("@tabnas/expr");
|
|
@@ -50,8 +68,8 @@ const KeyFuncVal_1 = require("./val/KeyFuncVal");
|
|
|
50
68
|
const TypeFuncVal_1 = require("./val/TypeFuncVal");
|
|
51
69
|
const HideFuncVal_1 = require("./val/HideFuncVal");
|
|
52
70
|
const DeprecateFuncVal_1 = require("./val/DeprecateFuncVal");
|
|
53
|
-
const IdFuncVal_1 = require("./val/IdFuncVal");
|
|
54
71
|
const ReferFuncVal_1 = require("./val/ReferFuncVal");
|
|
72
|
+
const GraphAtomVal_1 = require("./val/GraphAtomVal");
|
|
55
73
|
const PackFuncVal_1 = require("./val/PackFuncVal");
|
|
56
74
|
const EachFuncVal_1 = require("./val/EachFuncVal");
|
|
57
75
|
const FilterFuncVal_1 = require("./val/FilterFuncVal");
|
|
@@ -61,6 +79,7 @@ const AggFuncVal_1 = require("./val/AggFuncVal");
|
|
|
61
79
|
const PlaceVal_1 = require("./val/PlaceVal");
|
|
62
80
|
const MoveFuncVal_1 = require("./val/MoveFuncVal");
|
|
63
81
|
const PathFuncVal_1 = require("./val/PathFuncVal");
|
|
82
|
+
const ContainerKindVal_1 = require("./val/ContainerKindVal");
|
|
64
83
|
const PrefFuncVal_1 = require("./val/PrefFuncVal");
|
|
65
84
|
const CloseFuncVal_1 = require("./val/CloseFuncVal");
|
|
66
85
|
const OpenFuncVal_1 = require("./val/OpenFuncVal");
|
|
@@ -102,6 +121,12 @@ function bigVal(res) {
|
|
|
102
121
|
const CC_0 = 48;
|
|
103
122
|
const CC_d = 100;
|
|
104
123
|
const CC_D = 68;
|
|
124
|
+
// THE ALIAS SIGIL. `%` is part of an alias's name, so the name is one
|
|
125
|
+
// lexeme wherever it appears and its meaning is decided by position:
|
|
126
|
+
// a BINDING in key position (`%uint8: …` declares), a USE in value
|
|
127
|
+
// position (`listen: %uint8` refers). docs/design/ALIASES.0.md §4.
|
|
128
|
+
const CC_PCT = 37;
|
|
129
|
+
const ALIAS_RE = /^%[A-Za-z_][A-Za-z0-9_]*/;
|
|
105
130
|
let AontuJsonic = function AontuLang(jsonic) {
|
|
106
131
|
jsonic.use(asPlugin(path_1.Path));
|
|
107
132
|
// Only # line comments are valid Aontu syntax (see
|
|
@@ -112,6 +137,35 @@ let AontuJsonic = function AontuLang(jsonic) {
|
|
|
112
137
|
terms = dropUnfilled(terms);
|
|
113
138
|
if (0 === terms.length)
|
|
114
139
|
return incompleteNil(r, ctx);
|
|
140
|
+
// AN ALIAS IS NOT A PATH SEGMENT. `$.%foo` is refused: the alias
|
|
141
|
+
// namespace and the path namespace are disjoint, and an alias is
|
|
142
|
+
// reached by writing `%foo` and only that.
|
|
143
|
+
//
|
|
144
|
+
// The engine spells an alias reference AS a root reference to the
|
|
145
|
+
// declaration -- which is what gives it order independence and a
|
|
146
|
+
// cycle check shared with paths -- but that is an implementation of
|
|
147
|
+
// the name, not a second way to write it. Left writable, the two
|
|
148
|
+
// spellings would drift apart the moment aliases stop being
|
|
149
|
+
// file-shaped, and `$.%b` inside an included file would reach the
|
|
150
|
+
// INCLUDER's `%b` rather than its own, which is exactly the
|
|
151
|
+
// cross-file capture the sigil exists to prevent.
|
|
152
|
+
// `%foo` lexes to the reference itself, so in `$.%foo` it arrives
|
|
153
|
+
// as a TERM rather than as a string segment -- both shapes are
|
|
154
|
+
// checked, since a quoted `$."%foo"` would arrive as the string.
|
|
155
|
+
// Terms here are always Vals -- dropUnfilled has removed the
|
|
156
|
+
// nulls, and the dot rules never hand over a raw string -- so the
|
|
157
|
+
// shapes are exactly three: a RefVal (peg is the segment array), a
|
|
158
|
+
// StringVal (peg is the segment), and anything else (a numeric or
|
|
159
|
+
// exact segment, which cannot be an alias name).
|
|
160
|
+
for (const t of terms) {
|
|
161
|
+
const segs = Array.isArray(t.peg) ? t.peg :
|
|
162
|
+
('string' === typeof t.peg ? [t.peg] : []);
|
|
163
|
+
for (const seg of segs) {
|
|
164
|
+
if ('string' === typeof seg && ALIAS_RE.test(seg)) {
|
|
165
|
+
return addsite(new NilVal_1.NilVal({ why: 'alias_in_path' }), r, ctx);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
115
169
|
return addsite(new RefVal_1.RefVal({ peg: terms, prefix }), r, ctx);
|
|
116
170
|
};
|
|
117
171
|
jsonic.options({ comment: { def: null } });
|
|
@@ -183,9 +237,41 @@ let AontuJsonic = function AontuLang(jsonic) {
|
|
|
183
237
|
check: (lex) => {
|
|
184
238
|
// Guard first, on char codes: this hook runs at every text
|
|
185
239
|
// position, and the common case (any run that cannot be a `0d`
|
|
186
|
-
// literal) must cost two char reads and no
|
|
240
|
+
// literal or an alias) must cost two char reads and no
|
|
241
|
+
// allocation.
|
|
187
242
|
const pnt = lex.pnt;
|
|
188
243
|
const src = lex.src;
|
|
244
|
+
// AN ALIAS NAME IS CLAIMED WHOLE, for the same reason the `0d`
|
|
245
|
+
// run below is: the text matcher's ender regexp would otherwise
|
|
246
|
+
// carve `%uint8` at the `%` and emit the sigil as its own token,
|
|
247
|
+
// leaving a bare `uint8` behind -- which is exactly the capture
|
|
248
|
+
// the sigil exists to prevent. Claiming it here, before that
|
|
249
|
+
// ender runs, keeps the name one lexeme.
|
|
250
|
+
//
|
|
251
|
+
// The token's SOURCE is the whole `%name`, which is what makes
|
|
252
|
+
// the same lexeme work in both positions: jsonic keys a pair by
|
|
253
|
+
// the token's source text (`0d1: 5` yields the key `0d1`), so a
|
|
254
|
+
// declaration reads as the key `%uint8`, while a value position
|
|
255
|
+
// calls the function below and gets the reference.
|
|
256
|
+
if (CC_PCT === src.charCodeAt(pnt.sI)) {
|
|
257
|
+
const ares = ALIAS_RE.exec(lex.refwd());
|
|
258
|
+
if (null == ares) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const asrc = ares[0];
|
|
262
|
+
const atkn = lex.token('#VL',
|
|
263
|
+
// AN ALIAS REFERENCE IS A PATH REFERENCE. `%uint8` is
|
|
264
|
+
// `$.%uint8`: root-absolute, one segment, spelled with the
|
|
265
|
+
// sigil the declaration is spelled with. Everything the
|
|
266
|
+
// design asks of it -- order independence, alias-of-alias,
|
|
267
|
+
// redeclaration unifying, cycle refusal spanning both
|
|
268
|
+
// namespaces -- is then the reference machinery already in
|
|
269
|
+
// the language, not a second resolver beside it.
|
|
270
|
+
(r, ctx) => addsite(new RefVal_1.RefVal({ peg: [asrc], absolute: true }), r, ctx), asrc, pnt);
|
|
271
|
+
pnt.sI += asrc.length;
|
|
272
|
+
pnt.cI += asrc.length;
|
|
273
|
+
return { done: true, token: atkn };
|
|
274
|
+
}
|
|
189
275
|
if (CC_0 !== src.charCodeAt(pnt.sI)) {
|
|
190
276
|
return undefined;
|
|
191
277
|
}
|
|
@@ -338,6 +424,13 @@ help isolate the syntax error.`,
|
|
|
338
424
|
move: MoveFuncVal_1.MoveFuncVal,
|
|
339
425
|
path: PathFuncVal_1.PathFuncVal,
|
|
340
426
|
pref: PrefFuncVal_1.PrefFuncVal,
|
|
427
|
+
// First-class paths and the container kinds
|
|
428
|
+
// (docs/design/PATHS.0.md). `path(p)` CAPTURES a path as a value;
|
|
429
|
+
// `path()`, `map()` and `list()` are kinds -- the vacuous
|
|
430
|
+
// constructor call admits its values and defaults to nothing,
|
|
431
|
+
// where the container LITERALS `{}`/`[]` default to empty.
|
|
432
|
+
map: ContainerKindVal_1.MapFuncVal,
|
|
433
|
+
list: ContainerKindVal_1.ListFuncVal,
|
|
341
434
|
close: CloseFuncVal_1.CloseFuncVal,
|
|
342
435
|
open: OpenFuncVal_1.OpenFuncVal,
|
|
343
436
|
super: SuperFuncVal_1.SuperFuncVal,
|
|
@@ -368,16 +461,17 @@ help isolate the syntax error.`,
|
|
|
368
461
|
// record rides the result (Val.deprecation) and canon renders the
|
|
369
462
|
// call back (canonRiders).
|
|
370
463
|
deprecate: DeprecateFuncVal_1.DeprecateFuncVal,
|
|
371
|
-
// G4 phase 1: the identity mark. Written as a conjunct
|
|
372
|
-
// (`id(svc/auth) & {…}`), it resolves to the unit carrying the
|
|
373
|
-
// name, and every node in one evaluation with that name is
|
|
374
|
-
// unified with every other.
|
|
375
|
-
id: IdFuncVal_1.IdFuncVal,
|
|
376
464
|
// G4 phase 2: the checked, typed, LINK-shaped reference. A
|
|
377
|
-
// constraint on a string field: the string must be
|
|
378
|
-
//
|
|
379
|
-
// flows INTO the target. The field keeps the string.
|
|
465
|
+
// constraint on a string field: the string must be a TREE ADDRESS
|
|
466
|
+
// (`$.a.b` or `.b`), the address must resolve, and the optional
|
|
467
|
+
// argument flows INTO the target. The field keeps the string.
|
|
380
468
|
refer: ReferFuncVal_1.ReferFuncVal,
|
|
469
|
+
rel: ReferFuncVal_1.RelFuncVal,
|
|
470
|
+
// RELATIONS P2 (docs/design/RELATIONS.0.md §3.3): the graph
|
|
471
|
+
// atoms, conjoined at the field whose key is the predicate they
|
|
472
|
+
// govern. Lattice-inert; the verdict lands at generation.
|
|
473
|
+
acyclic: GraphAtomVal_1.AcyclicFuncVal,
|
|
474
|
+
inverse: GraphAtomVal_1.InverseFuncVal,
|
|
381
475
|
// G8 phase 1: the generation combinators. `pack` makes one keyed
|
|
382
476
|
// child per child of its data, `each` one list element; both clone
|
|
383
477
|
// their template per destination exactly as a spread does, and both
|
|
@@ -415,6 +509,11 @@ help isolate the syntax error.`,
|
|
|
415
509
|
// RECORDS: `sum(pick($.lines, amountCents))`. Not a clever `each`
|
|
416
510
|
// template -- `each` MEETS each child, and a meet cannot select.
|
|
417
511
|
pick: AggFuncVal_1.PickFuncVal,
|
|
512
|
+
// G9 phase 2: the fold to a STRING. `sum` folds with `add`; this
|
|
513
|
+
// folds with `+`, so it inherits the one number-to-text rule and
|
|
514
|
+
// the language does not grow a second. It is the primitive that
|
|
515
|
+
// turns a bag of computed lines into a file.
|
|
516
|
+
join: AggFuncVal_1.JoinFuncVal,
|
|
418
517
|
};
|
|
419
518
|
// A dangling operator (`a:1|`, `a:$`, `a:*` at end of input) leaves
|
|
420
519
|
// null/undefined unfilled terms. Junction ops drop them (so `a:1&`
|
|
@@ -424,10 +523,8 @@ help isolate the syntax error.`,
|
|
|
424
523
|
const dropUnfilled = (terms) => terms.filter((t) => null != t);
|
|
425
524
|
const incompleteNil = (r, ctx) => addsite(new NilVal_1.NilVal({ why: 'incomplete_expression' }), r, ctx);
|
|
426
525
|
// Build a call from a NAME and the argument terms as the author
|
|
427
|
-
// wrote them
|
|
428
|
-
//
|
|
429
|
-
// the arity check, the comma-group rule and the raw-value conversion
|
|
430
|
-
// are stated once and both spellings get all three.
|
|
526
|
+
// wrote them: the arity check, the comma-group rule and the
|
|
527
|
+
// raw-value conversion, stated once.
|
|
431
528
|
const buildCall = (r, ctx, fname, argterms) => {
|
|
432
529
|
const funcval = funcMap[fname];
|
|
433
530
|
// Arity is known for every built-in, so a surplus or missing
|
|
@@ -457,12 +554,6 @@ help isolate the syntax error.`,
|
|
|
457
554
|
want: arityText(arity[0], arity[1]),
|
|
458
555
|
got: '' + got,
|
|
459
556
|
};
|
|
460
|
-
// The CALL AS WRITTEN rides the refusal (G8 phase 4): a pipe
|
|
461
|
-
// rebuilds `x |> upper()` as `upper(x)`, and the arity it fails
|
|
462
|
-
// on here is the arity of a call one argument short of the one
|
|
463
|
-
// the author actually wrote.
|
|
464
|
-
nil._callname = fname;
|
|
465
|
-
nil._callterms = argterms;
|
|
466
557
|
return addsite(nil, r, ctx);
|
|
467
558
|
}
|
|
468
559
|
}
|
|
@@ -491,38 +582,8 @@ help isolate the syntax error.`,
|
|
|
491
582
|
const val = null == funcval ?
|
|
492
583
|
new NilVal_1.NilVal({ why: 'unknown_function' }) :
|
|
493
584
|
new funcval({ peg: args });
|
|
494
|
-
// The call as written, for the pipe to rebuild from. Parse-time
|
|
495
|
-
// only: nothing downstream reads it, and a clone does not carry it.
|
|
496
|
-
//
|
|
497
|
-
// NOT on a constraint atom that BUILT: an atom with its argument
|
|
498
|
-
// list complete is a residual, not a call waiting for a subject,
|
|
499
|
-
// and `1 |> neq(2,3)` is asking for `1 & neq(2,3)` -- which is
|
|
500
|
-
// what `&` is for. (An atom the arity check REFUSED still carries
|
|
501
|
-
// it, on the nil above: `1 |> min()` is `min(1)`, and that is a
|
|
502
|
-
// call waiting for a subject.) The Go port cannot rebuild a built
|
|
503
|
-
// atom at all -- its residual keeps no atom name -- so this is
|
|
504
|
-
// also what keeps the two ports answering the same thing.
|
|
505
|
-
if (true !== val.isConstraint) {
|
|
506
|
-
val._callname = fname;
|
|
507
|
-
val._callterms = argterms;
|
|
508
|
-
}
|
|
509
585
|
return val;
|
|
510
586
|
};
|
|
511
|
-
// The argument terms `f(...)` would have been written with, had the
|
|
512
|
-
// piped value been written into it. A comma group is one raw-array
|
|
513
|
-
// term: for a POSITIONAL function the group is separate arguments,
|
|
514
|
-
// so the piped value joins them; for a constraint atom the group IS
|
|
515
|
-
// the argument list, so the piped value joins the list instead.
|
|
516
|
-
const pipeTerms = (call, val) => {
|
|
517
|
-
const written = call._callterms;
|
|
518
|
-
const group = 1 === written.length && Array.isArray(written[0]) ?
|
|
519
|
-
written[0] : written;
|
|
520
|
-
if (0 === group.length) {
|
|
521
|
-
return [val];
|
|
522
|
-
}
|
|
523
|
-
return true === POSITIONAL_ARG_FUNCS[call._callname] ?
|
|
524
|
-
[val, ...group] : [[val, ...group]];
|
|
525
|
-
};
|
|
526
587
|
let opmap = {
|
|
527
588
|
'conjunct-infix': (r, ctx, _op, terms) => addsite(new ConjunctVal_1.ConjunctVal({ peg: dropUnfilled(terms) }), r, ctx),
|
|
528
589
|
'disjunct-infix': (r, ctx, _op, terms) => addsite(new DisjunctVal_1.DisjunctVal({ peg: dropUnfilled(terms) }), r, ctx),
|
|
@@ -534,11 +595,47 @@ help isolate the syntax error.`,
|
|
|
534
595
|
'star-prefix': (r, ctx, _op, terms) => {
|
|
535
596
|
if (null == terms[0])
|
|
536
597
|
return incompleteNil(r, ctx);
|
|
598
|
+
// A PREFERENCE MARKS A VALUE, AND A BARE KEY IS NOT ONE.
|
|
599
|
+
// `*a: 1` has no braces, so the prefix took the whole IMPLICIT
|
|
600
|
+
// map as its operand and the document silently became
|
|
601
|
+
// `*{"a":1}` -- `*a: 1, b: 2` became a one-element LIST, losing
|
|
602
|
+
// `b` outright. Neither is anything the author wrote.
|
|
603
|
+
//
|
|
604
|
+
// The accident is confined to the first position of the implicit
|
|
605
|
+
// top-level map, which is the only place no brace has yet
|
|
606
|
+
// committed the rule to a map: `{*a: 1}` and `a: 1, *b: 2` are
|
|
607
|
+
// ALREADY parse errors. This makes the third spelling agree with
|
|
608
|
+
// them rather than inventing a meaning for it.
|
|
609
|
+
//
|
|
610
|
+
// A BRACED operand is untouched, and that is the whole of the
|
|
611
|
+
// distinction: `*{x:1}` and `*[1]` are the real spelling, they
|
|
612
|
+
// are what `*{x:1} | *{y:2}` needs, and the shared spec pins them
|
|
613
|
+
// (11 rows). The open token's own source text is what separates
|
|
614
|
+
// the two -- `{` or `[` for a braced bag, the first key or
|
|
615
|
+
// element for an implicit one.
|
|
616
|
+
const bag = terms[0];
|
|
617
|
+
if ((bag.isMap && '{' !== bag.site.src) ||
|
|
618
|
+
(bag.isList && '[' !== bag.site.src)) {
|
|
619
|
+
return addsite(new NilVal_1.NilVal({ why: 'pref_implicit_bag' }), r, ctx);
|
|
620
|
+
}
|
|
537
621
|
return addsite(new PrefVal_1.PrefVal({ peg: terms[0] }), r, ctx);
|
|
538
622
|
},
|
|
539
623
|
'dollar-prefix': (r, ctx, _op, terms) => {
|
|
540
624
|
if (null == terms[0])
|
|
541
625
|
return incompleteNil(r, ctx);
|
|
626
|
+
// A refusal from the dot rule below (an alias used as a path
|
|
627
|
+
// segment) rides straight through: wrapping it in a VarVal would
|
|
628
|
+
// replace `alias_in_path` with a var whose peg is a nil.
|
|
629
|
+
if (terms[0]?.isNil) {
|
|
630
|
+
return terms[0];
|
|
631
|
+
}
|
|
632
|
+
// `$%foo` -- the sigil directly after the root -- reaches here
|
|
633
|
+
// as the alias reference rather than through the dot rule, and
|
|
634
|
+
// is refused for the same reason.
|
|
635
|
+
if (terms[0] instanceof RefVal_1.RefVal &&
|
|
636
|
+
terms[0].peg.some((seg) => 'string' === typeof seg && ALIAS_RE.test(seg))) {
|
|
637
|
+
return addsite(new NilVal_1.NilVal({ why: 'alias_in_path' }), r, ctx);
|
|
638
|
+
}
|
|
542
639
|
// $.a.b absolute path
|
|
543
640
|
if (terms[0] instanceof RefVal_1.RefVal) {
|
|
544
641
|
terms[0].absolute = true;
|
|
@@ -593,37 +690,6 @@ help isolate the syntax error.`,
|
|
|
593
690
|
return incompleteNil(r, ctx);
|
|
594
691
|
return addsite(val, r, ctx);
|
|
595
692
|
},
|
|
596
|
-
// THE PIPE `|>` (G8 phase 4): parse-time sugar and nothing else.
|
|
597
|
-
// `x |> f(a)` IS `f(x, a)` -- the piped value goes in as the FIRST
|
|
598
|
-
// argument, Elixir-style, because every Aontu call is data-first
|
|
599
|
-
// already (`close(x)`, `pack(data, tmpl)`) and a pipe must read the
|
|
600
|
-
// way the calls it replaces read. It never reaches a Val: by the
|
|
601
|
-
// time the tree exists the call is an ordinary call, which is why
|
|
602
|
-
// canon can never emit the token and the two ports' canon stay
|
|
603
|
-
// byte-identical without either knowing about it.
|
|
604
|
-
'pipe-infix': (r, ctx, _op, terms) => {
|
|
605
|
-
const val = terms[0];
|
|
606
|
-
const call = terms[1];
|
|
607
|
-
if (null == val || null == call)
|
|
608
|
-
return incompleteNil(r, ctx);
|
|
609
|
-
// The right-hand side is a CALL: either one the func handler
|
|
610
|
-
// already built, or one it refused for an arity the pipe is about
|
|
611
|
-
// to satisfy. Both carry what they were written as.
|
|
612
|
-
if (null != call._callname) {
|
|
613
|
-
return buildCall(r, ctx, call._callname, pipeTerms(call, val));
|
|
614
|
-
}
|
|
615
|
-
// ... or a bare NAME, which is the whole point of the short
|
|
616
|
-
// spelling: `x |> upper` is `upper(x)`. A bare word has already
|
|
617
|
-
// become a string VALUE by the time an infix operator sees it, so
|
|
618
|
-
// this is where a string becomes a call.
|
|
619
|
-
if (true === call?.isScalar && 'string' === typeof call.peg &&
|
|
620
|
-
null != funcMap[call.peg]) {
|
|
621
|
-
return buildCall(r, ctx, call.peg, [val]);
|
|
622
|
-
}
|
|
623
|
-
// Anything else is not a call, and a pipe into a non-call is a
|
|
624
|
-
// mistake in the source rather than a value.
|
|
625
|
-
return addsite(new NilVal_1.NilVal({ why: 'pipe_target' }), r, ctx);
|
|
626
|
-
},
|
|
627
693
|
'func-paren': (r, ctx, _op, terms) => {
|
|
628
694
|
let val = terms[1];
|
|
629
695
|
const fname = terms[0];
|
|
@@ -651,13 +717,6 @@ help isolate the syntax error.`,
|
|
|
651
717
|
'disjunct': {
|
|
652
718
|
infix: true, src: '|', left: 14_000_000, right: 15_000_000
|
|
653
719
|
},
|
|
654
|
-
// G8 phase 4: the pipe. LOOSEST of all the infix operators, so
|
|
655
|
-
// `a & b |> f` pipes the whole meet and not just `b` -- a pipe
|
|
656
|
-
// reads as "and then", which is a statement about everything to
|
|
657
|
-
// its left. Kept in lock-step with the op table in go/lang.go.
|
|
658
|
-
'pipe-infix': {
|
|
659
|
-
infix: true, src: '|>', left: 12_000_000, right: 13_000_000
|
|
660
|
-
},
|
|
661
720
|
'plus-infix': {
|
|
662
721
|
src: '+',
|
|
663
722
|
infix: true,
|
|
@@ -740,6 +799,7 @@ help isolate the syntax error.`,
|
|
|
740
799
|
const TX = jsonic.token.TX;
|
|
741
800
|
const NR = jsonic.token.NR;
|
|
742
801
|
const QM = jsonic.token.QM;
|
|
802
|
+
const VL = jsonic.token.VL;
|
|
743
803
|
const OPTKEY = [TX, ST, NR];
|
|
744
804
|
jsonic.rule('expr', (rs) => {
|
|
745
805
|
rs.close([
|
|
@@ -861,6 +921,7 @@ help isolate the syntax error.`,
|
|
|
861
921
|
])
|
|
862
922
|
.bc((r, ctx) => {
|
|
863
923
|
const optionalKeys = r.u.aontu_optional_keys ?? [];
|
|
924
|
+
const aliasKeys = r.u.aontu_alias_keys ?? [];
|
|
864
925
|
let mo = r.node;
|
|
865
926
|
// An elided value (`a:`) leaves a raw null/undefined that never
|
|
866
927
|
// passed through the val rule. It is REFUSED rather than made a
|
|
@@ -920,12 +981,14 @@ help isolate the syntax error.`,
|
|
|
920
981
|
// TODO: needs addpath?
|
|
921
982
|
let mopv = new MapVal_1.MapVal({ peg: mop });
|
|
922
983
|
mopv.optionalKeys = optionalKeys;
|
|
984
|
+
mopv.aliasKeys = aliasKeys;
|
|
923
985
|
r.node =
|
|
924
986
|
addsite(new ConjunctVal_1.ConjunctVal({ peg: [mopv, ...mo.___merge] }), r, ctx);
|
|
925
987
|
}
|
|
926
988
|
else {
|
|
927
989
|
r.node = addsite(new MapVal_1.MapVal({ peg: mo }), r, ctx);
|
|
928
990
|
r.node.optionalKeys = optionalKeys;
|
|
991
|
+
r.node.aliasKeys = aliasKeys;
|
|
929
992
|
}
|
|
930
993
|
return undefined;
|
|
931
994
|
})
|
|
@@ -1047,6 +1110,31 @@ help isolate the syntax error.`,
|
|
|
1047
1110
|
})
|
|
1048
1111
|
.bc((rule) => {
|
|
1049
1112
|
// TRAVERSE PARENTS TO GET PATH
|
|
1113
|
+
// A DECLARATION IS A PAIR WHOSE KEY IS AN ALIAS NAME. The lexer
|
|
1114
|
+
// claims `%name` whole and hands it over as a #VL token whose
|
|
1115
|
+
// SOURCE is the name, so the key TEXT alone cannot be the test:
|
|
1116
|
+
// a quoted `"%a": 1` is an ordinary key that merely starts with
|
|
1117
|
+
// the sigil, and erasing that would be wrong. The token is what
|
|
1118
|
+
// separates them.
|
|
1119
|
+
//
|
|
1120
|
+
// Recorded on the enclosing map, never on the value, and that is
|
|
1121
|
+
// the point: a reference COPIES the value it resolves to, so a
|
|
1122
|
+
// mark riding the value would erase the referring field too.
|
|
1123
|
+
// Being a property of the map is also what carries it through a
|
|
1124
|
+
// meet, the way optional keys are carried.
|
|
1125
|
+
const ktkn = rule.o0;
|
|
1126
|
+
if (null != ktkn && VL === ktkn.tin && ALIAS_RE.test('' + ktkn.src)) {
|
|
1127
|
+
const holder = rule.parent;
|
|
1128
|
+
const aname = '' + ktkn.src;
|
|
1129
|
+
// Always recorded here; whether the map is ALLOWED to carry
|
|
1130
|
+
// declarations is decided on the VALUE (MapVal.unify), not at
|
|
1131
|
+
// the parse. The parse cannot see it: an INCLUDED file's
|
|
1132
|
+
// declarations are at the root of their own text, and only
|
|
1133
|
+
// once the loaded map is placed does it become apparent that
|
|
1134
|
+
// root is not the document's.
|
|
1135
|
+
holder.u.aontu_alias_keys = (holder.u.aontu_alias_keys || []);
|
|
1136
|
+
holder.u.aontu_alias_keys.push(aname);
|
|
1137
|
+
}
|
|
1050
1138
|
if (rule.u.spread) {
|
|
1051
1139
|
rule.node[type_1.SPREAD] =
|
|
1052
1140
|
(rule.node[type_1.SPREAD] || { o: rule.o0.src, v: [] });
|
|
@@ -1083,7 +1171,10 @@ help isolate the syntax error.`,
|
|
|
1083
1171
|
s: [QM, CL],
|
|
1084
1172
|
c: (r) => r.prev.u.aontu_optional,
|
|
1085
1173
|
p: 'val',
|
|
1086
|
-
u: {
|
|
1174
|
+
u: {
|
|
1175
|
+
spread: true, done: true, list: true, pair: true,
|
|
1176
|
+
aontu_optional_elem: true,
|
|
1177
|
+
},
|
|
1087
1178
|
a: (r) => {
|
|
1088
1179
|
pairkey(r.prev);
|
|
1089
1180
|
r.u.key = r.prev.u.key;
|
|
@@ -1091,18 +1182,17 @@ help isolate the syntax error.`,
|
|
|
1091
1182
|
},
|
|
1092
1183
|
g: 'aontu-optional-elem'
|
|
1093
1184
|
},
|
|
1094
|
-
// A PLAIN pair in list position
|
|
1095
|
-
//
|
|
1096
|
-
//
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1099
|
-
//
|
|
1100
|
-
//
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
//
|
|
1104
|
-
//
|
|
1105
|
-
// and it made the two ports disagree on generate as well as canon.
|
|
1185
|
+
// A PLAIN pair in list position IS A SINGLE-KEY MAP ELEMENT:
|
|
1186
|
+
// `[a:1, b:2]` is `[{a:1}, {b:2}]` (the rule @tabnas/jsonic
|
|
1187
|
+
// spells as `list.pair`). This REVERSES issue #40's "a pair is
|
|
1188
|
+
// not an element": that rule was chosen because jsonic wrote
|
|
1189
|
+
// the pair at `node[key]` -- an array PROPERTY that never
|
|
1190
|
+
// showed up for a text key and an INDEX for a numeric one --
|
|
1191
|
+
// and inert beat that incoherence. But inert was itself a
|
|
1192
|
+
// silent drop: `x: [a:1, b:2]` evaluated to `x: []`, the
|
|
1193
|
+
// author's data gone at exit 0. The element is built in the
|
|
1194
|
+
// bc below, where the value is already a Val; the snapshot
|
|
1195
|
+
// still neutralises jsonic's raw slot write first.
|
|
1106
1196
|
{
|
|
1107
1197
|
s: [OPTKEY, CL], p: 'val',
|
|
1108
1198
|
u: { spread: true, done: true, list: true, pair: true },
|
|
@@ -1113,26 +1203,291 @@ help isolate the syntax error.`,
|
|
|
1113
1203
|
g: 'aontu-plain-pair-elem'
|
|
1114
1204
|
}
|
|
1115
1205
|
])
|
|
1116
|
-
|
|
1206
|
+
// NOTE: manually adjust path - the twin of the `pair` rule's hook
|
|
1207
|
+
// above, and for the same reason, one layer down.
|
|
1208
|
+
//
|
|
1209
|
+
// Every alt above contributes NO element: a `&:` spread is a
|
|
1210
|
+
// constraint on the elements, and a `k:v` pair in list position is
|
|
1211
|
+
// simply not one (the `aontu-plain-pair-elem` note above). The array
|
|
1212
|
+
// slot they briefly occupy is already given back by
|
|
1213
|
+
// restorePairSlot. The PATH index was not: @tabnas/path's
|
|
1214
|
+
// `@elem-ao` increments `r.k.index` for every elem rule it sees, so
|
|
1215
|
+
// each of these stole an index and every later element's path was
|
|
1216
|
+
// one too high — `[&: integer, 10, 20, "bad"]` reported the bad
|
|
1217
|
+
// value at `$.l.3` while `aontu get $.l.2` returned it, and on a
|
|
1218
|
+
// one-element list the path pointed off the end. Generation was
|
|
1219
|
+
// never wrong, which is why nothing caught it: the array is right
|
|
1220
|
+
// and only the labels on it were shifted (BUGS.md 44).
|
|
1221
|
+
//
|
|
1222
|
+
// Rewinding here rather than in the plugin keeps the plugin's rule
|
|
1223
|
+
// ("in an array, the path property is the element index") true —
|
|
1224
|
+
// these alts are the aontu-specific exceptions to what counts as an
|
|
1225
|
+
// element, so the correction belongs with the grammar that
|
|
1226
|
+
// introduces them. The child is re-pathed because the plugin has
|
|
1227
|
+
// already stamped it with the index being given back: a spread
|
|
1228
|
+
// takes the `'&'` segment its map twin takes, and a pair takes its
|
|
1229
|
+
// key, as a map entry would.
|
|
1230
|
+
.ao((r) => {
|
|
1231
|
+
// A pair IS an element now, so it keeps the index @tabnas/path
|
|
1232
|
+
// gave it, and its VALUE is pathed through both the index and
|
|
1233
|
+
// the key (`[a: $.nope]` fails at $.l.0.a). Only the `&:`
|
|
1234
|
+
// spread still contributes no element and gives its index back
|
|
1235
|
+
// (BUGS.md 44).
|
|
1236
|
+
if (0 < r.d && r.u.spread && !r.u.pair) {
|
|
1237
|
+
r.k.index = r.k.index - 1;
|
|
1238
|
+
const seg = '&';
|
|
1239
|
+
r.child.k.path = [...r.k.path, seg];
|
|
1240
|
+
r.child.k.key = seg;
|
|
1241
|
+
}
|
|
1242
|
+
else if (0 < r.d && r.u.pair) {
|
|
1243
|
+
// The element's index is the array length: everything before
|
|
1244
|
+
// it is already pushed, and the pair's own map is pushed at
|
|
1245
|
+
// close. `r.k.index` is not usable here -- the path plugin
|
|
1246
|
+
// counts only the elements it pushes itself, and this one is
|
|
1247
|
+
// aontu's.
|
|
1248
|
+
const seg = '' + r.u.key;
|
|
1249
|
+
r.child.k.path =
|
|
1250
|
+
[...r.k.path, '' + (r.node?.length ?? 0), seg];
|
|
1251
|
+
r.child.k.key = seg;
|
|
1252
|
+
}
|
|
1253
|
+
})
|
|
1254
|
+
.bc((rule, ctx) => {
|
|
1117
1255
|
// TRAVERSE PARENTS TO GET PATH
|
|
1118
|
-
|
|
1256
|
+
// Only the `&:` alternative is a SPREAD. All four alts above set
|
|
1257
|
+
// `spread: true` -- it is what marks them as contributing no
|
|
1258
|
+
// element -- so this guard needs the narrower test, and `pair`
|
|
1259
|
+
// is what distinguishes a `k:v` in list position from a spread.
|
|
1260
|
+
//
|
|
1261
|
+
// Without it a pair BUILT the spread record, with `o` taken from
|
|
1262
|
+
// its own key rather than '&': `[x:1, &:integer, "bad"]` left
|
|
1263
|
+
// `{o:'x', v:[1, integer]}`, and ListVal's `'&' === spread.o`
|
|
1264
|
+
// then discarded the real constraint -- so the element spread
|
|
1265
|
+
// was silently dropped and the bad value generated (BUGS.md 46).
|
|
1266
|
+
// A pair alone did it too: `[x:1, 10]` produced a spread record
|
|
1267
|
+
// out of nothing.
|
|
1268
|
+
if (rule.u.spread && !rule.u.pair) {
|
|
1119
1269
|
rule.node[type_1.SPREAD] =
|
|
1120
1270
|
(rule.node[type_1.SPREAD] || { o: rule.o0.src, v: [] });
|
|
1121
1271
|
rule.node[type_1.SPREAD].v.push(rule.child.node);
|
|
1122
1272
|
}
|
|
1273
|
+
// The slot is given back BEFORE the element is added: the
|
|
1274
|
+
// restore undoes jsonic's raw write (a property for a text
|
|
1275
|
+
// key, an INDEX for a numeric one -- restoring length is what
|
|
1276
|
+
// keeps `[1:2]` from padding with a null), and the push then
|
|
1277
|
+
// appends cleanly after it.
|
|
1123
1278
|
restorePairSlot(rule);
|
|
1279
|
+
// THE SINGLE-KEY MAP ELEMENT, for both pair spellings. The
|
|
1280
|
+
// value is a Val already (`p: 'val'`), so the map is built
|
|
1281
|
+
// exactly as the map rule builds one -- and an elided value
|
|
1282
|
+
// (`[a:]`) is refused exactly as the map rule refuses one
|
|
1283
|
+
// (issue #48): a key with nothing after the colon is a
|
|
1284
|
+
// mistake, not an empty value.
|
|
1285
|
+
if (true === rule.u.pair) {
|
|
1286
|
+
const key = '' + rule.u.key;
|
|
1287
|
+
let v = rule.child.node;
|
|
1288
|
+
if (null == v) {
|
|
1289
|
+
v = addsite(new NilVal_1.NilVal({ why: 'elided_value' }), rule, ctx);
|
|
1290
|
+
v.path = [...(rule.k?.path ?? []),
|
|
1291
|
+
'' + rule.node.length, key];
|
|
1292
|
+
}
|
|
1293
|
+
const mv = addsite(new MapVal_1.MapVal({ peg: { [key]: v } }), rule, ctx);
|
|
1294
|
+
// `[a?: 1]` is `[{a?: 1}]`: the key is optional IN the
|
|
1295
|
+
// element, so the two spellings stay one rule apart rather
|
|
1296
|
+
// than two behaviours apart.
|
|
1297
|
+
if (true === rule.u.aontu_optional_elem) {
|
|
1298
|
+
mv.optionalKeys = [key];
|
|
1299
|
+
}
|
|
1300
|
+
rule.node.push(mv);
|
|
1301
|
+
}
|
|
1124
1302
|
return undefined;
|
|
1125
1303
|
})
|
|
1126
1304
|
.close([{ s: [CJ, CL], r: 'elem', b: 2, g: 'spread,json,more' }]);
|
|
1127
1305
|
return rs;
|
|
1128
1306
|
});
|
|
1129
1307
|
};
|
|
1308
|
+
// INCLUDE_KINDS IS THE RULE FOR WHAT AN INCLUDE MEANS (ADR-012,
|
|
1309
|
+
// use-cases/BUGS.md §49). An extension is on this list or it is not
|
|
1310
|
+
// read at all, and its entry says WHICH OF TWO THINGS the file is.
|
|
1311
|
+
//
|
|
1312
|
+
// `source` — Aontu, with everything the language has: types, defaults,
|
|
1313
|
+
// references, constraints, its own includes. Two extensions, and they
|
|
1314
|
+
// are the ones this project owns.
|
|
1315
|
+
//
|
|
1316
|
+
// A FORMAT NAME — configuration DATA, parsed by that format's own
|
|
1317
|
+
// parser into the JSON value it denotes, which then becomes Aontu
|
|
1318
|
+
// values like any other data. Every one of these formats maps onto
|
|
1319
|
+
// JSON, which is why one word covers them: a `.toml` file is a map of
|
|
1320
|
+
// scalars, lists and maps, and so is the `.aon` file that unifies with
|
|
1321
|
+
// it. What the format does NOT get is the language — a `&` in a YAML
|
|
1322
|
+
// file is a YAML anchor, not a spread key, because the YAML parser
|
|
1323
|
+
// reads it, not this one.
|
|
1324
|
+
//
|
|
1325
|
+
// The parsers are @tabnas's, one per format, and the Go port uses the
|
|
1326
|
+
// same ones (ADR-001): the two implementations agree because they are
|
|
1327
|
+
// running the same grammar, not because two hand-written readers were
|
|
1328
|
+
// kept in step.
|
|
1329
|
+
//
|
|
1330
|
+
// This table and go/source.go's includeKinds are the same table.
|
|
1331
|
+
const INCLUDE_KINDS = {
|
|
1332
|
+
aon: 'source',
|
|
1333
|
+
aontu: 'source',
|
|
1334
|
+
json: 'json',
|
|
1335
|
+
// JSON-LD is JSON: a `@context` is a key like any other here, and
|
|
1336
|
+
// what it MEANS is the vocabulary's business, not the reader's.
|
|
1337
|
+
jsonld: 'json',
|
|
1338
|
+
jsonc: 'jsonc',
|
|
1339
|
+
json5: 'json5',
|
|
1340
|
+
jsonic: 'jsonic',
|
|
1341
|
+
jsc: 'jsonic',
|
|
1342
|
+
toml: 'toml',
|
|
1343
|
+
yaml: 'yaml',
|
|
1344
|
+
yml: 'yaml',
|
|
1345
|
+
ini: 'ini',
|
|
1346
|
+
};
|
|
1347
|
+
// `.csv` IS DELIBERATELY ABSENT, and the reason is ADR-001 rather than
|
|
1348
|
+
// taste. The two ports' CSV parsers disagree about what a CSV file IS:
|
|
1349
|
+
// one answers header-keyed records with string fields, the other raw
|
|
1350
|
+
// rows including the header, with numbers parsed. Admitting it would
|
|
1351
|
+
// admit a divergence into the one thing this project refuses to have
|
|
1352
|
+
// one in. Recorded in ADR-012 and pinned by file.tsv's load-ext-csv.
|
|
1353
|
+
// The multisource kind of a path: the LAST segment's extension, without
|
|
1354
|
+
// its dot, lowercased -- `''` for a name that has none. The rule is
|
|
1355
|
+
// @tabnas/multisource's own extKind (and Go's filepath.Ext), copied
|
|
1356
|
+
// rather than imported because it decides what a source IS: a dot in a
|
|
1357
|
+
// parent folder (`/my.app/conf`) must not read as an extension.
|
|
1358
|
+
function extKindOf(full) {
|
|
1359
|
+
const seg = full.match(/[^\\/]*$/)[0];
|
|
1360
|
+
return (seg.match(/\.([^.]*)$/) || ['', ''])[1].toLowerCase();
|
|
1361
|
+
}
|
|
1362
|
+
// The refusal message, naming the extension -- because the extension is
|
|
1363
|
+
// the whole reason, and a reader told only "not readable" has to guess
|
|
1364
|
+
// which part of the path the engine objected to. Byte-identical to Go's
|
|
1365
|
+
// extensionMsg.
|
|
1366
|
+
function extensionMsg(path, ext) {
|
|
1367
|
+
const which = '' === ext ? 'no extension' : 'extension: .' + ext;
|
|
1368
|
+
return 'include not readable: ' + path + ' (' + which + ')';
|
|
1369
|
+
}
|
|
1370
|
+
// THE RULE ALSO HOLDS FOR A RESOLVER THIS ENGINE DID NOT WRITE.
|
|
1371
|
+
// gateExtension refuses an unlisted extension inside makeModelResolver,
|
|
1372
|
+
// which is the default; a HOST may supply its own through
|
|
1373
|
+
// `AontuOptions.resolver`, and that one has never heard of
|
|
1374
|
+
// INCLUDE_KINDS. Without this the host's resolution would fall to
|
|
1375
|
+
// multisource's own default for an unnamed kind, which hands the file
|
|
1376
|
+
// back as TEXT — or, for `.js`, EXECUTES it. So the two roads end in
|
|
1377
|
+
// one place: whatever chose the source, an extension off the list is
|
|
1378
|
+
// refused with the same code and the same message.
|
|
1379
|
+
const refuseProcessor = (res) => {
|
|
1380
|
+
// `full` is the one part a host resolution may leave out -- it is the
|
|
1381
|
+
// path the resolver CHOSE, and a resolver that answers from something
|
|
1382
|
+
// other than a filesystem need not have one. The written path always
|
|
1383
|
+
// reaches here, so it is the fallback.
|
|
1384
|
+
const err = new Error(extensionMsg(res.path, extKindOf(res.full ?? res.path)));
|
|
1385
|
+
err.code = 'include_extension';
|
|
1386
|
+
throw err;
|
|
1387
|
+
};
|
|
1388
|
+
// ONE READER PER FORMAT, BUILT ONCE. These are stateless parsers and
|
|
1389
|
+
// building a jsonic instance is not free, so they are made at module
|
|
1390
|
+
// load rather than per include. The file name is passed through so a
|
|
1391
|
+
// syntax error inside an included `.toml` names the `.toml`.
|
|
1392
|
+
const DATA_READERS = (() => {
|
|
1393
|
+
const viaPlugin = (plugin) => {
|
|
1394
|
+
const jsonic = jsonic_1.Jsonic.make().use(plugin);
|
|
1395
|
+
return (src, fileName) => jsonic(src, { fileName });
|
|
1396
|
+
};
|
|
1397
|
+
const toml = viaPlugin(toml_1.Toml);
|
|
1398
|
+
// The strict RFC 8259 reader is its own parser rather than a
|
|
1399
|
+
// plugin, and it is `make().parse` rather than the module's bare
|
|
1400
|
+
// `parse`: only the instance carries the meta bag, and without it
|
|
1401
|
+
// a syntax error in an included `.json` says `<no-file>`.
|
|
1402
|
+
const json = (0, json_1.make)();
|
|
1403
|
+
return {
|
|
1404
|
+
json: (src, fileName) => json.parse(src, { fileName }),
|
|
1405
|
+
jsonc: viaPlugin(jsonc_1.Jsonc),
|
|
1406
|
+
json5: viaPlugin(json5_1.Json5),
|
|
1407
|
+
// Plain jsonic needs no plugin: it IS the base parser.
|
|
1408
|
+
jsonic: (src, fileName) => (0, jsonic_1.Jsonic)(src, { fileName }),
|
|
1409
|
+
toml: (src, fileName) => tomlDates(toml(src, fileName)),
|
|
1410
|
+
yaml: viaPlugin(yaml_1.Yaml),
|
|
1411
|
+
ini: viaPlugin(ini_1.Ini),
|
|
1412
|
+
};
|
|
1413
|
+
})();
|
|
1414
|
+
/**
|
|
1415
|
+
* A TOML document with its dates as the TEXT they were written as.
|
|
1416
|
+
*
|
|
1417
|
+
* TOML HAS DATES AND JSON DOES NOT, so the reader cannot hand one over
|
|
1418
|
+
* as itself: it answers with a marker object carrying the kind and the
|
|
1419
|
+
* source text. The value that reaches a document is that TEXT, which is
|
|
1420
|
+
* what a JSON document carries for a date anyway — and it is what the
|
|
1421
|
+
* Go port produces too, from a `*TomlTime` holding those same two
|
|
1422
|
+
* fields (`dataToValDepth`, go/source.go). Without this the same file
|
|
1423
|
+
* is a nested map in one port and a string in the other, which is the
|
|
1424
|
+
* class of divergence ADR-012 exists to stop.
|
|
1425
|
+
*
|
|
1426
|
+
* The guard is exact — one key, `__toml__`, holding a `kind` and a
|
|
1427
|
+
* `src` string — so a document whose own data happens to use the name
|
|
1428
|
+
* passes through untouched.
|
|
1429
|
+
*/
|
|
1430
|
+
function tomlDates(node) {
|
|
1431
|
+
if (Array.isArray(node)) {
|
|
1432
|
+
return node.map(tomlDates);
|
|
1433
|
+
}
|
|
1434
|
+
if (null === node || 'object' !== typeof node) {
|
|
1435
|
+
return node;
|
|
1436
|
+
}
|
|
1437
|
+
const keys = Object.keys(node);
|
|
1438
|
+
const mark = node.__toml__;
|
|
1439
|
+
if (1 === keys.length && '__toml__' === keys[0] && null != mark &&
|
|
1440
|
+
'string' === typeof mark.kind && 'string' === typeof mark.src) {
|
|
1441
|
+
return mark.src;
|
|
1442
|
+
}
|
|
1443
|
+
const out = {};
|
|
1444
|
+
for (const k of keys) {
|
|
1445
|
+
out[k] = tomlDates(node[k]);
|
|
1446
|
+
}
|
|
1447
|
+
return out;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Read one included file as DATA in the named format.
|
|
1451
|
+
*
|
|
1452
|
+
* The parser hands back the JSON value the file denotes — plain maps,
|
|
1453
|
+
* lists and scalars — and rawToVal turns that into Vals. THE
|
|
1454
|
+
* CONVERSION HAPPENS HERE, not at the top level, because an include is
|
|
1455
|
+
* usually not at the top level: `a: @"conf.toml"` puts the value under
|
|
1456
|
+
* a key, where a raw JavaScript object is something the tree cannot
|
|
1457
|
+
* unify with (the crash that was BUGS §49b).
|
|
1458
|
+
*/
|
|
1459
|
+
const dataProcessor = (format) => (res) => {
|
|
1460
|
+
res.val = rawToVal(DATA_READERS[format](res.src, res.path));
|
|
1461
|
+
};
|
|
1462
|
+
/**
|
|
1463
|
+
* The multisource processor map, built FROM the include table so the
|
|
1464
|
+
* two cannot drift: every extension the table names gets the reader
|
|
1465
|
+
* the table names for it, and the two kinds that are not in the table
|
|
1466
|
+
* refuse.
|
|
1467
|
+
*/
|
|
1468
|
+
function includeProcessors() {
|
|
1469
|
+
const map = {
|
|
1470
|
+
// multisource's fallback for an extension no entry names, so it is
|
|
1471
|
+
// the one that catches whatever the resolver's gate did not.
|
|
1472
|
+
'': refuseProcessor,
|
|
1473
|
+
// ... and the one upstream default that would EXECUTE the file.
|
|
1474
|
+
js: refuseProcessor,
|
|
1475
|
+
};
|
|
1476
|
+
const source = (0, jsonic_2.makeJsonicProcessor)();
|
|
1477
|
+
for (const kind of Object.keys(INCLUDE_KINDS)) {
|
|
1478
|
+
const format = INCLUDE_KINDS[kind];
|
|
1479
|
+
map[kind] = 'source' === format ? source : dataProcessor(format);
|
|
1480
|
+
}
|
|
1481
|
+
return map;
|
|
1482
|
+
}
|
|
1130
1483
|
// SECURITY: under the DEFAULT ('system') include capability this
|
|
1131
|
-
// resolver reads any file
|
|
1132
|
-
//
|
|
1133
|
-
//
|
|
1134
|
-
//
|
|
1135
|
-
//
|
|
1484
|
+
// resolver reads any file the process can reach — @"path" follows
|
|
1485
|
+
// relative paths (`@"../../etc/passwd.aon"`) and symlinks — so treat
|
|
1486
|
+
// opening an untrusted source as reading your disk. It no longer RUNS
|
|
1487
|
+
// one: @"pkg" could require() an arbitrary installed module until
|
|
1488
|
+
// ADR-012, which refuses a `.js` entry point by the same rule that
|
|
1489
|
+
// refuses `.txt`. The trust profile (G5, docs/trust.md) is the
|
|
1490
|
+
// confinement surface: `trust.include` of
|
|
1136
1491
|
// 'none', `{ mem }` or `{ root }` restricts what `@"..."` may resolve,
|
|
1137
1492
|
// and a denied resolution is a deterministic parse-stage
|
|
1138
1493
|
// `include_denied` error.
|
|
@@ -1214,6 +1569,25 @@ function makeModelResolver(options) {
|
|
|
1214
1569
|
err.code = 'include_denied';
|
|
1215
1570
|
throw err;
|
|
1216
1571
|
};
|
|
1572
|
+
// AN UNREADABLE EXTENSION THROWS, exactly as a denial does, and for
|
|
1573
|
+
// the same reason: a bare-member include (`@"notes.txt"` at the top
|
|
1574
|
+
// of a file) MERGES into the enclosing map, and a nil contributes no
|
|
1575
|
+
// keys, so an injected refusal would vanish and leave a plausible,
|
|
1576
|
+
// silently-partial document. Lang.parse turns the throw into the
|
|
1577
|
+
// parse-stage `include_extension` nil.
|
|
1578
|
+
const refuseExtension = (path, full) => {
|
|
1579
|
+
const err = new Error(extensionMsg(path, extKindOf(full)));
|
|
1580
|
+
err.code = 'include_extension';
|
|
1581
|
+
throw err;
|
|
1582
|
+
};
|
|
1583
|
+
// The gate every leg that RESOLVES A NAME passes through. The std and
|
|
1584
|
+
// module legs do not: both state `kind: 'aon'` because what they
|
|
1585
|
+
// serve is Aontu source by construction, not by its spelling.
|
|
1586
|
+
const gateExtension = (path, full) => {
|
|
1587
|
+
if (undefined === INCLUDE_KINDS[extKindOf(full)]) {
|
|
1588
|
+
refuseExtension(path, full);
|
|
1589
|
+
}
|
|
1590
|
+
};
|
|
1217
1591
|
// The user cache: whatever the host named, else the platform rule
|
|
1218
1592
|
// (`modCacheDir`, ts/src/mod.ts) the tooling writes by.
|
|
1219
1593
|
const modCache = (opts) => {
|
|
@@ -1280,6 +1654,11 @@ function makeModelResolver(options) {
|
|
|
1280
1654
|
let res = memResolver(path, popts, rule, ctx, jsonic);
|
|
1281
1655
|
res.path = path;
|
|
1282
1656
|
if (res.found) {
|
|
1657
|
+
// THE EXTENSION DECIDES HERE TOO. A virtual file set is still a
|
|
1658
|
+
// file set: its keys carry extensions, and the same rule has to
|
|
1659
|
+
// read them, or the mem capability becomes a way to include what
|
|
1660
|
+
// the filesystem would refuse.
|
|
1661
|
+
gateExtension(path, res.full ?? path);
|
|
1283
1662
|
record(ctx, res.full ?? path, 'mem');
|
|
1284
1663
|
return res;
|
|
1285
1664
|
}
|
|
@@ -1328,6 +1707,10 @@ function makeModelResolver(options) {
|
|
|
1328
1707
|
if (null != rootDir && outsideRoot(rootDir, full)) {
|
|
1329
1708
|
deny(path);
|
|
1330
1709
|
}
|
|
1710
|
+
// After the trust check, not before: a file outside the
|
|
1711
|
+
// confinement root is denied whatever it is called, and answering
|
|
1712
|
+
// "extension" there would say the file exists.
|
|
1713
|
+
gateExtension(path, full);
|
|
1331
1714
|
// The warning window for the staged default flip (G5 phase 6):
|
|
1332
1715
|
// under 'system', the CLI supplies trustWarn and the entry root,
|
|
1333
1716
|
// and every resolution escaping that root names the flag a future
|
|
@@ -1350,6 +1733,7 @@ function makeModelResolver(options) {
|
|
|
1350
1733
|
res = pkgResolver(path, popts, rule, ctx, jsonic);
|
|
1351
1734
|
res.path = path;
|
|
1352
1735
|
if (res.found) {
|
|
1736
|
+
gateExtension(path, res.full);
|
|
1353
1737
|
if (null != options.trustWarn) {
|
|
1354
1738
|
options.trustWarn('pkg', res.full);
|
|
1355
1739
|
}
|
|
@@ -1360,62 +1744,60 @@ function makeModelResolver(options) {
|
|
|
1360
1744
|
return res;
|
|
1361
1745
|
};
|
|
1362
1746
|
}
|
|
1363
|
-
//
|
|
1747
|
+
// THE SIGNATURE REGISTRY (docs/design/SIGNATURES.0.md). The call
|
|
1748
|
+
// surface is DECLARED in test/spec/signature.tsv and parsed by the
|
|
1749
|
+
// signature grammar (ts/src/sig.ts) from the build-time-inlined copy;
|
|
1750
|
+
// the arity table and the positional set below are DERIVED from the
|
|
1751
|
+
// parsed registry (funcSig, ts/src/sig.ts), so the declaration is the
|
|
1752
|
+
// one source. go/func.go derives the same two tables from the same
|
|
1753
|
+
// text.
|
|
1364
1754
|
// The functions whose comma-separated arguments are distinct POSITIONS
|
|
1365
1755
|
// rather than one argument list. See the func-paren handler above: this
|
|
1366
1756
|
// is the set whose comma group is expanded back into separate `peg`
|
|
1367
|
-
// entries.
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1757
|
+
// entries. Derived: two or more declared argument slots, excluding the
|
|
1758
|
+
// residual producers (`constraint` results) -- the constraint atoms
|
|
1759
|
+
// make the same expansion in their own constructor (`atomArgs`,
|
|
1760
|
+
// ConstraintVal.ts, deliberately before the settled check), which is
|
|
1761
|
+
// why they are not in this set; `must` is the load-bearing example.
|
|
1762
|
+
// Arithmetic is here because `sub` is not commutative: `sub(a, b)`
|
|
1763
|
+
// reaching the engine as one two-element list would lose which is
|
|
1764
|
+
// which.
|
|
1765
|
+
const POSITIONAL_ARG_FUNCS = {};
|
|
1766
|
+
for (const name in sig_1.funcSig) {
|
|
1767
|
+
if (2 <= sig_1.funcSig[name].args.length && 'constraint' !== sig_1.funcSig[name].out) {
|
|
1768
|
+
POSITIONAL_ARG_FUNCS[name] = true;
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1377
1771
|
// [min, max]; a max of -1 is unbounded. Every name in funcMap has an
|
|
1378
1772
|
// entry, and the arity is a property of the language rather than of
|
|
1379
|
-
// either port -- go/func.go
|
|
1380
|
-
//
|
|
1381
|
-
//
|
|
1382
|
-
//
|
|
1383
|
-
//
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
const
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
match: [3, -1],
|
|
1408
|
-
// Two operands, always. Arithmetic has no variadic reading that is
|
|
1409
|
-
// not a fold, and a fold is the recursion this language refuses.
|
|
1410
|
-
add: [2, 2], sub: [2, 2], mul: [2, 2],
|
|
1411
|
-
div: [2, 2], mod: [2, 2], rem: [2, 2],
|
|
1412
|
-
// One bag. The operation is fixed, so there is nothing else to pass:
|
|
1413
|
-
// a fold's second argument is a FUNCTION, and this language has none
|
|
1414
|
-
// to give it.
|
|
1415
|
-
sum: [1, 1], least: [1, 1], greatest: [1, 1],
|
|
1416
|
-
// The bag, and the key to take from each of its children.
|
|
1417
|
-
pick: [2, 2],
|
|
1418
|
-
};
|
|
1773
|
+
// either port -- go/func.go derives the same table. A required slot
|
|
1774
|
+
// counts toward the minimum; a rest slot makes the maximum unbounded
|
|
1775
|
+
// and counts its group size (one, for a plain rest type) toward the
|
|
1776
|
+
// minimum, which is what gives `match` its floor of three and `neq`
|
|
1777
|
+
// its floor of one.
|
|
1778
|
+
function sigArity(sig) {
|
|
1779
|
+
let min = 0;
|
|
1780
|
+
let max = 0;
|
|
1781
|
+
for (const a of sig.args) {
|
|
1782
|
+
if (true === a.rest) {
|
|
1783
|
+
min += undefined === a.group ? 1 : a.group.length;
|
|
1784
|
+
max = -1;
|
|
1785
|
+
}
|
|
1786
|
+
else {
|
|
1787
|
+
if (true !== a.opt) {
|
|
1788
|
+
min++;
|
|
1789
|
+
}
|
|
1790
|
+
if (-1 !== max) {
|
|
1791
|
+
max++;
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
return [min, max];
|
|
1796
|
+
}
|
|
1797
|
+
const funcArity = {};
|
|
1798
|
+
for (const name in sig_1.funcSig) {
|
|
1799
|
+
funcArity[name] = sigArity(sig_1.funcSig[name]);
|
|
1800
|
+
}
|
|
1419
1801
|
// writtenArgCount counts the arguments as the AUTHOR wrote them.
|
|
1420
1802
|
//
|
|
1421
1803
|
// It cannot simply be terms.length: a comma group reaches the func-paren
|
|
@@ -1447,11 +1829,11 @@ function arityText(lo, hi) {
|
|
|
1447
1829
|
if (lo !== hi) {
|
|
1448
1830
|
return 0 === lo ? 'no arguments or one' : 'one argument or two';
|
|
1449
1831
|
}
|
|
1450
|
-
//
|
|
1451
|
-
//
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1832
|
+
// The {0,0} arm returned with the container kinds and acyclic()
|
|
1833
|
+
// (ADR-015): `map(1)` must not claim map takes exactly one.
|
|
1834
|
+
if (0 === hi) {
|
|
1835
|
+
return 'no arguments';
|
|
1836
|
+
}
|
|
1455
1837
|
if (2 === hi) {
|
|
1456
1838
|
return 'exactly two arguments';
|
|
1457
1839
|
}
|
|
@@ -1489,15 +1871,21 @@ function opCharHint(src) {
|
|
|
1489
1871
|
return '';
|
|
1490
1872
|
}
|
|
1491
1873
|
function rawToVal(n) {
|
|
1492
|
-
if (
|
|
1493
|
-
return new NullVal_1.NullVal({ peg: null });
|
|
1494
|
-
}
|
|
1495
|
-
if (true === n.isVal) {
|
|
1874
|
+
if (true === n?.isVal) {
|
|
1496
1875
|
return n;
|
|
1497
1876
|
}
|
|
1498
1877
|
if (Array.isArray(n)) {
|
|
1499
1878
|
return new ListVal_1.ListVal({ peg: n.map(rawToVal) });
|
|
1500
1879
|
}
|
|
1880
|
+
// THE SCALAR ARMS ARE WHERE A CONFIG FILE BECOMES VALUES. Every
|
|
1881
|
+
// format on the include table is read by its own parser into plain
|
|
1882
|
+
// JavaScript -- a string, a number, a map -- and this is the walk
|
|
1883
|
+
// that turns that into Vals (dataProcessor, ADR-012). The two arms
|
|
1884
|
+
// above are the other caller: a raw expression TERM, which the
|
|
1885
|
+
// expression grammar hands over already built.
|
|
1886
|
+
if (null == n) {
|
|
1887
|
+
return new NullVal_1.NullVal({ peg: null });
|
|
1888
|
+
}
|
|
1501
1889
|
const t = typeof n;
|
|
1502
1890
|
if ('string' === t) {
|
|
1503
1891
|
return new StringVal_1.StringVal({ peg: n });
|
|
@@ -1512,14 +1900,20 @@ function rawToVal(n) {
|
|
|
1512
1900
|
if ('boolean' === t) {
|
|
1513
1901
|
return new BooleanVal_1.BooleanVal({ peg: n });
|
|
1514
1902
|
}
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1903
|
+
// AND EVERYTHING ELSE IS A MAP, with no arm after it because there is
|
|
1904
|
+
// nothing after it. Every reader on the include table answers with
|
|
1905
|
+
// the JSON kinds and no others -- probed, including the two that
|
|
1906
|
+
// could plausibly escape them: a big integer comes back a `number`,
|
|
1907
|
+
// and a TOML date is normalised to its text before it gets here. The
|
|
1908
|
+
// one include that could hand over a function was `.js`, which
|
|
1909
|
+
// ADR-012 refuses. `parse_unknown` lived here for that case and has
|
|
1910
|
+
// no producer left in this port; the Go twin keeps its own, where the
|
|
1911
|
+
// type switch really can be handed something unaccounted for.
|
|
1912
|
+
const peg = {};
|
|
1913
|
+
for (const k in n) {
|
|
1914
|
+
peg[k] = rawToVal(n[k]);
|
|
1521
1915
|
}
|
|
1522
|
-
return new
|
|
1916
|
+
return new MapVal_1.MapVal({ peg });
|
|
1523
1917
|
}
|
|
1524
1918
|
class Lang {
|
|
1525
1919
|
constructor(options) {
|
|
@@ -1539,11 +1933,22 @@ class Lang {
|
|
|
1539
1933
|
// works. `.jsonic` is retired (no longer auto-resolved); the
|
|
1540
1934
|
// default `['jsonic','jsc','json','js']` is overridden here.
|
|
1541
1935
|
// (Upstream option name is the misspelled `implictExt`.)
|
|
1936
|
+
//
|
|
1937
|
+
// Only these two are SEARCHED for a bare `@"name"`; `.json` and
|
|
1938
|
+
// `.jsonld` are read when NAMED, which is how a vendored
|
|
1939
|
+
// vocabulary is always written.
|
|
1542
1940
|
implictExt: ['aon', 'aontu'],
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1941
|
+
// ONE ENTRY PER EXTENSION THE TABLE NAMES, built from it (see
|
|
1942
|
+
// includeProcessors) so the rule and its wiring cannot drift.
|
|
1943
|
+
//
|
|
1944
|
+
// The upstream defaults are REPLACED, not extended. Its `json`
|
|
1945
|
+
// entry is what made that extension the one that crashed: it
|
|
1946
|
+
// hands back a raw JS object where the aontu grammar produces
|
|
1947
|
+
// Vals, and the tree then met a value it could not convert
|
|
1948
|
+
// (BUGS §49b). Its `js` entry EXECUTES the file, which is not
|
|
1949
|
+
// something an extension should be able to ask for. And its
|
|
1950
|
+
// fallback hands any other file back as TEXT.
|
|
1951
|
+
processor: includeProcessors()
|
|
1547
1952
|
})
|
|
1548
1953
|
.use(AontuJsonic);
|
|
1549
1954
|
}
|
|
@@ -1584,18 +1989,15 @@ class Lang {
|
|
|
1584
1989
|
}
|
|
1585
1990
|
}
|
|
1586
1991
|
catch (e) {
|
|
1587
|
-
if ('include_denied' === e?.code ||
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
//
|
|
1591
|
-
//
|
|
1592
|
-
//
|
|
1593
|
-
//
|
|
1594
|
-
//
|
|
1595
|
-
//
|
|
1596
|
-
// reason: the resolver THROWS so a bare-member include cannot
|
|
1597
|
-
// vanish in the merge, and the code survives here as the
|
|
1598
|
-
// parse-stage nil the registry pins (errcodes.tsv).
|
|
1992
|
+
if ('include_denied' === e?.code || 'include_extension' === e?.code ||
|
|
1993
|
+
mod_1.MODULE_REFUSAL_CODES.has(e?.code)) {
|
|
1994
|
+
// A denied include (G5), an include whose extension is not read
|
|
1995
|
+
// as Aontu source (ADR-012, INCLUDE_KINDS), and a module that is
|
|
1996
|
+
// missing, fails its pin, or names a path that escapes its store
|
|
1997
|
+
// (G6 phase 2) are refused the same way, for the same reason: the
|
|
1998
|
+
// resolver THROWS so a bare-member include cannot vanish in the
|
|
1999
|
+
// merge, and the code survives here as the parse-stage nil the
|
|
2000
|
+
// registry pins (errcodes.tsv).
|
|
1599
2001
|
val = new NilVal_1.NilVal({
|
|
1600
2002
|
why: 'parse',
|
|
1601
2003
|
err: new NilVal_1.NilVal({
|