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.
Files changed (197) hide show
  1. package/bin/aontu-lsp.js +6 -0
  2. package/bin/aontu.js +7 -0
  3. package/dist/aontu.d.ts +4 -2
  4. package/dist/aontu.js +21 -8
  5. package/dist/aontu.js.map +1 -1
  6. package/dist/cli.d.ts +0 -1
  7. package/dist/cli.js +43 -53
  8. package/dist/cli.js.map +1 -1
  9. package/dist/ctx.js +1 -1
  10. package/dist/ctx.js.map +1 -1
  11. package/dist/err.js +3 -2
  12. package/dist/err.js.map +1 -1
  13. package/dist/exactjson.d.ts +37 -0
  14. package/dist/exactjson.js +228 -0
  15. package/dist/exactjson.js.map +1 -0
  16. package/dist/hints.d.ts +3 -1
  17. package/dist/hints.js +187 -5
  18. package/dist/hints.js.map +1 -1
  19. package/dist/keyorder.d.ts +9 -0
  20. package/dist/keyorder.js +65 -0
  21. package/dist/keyorder.js.map +1 -0
  22. package/dist/lang.js +263 -30
  23. package/dist/lang.js.map +1 -1
  24. package/dist/lsp-server.d.ts +2 -2
  25. package/dist/lsp-server.js +10 -10
  26. package/dist/lsp-server.js.map +1 -1
  27. package/dist/lsp.js +39 -21
  28. package/dist/lsp.js.map +1 -1
  29. package/dist/site.js +1 -1
  30. package/dist/site.js.map +1 -1
  31. package/dist/tsconfig.tsbuildinfo +1 -1
  32. package/dist/unify.js +48 -1
  33. package/dist/unify.js.map +1 -1
  34. package/dist/utility.js +3 -8
  35. package/dist/utility.js.map +1 -1
  36. package/dist/val/BagVal.js +9 -6
  37. package/dist/val/BagVal.js.map +1 -1
  38. package/dist/val/BigDecimalVal.d.ts +13 -0
  39. package/dist/val/BigDecimalVal.js +80 -0
  40. package/dist/val/BigDecimalVal.js.map +1 -0
  41. package/dist/val/BigIntegerVal.d.ts +11 -0
  42. package/dist/val/BigIntegerVal.js +75 -0
  43. package/dist/val/BigIntegerVal.js.map +1 -0
  44. package/dist/val/BooleanVal.js +1 -1
  45. package/dist/val/BooleanVal.js.map +1 -1
  46. package/dist/val/CloseFuncVal.js +1 -1
  47. package/dist/val/CloseFuncVal.js.map +1 -1
  48. package/dist/val/ConjunctVal.js +1 -1
  49. package/dist/val/ConjunctVal.js.map +1 -1
  50. package/dist/val/ConstraintVal.d.ts +56 -0
  51. package/dist/val/ConstraintVal.js +407 -0
  52. package/dist/val/ConstraintVal.js.map +1 -0
  53. package/dist/val/CopyFuncVal.js +1 -1
  54. package/dist/val/CopyFuncVal.js.map +1 -1
  55. package/dist/val/Decimal.d.ts +32 -0
  56. package/dist/val/Decimal.js +318 -0
  57. package/dist/val/Decimal.js.map +1 -0
  58. package/dist/val/DisjunctVal.js +1 -29
  59. package/dist/val/DisjunctVal.js.map +1 -1
  60. package/dist/val/ExpectVal.js +1 -1
  61. package/dist/val/ExpectVal.js.map +1 -1
  62. package/dist/val/FeatureVal.js +1 -1
  63. package/dist/val/FeatureVal.js.map +1 -1
  64. package/dist/val/FuncBaseVal.js +1 -1
  65. package/dist/val/FuncBaseVal.js.map +1 -1
  66. package/dist/val/HideFuncVal.js +1 -1
  67. package/dist/val/HideFuncVal.js.map +1 -1
  68. package/dist/val/IntegerVal.d.ts +2 -0
  69. package/dist/val/IntegerVal.js +77 -5
  70. package/dist/val/IntegerVal.js.map +1 -1
  71. package/dist/val/JunctionVal.js +1 -1
  72. package/dist/val/JunctionVal.js.map +1 -1
  73. package/dist/val/KeyFuncVal.d.ts +1 -1
  74. package/dist/val/KeyFuncVal.js +34 -4
  75. package/dist/val/KeyFuncVal.js.map +1 -1
  76. package/dist/val/ListVal.js +7 -7
  77. package/dist/val/ListVal.js.map +1 -1
  78. package/dist/val/LowerFuncVal.js +21 -4
  79. package/dist/val/LowerFuncVal.js.map +1 -1
  80. package/dist/val/MapVal.js +17 -13
  81. package/dist/val/MapVal.js.map +1 -1
  82. package/dist/val/MoveFuncVal.js +26 -9
  83. package/dist/val/MoveFuncVal.js.map +1 -1
  84. package/dist/val/NilVal.d.ts +1 -0
  85. package/dist/val/NilVal.js +10 -3
  86. package/dist/val/NilVal.js.map +1 -1
  87. package/dist/val/NullVal.js +1 -1
  88. package/dist/val/NullVal.js.map +1 -1
  89. package/dist/val/NumberVal.d.ts +1 -0
  90. package/dist/val/NumberVal.js +25 -4
  91. package/dist/val/NumberVal.js.map +1 -1
  92. package/dist/val/OpBaseVal.js +10 -8
  93. package/dist/val/OpBaseVal.js.map +1 -1
  94. package/dist/val/OpenFuncVal.js +1 -1
  95. package/dist/val/OpenFuncVal.js.map +1 -1
  96. package/dist/val/PathFuncVal.js +10 -2
  97. package/dist/val/PathFuncVal.js.map +1 -1
  98. package/dist/val/PlusOpVal.d.ts +3 -1
  99. package/dist/val/PlusOpVal.js +140 -29
  100. package/dist/val/PlusOpVal.js.map +1 -1
  101. package/dist/val/PrefFuncVal.js +1 -1
  102. package/dist/val/PrefFuncVal.js.map +1 -1
  103. package/dist/val/PrefVal.d.ts +2 -0
  104. package/dist/val/PrefVal.js +44 -7
  105. package/dist/val/PrefVal.js.map +1 -1
  106. package/dist/val/RefVal.d.ts +2 -0
  107. package/dist/val/RefVal.js +135 -11
  108. package/dist/val/RefVal.js.map +1 -1
  109. package/dist/val/ScalarKindVal.d.ts +12 -2
  110. package/dist/val/ScalarKindVal.js +108 -13
  111. package/dist/val/ScalarKindVal.js.map +1 -1
  112. package/dist/val/ScalarVal.d.ts +1 -0
  113. package/dist/val/ScalarVal.js +18 -3
  114. package/dist/val/ScalarVal.js.map +1 -1
  115. package/dist/val/StringVal.js +1 -1
  116. package/dist/val/StringVal.js.map +1 -1
  117. package/dist/val/SuperFuncVal.d.ts +1 -1
  118. package/dist/val/SuperFuncVal.js +15 -2
  119. package/dist/val/SuperFuncVal.js.map +1 -1
  120. package/dist/val/TopVal.js +1 -1
  121. package/dist/val/TopVal.js.map +1 -1
  122. package/dist/val/TypeFuncVal.js +1 -1
  123. package/dist/val/TypeFuncVal.js.map +1 -1
  124. package/dist/val/UpperFuncVal.js +21 -4
  125. package/dist/val/UpperFuncVal.js.map +1 -1
  126. package/dist/val/Val.d.ts +1 -1
  127. package/dist/val/Val.js +11 -21
  128. package/dist/val/Val.js.map +1 -1
  129. package/dist/val/VarVal.js +8 -2
  130. package/dist/val/VarVal.js.map +1 -1
  131. package/dist/val/numcmp.d.ts +14 -0
  132. package/dist/val/numcmp.js +134 -0
  133. package/dist/val/numcmp.js.map +1 -0
  134. package/dist/val/numkind.d.ts +6 -0
  135. package/dist/val/numkind.js +224 -0
  136. package/dist/val/numkind.js.map +1 -0
  137. package/dist/val/valutil.d.ts +1 -0
  138. package/dist/val/valutil.js +35 -0
  139. package/dist/val/valutil.js.map +1 -1
  140. package/package.json +7 -5
  141. package/src/aontu.ts +26 -9
  142. package/src/cli.ts +48 -21
  143. package/src/ctx.ts +1 -1
  144. package/src/err.ts +3 -2
  145. package/src/exactjson.ts +254 -0
  146. package/src/hints.ts +213 -4
  147. package/src/keyorder.ts +73 -0
  148. package/src/lang.ts +303 -31
  149. package/src/lsp-server.ts +17 -15
  150. package/src/lsp.ts +37 -14
  151. package/src/site.ts +1 -1
  152. package/src/type.ts +1 -1
  153. package/src/unify.ts +52 -1
  154. package/src/utility.ts +3 -9
  155. package/src/val/BagVal.ts +9 -6
  156. package/src/val/BigDecimalVal.ts +117 -0
  157. package/src/val/BigIntegerVal.ts +110 -0
  158. package/src/val/BooleanVal.ts +1 -1
  159. package/src/val/CloseFuncVal.ts +1 -1
  160. package/src/val/ConjunctVal.ts +1 -1
  161. package/src/val/ConstraintVal.ts +561 -0
  162. package/src/val/CopyFuncVal.ts +1 -1
  163. package/src/val/Decimal.ts +385 -0
  164. package/src/val/DisjunctVal.ts +1 -37
  165. package/src/val/ExpectVal.ts +1 -1
  166. package/src/val/FeatureVal.ts +1 -1
  167. package/src/val/FuncBaseVal.ts +1 -1
  168. package/src/val/HideFuncVal.ts +1 -1
  169. package/src/val/IntegerVal.ts +81 -5
  170. package/src/val/JunctionVal.ts +1 -1
  171. package/src/val/KeyFuncVal.ts +36 -4
  172. package/src/val/ListVal.ts +3 -5
  173. package/src/val/LowerFuncVal.ts +22 -5
  174. package/src/val/MapVal.ts +13 -9
  175. package/src/val/MoveFuncVal.ts +27 -10
  176. package/src/val/NilVal.ts +13 -3
  177. package/src/val/NullVal.ts +1 -1
  178. package/src/val/NumberVal.ts +27 -4
  179. package/src/val/OpBaseVal.ts +10 -10
  180. package/src/val/OpenFuncVal.ts +1 -1
  181. package/src/val/PathFuncVal.ts +10 -2
  182. package/src/val/PlusOpVal.ts +168 -32
  183. package/src/val/PrefFuncVal.ts +1 -1
  184. package/src/val/PrefVal.ts +70 -8
  185. package/src/val/RefVal.ts +143 -11
  186. package/src/val/ScalarKindVal.ts +123 -12
  187. package/src/val/ScalarVal.ts +20 -3
  188. package/src/val/StringVal.ts +1 -1
  189. package/src/val/SuperFuncVal.ts +15 -2
  190. package/src/val/TopVal.ts +1 -1
  191. package/src/val/TypeFuncVal.ts +1 -1
  192. package/src/val/UpperFuncVal.ts +22 -5
  193. package/src/val/Val.ts +13 -8
  194. package/src/val/VarVal.ts +8 -2
  195. package/src/val/numcmp.ts +189 -0
  196. package/src/val/numkind.ts +256 -0
  197. package/src/val/valutil.ts +36 -0
@@ -23,6 +23,7 @@ import {
23
23
  import { top } from './top'
24
24
 
25
25
  import { FeatureVal } from './FeatureVal'
26
+ import { ScalarKindVal, kindFamily } from './ScalarKindVal'
26
27
 
27
28
 
28
29
  class PrefVal extends FeatureVal {
@@ -30,7 +31,26 @@ class PrefVal extends FeatureVal {
30
31
  isGenable = true
31
32
  cjo = 30000
32
33
 
33
- superpeg: Val
34
+ // The preferred value's own type: the yardstick for "did the peer say
35
+ // anything I did not already say?". A peer that resolves to this (or
36
+ // to the family gate below) leaves the preference standing.
37
+ // Both are assigned by resuper(), called from the constructor.
38
+ superpeg!: Val
39
+
40
+ // The gate an overriding peer must pass. A preference is a DEFAULT, so
41
+ // a concrete peer replaces it -- but only within the preferred value's
42
+ // FAMILY: `*lower(1.1) & a` is a conflict, not an override.
43
+ //
44
+ // Family and not leaf, because the number tower made `integer` and
45
+ // `float` disjoint siblings under `number`. Gating on the leaf would
46
+ // turn `*lower(2.2) & 3` (a float default overridden by an integer)
47
+ // into an error, which is a tightening no document asked for; gating
48
+ // on the family keeps it working and makes the mirror case
49
+ // (`*2 & 3.0`, an error before the tower) work too. For every
50
+ // non-numeric value the family root IS the leaf, so this is the
51
+ // preferred value's type as before.
52
+ familypeg!: Val
53
+
34
54
  rank: number = 0
35
55
 
36
56
  constructor(
@@ -46,18 +66,54 @@ class PrefVal extends FeatureVal {
46
66
  this.rank = 1 + spec.peg.rank
47
67
  }
48
68
 
49
- this.superpeg = this.peg.superior()
69
+ this.resuper()
50
70
  // console.log('PVC', this.peg.canon, this.superpeg.canon)
51
71
  }
52
72
 
53
73
 
74
+ // Recompute the type yardstick and the override gate from the current
75
+ // peg. Called again whenever the peg resolves (e.g. a ref).
76
+ private resuper() {
77
+ const peg: any = this.peg
78
+
79
+ // A preference whose peg is ITSELF a kind (`*integer`) constrains
80
+ // nothing: there is no type-of-a-type in this lattice, so any peer
81
+ // wins. (Pinned by test/spec/var.tsv:var-pref-kind-narrow. Before
82
+ // the tower this fell out of a ScalarKindVal's superior being top;
83
+ // now that a leaf kind lifts to `number`, it has to be said.)
84
+ if (true === peg.isScalarKind) {
85
+ this.superpeg = top()
86
+ this.familypeg = this.superpeg
87
+ return
88
+ }
89
+
90
+ const sup: any = peg.superior()
91
+ this.superpeg = sup
92
+
93
+ // No optional chain: superior() is contractually non-null (every
94
+ // Val returns one, a NilVal returning itself), so guarding against
95
+ // nullish here would claim a possibility the type does not have.
96
+ if (true === sup.isScalarKind) {
97
+ const family = kindFamily(sup.peg)
98
+ // The gate stands in for the preferred value in any conflict it
99
+ // reports, so it must carry the same site and path as the type it
100
+ // widens -- otherwise NilVal.make picks a different primary and
101
+ // the error moves to the wrong path.
102
+ this.familypeg = family === sup.peg ?
103
+ sup : sup.place(new ScalarKindVal({ peg: family, path: sup.path }))
104
+ }
105
+ else {
106
+ this.familypeg = sup
107
+ }
108
+ }
109
+
110
+
54
111
  // PrefVal unify always returns a PrefVal
55
112
  // PrefVals can only be removed by becoming Nil in a Disjunct
56
113
  unify(peer: Val, ctx: AontuContext): Val {
57
114
  peer = peer ?? top()
58
115
 
59
116
  const te = ctx.explain && explainOpen(ctx, ctx.explain, 'Pref', this, peer)
60
- let done = true
61
117
  let out: Val = this
62
118
  let why = ''
63
119
 
@@ -66,7 +122,7 @@ class PrefVal extends FeatureVal {
66
122
  this.peg, top(), 'pref/resolve')
67
123
  // console.log('PREF-RESOLVED', this.peg.canon, '->', resolved)
68
124
  this.peg = resolved
69
- this.superpeg = this.peg.superior()
125
+ this.resuper()
70
126
  }
71
127
 
72
128
  if (peer instanceof PrefVal) {
@@ -107,8 +163,12 @@ class PrefVal extends FeatureVal {
107
163
  why += 'super-'
108
164
 
109
165
  out = unite(te ? ctx.clone({ explain: ec(te, 'SUPER') }) : ctx,
110
- this.superpeg, peer, 'pref-super/' + this.id)
111
- if (out.same(this.superpeg)) {
166
+ this.familypeg, peer, 'pref-super/' + this.id)
167
+
168
+ // The peer added nothing beyond a type the preferred value already
169
+ // satisfies (`*1 & integer`, `*1 & number`), so the preference
170
+ // stands. Anything else is a concrete override and wins.
171
+ if (out.same(this.superpeg) || out.same(this.familypeg)) {
112
172
  out = this.peg
113
173
  why += 'same'
114
174
  }
@@ -119,7 +179,9 @@ class PrefVal extends FeatureVal {
119
179
  why += 'none'
120
180
  }
121
181
 
122
- out.dc = done ? DONE : this.dc + 1
182
+ // Every pref result is DONE, including a stuck conjunct from the
183
+ // superior-unify (mirrored by PrefVal.Unify in go/pref.go).
184
+ out.dc = DONE
123
185
 
124
186
  // console.log('PREFVAL-OUT', why, this.canon, peer.canon, '->', out.canon, out.done)
125
187
 
@@ -165,7 +227,7 @@ class PrefVal extends FeatureVal {
165
227
 
166
228
  return val.gen(ctx)
167
229
  }
168
- }
230
+ } /* node:coverage ignore next 6 */
169
231
 
170
232
 
171
233
  export {
package/src/val/RefVal.ts CHANGED
@@ -37,9 +37,18 @@ import { NumberVal } from './NumberVal'
37
37
  import { ConjunctVal } from './ConjunctVal'
38
38
  import { VarVal } from './VarVal'
39
39
  import { FeatureVal } from './FeatureVal'
40
+ import { integerDigits } from './numkind'
41
+ import { BigIntegerVal } from './BigIntegerVal'
42
+ import { BigDecimalVal } from './BigDecimalVal'
40
43
 
41
44
 
42
45
 
46
+ // A path segment no spelling can produce, used when append meets a Val
47
+ // class it has no rule for. A key cannot contain a NUL, so this can never
48
+ // match, which turns a silent path-shortening bug into a visible miss.
49
+ const UNSPELLABLE_SEGMENT = '\u0000unspellable'
50
+
51
+
43
52
  class RefVal extends FeatureVal {
44
53
  isRef = true
45
54
  isGenable = true
@@ -59,8 +68,9 @@ class RefVal extends FeatureVal {
59
68
  super(spec, ctx)
60
69
  this.peg = []
61
70
 
62
- this.absolute = true === this.absolute ? true : // absolute sticks
63
- true === spec.absolute ? true : false
71
+ // The field initialiser (absolute = false) has just run, so only
72
+ // the spec can carry absoluteness in (RefVal.clone re-passes it).
73
+ this.absolute = true === spec.absolute
64
74
 
65
75
  this.prefix = true === spec.prefix
66
76
 
@@ -102,6 +112,22 @@ class RefVal extends FeatureVal {
102
112
  this.peg.push(...partvals)
103
113
  }
104
114
 
115
+ // THE EXACT LEAVES ARE PATH TEXT LIKE ANY OTHER SPELLING. `0d1` as a
116
+ // segment addresses the key literally spelled `0d1` -- the same key
117
+ // `a:{0d1:7}` creates -- and is NOT the number 1, so it neither
118
+ // indexes a list nor reaches a key spelled `1`.
119
+ //
120
+ // Without these branches the part fell off the end of the chain and
121
+ // was SILENTLY DROPPED, so `$.a.0d0` resolved to `$.a` and handed back
122
+ // the CONTAINER. That is a wrong value, not a miss -- strictly worse
123
+ // than an error -- and it made `$.a.0d0` and `$.a.0d1` denote the same
124
+ // location.
125
+ else if (part instanceof BigIntegerVal || part instanceof BigDecimalVal) {
126
+ // A bigdecimal splits on its point exactly as a float does, so
127
+ // `$.x.0d1.5` addresses two levels.
128
+ this.peg.push(...part.src.split('.'))
129
+ }
130
+
105
131
  else if (part instanceof VarVal) {
106
132
  partval = part
107
133
  this.peg.push(partval)
@@ -131,6 +157,16 @@ class RefVal extends FeatureVal {
131
157
 
132
158
  this.peg.push(...part.peg)
133
159
  }
160
+
161
+ // A closed chain, deliberately. Every branch above ends in a push, so
162
+ // an unhandled Val class used to fall through in SILENCE and shorten
163
+ // the path by one segment -- which is how the two exact leaves, added
164
+ // by the number tower, made references resolve to their own container.
165
+ // A segment that cannot be spelled is pushed as one that cannot match,
166
+ // so the reference misses loudly instead of succeeding wrongly.
167
+ else {
168
+ this.peg.push(UNSPELLABLE_SEGMENT)
169
+ }
134
170
  }
135
171
 
136
172
 
@@ -146,14 +182,13 @@ class RefVal extends FeatureVal {
146
182
  // TODO: not resolved when all Vals in path are done is an error
147
183
  // as path cannot be found
148
184
  // let resolved: Val | undefined = null == ctx ? this : ctx.find(this)
149
- let found: Val | undefined = null == ctx ? this : this.find(ctx)
185
+ let found: Val | undefined = this.find(ctx)
150
186
 
187
+ // `?? this` makes resolved non-nullish, so an unresolved reference
188
+ // takes the RefVal arm below rather than a separate null arm.
151
189
  const resolved = found ?? this
152
190
 
153
- if (null == resolved && this.canon === peer.canon) {
154
- out = this
155
- }
156
- else if (resolved instanceof RefVal) {
191
+ if (resolved instanceof RefVal) {
157
192
  if (peer.isTop) {
158
193
  out = this
159
194
  why = 'pt'
@@ -272,7 +307,19 @@ class RefVal extends FeatureVal {
272
307
  // The resolved variable IS a path segment: $seg.r with
273
308
  // seg="x" reads ...x.r (previously the coerced value was
274
309
  // dropped, silently reading the path without it).
275
- parts.push('' + part.peg)
310
+ //
311
+ // Integer kind renders its EXACT digits -- the FOURTH site
312
+ // to get this wrong (see integerDigits and #21). `'' + peg`
313
+ // on a JS number gives the shortest round-tripping form, so
314
+ // a variable bound to 2^60 addressed the key
315
+ // "1152921504606847000" and missed the real one. Go's
316
+ // ref.go dispatches on kind here and was already correct.
317
+ //
318
+ // Every other kind is already right under `'' +`: a bigint
319
+ // and a Decimal stringify to exact digits, and a float must
320
+ // keep JS parity.
321
+ parts.push(part.isInteger ?
322
+ integerDigits(part.peg as number) : '' + part.peg)
276
323
  }
277
324
  }
278
325
  }
@@ -302,8 +349,20 @@ class RefVal extends FeatureVal {
302
349
  (p === sep ? a.length = a.length - 1 : a.push(p), a)), [])
303
350
 
304
351
  if (modes.includes('KEY')) {
352
+ // STRINGIFY. A LIST index arrives here as a JS NUMBER (jsonic puts
353
+ // it in the path as one, and lang.ts copies the path wholesale),
354
+ // so `.$KEY` inside a list built a StringVal whose peg was the
355
+ // number 0 -- an ill-formed value, not a design choice: it canoned
356
+ // as a bare 0, generated a JSON number, satisfied `number` and
357
+ // failed `string`. Go stringifies, and key() already agreed with
358
+ // Go, so this port disagreed with itself.
359
+ //
360
+ // Coerced HERE, at the consumption site, rather than by
361
+ // normalising Val.path: the numeric segment originates in jsonic's
362
+ // own r.k.path and every other path consumer (find's descent,
363
+ // key(), the clone/spread machinery) already handles it.
305
364
  let key = this.path[this.path.length - 2]
306
- let sv = new StringVal({ peg: null == key ? '' : key }, ctx)
365
+ let sv = new StringVal({ peg: null == key ? '' : '' + key }, ctx)
307
366
 
308
367
  // TODO: other props?
309
368
  sv.dc = DONE
@@ -354,8 +413,16 @@ class RefVal extends FeatureVal {
354
413
  else if (pI === refpath.length) {
355
414
  out = node
356
415
 
416
+ // A reference landing on another reference may be a PROVEN
417
+ // mutual cycle (a: $.b, b: $.a) -- follow the plain-ref chain
418
+ // and, if it revisits a node, report path_cycle now instead of
419
+ // deferring every pass and dying later as a generic ref error.
420
+ // No proof (chain leaves plain refs, or ends) defers as before.
421
+ if (null != out && (out as any).isRef && this.detectRefCycle(ctx)) {
422
+ out = makeNilErr(ctx, 'path_cycle', this)
423
+ }
357
424
  // Types and hidden values are cloned and made concrete
358
- if (null != out) { // && (out.mark.type || out.mark.hide)) {
425
+ else if (null != out) { // && (out.mark.type || out.mark.hide)) {
359
426
 
360
427
  // console.log('FOUND-A', out)
361
428
 
@@ -397,6 +464,71 @@ class RefVal extends FeatureVal {
397
464
  }
398
465
 
399
466
 
467
+ // Follow the chain of plain references from this node; true iff the
468
+ // chain revisits a node -- a PROVEN reference cycle, distinct from a
469
+ // merely unresolved reference. Detection is only on the resolution
470
+ // chain revisiting a node, never on syntactic shape: a chain that
471
+ // passes through a variable segment, a conjunct, a function, or any
472
+ // non-ref value yields no proof and the ref defers as before.
473
+ detectRefCycle(ctx: AontuContext): boolean {
474
+ const seen = new Set<number>()
475
+ let cur: RefVal = this
476
+ // No hop cap: every iteration either returns or adds a NEW ref to
477
+ // `seen`, and the tree holds finitely many refs, so the chase
478
+ // terminates at the first repetition or the first non-ref — a cap
479
+ // would just make cycles longer than it invisible.
480
+ for (; ;) {
481
+ if (seen.has(cur.id)) {
482
+ return true
483
+ }
484
+ seen.add(cur.id)
485
+ const rp = cur.plainRefPath()
486
+ if (null == rp) {
487
+ return false
488
+ }
489
+ let node: any = ctx.root
490
+ for (let i = 0; i < rp.length && null != node; i++) {
491
+ node = (node.isMap || node.isList) ? node.peg[rp[i]] : undefined
492
+ }
493
+ if (null == node || !node.isRef) {
494
+ return false
495
+ }
496
+ cur = node
497
+ }
498
+ }
499
+
500
+
501
+ // The resolved absolute path of a reference whose segments are all
502
+ // plain strings; undefined when the ref has variable segments (no
503
+ // cycle proof is attempted for those). Mirrors find's refpath
504
+ // computation for the plain case, including the `.` prefix reduction.
505
+ plainRefPath(): string[] | undefined {
506
+ const parts: string[] = []
507
+ for (const p of this.peg) {
508
+ if ('string' !== typeof p) {
509
+ return undefined
510
+ }
511
+ parts.push(p)
512
+ }
513
+ const refpath = this.absolute ? parts :
514
+ this.path.slice(0, -1).concat(parts)
515
+ const reduced: string[] = []
516
+ for (const p of refpath) {
517
+ if ('.' === p) {
518
+ // A parent step off the top of the path proves nothing.
519
+ if (0 === reduced.length) {
520
+ return undefined
521
+ }
522
+ reduced.length = reduced.length - 1
523
+ }
524
+ else {
525
+ reduced.push(p)
526
+ }
527
+ }
528
+ return reduced
529
+ }
530
+
531
+
400
532
  same(peer: Val): boolean {
401
533
  return null == peer ? false : this.peg === peer.peg
402
534
  }
@@ -450,7 +582,7 @@ class RefVal extends FeatureVal {
450
582
  ].filter(p => '' != p).join(',')
451
583
  }
452
584
 
453
- }
585
+ } /* node:coverage ignore next 6 */
454
586
 
455
587
 
456
588
  export {
@@ -24,17 +24,101 @@ import { makeNilErr, AontuError } from '../err'
24
24
  import { FeatureVal } from './FeatureVal'
25
25
 
26
26
 
27
- // A ScalarKind for integers. Number includes floats.
27
+ // Kind markers.
28
+ //
29
+ // A ScalarKindVal's canon is its marker's constructor name, lowercased,
30
+ // so these class names are load-bearing language surface: `Integer` is
31
+ // the keyword `integer`, `Float` is the keyword `float`.
32
+ //
33
+ // The numeric part of the lattice is a pure supertype over disjoint
34
+ // leaves:
35
+ //
36
+ // number (the global Number constructor)
37
+ // |- integer Integer int64-window exact
38
+ // |- float Float IEEE-754 binary64
39
+ // |- biginteger BigInteger exact, opt-in via 0d
40
+ // |- bigdecimal BigDecimal exact, opt-in via 0d
41
+ //
42
+ // The global `Number` constructor is the marker for the SUPERTYPE only:
43
+ // no concrete ScalarVal ever carries it. A binary64 value is FLOAT kind
44
+ // (NumberVal is the binary64 leaf; the class name is historical).
45
+ //
46
+ // A ScalarKindVal for `number` matches a concrete value of any numeric
47
+ // leaf, and meeting it with a leaf yields that leaf. Two distinct leaves
48
+ // are disjoint sets with no common lower bound, so they do not unify.
49
+
50
+ // A ScalarKind for the int64-window exact integers.
28
51
  class Integer { }
29
52
 
53
+ // A ScalarKind for IEEE-754 binary64 values.
54
+ class Float { }
55
+
56
+ // A ScalarKind for the unbounded exact integers, reached only by a `0d`
57
+ // literal with no fraction and no exponent.
58
+ class BigInteger { }
59
+
60
+ // A ScalarKind for the exact base-10 decimals, reached only by a `0d`
61
+ // literal carrying a `.` or an exponent.
62
+ class BigDecimal { }
63
+
30
64
  // A ScalarKind for null.
31
65
  class Null { }
32
66
 
33
67
 
68
+ // The immediate lattice parent of each kind marker. A marker absent from
69
+ // this table sits directly under top (string, boolean, null -- and
70
+ // `number` itself, which is the root of the numeric family).
71
+ //
72
+ // Adding a leaf is one row.
73
+ const KIND_PARENT = new Map<any, any>([
74
+ [Integer, Number],
75
+ [Float, Number],
76
+ [BigInteger, Number],
77
+ [BigDecimal, Number],
78
+ ])
79
+
80
+
81
+ // The immediate lattice superior of a kind marker, or undefined when the
82
+ // marker's superior is top.
83
+ function kindParent(kind: any): any {
84
+ return KIND_PARENT.get(kind)
85
+ }
86
+
87
+
88
+ // The root of a kind marker's family: the highest ancestor below top.
89
+ // Every numeric leaf answers `number`; a kind with no parent is its own
90
+ // family root (string, boolean, null).
91
+ function kindFamily(kind: any): any {
92
+ let root = kind
93
+ for (let k = KIND_PARENT.get(kind); null != k; k = KIND_PARENT.get(k)) {
94
+ root = k
95
+ }
96
+ return root
97
+ }
98
+
99
+
100
+ // True when `sub` is `sup`, or sits anywhere below it. The numeric
101
+ // lattice is two deep today; walking the chain keeps this correct if it
102
+ // ever deepens.
103
+ function kindSubsumes(sup: any, sub: any): boolean {
104
+ for (let k = sub; null != k; k = KIND_PARENT.get(k)) {
105
+ if (k === sup) {
106
+ return true
107
+ }
108
+ }
109
+ return false
110
+ }
111
+
112
+
34
113
  type ScalarConstructor =
35
114
  StringConstructor |
36
115
  NumberConstructor |
37
116
  BooleanConstructor |
117
+ (typeof Integer) |
118
+ (typeof Float) |
119
+ (typeof BigInteger) |
120
+ (typeof BigDecimal) |
121
+ (typeof Null) |
38
122
  (typeof Integer.constructor)
39
123
 
40
124
 
@@ -64,13 +148,19 @@ class ScalarKindVal extends FeatureVal {
64
148
 
65
149
  let out: Val = this
66
150
 
67
- if (peerIsScalarVal) {
151
+ if ((peer as any).isConstraint) {
152
+ // The constraint algebra owns the kind-meets-constraint rules
153
+ // (narrowing, domain checks) -- delegate, so disjunct trials and
154
+ // direct drives agree with the sorted conjunct fold.
155
+ out = (peer as any).unify(this, ctx)
156
+ }
157
+ else if (peerIsScalarVal) {
68
158
  let peerKind = (peer as any).kind
69
159
 
70
- if (peerKind === this.peg) {
71
- out = peer
72
- }
73
- else if (Number === this.peg && Integer === peerKind) {
160
+ // A kind admits a concrete value of that kind, or of any kind
161
+ // below it: `float & 1.5` is 1.5, and `number & 1` is 1 because
162
+ // integer sits under number.
163
+ if (kindSubsumes(this.peg, peerKind)) {
74
164
  out = peer
75
165
  }
76
166
  else {
@@ -78,13 +168,16 @@ class ScalarKindVal extends FeatureVal {
78
168
  }
79
169
  }
80
170
  else if (peerIsScalarKind) {
81
- if (Number === this.peg && Integer === peer.peg) {
82
- out = peer
83
- }
84
- else if (Number === peer.peg && Integer === this.peg) {
171
+ // The meet of two kinds is the narrower one when they are
172
+ // comparable. Distinct leaves (float & integer) are disjoint sets
173
+ // of values, so they have no common lower bound and fail.
174
+ if (this.peg === peer.peg) {
85
175
  out = this
86
176
  }
87
- else if (this.peg === peer.peg) {
177
+ else if (kindSubsumes(this.peg, peer.peg)) {
178
+ out = peer
179
+ }
180
+ else if (kindSubsumes(peer.peg, this.peg)) {
88
181
  out = this
89
182
  }
90
183
  else {
@@ -108,18 +201,36 @@ class ScalarKindVal extends FeatureVal {
108
201
  }
109
202
 
110
203
 
204
+ // The super() ladder: a leaf kind lifts to its parent kind
205
+ // (super(integer) -> number, super(float) -> number), and a kind with
206
+ // no parent lifts to top (super(number) -> top). A concrete value
207
+ // lifts to its own leaf via ScalarVal.superior.
208
+ superior(): Val {
209
+ const parent = kindParent(this.peg)
210
+ return null == parent ?
211
+ super.superior() :
212
+ this.place(new ScalarKindVal({ peg: parent }))
213
+ }
214
+
215
+
111
216
  same(peer: any): boolean {
112
217
  let out = peer?.isScalarKind ? this.peg === peer?.peg : super.same(peer)
113
218
  return out
114
219
  }
115
220
 
116
221
 
117
- }
222
+ } /* node:coverage ignore next 15 */
118
223
 
119
224
 
120
225
  export {
226
+ BigDecimal,
227
+ BigInteger,
228
+ Float,
121
229
  Integer,
122
230
  Null,
123
231
  ScalarConstructor,
124
232
  ScalarKindVal,
233
+ kindFamily,
234
+ kindParent,
235
+ kindSubsumes,
125
236
  }
@@ -60,7 +60,7 @@ class ScalarVal extends Val {
60
60
  let out: Val
61
61
 
62
62
  // Exactly equal scalars are handled in unify.unite
63
- if (peer.isScalarKind) {
63
+ if (peer.isScalarKind || (peer as any).isConstraint) {
64
64
  out = peer.unify(this, ctx)
65
65
  }
66
66
  else if (peer.isTop) {
@@ -87,7 +87,24 @@ class ScalarVal extends Val {
87
87
 
88
88
 
89
89
  same(peer: any): boolean {
90
- return peer?.isScalar ? peer.peg === this.peg : super.same(peer)
90
+ // Two concrete scalars are the same only when KIND and value both
91
+ // match. Comparing peg alone is a leftover from before integer and
92
+ // number were distinct kinds; without the kind test `1|1.0` would
93
+ // collapse to a single alternative and `(1|1.0) & 1.0` could pick
94
+ // the integer.
95
+ return peer?.isScalar ?
96
+ (peer.kind === this.kind && this.samePeg(peer.peg)) :
97
+ super.same(peer)
98
+ }
99
+
100
+
101
+ // Value comparison for this leaf's peg (D2: identity is kind AND
102
+ // value, NEVER the identity of the object holding the value). `===` is
103
+ // right for every peg that is a primitive -- including a bigint, where
104
+ // two separately built copies of the same number compare equal -- and
105
+ // wrong for a peg that is an object, so BigDecimalVal overrides it.
106
+ samePeg(peg: any): boolean {
107
+ return peg === this.peg
91
108
  }
92
109
 
93
110
 
@@ -104,7 +121,7 @@ class ScalarVal extends Val {
104
121
  }))
105
122
  }
106
123
 
107
- }
124
+ } /* node:coverage ignore next 6 */
108
125
 
109
126
 
110
127
  export {
@@ -30,7 +30,7 @@ class StringVal extends ScalarVal {
30
30
  return JSON.stringify(this.peg)
31
31
  }
32
32
 
33
- }
33
+ } /* node:coverage ignore next 5 */
34
34
 
35
35
  export {
36
36
  StringVal,
@@ -34,11 +34,24 @@ class SuperFuncVal extends FuncBaseVal {
34
34
  }
35
35
 
36
36
 
37
- resolve(_ctx: AontuContext, _args: Val[]) {
37
+ resolve(_ctx: AontuContext, args: Val[]) {
38
+ // super(x) is the lattice-superior of its ARGUMENT, not of the
39
+ // super() call itself: super(1) -> integer, super(1.5) -> number,
40
+ // super(a) -> string, super(true) -> boolean. Returning the
41
+ // function's own superior (top) is what made super() inert.
42
+ const arg: any = args?.[0]
43
+ if (arg?.isVal) {
44
+ const sup = arg.superior()
45
+ // Where the argument has no meaningful superior (superior()
46
+ // defaults to top), fall back to the previous behaviour.
47
+ if (null != sup && true !== sup.isTop) {
48
+ return this.place(sup)
49
+ }
50
+ }
38
51
  return this.place(this.superior())
39
52
  }
40
53
 
41
- }
54
+ } /* node:coverage ignore next 6 */
42
55
 
43
56
 
44
57
  export {
package/src/val/TopVal.ts CHANGED
@@ -61,7 +61,7 @@ class TopVal extends Val {
61
61
  return undefined
62
62
  }
63
63
 
64
- }
64
+ } /* node:coverage ignore next 6 */
65
65
 
66
66
 
67
67
  export {
@@ -61,7 +61,7 @@ class TypeFuncVal extends FuncBaseVal {
61
61
 
62
62
  return out
63
63
  }
64
- }
64
+ } /* node:coverage ignore next 6 */
65
65
 
66
66
 
67
67
  export {