aontu 0.46.0 → 0.48.1
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/dist/aontu.js +5 -0
- package/dist/aontu.js.map +1 -1
- package/dist/err.js +1 -1
- package/dist/err.js.map +1 -1
- package/dist/hints.js +1 -0
- package/dist/hints.js.map +1 -1
- package/dist/lang.d.ts +1 -1
- package/dist/lang.js +45 -18
- package/dist/lang.js.map +1 -1
- package/dist/lsp-server.d.ts +14 -0
- package/dist/lsp-server.js +87 -0
- package/dist/lsp-server.js.map +1 -0
- package/dist/lsp.d.ts +71 -0
- package/dist/lsp.js +375 -0
- package/dist/lsp.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/type.d.ts +1 -1
- package/dist/unify.js +31 -4
- package/dist/unify.js.map +1 -1
- package/dist/utility.js +9 -2
- package/dist/utility.js.map +1 -1
- package/dist/val/BagVal.js +20 -2
- package/dist/val/BagVal.js.map +1 -1
- package/dist/val/DisjunctVal.js +25 -16
- package/dist/val/DisjunctVal.js.map +1 -1
- package/dist/val/IntegerVal.js +1 -0
- package/dist/val/IntegerVal.js.map +1 -1
- package/dist/val/LowerFuncVal.d.ts +1 -1
- package/dist/val/LowerFuncVal.js +1 -1
- package/dist/val/LowerFuncVal.js.map +1 -1
- package/dist/val/MapVal.js +127 -16
- package/dist/val/MapVal.js.map +1 -1
- package/dist/val/NumberVal.js +7 -2
- package/dist/val/NumberVal.js.map +1 -1
- package/dist/val/OpBaseVal.d.ts +1 -1
- package/dist/val/OpBaseVal.js +1 -1
- package/dist/val/OpBaseVal.js.map +1 -1
- package/dist/val/ScalarVal.js +3 -1
- package/dist/val/ScalarVal.js.map +1 -1
- package/dist/val/Val.d.ts +2 -0
- package/dist/val/Val.js +16 -1
- package/dist/val/Val.js.map +1 -1
- package/dist/val/VarVal.js +5 -3
- package/dist/val/VarVal.js.map +1 -1
- package/package.json +22 -14
- package/src/aontu.ts +5 -0
- package/src/err.ts +1 -1
- package/src/hints.ts +2 -0
- package/src/lang.ts +46 -18
- package/src/lsp-server.ts +109 -0
- package/src/lsp.ts +486 -0
- package/src/type.ts +1 -1
- package/src/unify.ts +33 -4
- package/src/utility.ts +11 -2
- package/src/val/BagVal.ts +22 -2
- package/src/val/DisjunctVal.ts +27 -18
- package/src/val/IntegerVal.ts +1 -0
- package/src/val/LowerFuncVal.ts +1 -1
- package/src/val/MapVal.ts +138 -23
- package/src/val/NumberVal.ts +7 -2
- package/src/val/OpBaseVal.ts +1 -1
- package/src/val/ScalarVal.ts +3 -1
- package/src/val/Val.ts +18 -1
- package/src/val/VarVal.ts +6 -4
package/src/val/LowerFuncVal.ts
CHANGED
package/src/val/MapVal.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { unite } from '../unify'
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
propagateMarks,
|
|
19
|
+
walk,
|
|
19
20
|
explainOpen,
|
|
20
21
|
ec,
|
|
21
22
|
explainClose,
|
|
@@ -33,6 +34,54 @@ import { NilVal } from './NilVal'
|
|
|
33
34
|
import { BagVal } from './BagVal'
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
// Structural snapshots of ref spreads (see MapVal.unify), keyed by the
|
|
38
|
+
// ref's canon + source site rather than object identity: spread
|
|
39
|
+
// application clones templates (and the refs inside them) freely, and a
|
|
40
|
+
// clone must find the snapshot its parse-origin ref captured on an early
|
|
41
|
+
// pass. The map lives on the unify root ctx (see Unify), so it persists
|
|
42
|
+
// across fixpoint passes and is GC'd with the run.
|
|
43
|
+
function spreadSnapKey(cj: any): string {
|
|
44
|
+
return cj.canon + '~' + (cj.site?.row ?? -1) + ':' + (cj.site?.col ?? -1)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Snapshot a path-dependent ref spread to its structural target once,
|
|
48
|
+
// while inner key()/path() funcs in the target are still unresolved (see
|
|
49
|
+
// the call site comments in MapVal.unify). Shared by the direct
|
|
50
|
+
// application path and the deferred-spread early-snapshot walk.
|
|
51
|
+
function snapshotRefSpread(cj: any, ctx: AontuContext): Val | undefined {
|
|
52
|
+
let snapmap: Map<string, Val> | undefined = (ctx as any).snapmap
|
|
53
|
+
if (undefined === snapmap) {
|
|
54
|
+
// Direct Val.unify use without a Unify run: degrade to a ctx-local
|
|
55
|
+
// map (snapshots then live only for that subtree, as before).
|
|
56
|
+
snapmap = new Map()
|
|
57
|
+
; (ctx as any).snapmap = snapmap
|
|
58
|
+
}
|
|
59
|
+
const sk = spreadSnapKey(cj)
|
|
60
|
+
let snap: Val | undefined = snapmap.get(sk)
|
|
61
|
+
if (undefined === snap) {
|
|
62
|
+
let tgt: Val | undefined = cj.find(ctx)
|
|
63
|
+
// A ref to a type() resolves to its inner template — snapshot that,
|
|
64
|
+
// so a type-wrapped ref behaves like a plain-map ref spread.
|
|
65
|
+
if (tgt && (tgt as any).isTypeFunc) tgt = (tgt as any).peg?.[0]
|
|
66
|
+
// Only snapshot a found, path-dependent target. If the target is not
|
|
67
|
+
// present yet (it may be introduced by a later conjunct/merge), do
|
|
68
|
+
// NOT cache — retry on the next fixpoint pass.
|
|
69
|
+
if (tgt && tgt.isVal && tgt.isPathDependent) {
|
|
70
|
+
snap = tgt.clone(ctx)
|
|
71
|
+
// Clear TYPE marks on the snapshot (recursively): a type() template
|
|
72
|
+
// constrains values but must not make the spread destination
|
|
73
|
+
// type-invisible at any depth. HIDE marks are preserved.
|
|
74
|
+
walk(snap, (_k: any, v: Val) => {
|
|
75
|
+
v.mark.type = false
|
|
76
|
+
return v
|
|
77
|
+
})
|
|
78
|
+
snapmap.set(sk, snap)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return snap
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
|
|
36
85
|
class MapVal extends BagVal {
|
|
37
86
|
isMap = true
|
|
38
87
|
|
|
@@ -112,12 +161,22 @@ class MapVal extends BagVal {
|
|
|
112
161
|
}
|
|
113
162
|
|
|
114
163
|
if (!exit) {
|
|
164
|
+
// Combine two spread constraints. Identical templates (same canon)
|
|
165
|
+
// collapse to one: re-unifying them resolves key()/path() at the
|
|
166
|
+
// shared intermediate path, producing spurious values (f1bb1063).
|
|
167
|
+
// Distinct templates are unified in place — unite is idempotent,
|
|
168
|
+
// whereas deferring the distinct case into a fresh ConjunctVal (as
|
|
169
|
+
// f1bb1063 did) re-wraps every fixpoint pass, growing the conjunct
|
|
170
|
+
// without bound and non-terminating on real models (the apidef +
|
|
171
|
+
// sdkgen entity schemas each contribute a `&:` spread with name:key(),
|
|
172
|
+
// combined here). unite resolves key()/path() at each destination via
|
|
173
|
+
// spreadClone below, so nested + sibling key() cases stay correct
|
|
174
|
+
// (test/spec/spread-nested-key, spread-key-all).
|
|
115
175
|
out.spread.cj = null == out.spread.cj ? peer.spread.cj : (
|
|
116
|
-
null == peer.spread.cj ? out.spread.cj :
|
|
117
|
-
out.spread.cj
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
)
|
|
176
|
+
null == peer.spread.cj ? out.spread.cj :
|
|
177
|
+
out.spread.cj.canon === peer.spread.cj.canon ? out.spread.cj :
|
|
178
|
+
unite(te ? ctx.clone({ explain: ec(te, 'SPR') }) : ctx,
|
|
179
|
+
out.spread.cj, peer.spread.cj, 'map-self')
|
|
121
180
|
)
|
|
122
181
|
}
|
|
123
182
|
}
|
|
@@ -133,25 +192,69 @@ class MapVal extends BagVal {
|
|
|
133
192
|
|
|
134
193
|
let spread_cj = out.spread.cj ?? TOP
|
|
135
194
|
|
|
195
|
+
// Snapshot a path-dependent *ref* spread to its structural target
|
|
196
|
+
// once (while inner key()/path() funcs are still unresolved), so
|
|
197
|
+
// later fixpoint passes don't re-resolve the ref against the mutated
|
|
198
|
+
// tree and capture the target's own resolved key()/path() literals,
|
|
199
|
+
// which would leak the source key into the spread destination.
|
|
200
|
+
if (spread_cj.isRef && (spread_cj as any).find) {
|
|
201
|
+
const snap = snapshotRefSpread(spread_cj, ctx)
|
|
202
|
+
if (snap) spread_cj = snap
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// A type() used as a spread applies as its inner template: emit the
|
|
206
|
+
// (constrained) values at each destination rather than marking the
|
|
207
|
+
// destination as a type. I.e. `&:type({k:key(),x:number})` behaves
|
|
208
|
+
// like the non-type spread `&:{k:key(),x:number}` — key() resolves
|
|
209
|
+
// to the destination key, kinds constrain, fields are emitted.
|
|
210
|
+
if ((spread_cj as any).isTypeFunc) {
|
|
211
|
+
spread_cj = (spread_cj as any).peg?.[0] ?? TOP
|
|
212
|
+
}
|
|
213
|
+
|
|
136
214
|
// Always unify own children first
|
|
137
215
|
for (let key in this.peg) {
|
|
138
|
-
const keyctx = ctx.descend(key)
|
|
139
|
-
|
|
140
|
-
const key_spread_cj = spread_cj.spreadClone(keyctx)
|
|
141
216
|
const child = this.peg[key]
|
|
217
|
+
const keyctx = ctx.descend(key)
|
|
142
218
|
|
|
143
219
|
propagateMarks(this, child)
|
|
144
220
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
221
|
+
// Apply the spread constraint ONCE per child (marked with the
|
|
222
|
+
// constraint's id below): the first application merges the
|
|
223
|
+
// template into the child (with key()/path() placeholders that
|
|
224
|
+
// resolve in place on later passes), so the constraint content
|
|
225
|
+
// is inside the child from then on and only self-unification is
|
|
226
|
+
// needed to progress it. Re-applying on every fixpoint pass and
|
|
227
|
+
// every conjunct-fold step is the identity (unite is idempotent)
|
|
228
|
+
// but costs O(keys) deep template clones per pass on large
|
|
229
|
+
// models — the dominant cost on generated-SDK model trees.
|
|
230
|
+
let oval: Val
|
|
231
|
+
if (undefined !== child && !spread_cj.isTop
|
|
232
|
+
&& (child as any)._spr === (spread_cj as any).id) {
|
|
233
|
+
oval = child.done ? child :
|
|
234
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
|
|
235
|
+
child, TOP, 'map-own')
|
|
236
|
+
; (oval as any)._spr = (spread_cj as any).id
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
const key_spread_cj = spread_cj.spreadClone(keyctx)
|
|
240
|
+
|
|
241
|
+
oval =
|
|
242
|
+
undefined === child ? key_spread_cj :
|
|
243
|
+
child.isNil ? child :
|
|
244
|
+
key_spread_cj.isNil ? key_spread_cj :
|
|
245
|
+
key_spread_cj.isTop && child.done ? child :
|
|
246
|
+
child.isTop && key_spread_cj.done ? key_spread_cj :
|
|
247
|
+
unite(te ? keyctx.clone({ explain: ec(te, 'KEY:' + key) }) : keyctx,
|
|
248
|
+
child, key_spread_cj, 'map-own')
|
|
249
|
+
|
|
250
|
+
if (!spread_cj.isTop && !oval.isNil) {
|
|
251
|
+
; (oval as any)._spr = (spread_cj as any).id
|
|
252
|
+
}
|
|
253
|
+
}
|
|
153
254
|
|
|
154
|
-
|
|
255
|
+
out.peg[key] = oval
|
|
256
|
+
|
|
257
|
+
done = (done && DONE === oval.dc)
|
|
155
258
|
}
|
|
156
259
|
|
|
157
260
|
const allowedKeys: string[] = this.closed ? Object.keys(this.peg) : []
|
|
@@ -187,11 +290,20 @@ class MapVal extends BagVal {
|
|
|
187
290
|
child, peerchild, 'map-peer')
|
|
188
291
|
|
|
189
292
|
if (this.spread.cj) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
293
|
+
// Same apply-once discipline as the own-key loop: once the
|
|
294
|
+
// constraint is merged into the value (marked with the
|
|
295
|
+
// constraint's id), later passes only self-unify.
|
|
296
|
+
if ((oval as any)._spr !== (spread_cj as any).id) {
|
|
297
|
+
let key_spread_cj = spread_cj.spreadClone(peerctx)
|
|
298
|
+
|
|
299
|
+
oval = out.peg[peerkey] =
|
|
300
|
+
unite(te ? peerctx.clone({ explain: ec(te, 'PSP:' + peerkey) }) : peerctx,
|
|
301
|
+
oval, key_spread_cj, 'map-peer-spread')
|
|
302
|
+
|
|
303
|
+
if (!spread_cj.isTop && !oval.isNil) {
|
|
304
|
+
; (oval as any)._spr = (spread_cj as any).id
|
|
305
|
+
}
|
|
306
|
+
}
|
|
195
307
|
}
|
|
196
308
|
|
|
197
309
|
propagateMarks(this, oval)
|
|
@@ -307,7 +419,10 @@ class MapVal extends BagVal {
|
|
|
307
419
|
|
|
308
420
|
|
|
309
421
|
get canon() {
|
|
310
|
-
|
|
422
|
+
// Keys are emitted alphabetically so the canonical form is
|
|
423
|
+
// independent of insertion/unification order (and matches the Go
|
|
424
|
+
// port, whose JSON marshaling also sorts keys).
|
|
425
|
+
let keys = Object.keys(this.peg).sort()
|
|
311
426
|
return '' +
|
|
312
427
|
// this.errcanon() +
|
|
313
428
|
// (this.mark.type ? '<type>' : '') +
|
package/src/val/NumberVal.ts
CHANGED
|
@@ -27,7 +27,9 @@ class NumberVal extends ScalarVal {
|
|
|
27
27
|
spec: ValSpec,
|
|
28
28
|
ctx?: AontuContext
|
|
29
29
|
) {
|
|
30
|
-
|
|
30
|
+
// Number.isFinite (not the coercing global isNaN, which lets null/''
|
|
31
|
+
// through as "numbers") — only an actual finite number is valid.
|
|
32
|
+
if (!Number.isFinite(spec.peg)) {
|
|
31
33
|
// TODO: use Nil?
|
|
32
34
|
throw new AontuError('not-number: ' + spec.peg)
|
|
33
35
|
}
|
|
@@ -47,9 +49,12 @@ class NumberVal extends ScalarVal {
|
|
|
47
49
|
}
|
|
48
50
|
else if (
|
|
49
51
|
peer.isScalar &&
|
|
52
|
+
peer.kind === this.kind &&
|
|
50
53
|
peer.peg === this.peg
|
|
51
54
|
) {
|
|
52
|
-
|
|
55
|
+
// Same kind (both Number) and equal value: integer/float no
|
|
56
|
+
// longer cross-unify, so peer is necessarily a NumberVal here.
|
|
57
|
+
out = this
|
|
53
58
|
}
|
|
54
59
|
else if (peer.isTop) {
|
|
55
60
|
out = this
|
package/src/val/OpBaseVal.ts
CHANGED
package/src/val/ScalarVal.ts
CHANGED
|
@@ -92,7 +92,9 @@ class ScalarVal extends Val {
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
gen(_ctx?: AontuContext) {
|
|
95
|
-
|
|
95
|
+
// Normalize negative zero to 0 for deterministic output (JSON has
|
|
96
|
+
// no -0, and the Go port produces 0).
|
|
97
|
+
return Object.is(this.peg, -0) ? 0 : this.peg
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
|
package/src/val/Val.ts
CHANGED
|
@@ -54,6 +54,12 @@ const SPREAD = Symbol('spread')
|
|
|
54
54
|
const EMPTY_ERR: any[] = Object.freeze([]) as unknown as any[]
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
// Process-global, monotonic Val id source. Correctness only requires ids
|
|
58
|
+
// to be unique within a single unify run (fast-path identity checks,
|
|
59
|
+
// `same()`), which holds. It is NOT reset between generate() calls, so in
|
|
60
|
+
// a long-running host (e.g. the LSP) it grows for the process lifetime;
|
|
61
|
+
// that is acceptable — an id is a small number and is never used as a
|
|
62
|
+
// memory key. TODO: switch to the per-run ctx.vc counter (see ctx.ts).
|
|
57
63
|
let ID = 1000
|
|
58
64
|
|
|
59
65
|
|
|
@@ -65,6 +71,7 @@ abstract class Val {
|
|
|
65
71
|
|
|
66
72
|
declare isTop: boolean
|
|
67
73
|
declare isNil: boolean
|
|
74
|
+
declare isNull: boolean
|
|
68
75
|
declare isMap: boolean
|
|
69
76
|
declare isList: boolean
|
|
70
77
|
declare isScalar: boolean
|
|
@@ -79,6 +86,7 @@ abstract class Val {
|
|
|
79
86
|
declare isBoolean: boolean
|
|
80
87
|
declare isConjunct: boolean
|
|
81
88
|
declare isDisjunct: boolean
|
|
89
|
+
declare isExpect: boolean
|
|
82
90
|
declare isJunction: boolean
|
|
83
91
|
|
|
84
92
|
// Conjunct sort order. Lower values sort first in norm().
|
|
@@ -272,7 +280,14 @@ abstract class Val {
|
|
|
272
280
|
return v
|
|
273
281
|
}
|
|
274
282
|
|
|
275
|
-
//
|
|
283
|
+
// CONTRACT: implementations should treat `this` and `peer` as
|
|
284
|
+
// immutable and return a new Val. KNOWN EXCEPTION: the MapVal/ListVal
|
|
285
|
+
// fast-path for a TOP peer returns and refines `this` in place (an
|
|
286
|
+
// intentional optimization for the fixpoint loop). The practical
|
|
287
|
+
// consequence is that a parsed/unified tree is SINGLE-USE — do not
|
|
288
|
+
// re-unify or re-generate the same Val, and do not share it across
|
|
289
|
+
// threads. The public Aontu.unify/generate entry points re-parse per
|
|
290
|
+
// call, so this only matters if you hold and reuse a Val yourself.
|
|
276
291
|
unify(_peer: Val, _ctx: AontuContext): Val { return this }
|
|
277
292
|
|
|
278
293
|
// TODO: indicate marks in some way that is ignored by reparse.
|
|
@@ -363,6 +378,7 @@ Object.assign(Val.prototype, {
|
|
|
363
378
|
|
|
364
379
|
isTop: false,
|
|
365
380
|
isNil: false,
|
|
381
|
+
isNull: false,
|
|
366
382
|
isMap: false,
|
|
367
383
|
isList: false,
|
|
368
384
|
isScalar: false,
|
|
@@ -377,6 +393,7 @@ Object.assign(Val.prototype, {
|
|
|
377
393
|
isBoolean: false,
|
|
378
394
|
isConjunct: false,
|
|
379
395
|
isDisjunct: false,
|
|
396
|
+
isExpect: false,
|
|
380
397
|
isJunction: false,
|
|
381
398
|
|
|
382
399
|
cjo: 99999,
|
package/src/val/VarVal.ts
CHANGED
|
@@ -79,13 +79,15 @@ class VarVal extends FeatureVal {
|
|
|
79
79
|
if (!(nameVal.isRef) && DONE === nameVal.dc) {
|
|
80
80
|
if (nameVal instanceof StringVal) {
|
|
81
81
|
let found = ctx.vars[nameVal.peg]
|
|
82
|
-
if (undefined === found) {
|
|
83
|
-
out = makeNilErr(ctx, 'unknown_var', this, peer)
|
|
84
|
-
}
|
|
85
82
|
|
|
86
83
|
// TODO: support complex values
|
|
87
84
|
const ft = typeof found
|
|
88
|
-
|
|
85
|
+
// Single ladder: a missing var must report `unknown_var` and not
|
|
86
|
+
// fall through to the `invalid_var_kind` default below.
|
|
87
|
+
if (undefined === found) {
|
|
88
|
+
out = makeNilErr(ctx, 'unknown_var', this, peer)
|
|
89
|
+
}
|
|
90
|
+
else if (null === found) {
|
|
89
91
|
out = this.place(new NullVal({ peg: null }))
|
|
90
92
|
}
|
|
91
93
|
else if ('string' === ft) {
|