aontu 0.52.1 → 0.54.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 (310) hide show
  1. package/README.md +88 -0
  2. package/bin/aontu-mcp.js +4 -0
  3. package/dist/agentsmd.d.ts +16 -0
  4. package/dist/agentsmd.js +107 -0
  5. package/dist/agentsmd.js.map +1 -0
  6. package/dist/aontu.d.ts +15 -3
  7. package/dist/aontu.js +126 -5
  8. package/dist/aontu.js.map +1 -1
  9. package/dist/cli.d.ts +45 -1
  10. package/dist/cli.js +2845 -69
  11. package/dist/cli.js.map +1 -1
  12. package/dist/ctx.d.ts +18 -0
  13. package/dist/ctx.js +44 -0
  14. package/dist/ctx.js.map +1 -1
  15. package/dist/diff.d.ts +22 -0
  16. package/dist/diff.js +141 -0
  17. package/dist/diff.js.map +1 -0
  18. package/dist/err.d.ts +3 -1
  19. package/dist/err.js +45 -8
  20. package/dist/err.js.map +1 -1
  21. package/dist/graph.d.ts +13 -0
  22. package/dist/graph.js +110 -0
  23. package/dist/graph.js.map +1 -0
  24. package/dist/hcanon.d.ts +3 -0
  25. package/dist/hcanon.js +145 -0
  26. package/dist/hcanon.js.map +1 -0
  27. package/dist/hints.js +235 -8
  28. package/dist/hints.js.map +1 -1
  29. package/dist/jsonschema.d.ts +20 -0
  30. package/dist/jsonschema.js +425 -0
  31. package/dist/jsonschema.js.map +1 -0
  32. package/dist/lang.js +955 -110
  33. package/dist/lang.js.map +1 -1
  34. package/dist/lsp.d.ts +9 -2
  35. package/dist/lsp.js +343 -48
  36. package/dist/lsp.js.map +1 -1
  37. package/dist/mcp-server.d.ts +20 -0
  38. package/dist/mcp-server.js +147 -0
  39. package/dist/mcp-server.js.map +1 -0
  40. package/dist/mcp.d.ts +42 -0
  41. package/dist/mcp.js +941 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mod-tool.d.ts +58 -0
  44. package/dist/mod-tool.js +532 -0
  45. package/dist/mod-tool.js.map +1 -0
  46. package/dist/mod.d.ts +35 -0
  47. package/dist/mod.js +345 -0
  48. package/dist/mod.js.map +1 -0
  49. package/dist/patch.d.ts +49 -0
  50. package/dist/patch.js +506 -0
  51. package/dist/patch.js.map +1 -0
  52. package/dist/provenance.d.ts +41 -0
  53. package/dist/provenance.js +336 -0
  54. package/dist/provenance.js.map +1 -0
  55. package/dist/query.d.ts +27 -0
  56. package/dist/query.js +294 -0
  57. package/dist/query.js.map +1 -0
  58. package/dist/reach.d.ts +15 -0
  59. package/dist/reach.js +168 -0
  60. package/dist/reach.js.map +1 -0
  61. package/dist/relation.d.ts +23 -0
  62. package/dist/relation.js +230 -0
  63. package/dist/relation.js.map +1 -0
  64. package/dist/report-sarif.d.ts +14 -0
  65. package/dist/report-sarif.js +102 -0
  66. package/dist/report-sarif.js.map +1 -0
  67. package/dist/sig.d.ts +25 -0
  68. package/dist/sig.js +277 -0
  69. package/dist/sig.js.map +1 -0
  70. package/dist/sigdecl.d.ts +2 -0
  71. package/dist/sigdecl.js +11 -0
  72. package/dist/sigdecl.js.map +1 -0
  73. package/dist/siggate.d.ts +4 -0
  74. package/dist/siggate.js +90 -0
  75. package/dist/siggate.js.map +1 -0
  76. package/dist/site.d.ts +4 -0
  77. package/dist/site.js +31 -0
  78. package/dist/site.js.map +1 -1
  79. package/dist/std.d.ts +1 -0
  80. package/dist/std.js +129 -0
  81. package/dist/std.js.map +1 -0
  82. package/dist/subsume.d.ts +39 -0
  83. package/dist/subsume.js +573 -0
  84. package/dist/subsume.js.map +1 -0
  85. package/dist/trim.d.ts +19 -0
  86. package/dist/trim.js +155 -0
  87. package/dist/trim.js.map +1 -0
  88. package/dist/tsconfig.tsbuildinfo +1 -1
  89. package/dist/type.d.ts +17 -1
  90. package/dist/type.js.map +1 -1
  91. package/dist/unify.d.ts +2 -1
  92. package/dist/unify.js +234 -38
  93. package/dist/unify.js.map +1 -1
  94. package/dist/utility.d.ts +8 -1
  95. package/dist/utility.js +68 -1
  96. package/dist/utility.js.map +1 -1
  97. package/dist/val/AggFuncVal.d.ts +44 -0
  98. package/dist/val/AggFuncVal.js +364 -0
  99. package/dist/val/AggFuncVal.js.map +1 -0
  100. package/dist/val/ArithFuncVal.d.ts +31 -0
  101. package/dist/val/ArithFuncVal.js +62 -0
  102. package/dist/val/ArithFuncVal.js.map +1 -0
  103. package/dist/val/BagVal.d.ts +7 -0
  104. package/dist/val/BagVal.js +150 -11
  105. package/dist/val/BagVal.js.map +1 -1
  106. package/dist/val/CloseFuncVal.js +9 -1
  107. package/dist/val/CloseFuncVal.js.map +1 -1
  108. package/dist/val/ConjunctVal.d.ts +1 -1
  109. package/dist/val/ConjunctVal.js +19 -0
  110. package/dist/val/ConjunctVal.js.map +1 -1
  111. package/dist/val/ConstraintVal.d.ts +7 -1
  112. package/dist/val/ConstraintVal.js +523 -45
  113. package/dist/val/ConstraintVal.js.map +1 -1
  114. package/dist/val/ContainerKindVal.d.ts +35 -0
  115. package/dist/val/ContainerKindVal.js +99 -0
  116. package/dist/val/ContainerKindVal.js.map +1 -0
  117. package/dist/val/CopyFuncVal.d.ts +1 -2
  118. package/dist/val/CopyFuncVal.js.map +1 -1
  119. package/dist/val/Decimal.d.ts +1 -0
  120. package/dist/val/Decimal.js +13 -0
  121. package/dist/val/Decimal.js.map +1 -1
  122. package/dist/val/DeprecateFuncVal.d.ts +11 -0
  123. package/dist/val/DeprecateFuncVal.js +47 -0
  124. package/dist/val/DeprecateFuncVal.js.map +1 -0
  125. package/dist/val/DisjunctVal.d.ts +1 -2
  126. package/dist/val/DisjunctVal.js +269 -53
  127. package/dist/val/DisjunctVal.js.map +1 -1
  128. package/dist/val/EachFuncVal.d.ts +15 -0
  129. package/dist/val/EachFuncVal.js +75 -0
  130. package/dist/val/EachFuncVal.js.map +1 -0
  131. package/dist/val/ExpectVal.js +62 -4
  132. package/dist/val/ExpectVal.js.map +1 -1
  133. package/dist/val/FeatureVal.js +1 -1
  134. package/dist/val/FeatureVal.js.map +1 -1
  135. package/dist/val/FilterFuncVal.d.ts +15 -0
  136. package/dist/val/FilterFuncVal.js +91 -0
  137. package/dist/val/FilterFuncVal.js.map +1 -0
  138. package/dist/val/FuncBaseVal.d.ts +7 -1
  139. package/dist/val/FuncBaseVal.js +183 -2
  140. package/dist/val/FuncBaseVal.js.map +1 -1
  141. package/dist/val/GraphAtomVal.d.ts +39 -0
  142. package/dist/val/GraphAtomVal.js +184 -0
  143. package/dist/val/GraphAtomVal.js.map +1 -0
  144. package/dist/val/HideFuncVal.js.map +1 -1
  145. package/dist/val/JunctionVal.js +24 -1
  146. package/dist/val/JunctionVal.js.map +1 -1
  147. package/dist/val/KeyFuncVal.d.ts +1 -1
  148. package/dist/val/KeyFuncVal.js +38 -30
  149. package/dist/val/KeyFuncVal.js.map +1 -1
  150. package/dist/val/ListVal.js +94 -14
  151. package/dist/val/ListVal.js.map +1 -1
  152. package/dist/val/LowerFuncVal.js.map +1 -1
  153. package/dist/val/MapVal.d.ts +1 -0
  154. package/dist/val/MapVal.js +146 -14
  155. package/dist/val/MapVal.js.map +1 -1
  156. package/dist/val/MatchFuncVal.d.ts +15 -0
  157. package/dist/val/MatchFuncVal.js +107 -0
  158. package/dist/val/MatchFuncVal.js.map +1 -0
  159. package/dist/val/MoveFuncVal.js.map +1 -1
  160. package/dist/val/NilVal.js +24 -0
  161. package/dist/val/NilVal.js.map +1 -1
  162. package/dist/val/OpBaseVal.d.ts +1 -1
  163. package/dist/val/OpBaseVal.js +19 -1
  164. package/dist/val/OpBaseVal.js.map +1 -1
  165. package/dist/val/OpenFuncVal.js +4 -1
  166. package/dist/val/OpenFuncVal.js.map +1 -1
  167. package/dist/val/PackFuncVal.d.ts +15 -0
  168. package/dist/val/PackFuncVal.js +108 -0
  169. package/dist/val/PackFuncVal.js.map +1 -0
  170. package/dist/val/PathFuncVal.d.ts +2 -2
  171. package/dist/val/PathFuncVal.js +75 -16
  172. package/dist/val/PathFuncVal.js.map +1 -1
  173. package/dist/val/PathVal.d.ts +25 -0
  174. package/dist/val/PathVal.js +150 -0
  175. package/dist/val/PathVal.js.map +1 -0
  176. package/dist/val/PlaceVal.d.ts +13 -0
  177. package/dist/val/PlaceVal.js +131 -0
  178. package/dist/val/PlaceVal.js.map +1 -0
  179. package/dist/val/PlusOpVal.d.ts +2 -1
  180. package/dist/val/PlusOpVal.js +61 -37
  181. package/dist/val/PlusOpVal.js.map +1 -1
  182. package/dist/val/PrefFuncVal.js.map +1 -1
  183. package/dist/val/PrefVal.d.ts +4 -2
  184. package/dist/val/PrefVal.js +223 -41
  185. package/dist/val/PrefVal.js.map +1 -1
  186. package/dist/val/RecurseVal.d.ts +19 -0
  187. package/dist/val/RecurseVal.js +217 -0
  188. package/dist/val/RecurseVal.js.map +1 -0
  189. package/dist/val/RefVal.d.ts +3 -2
  190. package/dist/val/RefVal.js +181 -66
  191. package/dist/val/RefVal.js.map +1 -1
  192. package/dist/val/ReferFuncVal.d.ts +59 -0
  193. package/dist/val/ReferFuncVal.js +604 -0
  194. package/dist/val/ReferFuncVal.js.map +1 -0
  195. package/dist/val/ScalarKindVal.d.ts +4 -3
  196. package/dist/val/ScalarKindVal.js +12 -12
  197. package/dist/val/ScalarKindVal.js.map +1 -1
  198. package/dist/val/SuperFuncVal.d.ts +4 -2
  199. package/dist/val/SuperFuncVal.js +118 -14
  200. package/dist/val/SuperFuncVal.js.map +1 -1
  201. package/dist/val/TopVal.d.ts +1 -1
  202. package/dist/val/TopVal.js.map +1 -1
  203. package/dist/val/TypeFuncVal.js.map +1 -1
  204. package/dist/val/UpperFuncVal.js.map +1 -1
  205. package/dist/val/Val.d.ts +7 -1
  206. package/dist/val/Val.js +170 -2
  207. package/dist/val/Val.js.map +1 -1
  208. package/dist/val/VarVal.js.map +1 -1
  209. package/dist/val/arith.d.ts +6 -0
  210. package/dist/val/arith.js +173 -0
  211. package/dist/val/arith.js.map +1 -0
  212. package/dist/vet.d.ts +45 -0
  213. package/dist/vet.js +802 -0
  214. package/dist/vet.js.map +1 -0
  215. package/dist/view.d.ts +85 -0
  216. package/dist/view.js +1948 -0
  217. package/dist/view.js.map +1 -0
  218. package/dist/walk.d.ts +2 -0
  219. package/dist/walk.js +91 -0
  220. package/dist/walk.js.map +1 -0
  221. package/grammar/aontu.gbnf +138 -0
  222. package/grammar/aontu.lark +118 -0
  223. package/grammar/aontu.tmLanguage.json +184 -0
  224. package/package.json +30 -8
  225. package/skill/SKILL.md +37 -0
  226. package/skill/error-codes.md +62 -0
  227. package/skill/examples.md +99 -0
  228. package/skill/grammar-card.md +56 -0
  229. package/src/agentsmd.ts +135 -0
  230. package/src/aontu.ts +166 -5
  231. package/src/cli.ts +3324 -72
  232. package/src/ctx.ts +93 -1
  233. package/src/diff.ts +198 -0
  234. package/src/err.ts +50 -8
  235. package/src/graph.ts +185 -0
  236. package/src/hcanon.ts +167 -0
  237. package/src/hints.ts +291 -8
  238. package/src/jsonschema.ts +552 -0
  239. package/src/lang.ts +1049 -110
  240. package/src/lsp.ts +351 -49
  241. package/src/mcp-server.ts +187 -0
  242. package/src/mcp.ts +1130 -0
  243. package/src/mod-tool.ts +718 -0
  244. package/src/mod.ts +453 -0
  245. package/src/patch.ts +628 -0
  246. package/src/provenance.ts +437 -0
  247. package/src/query.ts +381 -0
  248. package/src/reach.ts +213 -0
  249. package/src/relation.ts +298 -0
  250. package/src/report-sarif.ts +137 -0
  251. package/src/sig.ts +345 -0
  252. package/src/sigdecl.ts +11 -0
  253. package/src/siggate.ts +144 -0
  254. package/src/site.ts +36 -1
  255. package/src/std.ts +131 -0
  256. package/src/subsume.ts +739 -0
  257. package/src/trim.ts +195 -0
  258. package/src/tsconfig.json +10 -4
  259. package/src/type.ts +51 -2
  260. package/src/unify.ts +253 -38
  261. package/src/utility.ts +79 -1
  262. package/src/val/AggFuncVal.ts +546 -0
  263. package/src/val/ArithFuncVal.ts +108 -0
  264. package/src/val/BagVal.ts +158 -12
  265. package/src/val/CloseFuncVal.ts +9 -1
  266. package/src/val/ConjunctVal.ts +20 -0
  267. package/src/val/ConstraintVal.ts +576 -48
  268. package/src/val/ContainerKindVal.ts +158 -0
  269. package/src/val/CopyFuncVal.ts +0 -1
  270. package/src/val/Decimal.ts +15 -0
  271. package/src/val/DeprecateFuncVal.ts +84 -0
  272. package/src/val/DisjunctVal.ts +282 -59
  273. package/src/val/EachFuncVal.ts +133 -0
  274. package/src/val/ExpectVal.ts +65 -8
  275. package/src/val/FeatureVal.ts +1 -1
  276. package/src/val/FilterFuncVal.ts +154 -0
  277. package/src/val/FuncBaseVal.ts +206 -2
  278. package/src/val/GraphAtomVal.ts +264 -0
  279. package/src/val/HideFuncVal.ts +0 -2
  280. package/src/val/JunctionVal.ts +24 -1
  281. package/src/val/KeyFuncVal.ts +39 -35
  282. package/src/val/ListVal.ts +101 -15
  283. package/src/val/LowerFuncVal.ts +0 -1
  284. package/src/val/MapVal.ts +153 -14
  285. package/src/val/MatchFuncVal.ts +176 -0
  286. package/src/val/MoveFuncVal.ts +0 -2
  287. package/src/val/NilVal.ts +25 -0
  288. package/src/val/OpBaseVal.ts +20 -1
  289. package/src/val/OpenFuncVal.ts +4 -2
  290. package/src/val/PackFuncVal.ts +175 -0
  291. package/src/val/PathFuncVal.ts +107 -20
  292. package/src/val/PathVal.ts +221 -0
  293. package/src/val/PlaceVal.ts +193 -0
  294. package/src/val/PlusOpVal.ts +67 -39
  295. package/src/val/PrefFuncVal.ts +0 -1
  296. package/src/val/PrefVal.ts +251 -60
  297. package/src/val/RecurseVal.ts +285 -0
  298. package/src/val/RefVal.ts +183 -78
  299. package/src/val/ReferFuncVal.ts +732 -0
  300. package/src/val/ScalarKindVal.ts +12 -13
  301. package/src/val/SuperFuncVal.ts +137 -13
  302. package/src/val/TopVal.ts +1 -2
  303. package/src/val/TypeFuncVal.ts +0 -2
  304. package/src/val/UpperFuncVal.ts +0 -1
  305. package/src/val/Val.ts +216 -4
  306. package/src/val/VarVal.ts +0 -1
  307. package/src/val/arith.ts +319 -0
  308. package/src/vet.ts +1025 -0
  309. package/src/view.ts +2563 -0
  310. package/src/walk.ts +99 -0
@@ -0,0 +1,319 @@
1
+ /* Copyright (c) 2025 Richard Rodger, MIT License */
2
+
3
+
4
+ // THE ARITHMETIC FAMILY -- add, sub, mul, div, mod, rem.
5
+ //
6
+ // The review's finding I: "Arithmetic stops at `+`", so "prod gets
7
+ // double the replicas" is inexpressible and Kubernetes quantity strings
8
+ // silently CONCATENATE ("500m" + "500m" is "500m500m"). The design
9
+ // pre-registered the semantics these functions must have
10
+ // (docs/capability-review/g8-generation.md, "Arithmetic semantics,
11
+ // pre-registered") and the boundary that keeps `-` `*` `/` `%`
12
+ // reserved: maths arrives as functions or not at all.
13
+ //
14
+ // THE FAMILY IS NUMERIC, WHICH IS WHAT MAKES `add` MORE THAN A SYNONYM
15
+ // FOR `+`. The operator is polymorphic -- concatenation for strings,
16
+ // disjunction for booleans, addition for numbers -- and that is why the
17
+ // quantity strings above concatenate instead of failing. `add("500m",
18
+ // "500m")` is a located error, because a function named for a numeric
19
+ // operation has no business inventing a string. So the two spellings
20
+ // mean different things, and both are kept.
21
+ //
22
+ // Everything else here is the number tower's existing law, applied to
23
+ // five more operations (docs/design/number-model.md):
24
+ //
25
+ // R5 CONTAGION no operation introduces a kind narrower than its
26
+ // operands, so an integer result needs integer
27
+ // operands.
28
+ // D6 EXACT LADDER integer < biginteger < bigdecimal. A mixed exact
29
+ // operation promotes to the WIDEST operand, is
30
+ // computed exactly, and never demotes.
31
+ // FLOAT IS OFF IT binary64 mixed with either big leaf is a hard
32
+ // error in both operand orders, because promotion
33
+ // either way throws away exactness the document
34
+ // asked for by writing `0d`.
35
+ // NO ROUNDING an exact result that will not store is refused,
36
+ // never rounded to fit.
37
+ //
38
+ // and three refusals this file adds, which the pre-registration named:
39
+ //
40
+ // div/mod/rem by ZERO is a hard error in every kind. A ground-truth
41
+ // language has no business manufacturing infinity, and Aontu cannot
42
+ // even write one down -- an overflowing literal is a `not_number`
43
+ // error nil -- so propagating one would invent a value no generated
44
+ // JSON could carry.
45
+ //
46
+ // A NON-FINITE FLOAT RESULT is a located error. This one was already
47
+ // reachable through `+` and reported as neither: `1.0e308+1.0e308`
48
+ // crashed TypeScript with `[aontu/internal]` and leaked Go's raw
49
+ // `json: unsupported value: +Inf` with no code at all (use-cases/
50
+ // BUGS.md 39). PlusOpVal and its Go twin now go through the same
51
+ // check.
52
+ //
53
+ // DIVISION IS NOT CLOSED OVER THE DECIMAL LEAF, so div/mod/rem refuse
54
+ // a bigdecimal operand rather than rounding one third to fit. See
55
+ // `inexact_divide`'s hint: scale to integers, which is the money wire
56
+ // convention anyway, or accept a float.
57
+
58
+
59
+ import type { Val } from '../type'
60
+
61
+ import { AontuContext } from '../ctx'
62
+ import { makeNilErr } from '../err'
63
+
64
+ import { IntegerVal } from './IntegerVal'
65
+ import { NumberVal } from './NumberVal'
66
+ import { BigIntegerVal } from './BigIntegerVal'
67
+ import { BigDecimalVal } from './BigDecimalVal'
68
+ import { Decimal, decimalOverBudget } from './Decimal'
69
+ import { isIntegerStorable } from './numkind'
70
+
71
+
72
+ type ArithOp = 'add' | 'sub' | 'mul' | 'div' | 'mod' | 'rem'
73
+
74
+ // The three that divide, and therefore the three that can be handed a
75
+ // zero divisor and cannot answer over the decimal leaf.
76
+ function divides(op: ArithOp): boolean {
77
+ return 'div' === op || 'mod' === op || 'rem' === op
78
+ }
79
+
80
+
81
+ // The numeric leaves, told apart by their own flags rather than by the
82
+ // JavaScript type of the peg: `integer` and `float` share `number`.
83
+ type ArithKind = 'integer' | 'float' | 'biginteger' | 'bigdecimal'
84
+
85
+ const EXACT_RANK: Record<string, number> = {
86
+ integer: 1,
87
+ biginteger: 2,
88
+ bigdecimal: 3,
89
+ }
90
+
91
+
92
+ function isBig(k: ArithKind): boolean {
93
+ return 'biginteger' === k || 'bigdecimal' === k
94
+ }
95
+
96
+
97
+ // A pref operand contributes its preferred value, and therefore that
98
+ // value's kind too -- the same rule `+` applies.
99
+ function unpref(v: any): any {
100
+ while (v?.isPref) {
101
+ v = v.peg
102
+ }
103
+ return v
104
+ }
105
+
106
+
107
+ function arithKind(v: any): ArithKind | undefined {
108
+ // Every caller hands a driven Val (an operand past the signature
109
+ // gate, or a bag member from the aggregate fold), so scalarhood is
110
+ // the one question -- a container member lands here.
111
+ if (true !== v?.isScalar) {
112
+ return undefined
113
+ }
114
+ if (v.isBigInteger) {
115
+ return 'biginteger'
116
+ }
117
+ if (v.isBigDecimal) {
118
+ return 'bigdecimal'
119
+ }
120
+ if (v.isInteger) {
121
+ return 'integer'
122
+ }
123
+ return 'number' === typeof v.peg ? 'float' : undefined
124
+ }
125
+
126
+
127
+ // An exact-ladder operand as an exact integer. Only reached for the two
128
+ // integral leaves; an `integer` peg is integral by construction.
129
+ function asInteger(v: any, k: ArithKind): bigint {
130
+ return 'biginteger' === k ? v.peg : BigInt(v.peg)
131
+ }
132
+
133
+
134
+ function asDecimal(v: any, k: ArithKind): Decimal {
135
+ return 'bigdecimal' === k ? v.peg : new Decimal(asInteger(v, k), 0)
136
+ }
137
+
138
+
139
+ // The whole family, in one function, because every rule above is a rule
140
+ // about ARITHMETIC and not about any one operation. `node` is the value
141
+ // the error is located at -- the call, or the `+` op.
142
+ // `attempt` is the name the ERROR reports, which is the operation
143
+ // except when a fold borrows one: `sum` adds, but a bad member is the
144
+ // author's `sum` call and must say so.
145
+ function arith(
146
+ ctx: AontuContext | undefined,
147
+ op: ArithOp,
148
+ node: Val,
149
+ a: Val,
150
+ b: Val,
151
+ attempt?: string
152
+ ): Val {
153
+ const name = attempt ?? op
154
+ const av: any = unpref(a)
155
+ const bv: any = unpref(b)
156
+ const ak = arithKind(av)
157
+ const bk = arithKind(bv)
158
+
159
+ // A non-numeric operand is not something to wait for: `resolve` is
160
+ // only reached once every argument has settled, so a kind, a map, a
161
+ // string or a boolean here is the author's mistake and is named as
162
+ // one. (`+` differs, and must: it has answers for strings and
163
+ // booleans.)
164
+ if (undefined === ak || undefined === bk) {
165
+ return makeNilErr(ctx, 'invalid-arg', node, undefined, name)
166
+ }
167
+
168
+ // A big leaf never silently becomes a binary float, in EITHER operand
169
+ // order. The error names both leaves in operand order.
170
+ if (('float' === ak && isBig(bk)) || (isBig(ak) && 'float' === bk)) {
171
+ return makeNilErr(ctx, 'exact_float_mix', node, undefined, name,
172
+ { left: ak, right: bk })
173
+ }
174
+
175
+ if ('float' === ak || 'float' === bk) {
176
+ return floatArith(ctx, op, name, node, av.peg, bv.peg)
177
+ }
178
+
179
+ const rank = EXACT_RANK[bk] < EXACT_RANK[ak] ? EXACT_RANK[ak] : EXACT_RANK[bk]
180
+
181
+ if (EXACT_RANK.bigdecimal === rank) {
182
+ return decimalArith(ctx, op, name, node, asDecimal(av, ak), asDecimal(bv, bk))
183
+ }
184
+
185
+ return integerArith(ctx, op, name, node, asInteger(av, ak),
186
+ asInteger(bv, bk), EXACT_RANK.biginteger === rank)
187
+ }
188
+
189
+
190
+ // IEEE-754 binary64, with the JSON-superset constraint still biting: an
191
+ // infinite or NaN result is a located error rather than a value, because
192
+ // there is no way to write one down and no JSON that could carry it.
193
+ function floatArith(
194
+ ctx: AontuContext | undefined,
195
+ op: ArithOp,
196
+ name: string,
197
+ node: Val,
198
+ x: number,
199
+ y: number
200
+ ): Val {
201
+ if (divides(op) && 0 === y) {
202
+ return makeNilErr(ctx, 'divide_by_zero', node, undefined, name)
203
+ }
204
+
205
+ const out =
206
+ 'add' === op ? x + y :
207
+ 'sub' === op ? x - y :
208
+ 'mul' === op ? x * y :
209
+ 'div' === op ? x / y :
210
+ // Truncated remainder, sign following the DIVIDEND, which is
211
+ // what JavaScript's `%` and Go's math.Mod both give...
212
+ 'rem' === op ? x % y :
213
+ // ...and the floored modulus, sign following the DIVISOR,
214
+ // built from it. Adding the divisor back moves a remainder
215
+ // whose sign disagrees into agreement, and leaves an exact
216
+ // zero alone.
217
+ flooredMod(x % y, y)
218
+
219
+ return Number.isFinite(out) ?
220
+ new NumberVal({ peg: out }) :
221
+ makeNilErr(ctx, 'float_overflow', node, undefined, name)
222
+ }
223
+
224
+
225
+ function flooredMod(rem: number, y: number): number {
226
+ return 0 !== rem && (rem < 0) !== (y < 0) ? rem + y : rem
227
+ }
228
+
229
+
230
+ // The exact integral leaves. Both compute in bigint, so nothing passes
231
+ // through binary64 and nothing rounds; only the storage test at the end
232
+ // differs, because `biginteger` is unbounded and `integer` is not.
233
+ function integerArith(
234
+ ctx: AontuContext | undefined,
235
+ op: ArithOp,
236
+ name: string,
237
+ node: Val,
238
+ x: bigint,
239
+ y: bigint,
240
+ big: boolean
241
+ ): Val {
242
+ if (divides(op) && 0n === y) {
243
+ return makeNilErr(ctx, 'divide_by_zero', node, undefined, name)
244
+ }
245
+
246
+ const out =
247
+ 'add' === op ? x + y :
248
+ 'sub' === op ? x - y :
249
+ 'mul' === op ? x * y :
250
+ // TRUNCATION TOWARD ZERO, stated once here rather than left to
251
+ // whichever host `/` each port happens to call: div(-7, 2) is
252
+ // -3, not -4. BigInt division truncates, and so does Go's
253
+ // big.Int.Quo (its Div floors, which is why the Go twin must
254
+ // not use it).
255
+ 'div' === op ? x / y :
256
+ 'rem' === op ? x % y :
257
+ flooredModBig(x % y, y)
258
+
259
+ if (big) {
260
+ // Unbounded and exact: nothing to check, and no demotion to
261
+ // `integer` however small the result.
262
+ return new BigIntegerVal({ peg: out })
263
+ }
264
+
265
+ // The result faces the SAME storage contract R1 puts on a literal --
266
+ // integral, inside the int64 window, and exactly representable in
267
+ // binary64 -- because Go's int64 holds results TypeScript's double
268
+ // cannot, and without a shared test a document would resolve in one
269
+ // port and round in the other.
270
+ return isIntegerStorable(out) ?
271
+ new IntegerVal({ peg: Number(out) }) :
272
+ makeNilErr(ctx, 'inexact_integer_sum', node, undefined, name,
273
+ { sum: out.toString() })
274
+ }
275
+
276
+
277
+ function flooredModBig(rem: bigint, y: bigint): bigint {
278
+ return 0n !== rem && (rem < 0n) !== (y < 0n) ? rem + y : rem
279
+ }
280
+
281
+
282
+ // The decimal leaf. Addition, subtraction and multiplication are exact
283
+ // coefficient arithmetic and land here; division does not, and says so.
284
+ function decimalArith(
285
+ ctx: AontuContext | undefined,
286
+ op: ArithOp,
287
+ name: string,
288
+ node: Val,
289
+ x: Decimal,
290
+ y: Decimal
291
+ ): Val {
292
+ if (divides(op)) {
293
+ // EXACT DECIMAL DIVISION IS NOT CLOSED: one third has no finite
294
+ // decimal form, so a `div` over this leaf either rounds -- the one
295
+ // thing the leaf exists to refuse -- or refuses. It refuses, and the
296
+ // hint names both ways out.
297
+ return makeNilErr(ctx, 'inexact_divide', node, undefined, name)
298
+ }
299
+
300
+ const out =
301
+ 'add' === op ? x.add(y) :
302
+ 'sub' === op ? x.add(y.negate()) :
303
+ x.multiply(y)
304
+
305
+ // The budget applies to RESULTS as well as literals: an exact answer
306
+ // too wide to hold is refused, never rounded to fit.
307
+ return decimalOverBudget(out) ?
308
+ makeNilErr(ctx, 'decimal_budget', node, undefined, name) :
309
+ new BigDecimalVal({ peg: out })
310
+ } /* node:coverage ignore next 9 */
311
+
312
+
313
+ export type {
314
+ ArithOp,
315
+ }
316
+
317
+ export {
318
+ arith,
319
+ }