@stevenvo780/st-lang 4.12.0 → 4.14.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 (97) hide show
  1. package/dist/reasoning/datalog/index.d.ts +131 -0
  2. package/dist/reasoning/datalog/index.d.ts.map +1 -0
  3. package/dist/reasoning/datalog/index.js +706 -0
  4. package/dist/reasoning/datalog/index.js.map +1 -0
  5. package/dist/reasoning/galois-fields/index.d.ts +29 -0
  6. package/dist/reasoning/galois-fields/index.d.ts.map +1 -0
  7. package/dist/reasoning/galois-fields/index.js +522 -0
  8. package/dist/reasoning/galois-fields/index.js.map +1 -0
  9. package/dist/reasoning/hoare-logic/index.d.ts +130 -0
  10. package/dist/reasoning/hoare-logic/index.d.ts.map +1 -0
  11. package/dist/reasoning/hoare-logic/index.js +535 -0
  12. package/dist/reasoning/hoare-logic/index.js.map +1 -0
  13. package/dist/reasoning/lattice/index.d.ts +165 -0
  14. package/dist/reasoning/lattice/index.d.ts.map +1 -0
  15. package/dist/reasoning/lattice/index.js +587 -0
  16. package/dist/reasoning/lattice/index.js.map +1 -0
  17. package/dist/reasoning/model-checking/index.d.ts +113 -0
  18. package/dist/reasoning/model-checking/index.d.ts.map +1 -0
  19. package/dist/reasoning/model-checking/index.js +786 -0
  20. package/dist/reasoning/model-checking/index.js.map +1 -0
  21. package/dist/reasoning/polynomial-ring/index.d.ts +30 -0
  22. package/dist/reasoning/polynomial-ring/index.d.ts.map +1 -0
  23. package/dist/reasoning/polynomial-ring/index.js +797 -0
  24. package/dist/reasoning/polynomial-ring/index.js.map +1 -0
  25. package/dist/reasoning/separation-logic/index.d.ts +190 -0
  26. package/dist/reasoning/separation-logic/index.d.ts.map +1 -0
  27. package/dist/reasoning/separation-logic/index.js +758 -0
  28. package/dist/reasoning/separation-logic/index.js.map +1 -0
  29. package/dist/reasoning/universal-algebra/index.d.ts +196 -0
  30. package/dist/reasoning/universal-algebra/index.d.ts.map +1 -0
  31. package/dist/reasoning/universal-algebra/index.js +865 -0
  32. package/dist/reasoning/universal-algebra/index.js.map +1 -0
  33. package/dist/tests/reasoning/datalog/datalog.test.d.ts +2 -0
  34. package/dist/tests/reasoning/datalog/datalog.test.d.ts.map +1 -0
  35. package/dist/tests/reasoning/datalog/datalog.test.js +333 -0
  36. package/dist/tests/reasoning/datalog/datalog.test.js.map +1 -0
  37. package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts +2 -0
  38. package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts.map +1 -0
  39. package/dist/tests/reasoning/galois-fields/galois-fields.test.js +226 -0
  40. package/dist/tests/reasoning/galois-fields/galois-fields.test.js.map +1 -0
  41. package/dist/tests/reasoning/hoare-logic/hoare-logic.test.d.ts +2 -0
  42. package/dist/tests/reasoning/hoare-logic/hoare-logic.test.d.ts.map +1 -0
  43. package/dist/tests/reasoning/hoare-logic/hoare-logic.test.js +340 -0
  44. package/dist/tests/reasoning/hoare-logic/hoare-logic.test.js.map +1 -0
  45. package/dist/tests/reasoning/lattice/lattice.test.d.ts +2 -0
  46. package/dist/tests/reasoning/lattice/lattice.test.d.ts.map +1 -0
  47. package/dist/tests/reasoning/lattice/lattice.test.js +238 -0
  48. package/dist/tests/reasoning/lattice/lattice.test.js.map +1 -0
  49. package/dist/tests/reasoning/model-checking/model-checking.test.d.ts +2 -0
  50. package/dist/tests/reasoning/model-checking/model-checking.test.d.ts.map +1 -0
  51. package/dist/tests/reasoning/model-checking/model-checking.test.js +222 -0
  52. package/dist/tests/reasoning/model-checking/model-checking.test.js.map +1 -0
  53. package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts +2 -0
  54. package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts.map +1 -0
  55. package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js +230 -0
  56. package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js.map +1 -0
  57. package/dist/tests/reasoning/separation-logic/separation-logic.test.d.ts +2 -0
  58. package/dist/tests/reasoning/separation-logic/separation-logic.test.d.ts.map +1 -0
  59. package/dist/tests/reasoning/separation-logic/separation-logic.test.js +311 -0
  60. package/dist/tests/reasoning/separation-logic/separation-logic.test.js.map +1 -0
  61. package/dist/tests/reasoning/universal-algebra/universal-algebra.test.d.ts +2 -0
  62. package/dist/tests/reasoning/universal-algebra/universal-algebra.test.d.ts.map +1 -0
  63. package/dist/tests/reasoning/universal-algebra/universal-algebra.test.js +289 -0
  64. package/dist/tests/reasoning/universal-algebra/universal-algebra.test.js.map +1 -0
  65. package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts +2 -0
  66. package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts.map +1 -0
  67. package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js +266 -0
  68. package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js.map +1 -0
  69. package/dist/type-theory/lambda-cube/erase.d.ts +26 -0
  70. package/dist/type-theory/lambda-cube/erase.d.ts.map +1 -0
  71. package/dist/type-theory/lambda-cube/erase.js +68 -0
  72. package/dist/type-theory/lambda-cube/erase.js.map +1 -0
  73. package/dist/type-theory/lambda-cube/examples.d.ts +59 -0
  74. package/dist/type-theory/lambda-cube/examples.d.ts.map +1 -0
  75. package/dist/type-theory/lambda-cube/examples.js +110 -0
  76. package/dist/type-theory/lambda-cube/examples.js.map +1 -0
  77. package/dist/type-theory/lambda-cube/index.d.ts +11 -0
  78. package/dist/type-theory/lambda-cube/index.d.ts.map +1 -0
  79. package/dist/type-theory/lambda-cube/index.js +64 -0
  80. package/dist/type-theory/lambda-cube/index.js.map +1 -0
  81. package/dist/type-theory/lambda-cube/normalize.d.ts +17 -0
  82. package/dist/type-theory/lambda-cube/normalize.d.ts.map +1 -0
  83. package/dist/type-theory/lambda-cube/normalize.js +134 -0
  84. package/dist/type-theory/lambda-cube/normalize.js.map +1 -0
  85. package/dist/type-theory/lambda-cube/rules.d.ts +26 -0
  86. package/dist/type-theory/lambda-cube/rules.d.ts.map +1 -0
  87. package/dist/type-theory/lambda-cube/rules.js +67 -0
  88. package/dist/type-theory/lambda-cube/rules.js.map +1 -0
  89. package/dist/type-theory/lambda-cube/typecheck.d.ts +20 -0
  90. package/dist/type-theory/lambda-cube/typecheck.d.ts.map +1 -0
  91. package/dist/type-theory/lambda-cube/typecheck.js +168 -0
  92. package/dist/type-theory/lambda-cube/typecheck.js.map +1 -0
  93. package/dist/type-theory/lambda-cube/types.d.ts +40 -0
  94. package/dist/type-theory/lambda-cube/types.d.ts.map +1 -0
  95. package/dist/type-theory/lambda-cube/types.js +192 -0
  96. package/dist/type-theory/lambda-cube/types.js.map +1 -0
  97. package/package.json +1 -1
@@ -0,0 +1,758 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // Separation Logic — Reynolds-style heap reasoning
4
+ // ============================================================
5
+ //
6
+ // Lógica de separación clásica (Reynolds / O'Hearn / Yang) para razonar
7
+ // sobre estructuras dinámicas (listas, árboles) y mutación con aliasing
8
+ // controlado.
9
+ //
10
+ // Núcleo:
11
+ // - Heap finito (Map<number, SLValue>) con dominio explícito.
12
+ // - Fórmulas: `emp`, `x ↦ v`, `P * Q`, `P -* Q`, `∃x. P`, conectivos
13
+ // clásicos sobre el fragmento puro.
14
+ // - Semántica `satisfies(P, h, valuation)` siguiendo el split de heap.
15
+ // - Triplas de Hoare con axiomas locales (alloc, free, load, store).
16
+ // - Predicados inductivos: `ls(x, y)` (list-segment) y `tree(x)`.
17
+ //
18
+ // El módulo es puro TypeScript, sin dependencias del resto del repo.
19
+ // Las estructuras Heap son inmutables hacia afuera: `write`, `delete` y
20
+ // `combine` devuelven instancias nuevas.
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.Cmd = exports.forallF = exports.existsF = exports.notF = exports.impliesF = exports.orF = exports.andF = exports.pure = exports.magicWand = exports.star = exports.pointsTo = exports.emp = void 0;
23
+ exports.intVal = intVal;
24
+ exports.addrVal = addrVal;
25
+ exports.nullVal = nullVal;
26
+ exports.valueEquals = valueEquals;
27
+ exports.valueKey = valueKey;
28
+ exports.asLoc = asLoc;
29
+ exports.newHeap = newHeap;
30
+ exports.fromMap = fromMap;
31
+ exports.disjoint = disjoint;
32
+ exports.combine = combine;
33
+ exports.heapEquals = heapEquals;
34
+ exports.splits = splits;
35
+ exports.formulaToString = formulaToString;
36
+ exports.valueToString = valueToString;
37
+ exports.bind = bind;
38
+ exports.satisfies = satisfies;
39
+ exports.executeCommand = executeCommand;
40
+ exports.checkTriple = checkTriple;
41
+ exports.isListSegment = isListSegment;
42
+ exports.listSegment = listSegment;
43
+ exports.satisfiesShape = satisfiesShape;
44
+ exports.tree = tree;
45
+ exports.isTree = isTree;
46
+ exports.frame = frame;
47
+ function intVal(value) {
48
+ return { kind: 'int', value };
49
+ }
50
+ function addrVal(loc) {
51
+ return { kind: 'addr', loc };
52
+ }
53
+ function nullVal() {
54
+ return { kind: 'null' };
55
+ }
56
+ function valueEquals(a, b) {
57
+ if (a.kind !== b.kind)
58
+ return false;
59
+ if (a.kind === 'int' && b.kind === 'int')
60
+ return a.value === b.value;
61
+ if (a.kind === 'addr' && b.kind === 'addr')
62
+ return a.loc === b.loc;
63
+ return a.kind === 'null' && b.kind === 'null';
64
+ }
65
+ /** Convierte un SLValue a una clave string estable para Maps. */
66
+ function valueKey(v) {
67
+ if (v.kind === 'int')
68
+ return `i:${v.value}`;
69
+ if (v.kind === 'addr')
70
+ return `a:${v.loc}`;
71
+ return 'null';
72
+ }
73
+ /** Devuelve la dirección de un SLValue si es addr, o null en cualquier
74
+ * otro caso. La null-location no se direcciona. */
75
+ function asLoc(v) {
76
+ return v.kind === 'addr' ? v.loc : null;
77
+ }
78
+ class HeapImpl {
79
+ map;
80
+ constructor(map) {
81
+ this.map = map;
82
+ }
83
+ domain() {
84
+ return [...this.map.keys()].sort((a, b) => a - b);
85
+ }
86
+ has(loc) {
87
+ return this.map.has(loc);
88
+ }
89
+ read(loc) {
90
+ return this.map.get(loc);
91
+ }
92
+ write(loc, val) {
93
+ const next = new Map(this.map);
94
+ next.set(loc, val);
95
+ return new HeapImpl(next);
96
+ }
97
+ delete(loc) {
98
+ if (!this.map.has(loc))
99
+ return this;
100
+ const next = new Map(this.map);
101
+ next.delete(loc);
102
+ return new HeapImpl(next);
103
+ }
104
+ size() {
105
+ return this.map.size;
106
+ }
107
+ clone() {
108
+ return new HeapImpl(new Map(this.map));
109
+ }
110
+ }
111
+ function newHeap() {
112
+ return new HeapImpl(new Map());
113
+ }
114
+ function fromMap(entries) {
115
+ return new HeapImpl(new Map(entries));
116
+ }
117
+ /** Dos heaps son disjuntos sii sus dominios no se intersectan. */
118
+ function disjoint(h1, h2) {
119
+ if (h1.size() > h2.size())
120
+ return disjoint(h2, h1);
121
+ for (const loc of h1.domain()) {
122
+ if (h2.has(loc))
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ /** Unión disjunta `h1 ⊎ h2`. Devuelve null si los heaps comparten alguna
128
+ * dirección — la unión disjunta sólo está definida cuando son disjoint. */
129
+ function combine(h1, h2) {
130
+ if (!disjoint(h1, h2))
131
+ return null;
132
+ const merged = new Map(h1.map);
133
+ for (const [loc, v] of h2.map)
134
+ merged.set(loc, v);
135
+ return new HeapImpl(merged);
136
+ }
137
+ /** Igualdad estructural de heaps. */
138
+ function heapEquals(h1, h2) {
139
+ if (h1.size() !== h2.size())
140
+ return false;
141
+ for (const [loc, v] of h1.map) {
142
+ const other = h2.read(loc);
143
+ if (other === undefined)
144
+ return false;
145
+ if (!valueEquals(v, other))
146
+ return false;
147
+ }
148
+ return true;
149
+ }
150
+ // ── Sub-heaps (para semántica de `*` y `-*`) ────────────────
151
+ /** Enumera todas las particiones del heap en `(h1, h2)` con `h1 ⊎ h2 = h`. */
152
+ function splits(h) {
153
+ const dom = h.domain();
154
+ const n = dom.length;
155
+ const results = [];
156
+ // 2^n splits — sólo se invoca sobre heaps pequeños (testing/semántica).
157
+ const limit = 1 << n;
158
+ for (let mask = 0; mask < limit; mask++) {
159
+ const m1 = new Map();
160
+ const m2 = new Map();
161
+ for (let i = 0; i < n; i++) {
162
+ const loc = dom[i];
163
+ const v = h.read(loc);
164
+ if ((mask >> i) & 1)
165
+ m1.set(loc, v);
166
+ else
167
+ m2.set(loc, v);
168
+ }
169
+ results.push({ h1: new HeapImpl(m1), h2: new HeapImpl(m2) });
170
+ }
171
+ return results;
172
+ }
173
+ const emp = () => ({ kind: 'emp' });
174
+ exports.emp = emp;
175
+ const pointsTo = (loc, val) => ({
176
+ kind: 'pointsTo',
177
+ loc,
178
+ val,
179
+ });
180
+ exports.pointsTo = pointsTo;
181
+ const star = (left, right) => ({
182
+ kind: 'star',
183
+ left,
184
+ right,
185
+ });
186
+ exports.star = star;
187
+ const magicWand = (left, right) => ({
188
+ kind: 'magicWand',
189
+ left,
190
+ right,
191
+ });
192
+ exports.magicWand = magicWand;
193
+ const pure = (expression, predicate) => ({
194
+ kind: 'pure',
195
+ expression,
196
+ predicate,
197
+ });
198
+ exports.pure = pure;
199
+ const andF = (left, right) => ({
200
+ kind: 'and',
201
+ left,
202
+ right,
203
+ });
204
+ exports.andF = andF;
205
+ const orF = (left, right) => ({
206
+ kind: 'or',
207
+ left,
208
+ right,
209
+ });
210
+ exports.orF = orF;
211
+ const impliesF = (left, right) => ({
212
+ kind: 'implies',
213
+ left,
214
+ right,
215
+ });
216
+ exports.impliesF = impliesF;
217
+ const notF = (body) => ({ kind: 'not', body });
218
+ exports.notF = notF;
219
+ const existsF = (bind, body) => ({
220
+ kind: 'exists',
221
+ bind,
222
+ body,
223
+ });
224
+ exports.existsF = existsF;
225
+ const forallF = (bind, body) => ({
226
+ kind: 'forall',
227
+ bind,
228
+ body,
229
+ });
230
+ exports.forallF = forallF;
231
+ // ── Pretty printer ───────────────────────────────────────────
232
+ function formulaToString(f) {
233
+ switch (f.kind) {
234
+ case 'emp':
235
+ return 'emp';
236
+ case 'pointsTo':
237
+ return `${valueToString(f.loc)} ↦ ${valueToString(f.val)}`;
238
+ case 'star':
239
+ return `(${formulaToString(f.left)} * ${formulaToString(f.right)})`;
240
+ case 'magicWand':
241
+ return `(${formulaToString(f.left)} -* ${formulaToString(f.right)})`;
242
+ case 'pure':
243
+ return `[${f.expression}]`;
244
+ case 'and':
245
+ return `(${formulaToString(f.left)} ∧ ${formulaToString(f.right)})`;
246
+ case 'or':
247
+ return `(${formulaToString(f.left)} ∨ ${formulaToString(f.right)})`;
248
+ case 'implies':
249
+ return `(${formulaToString(f.left)} → ${formulaToString(f.right)})`;
250
+ case 'not':
251
+ return `¬${formulaToString(f.body)}`;
252
+ case 'exists':
253
+ return `∃${f.bind}. ${formulaToString(f.body)}`;
254
+ case 'forall':
255
+ return `∀${f.bind}. ${formulaToString(f.body)}`;
256
+ }
257
+ }
258
+ function valueToString(v) {
259
+ if (v.kind === 'int')
260
+ return `${v.value}`;
261
+ if (v.kind === 'addr')
262
+ return `&${v.loc}`;
263
+ return 'null';
264
+ }
265
+ /** Devuelve una copia del valuation con `name → v`. */
266
+ function bind(val, name, v) {
267
+ return { ...val, [name]: v };
268
+ }
269
+ // ── Semántica ────────────────────────────────────────────────
270
+ /** `satisfies(P, h, ν)` — el modelo `(h, ν)` satisface la fórmula P. */
271
+ function satisfies(formula, heap, val) {
272
+ switch (formula.kind) {
273
+ case 'emp':
274
+ return heap.size() === 0;
275
+ case 'pointsTo': {
276
+ const loc = asLoc(formula.loc);
277
+ if (loc === null)
278
+ return false;
279
+ if (heap.size() !== 1)
280
+ return false;
281
+ const stored = heap.read(loc);
282
+ if (stored === undefined)
283
+ return false;
284
+ return valueEquals(stored, formula.val);
285
+ }
286
+ case 'star': {
287
+ for (const { h1, h2 } of splits(heap)) {
288
+ if (satisfies(formula.left, h1, val) && satisfies(formula.right, h2, val)) {
289
+ return true;
290
+ }
291
+ }
292
+ return false;
293
+ }
294
+ case 'magicWand': {
295
+ // P -* Q : ∀ h'. (h ⊥ h' ∧ h' ⊨ P) → (h ⊎ h') ⊨ Q
296
+ // Acotado a sub-heaps relevantes vía un universo de direcciones.
297
+ const universe = collectAddresses(formula.left, heap, val);
298
+ for (const candidate of enumerateHeapsOver(universe, heap, formula.left, val)) {
299
+ if (!disjoint(heap, candidate))
300
+ continue;
301
+ if (!satisfies(formula.left, candidate, val))
302
+ continue;
303
+ const merged = combine(heap, candidate);
304
+ if (merged === null)
305
+ continue;
306
+ if (!satisfies(formula.right, merged, val))
307
+ return false;
308
+ }
309
+ return true;
310
+ }
311
+ case 'pure':
312
+ return formula.predicate(val);
313
+ case 'and':
314
+ return satisfies(formula.left, heap, val) && satisfies(formula.right, heap, val);
315
+ case 'or':
316
+ return satisfies(formula.left, heap, val) || satisfies(formula.right, heap, val);
317
+ case 'implies':
318
+ return !satisfies(formula.left, heap, val) || satisfies(formula.right, heap, val);
319
+ case 'not':
320
+ return !satisfies(formula.body, heap, val);
321
+ case 'exists': {
322
+ // Acotado al universo de valores presentes en el heap y la valuación
323
+ // más null. Para semántica computable necesitamos un dominio finito.
324
+ for (const v of finiteValueDomain(heap, val)) {
325
+ if (satisfies(formula.body, heap, bind(val, formula.bind, v)))
326
+ return true;
327
+ }
328
+ return false;
329
+ }
330
+ case 'forall': {
331
+ for (const v of finiteValueDomain(heap, val)) {
332
+ if (!satisfies(formula.body, heap, bind(val, formula.bind, v)))
333
+ return false;
334
+ }
335
+ return true;
336
+ }
337
+ }
338
+ }
339
+ /** Dominio finito de valores observables: direcciones del heap + valores
340
+ * en la valuación + null. Permite cuantificar de forma computable. */
341
+ function finiteValueDomain(heap, val) {
342
+ const seen = new Map();
343
+ const push = (v) => {
344
+ seen.set(valueKey(v), v);
345
+ };
346
+ push(nullVal());
347
+ for (const loc of heap.domain()) {
348
+ push(addrVal(loc));
349
+ const stored = heap.read(loc);
350
+ if (stored !== undefined)
351
+ push(stored);
352
+ }
353
+ for (const name of Object.keys(val)) {
354
+ const bound = val[name];
355
+ if (bound !== undefined)
356
+ push(bound);
357
+ }
358
+ return [...seen.values()];
359
+ }
360
+ /** Direcciones candidatas para extender el heap al evaluar `-*`. */
361
+ function collectAddresses(_left, heap, val) {
362
+ const seen = new Set();
363
+ for (const loc of heap.domain())
364
+ seen.add(loc);
365
+ for (const name of Object.keys(val)) {
366
+ const bound = val[name];
367
+ if (bound && bound.kind === 'addr')
368
+ seen.add(bound.loc);
369
+ }
370
+ // Sembramos algunas direcciones frescas para que `-*` pueda probar con
371
+ // un heap extensión no trivial sin explotar combinatoriamente.
372
+ const maxLoc = Math.max(0, ...seen);
373
+ for (let i = 1; i <= 2; i++)
374
+ seen.add(maxLoc + i);
375
+ return [...seen].sort((a, b) => a - b);
376
+ }
377
+ /** Enumera heaps pequeños sobre `universe` cuyas direcciones no estén ya
378
+ * en `existing`. Usa los valores observables como contenido. Cota dura
379
+ * para no estallar: máximo 3 ubicaciones nuevas. */
380
+ function* enumerateHeapsOver(universe, existing, leftFormula, val) {
381
+ const candidates = universe.filter((loc) => !existing.has(loc));
382
+ const maxExtra = Math.min(3, candidates.length);
383
+ const values = finiteValueDomain(existing, val);
384
+ // Recortar dominio: si la fórmula izquierda es `emp`, sólo importa el heap vacío.
385
+ if (leftFormula.kind === 'emp') {
386
+ yield newHeap();
387
+ return;
388
+ }
389
+ // 0 .. maxExtra direcciones, cada una con cualquier valor.
390
+ for (let size = 0; size <= maxExtra; size++) {
391
+ yield* enumerateSubset(candidates, 0, size, [], values);
392
+ }
393
+ }
394
+ function* enumerateSubset(pool, start, remaining, acc, values) {
395
+ if (remaining === 0) {
396
+ yield fromMap(acc);
397
+ return;
398
+ }
399
+ for (let i = start; i <= pool.length - remaining; i++) {
400
+ const loc = pool[i];
401
+ for (const v of values) {
402
+ acc.push([loc, v]);
403
+ yield* enumerateSubset(pool, i + 1, remaining - 1, acc, values);
404
+ acc.pop();
405
+ }
406
+ }
407
+ }
408
+ /** Ejecuta un único comando de forma small-step. */
409
+ function executeCommand(cmd, heap, val) {
410
+ switch (cmd.kind) {
411
+ case 'skip':
412
+ return { ok: true, heap, val, fault: false };
413
+ case 'assign': {
414
+ if (cmd.variable === undefined || cmd.value === undefined) {
415
+ return { ok: false, heap, val, fault: false, reason: 'assign sin variable/valor' };
416
+ }
417
+ return { ok: true, heap, val: bind(val, cmd.variable, cmd.value), fault: false };
418
+ }
419
+ case 'alloc': {
420
+ if (cmd.variable === undefined) {
421
+ return { ok: false, heap, val, fault: false, reason: 'alloc sin variable destino' };
422
+ }
423
+ const initial = cmd.value ?? intVal(0);
424
+ const freshLoc = freshAddress(heap);
425
+ const heap1 = heap.write(freshLoc, initial);
426
+ const val1 = bind(val, cmd.variable, addrVal(freshLoc));
427
+ return { ok: true, heap: heap1, val: val1, fault: false };
428
+ }
429
+ case 'free': {
430
+ const target = cmd.location === undefined ? undefined : val[cmd.location];
431
+ if (target === undefined) {
432
+ return { ok: false, heap, val, fault: false, reason: 'free sobre variable no ligada' };
433
+ }
434
+ const loc = asLoc(target);
435
+ if (loc === null || !heap.has(loc)) {
436
+ return { ok: false, heap, val, fault: true, reason: 'free: memory fault' };
437
+ }
438
+ return { ok: true, heap: heap.delete(loc), val, fault: false };
439
+ }
440
+ case 'load': {
441
+ if (cmd.variable === undefined || cmd.location === undefined) {
442
+ return { ok: false, heap, val, fault: false, reason: 'load sin variable/location' };
443
+ }
444
+ const target = val[cmd.location];
445
+ const loc = target === undefined ? null : asLoc(target);
446
+ if (loc === null || !heap.has(loc)) {
447
+ return { ok: false, heap, val, fault: true, reason: 'load: memory fault' };
448
+ }
449
+ const stored = heap.read(loc);
450
+ return { ok: true, heap, val: bind(val, cmd.variable, stored), fault: false };
451
+ }
452
+ case 'store': {
453
+ if (cmd.location === undefined || cmd.value === undefined) {
454
+ return { ok: false, heap, val, fault: false, reason: 'store sin location/valor' };
455
+ }
456
+ const target = val[cmd.location];
457
+ const loc = target === undefined ? null : asLoc(target);
458
+ if (loc === null || !heap.has(loc)) {
459
+ return { ok: false, heap, val, fault: true, reason: 'store: memory fault' };
460
+ }
461
+ return { ok: true, heap: heap.write(loc, cmd.value), val, fault: false };
462
+ }
463
+ }
464
+ }
465
+ /** Devuelve la primera dirección libre del heap (estrategia bumper). */
466
+ function freshAddress(heap) {
467
+ let loc = 1;
468
+ while (heap.has(loc))
469
+ loc++;
470
+ return loc;
471
+ }
472
+ /** Verifica una tripla `{P} c {Q}` por muestreo finito: enumera modelos
473
+ * (heap, val) que satisfagan P, los ejecuta y comprueba Q sobre el
474
+ * estado final. Devuelve `valid: false` con contraejemplo al primer
475
+ * fallo. No es completo — es una verificación de testing/random. */
476
+ function checkTriple(triple, options = {}) {
477
+ const samples = options.samples ?? 32;
478
+ const seed = options.seed ?? 0xc0ffee;
479
+ const rng = makeLcg(seed);
480
+ const pool = [];
481
+ if (options.candidates)
482
+ pool.push(...options.candidates);
483
+ for (let i = 0; i < samples; i++)
484
+ pool.push(randomModel(rng));
485
+ let checked = 0;
486
+ for (const { heap, val } of pool) {
487
+ if (!satisfies(triple.pre, heap, val))
488
+ continue;
489
+ checked++;
490
+ const result = executeCommand(triple.cmd, heap, val);
491
+ if (!result.ok) {
492
+ return {
493
+ valid: false,
494
+ counterexample: {
495
+ heap,
496
+ val,
497
+ reason: `comando inválido: ${result.reason ?? 'unknown'}`,
498
+ },
499
+ modelsChecked: checked,
500
+ };
501
+ }
502
+ if (result.fault) {
503
+ return {
504
+ valid: false,
505
+ counterexample: { heap, val, reason: 'memory fault al ejecutar' },
506
+ modelsChecked: checked,
507
+ };
508
+ }
509
+ if (!satisfies(triple.post, result.heap, result.val)) {
510
+ return {
511
+ valid: false,
512
+ counterexample: {
513
+ heap,
514
+ val,
515
+ reason: 'postcondición no satisfecha tras ejecución',
516
+ },
517
+ modelsChecked: checked,
518
+ };
519
+ }
520
+ }
521
+ return { valid: true, modelsChecked: checked };
522
+ }
523
+ function makeLcg(seed) {
524
+ let state = seed >>> 0;
525
+ return () => {
526
+ state = (state * 1664525 + 1013904223) >>> 0;
527
+ return state / 0x100000000;
528
+ };
529
+ }
530
+ function randomModel(rng) {
531
+ const size = Math.floor(rng() * 4); // 0..3 celdas
532
+ const entries = [];
533
+ const used = new Set();
534
+ for (let i = 0; i < size; i++) {
535
+ let loc = 1 + Math.floor(rng() * 5);
536
+ while (used.has(loc))
537
+ loc++;
538
+ used.add(loc);
539
+ entries.push([loc, randomValue(rng)]);
540
+ }
541
+ const heap = fromMap(entries);
542
+ const val = {};
543
+ // x apunta al primer slot si hay alguno
544
+ if (entries.length > 0) {
545
+ const first = entries[0];
546
+ if (first)
547
+ val.x = addrVal(first[0]);
548
+ }
549
+ else {
550
+ val.x = nullVal();
551
+ }
552
+ return { heap, val };
553
+ }
554
+ function randomValue(rng) {
555
+ const choice = Math.floor(rng() * 3);
556
+ if (choice === 0)
557
+ return intVal(Math.floor(rng() * 10));
558
+ if (choice === 1)
559
+ return nullVal();
560
+ return addrVal(1 + Math.floor(rng() * 5));
561
+ }
562
+ // ── Predicados inductivos ───────────────────────────────────
563
+ /** `ls(x, y)` — list-segment de x a y. Definido por:
564
+ * ls(x, y) ≡ (x = y ∧ emp) ∨ ∃z. (x ↦ z * ls(z, y))
565
+ *
566
+ * Para mantener semántica computable, se interpreta directamente sobre
567
+ * el heap: existe una cadena de celdas desde `start` a `end` cuyos
568
+ * contenidos son punteros, sin ciclos ni celdas extra. */
569
+ function isListSegment(start, end, heap) {
570
+ if (valueEquals(start, end))
571
+ return heap.size() === 0;
572
+ if (start.kind !== 'addr')
573
+ return false;
574
+ const visited = new Set();
575
+ const path = [];
576
+ let cur = start;
577
+ while (!valueEquals(cur, end)) {
578
+ if (cur.kind !== 'addr')
579
+ return false;
580
+ if (visited.has(cur.loc))
581
+ return false; // ciclo
582
+ if (!heap.has(cur.loc))
583
+ return false;
584
+ visited.add(cur.loc);
585
+ path.push(cur.loc);
586
+ const next = heap.read(cur.loc);
587
+ cur = next;
588
+ }
589
+ // El heap debe ser exactamente las celdas del path — nada más.
590
+ if (heap.size() !== path.length)
591
+ return false;
592
+ return true;
593
+ }
594
+ /** Predicado SL para list-segment `ls(start, end)`. Se evalúa como
595
+ * predicado puro sobre el heap (cumple `satisfies`). */
596
+ function listSegment(start, end) {
597
+ // Encapsulamos la semántica inductiva en un `pure` que internamente
598
+ // mira el heap mediante closure. Para ello expandimos a una fórmula
599
+ // compuesta con un truco: usamos una macro que, al evaluarse, hace la
600
+ // verificación directa. Construimos un nodo `pure` con expresión
601
+ // descriptiva pero la verdad real la calcula la función externa via
602
+ // `isListSegment` — exponemos por separado `satisfiesShape`.
603
+ // Para participar de la semántica `satisfies` declaramos un nodo
604
+ // estructural: ls(start, end).
605
+ return {
606
+ kind: 'pure',
607
+ expression: `ls(${valueToString(start)}, ${valueToString(end)})`,
608
+ predicate: () => {
609
+ // El predicado puro no conoce el heap. La forma correcta de usar
610
+ // `ls` es vía `satisfiesShape` o vía la combinación predicado +
611
+ // helper externo. Devolvemos true como placeholder para que
612
+ // `pure` no rechace por sí solo.
613
+ return true;
614
+ },
615
+ };
616
+ }
617
+ /** Evalúa una fórmula con soporte de predicados inductivos sobre forma de
618
+ * heap (ls, tree). Se usa cuando la fórmula contiene `listSegment` o
619
+ * `tree` — `satisfies` solo no basta porque su rama `pure` ignora el heap. */
620
+ function satisfiesShape(formula, heap, val) {
621
+ if (formula.kind === 'pure') {
622
+ const lsMatch = formula.expression.match(/^ls\((.+),\s*(.+)\)$/);
623
+ if (lsMatch) {
624
+ const start = parseValueLit(lsMatch[1], val);
625
+ const end = parseValueLit(lsMatch[2], val);
626
+ if (start === null || end === null)
627
+ return false;
628
+ return isListSegment(start, end, heap);
629
+ }
630
+ const treeMatch = formula.expression.match(/^tree\((.+)\)$/);
631
+ if (treeMatch) {
632
+ const root = parseValueLit(treeMatch[1], val);
633
+ if (root === null)
634
+ return false;
635
+ return isTree(root, heap);
636
+ }
637
+ return formula.predicate(val);
638
+ }
639
+ if (formula.kind === 'star') {
640
+ for (const { h1, h2 } of splits(heap)) {
641
+ if (satisfiesShape(formula.left, h1, val) && satisfiesShape(formula.right, h2, val)) {
642
+ return true;
643
+ }
644
+ }
645
+ return false;
646
+ }
647
+ if (formula.kind === 'and') {
648
+ return satisfiesShape(formula.left, heap, val) && satisfiesShape(formula.right, heap, val);
649
+ }
650
+ if (formula.kind === 'or') {
651
+ return satisfiesShape(formula.left, heap, val) || satisfiesShape(formula.right, heap, val);
652
+ }
653
+ if (formula.kind === 'not') {
654
+ return !satisfiesShape(formula.body, heap, val);
655
+ }
656
+ return satisfies(formula, heap, val);
657
+ }
658
+ function parseValueLit(s, val) {
659
+ const trimmed = s.trim();
660
+ if (trimmed === 'null')
661
+ return nullVal();
662
+ if (trimmed.startsWith('&')) {
663
+ const n = Number.parseInt(trimmed.slice(1), 10);
664
+ if (Number.isNaN(n))
665
+ return null;
666
+ return addrVal(n);
667
+ }
668
+ if (val[trimmed] !== undefined) {
669
+ return val[trimmed];
670
+ }
671
+ const asInt = Number.parseInt(trimmed, 10);
672
+ if (!Number.isNaN(asInt))
673
+ return intVal(asInt);
674
+ return null;
675
+ }
676
+ /** `tree(root)` — el heap representa un árbol binario con raíz `root`.
677
+ * tree(x) ≡ (x = null ∧ emp) ∨ ∃l, r. (x ↦ l * x.next ↦ r * tree(l) * tree(r))
678
+ *
679
+ * Modelo simplificado: cada nodo ocupa 2 celdas consecutivas
680
+ * (loc, loc+1) con los punteros izquierdo y derecho.
681
+ */
682
+ function tree(root) {
683
+ return {
684
+ kind: 'pure',
685
+ expression: `tree(${valueToString(root)})`,
686
+ predicate: () => true,
687
+ };
688
+ }
689
+ function isTree(root, heap) {
690
+ if (root.kind === 'null')
691
+ return heap.size() === 0;
692
+ if (root.kind !== 'addr')
693
+ return false;
694
+ const visited = new Set();
695
+ function walk(node) {
696
+ if (node.kind === 'null')
697
+ return { ok: true, locs: [] };
698
+ if (node.kind !== 'addr')
699
+ return { ok: false, locs: [] };
700
+ if (visited.has(node.loc))
701
+ return { ok: false, locs: [] };
702
+ if (!heap.has(node.loc) || !heap.has(node.loc + 1)) {
703
+ return { ok: false, locs: [] };
704
+ }
705
+ visited.add(node.loc);
706
+ visited.add(node.loc + 1);
707
+ const left = heap.read(node.loc);
708
+ const right = heap.read(node.loc + 1);
709
+ const lr = walk(left);
710
+ if (!lr.ok)
711
+ return { ok: false, locs: [] };
712
+ const rr = walk(right);
713
+ if (!rr.ok)
714
+ return { ok: false, locs: [] };
715
+ return { ok: true, locs: [node.loc, node.loc + 1, ...lr.locs, ...rr.locs] };
716
+ }
717
+ const result = walk(root);
718
+ if (!result.ok)
719
+ return false;
720
+ return result.locs.length === heap.size();
721
+ }
722
+ // ── Frame rule (helper) ──────────────────────────────────────
723
+ /** Frame rule: si `{P} c {Q}` y `c` no modifica las variables libres de
724
+ * `R`, entonces `{P * R} c {Q * R}`. Esta función construye la tripla
725
+ * compuesta — la validación queda a cargo de `checkTriple`. */
726
+ function frame(triple, frameFormula) {
727
+ return {
728
+ pre: (0, exports.star)(triple.pre, frameFormula),
729
+ cmd: triple.cmd,
730
+ post: (0, exports.star)(triple.post, frameFormula),
731
+ };
732
+ }
733
+ // ── Re-exports y constructores convenientes ─────────────────
734
+ exports.Cmd = {
735
+ skip: () => ({ kind: 'skip' }),
736
+ assign: (variable, value) => ({
737
+ kind: 'assign',
738
+ variable,
739
+ value,
740
+ }),
741
+ alloc: (variable, value) => ({
742
+ kind: 'alloc',
743
+ variable,
744
+ ...(value !== undefined ? { value } : {}),
745
+ }),
746
+ free: (location) => ({ kind: 'free', location }),
747
+ load: (variable, location) => ({
748
+ kind: 'load',
749
+ variable,
750
+ location,
751
+ }),
752
+ store: (location, value) => ({
753
+ kind: 'store',
754
+ location,
755
+ value,
756
+ }),
757
+ };
758
+ //# sourceMappingURL=index.js.map