pabst-checker 0.12.1 → 0.13.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/README.md +8 -5
- package/dist/prefix-parser.js +33 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,8 +10,9 @@ non-zero provided the second argument is an integer. Can you spot the error?
|
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
/**
|
|
13
|
-
* @ensures{nonzero} forall (x: bigint) (y: number)
|
|
13
|
+
* @ensures{nonzero} forall (x: bigint) (y: number) {
|
|
14
14
|
* Number.isInteger(y) ==> foo(x, y) !== 0
|
|
15
|
+
* }
|
|
15
16
|
*/
|
|
16
17
|
export function foo(x: bigint, y: number): number {
|
|
17
18
|
return Number(x % 2n) + (y % 2) + 1;
|
|
@@ -144,14 +145,16 @@ spelling of an equation is a plain `Object.is` call).
|
|
|
144
145
|
|
|
145
146
|
```ts
|
|
146
147
|
/**
|
|
147
|
-
* @ensures{guarded} forall (x: int)
|
|
148
|
+
* @ensures{guarded} forall (x: int) {
|
|
148
149
|
* isPrime(x) ∧ x > 2 → isOdd(x)
|
|
150
|
+
* }
|
|
149
151
|
*/
|
|
150
152
|
```
|
|
151
153
|
|
|
152
|
-
- **Quantifier:** `forall` / `∀`, one-or-more binder groups, then
|
|
153
|
-
|
|
154
|
-
`exists` is intentionally rejected (PBT
|
|
154
|
+
- **Quantifier:** `forall` / `∀`, one-or-more binder groups, then the body
|
|
155
|
+
in braces: `forall (x: int) { ... }`. Lean-style grouping `(x y: int)` is
|
|
156
|
+
supported. Existential `∃` / `exists` is intentionally rejected (PBT
|
|
157
|
+
cannot soundly confirm existence).
|
|
155
158
|
- **Domains:** `int`, `nat`, `number`, `boolean`, `string`, `bigint`.
|
|
156
159
|
A numeric domain (`int`, `nat`, `number`, `bigint`) may be constrained to
|
|
157
160
|
an interval: `forall (x: int ∈ [1, 30])` (ASCII fallback: `in`). Each
|
package/dist/prefix-parser.js
CHANGED
|
@@ -27,12 +27,21 @@ export function parsePrefix(formula) {
|
|
|
27
27
|
if (binders.length === 0) {
|
|
28
28
|
throw new PabstError(`expected at least one binder group '(x: domain)' after forall`);
|
|
29
29
|
}
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
const open = toks[i];
|
|
31
|
+
if (open && open.text === ",") {
|
|
32
|
+
throw new PabstError(`since pabst 0.13.0 the property body goes in braces instead of after ` +
|
|
33
|
+
`a comma: forall (x: int) { ... }`);
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
if (!open || open.text !== "{") {
|
|
36
|
+
const at = open?.start ?? formula.length;
|
|
37
|
+
throw new PabstError(`expected '{' to open the property body, got: ${formula.slice(at, at + 60)}`);
|
|
38
|
+
}
|
|
39
|
+
const close = braceExtent(toks, i, formula);
|
|
40
|
+
const after = toks[close + 1];
|
|
41
|
+
if (after) {
|
|
42
|
+
throw new PabstError(`unexpected text after the property body's closing '}': ${formula.slice(after.start, after.start + 60)}`);
|
|
43
|
+
}
|
|
44
|
+
const body = formula.slice(open.end, toks[close].start).trim();
|
|
36
45
|
if (body.length === 0)
|
|
37
46
|
throw new PabstError(`property body is empty`);
|
|
38
47
|
return { binders, body };
|
|
@@ -99,6 +108,25 @@ function groupExtent(toks, open, formula, unterminatedSlash) {
|
|
|
99
108
|
: "";
|
|
100
109
|
throw new PabstError(`unbalanced parentheses in binder group: ${formula.slice(toks[open].start)}${hint}`);
|
|
101
110
|
}
|
|
111
|
+
/** Index of the '}' matching the '{' at toks[open]. Only genuine brace
|
|
112
|
+
* tokens take part in the count: scanTokens packages string, regex, and
|
|
113
|
+
* template text into single tokens and rescans a substitution-closing '}'
|
|
114
|
+
* into a TemplateMiddle/TemplateTail, so every OpenBraceToken in the
|
|
115
|
+
* stream has a real CloseBraceToken partner. */
|
|
116
|
+
function braceExtent(toks, open, formula) {
|
|
117
|
+
let depth = 0;
|
|
118
|
+
for (let j = open; j < toks.length; j++) {
|
|
119
|
+
const kind = toks[j].kind;
|
|
120
|
+
if (kind === ts.SyntaxKind.OpenBraceToken)
|
|
121
|
+
depth++;
|
|
122
|
+
else if (kind === ts.SyntaxKind.CloseBraceToken) {
|
|
123
|
+
depth--;
|
|
124
|
+
if (depth === 0)
|
|
125
|
+
return j;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw new PabstError(`unbalanced braces in property body: ${formula.slice(toks[open].start)}`);
|
|
129
|
+
}
|
|
102
130
|
/** Index of the token closing a plausible two-endpoint interval starting at
|
|
103
131
|
* toks[at], or -1 when none starts there — the group scan then proceeds as
|
|
104
132
|
* usual and the guard text reaches parseRange for the precise complaint. */
|