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.
Files changed (356) hide show
  1. package/README.md +1 -1
  2. package/dist/agentsmd.js +0 -27
  3. package/dist/agentsmd.js.map +1 -1
  4. package/dist/alias.js.map +1 -1
  5. package/dist/allow.js +0 -92
  6. package/dist/allow.js.map +1 -1
  7. package/dist/aontu.d.ts +1 -1
  8. package/dist/aontu.js +1 -79
  9. package/dist/aontu.js.map +1 -1
  10. package/dist/aontumodel.d.ts +4 -0
  11. package/dist/aontumodel.js +33 -0
  12. package/dist/aontumodel.js.map +1 -0
  13. package/dist/cli.js +101 -620
  14. package/dist/cli.js.map +1 -1
  15. package/dist/ctx.js +0 -48
  16. package/dist/ctx.js.map +1 -1
  17. package/dist/diff.js +0 -32
  18. package/dist/diff.js.map +1 -1
  19. package/dist/err.js +0 -40
  20. package/dist/err.js.map +1 -1
  21. package/dist/escape.js +0 -45
  22. package/dist/escape.js.map +1 -1
  23. package/dist/exactjson.d.ts +0 -35
  24. package/dist/exactjson.js +0 -131
  25. package/dist/exactjson.js.map +1 -1
  26. package/dist/format.js +13 -203
  27. package/dist/format.js.map +1 -1
  28. package/dist/grammar.d.ts +9 -0
  29. package/dist/grammar.js +54 -0
  30. package/dist/grammar.js.map +1 -0
  31. package/dist/graph.js +0 -26
  32. package/dist/graph.js.map +1 -1
  33. package/dist/hcanon.js +0 -82
  34. package/dist/hcanon.js.map +1 -1
  35. package/dist/helpdoc.js +1 -1
  36. package/dist/helpdoc.js.map +1 -1
  37. package/dist/hints.d.ts +0 -6
  38. package/dist/hints.js +54 -55
  39. package/dist/hints.js.map +1 -1
  40. package/dist/jsonschema.js +0 -114
  41. package/dist/jsonschema.js.map +1 -1
  42. package/dist/keyorder.d.ts +0 -7
  43. package/dist/keyorder.js +0 -41
  44. package/dist/keyorder.js.map +1 -1
  45. package/dist/lang.js +17 -915
  46. package/dist/lang.js.map +1 -1
  47. package/dist/lower.js +11 -61
  48. package/dist/lower.js.map +1 -1
  49. package/dist/lsp-server.js +0 -16
  50. package/dist/lsp-server.js.map +1 -1
  51. package/dist/lsp.d.ts +1 -1
  52. package/dist/lsp.js +12 -159
  53. package/dist/lsp.js.map +1 -1
  54. package/dist/mcp-server.js +0 -26
  55. package/dist/mcp-server.js.map +1 -1
  56. package/dist/mcp.js +0 -113
  57. package/dist/mcp.js.map +1 -1
  58. package/dist/mod-tool.js +0 -130
  59. package/dist/mod-tool.js.map +1 -1
  60. package/dist/mod.js +0 -162
  61. package/dist/mod.js.map +1 -1
  62. package/dist/patch.js +0 -217
  63. package/dist/patch.js.map +1 -1
  64. package/dist/provenance.js +0 -140
  65. package/dist/provenance.js.map +1 -1
  66. package/dist/query.js +0 -75
  67. package/dist/query.js.map +1 -1
  68. package/dist/reach.js +0 -43
  69. package/dist/reach.js.map +1 -1
  70. package/dist/relation.js +0 -61
  71. package/dist/relation.js.map +1 -1
  72. package/dist/render.js +20 -135
  73. package/dist/render.js.map +1 -1
  74. package/dist/report-sarif.d.ts +0 -11
  75. package/dist/report-sarif.js +0 -28
  76. package/dist/report-sarif.js.map +1 -1
  77. package/dist/sig.js +0 -35
  78. package/dist/sig.js.map +1 -1
  79. package/dist/sigdecl.js +1 -1
  80. package/dist/sigdecl.js.map +1 -1
  81. package/dist/siggate.js +0 -4
  82. package/dist/siggate.js.map +1 -1
  83. package/dist/site.js +3 -29
  84. package/dist/site.js.map +1 -1
  85. package/dist/subsume.d.ts +0 -10
  86. package/dist/subsume.js +0 -137
  87. package/dist/subsume.js.map +1 -1
  88. package/dist/template.d.ts +2 -1
  89. package/dist/template.js +58 -138
  90. package/dist/template.js.map +1 -1
  91. package/dist/trim.js +0 -41
  92. package/dist/trim.js.map +1 -1
  93. package/dist/tsconfig.tsbuildinfo +1 -1
  94. package/dist/type.js.map +1 -1
  95. package/dist/unify.js +12 -242
  96. package/dist/unify.js.map +1 -1
  97. package/dist/utility.js +0 -22
  98. package/dist/utility.js.map +1 -1
  99. package/dist/val/AbnfFuncVal.d.ts +18 -0
  100. package/dist/val/AbnfFuncVal.js +132 -0
  101. package/dist/val/AbnfFuncVal.js.map +1 -0
  102. package/dist/val/AbsentVal.d.ts +11 -0
  103. package/dist/val/AbsentVal.js +30 -0
  104. package/dist/val/AbsentVal.js.map +1 -0
  105. package/dist/val/AggFuncVal.d.ts +10 -1
  106. package/dist/val/AggFuncVal.js +104 -116
  107. package/dist/val/AggFuncVal.js.map +1 -1
  108. package/dist/val/ArithFuncVal.js +0 -12
  109. package/dist/val/ArithFuncVal.js.map +1 -1
  110. package/dist/val/BagVal.js +1 -78
  111. package/dist/val/BagVal.js.map +1 -1
  112. package/dist/val/BigDecimalVal.js +0 -16
  113. package/dist/val/BigDecimalVal.js.map +1 -1
  114. package/dist/val/BigIntegerVal.js +0 -16
  115. package/dist/val/BigIntegerVal.js.map +1 -1
  116. package/dist/val/CloseFuncVal.js +0 -9
  117. package/dist/val/CloseFuncVal.js.map +1 -1
  118. package/dist/val/CmpFuncVal.js +2 -59
  119. package/dist/val/CmpFuncVal.js.map +1 -1
  120. package/dist/val/ConjunctVal.js +0 -29
  121. package/dist/val/ConjunctVal.js.map +1 -1
  122. package/dist/val/ConstraintVal.js +0 -500
  123. package/dist/val/ConstraintVal.js.map +1 -1
  124. package/dist/val/ContainerKindVal.js +0 -2
  125. package/dist/val/ContainerKindVal.js.map +1 -1
  126. package/dist/val/CopyFuncVal.js +0 -3
  127. package/dist/val/CopyFuncVal.js.map +1 -1
  128. package/dist/val/Decimal.js +0 -179
  129. package/dist/val/Decimal.js.map +1 -1
  130. package/dist/val/DeprecateFuncVal.js.map +1 -1
  131. package/dist/val/DisjunctVal.js +0 -152
  132. package/dist/val/DisjunctVal.js.map +1 -1
  133. package/dist/val/EachFuncVal.js +0 -3
  134. package/dist/val/EachFuncVal.js.map +1 -1
  135. package/dist/val/EmitFuncVal.d.ts +1 -1
  136. package/dist/val/EmitFuncVal.js +6 -119
  137. package/dist/val/EmitFuncVal.js.map +1 -1
  138. package/dist/val/ExpectVal.js +0 -62
  139. package/dist/val/ExpectVal.js.map +1 -1
  140. package/dist/val/FilterFuncVal.js +0 -25
  141. package/dist/val/FilterFuncVal.js.map +1 -1
  142. package/dist/val/FuncBaseVal.d.ts +1 -0
  143. package/dist/val/FuncBaseVal.js +7 -127
  144. package/dist/val/FuncBaseVal.js.map +1 -1
  145. package/dist/val/GraphAtomVal.js +0 -15
  146. package/dist/val/GraphAtomVal.js.map +1 -1
  147. package/dist/val/HideFuncVal.js +0 -13
  148. package/dist/val/HideFuncVal.js.map +1 -1
  149. package/dist/val/IntegerVal.js +0 -61
  150. package/dist/val/IntegerVal.js.map +1 -1
  151. package/dist/val/JunctionVal.js +0 -20
  152. package/dist/val/JunctionVal.js.map +1 -1
  153. package/dist/val/KeyFuncVal.js +0 -46
  154. package/dist/val/KeyFuncVal.js.map +1 -1
  155. package/dist/val/ListVal.js +0 -57
  156. package/dist/val/ListVal.js.map +1 -1
  157. package/dist/val/LowerFuncVal.js +0 -11
  158. package/dist/val/LowerFuncVal.js.map +1 -1
  159. package/dist/val/MapVal.js +0 -151
  160. package/dist/val/MapVal.js.map +1 -1
  161. package/dist/val/MatchFuncVal.js +0 -27
  162. package/dist/val/MatchFuncVal.js.map +1 -1
  163. package/dist/val/{FormFuncVal.d.ts → MaybeFuncVal.d.ts} +5 -5
  164. package/dist/val/MaybeFuncVal.js +50 -0
  165. package/dist/val/MaybeFuncVal.js.map +1 -0
  166. package/dist/val/MoveFuncVal.js +0 -18
  167. package/dist/val/MoveFuncVal.js.map +1 -1
  168. package/dist/val/NilVal.js +0 -60
  169. package/dist/val/NilVal.js.map +1 -1
  170. package/dist/val/NomFuncVal.js +8 -42
  171. package/dist/val/NomFuncVal.js.map +1 -1
  172. package/dist/val/NumberVal.js +0 -15
  173. package/dist/val/NumberVal.js.map +1 -1
  174. package/dist/val/OpBaseVal.d.ts +1 -0
  175. package/dist/val/OpBaseVal.js +3 -15
  176. package/dist/val/OpBaseVal.js.map +1 -1
  177. package/dist/val/PackFuncVal.js +0 -34
  178. package/dist/val/PackFuncVal.js.map +1 -1
  179. package/dist/val/PathFuncVal.js +0 -6
  180. package/dist/val/PathFuncVal.js.map +1 -1
  181. package/dist/val/PathVal.js +0 -41
  182. package/dist/val/PathVal.js.map +1 -1
  183. package/dist/val/PlaceVal.js +0 -25
  184. package/dist/val/PlaceVal.js.map +1 -1
  185. package/dist/val/PlusOpVal.d.ts +1 -7
  186. package/dist/val/PlusOpVal.js +13 -74
  187. package/dist/val/PlusOpVal.js.map +1 -1
  188. package/dist/val/PrefFuncVal.js +0 -1
  189. package/dist/val/PrefFuncVal.js.map +1 -1
  190. package/dist/val/PrefVal.js +0 -167
  191. package/dist/val/PrefVal.js.map +1 -1
  192. package/dist/val/RecurseVal.js +0 -55
  193. package/dist/val/RecurseVal.js.map +1 -1
  194. package/dist/val/RefVal.js +0 -282
  195. package/dist/val/RefVal.js.map +1 -1
  196. package/dist/val/ReferFuncVal.js +0 -232
  197. package/dist/val/ReferFuncVal.js.map +1 -1
  198. package/dist/val/ScalarKindVal.js +0 -49
  199. package/dist/val/ScalarKindVal.js.map +1 -1
  200. package/dist/val/ScalarVal.js +0 -11
  201. package/dist/val/ScalarVal.js.map +1 -1
  202. package/dist/val/StrFuncVal.js +0 -18
  203. package/dist/val/StrFuncVal.js.map +1 -1
  204. package/dist/val/SuperFuncVal.js +0 -32
  205. package/dist/val/SuperFuncVal.js.map +1 -1
  206. package/dist/val/TopVal.js +0 -1
  207. package/dist/val/TopVal.js.map +1 -1
  208. package/dist/val/TranslateFuncVal.js +1 -3
  209. package/dist/val/TranslateFuncVal.js.map +1 -1
  210. package/dist/val/UpperFuncVal.js +0 -11
  211. package/dist/val/UpperFuncVal.js.map +1 -1
  212. package/dist/val/Val.d.ts +1 -0
  213. package/dist/val/Val.js +2 -133
  214. package/dist/val/Val.js.map +1 -1
  215. package/dist/val/VarVal.js +0 -12
  216. package/dist/val/VarVal.js.map +1 -1
  217. package/dist/val/arith.js +0 -37
  218. package/dist/val/arith.js.map +1 -1
  219. package/dist/val/caserange.js +0 -61
  220. package/dist/val/caserange.js.map +1 -1
  221. package/dist/val/members.js +0 -6
  222. package/dist/val/members.js.map +1 -1
  223. package/dist/val/numcmp.js +0 -11
  224. package/dist/val/numcmp.js.map +1 -1
  225. package/dist/val/numkind.js +0 -145
  226. package/dist/val/numkind.js.map +1 -1
  227. package/dist/val/valutil.js +0 -16
  228. package/dist/val/valutil.js.map +1 -1
  229. package/dist/vet.js +0 -461
  230. package/dist/vet.js.map +1 -1
  231. package/dist/view.js +0 -414
  232. package/dist/view.js.map +1 -1
  233. package/dist/walk.js +0 -41
  234. package/dist/walk.js.map +1 -1
  235. package/grammar/aontu.abnf +8 -6
  236. package/grammar/aontu.gbnf +4 -4
  237. package/grammar/aontu.lark +4 -4
  238. package/grammar/aontu.tmLanguage.json +1 -1
  239. package/package.json +4 -2
  240. package/src/agentsmd.ts +0 -35
  241. package/src/alias.ts +0 -39
  242. package/src/allow.ts +1 -96
  243. package/src/aontu.ts +3 -110
  244. package/src/aontumodel.ts +32 -0
  245. package/src/cli.ts +113 -682
  246. package/src/ctx.ts +0 -103
  247. package/src/diff.ts +0 -40
  248. package/src/err.ts +0 -40
  249. package/src/escape.ts +0 -46
  250. package/src/exactjson.ts +0 -131
  251. package/src/format.ts +14 -257
  252. package/src/grammar.ts +72 -0
  253. package/src/graph.ts +0 -61
  254. package/src/hcanon.ts +0 -82
  255. package/src/helpdoc.ts +1 -1
  256. package/src/hints.ts +66 -57
  257. package/src/jsonschema.ts +0 -123
  258. package/src/keyorder.ts +0 -42
  259. package/src/lang.ts +19 -931
  260. package/src/lower.ts +12 -62
  261. package/src/lsp-server.ts +0 -16
  262. package/src/lsp.ts +12 -180
  263. package/src/mcp-server.ts +0 -31
  264. package/src/mcp.ts +0 -130
  265. package/src/mod-tool.ts +0 -158
  266. package/src/mod.ts +0 -178
  267. package/src/patch.ts +0 -232
  268. package/src/provenance.ts +0 -183
  269. package/src/query.ts +0 -84
  270. package/src/reach.ts +0 -53
  271. package/src/relation.ts +0 -84
  272. package/src/render.ts +20 -172
  273. package/src/report-sarif.ts +0 -48
  274. package/src/sig.ts +0 -35
  275. package/src/sigdecl.ts +1 -1
  276. package/src/siggate.ts +0 -30
  277. package/src/site.ts +3 -29
  278. package/src/subsume.ts +1 -161
  279. package/src/template.ts +69 -140
  280. package/src/trim.ts +0 -53
  281. package/src/type.ts +2 -45
  282. package/src/unify.ts +13 -251
  283. package/src/utility.ts +0 -31
  284. package/src/val/AbnfFuncVal.ts +181 -0
  285. package/src/val/AbsentVal.ts +54 -0
  286. package/src/val/AggFuncVal.ts +152 -188
  287. package/src/val/ArithFuncVal.ts +0 -20
  288. package/src/val/BagVal.ts +1 -78
  289. package/src/val/BigDecimalVal.ts +0 -16
  290. package/src/val/BigIntegerVal.ts +0 -16
  291. package/src/val/CloseFuncVal.ts +0 -9
  292. package/src/val/CmpFuncVal.ts +4 -166
  293. package/src/val/ConjunctVal.ts +0 -33
  294. package/src/val/ConstraintVal.ts +2 -537
  295. package/src/val/ContainerKindVal.ts +0 -18
  296. package/src/val/CopyFuncVal.ts +0 -5
  297. package/src/val/Decimal.ts +1 -185
  298. package/src/val/DeprecateFuncVal.ts +0 -10
  299. package/src/val/DisjunctVal.ts +0 -157
  300. package/src/val/EachFuncVal.ts +0 -40
  301. package/src/val/EmitFuncVal.ts +8 -208
  302. package/src/val/ExpectVal.ts +0 -62
  303. package/src/val/FilterFuncVal.ts +0 -55
  304. package/src/val/FuncBaseVal.ts +9 -130
  305. package/src/val/GraphAtomVal.ts +0 -42
  306. package/src/val/HideFuncVal.ts +0 -15
  307. package/src/val/IntegerVal.ts +0 -61
  308. package/src/val/JunctionVal.ts +0 -20
  309. package/src/val/KeyFuncVal.ts +0 -48
  310. package/src/val/ListVal.ts +0 -59
  311. package/src/val/LowerFuncVal.ts +0 -12
  312. package/src/val/MapVal.ts +0 -151
  313. package/src/val/MatchFuncVal.ts +0 -59
  314. package/src/val/MaybeFuncVal.ts +86 -0
  315. package/src/val/MoveFuncVal.ts +0 -20
  316. package/src/val/NilVal.ts +0 -60
  317. package/src/val/NomFuncVal.ts +8 -95
  318. package/src/val/NumberVal.ts +0 -16
  319. package/src/val/OpBaseVal.ts +4 -17
  320. package/src/val/PackFuncVal.ts +0 -63
  321. package/src/val/PathFuncVal.ts +0 -32
  322. package/src/val/PathVal.ts +0 -66
  323. package/src/val/PlaceVal.ts +0 -45
  324. package/src/val/PlusOpVal.ts +18 -75
  325. package/src/val/PrefFuncVal.ts +0 -1
  326. package/src/val/PrefVal.ts +0 -179
  327. package/src/val/RecurseVal.ts +0 -81
  328. package/src/val/RefVal.ts +1 -285
  329. package/src/val/ReferFuncVal.ts +0 -255
  330. package/src/val/ScalarKindVal.ts +0 -50
  331. package/src/val/ScalarVal.ts +0 -12
  332. package/src/val/StrFuncVal.ts +0 -44
  333. package/src/val/SuperFuncVal.ts +0 -42
  334. package/src/val/TopVal.ts +0 -1
  335. package/src/val/TranslateFuncVal.ts +1 -51
  336. package/src/val/UpperFuncVal.ts +0 -12
  337. package/src/val/Val.ts +3 -192
  338. package/src/val/VarVal.ts +0 -15
  339. package/src/val/arith.ts +0 -92
  340. package/src/val/caserange.ts +0 -62
  341. package/src/val/members.ts +0 -23
  342. package/src/val/numcmp.ts +1 -27
  343. package/src/val/numkind.ts +0 -149
  344. package/src/val/valutil.ts +0 -16
  345. package/src/vet.ts +1 -582
  346. package/src/view.ts +0 -507
  347. package/src/walk.ts +0 -41
  348. package/dist/std.d.ts +0 -3
  349. package/dist/std.js +0 -672
  350. package/dist/std.js.map +0 -1
  351. package/dist/val/FormFuncVal.js +0 -55
  352. package/dist/val/FormFuncVal.js.map +0 -1
  353. package/dist/val/NamerFuncVal.d.ts +0 -12
  354. package/dist/val/NamerFuncVal.js +0 -176
  355. package/dist/val/NamerFuncVal.js.map +0 -1
  356. 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 { STD_SOURCES, AONTU_SCHEME, AONTU_MODELS } from './std'
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, JoinFuncVal,
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
- // Projection, which is what lets the aggregates reach a bag of
969
- // RECORDS: `sum(pick($.lines, amountCents))`. Not a clever `each`
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 // Was text
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
- // `.csv` IS DELIBERATELY ABSENT, and the reason is ADR-001 rather than
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
- // SECURITY: under the DEFAULT ('system') include capability this
2266
- // resolver reads any file the process can reach — @"path" follows
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 std and
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 = STD_SOURCES[path]
1686
+ const model = AONTU_SOURCES[path]
2478
1687
  if (null == model) {
2479
1688
  modelNotFound(path)
2480
1689
  }
2481
- record(ctx, path, 'std')
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
  }