aontu 0.62.0 → 0.63.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/README.md +1 -1
- package/dist/agentsmd.js +0 -27
- package/dist/agentsmd.js.map +1 -1
- package/dist/alias.js.map +1 -1
- package/dist/allow.js +0 -92
- package/dist/allow.js.map +1 -1
- package/dist/aontu.d.ts +1 -1
- package/dist/aontu.js +1 -79
- package/dist/aontu.js.map +1 -1
- package/dist/aontumodel.d.ts +4 -0
- package/dist/aontumodel.js +33 -0
- package/dist/aontumodel.js.map +1 -0
- package/dist/cli.js +101 -620
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +0 -48
- package/dist/ctx.js.map +1 -1
- package/dist/diff.js +0 -32
- package/dist/diff.js.map +1 -1
- package/dist/err.js +0 -40
- package/dist/err.js.map +1 -1
- package/dist/escape.js +0 -45
- package/dist/escape.js.map +1 -1
- package/dist/exactjson.d.ts +0 -35
- package/dist/exactjson.js +0 -131
- package/dist/exactjson.js.map +1 -1
- package/dist/format.js +13 -203
- package/dist/format.js.map +1 -1
- package/dist/grammar.d.ts +9 -0
- package/dist/grammar.js +54 -0
- package/dist/grammar.js.map +1 -0
- package/dist/graph.js +0 -26
- package/dist/graph.js.map +1 -1
- package/dist/hcanon.js +0 -82
- package/dist/hcanon.js.map +1 -1
- package/dist/helpdoc.js +1 -1
- package/dist/helpdoc.js.map +1 -1
- package/dist/hints.d.ts +0 -6
- package/dist/hints.js +54 -55
- package/dist/hints.js.map +1 -1
- package/dist/jsonschema.js +0 -114
- package/dist/jsonschema.js.map +1 -1
- package/dist/keyorder.d.ts +0 -7
- package/dist/keyorder.js +0 -41
- package/dist/keyorder.js.map +1 -1
- package/dist/lang.js +17 -915
- package/dist/lang.js.map +1 -1
- package/dist/lower.js +11 -61
- package/dist/lower.js.map +1 -1
- package/dist/lsp-server.js +0 -16
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.d.ts +1 -1
- package/dist/lsp.js +12 -159
- package/dist/lsp.js.map +1 -1
- package/dist/mcp-server.js +0 -26
- package/dist/mcp-server.js.map +1 -1
- package/dist/mcp.js +0 -113
- package/dist/mcp.js.map +1 -1
- package/dist/mod-tool.js +0 -130
- package/dist/mod-tool.js.map +1 -1
- package/dist/mod.js +0 -162
- package/dist/mod.js.map +1 -1
- package/dist/patch.js +0 -217
- package/dist/patch.js.map +1 -1
- package/dist/provenance.js +0 -140
- package/dist/provenance.js.map +1 -1
- package/dist/query.js +0 -75
- package/dist/query.js.map +1 -1
- package/dist/reach.js +0 -43
- package/dist/reach.js.map +1 -1
- package/dist/relation.js +0 -61
- package/dist/relation.js.map +1 -1
- package/dist/render.js +20 -135
- package/dist/render.js.map +1 -1
- package/dist/report-sarif.d.ts +0 -11
- package/dist/report-sarif.js +0 -28
- package/dist/report-sarif.js.map +1 -1
- package/dist/sig.js +0 -35
- package/dist/sig.js.map +1 -1
- package/dist/sigdecl.js +1 -1
- package/dist/sigdecl.js.map +1 -1
- package/dist/siggate.js +0 -4
- package/dist/siggate.js.map +1 -1
- package/dist/site.js +3 -29
- package/dist/site.js.map +1 -1
- package/dist/subsume.d.ts +0 -10
- package/dist/subsume.js +0 -137
- package/dist/subsume.js.map +1 -1
- package/dist/template.d.ts +2 -1
- package/dist/template.js +58 -138
- package/dist/template.js.map +1 -1
- package/dist/trim.js +0 -41
- package/dist/trim.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.js.map +1 -1
- package/dist/unify.js +12 -242
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +0 -22
- package/dist/utility.js.map +1 -1
- package/dist/val/AbnfFuncVal.d.ts +18 -0
- package/dist/val/AbnfFuncVal.js +132 -0
- package/dist/val/AbnfFuncVal.js.map +1 -0
- package/dist/val/AbsentVal.d.ts +11 -0
- package/dist/val/AbsentVal.js +30 -0
- package/dist/val/AbsentVal.js.map +1 -0
- package/dist/val/AggFuncVal.d.ts +10 -1
- package/dist/val/AggFuncVal.js +104 -116
- package/dist/val/AggFuncVal.js.map +1 -1
- package/dist/val/ArithFuncVal.js +0 -12
- package/dist/val/ArithFuncVal.js.map +1 -1
- package/dist/val/BagVal.js +1 -78
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.js +0 -16
- package/dist/val/BigDecimalVal.js.map +1 -1
- package/dist/val/BigIntegerVal.js +0 -16
- package/dist/val/BigIntegerVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +0 -9
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/CmpFuncVal.js +2 -59
- package/dist/val/CmpFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +0 -29
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.js +0 -500
- package/dist/val/ConstraintVal.js.map +1 -1
- package/dist/val/ContainerKindVal.js +0 -2
- package/dist/val/ContainerKindVal.js.map +1 -1
- package/dist/val/CopyFuncVal.js +0 -3
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.js +0 -179
- package/dist/val/Decimal.js.map +1 -1
- package/dist/val/DeprecateFuncVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +0 -152
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/EachFuncVal.js +0 -3
- package/dist/val/EachFuncVal.js.map +1 -1
- package/dist/val/EmitFuncVal.d.ts +1 -1
- package/dist/val/EmitFuncVal.js +6 -119
- package/dist/val/EmitFuncVal.js.map +1 -1
- package/dist/val/ExpectVal.js +0 -62
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FilterFuncVal.js +0 -25
- package/dist/val/FilterFuncVal.js.map +1 -1
- package/dist/val/FuncBaseVal.d.ts +1 -0
- package/dist/val/FuncBaseVal.js +7 -127
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/GraphAtomVal.js +0 -15
- package/dist/val/GraphAtomVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +0 -13
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.js +0 -61
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +0 -20
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.js +0 -46
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +0 -57
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +0 -11
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +0 -151
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MatchFuncVal.js +0 -27
- package/dist/val/MatchFuncVal.js.map +1 -1
- package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
- package/dist/val/MaybeFuncVal.js +50 -0
- package/dist/val/MaybeFuncVal.js.map +1 -0
- package/dist/val/MoveFuncVal.js +0 -18
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.js +0 -60
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NomFuncVal.js +8 -42
- package/dist/val/NomFuncVal.js.map +1 -1
- package/dist/val/NumberVal.js +0 -15
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -0
- package/dist/val/OpBaseVal.js +3 -15
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/PackFuncVal.js +0 -34
- package/dist/val/PackFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +0 -6
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PathVal.js +0 -41
- package/dist/val/PathVal.js.map +1 -1
- package/dist/val/PlaceVal.js +0 -25
- package/dist/val/PlaceVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +1 -7
- package/dist/val/PlusOpVal.js +13 -74
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +0 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.js +0 -167
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RecurseVal.js +0 -55
- package/dist/val/RecurseVal.js.map +1 -1
- package/dist/val/RefVal.js +0 -282
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ReferFuncVal.js +0 -232
- package/dist/val/ReferFuncVal.js.map +1 -1
- package/dist/val/ScalarKindVal.js +0 -49
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.js +0 -11
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StrFuncVal.js +0 -18
- package/dist/val/StrFuncVal.js.map +1 -1
- package/dist/val/SuperFuncVal.js +0 -32
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +0 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TranslateFuncVal.js +1 -3
- package/dist/val/TranslateFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +0 -11
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -0
- package/dist/val/Val.js +2 -133
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +0 -12
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/arith.js +0 -37
- package/dist/val/arith.js.map +1 -1
- package/dist/val/caserange.js +0 -61
- package/dist/val/caserange.js.map +1 -1
- package/dist/val/members.js +0 -6
- package/dist/val/members.js.map +1 -1
- package/dist/val/numcmp.js +0 -11
- package/dist/val/numcmp.js.map +1 -1
- package/dist/val/numkind.js +0 -145
- package/dist/val/numkind.js.map +1 -1
- package/dist/val/valutil.js +0 -16
- package/dist/val/valutil.js.map +1 -1
- package/dist/vet.js +0 -461
- package/dist/vet.js.map +1 -1
- package/dist/view.js +0 -414
- package/dist/view.js.map +1 -1
- package/dist/walk.js +0 -41
- package/dist/walk.js.map +1 -1
- package/grammar/aontu.abnf +8 -6
- package/grammar/aontu.gbnf +4 -4
- package/grammar/aontu.lark +4 -4
- package/grammar/aontu.tmLanguage.json +1 -1
- package/package.json +4 -2
- package/src/agentsmd.ts +0 -35
- package/src/alias.ts +0 -39
- package/src/allow.ts +1 -96
- package/src/aontu.ts +3 -110
- package/src/aontumodel.ts +32 -0
- package/src/cli.ts +113 -682
- package/src/ctx.ts +0 -103
- package/src/diff.ts +0 -40
- package/src/err.ts +0 -40
- package/src/escape.ts +0 -46
- package/src/exactjson.ts +0 -131
- package/src/format.ts +14 -257
- package/src/grammar.ts +72 -0
- package/src/graph.ts +0 -61
- package/src/hcanon.ts +0 -82
- package/src/helpdoc.ts +1 -1
- package/src/hints.ts +66 -57
- package/src/jsonschema.ts +0 -123
- package/src/keyorder.ts +0 -42
- package/src/lang.ts +19 -931
- package/src/lower.ts +12 -62
- package/src/lsp-server.ts +0 -16
- package/src/lsp.ts +12 -180
- package/src/mcp-server.ts +0 -31
- package/src/mcp.ts +0 -130
- package/src/mod-tool.ts +0 -158
- package/src/mod.ts +0 -178
- package/src/patch.ts +0 -232
- package/src/provenance.ts +0 -183
- package/src/query.ts +0 -84
- package/src/reach.ts +0 -53
- package/src/relation.ts +0 -84
- package/src/render.ts +20 -172
- package/src/report-sarif.ts +0 -48
- package/src/sig.ts +0 -35
- package/src/sigdecl.ts +1 -1
- package/src/siggate.ts +0 -30
- package/src/site.ts +3 -29
- package/src/subsume.ts +1 -161
- package/src/template.ts +69 -140
- package/src/trim.ts +0 -53
- package/src/type.ts +2 -45
- package/src/unify.ts +13 -251
- package/src/utility.ts +0 -31
- package/src/val/AbnfFuncVal.ts +181 -0
- package/src/val/AbsentVal.ts +54 -0
- package/src/val/AggFuncVal.ts +152 -188
- package/src/val/ArithFuncVal.ts +0 -20
- package/src/val/BagVal.ts +1 -78
- package/src/val/BigDecimalVal.ts +0 -16
- package/src/val/BigIntegerVal.ts +0 -16
- package/src/val/CloseFuncVal.ts +0 -9
- package/src/val/CmpFuncVal.ts +4 -166
- package/src/val/ConjunctVal.ts +0 -33
- package/src/val/ConstraintVal.ts +2 -537
- package/src/val/ContainerKindVal.ts +0 -18
- package/src/val/CopyFuncVal.ts +0 -5
- package/src/val/Decimal.ts +1 -185
- package/src/val/DeprecateFuncVal.ts +0 -10
- package/src/val/DisjunctVal.ts +0 -157
- package/src/val/EachFuncVal.ts +0 -40
- package/src/val/EmitFuncVal.ts +8 -208
- package/src/val/ExpectVal.ts +0 -62
- package/src/val/FilterFuncVal.ts +0 -55
- package/src/val/FuncBaseVal.ts +9 -130
- package/src/val/GraphAtomVal.ts +0 -42
- package/src/val/HideFuncVal.ts +0 -15
- package/src/val/IntegerVal.ts +0 -61
- package/src/val/JunctionVal.ts +0 -20
- package/src/val/KeyFuncVal.ts +0 -48
- package/src/val/ListVal.ts +0 -59
- package/src/val/LowerFuncVal.ts +0 -12
- package/src/val/MapVal.ts +0 -151
- package/src/val/MatchFuncVal.ts +0 -59
- package/src/val/MaybeFuncVal.ts +86 -0
- package/src/val/MoveFuncVal.ts +0 -20
- package/src/val/NilVal.ts +0 -60
- package/src/val/NomFuncVal.ts +8 -95
- package/src/val/NumberVal.ts +0 -16
- package/src/val/OpBaseVal.ts +4 -17
- package/src/val/PackFuncVal.ts +0 -63
- package/src/val/PathFuncVal.ts +0 -32
- package/src/val/PathVal.ts +0 -66
- package/src/val/PlaceVal.ts +0 -45
- package/src/val/PlusOpVal.ts +18 -75
- package/src/val/PrefFuncVal.ts +0 -1
- package/src/val/PrefVal.ts +0 -179
- package/src/val/RecurseVal.ts +0 -81
- package/src/val/RefVal.ts +1 -285
- package/src/val/ReferFuncVal.ts +0 -255
- package/src/val/ScalarKindVal.ts +0 -50
- package/src/val/ScalarVal.ts +0 -12
- package/src/val/StrFuncVal.ts +0 -44
- package/src/val/SuperFuncVal.ts +0 -42
- package/src/val/TopVal.ts +0 -1
- package/src/val/TranslateFuncVal.ts +1 -51
- package/src/val/UpperFuncVal.ts +0 -12
- package/src/val/Val.ts +3 -192
- package/src/val/VarVal.ts +0 -15
- package/src/val/arith.ts +0 -92
- package/src/val/caserange.ts +0 -62
- package/src/val/members.ts +0 -23
- package/src/val/numcmp.ts +1 -27
- package/src/val/numkind.ts +0 -149
- package/src/val/valutil.ts +0 -16
- package/src/vet.ts +1 -582
- package/src/view.ts +0 -507
- package/src/walk.ts +0 -41
- package/dist/std.d.ts +0 -3
- package/dist/std.js +0 -672
- package/dist/std.js.map +0 -1
- package/dist/val/FormFuncVal.js +0 -55
- package/dist/val/FormFuncVal.js.map +0 -1
- package/dist/val/NamerFuncVal.d.ts +0 -12
- package/dist/val/NamerFuncVal.js +0 -176
- package/dist/val/NamerFuncVal.js.map +0 -1
- package/src/std.ts +0 -683
package/src/lang.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
/* Copyright (c) 2021-2025 Richard Rodger, MIT License */
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
// import { performance } from 'node:perf_hooks'
|
|
5
|
-
|
|
6
|
-
// Named imports, not `import * as`: the namespace form makes tsc emit
|
|
7
|
-
// the __importStar downlevel helper, whose branches no supported Node
|
|
8
|
-
// takes (the same rule cli.ts records). Aliased because `Path` is
|
|
9
|
-
// already the @tabnas/path plugin below.
|
|
10
4
|
import { existsSync, readFileSync, realpathSync } from 'node:fs'
|
|
11
5
|
import {
|
|
12
6
|
basename as pathBasename,
|
|
@@ -27,10 +21,6 @@ import {
|
|
|
27
21
|
} from '@tabnas/jsonic'
|
|
28
22
|
|
|
29
23
|
|
|
30
|
-
// THE CONFIG-FORMAT READERS (ADR-012). Each is a jsonic plugin for one
|
|
31
|
-
// format, so an included `.toml` or `.yaml` is parsed by a real parser
|
|
32
|
-
// for that format rather than guessed at by this one. `@tabnas/json` is
|
|
33
|
-
// the strict RFC 8259 reader, used for `.json` and `.jsonld`.
|
|
34
24
|
import { funcSig } from './sig'
|
|
35
25
|
import type { FuncSig } from './sig'
|
|
36
26
|
|
|
@@ -47,7 +37,6 @@ import {
|
|
|
47
37
|
MultiSource
|
|
48
38
|
} from '@tabnas/multisource'
|
|
49
39
|
|
|
50
|
-
// TODO: @tabnas/multisource should support virtual fs
|
|
51
40
|
|
|
52
41
|
import {
|
|
53
42
|
makeFileResolver
|
|
@@ -61,17 +50,11 @@ import {
|
|
|
61
50
|
makeMemResolver
|
|
62
51
|
} from '@tabnas/multisource/resolver/mem'
|
|
63
52
|
|
|
64
|
-
// The Aontu-source processor, TAKEN RATHER THAN ALIASED. The obvious
|
|
65
|
-
// spelling is the alias `aon: 'jsonic'`, which multisource resolves
|
|
66
|
-
// through its own processor map -- but `jsonic` is a FORMAT NAME in
|
|
67
|
-
// the include table now, so that alias resolved to the plain-jsonic
|
|
68
|
-
// DATA reader and every `.aon` include was suddenly parsed without the
|
|
69
|
-
// language in it. Naming the function leaves nothing to collide with.
|
|
70
53
|
import {
|
|
71
54
|
makeJsonicProcessor,
|
|
72
55
|
} from '@tabnas/multisource/processor/jsonic'
|
|
73
56
|
|
|
74
|
-
import {
|
|
57
|
+
import { AONTU_SOURCES, AONTU_SCHEME, AONTU_MODELS } from './aontumodel'
|
|
75
58
|
import {
|
|
76
59
|
parseModuleRef, resolveModule, modCacheDir, MODULE_REFUSAL_CODES,
|
|
77
60
|
} from './mod'
|
|
@@ -104,7 +87,6 @@ import {
|
|
|
104
87
|
} from './val/top'
|
|
105
88
|
|
|
106
89
|
|
|
107
|
-
|
|
108
90
|
import {
|
|
109
91
|
ScalarKindVal,
|
|
110
92
|
BigDecimal,
|
|
@@ -138,11 +120,13 @@ import { CopyFuncVal } from './val/CopyFuncVal'
|
|
|
138
120
|
import { KeyFuncVal } from './val/KeyFuncVal'
|
|
139
121
|
import { TypeFuncVal } from './val/TypeFuncVal'
|
|
140
122
|
import { HideFuncVal } from './val/HideFuncVal'
|
|
123
|
+
import { AbnfFuncVal, ParseFuncVal } from './val/AbnfFuncVal'
|
|
141
124
|
import { DeprecateFuncVal } from './val/DeprecateFuncVal'
|
|
142
125
|
import { ReferFuncVal, RelFuncVal } from './val/ReferFuncVal'
|
|
143
126
|
import { AcyclicFuncVal, InverseFuncVal } from './val/GraphAtomVal'
|
|
144
127
|
import { PackFuncVal } from './val/PackFuncVal'
|
|
145
128
|
import { CMP_FUNCS } from './val/CmpFuncVal'
|
|
129
|
+
import { MaybeFuncVal } from './val/MaybeFuncVal'
|
|
146
130
|
import { NomFuncVal } from './val/NomFuncVal'
|
|
147
131
|
import { TranslateFuncVal } from './val/TranslateFuncVal'
|
|
148
132
|
import { EachFuncVal } from './val/EachFuncVal'
|
|
@@ -156,7 +140,8 @@ import {
|
|
|
156
140
|
AddFuncVal, SubFuncVal, MulFuncVal, DivFuncVal, ModFuncVal, RemFuncVal,
|
|
157
141
|
} from './val/ArithFuncVal'
|
|
158
142
|
import {
|
|
159
|
-
SumFuncVal, LeastFuncVal, GreatestFuncVal, PickFuncVal,
|
|
143
|
+
SumFuncVal, LeastFuncVal, GreatestFuncVal, PickFuncVal, SortFuncVal,
|
|
144
|
+
JoinFuncVal,
|
|
160
145
|
} from './val/AggFuncVal'
|
|
161
146
|
import { PlaceVal } from './val/PlaceVal'
|
|
162
147
|
import { MoveFuncVal } from './val/MoveFuncVal'
|
|
@@ -182,34 +167,13 @@ import {
|
|
|
182
167
|
const asPlugin = (p: unknown): Plugin => p as Plugin
|
|
183
168
|
|
|
184
169
|
|
|
185
|
-
// Build the Val for a matched `0d` literal (see the `0d` value matcher
|
|
186
|
-
// below). Leaf by source: digits only is a biginteger, a `.` or an
|
|
187
|
-
// exponent makes it a bigdecimal.
|
|
188
|
-
//
|
|
189
|
-
// A literal over the D6 exactness budget becomes a LOCATED ERROR here
|
|
190
|
-
// and not a rounded or expanded value: `0d1e1000000000` has a one-digit
|
|
191
|
-
// coefficient, so only the scale bound catches it, and it is caught at
|
|
192
|
-
// parse -- before plain-form rendering would try to materialise a
|
|
193
|
-
// gigabyte of zeros.
|
|
194
|
-
// The source text of a negated exact literal. `-` is a prefix OPERATOR,
|
|
195
|
-
// not part of the literal, so the text has to be rebuilt here to keep
|
|
196
|
-
// `src` meaning "how this value is spelled" (see bigVal).
|
|
197
170
|
function negsrc(src: string): string {
|
|
198
|
-
// src is never empty here: it is a BIG_LITERAL_RE match (at minimum
|
|
199
|
-
// the two chars `0d`) or an already-negated spelling. Computed exact
|
|
200
|
-
// values do carry src '', but they are built at unify time, long
|
|
201
|
-
// after this parse-time opmap.
|
|
202
171
|
return src.startsWith('-') ? src.slice(1) : '-' + src
|
|
203
172
|
}
|
|
204
173
|
|
|
205
174
|
|
|
206
175
|
function bigVal(res: RegExpExecArray): Val {
|
|
207
176
|
const lit = readBigLiteral(res)
|
|
208
|
-
// `src` is the literal's own text, and it is not decoration: a path
|
|
209
|
-
// segment is spelled text, so `$.a.0d1` must address the key `0d1` --
|
|
210
|
-
// the same key `a:{0d1:7}` creates -- rather than the number 1. See
|
|
211
|
-
// RefVal.append. Without it the segment was empty and the reference
|
|
212
|
-
// silently resolved to its own container.
|
|
213
177
|
const src = res[0]
|
|
214
178
|
return 'biginteger' === lit.leaf ? new BigIntegerVal({ peg: lit.int, src }) :
|
|
215
179
|
'bigdecimal' === lit.leaf ? new BigDecimalVal({ peg: lit.dec, src }) :
|
|
@@ -222,45 +186,13 @@ const CC_0 = 48
|
|
|
222
186
|
const CC_d = 100
|
|
223
187
|
const CC_D = 68
|
|
224
188
|
|
|
225
|
-
// THE ALIAS SIGIL. `%` is part of an alias's name, so the name is one
|
|
226
|
-
// lexeme wherever it appears and its meaning is decided by position:
|
|
227
|
-
// a BINDING in key position (`%uint8 = …` declares), a USE in value
|
|
228
|
-
// position (`listen: %uint8` refers). docs/design/ALIASES.0.md §4.
|
|
229
189
|
const CC_PCT = 37
|
|
230
190
|
const ALIAS_RE = /^%[A-Za-z_][A-Za-z0-9_]*/
|
|
231
191
|
|
|
232
|
-
// THE DECLARATION OPERATOR. `%name = value` declares; the `=` is the
|
|
233
|
-
// pair's separator, lexed as the colon token so the declaration then
|
|
234
|
-
// parses as a pair whose key is the alias name (ALIASES.0.md X-1, as
|
|
235
|
-
// settled 2026-09-05). `=` is syntax ONLY there: anywhere else it is
|
|
236
|
-
// punctuation outside its syntax, and the bare-text scan below refuses
|
|
237
|
-
// it (`foo = 1`, `a: x=y`).
|
|
238
192
|
const CC_EQ = 61
|
|
239
193
|
const CC_SP = 32
|
|
240
194
|
const CC_TAB = 9
|
|
241
195
|
|
|
242
|
-
// THE BARE-TEXT RULE. A bare string holds letters, digits, `-` and `_`,
|
|
243
|
-
// and nothing else. Every other punctuation character is either SYNTAX,
|
|
244
|
-
// where the grammar gives it a meaning, or an ERROR where it does not
|
|
245
|
-
// -- never silently part of a string. `x=y`, `6/2`, `50%` and `>10`
|
|
246
|
-
// were all bare strings once, each a well-formed wrong document, and
|
|
247
|
-
// each is refused now, naming the character (bare_punct).
|
|
248
|
-
//
|
|
249
|
-
// scanBareRun classifies each character of a run three ways, in this
|
|
250
|
-
// order: TEXT continues the run; an ENDER stops it; anything else is
|
|
251
|
-
// BAD. The ender set is the lexer's own -- space, line, fixed tokens,
|
|
252
|
-
// comment starters -- read from the config its text matcher was built
|
|
253
|
-
// from, so it cannot drift from the grammar. A bad run is still scanned
|
|
254
|
-
// to its ender, so the refusal claims the whole spelling and the lexer
|
|
255
|
-
// never reads the tail of it as syntax.
|
|
256
|
-
//
|
|
257
|
-
// `-` is text wherever the scan sees it. A run never STARTS on one:
|
|
258
|
-
// `-` is the sign of a number and the negation prefix, a fixed token
|
|
259
|
-
// the fixed matcher claims before either scanning stage can run, so
|
|
260
|
-
// `a:-1` is the negation of 1 and `a:6-2` the string. The `+` of an
|
|
261
|
-
// exponent (`1e+2`) is admitted only by the NUMBER stage's scan, the
|
|
262
|
-
// one stage that can make a number of it. Mirrors scanBareRun in
|
|
263
|
-
// go/lang.go, decision for decision.
|
|
264
196
|
const CC_9 = 57
|
|
265
197
|
const CC_A = 65
|
|
266
198
|
const CC_Z = 90
|
|
@@ -345,34 +277,10 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
345
277
|
|
|
346
278
|
jsonic.use(asPlugin(Path))
|
|
347
279
|
|
|
348
|
-
// Only # line comments are valid Aontu syntax (see
|
|
349
|
-
// docs/reference-language.md; go/lang.go sets the same). Clear the
|
|
350
|
-
// underlying jsonic comment markers entirely, then define # directly,
|
|
351
|
-
// so the comment set is hash-only regardless of those defaults.
|
|
352
280
|
let dotRef = (r: Rule, ctx: JsonicContext, terms: any, prefix: boolean) => {
|
|
353
281
|
terms = dropUnfilled(terms)
|
|
354
282
|
if (0 === terms.length) return incompleteNil(r, ctx)
|
|
355
283
|
|
|
356
|
-
// AN ALIAS IS NOT A PATH SEGMENT. `$.%foo` is refused: the alias
|
|
357
|
-
// namespace and the path namespace are disjoint, and an alias is
|
|
358
|
-
// reached by writing `%foo` and only that.
|
|
359
|
-
//
|
|
360
|
-
// The engine spells an alias reference AS a root reference to the
|
|
361
|
-
// declaration -- which is what gives it order independence and a
|
|
362
|
-
// cycle check shared with paths -- but that is an implementation of
|
|
363
|
-
// the name, not a second way to write it. Left writable, the two
|
|
364
|
-
// spellings would drift apart the moment aliases stop being
|
|
365
|
-
// file-shaped, and `$.%b` inside an included file would reach the
|
|
366
|
-
// INCLUDER's `%b` rather than its own, which is exactly the
|
|
367
|
-
// cross-file capture the sigil exists to prevent.
|
|
368
|
-
// `%foo` lexes to the reference itself, so in `$.%foo` it arrives
|
|
369
|
-
// as a TERM rather than as a string segment -- both shapes are
|
|
370
|
-
// checked, since a quoted `$."%foo"` would arrive as the string.
|
|
371
|
-
// Terms here are always Vals -- dropUnfilled has removed the
|
|
372
|
-
// nulls, and the dot rules never hand over a raw string -- so the
|
|
373
|
-
// shapes are exactly three: a RefVal (peg is the segment array), a
|
|
374
|
-
// StringVal (peg is the segment), and anything else (a numeric or
|
|
375
|
-
// exact segment, which cannot be an alias name).
|
|
376
284
|
for (const t of terms) {
|
|
377
285
|
const segs: any[] =
|
|
378
286
|
Array.isArray(t.peg) ? t.peg :
|
|
@@ -397,49 +305,13 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
397
305
|
},
|
|
398
306
|
})
|
|
399
307
|
|
|
400
|
-
// Digit separators are legal only as a SINGLE separator BETWEEN
|
|
401
|
-
// digits (the rule test/spec/engine-parity.tsv records as the engine's
|
|
402
|
-
// adjudication; pinned by the sep-* rows in
|
|
403
|
-
// test/spec/number-model.tsv). The engine's number matcher
|
|
404
|
-
// enforces most of that already — `1_`, `_1`, `1_.5`, `1._5`, `1e_2`,
|
|
405
|
-
// `1e2_` all fall through to text — but two gaps remain: a REPEATED
|
|
406
|
-
// separator (`1__0` lexed as 10) and a separator at the edge of a
|
|
407
|
-
// base-prefixed digit run (`0x_ff`, `0xff_` lexed as 255). Both
|
|
408
|
-
// silently accept a typo as a different number, so aontu declines the
|
|
409
|
-
// whole run instead and it lexes as text ("1__0"), exactly as `1_`
|
|
410
|
-
// already does.
|
|
411
|
-
//
|
|
412
|
-
// `number.exclude` is tested against the matched number source and,
|
|
413
|
-
// when it matches, makes the matcher decline the entire span. Kept in
|
|
414
|
-
// lock-step with sepInvalid in go/lang.go, which is wired to the
|
|
415
|
-
// equivalent `Number.Exclude` hook (and to the Check hook, which
|
|
416
|
-
// constructs big base-prefixed tokens itself and so bypasses Exclude).
|
|
417
308
|
jsonic.options({
|
|
418
309
|
number: {
|
|
419
|
-
// `__` repeated separator; `0x_`/`0o_`/`0b_` separator opening a
|
|
420
|
-
// base-prefixed run; `_` closing any run. The prefix letter is
|
|
421
|
-
// matched case-insensitively so the rule does not depend on which
|
|
422
|
-
// prefix spellings the engine accepts.
|
|
423
310
|
exclude: /__|^[-+]?0[xXoObB]_|_$/,
|
|
424
311
|
|
|
425
|
-
// THE NUMBER STAGE OF THE BARE-TEXT RULE. The matcher runs before
|
|
426
|
-
// the text matcher and reads a number up to the next ender -- and
|
|
427
|
-
// `-` is an ender, being the negation prefix's fixed token, so it
|
|
428
|
-
// would take the `2026` of `2026-09-05` and leave `-09-05` to the
|
|
429
|
-
// grammar. The hook scans the whole run first and declines for
|
|
430
|
-
// the matcher wherever the run is not its to lex: a run with a
|
|
431
|
-
// bad character (the text stage refuses it), a run that is not a
|
|
432
|
-
// number at all (`2026-09-05`, `6-2` are text). Twin of tsNumCheck
|
|
433
|
-
// in go/lang.go.
|
|
434
312
|
check: (lex: any) => {
|
|
435
313
|
const pnt = lex.pnt
|
|
436
314
|
const src = lex.src
|
|
437
|
-
// The hook makes the matcher a candidate at every position, so
|
|
438
|
-
// the common case -- a run no number can open -- declines for it
|
|
439
|
-
// in one char read. A DIGIT opens a number here and nothing
|
|
440
|
-
// else: the sign and the dot open the matcher's own grammar, but
|
|
441
|
-
// they are fixed tokens (the prefix operators and member
|
|
442
|
-
// access), claimed before this hook can run.
|
|
443
315
|
const c = src.charCodeAt(pnt.sI)
|
|
444
316
|
if (!(CC_0 <= c && c <= CC_9)) {
|
|
445
317
|
return NOT_A_NUMBER
|
|
@@ -453,69 +325,17 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
453
325
|
},
|
|
454
326
|
})
|
|
455
327
|
|
|
456
|
-
// D3 -- the `0d` literal, the only route to the exact leaves
|
|
457
|
-
// (biginteger and bigdecimal). See BIG_LITERAL_RE for the grammar and
|
|
458
|
-
// the leaf-by-source rule.
|
|
459
|
-
//
|
|
460
|
-
// The literal is claimed by the TEXT MATCHER'S CHECK HOOK (the sibling
|
|
461
|
-
// of the `Number.Check` hook the Go port already uses for big
|
|
462
|
-
// base-prefixed literals), not by a `value.def` entry, because a `0d`
|
|
463
|
-
// run may contain a `.` and the two claim source differently:
|
|
464
|
-
//
|
|
465
|
-
// - A value def -- even a consuming one, matched against the full
|
|
466
|
-
// forward source -- is applied INSIDE the text matcher, AFTER its
|
|
467
|
-
// ender regexp has already carved the run at the `.`. The def
|
|
468
|
-
// claims `0d1.5` whole, but the matcher then still emits the
|
|
469
|
-
// ender's `.` as a fixed token, so `x:0d1.5` lexed as the
|
|
470
|
-
// bigdecimal FOLLOWED BY a dangling member-access dot (a path
|
|
471
|
-
// cycle). Verified, not theorised.
|
|
472
|
-
// - The check hook runs BEFORE that ender regexp and returns the
|
|
473
|
-
// token outright, so the run is claimed whole and nothing else is
|
|
474
|
-
// emitted.
|
|
475
|
-
//
|
|
476
|
-
// A `match.value` matcher (which runs ahead of every other matcher)
|
|
477
|
-
// also claims it correctly, but it is a candidate at EVERY lex
|
|
478
|
-
// position and materializes the forward source there: ~8% on a
|
|
479
|
-
// text-heavy document, for a syntax almost none of them use. The check
|
|
480
|
-
// hook only runs where the text matcher already runs, and measures at
|
|
481
|
-
// parity with not having it.
|
|
482
|
-
//
|
|
483
|
-
// The number matcher never sees these runs at all: it declines `0d…`
|
|
484
|
-
// outright, since `d` is not an ender.
|
|
485
328
|
jsonic.options({
|
|
486
329
|
text: {
|
|
487
330
|
check: (lex: any) => {
|
|
488
|
-
// Guard first, on char codes: this hook runs at every text
|
|
489
|
-
// position, and the common case (any run that cannot be a `0d`
|
|
490
|
-
// literal or an alias) must cost two char reads and no
|
|
491
|
-
// allocation.
|
|
492
331
|
const pnt = lex.pnt
|
|
493
332
|
const src = lex.src
|
|
494
333
|
|
|
495
|
-
// AN ALIAS NAME IS CLAIMED WHOLE, for the same reason the `0d`
|
|
496
|
-
// run below is: the text matcher's ender regexp would otherwise
|
|
497
|
-
// carve `%uint8` at the `%` and emit the sigil as its own token,
|
|
498
|
-
// leaving a bare `uint8` behind -- which is exactly the capture
|
|
499
|
-
// the sigil exists to prevent. Claiming it here, before that
|
|
500
|
-
// ender runs, keeps the name one lexeme.
|
|
501
|
-
//
|
|
502
|
-
// The token's SOURCE is the whole `%name`, which is what makes
|
|
503
|
-
// the same lexeme work in both positions: jsonic keys a pair by
|
|
504
|
-
// the token's source text (`0d1: 5` yields the key `0d1`), so a
|
|
505
|
-
// declaration reads as the key `%uint8`, while a value position
|
|
506
|
-
// calls the function below and gets the reference.
|
|
507
|
-
// A `%` that opens no name (`%`, `%1`, `50%`) falls through to
|
|
508
|
-
// the bare-text scan below, which refuses it.
|
|
509
334
|
const ares = CC_PCT === src.charCodeAt(pnt.sI) ?
|
|
510
335
|
ALIAS_RE.exec(lex.refwd()) : null
|
|
511
336
|
if (null != ares) {
|
|
512
337
|
const asrc = ares[0]
|
|
513
338
|
|
|
514
|
-
// A lone `=` after the name, across horizontal space only, is
|
|
515
|
-
// the declaration operator. Decided HERE, where the name is
|
|
516
|
-
// claimed, and only its position is kept: the very next text
|
|
517
|
-
// position is that `=`, since nothing but space sits between,
|
|
518
|
-
// so the mark cannot outlive its one use. `==` is not it.
|
|
519
339
|
let j = pnt.sI + asrc.length
|
|
520
340
|
while (j < src.length &&
|
|
521
341
|
(CC_SP === src.charCodeAt(j) || CC_TAB === src.charCodeAt(j))) {
|
|
@@ -527,13 +347,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
527
347
|
|
|
528
348
|
const atkn = lex.token(
|
|
529
349
|
'#VL',
|
|
530
|
-
// AN ALIAS REFERENCE IS A PATH REFERENCE. `%uint8` is
|
|
531
|
-
// `$.%uint8`: root-absolute, one segment, spelled with the
|
|
532
|
-
// sigil the declaration is spelled with. Everything the
|
|
533
|
-
// design asks of it -- order independence, alias-of-alias,
|
|
534
|
-
// redeclaration unifying, cycle refusal spanning both
|
|
535
|
-
// namespaces -- is then the reference machinery already in
|
|
536
|
-
// the language, not a second resolver beside it.
|
|
537
350
|
(r: Rule, ctx: JsonicContext) =>
|
|
538
351
|
addsite(new RefVal({ peg: [asrc], absolute: true }), r, ctx),
|
|
539
352
|
asrc,
|
|
@@ -543,11 +356,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
543
356
|
return { done: true, token: atkn }
|
|
544
357
|
}
|
|
545
358
|
|
|
546
|
-
// The `=` the alias arm above marked: the separator of a
|
|
547
|
-
// declaration, as a colon token whose source is `=`. The pair rule
|
|
548
|
-
// is then the pair rule, and the formatter writes the spelling it
|
|
549
|
-
// read. Marked in `use` so the pair rule can tell it from a colon,
|
|
550
|
-
// which no longer declares.
|
|
551
359
|
if (CC_EQ === src.charCodeAt(pnt.sI) && lex.aontu_eq_at === pnt.sI) {
|
|
552
360
|
delete lex.aontu_eq_at
|
|
553
361
|
const eqtkn = lex.token('#CL', undefined, '=', pnt, { aontu_eq: true })
|
|
@@ -559,18 +367,9 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
559
367
|
if (CC_0 === src.charCodeAt(pnt.sI)) {
|
|
560
368
|
const c1 = src.charCodeAt(pnt.sI + 1)
|
|
561
369
|
if (CC_d === c1 || CC_D === c1) {
|
|
562
|
-
// BIG_LITERAL_RE is `^`-anchored and read against the
|
|
563
|
-
// forward source (memoized per position by refwd), which is
|
|
564
|
-
// what lets it claim the `.` of `0d1.5`. A `0d` run it does
|
|
565
|
-
// not match falls through to the bare-text scan below.
|
|
566
370
|
const res = BIG_LITERAL_RE.exec(lex.refwd())
|
|
567
371
|
if (null != res) {
|
|
568
372
|
const msrc = res[0]
|
|
569
|
-
// The token value is a FUNCTION so Val construction
|
|
570
|
-
// happens at parse time, where the rule and context needed
|
|
571
|
-
// for the site exist (jsonic calls a #VL token's function
|
|
572
|
-
// value with them). A `0d` literal never spans a line, so
|
|
573
|
-
// only the source and column positions advance.
|
|
574
373
|
const tkn = lex.token(
|
|
575
374
|
'#VL',
|
|
576
375
|
(r: Rule, ctx: JsonicContext) => addsite(bigVal(res), r, ctx),
|
|
@@ -583,22 +382,10 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
583
382
|
}
|
|
584
383
|
}
|
|
585
384
|
|
|
586
|
-
// THE BARE-TEXT RULE (scanBareRun). Last, so that a name, a
|
|
587
|
-
// declaration operator and an exact literal are read before a
|
|
588
|
-
// run is judged as text.
|
|
589
|
-
// The run is never empty: every ender is a token an earlier
|
|
590
|
-
// matcher claims, so the text stage only opens on a character
|
|
591
|
-
// the scan classifies as text or as bad.
|
|
592
385
|
const run = scanBareRun(lex.cfg, src, pnt.sI, false)
|
|
593
386
|
const msrc = src.slice(pnt.sI, run.end)
|
|
594
387
|
|
|
595
388
|
if (-1 !== run.bad) {
|
|
596
|
-
// BAD: the run is refused whole, sited at the character (the
|
|
597
|
-
// mark in `use` is what tokenSite reads). As a VALUE the
|
|
598
|
-
// token's function builds the refusal at the parse, where the
|
|
599
|
-
// rule carries the position. As a KEY the token is read for
|
|
600
|
-
// its source alone, so the mark is what the pair and elem
|
|
601
|
-
// rules read to write the refusal where the map is built.
|
|
602
389
|
const ch = run.ch
|
|
603
390
|
const tkn = lex.token(
|
|
604
391
|
'#VL',
|
|
@@ -615,11 +402,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
615
402
|
return { done: true, token: tkn }
|
|
616
403
|
}
|
|
617
404
|
|
|
618
|
-
// CLEAN, with a `-` past its start (`team-payments`,
|
|
619
|
-
// `2026-09-05`): claimed here as one text token, because the
|
|
620
|
-
// default matcher's ender set would carve the run at the `-`.
|
|
621
|
-
// Any other clean run is the default matcher's, which also reads
|
|
622
|
-
// the value keywords and the `_` hole.
|
|
623
405
|
if (-1 !== msrc.indexOf('-')) {
|
|
624
406
|
const tkn = lex.token('#TX', msrc, msrc, pnt)
|
|
625
407
|
pnt.sI += msrc.length
|
|
@@ -632,20 +414,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
632
414
|
},
|
|
633
415
|
})
|
|
634
416
|
|
|
635
|
-
// TODO: refactor Val constructor
|
|
636
|
-
// let addsite = (v: Val, p: string[]) => (v.path = [...(p || [])], v)
|
|
637
|
-
// WHERE A TOKEN SITES A VALUE: the token's own position and text --
|
|
638
|
-
// except for a token the bare-text rule marked (`aontu_bad`), which
|
|
639
|
-
// sites at the offending CHARACTER. Its column is the character's
|
|
640
|
-
// first occurrence in the run (every character before it is text,
|
|
641
|
-
// and it is not), and its text is the character alone. Read here and
|
|
642
|
-
// nowhere else, so a value sited from such a token lands on the
|
|
643
|
-
// character however many times the parse re-sites it.
|
|
644
|
-
//
|
|
645
|
-
// A TOKEN WITH NO TEXT IS NO SPAN, in both ports, and the extent is
|
|
646
|
-
// DERIVED from the text rather than read from the token's own `len`
|
|
647
|
-
// — so the Go twin, whose token has no len field, computes the
|
|
648
|
-
// identical number with utf16Len and nothing has to be kept in step.
|
|
649
417
|
type TokenSite = { row: number, col: number, src: string, len: number }
|
|
650
418
|
const NO_SITE: TokenSite = { row: -1, col: -1, src: '', len: -1 }
|
|
651
419
|
const tokenSite = (tkn: any): TokenSite => {
|
|
@@ -677,14 +445,6 @@ let AontuJsonic: Plugin = function AontuLang(jsonic: Jsonic) {
|
|
|
677
445
|
return v
|
|
678
446
|
}
|
|
679
447
|
|
|
680
|
-
// THE KEY REFUSALS a pair may carry, decided from its key TOKEN --
|
|
681
|
-
// never from the key text alone, since a quoted `"%a"` or `"x=y"` is
|
|
682
|
-
// an ordinary key: a declaration spelled with a colon (alias_colon),
|
|
683
|
-
// and a key the bare-text rule refuses (bare_punct). Undefined for an
|
|
684
|
-
// ordinary key, and for a declaration (`%a = 1`), which is a binding
|
|
685
|
-
// (isAliasDecl), not a refusal. Asked FIRST by the pair rule and by
|
|
686
|
-
// the elem rule, before the declaration is, so a pair in list
|
|
687
|
-
// position is held to the map's rules.
|
|
688
448
|
const isAliasDecl = (ktkn: any, sep: any): boolean =>
|
|
689
449
|
null != ktkn && VL === ktkn.tin && ALIAS_RE.test('' + ktkn.src) &&
|
|
690
450
|
true === sep?.use?.aontu_eq
|
|
@@ -729,10 +489,6 @@ help isolate the syntax error.`,
|
|
|
729
489
|
},
|
|
730
490
|
value: {
|
|
731
491
|
def: {
|
|
732
|
-
// NOTE: specify with functions as jsonic/deep will
|
|
733
|
-
// remove class prototype as options are assumed plain
|
|
734
|
-
// (except for functions).
|
|
735
|
-
// TODO: jsonic should be able to pass context into these
|
|
736
492
|
'string': {
|
|
737
493
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
738
494
|
addsite(new ScalarKindVal({ peg: String }), r, ctx)
|
|
@@ -752,8 +508,6 @@ help isolate the syntax error.`,
|
|
|
752
508
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
753
509
|
addsite(new ScalarKindVal({ peg: Float }), r, ctx)
|
|
754
510
|
},
|
|
755
|
-
// The two exact leaves. Their keywords are the marker class
|
|
756
|
-
// names lowercased, which is also how ScalarKindVal canons them.
|
|
757
511
|
'biginteger': {
|
|
758
512
|
val: (r: Rule, ctx: JsonicContext) =>
|
|
759
513
|
addsite(new ScalarKindVal({ peg: BigInteger }), r, ctx)
|
|
@@ -772,7 +526,6 @@ help isolate the syntax error.`,
|
|
|
772
526
|
addsite(new NilVal({ why: 'literal_nil' }), r, ctx)
|
|
773
527
|
},
|
|
774
528
|
|
|
775
|
-
// TODO: FIX: need a TOP instance to hold path
|
|
776
529
|
'top': { val: () => top() },
|
|
777
530
|
|
|
778
531
|
// G8 phase 3: the placeholder. A BARE `_` is the hole; `"_"`
|
|
@@ -792,7 +545,6 @@ help isolate the syntax error.`,
|
|
|
792
545
|
|
|
793
546
|
if (pval?.isVal && cval?.isVal) {
|
|
794
547
|
|
|
795
|
-
// TODO: test multi element conjuncts work
|
|
796
548
|
if (pval.isConjunct && cval.isConjunct) {
|
|
797
549
|
(pval as ConjunctVal).append(cval)
|
|
798
550
|
return pval
|
|
@@ -809,14 +561,6 @@ help isolate the syntax error.`,
|
|
|
809
561
|
// Handle defered conjuncts, where MapVal does not yet
|
|
810
562
|
// exist, by creating ConjunctVal later.
|
|
811
563
|
else {
|
|
812
|
-
// AN INCLUDE UNIFIES IN PLACE. multisource calls this hook at
|
|
813
|
-
// the `@`'s own source position, so `prev` holds exactly the
|
|
814
|
-
// pairs written BEFORE it. Folding the loaded map's keys in
|
|
815
|
-
// here -- host-so-far first, arriving value second -- is what
|
|
816
|
-
// inlining the loaded bytes at the `@` does, and mirrors
|
|
817
|
-
// go/lang.go's Map.Merge, which multisource-go drives one key
|
|
818
|
-
// at a time. A non-map load has no keys to fold and stays a
|
|
819
|
-
// deferred conjunct arm.
|
|
820
564
|
if (true === (cval as any)?.isMap) {
|
|
821
565
|
const lm: any = cval
|
|
822
566
|
for (const k of Object.keys(lm.peg)) {
|
|
@@ -826,10 +570,6 @@ help isolate the syntax error.`,
|
|
|
826
570
|
? new ConjunctVal({ peg: [own, lm.peg[k]] })
|
|
827
571
|
: lm.peg[k])
|
|
828
572
|
}
|
|
829
|
-
// The loaded map's spread joins THIS map's spread list at
|
|
830
|
-
// the `@`'s position: the parse pushes each `&:` onto the
|
|
831
|
-
// node as it is read, so `prev[SPREAD].v` already holds the
|
|
832
|
-
// spreads written before the `@` and nothing after it.
|
|
833
573
|
if (null != lm.spread?.cj) {
|
|
834
574
|
;(prev as any)[SPREAD] =
|
|
835
575
|
((prev as any)[SPREAD] || { o: '&', v: [] })
|
|
@@ -861,21 +601,12 @@ help isolate the syntax error.`,
|
|
|
861
601
|
path: PathFuncVal,
|
|
862
602
|
pref: PrefFuncVal,
|
|
863
603
|
|
|
864
|
-
// First-class paths and the container kinds
|
|
865
|
-
// (docs/design/PATHS.0.md). `path(p)` CAPTURES a path as a value;
|
|
866
|
-
// `path()`, `map()` and `list()` are kinds -- the vacuous
|
|
867
|
-
// constructor call admits its values and defaults to nothing,
|
|
868
|
-
// where the container LITERALS `{}`/`[]` default to empty.
|
|
869
604
|
map: MapFuncVal,
|
|
870
605
|
list: ListFuncVal,
|
|
871
606
|
close: CloseFuncVal,
|
|
872
607
|
open: OpenFuncVal,
|
|
873
608
|
super: SuperFuncVal,
|
|
874
609
|
|
|
875
|
-
// The constraint algebra's Band A atoms (G1 phase 1;
|
|
876
|
-
// docs/reference-language.md, "The constraint algebra"): bounds
|
|
877
|
-
// and exclusion enter through the function registry — the
|
|
878
|
-
// established extension point — with zero grammar change.
|
|
879
610
|
min: MinConstraintVal,
|
|
880
611
|
max: MaxConstraintVal,
|
|
881
612
|
above: AboveConstraintVal,
|
|
@@ -886,28 +617,19 @@ help isolate the syntax error.`,
|
|
|
886
617
|
// host regex engines agree on (nonPortableRe in ConstraintVal.ts).
|
|
887
618
|
re: ReConstraintVal,
|
|
888
619
|
|
|
889
|
-
// G1 phase 3: the sizing atoms. Both are properties of a CONTAINER
|
|
890
|
-
// (or, for length, of a string) rather than comparisons against a
|
|
891
|
-
// value, which is why `unique` is the one built-in taking no
|
|
892
|
-
// argument at all.
|
|
893
620
|
length: LengthConstraintVal,
|
|
894
621
|
unique: UniqueConstraintVal,
|
|
895
622
|
|
|
896
|
-
// G1 phase 5: Band B. `must` is the one atom the algebra does not
|
|
897
|
-
// reason about -- it is checked against the finished value and
|
|
898
|
-
// reported with the author's own message, never simplified and
|
|
899
|
-
// never consulted for emptiness or subsumption.
|
|
900
623
|
must: MustConstraintVal,
|
|
901
624
|
|
|
625
|
+
abnf: AbnfFuncVal,
|
|
626
|
+
parse: ParseFuncVal,
|
|
627
|
+
|
|
902
628
|
// G3 phase 4: the deprecation mark. Unification-transparent; the
|
|
903
629
|
// record rides the result (Val.deprecation) and canon renders the
|
|
904
630
|
// call back (canonRiders).
|
|
905
631
|
deprecate: DeprecateFuncVal,
|
|
906
632
|
|
|
907
|
-
// G4 phase 2: the checked, typed, LINK-shaped reference. A
|
|
908
|
-
// constraint on a string field: the string must be a TREE ADDRESS
|
|
909
|
-
// (`$.a.b` or `.b`), the address must resolve, and the optional
|
|
910
|
-
// argument flows INTO the target. The field keeps the string.
|
|
911
633
|
refer: ReferFuncVal,
|
|
912
634
|
rel: RelFuncVal,
|
|
913
635
|
|
|
@@ -917,39 +639,13 @@ help isolate the syntax error.`,
|
|
|
917
639
|
acyclic: AcyclicFuncVal,
|
|
918
640
|
inverse: InverseFuncVal,
|
|
919
641
|
|
|
920
|
-
// G8 phase 1: generation to KEYED CHILDREN. `pack` makes one keyed
|
|
921
|
-
// child per child of its data, cloning its template per
|
|
922
|
-
// destination exactly as a spread does, and waits for the model to
|
|
923
|
-
// settle before it fires (the staging rule, G8 phase 0).
|
|
924
642
|
pack: PackFuncVal,
|
|
925
643
|
|
|
926
|
-
// RENDER P6: the order-preserving map, and generation to a LIST.
|
|
927
|
-
// `each` makes one list element per child of its data, being the
|
|
928
|
-
// template with `_` bound to the source child. It exists because
|
|
929
|
-
// `pick(pack(...))` re-sorts to code-point order, and a struct's
|
|
930
|
-
// fields or a file's imports are the model's order or they are
|
|
931
|
-
// wrong.
|
|
932
|
-
//
|
|
933
|
-
// Mentioning the hole makes it a BOUND instead: `each(d, _ & t)`
|
|
934
|
-
// meets each child with `t`, and `each(d, _)` is a bag's members
|
|
935
|
-
// as a list. That is what the retired meet-only `each` spelled
|
|
936
|
-
// (ADR-026), and why this one could take the name (ADR-027).
|
|
937
644
|
each: EachFuncVal,
|
|
938
645
|
|
|
939
|
-
// G8 phase 2: selection. `filter` keeps the children of a bag that
|
|
940
|
-
// unify with a condition; `match` picks the first arm whose
|
|
941
|
-
// pattern the scrutinee unifies with. Both select by
|
|
942
|
-
// UNIFIABILITY, tried in trial mode, so neither adds a predicate
|
|
943
|
-
// language to the one the lattice already is.
|
|
944
646
|
filter: FilterFuncVal,
|
|
945
647
|
match: MatchFuncVal,
|
|
946
648
|
|
|
947
|
-
// The arithmetic family (the review's finding I). Maths beyond `+`
|
|
948
|
-
// arrives as FUNCTIONS — `-` `*` `/` `%` stay reserved — and the
|
|
949
|
-
// family is numeric where the operator is polymorphic, which is
|
|
950
|
-
// what makes `add` more than a second spelling of `+`: it refuses
|
|
951
|
-
// the string concatenation that silently answers `"500m" + "500m"`.
|
|
952
|
-
// Every rule they obey is in ts/src/val/arith.ts.
|
|
953
649
|
add: AddFuncVal,
|
|
954
650
|
sub: SubFuncVal,
|
|
955
651
|
mul: MulFuncVal,
|
|
@@ -957,85 +653,32 @@ help isolate the syntax error.`,
|
|
|
957
653
|
mod: ModFuncVal,
|
|
958
654
|
rem: RemFuncVal,
|
|
959
655
|
|
|
960
|
-
// Aggregation over a finite, settled bag (the review's finding I).
|
|
961
|
-
// `least` and `greatest` rather than min and max, which are already
|
|
962
|
-
// the atoms for a lower and an upper BOUND -- an aggregate over a
|
|
963
|
-
// set and a bound on a value must not share a spelling.
|
|
964
656
|
sum: SumFuncVal,
|
|
965
657
|
least: LeastFuncVal,
|
|
966
658
|
greatest: GreatestFuncVal,
|
|
967
659
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
// template -- `each(d, _ & t)` MEETS each child, and a meet cannot
|
|
971
|
-
// select.
|
|
660
|
+
maybe: MaybeFuncVal,
|
|
661
|
+
|
|
972
662
|
pick: PickFuncVal,
|
|
663
|
+
sort: SortFuncVal,
|
|
973
664
|
|
|
974
|
-
// G9 phase 2: the fold to a STRING. `sum` folds with `add`; this
|
|
975
|
-
// folds with `+`, so it inherits the one number-to-text rule and
|
|
976
|
-
// the language does not grow a second. It is the primitive that
|
|
977
|
-
// turns a bag of computed lines into a file.
|
|
978
665
|
join: JoinFuncVal,
|
|
979
666
|
|
|
980
|
-
// G9 phase 6: apply-templates. One flat list of pieces from a
|
|
981
|
-
// selection and a RULE TABLE -- for each node, the first template
|
|
982
|
-
// whose `match` it unifies with, its `body` instantiated at that
|
|
983
|
-
// node. The dispatch is the engine's because a body referenced by
|
|
984
|
-
// path resolves its references at the definition site, and the
|
|
985
|
-
// relative resolution that does exist is a dot count that does not
|
|
986
|
-
// survive a second dispatch (docs/design/EMIT.0.md).
|
|
987
667
|
emit: EmitFuncVal,
|
|
988
668
|
|
|
989
|
-
// G9 phase 6: the string builtins the rule layer needs. `esc`
|
|
990
|
-
// makes a value safe inside a literal and `usc` reads it back;
|
|
991
|
-
// `rep` and `split` derive names from model data. All four are
|
|
992
|
-
// ordinary string functions -- they know nothing about generation,
|
|
993
|
-
// which is why they can land before the renderer does.
|
|
994
669
|
esc: EscFuncVal,
|
|
995
670
|
usc: UscFuncVal,
|
|
996
671
|
rep: RepFuncVal,
|
|
997
672
|
split: SplitFuncVal,
|
|
998
673
|
|
|
999
|
-
// THE COMPONENT PRIMITIVES -- SPIKE (ts/src/val/CmpFuncVal.ts,
|
|
1000
|
-
// docs/design/JOSTRACA.0.md). jostraca's component set, spelled
|
|
1001
|
-
// as functions -- LOWER CASE, like every other builtin in this
|
|
1002
|
-
// language, while the node each one builds names the jostraca
|
|
1003
|
-
// component it drives (`file(...)` is aontu, `"File"` is
|
|
1004
|
-
// jostraca's). A document then says what FILES it produces
|
|
1005
|
-
// instead of filling in the `aontu:code` vocabulary as data.
|
|
1006
|
-
// Eight of jostraca's ten: `copy` and `list` are already builtins
|
|
1007
|
-
// here with unrelated meanings, so those two have no lower-case
|
|
1008
|
-
// spelling to take (see ts/src/val/CmpFuncVal.ts).
|
|
1009
|
-
//
|
|
1010
|
-
// TypeScript only, and so deliberately NOT in
|
|
1011
|
-
// test/spec/signature.tsv, BUILTIN_FUNCS (ts/src/lsp.ts) or the
|
|
1012
|
-
// grammar/ files -- each is asserted in cross-port parity and a
|
|
1013
|
-
// TS-only entry turns the Go suite red. Arity and argument shape
|
|
1014
|
-
// are refused in CmpFuncVal.resolve for the same reason.
|
|
1015
674
|
...CMP_FUNCS,
|
|
1016
675
|
|
|
1017
|
-
// NAME TRANSFORMATION -- SPIKE (ts/src/val/NamerFuncVal.ts).
|
|
1018
|
-
// Generated code is mostly names, and no two targets spell them
|
|
1019
|
-
// the same way. Splits a name into words whatever format it is
|
|
1020
|
-
// written in, then renders it in the target's. TypeScript only,
|
|
1021
|
-
// and out of the parity-pinned registries for the reason the
|
|
1022
|
-
// component primitives are.
|
|
1023
676
|
nom: NomFuncVal,
|
|
1024
677
|
|
|
1025
|
-
// PER-CHARACTER SUBSTITUTION AND DELETION -- SPIKE
|
|
1026
|
-
// (ts/src/val/TranslateFuncVal.ts), after `tr`. Reads the source
|
|
1027
|
-
// once and consults a table, which is what `rep` cannot do: a
|
|
1028
|
-
// per-character map spelled as N `rep` calls composes wrongly,
|
|
1029
|
-
// since each pass sees the previous one's output.
|
|
1030
678
|
translate: TranslateFuncVal,
|
|
1031
679
|
}
|
|
1032
680
|
|
|
1033
681
|
|
|
1034
|
-
// A dangling operator (`a:1|`, `a:$`, `a:*` at end of input) leaves
|
|
1035
|
-
// null/undefined unfilled terms. Junction ops drop them (so `a:1&`
|
|
1036
|
-
// and `a:1|` are just 1); ops missing a required operand become an
|
|
1037
|
-
// incomplete_expression nil, surfaced by generate() as a "Cannot
|
|
1038
|
-
// resolve value" error (the Go port mirrors this in lang.go).
|
|
1039
682
|
const dropUnfilled = (terms: any) => terms.filter((t: any) => null != t)
|
|
1040
683
|
|
|
1041
684
|
const incompleteNil = (r: Rule, ctx: JsonicContext) =>
|
|
@@ -1048,27 +691,10 @@ help isolate the syntax error.`,
|
|
|
1048
691
|
fname: string, argterms: any[]): any => {
|
|
1049
692
|
const funcval = funcMap[fname]
|
|
1050
693
|
|
|
1051
|
-
// Arity is known for every built-in, so a surplus or missing
|
|
1052
|
-
// argument is a mistake in the SOURCE, refused here where the
|
|
1053
|
-
// author can see it (issue #51). It was previously left to each
|
|
1054
|
-
// function to notice or not: the two ports disagreed on `upper()`
|
|
1055
|
-
// and on `close()`, and `min(1,2)` noticed nothing at all -- it
|
|
1056
|
-
// built a constraint that merely refused to generate later, with a
|
|
1057
|
-
// message about the map rather than about the call.
|
|
1058
|
-
//
|
|
1059
|
-
// Counted BEFORE the rawToVal pass below, which is what makes the
|
|
1060
|
-
// count possible: a comma group arrives as a RAW array and a
|
|
1061
|
-
// written list literal as a ListVal, and rawToVal turns the first
|
|
1062
|
-
// into the second.
|
|
1063
694
|
const arity = funcArity[fname]
|
|
1064
695
|
if (null != arity) {
|
|
1065
696
|
const got = writtenArgCount(argterms)
|
|
1066
697
|
if (got < arity[0] || (-1 !== arity[1] && got > arity[1])) {
|
|
1067
|
-
// details is assigned AFTER construction: the NilVal
|
|
1068
|
-
// constructor does not read it from its spec (only NilVal.make
|
|
1069
|
-
// does), so passing it in the spec left the hint's
|
|
1070
|
-
// {func}/{want}/{got} placeholders un-injected and printed
|
|
1071
|
-
// literally.
|
|
1072
698
|
const nil: any = new NilVal({ why: 'func_arity' })
|
|
1073
699
|
nil.details = {
|
|
1074
700
|
func: fname,
|
|
@@ -1079,22 +705,6 @@ help isolate the syntax error.`,
|
|
|
1079
705
|
}
|
|
1080
706
|
}
|
|
1081
707
|
|
|
1082
|
-
// rawToVal EVERY argument. A degenerate expression can hand this
|
|
1083
|
-
// handler raw parse values rather than Vals -- `pref(1-3)` arrives
|
|
1084
|
-
// as the plain numbers 1 and -3 -- and a func's peg is unified
|
|
1085
|
-
// element by element, so a raw one reached `arg.unify(...)` and
|
|
1086
|
-
// threw. The unifier's catch-all turned that into an `internal`
|
|
1087
|
-
// verdict: a crash reported as a unification result (issue #49).
|
|
1088
|
-
// The Go port has always converted here (asVal in evaluate).
|
|
1089
|
-
// A comma group is ONE raw-array term (see writtenArgCount).
|
|
1090
|
-
// For a function whose arguments are distinct POSITIONS —
|
|
1091
|
-
// deprecate's value and record, pack's and each's data and template
|
|
1092
|
-
// — the group is expanded back into them here, while a written list
|
|
1093
|
-
// literal, already a ListVal, stays one argument. The constraint
|
|
1094
|
-
// atoms make the same move in their own constructor (atomArgs,
|
|
1095
|
-
// ConstraintVal.ts), which is why they are not in this set:
|
|
1096
|
-
// `neq(1,2)` is one argument LIST, not two positions, and expanding
|
|
1097
|
-
// it here would take the list away from the code that reads it.
|
|
1098
708
|
let terms = argterms
|
|
1099
709
|
if (true === POSITIONAL_ARG_FUNCS[fname] && 1 === terms.length &&
|
|
1100
710
|
Array.isArray(terms[0])) {
|
|
@@ -1128,24 +738,6 @@ help isolate the syntax error.`,
|
|
|
1128
738
|
'star-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
1129
739
|
if (null == terms[0]) return incompleteNil(r, ctx)
|
|
1130
740
|
|
|
1131
|
-
// A PREFERENCE MARKS A VALUE, AND A BARE KEY IS NOT ONE.
|
|
1132
|
-
// `*a: 1` has no braces, so the prefix took the whole IMPLICIT
|
|
1133
|
-
// map as its operand and the document silently became
|
|
1134
|
-
// `*{"a":1}` -- `*a: 1, b: 2` became a one-element LIST, losing
|
|
1135
|
-
// `b` outright. Neither is anything the author wrote.
|
|
1136
|
-
//
|
|
1137
|
-
// The accident is confined to the first position of the implicit
|
|
1138
|
-
// top-level map, which is the only place no brace has yet
|
|
1139
|
-
// committed the rule to a map: `{*a: 1}` and `a: 1, *b: 2` are
|
|
1140
|
-
// ALREADY parse errors. This makes the third spelling agree with
|
|
1141
|
-
// them rather than inventing a meaning for it.
|
|
1142
|
-
//
|
|
1143
|
-
// A BRACED operand is untouched, and that is the whole of the
|
|
1144
|
-
// distinction: `*{x:1}` and `*[1]` are the real spelling, they
|
|
1145
|
-
// are what `*{x:1} | *{y:2}` needs, and the shared spec pins them
|
|
1146
|
-
// (11 rows). The open token's own source text is what separates
|
|
1147
|
-
// the two -- `{` or `[` for a braced bag, the first key or
|
|
1148
|
-
// element for an implicit one.
|
|
1149
741
|
const bag: any = terms[0]
|
|
1150
742
|
if ((bag.isMap && '{' !== bag.site.src) ||
|
|
1151
743
|
(bag.isList && '[' !== bag.site.src)) {
|
|
@@ -1171,7 +763,6 @@ help isolate the syntax error.`,
|
|
|
1171
763
|
'string' === typeof seg && ALIAS_RE.test(seg))) {
|
|
1172
764
|
return addsite(new NilVal({ why: 'alias_in_path' }), r, ctx)
|
|
1173
765
|
}
|
|
1174
|
-
// $.a.b absolute path
|
|
1175
766
|
if (terms[0] instanceof RefVal) {
|
|
1176
767
|
terms[0].absolute = true
|
|
1177
768
|
return terms[0]
|
|
@@ -1187,10 +778,6 @@ help isolate the syntax error.`,
|
|
|
1187
778
|
'negative-prefix': (r: Rule, ctx: JsonicContext, _op: Op, terms: any) => {
|
|
1188
779
|
let val = terms[0]
|
|
1189
780
|
if (null == val) return incompleteNil(r, ctx)
|
|
1190
|
-
// The exact leaves negate exactly and never change kind. R2/D5
|
|
1191
|
-
// holds here too: bigint has a single zero, and Decimal's
|
|
1192
|
-
// normalising constructor sends every zero to the same form, so
|
|
1193
|
-
// `-0d0` is `0d0` and `-0d0.0` is `0d0.0`.
|
|
1194
781
|
if (val instanceof BigIntegerVal) {
|
|
1195
782
|
return addsite(new BigIntegerVal(
|
|
1196
783
|
{ peg: -val.peg, src: negsrc(val.src) }), r, ctx)
|
|
@@ -1204,17 +791,9 @@ help isolate the syntax error.`,
|
|
|
1204
791
|
if (!(val instanceof IntegerVal) && !(val instanceof NumberVal)) {
|
|
1205
792
|
return addsite(new NilVal({ why: 'negative' }), r, ctx)
|
|
1206
793
|
}
|
|
1207
|
-
// Build a fresh Val rather than mutating in place: the expr plugin
|
|
1208
|
-
// can evaluate the same node twice (e.g. inside `*-1` or a
|
|
1209
|
-
// disjunct member), and an in-place `peg = -peg` applied twice
|
|
1210
|
-
// silently un-negates the number.
|
|
1211
794
|
let peg = -1 * val.peg
|
|
1212
795
|
// Normalize -0 to 0 (keeps the AST and canon free of negative zero).
|
|
1213
796
|
if (0 === peg) peg = 0
|
|
1214
|
-
// Negation never narrows the kind: a number stays a number. An
|
|
1215
|
-
// integer stays an integer unless the negation leaves the int64
|
|
1216
|
-
// range (only -(-2^63), which no literal can express), in which
|
|
1217
|
-
// case it widens to a number rather than failing.
|
|
1218
797
|
const out = val instanceof IntegerVal && isIntegerKind(peg)
|
|
1219
798
|
? new IntegerVal({ peg })
|
|
1220
799
|
: new NumberVal({ peg })
|
|
@@ -1249,7 +828,6 @@ help isolate the syntax error.`,
|
|
|
1249
828
|
jsonic
|
|
1250
829
|
.use(asPlugin(Expr), {
|
|
1251
830
|
op: {
|
|
1252
|
-
// disjunct < conjunct: c & b | a -> (c & b) | a
|
|
1253
831
|
'conjunct': {
|
|
1254
832
|
infix: true, src: '&', left: 16_000_000, right: 17_000_000
|
|
1255
833
|
},
|
|
@@ -1265,16 +843,6 @@ help isolate the syntax error.`,
|
|
|
1265
843
|
right: 21_000_000,
|
|
1266
844
|
},
|
|
1267
845
|
|
|
1268
|
-
// Re-base the unary prefixes for the same reason. Every aontu
|
|
1269
|
-
// operator sits far above the @tabnas/expr defaults, so the
|
|
1270
|
-
// default prefix binding power of 4_000_000 left unary `-`/`+`
|
|
1271
|
-
// LOOSER than every infix operator: `-1 & integer` parsed as
|
|
1272
|
-
// `-(1 & integer)`, and negative-prefix (below) then rejected
|
|
1273
|
-
// the composite operand as a `negative` error nil (likewise
|
|
1274
|
-
// `-2+3`, `-1|2`). Unary minus must bind tighter than `+`, `&`
|
|
1275
|
-
// and `|` — but still looser than `.` (dot-infix left is
|
|
1276
|
-
// 25_000_000), so `-0xFF.5` stays `-(0xFF.5)` and `$`.
|
|
1277
|
-
// Kept in lock-step with the op table in go/lang.go.
|
|
1278
846
|
'negative': {
|
|
1279
847
|
src: '-',
|
|
1280
848
|
prefix: true,
|
|
@@ -1330,21 +898,17 @@ help isolate the syntax error.`,
|
|
|
1330
898
|
remainder: null,
|
|
1331
899
|
},
|
|
1332
900
|
evaluate: (r: Rule, ctx: JsonicContext, op: Op, terms: any) => {
|
|
1333
|
-
// // console.log('EVAL-START', r.u)
|
|
1334
901
|
|
|
1335
902
|
if (
|
|
1336
903
|
'func-paren' === op.name
|
|
1337
|
-
// && !r.parent.prev?.u?.paren_preval
|
|
1338
904
|
&& !r.u?.paren_preval
|
|
1339
905
|
) {
|
|
1340
|
-
// terms = [new StringVal({ peg: '' }), ...terms]
|
|
1341
906
|
terms = ['', ...terms]
|
|
1342
907
|
}
|
|
1343
908
|
|
|
1344
909
|
|
|
1345
910
|
let val: Val = opmap[op.name](r, ctx, op, terms)
|
|
1346
911
|
|
|
1347
|
-
// // console.log('EVAL', terms, '->', val)
|
|
1348
912
|
|
|
1349
913
|
return val
|
|
1350
914
|
}
|
|
@@ -1366,16 +930,6 @@ help isolate the syntax error.`,
|
|
|
1366
930
|
|
|
1367
931
|
jsonic.rule('expr', (rs: RuleSpec) => {
|
|
1368
932
|
rs.close([
|
|
1369
|
-
// A `&` followed by `:` after an expression value belongs to the
|
|
1370
|
-
// enclosing map as a spread, not to the expression as a conjunct
|
|
1371
|
-
// — backtrack both tokens so the expression completes (and
|
|
1372
|
-
// evaluates to a Val) and the map's spread alts take over. This
|
|
1373
|
-
// is what makes `k1:$flag &:boolean` parse: without it the expr
|
|
1374
|
-
// plugin consumes the `&` as an infix conjunct, chokes on the
|
|
1375
|
-
// `:`, and leaves the raw unevaluated expr node in the map
|
|
1376
|
-
// (mirrors the expr-rule PrependClose in go/lang.go). The
|
|
1377
|
-
// `n: { expr: 0 }` reset matches the plugin's own expr-end alts —
|
|
1378
|
-
// the evaluation after-close only fires when the counter is 0.
|
|
1379
933
|
{ s: [CJ, CL], b: 2, n: { expr: 0 }, g: 'expr,expr-end,spread' },
|
|
1380
934
|
])
|
|
1381
935
|
return rs
|
|
@@ -1431,18 +985,6 @@ help isolate the syntax error.`,
|
|
|
1431
985
|
if (!Number.isFinite(r.node)) {
|
|
1432
986
|
valnode = addsite(new NilVal({ why: 'not_number' }), r, ctx)
|
|
1433
987
|
}
|
|
1434
|
-
// D7 -- A LOSSY INTEGER LITERAL IS REFUSED, NOT ROUNDED. The
|
|
1435
|
-
// token above is already a double, so a literal the double
|
|
1436
|
-
// cannot hold exactly (2^53+1, 0x7fffffffffffffff,
|
|
1437
|
-
// 0xffffffffffffffff) has ALREADY become a different number by
|
|
1438
|
-
// the time it gets here. Storing it would mean the document
|
|
1439
|
-
// silently means something other than what it says, so the
|
|
1440
|
-
// literal becomes a located error whose hint names the escape:
|
|
1441
|
-
// write it `0d…` and get the exact value.
|
|
1442
|
-
//
|
|
1443
|
-
// The rule is EXACTNESS, not magnitude -- 10^20 and 2^124 are
|
|
1444
|
-
// both far outside the int64 window and both land exactly on a
|
|
1445
|
-
// binary64, so both stay values (see isLossyIntegerLiteral).
|
|
1446
988
|
else if (isLossyIntegerLiteral(r.node, r.o0.src)) {
|
|
1447
989
|
const nil = new NilVal({ why: 'lossy_integer_literal' })
|
|
1448
990
|
nil.details = { src: r.o0.src }
|
|
@@ -1497,15 +1039,6 @@ help isolate the syntax error.`,
|
|
|
1497
1039
|
|
|
1498
1040
|
let mo = r.node
|
|
1499
1041
|
|
|
1500
|
-
// An elided value (`a:`) leaves a raw null/undefined that never
|
|
1501
|
-
// passed through the val rule. It is REFUSED rather than made a
|
|
1502
|
-
// null (issue #48): a key with nothing after the colon is a
|
|
1503
|
-
// mistake in the source, and turning it into a value made that
|
|
1504
|
-
// mistake indistinguishable from a deliberate `a:null`.
|
|
1505
|
-
//
|
|
1506
|
-
// A colon chain (`a: b:1`) is not an elision -- the value is the
|
|
1507
|
-
// nested pair, which the val rule does produce -- and neither is
|
|
1508
|
-
// a trailing comma.
|
|
1509
1042
|
for (const k in mo) {
|
|
1510
1043
|
if (null == mo[k] && '___merge' !== k &&
|
|
1511
1044
|
'___optional' !== k && '___alias' !== k) {
|
|
@@ -1517,12 +1050,6 @@ help isolate the syntax error.`,
|
|
|
1517
1050
|
en.path = [...(r.k?.path ?? []), k]
|
|
1518
1051
|
mo[k] = en
|
|
1519
1052
|
|
|
1520
|
-
// An elided value under an OPTIONAL key stops being optional.
|
|
1521
|
-
// Optionality is about a value that may be absent at
|
|
1522
|
-
// GENERATE; it does not excuse a source that stops after the
|
|
1523
|
-
// colon. Left optional, the refusal was dropped with the key
|
|
1524
|
-
// and `a?:` generated `{}` -- a silent nothing, which is
|
|
1525
|
-
// worse than either the old null or the error.
|
|
1526
1053
|
const oi = optionalKeys.indexOf(k)
|
|
1527
1054
|
if (-1 !== oi) {
|
|
1528
1055
|
optionalKeys.splice(oi, 1)
|
|
@@ -1530,35 +1057,18 @@ help isolate the syntax error.`,
|
|
|
1530
1057
|
}
|
|
1531
1058
|
}
|
|
1532
1059
|
|
|
1533
|
-
// ... and the OPTIONAL spelling, `a?:`, which does not leave a
|
|
1534
|
-
// null behind to be found: its value never reaches the node at
|
|
1535
|
-
// all, so the key is simply absent and the map generated without
|
|
1536
|
-
// it. A key recorded as optional but missing from the node was
|
|
1537
|
-
// written with nothing after its colon.
|
|
1538
1060
|
for (const k of optionalKeys) {
|
|
1539
1061
|
if (!(k in mo)) {
|
|
1540
1062
|
mo[k] = addsite(new NilVal({ why: 'elided_value' }), r, ctx)
|
|
1541
1063
|
}
|
|
1542
1064
|
}
|
|
1543
1065
|
|
|
1544
|
-
// An elided SPREAD (`x:$obj&:` with nothing after the colon)
|
|
1545
|
-
// refuses the whole map, not a key (issue #48). A spread is not a
|
|
1546
|
-
// child, so a refusal stored in its place has nothing to attach
|
|
1547
|
-
// to: `x:&:` has no children for the spread to apply to, and the
|
|
1548
|
-
// map would generate as `{}` with the mistake silently gone.
|
|
1549
|
-
// Refusing the container is what makes it visible at all.
|
|
1550
1066
|
const sp: any = (mo as any)[SPREAD]
|
|
1551
1067
|
if (sp && sp.v.some((sv: any) => null == sv)) {
|
|
1552
1068
|
r.node = addsite(new NilVal({ why: 'elided_value' }), r, ctx)
|
|
1553
1069
|
return undefined
|
|
1554
1070
|
}
|
|
1555
1071
|
|
|
1556
|
-
// A KEY REFUSAL (the pair rule records them: a declaration
|
|
1557
|
-
// spelled with a colon, a key the bare-text rule refuses) becomes
|
|
1558
|
-
// the refusal, in place of whatever followed the colon and sited
|
|
1559
|
-
// at the KEY rather than at the map -- at the offending character
|
|
1560
|
-
// of it, where there is one -- so the frame points at the
|
|
1561
|
-
// spelling to change.
|
|
1562
1072
|
for (const { key, tkn, why, details } of
|
|
1563
1073
|
(r.u.aontu_key_refusals ?? []) as any[]) {
|
|
1564
1074
|
const en: any = siteAt(addsite(new NilVal({ why }), r, ctx), tokenSite(tkn))
|
|
@@ -1587,7 +1097,6 @@ help isolate the syntax error.`,
|
|
|
1587
1097
|
let mop = { ...mo }
|
|
1588
1098
|
delete mop.___merge
|
|
1589
1099
|
|
|
1590
|
-
// TODO: needs addpath?
|
|
1591
1100
|
let mopv = new MapVal({ peg: mop })
|
|
1592
1101
|
mopv.optionalKeys = optionalKeys
|
|
1593
1102
|
mopv.aliasKeys = aliasKeys
|
|
@@ -1610,19 +1119,14 @@ help isolate the syntax error.`,
|
|
|
1610
1119
|
})
|
|
1611
1120
|
|
|
1612
1121
|
|
|
1613
|
-
|
|
1614
1122
|
jsonic.rule('list', (rs: RuleSpec) => {
|
|
1615
1123
|
rs
|
|
1616
|
-
// .open([{ s: [CJ, CL], p: 'pair', b: 2, g: 'spread' }])
|
|
1617
1124
|
|
|
1618
1125
|
.bc((r: Rule, ctx: JsonicContext) => {
|
|
1619
1126
|
const optionalKeys = r.u.aontu_optional_keys ?? []
|
|
1620
1127
|
|
|
1621
1128
|
let ao = r.node
|
|
1622
1129
|
|
|
1623
|
-
// An elided ELEMENT (`[,]`, `[1,,2]`) is refused for the same
|
|
1624
|
-
// reason as an elided map value (issue #48). A trailing comma
|
|
1625
|
-
// (`[1,]`) is not an elision and never reaches here.
|
|
1626
1130
|
for (let i = 0; i < ao.length; i++) {
|
|
1627
1131
|
if (null == ao[i]) {
|
|
1628
1132
|
// Pathed at the INDEX, for the same reason as the map case.
|
|
@@ -1643,43 +1147,23 @@ help isolate the syntax error.`,
|
|
|
1643
1147
|
return undefined
|
|
1644
1148
|
})
|
|
1645
1149
|
|
|
1646
|
-
// .close([{ s: [CJ, CL], b: 2, g: 'spread,json,more' }])
|
|
1647
1150
|
|
|
1648
1151
|
return rs
|
|
1649
1152
|
})
|
|
1650
1153
|
|
|
1651
1154
|
|
|
1652
|
-
// TODO: copied from jsonic grammar
|
|
1653
|
-
// jsonic should provide a way to export this
|
|
1654
1155
|
const pairkey = (r: Rule) => {
|
|
1655
1156
|
// Get key string value from first matching token of `Open` state.
|
|
1656
1157
|
const key_token = r.o0
|
|
1657
1158
|
const key =
|
|
1658
1159
|
ST === key_token.tin || TX === key_token.tin
|
|
1659
|
-
? key_token.val
|
|
1160
|
+
? key_token.val
|
|
1660
1161
|
: key_token.src // Was number, use original text
|
|
1661
1162
|
|
|
1662
1163
|
r.u.key = key
|
|
1663
1164
|
}
|
|
1664
1165
|
|
|
1665
1166
|
|
|
1666
|
-
// A pair in LIST position writes its value at `node[key]` like any
|
|
1667
|
-
// other pair, and the enclosing list's node is an ARRAY -- so a numeric
|
|
1668
|
-
// key lands on an index and becomes an element, and may land on an
|
|
1669
|
-
// index a real element already holds (`[5,0:1]`). Since the pair must
|
|
1670
|
-
// contribute nothing (issue #40), the slot is photographed before the
|
|
1671
|
-
// value is parsed and put back afterwards. Restoring beats deleting for
|
|
1672
|
-
// exactly the overwrite case: deleting `[5,0:1]`'s index 0 would take
|
|
1673
|
-
// the 5 with it, where restoring gives back the list the pair was
|
|
1674
|
-
// never part of.
|
|
1675
|
-
//
|
|
1676
|
-
// `length` is saved too: writing past the end grows an array, and
|
|
1677
|
-
// `[5,1?:9]` must be [5] again and not [5, <hole>].
|
|
1678
|
-
// Typed as a string bag deliberately: the slot may be named by a
|
|
1679
|
-
// non-numeric key (`[x:1]`), which the array type would refuse. Both
|
|
1680
|
-
// helpers run only from the elem rule, whose node is the enclosing
|
|
1681
|
-
// list, so neither guards against a non-array node — a guard there
|
|
1682
|
-
// proved unreachable and dead code is worse than none.
|
|
1683
1167
|
const asSlots = (r: Rule): Record<string, any> => r.node as any
|
|
1684
1168
|
|
|
1685
1169
|
const snapshotPairSlot = (r: Rule, key: string) => {
|
|
@@ -1749,40 +1233,14 @@ help isolate the syntax error.`,
|
|
|
1749
1233
|
.bc((rule: Rule) => {
|
|
1750
1234
|
// TRAVERSE PARENTS TO GET PATH
|
|
1751
1235
|
|
|
1752
|
-
// A DECLARATION IS A PAIR WHOSE KEY IS AN ALIAS NAME. The lexer
|
|
1753
|
-
// claims `%name` whole and hands it over as a #VL token whose
|
|
1754
|
-
// SOURCE is the name, so the key TEXT alone cannot be the test:
|
|
1755
|
-
// a quoted `"%a": 1` is an ordinary key that merely starts with
|
|
1756
|
-
// the sigil, and erasing that would be wrong. The token is what
|
|
1757
|
-
// separates them.
|
|
1758
|
-
//
|
|
1759
|
-
// Recorded on the enclosing map, never on the value, and that is
|
|
1760
|
-
// the point: a reference COPIES the value it resolves to, so a
|
|
1761
|
-
// mark riding the value would erase the referring field too.
|
|
1762
|
-
// Being a property of the map is also what carries it through a
|
|
1763
|
-
// meet, the way optional keys are carried.
|
|
1764
1236
|
const ktkn: any = rule.o0
|
|
1765
1237
|
const holder: any = rule.parent
|
|
1766
1238
|
const kr = keyRefusalOf(ktkn, rule.o1)
|
|
1767
1239
|
if (null != kr) {
|
|
1768
|
-
// A KEY REFUSAL (keyRefusalOf) is written where the map is
|
|
1769
|
-
// built, in the value's place and sited at the key, so the
|
|
1770
|
-
// frame points at the spelling to change. A declaration
|
|
1771
|
-
// spelled with a colon is refused rather than read as the
|
|
1772
|
-
// ordinary key `%foo` the text would otherwise become -- a
|
|
1773
|
-
// document written for the old form would then generate a
|
|
1774
|
-
// "%foo" field and every `%foo` use would resolve to nothing,
|
|
1775
|
-
// and neither says why.
|
|
1776
1240
|
holder.u.aontu_key_refusals = (holder.u.aontu_key_refusals || [])
|
|
1777
1241
|
holder.u.aontu_key_refusals.push({ key: '' + ktkn.src, tkn: ktkn, ...kr })
|
|
1778
1242
|
}
|
|
1779
1243
|
else if (isAliasDecl(ktkn, rule.o1)) {
|
|
1780
|
-
// Always recorded here; whether the map is ALLOWED to carry
|
|
1781
|
-
// declarations is decided on the VALUE (MapVal.unify), not at
|
|
1782
|
-
// the parse. The parse cannot see it: an INCLUDED file's
|
|
1783
|
-
// declarations are at the root of their own text, and only
|
|
1784
|
-
// once the loaded map is placed does it become apparent that
|
|
1785
|
-
// root is not the document's.
|
|
1786
1244
|
holder.u.aontu_alias_keys = (holder.u.aontu_alias_keys || [])
|
|
1787
1245
|
holder.u.aontu_alias_keys.push('' + ktkn.src)
|
|
1788
1246
|
}
|
|
@@ -1798,11 +1256,6 @@ help isolate the syntax error.`,
|
|
|
1798
1256
|
})
|
|
1799
1257
|
|
|
1800
1258
|
.close([
|
|
1801
|
-
// A following `&:` starts a sibling spread pair in the current
|
|
1802
|
-
// map: directly inside a braced map (pk<=0) at any depth, or in
|
|
1803
|
-
// the implicit top-level map (dmap<=1). Inside an implicit
|
|
1804
|
-
// colon-chain map (pk>0) it bubbles up instead (second alt), so
|
|
1805
|
-
// `a:b:1 &:2` attaches the spread to a's map, not b's.
|
|
1806
1259
|
{ s: [CJ, CL], c: (r) => r.lte('pk', 0) || r.lte('dmap', 1), r: 'pair', b: 2, g: 'spread,json,pair' },
|
|
1807
1260
|
{ s: [CJ, CL], b: 2, g: 'spread,json,more' }
|
|
1808
1261
|
])
|
|
@@ -1844,17 +1297,6 @@ help isolate the syntax error.`,
|
|
|
1844
1297
|
g: 'aontu-optional-elem'
|
|
1845
1298
|
},
|
|
1846
1299
|
|
|
1847
|
-
// A PLAIN pair in list position IS A SINGLE-KEY MAP ELEMENT:
|
|
1848
|
-
// `[a:1, b:2]` is `[{a:1}, {b:2}]` (the rule @tabnas/jsonic
|
|
1849
|
-
// spells as `list.pair`). This REVERSES issue #40's "a pair is
|
|
1850
|
-
// not an element": that rule was chosen because jsonic wrote
|
|
1851
|
-
// the pair at `node[key]` -- an array PROPERTY that never
|
|
1852
|
-
// showed up for a text key and an INDEX for a numeric one --
|
|
1853
|
-
// and inert beat that incoherence. But inert was itself a
|
|
1854
|
-
// silent drop: `x: [a:1, b:2]` evaluated to `x: []`, the
|
|
1855
|
-
// author's data gone at exit 0. The element is built in the
|
|
1856
|
-
// bc below, where the value is already a Val; the snapshot
|
|
1857
|
-
// still neutralises jsonic's raw slot write first.
|
|
1858
1300
|
{
|
|
1859
1301
|
s: [OPTKEY, CL], p: 'val',
|
|
1860
1302
|
u: { spread: true, done: true, list: true, pair: true },
|
|
@@ -1867,36 +1309,7 @@ help isolate the syntax error.`,
|
|
|
1867
1309
|
])
|
|
1868
1310
|
|
|
1869
1311
|
|
|
1870
|
-
// NOTE: manually adjust path - the twin of the `pair` rule's hook
|
|
1871
|
-
// above, and for the same reason, one layer down.
|
|
1872
|
-
//
|
|
1873
|
-
// Every alt above contributes NO element: a `&:` spread is a
|
|
1874
|
-
// constraint on the elements, and a `k:v` pair in list position is
|
|
1875
|
-
// simply not one (the `aontu-plain-pair-elem` note above). The array
|
|
1876
|
-
// slot they briefly occupy is already given back by
|
|
1877
|
-
// restorePairSlot. The PATH index was not: @tabnas/path's
|
|
1878
|
-
// `@elem-ao` increments `r.k.index` for every elem rule it sees, so
|
|
1879
|
-
// each of these stole an index and every later element's path was
|
|
1880
|
-
// one too high — `[&: integer, 10, 20, "bad"]` reported the bad
|
|
1881
|
-
// value at `$.l.3` while `aontu get $.l.2` returned it, and on a
|
|
1882
|
-
// one-element list the path pointed off the end. Generation was
|
|
1883
|
-
// never wrong, which is why nothing caught it: the array is right
|
|
1884
|
-
// and only the labels on it were shifted (BUGS.md 44).
|
|
1885
|
-
//
|
|
1886
|
-
// Rewinding here rather than in the plugin keeps the plugin's rule
|
|
1887
|
-
// ("in an array, the path property is the element index") true —
|
|
1888
|
-
// these alts are the aontu-specific exceptions to what counts as an
|
|
1889
|
-
// element, so the correction belongs with the grammar that
|
|
1890
|
-
// introduces them. The child is re-pathed because the plugin has
|
|
1891
|
-
// already stamped it with the index being given back: a spread
|
|
1892
|
-
// takes the `'&'` segment its map twin takes, and a pair takes its
|
|
1893
|
-
// key, as a map entry would.
|
|
1894
1312
|
.ao((r) => {
|
|
1895
|
-
// A pair IS an element now, so it keeps the index @tabnas/path
|
|
1896
|
-
// gave it, and its VALUE is pathed through both the index and
|
|
1897
|
-
// the key (`[a: $.nope]` fails at $.l.0.a). Only the `&:`
|
|
1898
|
-
// spread still contributes no element and gives its index back
|
|
1899
|
-
// (BUGS.md 44).
|
|
1900
1313
|
if (0 < r.d && r.u.spread && !r.u.pair) {
|
|
1901
1314
|
r.k.index = r.k.index - 1
|
|
1902
1315
|
|
|
@@ -1905,11 +1318,6 @@ help isolate the syntax error.`,
|
|
|
1905
1318
|
r.child.k.key = seg
|
|
1906
1319
|
}
|
|
1907
1320
|
else if (0 < r.d && r.u.pair) {
|
|
1908
|
-
// The element's index is the array length: everything before
|
|
1909
|
-
// it is already pushed, and the pair's own map is pushed at
|
|
1910
|
-
// close. `r.k.index` is not usable here -- the path plugin
|
|
1911
|
-
// counts only the elements it pushes itself, and this one is
|
|
1912
|
-
// aontu's.
|
|
1913
1321
|
const seg = '' + r.u.key
|
|
1914
1322
|
r.child.k.path =
|
|
1915
1323
|
[...r.k.path, '' + (r.node?.length ?? 0), seg]
|
|
@@ -1920,41 +1328,16 @@ help isolate the syntax error.`,
|
|
|
1920
1328
|
.bc((rule: Rule, ctx: JsonicContext) => {
|
|
1921
1329
|
// TRAVERSE PARENTS TO GET PATH
|
|
1922
1330
|
|
|
1923
|
-
// Only the `&:` alternative is a SPREAD. All four alts above set
|
|
1924
|
-
// `spread: true` -- it is what marks them as contributing no
|
|
1925
|
-
// element -- so this guard needs the narrower test, and `pair`
|
|
1926
|
-
// is what distinguishes a `k:v` in list position from a spread.
|
|
1927
|
-
//
|
|
1928
|
-
// Without it a pair BUILT the spread record, with `o` taken from
|
|
1929
|
-
// its own key rather than '&': `[x:1, &:integer, "bad"]` left
|
|
1930
|
-
// `{o:'x', v:[1, integer]}`, and ListVal's `'&' === spread.o`
|
|
1931
|
-
// then discarded the real constraint -- so the element spread
|
|
1932
|
-
// was silently dropped and the bad value generated (BUGS.md 46).
|
|
1933
|
-
// A pair alone did it too: `[x:1, 10]` produced a spread record
|
|
1934
|
-
// out of nothing.
|
|
1935
1331
|
if (rule.u.spread && !rule.u.pair) {
|
|
1936
1332
|
rule.node[SPREAD] =
|
|
1937
1333
|
(rule.node[SPREAD] || { o: rule.o0.src, v: [] })
|
|
1938
1334
|
rule.node[SPREAD].v.push(rule.child.node)
|
|
1939
1335
|
}
|
|
1940
1336
|
|
|
1941
|
-
// The slot is given back BEFORE the element is added: the
|
|
1942
|
-
// restore undoes jsonic's raw write (a property for a text
|
|
1943
|
-
// key, an INDEX for a numeric one -- restoring length is what
|
|
1944
|
-
// keeps `[1:2]` from padding with a null), and the push then
|
|
1945
|
-
// appends cleanly after it.
|
|
1946
1337
|
restorePairSlot(rule)
|
|
1947
1338
|
|
|
1948
|
-
// THE SINGLE-KEY MAP ELEMENT, for both pair spellings. The
|
|
1949
|
-
// value is a Val already (`p: 'val'`), so the map is built
|
|
1950
|
-
// exactly as the map rule builds one -- and an elided value
|
|
1951
|
-
// (`[a:]`) is refused exactly as the map rule refuses one
|
|
1952
|
-
// (issue #48): a key with nothing after the colon is a
|
|
1953
|
-
// mistake, not an empty value.
|
|
1954
1339
|
if (true === rule.u.pair) {
|
|
1955
1340
|
const key = '' + rule.u.key
|
|
1956
|
-
// The key TOKEN: the optional spelling's sits on the elem rule
|
|
1957
|
-
// before this one (`[x?: 1]` is two elem rules).
|
|
1958
1341
|
const ktkn: any = true === rule.u.aontu_optional_elem ?
|
|
1959
1342
|
rule.prev.o0 : rule.o0
|
|
1960
1343
|
let v: any = rule.child.node
|
|
@@ -1983,9 +1366,6 @@ help isolate the syntax error.`,
|
|
|
1983
1366
|
// "is this the top level" test reads the path, so an element
|
|
1984
1367
|
// of a top-level list must not read as the root.
|
|
1985
1368
|
mv.path = [...(rule.k?.path ?? []), '' + rule.node.length]
|
|
1986
|
-
// `[a?: 1]` is `[{a?: 1}]`: the key is optional IN the
|
|
1987
|
-
// element, so the two spellings stay one rule apart rather
|
|
1988
|
-
// than two behaviours apart.
|
|
1989
1369
|
if (true === rule.u.aontu_optional_elem) {
|
|
1990
1370
|
mv.optionalKeys = [key]
|
|
1991
1371
|
}
|
|
@@ -2009,45 +1389,11 @@ help isolate the syntax error.`,
|
|
|
2009
1389
|
}
|
|
2010
1390
|
|
|
2011
1391
|
|
|
2012
|
-
|
|
2013
|
-
// INCLUDE_KINDS IS THE RULE FOR WHAT AN INCLUDE MEANS (ADR-012,
|
|
2014
|
-
// use-cases/BUGS.md §49). An extension is on this list or it is not
|
|
2015
|
-
// read at all, and its entry says WHICH OF TWO THINGS the file is.
|
|
2016
|
-
//
|
|
2017
|
-
// `source` — Aontu, with everything the language has: types, defaults,
|
|
2018
|
-
// references, constraints, its own includes. Two extensions, and they
|
|
2019
|
-
// are the ones this project owns.
|
|
2020
|
-
//
|
|
2021
|
-
// `text` — the file's BYTES, as one string scalar. No parser is
|
|
2022
|
-
// chosen, so there is nothing for two ports to disagree about and
|
|
2023
|
-
// nothing in the file that can mean anything: `notes: @"notes.txt"`
|
|
2024
|
-
// is a document loading prose into a string. `.txt` is the only
|
|
2025
|
-
// extension here by default; `AontuOptions.textExt` (the CLI's
|
|
2026
|
-
// `--text-ext`) adds others, because which name a project keeps its
|
|
2027
|
-
// templates under is the project's business, not this table's.
|
|
2028
|
-
//
|
|
2029
|
-
// A FORMAT NAME — configuration DATA, parsed by that format's own
|
|
2030
|
-
// parser into the JSON value it denotes, which then becomes Aontu
|
|
2031
|
-
// values like any other data. Every one of these formats maps onto
|
|
2032
|
-
// JSON, which is why one word covers them: a `.toml` file is a map of
|
|
2033
|
-
// scalars, lists and maps, and so is the `.aon` file that unifies with
|
|
2034
|
-
// it. What the format does NOT get is the language — a `&` in a YAML
|
|
2035
|
-
// file is a YAML anchor, not a spread key, because the YAML parser
|
|
2036
|
-
// reads it, not this one.
|
|
2037
|
-
//
|
|
2038
|
-
// The parsers are @tabnas's, one per format, and the Go port uses the
|
|
2039
|
-
// same ones (ADR-001): the two implementations agree because they are
|
|
2040
|
-
// running the same grammar, not because two hand-written readers were
|
|
2041
|
-
// kept in step.
|
|
2042
|
-
//
|
|
2043
|
-
// This table and go/source.go's includeKinds are the same table.
|
|
2044
1392
|
const INCLUDE_KINDS: { [kind: string]: string } = {
|
|
2045
1393
|
aon: 'source',
|
|
2046
1394
|
aontu: 'source',
|
|
2047
1395
|
|
|
2048
1396
|
json: 'json',
|
|
2049
|
-
// JSON-LD is JSON: a `@context` is a key like any other here, and
|
|
2050
|
-
// what it MEANS is the vocabulary's business, not the reader's.
|
|
2051
1397
|
jsonld: 'json',
|
|
2052
1398
|
jsonc: 'jsonc',
|
|
2053
1399
|
json5: 'json5',
|
|
@@ -2062,26 +1408,12 @@ const INCLUDE_KINDS: { [kind: string]: string } = {
|
|
|
2062
1408
|
}
|
|
2063
1409
|
|
|
2064
1410
|
|
|
2065
|
-
// WHAT AN EXTENSION MEANS, for this parse. The table is the fixed
|
|
2066
|
-
// part; `textExt` is the per-parse widening, and it wins over nothing
|
|
2067
|
-
// -- a host cannot re-read `.toml` as text, because an extension the
|
|
2068
|
-
// table already names has a meaning documents rely on. Undefined is
|
|
2069
|
-
// the refusal, and it is the ONE place that decides it: the resolver's
|
|
2070
|
-
// gate and the processor map both ask here, so a widening cannot reach
|
|
2071
|
-
// one and not the other.
|
|
2072
1411
|
function includeFormat(
|
|
2073
1412
|
ext: string, textExt?: string[]): string | undefined {
|
|
2074
1413
|
const known = INCLUDE_KINDS[ext]
|
|
2075
1414
|
if (undefined !== known) {
|
|
2076
1415
|
return known
|
|
2077
1416
|
}
|
|
2078
|
-
// NOT EVEN AS TEXT. `.js` is the extension ADR-012 singles out
|
|
2079
|
-
// because multisource's own default EXECUTES it, and an extension
|
|
2080
|
-
// this project refuses on purpose stays refused however a flag is
|
|
2081
|
-
// spelled -- reading it is harmless, but a widening that can reach
|
|
2082
|
-
// the one name the rule names is a widening whose limit nobody can
|
|
2083
|
-
// state. Go's includeFormat holds the same list, and the two CLIs
|
|
2084
|
-
// are diffed on `--text-ext js` because they once disagreed here.
|
|
2085
1417
|
if (REFUSED_EXT.has(ext)) {
|
|
2086
1418
|
return undefined
|
|
2087
1419
|
}
|
|
@@ -2094,18 +1426,7 @@ function includeFormat(
|
|
|
2094
1426
|
// file type at all.
|
|
2095
1427
|
const REFUSED_EXT = new Set(['js', ''])
|
|
2096
1428
|
|
|
2097
|
-
|
|
2098
|
-
// taste. The two ports' CSV parsers disagree about what a CSV file IS:
|
|
2099
|
-
// one answers header-keyed records with string fields, the other raw
|
|
2100
|
-
// rows including the header, with numbers parsed. Admitting it would
|
|
2101
|
-
// admit a divergence into the one thing this project refuses to have
|
|
2102
|
-
// one in. Recorded in ADR-012 and pinned by file.tsv's load-ext-csv.
|
|
2103
|
-
|
|
2104
|
-
// The multisource kind of a path: the LAST segment's extension, without
|
|
2105
|
-
// its dot, lowercased -- `''` for a name that has none. The rule is
|
|
2106
|
-
// @tabnas/multisource's own extKind (and Go's filepath.Ext), copied
|
|
2107
|
-
// rather than imported because it decides what a source IS: a dot in a
|
|
2108
|
-
// parent folder (`/my.app/conf`) must not read as an extension.
|
|
1429
|
+
|
|
2109
1430
|
function extKindOf(full: string): string {
|
|
2110
1431
|
const seg = (full.match(/[^\\/]*$/) as string[])[0]
|
|
2111
1432
|
return (seg.match(/\.([^.]*)$/) || ['', ''])[1].toLowerCase()
|
|
@@ -2120,15 +1441,6 @@ function extensionMsg(path: string, ext: string): string {
|
|
|
2120
1441
|
return 'include not readable: ' + path + ' (' + which + ')'
|
|
2121
1442
|
}
|
|
2122
1443
|
|
|
2123
|
-
// THE RULE ALSO HOLDS FOR A RESOLVER THIS ENGINE DID NOT WRITE.
|
|
2124
|
-
// gateExtension refuses an unlisted extension inside makeModelResolver,
|
|
2125
|
-
// which is the default; a HOST may supply its own through
|
|
2126
|
-
// `AontuOptions.resolver`, and that one has never heard of
|
|
2127
|
-
// INCLUDE_KINDS. Without this the host's resolution would fall to
|
|
2128
|
-
// multisource's own default for an unnamed kind, which hands the file
|
|
2129
|
-
// back as TEXT — or, for `.js`, EXECUTES it. So the two roads end in
|
|
2130
|
-
// one place: whatever chose the source, an extension off the list is
|
|
2131
|
-
// refused with the same code and the same message.
|
|
2132
1444
|
const refuseProcessor = (res: any) => {
|
|
2133
1445
|
// `full` is the one part a host resolution may leave out -- it is the
|
|
2134
1446
|
// path the resolver CHOSE, and a resolver that answers from something
|
|
@@ -2168,22 +1480,6 @@ const DATA_READERS: { [format: string]: (src: string, fileName: string) => any }
|
|
|
2168
1480
|
}
|
|
2169
1481
|
})()
|
|
2170
1482
|
|
|
2171
|
-
/**
|
|
2172
|
-
* A TOML document with its dates as the TEXT they were written as.
|
|
2173
|
-
*
|
|
2174
|
-
* TOML HAS DATES AND JSON DOES NOT, so the reader cannot hand one over
|
|
2175
|
-
* as itself: it answers with a marker object carrying the kind and the
|
|
2176
|
-
* source text. The value that reaches a document is that TEXT, which is
|
|
2177
|
-
* what a JSON document carries for a date anyway — and it is what the
|
|
2178
|
-
* Go port produces too, from a `*TomlTime` holding those same two
|
|
2179
|
-
* fields (`dataToValDepth`, go/source.go). Without this the same file
|
|
2180
|
-
* is a nested map in one port and a string in the other, which is the
|
|
2181
|
-
* class of divergence ADR-012 exists to stop.
|
|
2182
|
-
*
|
|
2183
|
-
* The guard is exact — one key, `__toml__`, holding a `kind` and a
|
|
2184
|
-
* `src` string — so a document whose own data happens to use the name
|
|
2185
|
-
* passes through untouched.
|
|
2186
|
-
*/
|
|
2187
1483
|
function tomlDates(node: any): any {
|
|
2188
1484
|
if (Array.isArray(node)) {
|
|
2189
1485
|
return node.map(tomlDates)
|
|
@@ -2204,33 +1500,14 @@ function tomlDates(node: any): any {
|
|
|
2204
1500
|
return out
|
|
2205
1501
|
}
|
|
2206
1502
|
|
|
2207
|
-
/**
|
|
2208
|
-
* Read one included file as DATA in the named format.
|
|
2209
|
-
*
|
|
2210
|
-
* The parser hands back the JSON value the file denotes — plain maps,
|
|
2211
|
-
* lists and scalars — and rawToVal turns that into Vals. THE
|
|
2212
|
-
* CONVERSION HAPPENS HERE, not at the top level, because an include is
|
|
2213
|
-
* usually not at the top level: `a: @"conf.toml"` puts the value under
|
|
2214
|
-
* a key, where a raw JavaScript object is something the tree cannot
|
|
2215
|
-
* unify with (the crash that was BUGS §49b).
|
|
2216
|
-
*/
|
|
2217
1503
|
const dataProcessor = (format: string) => (res: any) => {
|
|
2218
1504
|
res.val = rawToVal(DATA_READERS[format](res.src, res.path))
|
|
2219
1505
|
}
|
|
2220
1506
|
|
|
2221
|
-
// TEXT IS NOT PARSED. The bytes multisource read are the value, so
|
|
2222
|
-
// this is the one processor with no reader behind it -- which is why
|
|
2223
|
-
// a `.txt` include cannot fail on content, only on being unreadable.
|
|
2224
1507
|
const textProcessor = (res: any) => {
|
|
2225
1508
|
res.val = new StringVal({ peg: res.src })
|
|
2226
1509
|
}
|
|
2227
1510
|
|
|
2228
|
-
/**
|
|
2229
|
-
* The multisource processor map, built FROM the include table so the
|
|
2230
|
-
* two cannot drift: every extension the table names gets the reader
|
|
2231
|
-
* the table names for it, and the two kinds that are not in the table
|
|
2232
|
-
* refuse.
|
|
2233
|
-
*/
|
|
2234
1511
|
function includeProcessors(textExt?: string[]): { [kind: string]: any } {
|
|
2235
1512
|
const map: { [kind: string]: any } = {
|
|
2236
1513
|
// multisource's fallback for an extension no entry names, so it is
|
|
@@ -2249,10 +1526,6 @@ function includeProcessors(textExt?: string[]): { [kind: string]: any } {
|
|
|
2249
1526
|
for (const kind of Object.keys(INCLUDE_KINDS)) {
|
|
2250
1527
|
map[kind] = forKind(kind)
|
|
2251
1528
|
}
|
|
2252
|
-
// A WIDENING NEVER OVERWRITES. `js` and the empty fallback refuse
|
|
2253
|
-
// above and stay refusing: `--text-ext js` would otherwise turn the
|
|
2254
|
-
// one extension ADR-012 singles out into a readable one, by a flag
|
|
2255
|
-
// whose whole promise is that it chooses no parser.
|
|
2256
1529
|
for (const ext of textExt ?? []) {
|
|
2257
1530
|
if (undefined === map[ext]) {
|
|
2258
1531
|
map[ext] = textProcessor
|
|
@@ -2262,17 +1535,8 @@ function includeProcessors(textExt?: string[]): { [kind: string]: any } {
|
|
|
2262
1535
|
}
|
|
2263
1536
|
|
|
2264
1537
|
|
|
2265
|
-
//
|
|
2266
|
-
//
|
|
2267
|
-
// relative paths (`@"../../etc/passwd.aon"`) and symlinks — so treat
|
|
2268
|
-
// opening an untrusted source as reading your disk. It no longer RUNS
|
|
2269
|
-
// one: @"pkg" could require() an arbitrary installed module until
|
|
2270
|
-
// ADR-012, which refuses a `.js` entry point by the same rule that
|
|
2271
|
-
// refuses `.txt`. The trust profile (G5, docs/trust.md) is the
|
|
2272
|
-
// confinement surface: `trust.include` of
|
|
2273
|
-
// 'none', `{ mem }` or `{ root }` restricts what `@"..."` may resolve,
|
|
2274
|
-
// and a denied resolution is a deterministic parse-stage
|
|
2275
|
-
// `include_denied` error.
|
|
1538
|
+
// What a resolved include may reach, and how it is confined:
|
|
1539
|
+
// docs/trust.md.
|
|
2276
1540
|
function makeModelResolver(options: any) {
|
|
2277
1541
|
const useRequire = options.require || require
|
|
2278
1542
|
const capability = options.trust?.include ?? 'system'
|
|
@@ -2284,23 +1548,10 @@ function makeModelResolver(options: any) {
|
|
|
2284
1548
|
'string' === typeof (capability as any).root
|
|
2285
1549
|
? pathResolve((capability as any).root) : undefined
|
|
2286
1550
|
|
|
2287
|
-
// Under the mem capability the CAPABILITY's file set is the whole
|
|
2288
|
-
// world; otherwise the host-injected `options.resolver.mem` entries
|
|
2289
|
-
// remain available under every capability but 'none' — they are
|
|
2290
|
-
// host-provided, not document-requested, so confining them would
|
|
2291
|
-
// confine the host against itself.
|
|
2292
|
-
// A LANGUAGE-SUPPLIED MODEL DOES NOT RIDE THIS LEG (ADR-028). Every
|
|
2293
|
-
// bundled schema is named under `aontu:` and answered by the scheme
|
|
2294
|
-
// leg above, which returns before the memory resolver is built, so
|
|
2295
|
-
// neither a host entry nor a capability's own file set can stand in
|
|
2296
|
-
// front of one: `aontu:system` is the engine's, always. That is what
|
|
2297
|
-
// the prefix buys, and it is why the bare-name leg that once let a
|
|
2298
|
-
// caller shadow `std/system` is gone.
|
|
2299
1551
|
let memResolver = makeMemResolver(memCapability
|
|
2300
1552
|
? { ...(capability as any).mem }
|
|
2301
1553
|
: { ...(options.resolver?.mem || {}) })
|
|
2302
1554
|
|
|
2303
|
-
// TODO: make this consistent with other resolvers
|
|
2304
1555
|
let fileResolver = makeFileResolver((spec: any) => {
|
|
2305
1556
|
return 'string' === typeof spec ? spec : spec?.peg
|
|
2306
1557
|
})
|
|
@@ -2310,23 +1561,6 @@ function makeModelResolver(options: any) {
|
|
|
2310
1561
|
...(options.resolver?.pkg || {})
|
|
2311
1562
|
})
|
|
2312
1563
|
|
|
2313
|
-
// Confinement is realpath-then-prefix-check (docs/trust.md): the
|
|
2314
|
-
// RESOLVED file's real path must sit below the root's real path, so a
|
|
2315
|
-
// symlink inside the root pointing outside it is an escape, not a
|
|
2316
|
-
// loophole. A path realpath cannot resolve falls back to the lexical
|
|
2317
|
-
// form — the comparison is then against what the resolver actually
|
|
2318
|
-
// read.
|
|
2319
|
-
// Real fs, deliberately: `options.fs` is not a sandbox (it feeds
|
|
2320
|
-
// parse text; the file leg reads through its own channel), so the
|
|
2321
|
-
// containment check must see the same filesystem that leg read from.
|
|
2322
|
-
// A path that does not (fully) exist cannot be realpath'd whole, and
|
|
2323
|
-
// falling back to the LEXICAL form compares apples to oranges when
|
|
2324
|
-
// the root itself sits behind a symlink -- on macOS a root under
|
|
2325
|
-
// /var realpaths to /private/var, so a merely-missing file inside it
|
|
2326
|
-
// reads as an escape. Realpath the deepest EXISTING ancestor and
|
|
2327
|
-
// re-attach the rest, so both sides of the check are in real
|
|
2328
|
-
// coordinates. (The MCP server's own confinement carries the twin of
|
|
2329
|
-
// this rule; its CI failure is what found the shape.)
|
|
2330
1564
|
const realpath = (p: string): string => {
|
|
2331
1565
|
try {
|
|
2332
1566
|
return realpathSync(p)
|
|
@@ -2346,12 +1580,6 @@ function makeModelResolver(options: any) {
|
|
|
2346
1580
|
return fullReal !== rootReal && !fullReal.startsWith(rootReal + pathSep)
|
|
2347
1581
|
}
|
|
2348
1582
|
|
|
2349
|
-
// A denial THROWS with the code; Lang.parse converts it to the
|
|
2350
|
-
// parse-stage `include_denied` nil (the same shape a syntax failure
|
|
2351
|
-
// takes). Raising beats injecting a nil value: a bare-member include
|
|
2352
|
-
// (`@"denied.aon"` at the top of a file) MERGES into the enclosing
|
|
2353
|
-
// map, and a nil contributes no keys, so an injected denial would
|
|
2354
|
-
// vanish and leave a plausible, silently-partial document.
|
|
2355
1583
|
const deny = (path: string): never => {
|
|
2356
1584
|
// Only 'none' and 'root' can deny: the mem capability's misses are
|
|
2357
1585
|
// not-found (its set is the whole world), so there is no third arm.
|
|
@@ -2362,12 +1590,6 @@ function makeModelResolver(options: any) {
|
|
|
2362
1590
|
throw err
|
|
2363
1591
|
}
|
|
2364
1592
|
|
|
2365
|
-
// AN UNREADABLE EXTENSION THROWS, exactly as a denial does, and for
|
|
2366
|
-
// the same reason: a bare-member include (`@"notes.txt"` at the top
|
|
2367
|
-
// of a file) MERGES into the enclosing map, and a nil contributes no
|
|
2368
|
-
// keys, so an injected refusal would vanish and leave a plausible,
|
|
2369
|
-
// silently-partial document. Lang.parse turns the throw into the
|
|
2370
|
-
// parse-stage `include_extension` nil.
|
|
2371
1593
|
const refuseExtension = (path: string, full: string): never => {
|
|
2372
1594
|
const err: any = new Error(extensionMsg(path, extKindOf(full)))
|
|
2373
1595
|
err.code = 'include_extension'
|
|
@@ -2386,8 +1608,8 @@ function makeModelResolver(options: any) {
|
|
|
2386
1608
|
throw err
|
|
2387
1609
|
}
|
|
2388
1610
|
|
|
2389
|
-
// The gate every leg that RESOLVES A NAME passes through. The
|
|
2390
|
-
// module legs do not: both state `kind: 'aon'` because what they
|
|
1611
|
+
// The gate every leg that RESOLVES A NAME passes through. The aontu:
|
|
1612
|
+
// and module legs do not: both state `kind: 'aon'` because what they
|
|
2391
1613
|
// serve is Aontu source by construction, not by its spelling.
|
|
2392
1614
|
const gateExtension = (path: string, full: string): void => {
|
|
2393
1615
|
if (undefined === includeFormat(extKindOf(full), options.textExt)) {
|
|
@@ -2460,25 +1682,12 @@ function makeModelResolver(options: any) {
|
|
|
2460
1682
|
deny(path)
|
|
2461
1683
|
}
|
|
2462
1684
|
|
|
2463
|
-
// THE LANGUAGE-SUPPLIED MODELS (docs/design/MODELS.0.md D1): an
|
|
2464
|
-
// `aontu:` name resolves from the engine's own table and nowhere
|
|
2465
|
-
// else -- the memory, module, file and package legs are never
|
|
2466
|
-
// asked, so nothing on disk can shadow one and a typo is refused
|
|
2467
|
-
// here, naming the set, rather than searched for. Available under
|
|
2468
|
-
// every capability but `none`, checked just above. A path that is
|
|
2469
|
-
// not a string (`a: @1`) is not a name at all: it falls through to
|
|
2470
|
-
// the legs below and is not found there, as it always was.
|
|
2471
|
-
//
|
|
2472
|
-
// This is the ONLY leg that serves a bundled model (ADR-028). The
|
|
2473
|
-
// vocabularies once had bare names (`std/system`) and a second leg
|
|
2474
|
-
// below to match them; the prefix is now the whole spelling, so one
|
|
2475
|
-
// leg answers for every language-supplied schema.
|
|
2476
1685
|
if ('string' === typeof path && path.startsWith(AONTU_SCHEME)) {
|
|
2477
|
-
const model =
|
|
1686
|
+
const model = AONTU_SOURCES[path]
|
|
2478
1687
|
if (null == model) {
|
|
2479
1688
|
modelNotFound(path)
|
|
2480
1689
|
}
|
|
2481
|
-
record(ctx, path, '
|
|
1690
|
+
record(ctx, path, 'aontu')
|
|
2482
1691
|
return { found: true, path, full: path, kind: 'aon', src: model, search: [] }
|
|
2483
1692
|
}
|
|
2484
1693
|
|
|
@@ -2495,11 +1704,6 @@ function makeModelResolver(options: any) {
|
|
|
2495
1704
|
return res
|
|
2496
1705
|
}
|
|
2497
1706
|
|
|
2498
|
-
// THE MODULE LEG (G6 phase 2, ts/src/mod.ts): memory -> MODULE ->
|
|
2499
|
-
// filesystem -> package. Memory stays FIRST so a sandbox and the
|
|
2500
|
-
// spec suite can stub a module path without touching disk; a path
|
|
2501
|
-
// that is not module-shaped falls straight through, so no existing
|
|
2502
|
-
// include can be routed somewhere new by this.
|
|
2503
1707
|
const modref = memCapability ? undefined : parseModuleRef(path)
|
|
2504
1708
|
if (null != modref) {
|
|
2505
1709
|
const msmeta = (ctx as any)?.meta?.multisource
|
|
@@ -2586,24 +1790,6 @@ function makeModelResolver(options: any) {
|
|
|
2586
1790
|
}
|
|
2587
1791
|
|
|
2588
1792
|
|
|
2589
|
-
// THE SIGNATURE REGISTRY (docs/design/SIGNATURES.0.md). The call
|
|
2590
|
-
// surface is DECLARED in test/spec/signature.tsv and parsed by the
|
|
2591
|
-
// signature grammar (ts/src/sig.ts) from the build-time-inlined copy;
|
|
2592
|
-
// the arity table and the positional set below are DERIVED from the
|
|
2593
|
-
// parsed registry (funcSig, ts/src/sig.ts), so the declaration is the
|
|
2594
|
-
// one source. go/func.go derives the same two tables from the same
|
|
2595
|
-
// text.
|
|
2596
|
-
// The functions whose comma-separated arguments are distinct POSITIONS
|
|
2597
|
-
// rather than one argument list. See the func-paren handler above: this
|
|
2598
|
-
// is the set whose comma group is expanded back into separate `peg`
|
|
2599
|
-
// entries. Derived: two or more declared argument slots, excluding the
|
|
2600
|
-
// residual producers (`constraint` results) -- the constraint atoms
|
|
2601
|
-
// make the same expansion in their own constructor (`atomArgs`,
|
|
2602
|
-
// ConstraintVal.ts, deliberately before the settled check), which is
|
|
2603
|
-
// why they are not in this set; `must` is the load-bearing example.
|
|
2604
|
-
// Arithmetic is here because `sub` is not commutative: `sub(a, b)`
|
|
2605
|
-
// reaching the engine as one two-element list would lose which is
|
|
2606
|
-
// which.
|
|
2607
1793
|
const POSITIONAL_ARG_FUNCS: Record<string, boolean> = {}
|
|
2608
1794
|
for (const name in funcSig) {
|
|
2609
1795
|
if (2 <= funcSig[name].args.length && 'constraint' !== funcSig[name].out) {
|
|
@@ -2611,14 +1797,6 @@ for (const name in funcSig) {
|
|
|
2611
1797
|
}
|
|
2612
1798
|
}
|
|
2613
1799
|
|
|
2614
|
-
// THE COMPONENT PRIMITIVES, listed by hand (the spike, see funcMap
|
|
2615
|
-
// above and ts/src/val/CmpFuncVal.ts). They carry no signature
|
|
2616
|
-
// declaration, so the derivation above cannot see them -- and
|
|
2617
|
-
// `file("main.ts", [...])` without an entry arrives as ONE raw array
|
|
2618
|
-
// term, which the call then reads as its spec. The leaves are here
|
|
2619
|
-
// too, so that a written second argument is COUNTED and refused as
|
|
2620
|
-
// the arity mistake it is rather than arriving as a one-element list
|
|
2621
|
-
// that is merely the wrong shape.
|
|
2622
1800
|
for (const name of Object.keys(CMP_FUNCS)) {
|
|
2623
1801
|
POSITIONAL_ARG_FUNCS[name] = true
|
|
2624
1802
|
}
|
|
@@ -2626,16 +1804,6 @@ POSITIONAL_ARG_FUNCS['nom'] = true
|
|
|
2626
1804
|
POSITIONAL_ARG_FUNCS['translate'] = true
|
|
2627
1805
|
|
|
2628
1806
|
|
|
2629
|
-
// [min, max]; a max of -1 is unbounded. Every DECLARED name has an
|
|
2630
|
-
// entry, and the arity is a property of the language rather than of
|
|
2631
|
-
// either port -- go/func.go derives the same table. The spike's
|
|
2632
|
-
// component primitives are the one exception: they carry no
|
|
2633
|
-
// declaration, so they have no entry here and refuse a bad count in
|
|
2634
|
-
// their own resolve (ts/src/val/CmpFuncVal.ts). A required slot
|
|
2635
|
-
// counts toward the minimum; a rest slot makes the maximum unbounded
|
|
2636
|
-
// and counts its group size (one, for a plain rest type) toward the
|
|
2637
|
-
// minimum, which is what gives `match` its floor of three and `neq`
|
|
2638
|
-
// its floor of one.
|
|
2639
1807
|
function sigArity(sig: FuncSig): [number, number] {
|
|
2640
1808
|
let min = 0
|
|
2641
1809
|
let max = 0
|
|
@@ -2662,14 +1830,6 @@ for (const name in funcSig) {
|
|
|
2662
1830
|
}
|
|
2663
1831
|
|
|
2664
1832
|
|
|
2665
|
-
// writtenArgCount counts the arguments as the AUTHOR wrote them.
|
|
2666
|
-
//
|
|
2667
|
-
// It cannot simply be terms.length: a comma group reaches the func-paren
|
|
2668
|
-
// handler as ONE term holding a raw array, so `upper("a","b")` and
|
|
2669
|
-
// `upper(["a","b"])` both arrive as a single argument. They are still
|
|
2670
|
-
// distinguishable, and that is what makes an arity check possible at
|
|
2671
|
-
// all -- the comma group is a RAW array, while a written list literal
|
|
2672
|
-
// has already been built into a ListVal by the list rule.
|
|
2673
1833
|
function writtenArgCount(terms: any[]): number {
|
|
2674
1834
|
if (1 === terms.length) {
|
|
2675
1835
|
// `terms[0]` is re-read rather than reusing a narrowed local:
|
|
@@ -2696,12 +1856,6 @@ function arityText(lo: number, hi: number): string {
|
|
|
2696
1856
|
if (0 === lo) {
|
|
2697
1857
|
return 'no arguments or one'
|
|
2698
1858
|
}
|
|
2699
|
-
// The case range gave `upper` and `lower` a span rather than a
|
|
2700
|
-
// pair, and a two-arm phrasing cannot say it: [1,3] read as "one
|
|
2701
|
-
// argument or two", which is a wrong count rather than an
|
|
2702
|
-
// imprecise one. Each arm here is a shape the table actually
|
|
2703
|
-
// carries, and each is pinned by a row -- a phrasing for a count no
|
|
2704
|
-
// entry has would be untested prose pretending to be tested.
|
|
2705
1859
|
return 3 === hi ? 'one to three arguments' : 'one argument or two'
|
|
2706
1860
|
}
|
|
2707
1861
|
// The {0,0} arm returned with the container kinds and acyclic()
|
|
@@ -2716,17 +1870,6 @@ function arityText(lo: number, hi: number): string {
|
|
|
2716
1870
|
}
|
|
2717
1871
|
|
|
2718
1872
|
|
|
2719
|
-
// rawToVal converts a raw parse node (or raw elements inside one) into
|
|
2720
|
-
// the matching Val. Used for implicit top-level lists, whose nodes skip
|
|
2721
|
-
// the aontu val rule conversions (mirrors asVal in go/lang.go; like
|
|
2722
|
-
// there, source text is unavailable, so an integral number is an
|
|
2723
|
-
// integer).
|
|
2724
|
-
// The targeted parse hint for CUE-trained authors and models: `>` and
|
|
2725
|
-
// `<` are not Aontu operators (the op-chars reservation stands), and an
|
|
2726
|
-
// agent that emits `number > 0` should be redirected to the bound
|
|
2727
|
-
// atoms, not left with a bare "unexpected character". Appended to a
|
|
2728
|
-
// parse error's message when the source carries an unquoted `<` or
|
|
2729
|
-
// `>`; the Go twin is opCharHint in go/lang.go, byte-identical text.
|
|
2730
1873
|
function opCharHint(src: string): string {
|
|
2731
1874
|
let q = ''
|
|
2732
1875
|
for (let i = 0; i < src.length; i++) {
|
|
@@ -2757,12 +1900,6 @@ function rawToVal(n: any): Val {
|
|
|
2757
1900
|
return new ListVal({ peg: n.map(rawToVal) })
|
|
2758
1901
|
}
|
|
2759
1902
|
|
|
2760
|
-
// THE SCALAR ARMS ARE WHERE A CONFIG FILE BECOMES VALUES. Every
|
|
2761
|
-
// format on the include table is read by its own parser into plain
|
|
2762
|
-
// JavaScript -- a string, a number, a map -- and this is the walk
|
|
2763
|
-
// that turns that into Vals (dataProcessor, ADR-012). The two arms
|
|
2764
|
-
// above are the other caller: a raw expression TERM, which the
|
|
2765
|
-
// expression grammar hands over already built.
|
|
2766
1903
|
if (null == n) {
|
|
2767
1904
|
return new NullVal({ peg: null })
|
|
2768
1905
|
}
|
|
@@ -2771,24 +1908,12 @@ function rawToVal(n: any): Val {
|
|
|
2771
1908
|
return new StringVal({ peg: n })
|
|
2772
1909
|
}
|
|
2773
1910
|
if ('number' === t) {
|
|
2774
|
-
// No source text here, so the "no '.'" condition is vacuous and the
|
|
2775
|
-
// integral + int64-range conditions decide (same helper as the val
|
|
2776
|
-
// rule, so the two paths cannot drift).
|
|
2777
1911
|
return isIntegerKind(n) ?
|
|
2778
1912
|
new IntegerVal({ peg: n }) : new NumberVal({ peg: n })
|
|
2779
1913
|
}
|
|
2780
1914
|
if ('boolean' === t) {
|
|
2781
1915
|
return new BooleanVal({ peg: n })
|
|
2782
1916
|
}
|
|
2783
|
-
// AND EVERYTHING ELSE IS A MAP, with no arm after it because there is
|
|
2784
|
-
// nothing after it. Every reader on the include table answers with
|
|
2785
|
-
// the JSON kinds and no others -- probed, including the two that
|
|
2786
|
-
// could plausibly escape them: a big integer comes back a `number`,
|
|
2787
|
-
// and a TOML date is normalised to its text before it gets here. The
|
|
2788
|
-
// one include that could hand over a function was `.js`, which
|
|
2789
|
-
// ADR-012 refuses. `parse_unknown` lived here for that case and has
|
|
2790
|
-
// no producer left in this port; the Go twin keeps its own, where the
|
|
2791
|
-
// type switch really can be handed something unaccounted for.
|
|
2792
1917
|
const peg: Record<string, Val> = {}
|
|
2793
1918
|
for (const k in n) {
|
|
2794
1919
|
peg[k] = rawToVal(n[k])
|
|
@@ -2804,7 +1929,6 @@ class Lang {
|
|
|
2804
1929
|
|
|
2805
1930
|
|
|
2806
1931
|
constructor(options?: Partial<AontuOptions>) {
|
|
2807
|
-
// const start = performance.now()
|
|
2808
1932
|
|
|
2809
1933
|
this.opts = Object.assign(DEFAULT_OPTS(), options) as AontuOptions
|
|
2810
1934
|
|
|
@@ -2821,25 +1945,7 @@ class Lang {
|
|
|
2821
1945
|
this.jsonic
|
|
2822
1946
|
.use(asPlugin(MultiSource), {
|
|
2823
1947
|
resolver: options?.resolver || modelResolver,
|
|
2824
|
-
// `.aon` is the preferred Aontu source extension; `.aontu` also
|
|
2825
|
-
// works. `.jsonic` is retired (no longer auto-resolved); the
|
|
2826
|
-
// default `['jsonic','jsc','json','js']` is overridden here.
|
|
2827
|
-
// (Upstream option name is the misspelled `implictExt`.)
|
|
2828
|
-
//
|
|
2829
|
-
// Only these two are SEARCHED for a bare `@"name"`; `.json` and
|
|
2830
|
-
// `.jsonld` are read when NAMED, which is how a vendored
|
|
2831
|
-
// vocabulary is always written.
|
|
2832
1948
|
implictExt: ['aon', 'aontu'],
|
|
2833
|
-
// ONE ENTRY PER EXTENSION THE TABLE NAMES, built from it (see
|
|
2834
|
-
// includeProcessors) so the rule and its wiring cannot drift.
|
|
2835
|
-
//
|
|
2836
|
-
// The upstream defaults are REPLACED, not extended. Its `json`
|
|
2837
|
-
// entry is what made that extension the one that crashed: it
|
|
2838
|
-
// hands back a raw JS object where the aontu grammar produces
|
|
2839
|
-
// Vals, and the tree then met a value it could not convert
|
|
2840
|
-
// (BUGS §49b). Its `js` entry EXECUTES the file, which is not
|
|
2841
|
-
// something an extension should be able to ask for. And its
|
|
2842
|
-
// fallback hands any other file back as TEXT.
|
|
2843
1949
|
processor: includeProcessors(this.opts.textExt)
|
|
2844
1950
|
})
|
|
2845
1951
|
.use(AontuJsonic)
|
|
@@ -2847,7 +1953,6 @@ class Lang {
|
|
|
2847
1953
|
|
|
2848
1954
|
|
|
2849
1955
|
parse(src: string, opts?: Partial<AontuOptions>): Val {
|
|
2850
|
-
// const start = performance.now()
|
|
2851
1956
|
|
|
2852
1957
|
// JSONIC-UPDATE - check meta
|
|
2853
1958
|
let jm: any = {
|
|
@@ -2874,7 +1979,6 @@ class Lang {
|
|
|
2874
1979
|
jm.log = opts.log
|
|
2875
1980
|
}
|
|
2876
1981
|
|
|
2877
|
-
// jm.log = -1
|
|
2878
1982
|
|
|
2879
1983
|
let val: Val
|
|
2880
1984
|
|
|
@@ -2892,14 +1996,6 @@ class Lang {
|
|
|
2892
1996
|
catch (e: any) {
|
|
2893
1997
|
if ('include_denied' === e?.code || 'include_extension' === e?.code ||
|
|
2894
1998
|
'multisource_not_found' === e?.code || MODULE_REFUSAL_CODES.has(e?.code)) {
|
|
2895
|
-
// A denied include (G5), an include whose extension is not read
|
|
2896
|
-
// as Aontu source (ADR-012, INCLUDE_KINDS), an `aontu:` name the
|
|
2897
|
-
// engine does not serve (MODELS.0.md D1), and a module that is
|
|
2898
|
-
// missing, fails its pin, or names a path that escapes its store
|
|
2899
|
-
// (G6 phase 2) are refused the same way, for the same reason: the
|
|
2900
|
-
// resolver THROWS so a bare-member include cannot vanish in the
|
|
2901
|
-
// merge, and the code survives here as the parse-stage nil the
|
|
2902
|
-
// registry pins (errcodes.tsv).
|
|
2903
1999
|
val = new NilVal({
|
|
2904
2000
|
why: 'parse',
|
|
2905
2001
|
err: new NilVal({
|
|
@@ -2915,14 +2011,6 @@ class Lang {
|
|
|
2915
2011
|
msg: e.message + opCharHint(src),
|
|
2916
2012
|
err: e,
|
|
2917
2013
|
})
|
|
2918
|
-
// THE POSITION TRAVELS WITH IT. The parser knows exactly where
|
|
2919
|
-
// it stopped -- it draws a caret there -- and the rendered
|
|
2920
|
-
// message carried the only copy, so `vet --format json`
|
|
2921
|
-
// reported row -1, col -1 for a document whose fault the human
|
|
2922
|
-
// renderer located to the character. A machine-readable report
|
|
2923
|
-
// that says "somewhere in this file" is the one a repair loop
|
|
2924
|
-
// can do nothing with. Both numbers are already 1-based here,
|
|
2925
|
-
// which is the base a site uses (go/lang.go does the same).
|
|
2926
2014
|
if ('number' === typeof e.lineNumber) {
|
|
2927
2015
|
syntax.site.row = e.lineNumber
|
|
2928
2016
|
}
|