mirror of
https://github.com/Z3Prover/z3
synced 2025-04-08 18:31:49 +00:00
Make high-level JS API more idiomatic/type-safe (#6101)
* make JS api more idiomatic * make JS api type-safe by default * use strings, not symbols, for results * add toString * add miracle sudoku example * ints should be ints * add error handling * add missing Cond to Context * fewer side-effecting getters
This commit is contained in:
parent
8234eeae40
commit
c15a000d9b
206
src/api/js/examples/high-level/miracle-sudoku.ts
Normal file
206
src/api/js/examples/high-level/miracle-sudoku.ts
Normal file
|
@ -0,0 +1,206 @@
|
|||
import { init } from '../../build/node';
|
||||
|
||||
import type { Solver, Arith } from '../../build/node';
|
||||
|
||||
// solve the "miracle sudoku"
|
||||
// https://www.youtube.com/watch?v=yKf9aUIxdb4
|
||||
// most of the interesting stuff is in `solve`
|
||||
// the process is:
|
||||
// - parse the board
|
||||
// - create a Solver
|
||||
// - create a Z3.Int variable for each square
|
||||
// - for known cells, add a constraint which says the variable for that cell equals that value
|
||||
// - add the usual uniqueness constraints
|
||||
// - add the special "miracle sudoku" constraints
|
||||
// - call `await solver.check()`
|
||||
// - if the result is "sat", the board is solvable
|
||||
// - call `solver.model()` to get a model, i.e. a concrete assignment of variables which satisfies the model
|
||||
// - for each variable, call `model.evaluate(v)` to recover its value
|
||||
|
||||
function parseSudoku(str: string) {
|
||||
// derive a list of { row, col, val } records, one for each specified position
|
||||
// from a string like
|
||||
// ....1..3.
|
||||
// ..9..5..8
|
||||
// 8.4..6.25
|
||||
// ......6..
|
||||
// ..8..4...
|
||||
// 12..87...
|
||||
// 3..9..2..
|
||||
// .65..8...
|
||||
// 9........
|
||||
|
||||
let cells = [];
|
||||
|
||||
let lines = str.trim().split('\n');
|
||||
if (lines.length !== 9) {
|
||||
throw new Error(`expected 9 lines, got ${lines.length}`);
|
||||
}
|
||||
for (let row = 0; row < 9; ++row) {
|
||||
let line = lines[row].trim();
|
||||
if (line.length !== 9) {
|
||||
throw new Error(`expected line of length 9, got length ${line.length}`);
|
||||
}
|
||||
for (let col = 0; col < 9; ++col) {
|
||||
let char = line[col];
|
||||
if (char === '.') {
|
||||
continue;
|
||||
}
|
||||
if (char < '1' || char > '9') {
|
||||
throw new Error(`expected digit or '.', got ${char}`);
|
||||
}
|
||||
cells.push({ row, col, value: char.codePointAt(0)! - 48 /* '0' */ });
|
||||
}
|
||||
}
|
||||
return cells;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
let { Context, em } = await init();
|
||||
|
||||
// if you use 'main' as your context name, you won't need to name it in types like Solver
|
||||
// if you're creating multiple contexts, give them different names
|
||||
// then the type system will prevent you from mixing them
|
||||
let Z3 = Context('main');
|
||||
|
||||
function addSudokuConstraints(solver: Solver, cells: Arith[][]) {
|
||||
// the usual constraints:
|
||||
|
||||
// every square is between 1 and 9
|
||||
for (let row of cells) {
|
||||
for (let cell of row) {
|
||||
solver.add(cell.ge(1));
|
||||
solver.add(cell.le(9));
|
||||
}
|
||||
}
|
||||
|
||||
// values in each row are unique
|
||||
for (let row of cells) {
|
||||
solver.add(Z3.Distinct(...row));
|
||||
}
|
||||
|
||||
// values in each column are unique
|
||||
for (let col = 0; col < 9; ++col) {
|
||||
solver.add(Z3.Distinct(...cells.map(row => row[col])));
|
||||
}
|
||||
|
||||
// values in each 3x3 subdivision are unique
|
||||
for (let suprow = 0; suprow < 3; ++suprow) {
|
||||
for (let supcol = 0; supcol < 3; ++supcol) {
|
||||
let square = [];
|
||||
for (let row = 0; row < 3; ++row) {
|
||||
for (let col = 0; col < 3; ++col) {
|
||||
square.push(cells[suprow * 3 + row][supcol * 3 + col]);
|
||||
}
|
||||
}
|
||||
solver.add(Z3.Distinct(...square));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function applyOffsets(x: number, y: number, offsets: [number, number][]) {
|
||||
let out = [];
|
||||
for (let offset of offsets) {
|
||||
let rx = x + offset[0];
|
||||
let ry = y + offset[1];
|
||||
if (rx >= 0 && rx < 9 && ry >= 0 && ry < 8) {
|
||||
out.push({ x: rx, y: ry });
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function addMiracleConstraints(s: Solver, cells: Arith[][]) {
|
||||
// the special "miracle sudoku" constraints
|
||||
|
||||
// any two cells separated by a knight's move or a kings move cannot contain the same digit
|
||||
let knightOffets: [number, number][] = [
|
||||
[1, -2],
|
||||
[2, -1],
|
||||
[2, 1],
|
||||
[1, 2],
|
||||
[-1, 2],
|
||||
[-2, 1],
|
||||
[-2, -1],
|
||||
[-1, -2],
|
||||
];
|
||||
let kingOffsets: [number, number][] = [
|
||||
[1, 1],
|
||||
[1, -1],
|
||||
[-1, 1],
|
||||
[-1, -1],
|
||||
]; // skipping immediately adjacent because those are covered by normal sudoku rules
|
||||
let allOffets = [...knightOffets, ...kingOffsets];
|
||||
for (let row = 0; row < 9; ++row) {
|
||||
for (let col = 0; col < 9; ++col) {
|
||||
for (let { x, y } of applyOffsets(row, col, allOffets)) {
|
||||
s.add(cells[row][col].neq(cells[x][y]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// any two orthogonally adjacent cells cannot contain consecutive digits
|
||||
let orthoOffsets: [number, number][] = [
|
||||
[0, 1],
|
||||
[0, -1],
|
||||
[1, 0],
|
||||
[-1, 0],
|
||||
];
|
||||
for (let row = 0; row < 9; ++row) {
|
||||
for (let col = 0; col < 9; ++col) {
|
||||
for (let { x, y } of applyOffsets(row, col, orthoOffsets)) {
|
||||
s.add(cells[row][col].sub(cells[x][y]).neq(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function solve(str: string) {
|
||||
let solver = new Z3.Solver();
|
||||
let cells = Array.from({ length: 9 }, (_, col) => Array.from({ length: 9 }, (_, row) => Z3.Int.const(`c_${row}_${col}`)));
|
||||
for (let { row, col, value } of parseSudoku(str)) {
|
||||
solver.add(cells[row][col].eq(value));
|
||||
}
|
||||
addSudokuConstraints(solver, cells);
|
||||
addMiracleConstraints(solver, cells); // remove this line to solve normal sudokus
|
||||
|
||||
let start = Date.now();
|
||||
console.log('starting... this may take a minute or two');
|
||||
let check = await solver.check();
|
||||
console.log(`problem was determined to be ${check} in ${Date.now() - start} ms`);
|
||||
if (check === 'sat') {
|
||||
let model = solver.model();
|
||||
let str = '';
|
||||
for (let row = 0; row < 9; ++row) {
|
||||
for (let col = 0; col < 9; ++col) {
|
||||
str += model.eval(cells[row][col]).toString() + (col === 8 ? '' : ' ');
|
||||
if (col === 2 || col === 5) {
|
||||
str += ' ';
|
||||
}
|
||||
}
|
||||
str += '\n';
|
||||
if (row === 2 || row === 5) {
|
||||
str += '\n';
|
||||
}
|
||||
}
|
||||
console.log(str);
|
||||
}
|
||||
}
|
||||
|
||||
await solve(`
|
||||
.........
|
||||
.........
|
||||
.........
|
||||
.........
|
||||
..1......
|
||||
......2..
|
||||
.........
|
||||
.........
|
||||
.........
|
||||
`);
|
||||
|
||||
em.PThread.terminateAllThreads();
|
||||
})().catch(e => {
|
||||
console.error('error', e);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
import assert from 'assert';
|
||||
import asyncToArray from 'iter-tools/methods/async-to-array';
|
||||
import { init, killThreads } from '../jest';
|
||||
import { Arith, Bool, Model, sat, unsat, Z3AssertionError, Z3HighLevel } from './types';
|
||||
import { Arith, Bool, Model, Z3AssertionError, Z3HighLevel } from './types';
|
||||
|
||||
/**
|
||||
* Generate all possible solutions from given assumptions.
|
||||
|
@ -31,7 +31,7 @@ async function* allSolutions<Name extends string>(...assertions: Bool<Name>[]):
|
|||
const solver = new assertions[0].ctx.Solver();
|
||||
solver.add(...assertions);
|
||||
|
||||
while ((await solver.check()) === sat) {
|
||||
while ((await solver.check()) === 'sat') {
|
||||
const model = solver.model();
|
||||
const decls = model.decls();
|
||||
if (decls.length === 0) {
|
||||
|
@ -59,13 +59,13 @@ async function prove(conjecture: Bool): Promise<void> {
|
|||
const solver = new conjecture.ctx.Solver();
|
||||
const { Not } = solver.ctx;
|
||||
solver.add(Not(conjecture));
|
||||
expect(await solver.check()).toStrictEqual(unsat);
|
||||
expect(await solver.check()).toStrictEqual('unsat');
|
||||
}
|
||||
|
||||
async function solve(conjecture: Bool): Promise<Model> {
|
||||
const solver = new conjecture.ctx.Solver();
|
||||
solver.add(conjecture);
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
return solver.model();
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ describe('high-level', () => {
|
|||
});
|
||||
|
||||
it('proves x = y implies g(x) = g(y)', async () => {
|
||||
const { Solver, Int, Function, Implies, Not } = new api.Context('main');
|
||||
const { Solver, Int, Function, Implies, Not } = api.Context('main');
|
||||
const solver = new Solver();
|
||||
|
||||
const sort = Int.sort();
|
||||
|
@ -106,11 +106,11 @@ describe('high-level', () => {
|
|||
|
||||
const conjecture = Implies(x.eq(y), g.call(x).eq(g.call(y)));
|
||||
solver.add(Not(conjecture));
|
||||
expect(await solver.check()).toStrictEqual(unsat);
|
||||
expect(await solver.check()).toStrictEqual('unsat');
|
||||
});
|
||||
|
||||
it('disproves x = y implies g(g(x)) = g(y)', async () => {
|
||||
const { Solver, Int, Function, Implies, Not } = new api.Context('main');
|
||||
const { Solver, Int, Function, Implies, Not } = api.Context('main');
|
||||
const solver = new Solver();
|
||||
|
||||
const sort = Int.sort();
|
||||
|
@ -119,14 +119,14 @@ describe('high-level', () => {
|
|||
const g = Function.declare('g', sort, sort);
|
||||
const conjecture = Implies(x.eq(y), g.call(g.call(x)).eq(g.call(y)));
|
||||
solver.add(Not(conjecture));
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
});
|
||||
|
||||
it('checks that Context matches', () => {
|
||||
const c1 = new api.Context('context');
|
||||
const c2 = new api.Context('context');
|
||||
const c3 = new api.Context('foo');
|
||||
const c4 = new api.Context('bar');
|
||||
const c1 = api.Context('context');
|
||||
const c2 = api.Context('context');
|
||||
const c3 = api.Context('foo');
|
||||
const c4 = api.Context('bar');
|
||||
|
||||
// Contexts with the same name don't do type checking during compile time.
|
||||
// We need to check for different context dynamically
|
||||
|
@ -144,7 +144,7 @@ describe('high-level', () => {
|
|||
|
||||
describe('booleans', () => {
|
||||
it("proves De Morgan's Law", async () => {
|
||||
const { Bool, Not, And, Eq, Or } = new api.Context('main');
|
||||
const { Bool, Not, And, Eq, Or } = api.Context('main');
|
||||
const [x, y] = [Bool.const('x'), Bool.const('y')];
|
||||
|
||||
const conjecture = Eq(Not(And(x, y)), Or(Not(x), Not(y)));
|
||||
|
@ -155,7 +155,7 @@ describe('high-level', () => {
|
|||
|
||||
describe('ints', () => {
|
||||
it('finds a model', async () => {
|
||||
const { Solver, Int, isIntVal } = new api.Context('main');
|
||||
const { Solver, Int, isIntVal } = api.Context('main');
|
||||
const solver = new Solver();
|
||||
const x = Int.const('x');
|
||||
const y = Int.const('y');
|
||||
|
@ -163,10 +163,10 @@ describe('high-level', () => {
|
|||
solver.add(x.ge(1)); // x >= 1
|
||||
solver.add(y.lt(x.add(3))); // y < x + 3
|
||||
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
|
||||
const model = solver.model();
|
||||
expect(model.length).toStrictEqual(2);
|
||||
expect(model.length()).toStrictEqual(2);
|
||||
|
||||
for (const decl of model) {
|
||||
expect(decl.arity()).toStrictEqual(0);
|
||||
|
@ -175,8 +175,8 @@ describe('high-level', () => {
|
|||
const yValueExpr = model.get(y);
|
||||
assert(isIntVal(xValueExpr));
|
||||
assert(isIntVal(yValueExpr));
|
||||
const xValue = xValueExpr.value;
|
||||
const yValue = yValueExpr.value;
|
||||
const xValue = xValueExpr.value();
|
||||
const yValue = yValueExpr.value();
|
||||
assert(typeof xValue === 'bigint');
|
||||
assert(typeof yValue === 'bigint');
|
||||
expect(xValue).toBeGreaterThanOrEqual(1n);
|
||||
|
@ -225,7 +225,7 @@ describe('high-level', () => {
|
|||
541972386
|
||||
`);
|
||||
|
||||
const { Solver, Int, Distinct, isIntVal } = new api.Context('main');
|
||||
const { Solver, Int, Distinct, isIntVal } = api.Context('main');
|
||||
|
||||
const cells: Arith[][] = [];
|
||||
// 9x9 matrix of integer variables
|
||||
|
@ -284,7 +284,7 @@ describe('high-level', () => {
|
|||
}
|
||||
}
|
||||
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
|
||||
const model = solver.model();
|
||||
const result = [];
|
||||
|
@ -293,7 +293,7 @@ describe('high-level', () => {
|
|||
for (let j = 0; j < 9; j++) {
|
||||
const cell = model.eval(cells[i][j]);
|
||||
assert(isIntVal(cell));
|
||||
const value = cell.value;
|
||||
const value = cell.value();
|
||||
assert(typeof value === 'bigint');
|
||||
expect(value).toBeGreaterThanOrEqual(0n);
|
||||
expect(value).toBeLessThanOrEqual(9n);
|
||||
|
@ -308,7 +308,7 @@ describe('high-level', () => {
|
|||
|
||||
describe('reals', () => {
|
||||
it('can work with numerals', async () => {
|
||||
const { Real, And } = new api.Context('main');
|
||||
const { Real, And } = api.Context('main');
|
||||
const n1 = Real.val('1/2');
|
||||
const n2 = Real.val('0.5');
|
||||
const n3 = Real.val(0.5);
|
||||
|
@ -322,7 +322,7 @@ describe('high-level', () => {
|
|||
it('can do non-linear arithmetic', async () => {
|
||||
api.setParam('pp.decimal', true);
|
||||
api.setParam('pp.decimal_precision', 20);
|
||||
const { Real, Solver, isReal, isRealVal } = new api.Context('main');
|
||||
const { Real, Solver, isReal, isRealVal } = api.Context('main');
|
||||
const x = Real.const('x');
|
||||
const y = Real.const('y');
|
||||
const z = Real.const('z');
|
||||
|
@ -331,7 +331,7 @@ describe('high-level', () => {
|
|||
solver.add(x.mul(x).add(y.mul(y)).eq(1)); // x^2 + y^2 == 1
|
||||
solver.add(x.mul(x).mul(x).add(z.mul(z).mul(z)).lt('1/2')); // x^3 + z^3 < 1/2
|
||||
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
const model = solver.model();
|
||||
|
||||
expect(isRealVal(model.get(x))).toStrictEqual(true);
|
||||
|
@ -344,7 +344,7 @@ describe('high-level', () => {
|
|||
|
||||
describe('bitvectors', () => {
|
||||
it('can do simple proofs', async () => {
|
||||
const { BitVec, Concat, Implies, isBitVecVal } = new api.Context('main');
|
||||
const { BitVec, Concat, Implies, isBitVecVal } = api.Context('main');
|
||||
|
||||
const x = BitVec.const('x', 32);
|
||||
|
||||
|
@ -354,7 +354,7 @@ describe('high-level', () => {
|
|||
assert(isBitVecVal(sSol) && isBitVecVal(uSol));
|
||||
let v = sSol.asSignedValue();
|
||||
expect(v - 10n <= 0n === v <= 10n).toStrictEqual(true);
|
||||
v = uSol.value;
|
||||
v = uSol.value();
|
||||
expect(v - 10n <= 0n === v <= 10n).toStrictEqual(true);
|
||||
|
||||
const y = BitVec.const('y', 32);
|
||||
|
@ -363,7 +363,7 @@ describe('high-level', () => {
|
|||
});
|
||||
|
||||
it('finds x and y such that: x ^ y - 103 == x * y', async () => {
|
||||
const { BitVec, isBitVecVal } = new api.Context('main');
|
||||
const { BitVec, isBitVecVal } = api.Context('main');
|
||||
|
||||
const x = BitVec.const('x', 32);
|
||||
const y = BitVec.const('y', 32);
|
||||
|
@ -382,28 +382,28 @@ describe('high-level', () => {
|
|||
|
||||
describe('Solver', () => {
|
||||
it('can use push and pop', async () => {
|
||||
const { Solver, Int } = new api.Context('main');
|
||||
const { Solver, Int } = api.Context('main');
|
||||
const solver = new Solver();
|
||||
const x = Int.const('x');
|
||||
|
||||
solver.add(x.gt(0));
|
||||
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
|
||||
solver.push();
|
||||
solver.add(x.lt(0));
|
||||
|
||||
expect(solver.numScopes()).toStrictEqual(1);
|
||||
expect(await solver.check()).toStrictEqual(unsat);
|
||||
expect(await solver.check()).toStrictEqual('unsat');
|
||||
|
||||
solver.pop();
|
||||
|
||||
expect(solver.numScopes()).toStrictEqual(0);
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
});
|
||||
|
||||
it('can find multiple solutions', async () => {
|
||||
const { Int, isIntVal } = new api.Context('main');
|
||||
const { Int, isIntVal } = api.Context('main');
|
||||
|
||||
const x = Int.const('x');
|
||||
|
||||
|
@ -413,7 +413,7 @@ describe('high-level', () => {
|
|||
.map(solution => {
|
||||
const expr = solution.eval(x);
|
||||
assert(isIntVal(expr));
|
||||
return expr.value;
|
||||
return expr.value();
|
||||
})
|
||||
.sort((a, b) => {
|
||||
assert(a !== null && b !== null && typeof a === 'bigint' && typeof b === 'bigint');
|
||||
|
@ -431,7 +431,7 @@ describe('high-level', () => {
|
|||
|
||||
describe('AstVector', () => {
|
||||
it('can use basic methods', async () => {
|
||||
const { Solver, AstVector, Int } = new api.Context('main');
|
||||
const { Solver, AstVector, Int } = api.Context('main');
|
||||
const solver = new Solver();
|
||||
|
||||
const vector = new AstVector<Arith>();
|
||||
|
@ -439,12 +439,12 @@ describe('high-level', () => {
|
|||
vector.push(Int.const(`int__${i}`));
|
||||
}
|
||||
|
||||
const length = vector.length;
|
||||
const length = vector.length();
|
||||
for (let i = 0; i < length; i++) {
|
||||
solver.add(vector.get(i).gt(1));
|
||||
}
|
||||
|
||||
expect(await solver.check()).toStrictEqual(sat);
|
||||
expect(await solver.check()).toStrictEqual('sat');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,13 +16,13 @@ import {
|
|||
} from '../low-level';
|
||||
|
||||
/** @hidden */
|
||||
export type AnySort<Name extends string = any> =
|
||||
export type AnySort<Name extends string = 'main'> =
|
||||
| Sort<Name>
|
||||
| BoolSort<Name>
|
||||
| ArithSort<Name>
|
||||
| BitVecSort<number, Name>;
|
||||
/** @hidden */
|
||||
export type AnyExpr<Name extends string = any> =
|
||||
export type AnyExpr<Name extends string = 'main'> =
|
||||
| Expr<Name>
|
||||
| Bool<Name>
|
||||
| Arith<Name>
|
||||
|
@ -31,10 +31,10 @@ export type AnyExpr<Name extends string = any> =
|
|||
| BitVec<number, Name>
|
||||
| BitVecNum<number, Name>;
|
||||
/** @hidden */
|
||||
export type AnyAst<Name extends string = any> = AnyExpr<Name> | AnySort<Name> | FuncDecl<Name>;
|
||||
export type AnyAst<Name extends string = 'main'> = AnyExpr<Name> | AnySort<Name> | FuncDecl<Name>;
|
||||
|
||||
/** @hidden */
|
||||
export type SortToExprMap<S extends AnySort<Name>, Name extends string = any> = S extends BoolSort
|
||||
export type SortToExprMap<S extends AnySort<Name>, Name extends string = 'main'> = S extends BoolSort
|
||||
? Bool<Name>
|
||||
: S extends ArithSort<Name>
|
||||
? Arith<Name>
|
||||
|
@ -45,7 +45,7 @@ export type SortToExprMap<S extends AnySort<Name>, Name extends string = any> =
|
|||
: never;
|
||||
|
||||
/** @hidden */
|
||||
export type CoercibleToExprMap<S extends CoercibleToExpr<Name>, Name extends string = any> = S extends bigint
|
||||
export type CoercibleToExprMap<S extends CoercibleToExpr<Name>, Name extends string = 'main'> = S extends bigint
|
||||
? IntNum<Name>
|
||||
: S extends number | CoercibleRational
|
||||
? RatNum<Name>
|
||||
|
@ -76,38 +76,20 @@ export type CoercibleToExprMap<S extends CoercibleToExpr<Name>, Name extends str
|
|||
export type CoercibleRational = { numerator: bigint | number; denominator: bigint | number };
|
||||
|
||||
/** @hidden */
|
||||
export type CoercibleToExpr<Name extends string = any> = number | bigint | boolean | CoercibleRational | Expr<Name>;
|
||||
export type CoercibleToExpr<Name extends string = 'main'> = number | bigint | boolean | CoercibleRational | Expr<Name>;
|
||||
|
||||
export class Z3Error extends Error {}
|
||||
export class Z3AssertionError extends Z3Error {}
|
||||
|
||||
/**
|
||||
* Returned by {@link Solver.check} when Z3 could find a solution
|
||||
* @category Global
|
||||
*/
|
||||
export const sat = Symbol('Solver found a solution');
|
||||
/**
|
||||
* Returned by {@link Solver.check} when Z3 couldn't find a solution
|
||||
* @category Global
|
||||
*/
|
||||
export const unsat = Symbol("Solver didn't find a solution");
|
||||
/**
|
||||
* Returned by {@link Solver.check} when Z3 couldn't reason about the assumptions
|
||||
* @category Global
|
||||
*/
|
||||
export const unknown = Symbol("Solver couldn't reason about the assumptions");
|
||||
/** @category Global */
|
||||
export type CheckSatResult = typeof sat | typeof unsat | typeof unknown;
|
||||
export type CheckSatResult = 'sat' | 'unsat' | 'unknown';
|
||||
|
||||
/** @hidden */
|
||||
export interface ContextCtor {
|
||||
new <Name extends string>(name: Name, options?: Record<string, any>): Context<Name>;
|
||||
<Name extends string>(name: Name, options?: Record<string, any>): Context<Name>;
|
||||
}
|
||||
|
||||
export interface Context<Name extends string = any> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Context';
|
||||
|
||||
export interface Context<Name extends string = 'main'> {
|
||||
/** @hidden */
|
||||
readonly ptr: Z3_context;
|
||||
/**
|
||||
|
@ -190,7 +172,7 @@ export interface Context<Name extends string = any> {
|
|||
/** @category Functions */
|
||||
isTactic(obj: unknown): obj is Tactic<Name>;
|
||||
/** @category Functions */
|
||||
isAstVector(obj: unknown): obj is AstVector<AnyAst<Name>, Name>;
|
||||
isAstVector(obj: unknown): obj is AstVector<Name, AnyAst<Name>>;
|
||||
/**
|
||||
* Returns whether two Asts are the same thing
|
||||
* @category Functions */
|
||||
|
@ -232,7 +214,7 @@ export interface Context<Name extends string = any> {
|
|||
*
|
||||
* @see {@link Solver}
|
||||
* @category Functions */
|
||||
solve(...assertions: Bool[]): Promise<Model | typeof unsat | typeof unknown>;
|
||||
solve(...assertions: Bool<Name>[]): Promise<Model<Name> | 'unsat' | 'unknown'>;
|
||||
|
||||
/////////////
|
||||
// Classes //
|
||||
|
@ -250,9 +232,9 @@ export interface Context<Name extends string = any> {
|
|||
*/
|
||||
readonly Model: new () => Model<Name>;
|
||||
/** @category Classes */
|
||||
readonly AstVector: new <Item extends Ast<Name> = AnyAst<Name>>() => AstVector<Item, Name>;
|
||||
readonly AstVector: new <Item extends Ast<Name> = AnyAst<Name>>() => AstVector<Name, Item>;
|
||||
/** @category Classes */
|
||||
readonly AstMap: new <Key extends Ast = AnyAst, Value extends Ast = AnyAst>() => AstMap<Key, Value, Name>;
|
||||
readonly AstMap: new <Key extends Ast<Name> = AnyAst<Name>, Value extends Ast<Name> = AnyAst<Name>>() => AstMap<Name, Key, Value>;
|
||||
/** @category Classes */
|
||||
readonly Tactic: new (name: string) => Tactic<Name>;
|
||||
|
||||
|
@ -309,7 +291,7 @@ export interface Context<Name extends string = any> {
|
|||
/** @category Operations */
|
||||
And(): Bool<Name>;
|
||||
/** @category Operations */
|
||||
And(vector: AstVector<Bool<Name>, Name>): Bool<Name>;
|
||||
And(vector: AstVector<Name, Bool<Name>>): Bool<Name>;
|
||||
/** @category Operations */
|
||||
And(...args: (Bool<Name> | boolean)[]): Bool<Name>;
|
||||
/** @category Operations */
|
||||
|
@ -317,7 +299,7 @@ export interface Context<Name extends string = any> {
|
|||
/** @category Operations */
|
||||
Or(): Bool<Name>;
|
||||
/** @category Operations */
|
||||
Or(vector: AstVector<Bool<Name>, Name>): Bool<Name>;
|
||||
Or(vector: AstVector<Name, Bool<Name>>): Bool<Name>;
|
||||
/** @category Operations */
|
||||
Or(...args: (Bool<Name> | boolean)[]): Bool<Name>;
|
||||
/** @category Operations */
|
||||
|
@ -368,9 +350,11 @@ export interface Context<Name extends string = any> {
|
|||
Int2BV<Bits extends number>(a: Arith<Name> | bigint | number, bits: Bits): BitVec<Bits, Name>;
|
||||
/** @category Operations */
|
||||
Concat(...bitvecs: BitVec<number, Name>[]): BitVec<number, Name>;
|
||||
/** @category Operations */
|
||||
Cond(probe: Probe<Name>, onTrue: Tactic<Name>, onFalse: Tactic<Name>): Tactic<Name>
|
||||
}
|
||||
|
||||
export interface Ast<Name extends string = any, Ptr = unknown> {
|
||||
export interface Ast<Name extends string = 'main', Ptr = unknown> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Ast' | Sort['__typename'] | FuncDecl['__typename'] | Expr['__typename'];
|
||||
|
||||
|
@ -380,7 +364,7 @@ export interface Ast<Name extends string = any, Ptr = unknown> {
|
|||
/** @virtual */
|
||||
get ast(): Z3_ast;
|
||||
/** @virtual */
|
||||
get id(): number;
|
||||
id(): number;
|
||||
|
||||
eqIdentity(other: Ast<Name>): boolean;
|
||||
neqIdentity(other: Ast<Name>): boolean;
|
||||
|
@ -392,7 +376,7 @@ export interface Ast<Name extends string = any, Ptr = unknown> {
|
|||
export interface SolverCtor<Name extends string> {
|
||||
new (): Solver<Name>;
|
||||
}
|
||||
export interface Solver<Name extends string = any> {
|
||||
export interface Solver<Name extends string = 'main'> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Solver';
|
||||
|
||||
|
@ -407,10 +391,10 @@ export interface Solver<Name extends string = any> {
|
|||
pop(num?: number): void;
|
||||
numScopes(): number;
|
||||
reset(): void;
|
||||
add(...exprs: (Bool<Name> | AstVector<Bool<Name>, Name>)[]): void;
|
||||
add(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]): void;
|
||||
addAndTrack(expr: Bool<Name>, constant: Bool<Name> | string): void;
|
||||
assertions(): AstVector<Bool<Name>, Name>;
|
||||
check(...exprs: (Bool<Name> | AstVector<Bool<Name>, Name>)[]): Promise<CheckSatResult>;
|
||||
assertions(): AstVector<Name, Bool<Name>>;
|
||||
check(...exprs: (Bool<Name> | AstVector<Name, Bool<Name>>)[]): Promise<CheckSatResult>;
|
||||
model(): Model<Name>;
|
||||
}
|
||||
|
||||
|
@ -418,14 +402,14 @@ export interface Solver<Name extends string = any> {
|
|||
export interface ModelCtor<Name extends string> {
|
||||
new (): Model<Name>;
|
||||
}
|
||||
export interface Model<Name extends string = any> extends Iterable<FuncDecl<Name>> {
|
||||
export interface Model<Name extends string = 'main'> extends Iterable<FuncDecl<Name>> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Model';
|
||||
|
||||
readonly ctx: Context<Name>;
|
||||
readonly ptr: Z3_model;
|
||||
|
||||
get length(): number;
|
||||
length(): number;
|
||||
|
||||
entries(): IterableIterator<[number, FuncDecl<Name>]>;
|
||||
keys(): IterableIterator<number>;
|
||||
|
@ -436,10 +420,10 @@ export interface Model<Name extends string = any> extends Iterable<FuncDecl<Name
|
|||
eval(expr: Arith<Name>, modelCompletion?: boolean): Arith<Name>;
|
||||
eval(expr: Expr<Name>, modelCompletion?: boolean): Expr<Name>;
|
||||
get(i: number): FuncDecl<Name>;
|
||||
get(from: number, to: number): FuncDecl[];
|
||||
get(from: number, to: number): FuncDecl<Name>[];
|
||||
get(declaration: FuncDecl<Name>): FuncInterp<Name> | Expr<Name>;
|
||||
get(constant: Expr<Name>): Expr<Name>;
|
||||
get(sort: Sort<Name>): AstVector<AnyExpr<Name>, Name>;
|
||||
get(sort: Sort<Name>): AstVector<Name, AnyExpr<Name>>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -461,7 +445,7 @@ export interface Model<Name extends string = any> extends Iterable<FuncDecl<Name
|
|||
export interface SortCreation<Name extends string> {
|
||||
declare(name: string): Sort<Name>;
|
||||
}
|
||||
export interface Sort<Name extends string = any> extends Ast<Name, Z3_sort> {
|
||||
export interface Sort<Name extends string = 'main'> extends Ast<Name, Z3_sort> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Sort' | BoolSort['__typename'] | ArithSort['__typename'] | BitVecSort['__typename'];
|
||||
|
||||
|
@ -476,7 +460,7 @@ export interface Sort<Name extends string = any> extends Ast<Name, Z3_sort> {
|
|||
/**
|
||||
* @category Functions
|
||||
*/
|
||||
export interface FuncInterp<Name extends string = any> {
|
||||
export interface FuncInterp<Name extends string = 'main'> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'FuncInterp';
|
||||
|
||||
|
@ -516,7 +500,7 @@ export interface RecFuncCreation<Name extends string> {
|
|||
/**
|
||||
* @category Functions
|
||||
*/
|
||||
export interface FuncDecl<Name extends string = any> extends Ast<Name, Z3_func_decl> {
|
||||
export interface FuncDecl<Name extends string = 'main'> extends Ast<Name, Z3_func_decl> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'FuncDecl';
|
||||
|
||||
|
@ -529,7 +513,7 @@ export interface FuncDecl<Name extends string = any> extends Ast<Name, Z3_func_d
|
|||
call(...args: CoercibleToExpr<Name>[]): AnyExpr<Name>;
|
||||
}
|
||||
|
||||
export interface Expr<Name extends string = any, S extends Sort<Name> = AnySort<Name>, Ptr = unknown>
|
||||
export interface Expr<Name extends string = 'main', S extends Sort<Name> = AnySort<Name>, Ptr = unknown>
|
||||
extends Ast<Name, Ptr> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Expr' | Bool['__typename'] | Arith['__typename'] | BitVec['__typename'];
|
||||
|
@ -546,7 +530,7 @@ export interface Expr<Name extends string = any, S extends Sort<Name> = AnySort<
|
|||
}
|
||||
|
||||
/** @category Booleans */
|
||||
export interface BoolSort<Name extends string = any> extends Sort<Name> {
|
||||
export interface BoolSort<Name extends string = 'main'> extends Sort<Name> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'BoolSort';
|
||||
|
||||
|
@ -554,7 +538,7 @@ export interface BoolSort<Name extends string = any> extends Sort<Name> {
|
|||
cast(expr: CoercibleToExpr<Name>): never;
|
||||
}
|
||||
/** @category Booleans */
|
||||
export interface BoolCreation<Name extends string = any> {
|
||||
export interface BoolCreation<Name extends string = 'main'> {
|
||||
sort(): BoolSort<Name>;
|
||||
|
||||
const(name: string): Bool<Name>;
|
||||
|
@ -565,7 +549,7 @@ export interface BoolCreation<Name extends string = any> {
|
|||
val(value: boolean): Bool<Name>;
|
||||
}
|
||||
/** @category Booleans */
|
||||
export interface Bool<Name extends string = any> extends Expr<Name, BoolSort<Name>, Z3_ast> {
|
||||
export interface Bool<Name extends string = 'main'> extends Expr<Name, BoolSort<Name>, Z3_ast> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Bool';
|
||||
|
||||
|
@ -579,7 +563,7 @@ export interface Bool<Name extends string = any> extends Expr<Name, BoolSort<Nam
|
|||
* A Sort that represents Integers or Real numbers
|
||||
* @category Arithmetic
|
||||
*/
|
||||
export interface ArithSort<Name extends string = any> extends Sort<Name> {
|
||||
export interface ArithSort<Name extends string = 'main'> extends Sort<Name> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'ArithSort';
|
||||
|
||||
|
@ -615,7 +599,7 @@ export interface RealCreation<Name extends string> {
|
|||
* Represents Integer or Real number expression
|
||||
* @category Arithmetic
|
||||
*/
|
||||
export interface Arith<Name extends string = any> extends Expr<Name, ArithSort<Name>, Z3_ast> {
|
||||
export interface Arith<Name extends string = 'main'> extends Expr<Name, ArithSort<Name>, Z3_ast> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Arith' | IntNum['__typename'] | RatNum['__typename'];
|
||||
|
||||
|
@ -683,11 +667,11 @@ export interface Arith<Name extends string = any> extends Expr<Name, ArithSort<N
|
|||
* A constant Integer value expression
|
||||
* @category Arithmetic
|
||||
*/
|
||||
export interface IntNum<Name extends string = any> extends Arith<Name> {
|
||||
export interface IntNum<Name extends string = 'main'> extends Arith<Name> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'IntNum';
|
||||
|
||||
get value(): bigint;
|
||||
value(): bigint;
|
||||
asString(): string;
|
||||
asBinary(): string;
|
||||
}
|
||||
|
@ -707,11 +691,11 @@ export interface IntNum<Name extends string = any> extends Arith<Name> {
|
|||
* ```
|
||||
* @category Arithmetic
|
||||
*/
|
||||
export interface RatNum<Name extends string = any> extends Arith<Name> {
|
||||
export interface RatNum<Name extends string = 'main'> extends Arith<Name> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'RatNum';
|
||||
|
||||
get value(): { numerator: bigint; denominator: bigint };
|
||||
value(): { numerator: bigint; denominator: bigint };
|
||||
numerator(): IntNum<Name>;
|
||||
denominator(): IntNum<Name>;
|
||||
asNumber(): number;
|
||||
|
@ -725,7 +709,7 @@ export interface RatNum<Name extends string = any> extends Arith<Name> {
|
|||
* @typeParam Bits - A number representing amount of bits for this sort
|
||||
* @category Bit Vectors
|
||||
*/
|
||||
export interface BitVecSort<Bits extends number = number, Name extends string = any> extends Sort<Name> {
|
||||
export interface BitVecSort<Bits extends number = number, Name extends string = 'main'> extends Sort<Name> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'BitVecSort';
|
||||
|
||||
|
@ -739,14 +723,14 @@ export interface BitVecSort<Bits extends number = number, Name extends string =
|
|||
* // 32
|
||||
* ```
|
||||
*/
|
||||
get size(): Bits;
|
||||
size(): Bits;
|
||||
|
||||
cast(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name>;
|
||||
cast(other: CoercibleToExpr<Name>): Expr<Name>;
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
export type CoercibleToBitVec<Bits extends number = number, Name extends string = any> =
|
||||
export type CoercibleToBitVec<Bits extends number = number, Name extends string = 'main'> =
|
||||
| bigint
|
||||
| number
|
||||
| BitVec<Bits, Name>;
|
||||
|
@ -769,7 +753,7 @@ export interface BitVecCreation<Name extends string> {
|
|||
* Represents Bit Vector expression
|
||||
* @category Bit Vectors
|
||||
*/
|
||||
export interface BitVec<Bits extends number = number, Name extends string = any>
|
||||
export interface BitVec<Bits extends number = number, Name extends string = 'main'>
|
||||
extends Expr<Name, BitVecSort<Bits, Name>, Z3_ast> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'BitVec' | BitVecNum['__typename'];
|
||||
|
@ -790,7 +774,7 @@ export interface BitVec<Bits extends number = number, Name extends string = any>
|
|||
* // 8
|
||||
* ```
|
||||
*/
|
||||
get size(): Bits;
|
||||
size(): Bits;
|
||||
|
||||
/** @category Arithmetic */
|
||||
add(other: CoercibleToBitVec<Bits, Name>): BitVec<Bits, Name>;
|
||||
|
@ -959,17 +943,17 @@ export interface BitVec<Bits extends number = number, Name extends string = any>
|
|||
* Represents Bit Vector constant value
|
||||
* @category Bit Vectors
|
||||
*/
|
||||
export interface BitVecNum<Bits extends number = number, Name extends string = any> extends BitVec<Bits, Name> {
|
||||
export interface BitVecNum<Bits extends number = number, Name extends string = 'main'> extends BitVec<Bits, Name> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'BitVecNum';
|
||||
|
||||
get value(): bigint;
|
||||
value(): bigint;
|
||||
asSignedValue(): bigint;
|
||||
asString(): string;
|
||||
asBinaryString(): string;
|
||||
}
|
||||
|
||||
export interface Probe<Name extends string = any> {
|
||||
export interface Probe<Name extends string = 'main'> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Probe';
|
||||
|
||||
|
@ -981,7 +965,7 @@ export interface Probe<Name extends string = any> {
|
|||
export interface TacticCtor<Name extends string> {
|
||||
new (name: string): Tactic<Name>;
|
||||
}
|
||||
export interface Tactic<Name extends string = any> {
|
||||
export interface Tactic<Name extends string = 'main'> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'Tactic';
|
||||
|
||||
|
@ -991,7 +975,7 @@ export interface Tactic<Name extends string = any> {
|
|||
|
||||
/** @hidden */
|
||||
export interface AstVectorCtor<Name extends string> {
|
||||
new <Item extends Ast<Name> = AnyAst<Name>>(): AstVector<Item, Name>;
|
||||
new <Item extends Ast<Name> = AnyAst<Name>>(): AstVector<Name, Item>;
|
||||
}
|
||||
/**
|
||||
* Stores multiple {@link Ast} objects
|
||||
|
@ -1009,13 +993,13 @@ export interface AstVectorCtor<Name extends string> {
|
|||
* // [2, x]
|
||||
* ```
|
||||
*/
|
||||
export interface AstVector<Item extends Ast<Name> = AnyAst, Name extends string = any> extends Iterable<Item> {
|
||||
export interface AstVector<Name extends string = 'main', Item extends Ast<Name> = AnyAst<Name>> extends Iterable<Item> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'AstVector';
|
||||
|
||||
readonly ctx: Context<Name>;
|
||||
readonly ptr: Z3_ast_vector;
|
||||
get length(): number;
|
||||
length(): number;
|
||||
|
||||
entries(): IterableIterator<[number, Item]>;
|
||||
keys(): IterableIterator<number>;
|
||||
|
@ -1031,7 +1015,7 @@ export interface AstVector<Item extends Ast<Name> = AnyAst, Name extends string
|
|||
|
||||
/** @hidden */
|
||||
export interface AstMapCtor<Name extends string> {
|
||||
new <Key extends Ast = AnyAst, Value extends Ast = AnyAst>(): AstMap<Key, Value, Name>;
|
||||
new <Key extends Ast<Name> = AnyAst<Name>, Value extends Ast<Name> = AnyAst<Name>>(): AstMap<Name, Key, Value>;
|
||||
}
|
||||
/**
|
||||
* Stores a mapping between different {@link Ast} objects
|
||||
|
@ -1054,7 +1038,7 @@ export interface AstMapCtor<Name extends string> {
|
|||
* // 0
|
||||
* ```
|
||||
*/
|
||||
export interface AstMap<Key extends Ast<Name> = AnyAst, Value extends Ast = AnyAst, Name extends string = any>
|
||||
export interface AstMap<Name extends string = 'main', Key extends Ast<Name> = AnyAst<Name>, Value extends Ast<Name> = AnyAst<Name>>
|
||||
extends Iterable<[Key, Value]> {
|
||||
/** @hidden */
|
||||
readonly __typename: 'AstMap';
|
||||
|
@ -1064,7 +1048,7 @@ export interface AstMap<Key extends Ast<Name> = AnyAst, Value extends Ast = AnyA
|
|||
get size(): number;
|
||||
|
||||
entries(): IterableIterator<[Key, Value]>;
|
||||
keys(): AstVector<Key, Name>;
|
||||
keys(): AstVector<Name, Key>;
|
||||
values(): IterableIterator<Value>;
|
||||
get(key: Key): Value | undefined;
|
||||
set(key: Key, value: Value): void;
|
||||
|
@ -1119,11 +1103,6 @@ export interface Z3HighLevel {
|
|||
*/
|
||||
getParam(name: string): string | null;
|
||||
|
||||
/**
|
||||
* Returns whether the given object is a {@link Context}
|
||||
*/
|
||||
isContext(obj: unknown): obj is Context;
|
||||
|
||||
/**
|
||||
* Use this to create new contexts
|
||||
* @see {@link Context}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Z3AssertionError } from './types';
|
||||
import { allSatisfy, assert, assertExhaustive, autoBind } from './utils';
|
||||
import { allSatisfy, assert, assertExhaustive } from './utils';
|
||||
|
||||
describe('allSatisfy', () => {
|
||||
it('returns null on empty array', () => {
|
||||
|
@ -56,28 +56,6 @@ describe('assertExhaustive', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('autoBind', () => {
|
||||
class Binded {
|
||||
readonly name = 'Richard';
|
||||
constructor(shouldBind: boolean) {
|
||||
if (shouldBind === true) {
|
||||
autoBind(this);
|
||||
}
|
||||
}
|
||||
|
||||
test(): string {
|
||||
return `Hello ${this.name}`;
|
||||
}
|
||||
}
|
||||
|
||||
it('binds this', () => {
|
||||
const { test: withoutBind } = new Binded(false);
|
||||
const { test: withBind } = new Binded(true);
|
||||
expect(() => withoutBind()).toThrowError(TypeError);
|
||||
expect(withBind()).toStrictEqual('Hello Richard');
|
||||
});
|
||||
});
|
||||
|
||||
describe('assert', () => {
|
||||
it('throws on failure', () => {
|
||||
expect(() => assert(false)).toThrowError(Z3AssertionError);
|
||||
|
|
|
@ -10,32 +10,6 @@ function getAllProperties(obj: Record<string, any>) {
|
|||
return properties;
|
||||
}
|
||||
|
||||
// https://github.com/sindresorhus/auto-bind
|
||||
// We modify it to use CommonJS instead of ESM
|
||||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
export function autoBind<Self extends Record<string | symbol, any>>(self: Self): Self {
|
||||
for (const [obj, key] of getAllProperties(self.constructor.prototype)) {
|
||||
if (key === 'constructor') {
|
||||
continue;
|
||||
}
|
||||
const descriptor = Reflect.getOwnPropertyDescriptor(obj, key);
|
||||
if (descriptor && typeof descriptor.value === 'function') {
|
||||
(self[key] as any) = self[key].bind(self);
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use to ensure that switches are checked to be exhaustive at compile time
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue