frml 0.1.0__tar.gz
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.
- frml-0.1.0/PKG-INFO +258 -0
- frml-0.1.0/README.md +248 -0
- frml-0.1.0/frml/__init__.py +3 -0
- frml-0.1.0/frml/__main__.py +4 -0
- frml-0.1.0/frml/ast_nodes.py +347 -0
- frml-0.1.0/frml/cli.py +120 -0
- frml-0.1.0/frml/errors.py +61 -0
- frml-0.1.0/frml/interpreter.py +580 -0
- frml-0.1.0/frml/lexer.py +187 -0
- frml-0.1.0/frml/parser.py +428 -0
- frml-0.1.0/frml/prover.py +785 -0
- frml-0.1.0/frml/typechecker.py +484 -0
- frml-0.1.0/frml/types.py +49 -0
- frml-0.1.0/frml.egg-info/PKG-INFO +258 -0
- frml-0.1.0/frml.egg-info/SOURCES.txt +27 -0
- frml-0.1.0/frml.egg-info/dependency_links.txt +1 -0
- frml-0.1.0/frml.egg-info/entry_points.txt +2 -0
- frml-0.1.0/frml.egg-info/requires.txt +1 -0
- frml-0.1.0/frml.egg-info/top_level.txt +1 -0
- frml-0.1.0/pyproject.toml +33 -0
- frml-0.1.0/setup.cfg +4 -0
- frml-0.1.0/tests/test_cli.py +141 -0
- frml-0.1.0/tests/test_errors.py +50 -0
- frml-0.1.0/tests/test_interpreter.py +585 -0
- frml-0.1.0/tests/test_lexer.py +158 -0
- frml-0.1.0/tests/test_parser.py +98 -0
- frml-0.1.0/tests/test_prover.py +567 -0
- frml-0.1.0/tests/test_typechecker.py +353 -0
- frml-0.1.0/tests/test_types.py +62 -0
frml-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: frml
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A minimal contract-based verification language backed by Z3
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/gvwilson/mccole
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: z3-solver>=4.12.0
|
|
10
|
+
|
|
11
|
+
# Tiny
|
|
12
|
+
|
|
13
|
+
Tiny is a small, statically typed, contract-based programming language for
|
|
14
|
+
teaching formal verification. It feels like a simplified combination of Python
|
|
15
|
+
and Dafny: ordinary imperative programs, plus `requires` / `ensures` contracts,
|
|
16
|
+
`assert`, loop `invariant`s, `decreases` termination measures, `old(...)`, and
|
|
17
|
+
`forall` / `exists` quantifiers. Verification is fully automatic: Tiny
|
|
18
|
+
translates each program into verification conditions and asks the [Z3][z3] SMT
|
|
19
|
+
solver to check them. There are no interactive proof tactics and no handwritten
|
|
20
|
+
SMT formulas.
|
|
21
|
+
|
|
22
|
+
A program that verifies looks like this:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
fn abs(x: Int) -> Int
|
|
26
|
+
ensures result >= 0
|
|
27
|
+
{
|
|
28
|
+
if x >= 0 {
|
|
29
|
+
return x;
|
|
30
|
+
} else {
|
|
31
|
+
return -x;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
fn main() -> Int
|
|
36
|
+
{
|
|
37
|
+
return abs(-7);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Running the verifier prints `VERIFIED`; running the program prints nothing and
|
|
42
|
+
uses `main`'s return value as the process exit code.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Tiny requires:
|
|
47
|
+
|
|
48
|
+
- Python 3.13 or newer, and
|
|
49
|
+
- the Python `z3` bindings (`z3-solver`).
|
|
50
|
+
|
|
51
|
+
### 1. Install the Z3 Python bindings
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv venv
|
|
55
|
+
source .venv/bin/activate
|
|
56
|
+
uv add z3-solver
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Install Tiny
|
|
60
|
+
|
|
61
|
+
From this directory:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
uv pip install -e .
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This installs a `tiny` command on your `PATH`. You can also run Tiny without
|
|
68
|
+
installing it, using the module form:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python3 -m tiny check examples/abs.tiny
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
Tiny exposes three subcommands.
|
|
77
|
+
|
|
78
|
+
### `tiny check FILE.tiny`
|
|
79
|
+
|
|
80
|
+
Parse, type-check, and verify the program. Prints `VERIFIED` on success, or
|
|
81
|
+
`FAILED`/`UNKNOWN` with source locations on failure.
|
|
82
|
+
|
|
83
|
+
### `tiny run FILE.tiny`
|
|
84
|
+
|
|
85
|
+
Type-check the program (without static verification) and execute `main()`. The
|
|
86
|
+
exit status is `main`'s return value.
|
|
87
|
+
|
|
88
|
+
### `tiny verify-run FILE.tiny`
|
|
89
|
+
|
|
90
|
+
Verify first, and then execute `main()` if verification succeeded.
|
|
91
|
+
|
|
92
|
+
## The language
|
|
93
|
+
|
|
94
|
+
### Types
|
|
95
|
+
|
|
96
|
+
Tiny has three basic types:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Int arbitrary-precision integers (no overflow)
|
|
100
|
+
Bool true or false
|
|
101
|
+
String a sequence of characters, written in double quotes
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Tiny supports homogeneous one-dimensional arrays:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
Array<Int> mutable, fixed-length, zero-indexed arrays
|
|
108
|
+
Array<Bool> mutable, fixed-length, zero-indexed arrays
|
|
109
|
+
Array<String> mutable, fixed-length, zero-indexed arrays of strings
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Nested arrays (`Array<Array<Int>>`) are not allowed, and Tiny does not (yet)
|
|
113
|
+
have a mapping (dictionary) type.
|
|
114
|
+
|
|
115
|
+
### Functions
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
fn max(a: Int, b: Int) -> Int
|
|
119
|
+
ensures result >= a
|
|
120
|
+
ensures result >= b
|
|
121
|
+
ensures result == a or result == b
|
|
122
|
+
{
|
|
123
|
+
if a >= b {
|
|
124
|
+
return a;
|
|
125
|
+
} else {
|
|
126
|
+
return b;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `requires` clauses are assumptions about the caller's inputs.
|
|
132
|
+
- `ensures` clauses are guarantees about the result.
|
|
133
|
+
- `result` names the return value.
|
|
134
|
+
- `old(e)` refers to the value of `e` at function entry.
|
|
135
|
+
- Multiple `requires`/`ensures` clauses are implicitly ANDed.
|
|
136
|
+
- A function may omit `-> TYPE`, making it a void procedure:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
fn increment_first(a: Array<Int>)
|
|
140
|
+
requires length(a) > 0
|
|
141
|
+
ensures a[0] == old(a[0]) + 1
|
|
142
|
+
{
|
|
143
|
+
a[0] = a[0] + 1;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- Recursive functions must declare a `decreases` measure.
|
|
148
|
+
|
|
149
|
+
### Statements
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
let NAME: TYPE = expr; // local variable declaration
|
|
153
|
+
NAME = expr; // scalar assignment
|
|
154
|
+
a[i] = expr; // array element assignment
|
|
155
|
+
if expr { ... } else { ... } // conditional (else optional)
|
|
156
|
+
while expr
|
|
157
|
+
invariant expr // loop invariants (optional)
|
|
158
|
+
decreases expr // termination measure (optional)
|
|
159
|
+
{ ... }
|
|
160
|
+
return expr; // (value-returning functions only)
|
|
161
|
+
assert expr; // runtime + static assertion
|
|
162
|
+
f(args); // procedure call statement
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Expressions
|
|
166
|
+
|
|
167
|
+
Operators, from lowest to highest precedence: `=>`, `or`, `and`, `==`/`!=`,
|
|
168
|
+
`<`/`<=`/`>`/`>=`, `+`/`-`/`++`, `*`/`/`/`%`, unary `!`/`-`/`` ` ``, then indexing
|
|
169
|
+
and calls.
|
|
170
|
+
|
|
171
|
+
`/` and `%` use Euclidean integer division compatible with Z3 (the remainder is
|
|
172
|
+
always non-negative). Division and modulo by zero are runtime errors and
|
|
173
|
+
verification obligations.
|
|
174
|
+
|
|
175
|
+
String literals are written in double quotes and support the usual C-style
|
|
176
|
+
backslash escapes (`\n`, `\t`, `\"`, `\\`, and so on). The `++` operator
|
|
177
|
+
concatenates two strings, and a single backtick before a value converts it to a
|
|
178
|
+
string (`5` is `"5"`, `x` is the string form of `x`'s value):
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
let greeting: String = "hello" ++ " " ++ "world";
|
|
182
|
+
let n: Int = 42;
|
|
183
|
+
let label: String = `n; // "42"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Quantifiers are available in specifications:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
forall i: Int :: 0 <= i and i < length(a) => a[i] >= 0
|
|
190
|
+
exists i: Int :: 0 <= i and i < length(a) and a[i] == 0
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`length(a)` is the single built-in function.
|
|
194
|
+
|
|
195
|
+
## Examples
|
|
196
|
+
|
|
197
|
+
The `examples/` directory contains small programs:
|
|
198
|
+
|
|
199
|
+
- `abs.tiny`, `max.tiny`, `count.tiny`: the core required examples.
|
|
200
|
+
- `required.tiny`: all three core examples plus a `main`.
|
|
201
|
+
- `factorial.tiny`: recursive function with `decreases`.
|
|
202
|
+
- `all_nonnegative.tiny`: array property proved with a quantifier loop invariant.
|
|
203
|
+
- `increment_first.tiny`: a procedure mutating an array, with `old`.
|
|
204
|
+
- `bad.tiny`: a deliberately unprovable postcondition (`FAILED`).
|
|
205
|
+
- `precondition.tiny`: a runtime precondition violation.
|
|
206
|
+
|
|
207
|
+
Try them:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
tiny check examples/required.tiny
|
|
211
|
+
tiny verify-run examples/factorial.tiny
|
|
212
|
+
tiny check examples/bad.tiny
|
|
213
|
+
tiny run examples/precondition.tiny
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Intentional limitations
|
|
217
|
+
|
|
218
|
+
These choices keep the verifier sound and the implementation small; they match
|
|
219
|
+
the specification's own "simplest recommended model":
|
|
220
|
+
|
|
221
|
+
Arrays are references, not values.
|
|
222
|
+
: `let b: Array<Int> = a;` (array-to-array assignment) is rejected to avoid
|
|
223
|
+
aliasing ambiguity. Create fresh arrays with array literals instead.
|
|
224
|
+
|
|
225
|
+
Functions return `Int` or `Bool`.
|
|
226
|
+
: Array-returning functions are not supported. Array mutation is expressed
|
|
227
|
+
with procedures, as shown earlier.
|
|
228
|
+
|
|
229
|
+
A call that takes array arguments must appear on its own.
|
|
230
|
+
: It may be a statement, or the whole right-hand side of, `let`, `return`, or
|
|
231
|
+
assignment. It may not be nested inside a larger expression (`f(a) + g(a)`
|
|
232
|
+
is rejected during verification).
|
|
233
|
+
|
|
234
|
+
Runtime quantifier checking is best-effort.
|
|
235
|
+
: Quantified postconditions over a finite array index range (`0 <= i and i <
|
|
236
|
+
length(a)`) are evaluated at runtime. Other quantified expressions are
|
|
237
|
+
skipped during runtime checking but still fully verified statically.
|
|
238
|
+
|
|
239
|
+
The verifier never claims success on a program it cannot prove. If Z3 cannot
|
|
240
|
+
decide an obligation it reports `UNKNOWN`, and a genuinely unprovable program is
|
|
241
|
+
reported as `FAILED`: it is never silently accepted.
|
|
242
|
+
|
|
243
|
+
## Project layout
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
tiny/
|
|
247
|
+
errors.py error types and source-location formatting
|
|
248
|
+
lexer.py tokenizer
|
|
249
|
+
parser.py recursive-descent parser -> AST
|
|
250
|
+
ast_nodes.py AST node definitions
|
|
251
|
+
types.py Int / Bool / Array<T> types
|
|
252
|
+
typechecker.py name resolution + static type checking
|
|
253
|
+
interpreter.py concrete executor with runtime checks
|
|
254
|
+
prover.py verification-condition generation + Z3 proof checking
|
|
255
|
+
cli.py the `tiny` command-line interface
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
[z3]: https://github.com/Z3Prover/z3
|
frml-0.1.0/README.md
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Tiny
|
|
2
|
+
|
|
3
|
+
Tiny is a small, statically typed, contract-based programming language for
|
|
4
|
+
teaching formal verification. It feels like a simplified combination of Python
|
|
5
|
+
and Dafny: ordinary imperative programs, plus `requires` / `ensures` contracts,
|
|
6
|
+
`assert`, loop `invariant`s, `decreases` termination measures, `old(...)`, and
|
|
7
|
+
`forall` / `exists` quantifiers. Verification is fully automatic: Tiny
|
|
8
|
+
translates each program into verification conditions and asks the [Z3][z3] SMT
|
|
9
|
+
solver to check them. There are no interactive proof tactics and no handwritten
|
|
10
|
+
SMT formulas.
|
|
11
|
+
|
|
12
|
+
A program that verifies looks like this:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
fn abs(x: Int) -> Int
|
|
16
|
+
ensures result >= 0
|
|
17
|
+
{
|
|
18
|
+
if x >= 0 {
|
|
19
|
+
return x;
|
|
20
|
+
} else {
|
|
21
|
+
return -x;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
fn main() -> Int
|
|
26
|
+
{
|
|
27
|
+
return abs(-7);
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Running the verifier prints `VERIFIED`; running the program prints nothing and
|
|
32
|
+
uses `main`'s return value as the process exit code.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Tiny requires:
|
|
37
|
+
|
|
38
|
+
- Python 3.13 or newer, and
|
|
39
|
+
- the Python `z3` bindings (`z3-solver`).
|
|
40
|
+
|
|
41
|
+
### 1. Install the Z3 Python bindings
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
uv venv
|
|
45
|
+
source .venv/bin/activate
|
|
46
|
+
uv add z3-solver
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. Install Tiny
|
|
50
|
+
|
|
51
|
+
From this directory:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
uv pip install -e .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This installs a `tiny` command on your `PATH`. You can also run Tiny without
|
|
58
|
+
installing it, using the module form:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
python3 -m tiny check examples/abs.tiny
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Usage
|
|
65
|
+
|
|
66
|
+
Tiny exposes three subcommands.
|
|
67
|
+
|
|
68
|
+
### `tiny check FILE.tiny`
|
|
69
|
+
|
|
70
|
+
Parse, type-check, and verify the program. Prints `VERIFIED` on success, or
|
|
71
|
+
`FAILED`/`UNKNOWN` with source locations on failure.
|
|
72
|
+
|
|
73
|
+
### `tiny run FILE.tiny`
|
|
74
|
+
|
|
75
|
+
Type-check the program (without static verification) and execute `main()`. The
|
|
76
|
+
exit status is `main`'s return value.
|
|
77
|
+
|
|
78
|
+
### `tiny verify-run FILE.tiny`
|
|
79
|
+
|
|
80
|
+
Verify first, and then execute `main()` if verification succeeded.
|
|
81
|
+
|
|
82
|
+
## The language
|
|
83
|
+
|
|
84
|
+
### Types
|
|
85
|
+
|
|
86
|
+
Tiny has three basic types:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Int arbitrary-precision integers (no overflow)
|
|
90
|
+
Bool true or false
|
|
91
|
+
String a sequence of characters, written in double quotes
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Tiny supports homogeneous one-dimensional arrays:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Array<Int> mutable, fixed-length, zero-indexed arrays
|
|
98
|
+
Array<Bool> mutable, fixed-length, zero-indexed arrays
|
|
99
|
+
Array<String> mutable, fixed-length, zero-indexed arrays of strings
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Nested arrays (`Array<Array<Int>>`) are not allowed, and Tiny does not (yet)
|
|
103
|
+
have a mapping (dictionary) type.
|
|
104
|
+
|
|
105
|
+
### Functions
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
fn max(a: Int, b: Int) -> Int
|
|
109
|
+
ensures result >= a
|
|
110
|
+
ensures result >= b
|
|
111
|
+
ensures result == a or result == b
|
|
112
|
+
{
|
|
113
|
+
if a >= b {
|
|
114
|
+
return a;
|
|
115
|
+
} else {
|
|
116
|
+
return b;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
- `requires` clauses are assumptions about the caller's inputs.
|
|
122
|
+
- `ensures` clauses are guarantees about the result.
|
|
123
|
+
- `result` names the return value.
|
|
124
|
+
- `old(e)` refers to the value of `e` at function entry.
|
|
125
|
+
- Multiple `requires`/`ensures` clauses are implicitly ANDed.
|
|
126
|
+
- A function may omit `-> TYPE`, making it a void procedure:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
fn increment_first(a: Array<Int>)
|
|
130
|
+
requires length(a) > 0
|
|
131
|
+
ensures a[0] == old(a[0]) + 1
|
|
132
|
+
{
|
|
133
|
+
a[0] = a[0] + 1;
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- Recursive functions must declare a `decreases` measure.
|
|
138
|
+
|
|
139
|
+
### Statements
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
let NAME: TYPE = expr; // local variable declaration
|
|
143
|
+
NAME = expr; // scalar assignment
|
|
144
|
+
a[i] = expr; // array element assignment
|
|
145
|
+
if expr { ... } else { ... } // conditional (else optional)
|
|
146
|
+
while expr
|
|
147
|
+
invariant expr // loop invariants (optional)
|
|
148
|
+
decreases expr // termination measure (optional)
|
|
149
|
+
{ ... }
|
|
150
|
+
return expr; // (value-returning functions only)
|
|
151
|
+
assert expr; // runtime + static assertion
|
|
152
|
+
f(args); // procedure call statement
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Expressions
|
|
156
|
+
|
|
157
|
+
Operators, from lowest to highest precedence: `=>`, `or`, `and`, `==`/`!=`,
|
|
158
|
+
`<`/`<=`/`>`/`>=`, `+`/`-`/`++`, `*`/`/`/`%`, unary `!`/`-`/`` ` ``, then indexing
|
|
159
|
+
and calls.
|
|
160
|
+
|
|
161
|
+
`/` and `%` use Euclidean integer division compatible with Z3 (the remainder is
|
|
162
|
+
always non-negative). Division and modulo by zero are runtime errors and
|
|
163
|
+
verification obligations.
|
|
164
|
+
|
|
165
|
+
String literals are written in double quotes and support the usual C-style
|
|
166
|
+
backslash escapes (`\n`, `\t`, `\"`, `\\`, and so on). The `++` operator
|
|
167
|
+
concatenates two strings, and a single backtick before a value converts it to a
|
|
168
|
+
string (`5` is `"5"`, `x` is the string form of `x`'s value):
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
let greeting: String = "hello" ++ " " ++ "world";
|
|
172
|
+
let n: Int = 42;
|
|
173
|
+
let label: String = `n; // "42"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Quantifiers are available in specifications:
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
forall i: Int :: 0 <= i and i < length(a) => a[i] >= 0
|
|
180
|
+
exists i: Int :: 0 <= i and i < length(a) and a[i] == 0
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`length(a)` is the single built-in function.
|
|
184
|
+
|
|
185
|
+
## Examples
|
|
186
|
+
|
|
187
|
+
The `examples/` directory contains small programs:
|
|
188
|
+
|
|
189
|
+
- `abs.tiny`, `max.tiny`, `count.tiny`: the core required examples.
|
|
190
|
+
- `required.tiny`: all three core examples plus a `main`.
|
|
191
|
+
- `factorial.tiny`: recursive function with `decreases`.
|
|
192
|
+
- `all_nonnegative.tiny`: array property proved with a quantifier loop invariant.
|
|
193
|
+
- `increment_first.tiny`: a procedure mutating an array, with `old`.
|
|
194
|
+
- `bad.tiny`: a deliberately unprovable postcondition (`FAILED`).
|
|
195
|
+
- `precondition.tiny`: a runtime precondition violation.
|
|
196
|
+
|
|
197
|
+
Try them:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
tiny check examples/required.tiny
|
|
201
|
+
tiny verify-run examples/factorial.tiny
|
|
202
|
+
tiny check examples/bad.tiny
|
|
203
|
+
tiny run examples/precondition.tiny
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Intentional limitations
|
|
207
|
+
|
|
208
|
+
These choices keep the verifier sound and the implementation small; they match
|
|
209
|
+
the specification's own "simplest recommended model":
|
|
210
|
+
|
|
211
|
+
Arrays are references, not values.
|
|
212
|
+
: `let b: Array<Int> = a;` (array-to-array assignment) is rejected to avoid
|
|
213
|
+
aliasing ambiguity. Create fresh arrays with array literals instead.
|
|
214
|
+
|
|
215
|
+
Functions return `Int` or `Bool`.
|
|
216
|
+
: Array-returning functions are not supported. Array mutation is expressed
|
|
217
|
+
with procedures, as shown earlier.
|
|
218
|
+
|
|
219
|
+
A call that takes array arguments must appear on its own.
|
|
220
|
+
: It may be a statement, or the whole right-hand side of, `let`, `return`, or
|
|
221
|
+
assignment. It may not be nested inside a larger expression (`f(a) + g(a)`
|
|
222
|
+
is rejected during verification).
|
|
223
|
+
|
|
224
|
+
Runtime quantifier checking is best-effort.
|
|
225
|
+
: Quantified postconditions over a finite array index range (`0 <= i and i <
|
|
226
|
+
length(a)`) are evaluated at runtime. Other quantified expressions are
|
|
227
|
+
skipped during runtime checking but still fully verified statically.
|
|
228
|
+
|
|
229
|
+
The verifier never claims success on a program it cannot prove. If Z3 cannot
|
|
230
|
+
decide an obligation it reports `UNKNOWN`, and a genuinely unprovable program is
|
|
231
|
+
reported as `FAILED`: it is never silently accepted.
|
|
232
|
+
|
|
233
|
+
## Project layout
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
tiny/
|
|
237
|
+
errors.py error types and source-location formatting
|
|
238
|
+
lexer.py tokenizer
|
|
239
|
+
parser.py recursive-descent parser -> AST
|
|
240
|
+
ast_nodes.py AST node definitions
|
|
241
|
+
types.py Int / Bool / Array<T> types
|
|
242
|
+
typechecker.py name resolution + static type checking
|
|
243
|
+
interpreter.py concrete executor with runtime checks
|
|
244
|
+
prover.py verification-condition generation + Z3 proof checking
|
|
245
|
+
cli.py the `tiny` command-line interface
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
[z3]: https://github.com/Z3Prover/z3
|