aontu 0.50.1 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aontu-lsp.js +6 -0
- package/bin/aontu.js +7 -0
- package/dist/aontu.d.ts +4 -2
- package/dist/aontu.js +21 -8
- package/dist/aontu.js.map +1 -1
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +43 -53
- package/dist/cli.js.map +1 -1
- package/dist/ctx.js +1 -1
- package/dist/ctx.js.map +1 -1
- package/dist/err.js +3 -2
- package/dist/err.js.map +1 -1
- package/dist/exactjson.d.ts +37 -0
- package/dist/exactjson.js +228 -0
- package/dist/exactjson.js.map +1 -0
- package/dist/hints.d.ts +3 -1
- package/dist/hints.js +187 -5
- package/dist/hints.js.map +1 -1
- package/dist/keyorder.d.ts +9 -0
- package/dist/keyorder.js +65 -0
- package/dist/keyorder.js.map +1 -0
- package/dist/lang.js +263 -30
- package/dist/lang.js.map +1 -1
- package/dist/lsp-server.d.ts +2 -2
- package/dist/lsp-server.js +10 -10
- package/dist/lsp-server.js.map +1 -1
- package/dist/lsp.js +39 -21
- package/dist/lsp.js.map +1 -1
- package/dist/site.js +1 -1
- package/dist/site.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/unify.js +48 -1
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +3 -8
- package/dist/utility.js.map +1 -1
- package/dist/val/BagVal.js +9 -6
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/BigDecimalVal.d.ts +13 -0
- package/dist/val/BigDecimalVal.js +80 -0
- package/dist/val/BigDecimalVal.js.map +1 -0
- package/dist/val/BigIntegerVal.d.ts +11 -0
- package/dist/val/BigIntegerVal.js +75 -0
- package/dist/val/BigIntegerVal.js.map +1 -0
- package/dist/val/BooleanVal.js +1 -1
- package/dist/val/BooleanVal.js.map +1 -1
- package/dist/val/CloseFuncVal.js +1 -1
- package/dist/val/CloseFuncVal.js.map +1 -1
- package/dist/val/ConjunctVal.js +1 -1
- package/dist/val/ConjunctVal.js.map +1 -1
- package/dist/val/ConstraintVal.d.ts +56 -0
- package/dist/val/ConstraintVal.js +407 -0
- package/dist/val/ConstraintVal.js.map +1 -0
- package/dist/val/CopyFuncVal.js +1 -1
- package/dist/val/CopyFuncVal.js.map +1 -1
- package/dist/val/Decimal.d.ts +32 -0
- package/dist/val/Decimal.js +318 -0
- package/dist/val/Decimal.js.map +1 -0
- package/dist/val/DisjunctVal.js +1 -29
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/ExpectVal.js +1 -1
- package/dist/val/ExpectVal.js.map +1 -1
- package/dist/val/FeatureVal.js +1 -1
- package/dist/val/FeatureVal.js.map +1 -1
- package/dist/val/FuncBaseVal.js +1 -1
- package/dist/val/FuncBaseVal.js.map +1 -1
- package/dist/val/HideFuncVal.js +1 -1
- package/dist/val/HideFuncVal.js.map +1 -1
- package/dist/val/IntegerVal.d.ts +2 -0
- package/dist/val/IntegerVal.js +77 -5
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/JunctionVal.js +1 -1
- package/dist/val/JunctionVal.js.map +1 -1
- package/dist/val/KeyFuncVal.d.ts +1 -1
- package/dist/val/KeyFuncVal.js +34 -4
- package/dist/val/KeyFuncVal.js.map +1 -1
- package/dist/val/ListVal.js +7 -7
- package/dist/val/ListVal.js.map +1 -1
- package/dist/val/LowerFuncVal.js +21 -4
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +17 -13
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/MoveFuncVal.js +26 -9
- package/dist/val/MoveFuncVal.js.map +1 -1
- package/dist/val/NilVal.d.ts +1 -0
- package/dist/val/NilVal.js +10 -3
- package/dist/val/NilVal.js.map +1 -1
- package/dist/val/NullVal.js +1 -1
- package/dist/val/NullVal.js.map +1 -1
- package/dist/val/NumberVal.d.ts +1 -0
- package/dist/val/NumberVal.js +25 -4
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.js +10 -8
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/OpenFuncVal.js +1 -1
- package/dist/val/OpenFuncVal.js.map +1 -1
- package/dist/val/PathFuncVal.js +10 -2
- package/dist/val/PathFuncVal.js.map +1 -1
- package/dist/val/PlusOpVal.d.ts +3 -1
- package/dist/val/PlusOpVal.js +140 -29
- package/dist/val/PlusOpVal.js.map +1 -1
- package/dist/val/PrefFuncVal.js +1 -1
- package/dist/val/PrefFuncVal.js.map +1 -1
- package/dist/val/PrefVal.d.ts +2 -0
- package/dist/val/PrefVal.js +44 -7
- package/dist/val/PrefVal.js.map +1 -1
- package/dist/val/RefVal.d.ts +2 -0
- package/dist/val/RefVal.js +135 -11
- package/dist/val/RefVal.js.map +1 -1
- package/dist/val/ScalarKindVal.d.ts +12 -2
- package/dist/val/ScalarKindVal.js +108 -13
- package/dist/val/ScalarKindVal.js.map +1 -1
- package/dist/val/ScalarVal.d.ts +1 -0
- package/dist/val/ScalarVal.js +18 -3
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/StringVal.js +1 -1
- package/dist/val/StringVal.js.map +1 -1
- package/dist/val/SuperFuncVal.d.ts +1 -1
- package/dist/val/SuperFuncVal.js +15 -2
- package/dist/val/SuperFuncVal.js.map +1 -1
- package/dist/val/TopVal.js +1 -1
- package/dist/val/TopVal.js.map +1 -1
- package/dist/val/TypeFuncVal.js +1 -1
- package/dist/val/TypeFuncVal.js.map +1 -1
- package/dist/val/UpperFuncVal.js +21 -4
- package/dist/val/UpperFuncVal.js.map +1 -1
- package/dist/val/Val.d.ts +1 -1
- package/dist/val/Val.js +11 -21
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +8 -2
- package/dist/val/VarVal.js.map +1 -1
- package/dist/val/numcmp.d.ts +14 -0
- package/dist/val/numcmp.js +134 -0
- package/dist/val/numcmp.js.map +1 -0
- package/dist/val/numkind.d.ts +6 -0
- package/dist/val/numkind.js +224 -0
- package/dist/val/numkind.js.map +1 -0
- package/dist/val/valutil.d.ts +1 -0
- package/dist/val/valutil.js +35 -0
- package/dist/val/valutil.js.map +1 -1
- package/package.json +7 -5
- package/src/aontu.ts +26 -9
- package/src/cli.ts +48 -21
- package/src/ctx.ts +1 -1
- package/src/err.ts +3 -2
- package/src/exactjson.ts +254 -0
- package/src/hints.ts +213 -4
- package/src/keyorder.ts +73 -0
- package/src/lang.ts +303 -31
- package/src/lsp-server.ts +17 -15
- package/src/lsp.ts +37 -14
- package/src/site.ts +1 -1
- package/src/type.ts +1 -1
- package/src/unify.ts +52 -1
- package/src/utility.ts +3 -9
- package/src/val/BagVal.ts +9 -6
- package/src/val/BigDecimalVal.ts +117 -0
- package/src/val/BigIntegerVal.ts +110 -0
- package/src/val/BooleanVal.ts +1 -1
- package/src/val/CloseFuncVal.ts +1 -1
- package/src/val/ConjunctVal.ts +1 -1
- package/src/val/ConstraintVal.ts +561 -0
- package/src/val/CopyFuncVal.ts +1 -1
- package/src/val/Decimal.ts +385 -0
- package/src/val/DisjunctVal.ts +1 -37
- package/src/val/ExpectVal.ts +1 -1
- package/src/val/FeatureVal.ts +1 -1
- package/src/val/FuncBaseVal.ts +1 -1
- package/src/val/HideFuncVal.ts +1 -1
- package/src/val/IntegerVal.ts +81 -5
- package/src/val/JunctionVal.ts +1 -1
- package/src/val/KeyFuncVal.ts +36 -4
- package/src/val/ListVal.ts +3 -5
- package/src/val/LowerFuncVal.ts +22 -5
- package/src/val/MapVal.ts +13 -9
- package/src/val/MoveFuncVal.ts +27 -10
- package/src/val/NilVal.ts +13 -3
- package/src/val/NullVal.ts +1 -1
- package/src/val/NumberVal.ts +27 -4
- package/src/val/OpBaseVal.ts +10 -10
- package/src/val/OpenFuncVal.ts +1 -1
- package/src/val/PathFuncVal.ts +10 -2
- package/src/val/PlusOpVal.ts +168 -32
- package/src/val/PrefFuncVal.ts +1 -1
- package/src/val/PrefVal.ts +70 -8
- package/src/val/RefVal.ts +143 -11
- package/src/val/ScalarKindVal.ts +123 -12
- package/src/val/ScalarVal.ts +20 -3
- package/src/val/StringVal.ts +1 -1
- package/src/val/SuperFuncVal.ts +15 -2
- package/src/val/TopVal.ts +1 -1
- package/src/val/TypeFuncVal.ts +1 -1
- package/src/val/UpperFuncVal.ts +22 -5
- package/src/val/Val.ts +13 -8
- package/src/val/VarVal.ts +8 -2
- package/src/val/numcmp.ts +189 -0
- package/src/val/numkind.ts +256 -0
- package/src/val/valutil.ts +36 -0
package/src/val/ListVal.ts
CHANGED
|
@@ -20,7 +20,6 @@ import {
|
|
|
20
20
|
explainOpen,
|
|
21
21
|
ec,
|
|
22
22
|
explainClose,
|
|
23
|
-
items,
|
|
24
23
|
} from '../utility'
|
|
25
24
|
|
|
26
25
|
import { makeNilErr, AontuError } from '../err'
|
|
@@ -33,7 +32,6 @@ import {
|
|
|
33
32
|
import { ConjunctVal } from './ConjunctVal'
|
|
34
33
|
import { NilVal } from './NilVal'
|
|
35
34
|
import { BagVal } from './BagVal'
|
|
36
|
-
import { empty } from './Val'
|
|
37
35
|
|
|
38
36
|
|
|
39
37
|
class ListVal extends BagVal {
|
|
@@ -121,9 +119,9 @@ class ListVal extends BagVal {
|
|
|
121
119
|
|
|
122
120
|
propagateMarks(this, child)
|
|
123
121
|
|
|
122
|
+
// child is non-nullish: propagateMarks above dereferences it.
|
|
124
123
|
out.peg[key] =
|
|
125
|
-
|
|
126
|
-
child.isNil ? child :
|
|
124
|
+
child.isNil ? child :
|
|
127
125
|
key_spread_cj.isNil ? key_spread_cj :
|
|
128
126
|
key_spread_cj.isTop && child.done ? child :
|
|
129
127
|
child.isTop && key_spread_cj.done ? key_spread_cj :
|
|
@@ -271,7 +269,7 @@ class ListVal extends BagVal {
|
|
|
271
269
|
this.peg[k].canon).join(',') +
|
|
272
270
|
']'
|
|
273
271
|
}
|
|
274
|
-
}
|
|
272
|
+
} /* node:coverage ignore next 8 */
|
|
275
273
|
|
|
276
274
|
|
|
277
275
|
|
package/src/val/LowerFuncVal.ts
CHANGED
|
@@ -15,7 +15,8 @@ import { makeNilErr } from '../err'
|
|
|
15
15
|
|
|
16
16
|
import { NilVal } from '../val/NilVal'
|
|
17
17
|
import { ScalarKindVal } from '../val/ScalarKindVal'
|
|
18
|
-
import {
|
|
18
|
+
import { makeScalarLike } from '../val/valutil'
|
|
19
|
+
import { Decimal } from '../val/Decimal'
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
|
|
@@ -43,14 +44,30 @@ class LowerFuncVal extends FuncBaseVal {
|
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
resolve(ctx: AontuContext | undefined, args: Val[]) {
|
|
46
|
-
|
|
47
|
+
// A missing argument (`lower()`) must produce an invalid-arg error
|
|
48
|
+
// value, as the Go port does — reading .peg off nothing threw a
|
|
49
|
+
// TypeError that the unifier could only report as an opaque
|
|
50
|
+
// internal error.
|
|
51
|
+
const arg = args?.[0]
|
|
52
|
+
const oldpeg = arg?.peg
|
|
47
53
|
const peg = 'string' === typeof oldpeg ? oldpeg.toLowerCase() :
|
|
48
54
|
'number' === typeof oldpeg ? Math.floor(oldpeg) :
|
|
49
|
-
|
|
55
|
+
// The exact leaves take an EXACT floor and keep their kind: a
|
|
56
|
+
// biginteger is already integral so it is its own floor, and a
|
|
57
|
+
// bigdecimal floors by coefficient arithmetic. Math.floor is not
|
|
58
|
+
// an option for either — it would round the value into binary64
|
|
59
|
+
// first, which is the loss the `0d` leaves exist to refuse.
|
|
60
|
+
'bigint' === typeof oldpeg ? oldpeg :
|
|
61
|
+
oldpeg instanceof Decimal ? oldpeg.floor() :
|
|
62
|
+
undefined
|
|
50
63
|
const out = this.place(
|
|
51
64
|
null == peg ?
|
|
52
65
|
makeNilErr(ctx, 'invalid-arg', this) :
|
|
53
|
-
|
|
66
|
+
// The floor keeps the ARGUMENT's kind (lower(2) is an integer 2,
|
|
67
|
+
// lower(1.9) is a number 1) — the function must not narrow
|
|
68
|
+
// number to integer, and this also makes the actual result kind
|
|
69
|
+
// agree with the superior() advertised below.
|
|
70
|
+
makeScalarLike(peg, arg)
|
|
54
71
|
)
|
|
55
72
|
return out
|
|
56
73
|
}
|
|
@@ -65,7 +82,7 @@ class LowerFuncVal extends FuncBaseVal {
|
|
|
65
82
|
super.superior()
|
|
66
83
|
}
|
|
67
84
|
|
|
68
|
-
}
|
|
85
|
+
} /* node:coverage ignore next 6 */
|
|
69
86
|
|
|
70
87
|
|
|
71
88
|
export {
|
package/src/val/MapVal.ts
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
import { ConjunctVal } from './ConjunctVal'
|
|
33
33
|
import { NilVal } from './NilVal'
|
|
34
34
|
import { BagVal } from './BagVal'
|
|
35
|
+
import { cmpCodePoint } from '../keyorder'
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
// Structural snapshots of ref spreads (see MapVal.unify), keyed by the
|
|
@@ -41,7 +42,7 @@ import { BagVal } from './BagVal'
|
|
|
41
42
|
// pass. The map lives on the unify root ctx (see Unify), so it persists
|
|
42
43
|
// across fixpoint passes and is GC'd with the run.
|
|
43
44
|
function spreadSnapKey(cj: any): string {
|
|
44
|
-
return cj.canon + '~' +
|
|
45
|
+
return cj.canon + '~' + cj.site.row + ':' + cj.site.col
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// Snapshot a path-dependent ref spread to its structural target once,
|
|
@@ -228,7 +229,9 @@ class MapVal extends BagVal {
|
|
|
228
229
|
// but costs O(keys) deep template clones per pass on large
|
|
229
230
|
// models — the dominant cost on generated-SDK model trees.
|
|
230
231
|
let oval: Val
|
|
231
|
-
|
|
232
|
+
// No `undefined !== child` here: propagateMarks above already
|
|
233
|
+
// dereferenced it, so a missing child would have thrown there.
|
|
234
|
+
if (!spread_cj.isTop
|
|
232
235
|
&& (child as any)._spr === (spread_cj as any).id) {
|
|
233
236
|
oval = child.done ? child :
|
|
234
237
|
unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
|
|
@@ -238,9 +241,9 @@ class MapVal extends BagVal {
|
|
|
238
241
|
else {
|
|
239
242
|
const key_spread_cj = spread_cj.spreadClone(keyctx)
|
|
240
243
|
|
|
244
|
+
// child is non-nullish: propagateMarks above dereferences it.
|
|
241
245
|
oval =
|
|
242
|
-
|
|
243
|
-
child.isNil ? child :
|
|
246
|
+
child.isNil ? child :
|
|
244
247
|
key_spread_cj.isNil ? key_spread_cj :
|
|
245
248
|
key_spread_cj.isTop && child.done ? child :
|
|
246
249
|
child.isTop && key_spread_cj.done ? key_spread_cj :
|
|
@@ -419,10 +422,11 @@ class MapVal extends BagVal {
|
|
|
419
422
|
|
|
420
423
|
|
|
421
424
|
get canon() {
|
|
422
|
-
// Keys are emitted
|
|
423
|
-
// independent of insertion/unification order
|
|
424
|
-
// port
|
|
425
|
-
|
|
425
|
+
// Keys are emitted in CODE POINT order so the canonical form is
|
|
426
|
+
// independent of insertion/unification order and matches the Go
|
|
427
|
+
// port. A bare .sort() is UTF-16 code-unit order, which puts an
|
|
428
|
+
// astral key ahead of everything in U+E000-U+FFFF -- see cmpCodePoint.
|
|
429
|
+
let keys = Object.keys(this.peg).sort(cmpCodePoint)
|
|
426
430
|
return '' +
|
|
427
431
|
// this.errcanon() +
|
|
428
432
|
// (this.mark.type ? '<type>' : '') +
|
|
@@ -447,7 +451,7 @@ class MapVal extends BagVal {
|
|
|
447
451
|
return this.spread.cj ? '&:' + this.spread.cj.inspect(null == d ? 0 : d + 1) : ''
|
|
448
452
|
}
|
|
449
453
|
|
|
450
|
-
}
|
|
454
|
+
} /* node:coverage ignore next 6 */
|
|
451
455
|
|
|
452
456
|
|
|
453
457
|
export {
|
package/src/val/MoveFuncVal.ts
CHANGED
|
@@ -57,25 +57,42 @@ class MoveFuncVal extends FuncBaseVal {
|
|
|
57
57
|
if (!orig.isNil) {
|
|
58
58
|
const src = orig.clone(ctx)
|
|
59
59
|
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
// THE HIDE-WALK ONLY RUNS WHEN THE CLONE IS A SEPARATE OBJECT.
|
|
61
|
+
//
|
|
62
|
+
// move() is the only function that marks the ORIGINAL rather than
|
|
63
|
+
// the clone -- that is how the value disappears from its old home
|
|
64
|
+
// and reappears at the destination. It relies on clone() returning
|
|
65
|
+
// something distinct to carry to the destination.
|
|
66
|
+
//
|
|
67
|
+
// TopVal.clone is the IDENTITY function, so for `move(top)` the
|
|
68
|
+
// clone and the original are the same object and the walk hid the
|
|
69
|
+
// very value being returned: the key vanished from generated output
|
|
70
|
+
// with no error, while canon still showed `x:top` -- the port
|
|
71
|
+
// disagreeing with itself.
|
|
72
|
+
//
|
|
73
|
+
// With the walk skipped, `move(top)` keeps `top` at the destination
|
|
74
|
+
// and then errors there because top is not generable, which is
|
|
75
|
+
// already what `x:top`, `x:copy(top)` and `x:pref(top)` do in both
|
|
76
|
+
// ports, and what `move(number)` does for the same reason.
|
|
77
|
+
if (src !== orig) {
|
|
78
|
+
if (src.isRef) {
|
|
79
|
+
src.mark._hide_found = true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
walk(orig, (_key: string | number | undefined, val: Val) => {
|
|
83
|
+
val.mark.hide = true
|
|
84
|
+
return val
|
|
85
|
+
})
|
|
62
86
|
}
|
|
63
87
|
|
|
64
|
-
walk(orig, (_key: string | number | undefined, val: Val) => {
|
|
65
|
-
val.mark.hide = true
|
|
66
|
-
return val
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
// out = new CopyFuncVal({ peg: [src] }, ctx)
|
|
70
88
|
out = new PrefFuncVal({ peg: [src] }, ctx)
|
|
71
|
-
// out = src
|
|
72
89
|
}
|
|
73
90
|
|
|
74
91
|
// console.log('MOVE-resolve', orig, out)
|
|
75
92
|
|
|
76
93
|
return out
|
|
77
94
|
}
|
|
78
|
-
}
|
|
95
|
+
} /* node:coverage ignore next 6 */
|
|
79
96
|
|
|
80
97
|
|
|
81
98
|
export {
|
package/src/val/NilVal.ts
CHANGED
|
@@ -18,6 +18,8 @@ import { Val, EMPTY_ERR } from './Val'
|
|
|
18
18
|
|
|
19
19
|
import { AontuError, descErr } from '../err'
|
|
20
20
|
|
|
21
|
+
import { codeClass } from '../hints'
|
|
22
|
+
|
|
21
23
|
|
|
22
24
|
class NilVal extends Val {
|
|
23
25
|
isNil = true
|
|
@@ -98,10 +100,10 @@ class NilVal extends Val {
|
|
|
98
100
|
if (spec && 'object' === typeof spec) {
|
|
99
101
|
this.why = spec?.why
|
|
100
102
|
this.msg = 'string' === typeof spec?.msg ? spec.msg : this.msg
|
|
101
|
-
this.err =
|
|
103
|
+
this.err =
|
|
102
104
|
Array.isArray(spec.err) ? [...spec.err] :
|
|
103
105
|
null != spec.err ? [spec.err] :
|
|
104
|
-
EMPTY_ERR
|
|
106
|
+
EMPTY_ERR
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
// Nil is always DONE, by definition.
|
|
@@ -135,6 +137,14 @@ class NilVal extends Val {
|
|
|
135
137
|
}
|
|
136
138
|
|
|
137
139
|
|
|
140
|
+
// The code's class from the shared registry (test/spec/errcodes.tsv):
|
|
141
|
+
// conflict | incomplete | reference | parse | budget | internal.
|
|
142
|
+
// A why-less nil classifies as its eventual gen-time code, nil_gen.
|
|
143
|
+
get class(): string {
|
|
144
|
+
return codeClass(null == this.why ? 'nil_gen' : String(this.why))
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
138
148
|
gen(ctx: AontuContext) {
|
|
139
149
|
// Unresolved nil cannot be generated, so always an error.
|
|
140
150
|
|
|
@@ -174,7 +184,7 @@ class NilVal extends Val {
|
|
|
174
184
|
//
|
|
175
185
|
// Do NOT use this sentinel for errors that may surface: those need
|
|
176
186
|
// real NilVals with proper site/path info for descErr formatting.
|
|
177
|
-
const TRIAL_NIL = new NilVal({ why: '|:trial-nil' })
|
|
187
|
+
const TRIAL_NIL = new NilVal({ why: '|:trial-nil' }) /* node:coverage ignore next 7 */
|
|
178
188
|
|
|
179
189
|
|
|
180
190
|
export {
|
package/src/val/NullVal.ts
CHANGED
package/src/val/NumberVal.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { makeNilErr, AontuError } from '../err'
|
|
13
13
|
|
|
14
14
|
import { ScalarVal } from './ScalarVal'
|
|
15
|
+
import { Float } from './ScalarKindVal'
|
|
15
16
|
|
|
16
17
|
import {
|
|
17
18
|
explainOpen,
|
|
@@ -20,6 +21,10 @@ import {
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
|
|
24
|
+
// The IEEE-754 binary64 leaf of the number tower. Its KIND is Float (the
|
|
25
|
+
// keyword `float`), not the `number` supertype: `number` names the family
|
|
26
|
+
// of numeric leaves and never tags a concrete value. The class name is
|
|
27
|
+
// historical -- `number` used to name this leaf.
|
|
23
28
|
class NumberVal extends ScalarVal {
|
|
24
29
|
isNumber = true
|
|
25
30
|
|
|
@@ -34,7 +39,7 @@ class NumberVal extends ScalarVal {
|
|
|
34
39
|
throw new AontuError('not-number: ' + spec.peg)
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
super({ ...spec, kind:
|
|
42
|
+
super({ ...spec, kind: Float }, ctx)
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
|
|
@@ -44,7 +49,7 @@ class NumberVal extends ScalarVal {
|
|
|
44
49
|
let out: Val = this
|
|
45
50
|
|
|
46
51
|
if (null != peer) {
|
|
47
|
-
if (peer.isScalarKind) {
|
|
52
|
+
if (peer.isScalarKind || (peer as any).isConstraint) {
|
|
48
53
|
out = peer.unify(this, ctx)
|
|
49
54
|
}
|
|
50
55
|
else if (
|
|
@@ -52,7 +57,7 @@ class NumberVal extends ScalarVal {
|
|
|
52
57
|
peer.kind === this.kind &&
|
|
53
58
|
peer.peg === this.peg
|
|
54
59
|
) {
|
|
55
|
-
// Same kind (both
|
|
60
|
+
// Same kind (both Float) and equal value: integer/float no
|
|
56
61
|
// longer cross-unify, so peer is necessarily a NumberVal here.
|
|
57
62
|
out = this
|
|
58
63
|
}
|
|
@@ -72,7 +77,25 @@ class NumberVal extends ScalarVal {
|
|
|
72
77
|
|
|
73
78
|
return out
|
|
74
79
|
}
|
|
75
|
-
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
// Canon must round-trip kind: reparsing the canon of a float-kind
|
|
83
|
+
// value has to yield a float-kind value again. A rendering with no
|
|
84
|
+
// '.' and no exponent (1 -> "1") would reparse as an integer, so it
|
|
85
|
+
// gains a ".0" suffix. Renderings that already carry a fraction, an
|
|
86
|
+
// exponent, or are NaN/Infinity are left alone.
|
|
87
|
+
//
|
|
88
|
+
// This is the CANON path only. String coercion inside `+` keeps plain
|
|
89
|
+
// JS parity ("a"+1.0 is "a1"), so it must not pick up the suffix.
|
|
90
|
+
get canon() {
|
|
91
|
+
const s = super.canon
|
|
92
|
+
return (s.includes('.') ||
|
|
93
|
+
s.includes('e') ||
|
|
94
|
+
s.includes('E') ||
|
|
95
|
+
s.includes('N') ||
|
|
96
|
+
s.includes('I')) ? s : s + '.0'
|
|
97
|
+
}
|
|
98
|
+
} /* node:coverage ignore next 6 */
|
|
76
99
|
|
|
77
100
|
|
|
78
101
|
export {
|
package/src/val/OpBaseVal.ts
CHANGED
|
@@ -67,7 +67,11 @@ class OpBaseVal extends FeatureVal {
|
|
|
67
67
|
|
|
68
68
|
unify(peer: Val, ctx: AontuContext): Val {
|
|
69
69
|
const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Op:' + this.opname(), this, peer)
|
|
70
|
-
|
|
70
|
+
// Declared without an initial value: every arm below assigns it, and
|
|
71
|
+
// seeding it with `this` made the two arms that stand the op read as
|
|
72
|
+
// redundant self-assignments. The arms themselves stay as they are —
|
|
73
|
+
// they mirror the dispatch switch in go/op.go arm for arm (ADR-001).
|
|
74
|
+
let out: Val
|
|
71
75
|
|
|
72
76
|
if (this.id == peer.id) {
|
|
73
77
|
return this
|
|
@@ -88,16 +92,12 @@ class OpBaseVal extends FeatureVal {
|
|
|
88
92
|
// console.log('OPVAL', this.id, this.opname(), pegdone, newpeg.map(p => p.canon))
|
|
89
93
|
|
|
90
94
|
if (pegdone) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
result =
|
|
94
|
-
|
|
95
|
-
if (null == result && this.canon === peer.canon) {
|
|
96
|
-
out = this
|
|
97
|
-
}
|
|
95
|
+
// `|| this` makes result truthy, so an op that cannot compute yet
|
|
96
|
+
// takes the OpBaseVal arm below rather than a separate null arm.
|
|
97
|
+
let result: Val = this.operate(ctx, newpeg) || this
|
|
98
98
|
|
|
99
99
|
// TODO: should be result.isOp
|
|
100
|
-
|
|
100
|
+
if (result instanceof OpBaseVal) {
|
|
101
101
|
if (peer.isTop) {
|
|
102
102
|
out = this
|
|
103
103
|
}
|
|
@@ -223,7 +223,7 @@ class OpBaseVal extends FeatureVal {
|
|
|
223
223
|
|
|
224
224
|
return undefined
|
|
225
225
|
}
|
|
226
|
-
}
|
|
226
|
+
} /* node:coverage ignore next 6 */
|
|
227
227
|
|
|
228
228
|
|
|
229
229
|
export {
|
package/src/val/OpenFuncVal.ts
CHANGED
package/src/val/PathFuncVal.ts
CHANGED
|
@@ -45,7 +45,15 @@ class PathFuncVal extends FuncBaseVal {
|
|
|
45
45
|
let arg = args[0]
|
|
46
46
|
|
|
47
47
|
if (0 === this.prepared) {
|
|
48
|
-
|
|
48
|
+
// A missing argument (`path()`) must produce an invalid-arg error
|
|
49
|
+
// value, as the Go port does -- reading .isScalar off nothing threw
|
|
50
|
+
// a TypeError that the unifier could only report as an opaque
|
|
51
|
+
// internal error. Same discipline breach, and same fix, as the one
|
|
52
|
+
// UpperFuncVal records; path() was missed in that sweep.
|
|
53
|
+
if (null == arg) {
|
|
54
|
+
arg = makeNilErr(ctx, 'invalid-arg', this)
|
|
55
|
+
}
|
|
56
|
+
else if (arg.isScalar) {
|
|
49
57
|
arg = this.place(new RefVal({ peg: [arg], absolute: false }))
|
|
50
58
|
}
|
|
51
59
|
else if (!arg.isRef) {
|
|
@@ -67,7 +75,7 @@ class PathFuncVal extends FuncBaseVal {
|
|
|
67
75
|
return out
|
|
68
76
|
}
|
|
69
77
|
|
|
70
|
-
}
|
|
78
|
+
} /* node:coverage ignore next 6 */
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
export {
|
package/src/val/PlusOpVal.ts
CHANGED
|
@@ -12,14 +12,53 @@ import {
|
|
|
12
12
|
} from '../ctx'
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
import { makeNilErr } from '../err'
|
|
15
16
|
|
|
16
17
|
import { IntegerVal } from '../val/IntegerVal'
|
|
17
18
|
import { NumberVal } from '../val/NumberVal'
|
|
18
19
|
import { StringVal } from '../val/StringVal'
|
|
19
20
|
import { BooleanVal } from '../val/BooleanVal'
|
|
21
|
+
import { BigIntegerVal } from '../val/BigIntegerVal'
|
|
22
|
+
import { BigDecimalVal } from '../val/BigDecimalVal'
|
|
23
|
+
import { Decimal, decimalOverBudget } from '../val/Decimal'
|
|
24
|
+
import { integerDigits } from '../val/numkind'
|
|
25
|
+
import { isIntegerStorable } from '../val/numkind'
|
|
20
26
|
import { OpBaseVal } from './OpBaseVal'
|
|
21
27
|
|
|
22
28
|
|
|
29
|
+
// The operand kinds `+` understands. Anything else (a kind, map, list,
|
|
30
|
+
// null, top, func) is not an operand at all and leaves the op
|
|
31
|
+
// unresolved.
|
|
32
|
+
type OpKind =
|
|
33
|
+
'string' | 'boolean' | 'integer' | 'float' | 'biginteger' | 'bigdecimal'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// D6 -- THE EXACT LADDER: integer < biginteger < bigdecimal. A mixed
|
|
37
|
+
// operation between exact leaves promotes to the WIDEST operand and is
|
|
38
|
+
// computed exactly, so `1 + 0d2` is a biginteger and `1 + 0d0.5` a
|
|
39
|
+
// bigdecimal.
|
|
40
|
+
//
|
|
41
|
+
// `float` is deliberately absent: it is OFF the ladder, keeping only its
|
|
42
|
+
// classic contagion against `integer`. Its absence from this table is
|
|
43
|
+
// what makes the float-with-big pairs fall through to the hard error
|
|
44
|
+
// below rather than promoting.
|
|
45
|
+
//
|
|
46
|
+
// Results NEVER demote: `0d5 + -0d2` stays a biginteger even though 3
|
|
47
|
+
// would fit an int64. Demotion would make the result kind depend on the
|
|
48
|
+
// values rather than on the operand kinds, and a document could then
|
|
49
|
+
// change leaf under a value edit.
|
|
50
|
+
const EXACT_RANK: Record<string, number> = {
|
|
51
|
+
integer: 1,
|
|
52
|
+
biginteger: 2,
|
|
53
|
+
bigdecimal: 3,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
function isBig(k: OpKind): boolean {
|
|
58
|
+
return 'biginteger' === k || 'bigdecimal' === k
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
|
|
23
62
|
class PlusOpVal extends OpBaseVal {
|
|
24
63
|
isPlusOp = true
|
|
25
64
|
|
|
@@ -40,60 +79,122 @@ class PlusOpVal extends OpBaseVal {
|
|
|
40
79
|
}
|
|
41
80
|
|
|
42
81
|
|
|
43
|
-
operate(
|
|
82
|
+
operate(ctx: AontuContext, args: Val[]) {
|
|
44
83
|
// Only concrete scalar operands are valid: anything else (kinds,
|
|
45
84
|
// maps, lists, null, top, funcs) must not coerce — the JS `+` would
|
|
46
85
|
// leak internals like "[object Object]" into output. A non-scalar
|
|
47
86
|
// operand leaves the op unresolved, which generate() reports.
|
|
48
|
-
const
|
|
49
|
-
// A pref operand contributes its preferred value (`pref(1)+2`)
|
|
87
|
+
const operand = (v: any) => {
|
|
88
|
+
// A pref operand contributes its preferred value (`pref(1)+2`),
|
|
89
|
+
// and therefore that value's kind too.
|
|
50
90
|
while (v?.isPref) {
|
|
51
91
|
v = v.peg
|
|
52
92
|
}
|
|
53
|
-
|
|
54
|
-
const t = typeof p
|
|
55
|
-
return 'string' === t || 'number' === t || 'boolean' === t ? p : undefined
|
|
93
|
+
return v
|
|
56
94
|
}
|
|
57
|
-
let a: any = prim(args[0])
|
|
58
|
-
let b: any = prim(args[1])
|
|
59
95
|
|
|
60
|
-
|
|
61
|
-
|
|
96
|
+
// The operand's LEAF, not the JavaScript type of its peg: integer
|
|
97
|
+
// and float share `number`, while the exact leaves are told apart
|
|
98
|
+
// from each other and from everything else by their own flags. The
|
|
99
|
+
// whole ladder below dispatches on this and never on `typeof`.
|
|
100
|
+
const opkind = (v: any): OpKind | undefined => {
|
|
101
|
+
if (!(v?.isVal && v.isScalar)) {
|
|
102
|
+
return undefined
|
|
103
|
+
}
|
|
104
|
+
if (v.isBigInteger) {
|
|
105
|
+
return 'biginteger'
|
|
106
|
+
}
|
|
107
|
+
if (v.isBigDecimal) {
|
|
108
|
+
return 'bigdecimal'
|
|
109
|
+
}
|
|
110
|
+
if (v.isInteger) {
|
|
111
|
+
return 'integer'
|
|
112
|
+
}
|
|
113
|
+
const t = typeof v.peg
|
|
114
|
+
return 'number' === t ? 'float' :
|
|
115
|
+
'string' === t ? 'string' :
|
|
116
|
+
'boolean' === t ? 'boolean' :
|
|
117
|
+
undefined
|
|
62
118
|
}
|
|
63
119
|
|
|
64
|
-
const
|
|
65
|
-
const
|
|
120
|
+
const av: any = operand(args[0])
|
|
121
|
+
const bv: any = operand(args[1])
|
|
122
|
+
const ak = opkind(av)
|
|
123
|
+
const bk = opkind(bv)
|
|
66
124
|
|
|
67
|
-
|
|
68
|
-
if ('boolean' === at && 'boolean' === bt) {
|
|
69
|
-
peg = a || b
|
|
70
|
-
}
|
|
71
|
-
else if ('string' === at || 'string' === bt) {
|
|
72
|
-
peg = String(a) + String(b)
|
|
73
|
-
}
|
|
74
|
-
else if ('boolean' === at || 'boolean' === bt) {
|
|
75
|
-
// boolean mixed with a number does not coerce (no JS 0/1).
|
|
125
|
+
if (undefined === ak || undefined === bk) {
|
|
76
126
|
return undefined
|
|
77
127
|
}
|
|
78
|
-
|
|
79
|
-
|
|
128
|
+
|
|
129
|
+
if ('boolean' === ak && 'boolean' === bk) {
|
|
130
|
+
return new BooleanVal({ peg: av.peg || bv.peg })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// STRING CONCATENATION RENDERS DIGITS, NOT KIND DECORATION. The `0d`
|
|
134
|
+
// marker is canon's way of naming the leaf and never belongs in a
|
|
135
|
+
// string, exactly as R4's `.0` float suffix does not ("q" + 0d0.1 is
|
|
136
|
+
// "q0.1", and "q" + 1.0 is "q1"). Decimal.toString() is that
|
|
137
|
+
// marker-free rendering, which is why canon() wraps it rather than
|
|
138
|
+
// the other way round.
|
|
139
|
+
if ('string' === ak || 'string' === bk) {
|
|
140
|
+
return new StringVal({ peg: digits(av, ak) + digits(bv, bk) })
|
|
80
141
|
}
|
|
81
142
|
|
|
82
|
-
|
|
143
|
+
// A boolean mixed with a number does not coerce (no JS 0/1).
|
|
144
|
+
if ('boolean' === ak || 'boolean' === bk) {
|
|
145
|
+
return undefined
|
|
146
|
+
}
|
|
83
147
|
|
|
84
|
-
|
|
148
|
+
// FLOAT IS OFF THE EXACT LADDER, and mixing it with either big leaf
|
|
149
|
+
// is a hard error in BOTH operand orders: a big type never silently
|
|
150
|
+
// becomes a binary float. Promotion the other way is no better —
|
|
151
|
+
// binary64 cannot hold every exact value, so either direction throws
|
|
152
|
+
// away exactness the document explicitly asked for by writing `0d`.
|
|
153
|
+
// The error names both leaves in operand order.
|
|
154
|
+
if (('float' === ak && isBig(bk)) || (isBig(ak) && 'float' === bk)) {
|
|
155
|
+
return makeNilErr(ctx, 'exact_float_mix', this, undefined, 'add',
|
|
156
|
+
{ left: ak, right: bk })
|
|
157
|
+
}
|
|
85
158
|
|
|
86
|
-
|
|
87
|
-
|
|
159
|
+
// Float with float or integer: unchanged R5 contagion, binary64
|
|
160
|
+
// addition, float result (`1 + 2.0` is `3.0`).
|
|
161
|
+
if ('float' === ak || 'float' === bk) {
|
|
162
|
+
return new NumberVal({ peg: av.peg + bv.peg })
|
|
88
163
|
}
|
|
89
|
-
|
|
90
|
-
|
|
164
|
+
|
|
165
|
+
// Both operands are on the exact ladder: promote to the widest.
|
|
166
|
+
const rank = EXACT_RANK[bk] < EXACT_RANK[ak] ? EXACT_RANK[ak] : EXACT_RANK[bk]
|
|
167
|
+
|
|
168
|
+
if (EXACT_RANK.bigdecimal === rank) {
|
|
169
|
+
// Exact scaled-decimal addition. The budget applies to results as
|
|
170
|
+
// well as literals (D6): an exact sum too wide to hold is refused,
|
|
171
|
+
// never rounded to fit.
|
|
172
|
+
const sum = decimal(av, ak).add(decimal(bv, bk))
|
|
173
|
+
return decimalOverBudget(sum) ?
|
|
174
|
+
makeNilErr(ctx, 'decimal_budget', this, undefined, 'add') :
|
|
175
|
+
new BigDecimalVal({ peg: sum })
|
|
91
176
|
}
|
|
92
|
-
|
|
93
|
-
|
|
177
|
+
|
|
178
|
+
// integer and biginteger both add as exact integers.
|
|
179
|
+
const sum = integer(av, ak) + integer(bv, bk)
|
|
180
|
+
|
|
181
|
+
if (EXACT_RANK.biginteger === rank) {
|
|
182
|
+
// Unbounded and exact: nothing to check, and no demotion to
|
|
183
|
+
// `integer` however small the result.
|
|
184
|
+
return new BigIntegerVal({ peg: sum })
|
|
94
185
|
}
|
|
95
186
|
|
|
96
|
-
|
|
187
|
+
// INTEGER + INTEGER IS COMPUTED EXACTLY, then offered to the integer
|
|
188
|
+
// leaf under the same storage contract R1 applies to a literal.
|
|
189
|
+
// Adding through float64 (as this did) silently rounded sums of
|
|
190
|
+
// exact operands — 4503599627370496 + 4503599627370497 came back as
|
|
191
|
+
// …992 — which is precisely the corruption the tower refuses. The
|
|
192
|
+
// sum that will not fit is an error pointing at `0d`, not a rounded
|
|
193
|
+
// answer.
|
|
194
|
+
return isIntegerStorable(sum) ?
|
|
195
|
+
new IntegerVal({ peg: Number(sum) }) :
|
|
196
|
+
makeNilErr(ctx, 'inexact_integer_sum', this, undefined, 'add',
|
|
197
|
+
{ sum: sum.toString() })
|
|
97
198
|
}
|
|
98
199
|
|
|
99
200
|
|
|
@@ -104,6 +205,41 @@ class PlusOpVal extends OpBaseVal {
|
|
|
104
205
|
}
|
|
105
206
|
|
|
106
207
|
|
|
208
|
+
// The digits of an operand for string concatenation: no `0d` marker, no
|
|
209
|
+
// R4 `.0` suffix — the plain rendering of the number, in every leaf.
|
|
210
|
+
//
|
|
211
|
+
// INTEGER KIND GOES THROUGH integerDigits. This was the THIRD site to
|
|
212
|
+
// render an integer-kind peg with JavaScript's shortest round-tripping
|
|
213
|
+
// form, so `"" + 1152921504606846976` produced "1152921504606847000" — a
|
|
214
|
+
// different integer — where Go produced the exact digits. See #21 and
|
|
215
|
+
// integerDigits, which exists so the set of such sites is greppable.
|
|
216
|
+
//
|
|
217
|
+
// The other leaves are already exact or already correct: a bigint
|
|
218
|
+
// stringifies to its exact digits, a Decimal renders its own, and a FLOAT
|
|
219
|
+
// must keep String() because its shortest form is the right answer and is
|
|
220
|
+
// what Go prints too (`"a" + 1.0` is "a1").
|
|
221
|
+
function digits(v: any, k: OpKind): string {
|
|
222
|
+
return 'bigdecimal' === k ? v.peg.toString() :
|
|
223
|
+
'integer' === k ? integerDigits(v.peg) :
|
|
224
|
+
String(v.peg)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
// An exact-ladder operand as an exact integer. Only reached for the two
|
|
229
|
+
// integral leaves; an `integer` peg is integral by construction, so
|
|
230
|
+
// BigInt() is exact.
|
|
231
|
+
function integer(v: any, k: OpKind): bigint {
|
|
232
|
+
return 'biginteger' === k ? v.peg : BigInt(v.peg)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
// An exact-ladder operand as a Decimal, for the promotion to bigdecimal.
|
|
237
|
+
// Scale 0 normalises to the one decimal place the leaf keeps.
|
|
238
|
+
function decimal(v: any, k: OpKind): Decimal {
|
|
239
|
+
return 'bigdecimal' === k ? v.peg : new Decimal(integer(v, k), 0)
|
|
240
|
+
} /* node:coverage ignore next 6 */
|
|
241
|
+
|
|
242
|
+
|
|
107
243
|
export {
|
|
108
244
|
PlusOpVal,
|
|
109
245
|
}
|