@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.
- package/dist/reasoning/datalog/index.d.ts +131 -0
- package/dist/reasoning/datalog/index.d.ts.map +1 -0
- package/dist/reasoning/datalog/index.js +706 -0
- package/dist/reasoning/datalog/index.js.map +1 -0
- package/dist/reasoning/galois-fields/index.d.ts +29 -0
- package/dist/reasoning/galois-fields/index.d.ts.map +1 -0
- package/dist/reasoning/galois-fields/index.js +522 -0
- package/dist/reasoning/galois-fields/index.js.map +1 -0
- package/dist/reasoning/hoare-logic/index.d.ts +130 -0
- package/dist/reasoning/hoare-logic/index.d.ts.map +1 -0
- package/dist/reasoning/hoare-logic/index.js +535 -0
- package/dist/reasoning/hoare-logic/index.js.map +1 -0
- package/dist/reasoning/lattice/index.d.ts +165 -0
- package/dist/reasoning/lattice/index.d.ts.map +1 -0
- package/dist/reasoning/lattice/index.js +587 -0
- package/dist/reasoning/lattice/index.js.map +1 -0
- package/dist/reasoning/model-checking/index.d.ts +113 -0
- package/dist/reasoning/model-checking/index.d.ts.map +1 -0
- package/dist/reasoning/model-checking/index.js +786 -0
- package/dist/reasoning/model-checking/index.js.map +1 -0
- package/dist/reasoning/polynomial-ring/index.d.ts +30 -0
- package/dist/reasoning/polynomial-ring/index.d.ts.map +1 -0
- package/dist/reasoning/polynomial-ring/index.js +797 -0
- package/dist/reasoning/polynomial-ring/index.js.map +1 -0
- package/dist/reasoning/separation-logic/index.d.ts +190 -0
- package/dist/reasoning/separation-logic/index.d.ts.map +1 -0
- package/dist/reasoning/separation-logic/index.js +758 -0
- package/dist/reasoning/separation-logic/index.js.map +1 -0
- package/dist/reasoning/universal-algebra/index.d.ts +196 -0
- package/dist/reasoning/universal-algebra/index.d.ts.map +1 -0
- package/dist/reasoning/universal-algebra/index.js +865 -0
- package/dist/reasoning/universal-algebra/index.js.map +1 -0
- package/dist/tests/reasoning/datalog/datalog.test.d.ts +2 -0
- package/dist/tests/reasoning/datalog/datalog.test.d.ts.map +1 -0
- package/dist/tests/reasoning/datalog/datalog.test.js +333 -0
- package/dist/tests/reasoning/datalog/datalog.test.js.map +1 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts +2 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.d.ts.map +1 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.js +226 -0
- package/dist/tests/reasoning/galois-fields/galois-fields.test.js.map +1 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.d.ts +2 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.d.ts.map +1 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.js +340 -0
- package/dist/tests/reasoning/hoare-logic/hoare-logic.test.js.map +1 -0
- package/dist/tests/reasoning/lattice/lattice.test.d.ts +2 -0
- package/dist/tests/reasoning/lattice/lattice.test.d.ts.map +1 -0
- package/dist/tests/reasoning/lattice/lattice.test.js +238 -0
- package/dist/tests/reasoning/lattice/lattice.test.js.map +1 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.d.ts +2 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.d.ts.map +1 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.js +222 -0
- package/dist/tests/reasoning/model-checking/model-checking.test.js.map +1 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts +2 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.d.ts.map +1 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js +230 -0
- package/dist/tests/reasoning/polynomial-ring/polynomial-ring.test.js.map +1 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.d.ts +2 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.d.ts.map +1 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.js +311 -0
- package/dist/tests/reasoning/separation-logic/separation-logic.test.js.map +1 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.d.ts +2 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.d.ts.map +1 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.js +289 -0
- package/dist/tests/reasoning/universal-algebra/universal-algebra.test.js.map +1 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts +2 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.d.ts.map +1 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js +266 -0
- package/dist/tests/type-theory/lambda-cube/lambda-cube.test.js.map +1 -0
- package/dist/type-theory/lambda-cube/erase.d.ts +26 -0
- package/dist/type-theory/lambda-cube/erase.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/erase.js +68 -0
- package/dist/type-theory/lambda-cube/erase.js.map +1 -0
- package/dist/type-theory/lambda-cube/examples.d.ts +59 -0
- package/dist/type-theory/lambda-cube/examples.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/examples.js +110 -0
- package/dist/type-theory/lambda-cube/examples.js.map +1 -0
- package/dist/type-theory/lambda-cube/index.d.ts +11 -0
- package/dist/type-theory/lambda-cube/index.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/index.js +64 -0
- package/dist/type-theory/lambda-cube/index.js.map +1 -0
- package/dist/type-theory/lambda-cube/normalize.d.ts +17 -0
- package/dist/type-theory/lambda-cube/normalize.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/normalize.js +134 -0
- package/dist/type-theory/lambda-cube/normalize.js.map +1 -0
- package/dist/type-theory/lambda-cube/rules.d.ts +26 -0
- package/dist/type-theory/lambda-cube/rules.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/rules.js +67 -0
- package/dist/type-theory/lambda-cube/rules.js.map +1 -0
- package/dist/type-theory/lambda-cube/typecheck.d.ts +20 -0
- package/dist/type-theory/lambda-cube/typecheck.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/typecheck.js +168 -0
- package/dist/type-theory/lambda-cube/typecheck.js.map +1 -0
- package/dist/type-theory/lambda-cube/types.d.ts +40 -0
- package/dist/type-theory/lambda-cube/types.d.ts.map +1 -0
- package/dist/type-theory/lambda-cube/types.js +192 -0
- package/dist/type-theory/lambda-cube/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
export type ImpBinop = '+' | '-' | '*' | '/' | '%' | '<' | '<=' | '>' | '>=' | '==' | '!=' | '&&' | '||';
|
|
2
|
+
export type ImpExpr = {
|
|
3
|
+
kind: 'const';
|
|
4
|
+
value: number;
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'bool';
|
|
7
|
+
value: boolean;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'var';
|
|
10
|
+
name: string;
|
|
11
|
+
} | {
|
|
12
|
+
kind: 'binop';
|
|
13
|
+
op: ImpBinop;
|
|
14
|
+
left: ImpExpr;
|
|
15
|
+
right: ImpExpr;
|
|
16
|
+
} | {
|
|
17
|
+
kind: 'not';
|
|
18
|
+
arg: ImpExpr;
|
|
19
|
+
};
|
|
20
|
+
export type ImpStmt = {
|
|
21
|
+
kind: 'skip';
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'assign';
|
|
24
|
+
var: string;
|
|
25
|
+
expr: ImpExpr;
|
|
26
|
+
} | {
|
|
27
|
+
kind: 'seq';
|
|
28
|
+
first: ImpStmt;
|
|
29
|
+
second: ImpStmt;
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'if';
|
|
32
|
+
cond: ImpExpr;
|
|
33
|
+
then: ImpStmt;
|
|
34
|
+
else: ImpStmt;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'while';
|
|
37
|
+
cond: ImpExpr;
|
|
38
|
+
invariant?: ImpExpr;
|
|
39
|
+
body: ImpStmt;
|
|
40
|
+
};
|
|
41
|
+
export interface HoareTriple {
|
|
42
|
+
pre: ImpExpr;
|
|
43
|
+
stmt: ImpStmt;
|
|
44
|
+
post: ImpExpr;
|
|
45
|
+
}
|
|
46
|
+
export declare const num: (n: number) => ImpExpr;
|
|
47
|
+
export declare const bool: (b: boolean) => ImpExpr;
|
|
48
|
+
export declare const v: (name: string) => ImpExpr;
|
|
49
|
+
export declare const binop: (op: ImpBinop, left: ImpExpr, right: ImpExpr) => ImpExpr;
|
|
50
|
+
export declare const not: (arg: ImpExpr) => ImpExpr;
|
|
51
|
+
export declare const and: (left: ImpExpr, right: ImpExpr) => ImpExpr;
|
|
52
|
+
export declare const or: (left: ImpExpr, right: ImpExpr) => ImpExpr;
|
|
53
|
+
export declare const eq: (left: ImpExpr, right: ImpExpr) => ImpExpr;
|
|
54
|
+
export declare const lt: (left: ImpExpr, right: ImpExpr) => ImpExpr;
|
|
55
|
+
export declare const le: (left: ImpExpr, right: ImpExpr) => ImpExpr;
|
|
56
|
+
export declare const skip: () => ImpStmt;
|
|
57
|
+
export declare const assign: (varName: string, expr: ImpExpr) => ImpStmt;
|
|
58
|
+
export declare function seq(...stmts: ImpStmt[]): ImpStmt;
|
|
59
|
+
export declare const ifS: (cond: ImpExpr, then_: ImpStmt, else_: ImpStmt) => ImpStmt;
|
|
60
|
+
export declare const whileS: (cond: ImpExpr, body: ImpStmt, invariant?: ImpExpr) => ImpStmt;
|
|
61
|
+
export declare function substitute(expr: ImpExpr, varName: string, replacement: ImpExpr): ImpExpr;
|
|
62
|
+
export declare function freeVars(expr: ImpExpr, acc?: Set<string>): Set<string>;
|
|
63
|
+
export declare function stmtVars(stmt: ImpStmt, acc?: Set<string>): Set<string>;
|
|
64
|
+
export type State = Record<string, number>;
|
|
65
|
+
export declare function evalExpr(expr: ImpExpr, state: State): number | boolean;
|
|
66
|
+
export type ExecError = {
|
|
67
|
+
error: string;
|
|
68
|
+
};
|
|
69
|
+
export declare function execStmt(stmt: ImpStmt, state: State, maxSteps?: number): State | ExecError;
|
|
70
|
+
export declare function wp(stmt: ImpStmt, post: ImpExpr): ImpExpr;
|
|
71
|
+
export declare function spExtension(stmt: ImpStmt, pre: ImpExpr): ImpExpr;
|
|
72
|
+
export declare function generateVCs(triple: HoareTriple): ImpExpr[];
|
|
73
|
+
export interface VerificationResult {
|
|
74
|
+
valid: boolean;
|
|
75
|
+
vcs: ImpExpr[];
|
|
76
|
+
failures: Array<{
|
|
77
|
+
vc: ImpExpr;
|
|
78
|
+
state?: State;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
export interface VerifyOptions {
|
|
82
|
+
samples?: number;
|
|
83
|
+
/** Rango de muestreo entero para variables (inclusive en ambos extremos). */
|
|
84
|
+
range?: [number, number];
|
|
85
|
+
/** Semilla determinista; si no, se usa Math.random. */
|
|
86
|
+
seed?: number;
|
|
87
|
+
/** Estados explícitos extra que el caller quiere verificar siempre. */
|
|
88
|
+
seedStates?: State[];
|
|
89
|
+
}
|
|
90
|
+
export declare function verifyTriple(triple: HoareTriple, opts?: VerifyOptions): VerificationResult;
|
|
91
|
+
/**
|
|
92
|
+
* Swap x ↔ y vía variable temporal `t`.
|
|
93
|
+
* t := x; x := y; y := t
|
|
94
|
+
* Tripleta canónica: {x = a ∧ y = b} swap {x = b ∧ y = a}
|
|
95
|
+
*/
|
|
96
|
+
export declare function programSwap(): ImpStmt;
|
|
97
|
+
/**
|
|
98
|
+
* Factorial: r := 1; k := 0; while k < n do { k := k+1; r := r * k }
|
|
99
|
+
* Tripleta: {n = N ∧ N ≥ 0} fact {r = N!}
|
|
100
|
+
* Invariant: k ≤ n ∧ r = k! (codificable como r = k!, k entre 0 y n)
|
|
101
|
+
*
|
|
102
|
+
* Como la lógica de Hoare aquí no tiene factorial nativo, exponemos el
|
|
103
|
+
* código y el invariant en forma `r > 0 ∧ k ≤ n` (suficiente para
|
|
104
|
+
* los tests de mantenimiento sintácticos con muestreo: el ejecutor
|
|
105
|
+
* confirma corrección concreta para n pequeños).
|
|
106
|
+
*/
|
|
107
|
+
export declare function programFactorial(): ImpStmt;
|
|
108
|
+
/**
|
|
109
|
+
* GCD por algoritmo de Euclides con restas:
|
|
110
|
+
* while x != y do { if x > y then x := x - y else y := y - x }
|
|
111
|
+
* Invariant: gcd(x, y) = gcd(a, b). Como no tenemos gcd nativo,
|
|
112
|
+
* usamos como invariant `x ≥ 1 ∧ y ≥ 1` (mantenido por restas
|
|
113
|
+
* positivas cuando x ≠ y y ambos positivos al entrar).
|
|
114
|
+
*/
|
|
115
|
+
export declare function programGCD(): ImpStmt;
|
|
116
|
+
/**
|
|
117
|
+
* Búsqueda lineal:
|
|
118
|
+
* i := 0; found := 0;
|
|
119
|
+
* while i < n && found == 0 do {
|
|
120
|
+
* if a == target then found := 1 else skip;
|
|
121
|
+
* i := i + 1
|
|
122
|
+
* }
|
|
123
|
+
* Modelo simplificado: `a` representa el elemento actual (no un array;
|
|
124
|
+
* el AST de IMP no tiene arrays). Sirve como esqueleto pedagógico
|
|
125
|
+
* para discutir el invariant `i ≤ n` y la post `found == 1 ∨ i == n`.
|
|
126
|
+
*/
|
|
127
|
+
export declare function programLinearSearch(): ImpStmt;
|
|
128
|
+
export declare function factorial(n: number): number;
|
|
129
|
+
export declare function gcd(a: number, b: number): number;
|
|
130
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/reasoning/hoare-logic/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// ============================================================
|
|
3
|
+
// ST Hoare Logic — Verificación de programas imperativos
|
|
4
|
+
// ============================================================
|
|
5
|
+
//
|
|
6
|
+
// Lógica de Hoare para un lenguaje IMP minimal (asignación, skip,
|
|
7
|
+
// secuencia, condicional, bucle). El motor calcula precondiciones
|
|
8
|
+
// más débiles (weakest precondition, wp) y postcondiciones más
|
|
9
|
+
// fuertes (strongest postcondition, sp), genera condiciones de
|
|
10
|
+
// verificación (VCs) y las refuta por búsqueda aleatoria sobre el
|
|
11
|
+
// universo entero acotado.
|
|
12
|
+
//
|
|
13
|
+
// Tripleta de Hoare:
|
|
14
|
+
// {P} c {Q} ≡ ejecutar c desde un estado que cumple P deja un
|
|
15
|
+
// estado que cumple Q (corrección parcial).
|
|
16
|
+
//
|
|
17
|
+
// Reglas (Hoare):
|
|
18
|
+
// skip : {P} skip {P}
|
|
19
|
+
// assign : {P[E/x]} x := E {P}
|
|
20
|
+
// seq : {P} c1 {R}, {R} c2 {Q} ⊢ {P} c1; c2 {Q}
|
|
21
|
+
// if : {P ∧ B} c1 {Q}, {P ∧ ¬B} c2 {Q} ⊢ {P} if B then c1 else c2 {Q}
|
|
22
|
+
// while : {I ∧ B} c {I} ⊢ {I} while B do c {I ∧ ¬B}
|
|
23
|
+
// weakening : P → P', {P'} c {Q'}, Q' → Q ⊢ {P} c {Q}
|
|
24
|
+
//
|
|
25
|
+
// Para `while` exigimos invariant explícito (campo `invariant`) o
|
|
26
|
+
// el VC pasa a ser trivial-falso (no podemos probar terminación
|
|
27
|
+
// parcial sin invariant). Las VCs se evalúan por muestreo aleatorio
|
|
28
|
+
// + estados sintéticos extraídos del programa: si alguna falla,
|
|
29
|
+
// devolvemos un contramodelo.
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.whileS = exports.ifS = exports.assign = exports.skip = exports.le = exports.lt = exports.eq = exports.or = exports.and = exports.not = exports.binop = exports.v = exports.bool = exports.num = void 0;
|
|
32
|
+
exports.seq = seq;
|
|
33
|
+
exports.substitute = substitute;
|
|
34
|
+
exports.freeVars = freeVars;
|
|
35
|
+
exports.stmtVars = stmtVars;
|
|
36
|
+
exports.evalExpr = evalExpr;
|
|
37
|
+
exports.execStmt = execStmt;
|
|
38
|
+
exports.wp = wp;
|
|
39
|
+
exports.spExtension = spExtension;
|
|
40
|
+
exports.generateVCs = generateVCs;
|
|
41
|
+
exports.verifyTriple = verifyTriple;
|
|
42
|
+
exports.programSwap = programSwap;
|
|
43
|
+
exports.programFactorial = programFactorial;
|
|
44
|
+
exports.programGCD = programGCD;
|
|
45
|
+
exports.programLinearSearch = programLinearSearch;
|
|
46
|
+
exports.factorial = factorial;
|
|
47
|
+
exports.gcd = gcd;
|
|
48
|
+
// ── Constructores de conveniencia ────────────────────────────
|
|
49
|
+
const num = (n) => ({ kind: 'const', value: n });
|
|
50
|
+
exports.num = num;
|
|
51
|
+
const bool = (b) => ({ kind: 'bool', value: b });
|
|
52
|
+
exports.bool = bool;
|
|
53
|
+
const v = (name) => ({ kind: 'var', name });
|
|
54
|
+
exports.v = v;
|
|
55
|
+
const binop = (op, left, right) => ({
|
|
56
|
+
kind: 'binop',
|
|
57
|
+
op,
|
|
58
|
+
left,
|
|
59
|
+
right,
|
|
60
|
+
});
|
|
61
|
+
exports.binop = binop;
|
|
62
|
+
const not = (arg) => ({ kind: 'not', arg });
|
|
63
|
+
exports.not = not;
|
|
64
|
+
const and = (left, right) => (0, exports.binop)('&&', left, right);
|
|
65
|
+
exports.and = and;
|
|
66
|
+
const or = (left, right) => (0, exports.binop)('||', left, right);
|
|
67
|
+
exports.or = or;
|
|
68
|
+
const eq = (left, right) => (0, exports.binop)('==', left, right);
|
|
69
|
+
exports.eq = eq;
|
|
70
|
+
const lt = (left, right) => (0, exports.binop)('<', left, right);
|
|
71
|
+
exports.lt = lt;
|
|
72
|
+
const le = (left, right) => (0, exports.binop)('<=', left, right);
|
|
73
|
+
exports.le = le;
|
|
74
|
+
const skip = () => ({ kind: 'skip' });
|
|
75
|
+
exports.skip = skip;
|
|
76
|
+
const assign = (varName, expr) => ({
|
|
77
|
+
kind: 'assign',
|
|
78
|
+
var: varName,
|
|
79
|
+
expr,
|
|
80
|
+
});
|
|
81
|
+
exports.assign = assign;
|
|
82
|
+
function seq(...stmts) {
|
|
83
|
+
if (stmts.length === 0)
|
|
84
|
+
return (0, exports.skip)();
|
|
85
|
+
if (stmts.length === 1)
|
|
86
|
+
return stmts[0];
|
|
87
|
+
// Foldr para mantener asociatividad derecha estable.
|
|
88
|
+
let acc = stmts[stmts.length - 1];
|
|
89
|
+
for (let i = stmts.length - 2; i >= 0; i--) {
|
|
90
|
+
acc = { kind: 'seq', first: stmts[i], second: acc };
|
|
91
|
+
}
|
|
92
|
+
return acc;
|
|
93
|
+
}
|
|
94
|
+
const ifS = (cond, then_, else_) => ({
|
|
95
|
+
kind: 'if',
|
|
96
|
+
cond,
|
|
97
|
+
then: then_,
|
|
98
|
+
else: else_,
|
|
99
|
+
});
|
|
100
|
+
exports.ifS = ifS;
|
|
101
|
+
const whileS = (cond, body, invariant) => ({
|
|
102
|
+
kind: 'while',
|
|
103
|
+
cond,
|
|
104
|
+
invariant,
|
|
105
|
+
body,
|
|
106
|
+
});
|
|
107
|
+
exports.whileS = whileS;
|
|
108
|
+
// ── Sustitución sintáctica P[E/x] ────────────────────────────
|
|
109
|
+
function substitute(expr, varName, replacement) {
|
|
110
|
+
switch (expr.kind) {
|
|
111
|
+
case 'const':
|
|
112
|
+
case 'bool':
|
|
113
|
+
return expr;
|
|
114
|
+
case 'var':
|
|
115
|
+
return expr.name === varName ? replacement : expr;
|
|
116
|
+
case 'binop':
|
|
117
|
+
return {
|
|
118
|
+
kind: 'binop',
|
|
119
|
+
op: expr.op,
|
|
120
|
+
left: substitute(expr.left, varName, replacement),
|
|
121
|
+
right: substitute(expr.right, varName, replacement),
|
|
122
|
+
};
|
|
123
|
+
case 'not':
|
|
124
|
+
return { kind: 'not', arg: substitute(expr.arg, varName, replacement) };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// ── Recolección de variables libres ──────────────────────────
|
|
128
|
+
function freeVars(expr, acc = new Set()) {
|
|
129
|
+
switch (expr.kind) {
|
|
130
|
+
case 'const':
|
|
131
|
+
case 'bool':
|
|
132
|
+
return acc;
|
|
133
|
+
case 'var':
|
|
134
|
+
acc.add(expr.name);
|
|
135
|
+
return acc;
|
|
136
|
+
case 'binop':
|
|
137
|
+
freeVars(expr.left, acc);
|
|
138
|
+
freeVars(expr.right, acc);
|
|
139
|
+
return acc;
|
|
140
|
+
case 'not':
|
|
141
|
+
freeVars(expr.arg, acc);
|
|
142
|
+
return acc;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function stmtVars(stmt, acc = new Set()) {
|
|
146
|
+
switch (stmt.kind) {
|
|
147
|
+
case 'skip':
|
|
148
|
+
return acc;
|
|
149
|
+
case 'assign':
|
|
150
|
+
acc.add(stmt.var);
|
|
151
|
+
freeVars(stmt.expr, acc);
|
|
152
|
+
return acc;
|
|
153
|
+
case 'seq':
|
|
154
|
+
stmtVars(stmt.first, acc);
|
|
155
|
+
stmtVars(stmt.second, acc);
|
|
156
|
+
return acc;
|
|
157
|
+
case 'if':
|
|
158
|
+
freeVars(stmt.cond, acc);
|
|
159
|
+
stmtVars(stmt.then, acc);
|
|
160
|
+
stmtVars(stmt.else, acc);
|
|
161
|
+
return acc;
|
|
162
|
+
case 'while':
|
|
163
|
+
freeVars(stmt.cond, acc);
|
|
164
|
+
if (stmt.invariant)
|
|
165
|
+
freeVars(stmt.invariant, acc);
|
|
166
|
+
stmtVars(stmt.body, acc);
|
|
167
|
+
return acc;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function evalExpr(expr, state) {
|
|
171
|
+
switch (expr.kind) {
|
|
172
|
+
case 'const':
|
|
173
|
+
return expr.value;
|
|
174
|
+
case 'bool':
|
|
175
|
+
return expr.value;
|
|
176
|
+
case 'var': {
|
|
177
|
+
const value = state[expr.name];
|
|
178
|
+
// Variables no definidas valen 0 (estado total). Esto simplifica
|
|
179
|
+
// el muestreo aleatorio y los VCs sobre programas pequeños.
|
|
180
|
+
return value === undefined ? 0 : value;
|
|
181
|
+
}
|
|
182
|
+
case 'not': {
|
|
183
|
+
const a = evalExpr(expr.arg, state);
|
|
184
|
+
return !toBool(a);
|
|
185
|
+
}
|
|
186
|
+
case 'binop': {
|
|
187
|
+
const l = evalExpr(expr.left, state);
|
|
188
|
+
const r = evalExpr(expr.right, state);
|
|
189
|
+
switch (expr.op) {
|
|
190
|
+
case '+':
|
|
191
|
+
return toNum(l) + toNum(r);
|
|
192
|
+
case '-':
|
|
193
|
+
return toNum(l) - toNum(r);
|
|
194
|
+
case '*':
|
|
195
|
+
return toNum(l) * toNum(r);
|
|
196
|
+
case '/': {
|
|
197
|
+
const rn = toNum(r);
|
|
198
|
+
if (rn === 0)
|
|
199
|
+
return 0; // división por cero → 0 (estado total)
|
|
200
|
+
return Math.trunc(toNum(l) / rn);
|
|
201
|
+
}
|
|
202
|
+
case '%': {
|
|
203
|
+
const rn = toNum(r);
|
|
204
|
+
if (rn === 0)
|
|
205
|
+
return 0;
|
|
206
|
+
return toNum(l) % rn;
|
|
207
|
+
}
|
|
208
|
+
case '<':
|
|
209
|
+
return toNum(l) < toNum(r);
|
|
210
|
+
case '<=':
|
|
211
|
+
return toNum(l) <= toNum(r);
|
|
212
|
+
case '>':
|
|
213
|
+
return toNum(l) > toNum(r);
|
|
214
|
+
case '>=':
|
|
215
|
+
return toNum(l) >= toNum(r);
|
|
216
|
+
case '==':
|
|
217
|
+
return toNum(l) === toNum(r);
|
|
218
|
+
case '!=':
|
|
219
|
+
return toNum(l) !== toNum(r);
|
|
220
|
+
case '&&':
|
|
221
|
+
return toBool(l) && toBool(r);
|
|
222
|
+
case '||':
|
|
223
|
+
return toBool(l) || toBool(r);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function toNum(v) {
|
|
229
|
+
return typeof v === 'boolean' ? (v ? 1 : 0) : v;
|
|
230
|
+
}
|
|
231
|
+
function toBool(v) {
|
|
232
|
+
return typeof v === 'boolean' ? v : v !== 0;
|
|
233
|
+
}
|
|
234
|
+
function execStmt(stmt, state, maxSteps = 10_000) {
|
|
235
|
+
const counter = { steps: 0, limit: maxSteps };
|
|
236
|
+
try {
|
|
237
|
+
return execInternal(stmt, { ...state }, counter);
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function execInternal(stmt, state, counter) {
|
|
244
|
+
if (counter.steps++ > counter.limit) {
|
|
245
|
+
throw new Error(`execStmt: límite de pasos excedido (${counter.limit})`);
|
|
246
|
+
}
|
|
247
|
+
switch (stmt.kind) {
|
|
248
|
+
case 'skip':
|
|
249
|
+
return state;
|
|
250
|
+
case 'assign': {
|
|
251
|
+
const value = evalExpr(stmt.expr, state);
|
|
252
|
+
state[stmt.var] = toNum(value);
|
|
253
|
+
return state;
|
|
254
|
+
}
|
|
255
|
+
case 'seq': {
|
|
256
|
+
execInternal(stmt.first, state, counter);
|
|
257
|
+
execInternal(stmt.second, state, counter);
|
|
258
|
+
return state;
|
|
259
|
+
}
|
|
260
|
+
case 'if': {
|
|
261
|
+
const cond = evalExpr(stmt.cond, state);
|
|
262
|
+
if (toBool(cond)) {
|
|
263
|
+
execInternal(stmt.then, state, counter);
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
execInternal(stmt.else, state, counter);
|
|
267
|
+
}
|
|
268
|
+
return state;
|
|
269
|
+
}
|
|
270
|
+
case 'while': {
|
|
271
|
+
while (toBool(evalExpr(stmt.cond, state))) {
|
|
272
|
+
if (counter.steps++ > counter.limit) {
|
|
273
|
+
throw new Error(`execStmt: límite de pasos excedido en while (${counter.limit})`);
|
|
274
|
+
}
|
|
275
|
+
execInternal(stmt.body, state, counter);
|
|
276
|
+
}
|
|
277
|
+
return state;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// ── Weakest precondition (wp) ────────────────────────────────
|
|
282
|
+
function wp(stmt, post) {
|
|
283
|
+
switch (stmt.kind) {
|
|
284
|
+
case 'skip':
|
|
285
|
+
return post;
|
|
286
|
+
case 'assign':
|
|
287
|
+
return substitute(post, stmt.var, stmt.expr);
|
|
288
|
+
case 'seq': {
|
|
289
|
+
const after = wp(stmt.second, post);
|
|
290
|
+
return wp(stmt.first, after);
|
|
291
|
+
}
|
|
292
|
+
case 'if': {
|
|
293
|
+
const wpThen = wp(stmt.then, post);
|
|
294
|
+
const wpElse = wp(stmt.else, post);
|
|
295
|
+
// (cond → wpThen) ∧ (¬cond → wpElse)
|
|
296
|
+
return (0, exports.and)((0, exports.or)((0, exports.not)(stmt.cond), wpThen), (0, exports.or)(stmt.cond, wpElse));
|
|
297
|
+
}
|
|
298
|
+
case 'while': {
|
|
299
|
+
// Sin invariant no podemos calcular wp exacto en general.
|
|
300
|
+
// Devolvemos `false` para forzar fallo de VC explícito.
|
|
301
|
+
if (!stmt.invariant)
|
|
302
|
+
return (0, exports.bool)(false);
|
|
303
|
+
// Con invariant I la wp es simplemente I; las verificaciones
|
|
304
|
+
// adicionales (mantenimiento, salida) se generan como VCs.
|
|
305
|
+
return stmt.invariant;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// ── Strongest postcondition (sp), versión sintáctica ─────────
|
|
310
|
+
//
|
|
311
|
+
// Implementación pragmática: para statements sin loops devolvemos
|
|
312
|
+
// una fórmula que describe el estado tras ejecutar `stmt`. Para
|
|
313
|
+
// loops sin invariant cae a `true` (no informativo).
|
|
314
|
+
function spExtension(stmt, pre) {
|
|
315
|
+
switch (stmt.kind) {
|
|
316
|
+
case 'skip':
|
|
317
|
+
return pre;
|
|
318
|
+
case 'assign': {
|
|
319
|
+
// Introducimos una variable fresca para el valor previo de stmt.var:
|
|
320
|
+
// sp(P, x := E) = ∃x'. P[x'/x] ∧ x = E[x'/x]
|
|
321
|
+
// Como no soportamos cuantificadores, aproximamos por la
|
|
322
|
+
// post-substitución del valor nuevo cuando E no menciona x.
|
|
323
|
+
// Si E menciona x, devolvemos `true` (no informativo).
|
|
324
|
+
const eVars = freeVars(stmt.expr);
|
|
325
|
+
if (!eVars.has(stmt.var)) {
|
|
326
|
+
return (0, exports.and)(pre, (0, exports.eq)((0, exports.v)(stmt.var), stmt.expr));
|
|
327
|
+
}
|
|
328
|
+
return (0, exports.bool)(true);
|
|
329
|
+
}
|
|
330
|
+
case 'seq':
|
|
331
|
+
return spExtension(stmt.second, spExtension(stmt.first, pre));
|
|
332
|
+
case 'if': {
|
|
333
|
+
const spThen = spExtension(stmt.then, (0, exports.and)(pre, stmt.cond));
|
|
334
|
+
const spElse = spExtension(stmt.else, (0, exports.and)(pre, (0, exports.not)(stmt.cond)));
|
|
335
|
+
return (0, exports.or)(spThen, spElse);
|
|
336
|
+
}
|
|
337
|
+
case 'while':
|
|
338
|
+
// Saliendo de un while, vale (I ∧ ¬cond) si hay invariant.
|
|
339
|
+
if (stmt.invariant)
|
|
340
|
+
return (0, exports.and)(stmt.invariant, (0, exports.not)(stmt.cond));
|
|
341
|
+
return (0, exports.bool)(true);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
// ── Generación de condiciones de verificación (VCs) ──────────
|
|
345
|
+
function generateVCs(triple) {
|
|
346
|
+
const vcs = [];
|
|
347
|
+
// VC global: pre → wp(stmt, post)
|
|
348
|
+
vcs.push(implies(triple.pre, wp(triple.stmt, triple.post)));
|
|
349
|
+
collectLoopVCs(triple.stmt, triple.post, vcs);
|
|
350
|
+
return vcs;
|
|
351
|
+
}
|
|
352
|
+
function implies(p, q) {
|
|
353
|
+
return (0, exports.or)((0, exports.not)(p), q);
|
|
354
|
+
}
|
|
355
|
+
function collectLoopVCs(stmt, post, out) {
|
|
356
|
+
switch (stmt.kind) {
|
|
357
|
+
case 'skip':
|
|
358
|
+
case 'assign':
|
|
359
|
+
return;
|
|
360
|
+
case 'seq':
|
|
361
|
+
collectLoopVCs(stmt.first, wp(stmt.second, post), out);
|
|
362
|
+
collectLoopVCs(stmt.second, post, out);
|
|
363
|
+
return;
|
|
364
|
+
case 'if':
|
|
365
|
+
collectLoopVCs(stmt.then, post, out);
|
|
366
|
+
collectLoopVCs(stmt.else, post, out);
|
|
367
|
+
return;
|
|
368
|
+
case 'while': {
|
|
369
|
+
if (!stmt.invariant) {
|
|
370
|
+
// VC trivialmente falso: no hay invariant.
|
|
371
|
+
out.push((0, exports.bool)(false));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const I = stmt.invariant;
|
|
375
|
+
// Mantenimiento: (I ∧ cond) → wp(body, I)
|
|
376
|
+
out.push(implies((0, exports.and)(I, stmt.cond), wp(stmt.body, I)));
|
|
377
|
+
// Salida: (I ∧ ¬cond) → post (sólo si este while es el último
|
|
378
|
+
// step antes de la post). Para keep it simple, lo generamos
|
|
379
|
+
// siempre — es una condición necesaria local en el contexto del
|
|
380
|
+
// VC global, no redundante.
|
|
381
|
+
out.push(implies((0, exports.and)(I, (0, exports.not)(stmt.cond)), post));
|
|
382
|
+
// Recurse sobre el body por loops anidados.
|
|
383
|
+
collectLoopVCs(stmt.body, I, out);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function verifyTriple(triple, opts = {}) {
|
|
389
|
+
const samples = opts.samples ?? 200;
|
|
390
|
+
const range = opts.range ?? [-5, 10];
|
|
391
|
+
const vcs = generateVCs(triple);
|
|
392
|
+
const vars = new Set();
|
|
393
|
+
freeVars(triple.pre, vars);
|
|
394
|
+
freeVars(triple.post, vars);
|
|
395
|
+
stmtVars(triple.stmt, vars);
|
|
396
|
+
const varNames = [...vars];
|
|
397
|
+
const rand = makeRandom(opts.seed);
|
|
398
|
+
const failures = [];
|
|
399
|
+
// Determinismo: incluimos estados "corner": todos 0, todos 1, todos -1.
|
|
400
|
+
const cornerStates = [];
|
|
401
|
+
for (const c of [0, 1, -1, range[0], range[1]]) {
|
|
402
|
+
const st = {};
|
|
403
|
+
for (const name of varNames)
|
|
404
|
+
st[name] = c;
|
|
405
|
+
cornerStates.push(st);
|
|
406
|
+
}
|
|
407
|
+
const allSeed = [...cornerStates, ...(opts.seedStates ?? [])];
|
|
408
|
+
for (const vc of vcs) {
|
|
409
|
+
let counterexample;
|
|
410
|
+
// VCs literalmente `false` se reportan como fallo sintáctico sin
|
|
411
|
+
// necesidad de muestrear.
|
|
412
|
+
if (vc.kind === 'bool' && vc.value === false) {
|
|
413
|
+
failures.push({ vc });
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
// 1) corner + seed states
|
|
417
|
+
for (const st of allSeed) {
|
|
418
|
+
if (!evalVC(vc, st)) {
|
|
419
|
+
counterexample = st;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// 2) random sampling
|
|
424
|
+
if (counterexample === undefined) {
|
|
425
|
+
for (let i = 0; i < samples; i++) {
|
|
426
|
+
const st = {};
|
|
427
|
+
for (const name of varNames) {
|
|
428
|
+
st[name] = randIntInRange(rand, range[0], range[1]);
|
|
429
|
+
}
|
|
430
|
+
if (!evalVC(vc, st)) {
|
|
431
|
+
counterexample = st;
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (counterexample !== undefined) {
|
|
437
|
+
failures.push({ vc, state: counterexample });
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
return { valid: failures.length === 0, vcs, failures };
|
|
441
|
+
}
|
|
442
|
+
function evalVC(vc, state) {
|
|
443
|
+
return toBool(evalExpr(vc, state));
|
|
444
|
+
}
|
|
445
|
+
function makeRandom(seed) {
|
|
446
|
+
if (seed === undefined)
|
|
447
|
+
return Math.random;
|
|
448
|
+
// Mulberry32 — pequeño PRNG determinista.
|
|
449
|
+
let s = seed >>> 0;
|
|
450
|
+
return () => {
|
|
451
|
+
s = (s + 0x6d2b79f5) >>> 0;
|
|
452
|
+
let t = s;
|
|
453
|
+
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
454
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
455
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
function randIntInRange(rand, lo, hi) {
|
|
459
|
+
return Math.floor(rand() * (hi - lo + 1)) + lo;
|
|
460
|
+
}
|
|
461
|
+
// ── Programas estándar de la literatura ──────────────────────
|
|
462
|
+
/**
|
|
463
|
+
* Swap x ↔ y vía variable temporal `t`.
|
|
464
|
+
* t := x; x := y; y := t
|
|
465
|
+
* Tripleta canónica: {x = a ∧ y = b} swap {x = b ∧ y = a}
|
|
466
|
+
*/
|
|
467
|
+
function programSwap() {
|
|
468
|
+
return seq((0, exports.assign)('t', (0, exports.v)('x')), (0, exports.assign)('x', (0, exports.v)('y')), (0, exports.assign)('y', (0, exports.v)('t')));
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Factorial: r := 1; k := 0; while k < n do { k := k+1; r := r * k }
|
|
472
|
+
* Tripleta: {n = N ∧ N ≥ 0} fact {r = N!}
|
|
473
|
+
* Invariant: k ≤ n ∧ r = k! (codificable como r = k!, k entre 0 y n)
|
|
474
|
+
*
|
|
475
|
+
* Como la lógica de Hoare aquí no tiene factorial nativo, exponemos el
|
|
476
|
+
* código y el invariant en forma `r > 0 ∧ k ≤ n` (suficiente para
|
|
477
|
+
* los tests de mantenimiento sintácticos con muestreo: el ejecutor
|
|
478
|
+
* confirma corrección concreta para n pequeños).
|
|
479
|
+
*/
|
|
480
|
+
function programFactorial() {
|
|
481
|
+
const body = seq((0, exports.assign)('k', (0, exports.binop)('+', (0, exports.v)('k'), (0, exports.num)(1))), (0, exports.assign)('r', (0, exports.binop)('*', (0, exports.v)('r'), (0, exports.v)('k'))));
|
|
482
|
+
const invariant = (0, exports.and)((0, exports.le)((0, exports.v)('k'), (0, exports.v)('n')), (0, exports.binop)('>=', (0, exports.v)('r'), (0, exports.num)(1)));
|
|
483
|
+
return seq((0, exports.assign)('r', (0, exports.num)(1)), (0, exports.assign)('k', (0, exports.num)(0)), (0, exports.whileS)((0, exports.binop)('<', (0, exports.v)('k'), (0, exports.v)('n')), body, invariant));
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* GCD por algoritmo de Euclides con restas:
|
|
487
|
+
* while x != y do { if x > y then x := x - y else y := y - x }
|
|
488
|
+
* Invariant: gcd(x, y) = gcd(a, b). Como no tenemos gcd nativo,
|
|
489
|
+
* usamos como invariant `x ≥ 1 ∧ y ≥ 1` (mantenido por restas
|
|
490
|
+
* positivas cuando x ≠ y y ambos positivos al entrar).
|
|
491
|
+
*/
|
|
492
|
+
function programGCD() {
|
|
493
|
+
const body = (0, exports.ifS)((0, exports.binop)('>', (0, exports.v)('x'), (0, exports.v)('y')), (0, exports.assign)('x', (0, exports.binop)('-', (0, exports.v)('x'), (0, exports.v)('y'))), (0, exports.assign)('y', (0, exports.binop)('-', (0, exports.v)('y'), (0, exports.v)('x'))));
|
|
494
|
+
const invariant = (0, exports.and)((0, exports.binop)('>=', (0, exports.v)('x'), (0, exports.num)(1)), (0, exports.binop)('>=', (0, exports.v)('y'), (0, exports.num)(1)));
|
|
495
|
+
return (0, exports.whileS)((0, exports.binop)('!=', (0, exports.v)('x'), (0, exports.v)('y')), body, invariant);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Búsqueda lineal:
|
|
499
|
+
* i := 0; found := 0;
|
|
500
|
+
* while i < n && found == 0 do {
|
|
501
|
+
* if a == target then found := 1 else skip;
|
|
502
|
+
* i := i + 1
|
|
503
|
+
* }
|
|
504
|
+
* Modelo simplificado: `a` representa el elemento actual (no un array;
|
|
505
|
+
* el AST de IMP no tiene arrays). Sirve como esqueleto pedagógico
|
|
506
|
+
* para discutir el invariant `i ≤ n` y la post `found == 1 ∨ i == n`.
|
|
507
|
+
*/
|
|
508
|
+
function programLinearSearch() {
|
|
509
|
+
const body = seq((0, exports.ifS)((0, exports.eq)((0, exports.v)('a'), (0, exports.v)('target')), (0, exports.assign)('found', (0, exports.num)(1)), (0, exports.skip)()), (0, exports.assign)('i', (0, exports.binop)('+', (0, exports.v)('i'), (0, exports.num)(1))));
|
|
510
|
+
const invariant = (0, exports.and)((0, exports.le)((0, exports.v)('i'), (0, exports.v)('n')), (0, exports.or)((0, exports.eq)((0, exports.v)('found'), (0, exports.num)(0)), (0, exports.eq)((0, exports.v)('found'), (0, exports.num)(1))));
|
|
511
|
+
return seq((0, exports.assign)('i', (0, exports.num)(0)), (0, exports.assign)('found', (0, exports.num)(0)), (0, exports.whileS)((0, exports.and)((0, exports.binop)('<', (0, exports.v)('i'), (0, exports.v)('n')), (0, exports.eq)((0, exports.v)('found'), (0, exports.num)(0))), body, invariant));
|
|
512
|
+
}
|
|
513
|
+
// ── Helper: cómputo factorial concreto (para los tests) ──────
|
|
514
|
+
function factorial(n) {
|
|
515
|
+
let r = 1;
|
|
516
|
+
for (let k = 1; k <= n; k++)
|
|
517
|
+
r *= k;
|
|
518
|
+
return r;
|
|
519
|
+
}
|
|
520
|
+
function gcd(a, b) {
|
|
521
|
+
let x = Math.abs(a);
|
|
522
|
+
let y = Math.abs(b);
|
|
523
|
+
if (x === 0)
|
|
524
|
+
return y;
|
|
525
|
+
if (y === 0)
|
|
526
|
+
return x;
|
|
527
|
+
while (x !== y) {
|
|
528
|
+
if (x > y)
|
|
529
|
+
x -= y;
|
|
530
|
+
else
|
|
531
|
+
y -= x;
|
|
532
|
+
}
|
|
533
|
+
return x;
|
|
534
|
+
}
|
|
535
|
+
//# sourceMappingURL=index.js.map
|