lawspec 0.6.0 → 0.8.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/API-MIGRATION.md +103 -0
- package/LANGUAGE.md +136 -0
- package/PRIMITIVES.md +150 -0
- package/README.md +55 -30
- package/REFINEMENTS.md +158 -0
- package/RUST.md +97 -0
- package/api.mjs +1 -1
- package/bin/lawspec.mjs +24 -35
- package/build.json +67 -11
- package/compatibility.json +102 -19
- package/core.wasm +0 -0
- package/doctor.mjs +31 -2
- package/examples/specs/algebra.lawspec +36 -8
- package/examples/specs/currying.lawspec +1 -1
- package/examples/specs/refinements.lawspec +86 -0
- package/examples/specs/scalar_adapters.lawspec +83 -0
- package/examples/specs/scalar_catalog.lawspec +166 -0
- package/examples/specs/scalars.lawspec +138 -0
- package/examples-command.mjs +2 -2
- package/index.d.ts +23 -13
- package/package.json +2 -2
- package/scalars.mjs +13 -0
- package/templates.mjs +25 -1
package/REFINEMENTS.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Refinements and abstract integers (0.8.0)
|
|
2
|
+
|
|
3
|
+
A refinement restricts a scalar domain with a pure Boolean expression. LawSpec
|
|
4
|
+
checks concrete examples, generates satisfying input tuples, and checks adapter
|
|
5
|
+
contracts during testing. It does not prove that an adapter is correct.
|
|
6
|
+
|
|
7
|
+
## Exact integers without a storage width
|
|
8
|
+
|
|
9
|
+
`Integer` is the mathematical integer domain. Integer literals default to it;
|
|
10
|
+
integer addition, subtraction, multiplication, negation, quotient, and remainder
|
|
11
|
+
produce it. Calculations use arbitrary precision internally. `BigInt` remains
|
|
12
|
+
available when a concrete arbitrary-precision adapter representation is wanted.
|
|
13
|
+
|
|
14
|
+
```lawspec
|
|
15
|
+
unit example.increment
|
|
16
|
+
successor :: (x :: Int8) -> (result :: Integer where result == x + 1)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This signature generates contract tests without a separate law. The maximum
|
|
20
|
+
input, `127`, requires the value `128`. Returning a wrapped `-128` fails.
|
|
21
|
+
`Integer` does not assert which storage width the implementation used.
|
|
22
|
+
|
|
23
|
+
Abstract integer results accept Python `int` (excluding `bool`), JavaScript or
|
|
24
|
+
TypeScript `bigint` and safe integral `number` values, Java/Kotlin standard signed
|
|
25
|
+
integral wrappers and `BigInteger` through `Number`, and Go signed/unsigned integer
|
|
26
|
+
values or `big.Int` values/pointers through `any`. Floating representations are
|
|
27
|
+
rejected even when their current value is integral. Haskell uses `IntegerValue`:
|
|
28
|
+
|
|
29
|
+
```haskell
|
|
30
|
+
successor :: Int8 -> IntegerValue
|
|
31
|
+
successor x = integerValue (toInteger x + 1)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`integerValue :: Integral a => a -> IntegerValue` erases the width losslessly.
|
|
35
|
+
Abstract integer arguments use the target's arbitrary-precision integer type.
|
|
36
|
+
Concrete signatures keep their existing checked native mappings.
|
|
37
|
+
|
|
38
|
+
## Inline and named refinements
|
|
39
|
+
|
|
40
|
+
Use `where` on a quantified input, function argument, or function result:
|
|
41
|
+
|
|
42
|
+
```lawspec
|
|
43
|
+
`for all` (x :: Int8) (y :: Int8 where y > Int8.max - x) .
|
|
44
|
+
add x y = x + y
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
A predicate can reference its own value and earlier inputs. A function result
|
|
48
|
+
predicate can also reference the function's named arguments. Forward value
|
|
49
|
+
references are errors. `Int8.min` and `Int8.max` denote representation bounds;
|
|
50
|
+
machine-sized bounds use the requested `machineBits` profile.
|
|
51
|
+
|
|
52
|
+
Named refinements distinguish type parameters from value parameters:
|
|
53
|
+
|
|
54
|
+
```lawspec
|
|
55
|
+
refinement Between
|
|
56
|
+
(T :: Type) (minimum :: T) (maximum :: T)
|
|
57
|
+
requires Ordered T is
|
|
58
|
+
(value :: T where minimum <= value && value <= maximum)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
refinement AdditionOverflows
|
|
62
|
+
(T :: Type) (left :: T)
|
|
63
|
+
requires Integer T Bounded T is
|
|
64
|
+
(right :: T where left + right > T.max)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
add :: (x :: Int8)
|
|
68
|
+
-> (y :: AdditionOverflows Int8 x)
|
|
69
|
+
-> (result :: Integer where result == x + y)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`(x :: Between Int8 1 10)` applies a refinement. Parenthesize compound arguments,
|
|
73
|
+
for example `Between Int8 (-10) (5 + 5)`. Later value-parameter types can use
|
|
74
|
+
earlier parameters. Value arguments must satisfy their declared domains,
|
|
75
|
+
including refinements. Substitution avoids capturing names from the call site.
|
|
76
|
+
Declarations can appear in any order within a unit; recursive aliases are errors.
|
|
77
|
+
Fixed refinements are the zero-parameter form:
|
|
78
|
+
|
|
79
|
+
```lawspec
|
|
80
|
+
refinement PositiveInt8 is (value :: Int8 where value > 0) end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Aliases preserve their underlying native representation. They can be nested in
|
|
84
|
+
`Nullable` and `Optional`; inner predicates are checked only for present values.
|
|
85
|
+
Type parameters range over the supported scalar types, including presence types.
|
|
86
|
+
|
|
87
|
+
`requires Integer T` describes integer capabilities in a generic declaration.
|
|
88
|
+
`Integer` entails `Eq` and `Ordered`. `Ordered` currently covers exact real numbers
|
|
89
|
+
and floats, with the existing IEEE comparisons (including NaN behavior).
|
|
90
|
+
`Bounded` covers fixed and machine-sized integers. Bounds refer to the underlying
|
|
91
|
+
integer representation, rather than a tighter interval inferred from a predicate.
|
|
92
|
+
Capabilities are checked on generic declarations and their specializations.
|
|
93
|
+
|
|
94
|
+
## Predicate expressions and contracts
|
|
95
|
+
|
|
96
|
+
Predicates support existing pure arithmetic, comparisons, scalar constructors,
|
|
97
|
+
conversions, and built-in helpers. `&&`, `||`, and `!` short-circuit; comparisons
|
|
98
|
+
bind more tightly than `&&`, which binds more tightly than `||`. Assertion `and`
|
|
99
|
+
continues to combine law conclusions. Exact/inexact mixing still needs an explicit
|
|
100
|
+
conversion, and floating expressions retain their declared precision.
|
|
101
|
+
|
|
102
|
+
`prelude.length` counts Unicode scalars for Text, code points for CodePointText,
|
|
103
|
+
UTF-16 units for Utf16Text, and octets for Bytes. `prelude.isPresent` and
|
|
104
|
+
`prelude.presentValue` inspect tagged presence values; guard extraction with
|
|
105
|
+
`isPresent` before accessing a possibly absent value.
|
|
106
|
+
|
|
107
|
+
Adapter calls are forbidden in refinements. Predicate errors such as division by
|
|
108
|
+
zero are reported as failures when evaluated; they are not ordinary rejection of
|
|
109
|
+
an input. Short-circuited branches are not evaluated by generation optimizations.
|
|
110
|
+
|
|
111
|
+
Every refined function signature creates a standalone property. Calls from other
|
|
112
|
+
laws also check argument preconditions, invoke the adapter once, snapshot its
|
|
113
|
+
result, and check postconditions. Calling a function outside its precondition is
|
|
114
|
+
a test failure, not a discarded example. Ordinary `implies` guards retain their
|
|
115
|
+
existing conditional behavior.
|
|
116
|
+
|
|
117
|
+
## Dependent generation and shrinking
|
|
118
|
+
|
|
119
|
+
A quantified law ranges over satisfying tuples. For `AdditionOverflows Int8 x`,
|
|
120
|
+
`x = 0` has no admissible `y`. The generator backtracks to choose another `x`;
|
|
121
|
+
it does not count that dead end as a passing test. Valid pairs satisfy:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
1 <= x <= 127
|
|
125
|
+
128 - x <= y <= 127
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The generator derives integer bounds from affine comparisons and conjunctions,
|
|
129
|
+
seeds direct comparison values and boundaries, and checks the complete predicate.
|
|
130
|
+
Predicates outside that analysis use bounded sampling. Small finite base-domain
|
|
131
|
+
products are enumerated exhaustively, retaining only satisfying tuples.
|
|
132
|
+
|
|
133
|
+
Shrinking checks refinements again and repairs dependent later inputs when an
|
|
134
|
+
earlier value changes. An overflowing counterexample can shrink to `(1, 127)`;
|
|
135
|
+
it cannot shrink to `(0, 127)` because that pair is outside the domain.
|
|
136
|
+
|
|
137
|
+
Compiler requests and `lawspec.json` accept:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"generation": {
|
|
142
|
+
"cases": 100,
|
|
143
|
+
"maxAttempts": 10000,
|
|
144
|
+
"maxShrinks": 1000,
|
|
145
|
+
"exhaustiveLimit": 4096
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
All limits are positive integers. These are the defaults for refinement properties.
|
|
151
|
+
`cases` counts accepted property inputs; explicit examples and boundary fixtures
|
|
152
|
+
are additional checks. Exhausted searches report an error with reproduction
|
|
153
|
+
information. Exhaustion does not prove that the mathematical domain is empty.
|
|
154
|
+
Statically established empty executable domains and invalid examples are rejected.
|
|
155
|
+
|
|
156
|
+
See [the bundled examples](examples/specs/refinements.lawspec) for overflow,
|
|
157
|
+
abstract integer arguments/results, dependent bounds, optional refinements,
|
|
158
|
+
floating classification, and raw-byte lengths.
|
package/RUST.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Rust backend (0.8)
|
|
2
|
+
|
|
3
|
+
Rust is the first backend built on LawSpec's typed core and testing plan. The
|
|
4
|
+
compiler remains implemented in Haskell. Rust generation does not parse source
|
|
5
|
+
expressions, infer their types, or expand refinement declarations.
|
|
6
|
+
|
|
7
|
+
## Project setup
|
|
8
|
+
|
|
9
|
+
Use Rust 1.85 or later, edition 2024, and Cargo. The generated project pins
|
|
10
|
+
Proptest 1.11.0, num-bigint 0.4.8, num-rational 0.4.2, num-complex 0.4.6, and
|
|
11
|
+
num-traits 0.2.19.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
lawspec init --target rust
|
|
15
|
+
lawspec doctor
|
|
16
|
+
lawspec generate
|
|
17
|
+
cargo test
|
|
18
|
+
cargo test --release
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Adapters are user-owned files under `src/`. LawSpec maintains
|
|
22
|
+
`lawspec_runtime.rs`, module declarations in `lawspec_modules.rs`, and tests
|
|
23
|
+
under `tests/`. The scaffolded `src/lib.rs` includes the generated declarations;
|
|
24
|
+
an existing library can include those declarations explicitly. Generation
|
|
25
|
+
preserves edited adapters and reports required signature updates.
|
|
26
|
+
|
|
27
|
+
The numeric runtime has no Proptest dependency. Framework support lives in a
|
|
28
|
+
separate generated file, `tests/support/lawspec_strategies.rs`.
|
|
29
|
+
|
|
30
|
+
## Owned adapter values
|
|
31
|
+
|
|
32
|
+
Arguments and results are owned Rust values. LawSpec does not add borrowing,
|
|
33
|
+
lifetimes, or pointer operations to its language. Tests clone values where an
|
|
34
|
+
expression needs to use an input more than once.
|
|
35
|
+
|
|
36
|
+
| LawSpec domain | Rust adapter representation |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `Bool` | `bool` |
|
|
39
|
+
| Fixed signed/unsigned integers | `i8`…`i64`, `u8`…`u64` |
|
|
40
|
+
| `IntSize`, `UIntSize`, `UIntPtr` | `isize`, `usize`, `usize` |
|
|
41
|
+
| `BigInt`, `BigUInt` | `BigInt`, `BigUint` |
|
|
42
|
+
| `Integer` input | `BigInt` |
|
|
43
|
+
| `Integer` result | `Integer`, with lossless `From` implementations |
|
|
44
|
+
| `Decimal`, `Rational` | Generated `Decimal`, `BigRational` |
|
|
45
|
+
| `Float32`, `Float64` | `f32`, `f64` |
|
|
46
|
+
| `Complex64`, `Complex128` | `Complex32`, `Complex64` from num-complex |
|
|
47
|
+
| `Char`, `Text` | `char`, `String` |
|
|
48
|
+
| `CodePoint`, `CodePointText` | Generated checked wrappers |
|
|
49
|
+
| `CodeUnit16`, `Utf16Text`, `Bytes` | `u16`, generated UTF-16 wrapper, `Vec<u8>` |
|
|
50
|
+
| `Symbol` | Generated identity type; cloning preserves identity |
|
|
51
|
+
| `Unit`, `Null`, `Undefined` | `()`, distinct generated absence types |
|
|
52
|
+
| `Nullable a`, `Optional a` | Distinct generated enums, including nested presence |
|
|
53
|
+
|
|
54
|
+
Names such as `Integer` and `Decimal` above are exported by the generated
|
|
55
|
+
`lawspec_runtime` module. Machine-sized native bindings check the requested
|
|
56
|
+
`machineBits` against the executing architecture.
|
|
57
|
+
|
|
58
|
+
For a declaration such as `successor :: Int8 -> Integer`, an implementation can
|
|
59
|
+
return a wider native integer through the logical result wrapper:
|
|
60
|
+
|
|
61
|
+
```rust
|
|
62
|
+
use crate::lawspec_runtime as ls;
|
|
63
|
+
|
|
64
|
+
pub fn successor(value: i8) -> ls::Integer {
|
|
65
|
+
(i16::from(value) + 1).into()
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Exact arithmetic in laws uses arbitrary precision. Passing its result to an
|
|
70
|
+
`i8` adapter parameter performs a checked conversion. A fractional or
|
|
71
|
+
out-of-range value fails with the adapter's context.
|
|
72
|
+
|
|
73
|
+
`Decimal` stores an arbitrary integer coefficient and exponent. Decimal
|
|
74
|
+
arithmetic is exact; explicit rounding uses the requested scale and ties to
|
|
75
|
+
even. Exact-to-float conversion rounds directly to the requested IEEE precision,
|
|
76
|
+
including subnormal and halfway cases.
|
|
77
|
+
|
|
78
|
+
## Refinements and generation
|
|
79
|
+
|
|
80
|
+
Small finite domains are enumerated. Larger domains use native Proptest
|
|
81
|
+
strategies. Integer bounds over earlier inputs become dependent strategies;
|
|
82
|
+
when a prefix has no possible continuation, generation retries the prefix.
|
|
83
|
+
Shrinking recomputes those dependent bounds and retains only valid tuples.
|
|
84
|
+
Refinement evaluation errors fail the test rather than being counted as rejected
|
|
85
|
+
samples. Generation limits prevent an empty or unreachable domain from passing
|
|
86
|
+
vacuously.
|
|
87
|
+
|
|
88
|
+
Contracts check preconditions, evaluate the adapter once, validate its native
|
|
89
|
+
result, and then check postconditions on that result.
|
|
90
|
+
|
|
91
|
+
## Custom layouts
|
|
92
|
+
|
|
93
|
+
`sourceDir` and `testDir` move generated sources and imports together. Set Cargo's
|
|
94
|
+
`[lib] path` to the library entry point under the selected source directory. For
|
|
95
|
+
test directories other than `tests`, register generated test files using Cargo
|
|
96
|
+
`[[test]]` entries. Doctor checks the selected Cargo package, edition, resolved
|
|
97
|
+
dependencies, library directory, and custom test registration.
|
package/api.mjs
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
import { loadCore } from './launcher.mjs';
|
|
3
3
|
export async function createCompiler() {
|
|
4
4
|
const call = await loadCore();
|
|
5
|
-
return { check: (input) => call({ ...input, method: 'check' }), expand: (input) => call({ ...input, method: 'expand' }), planGeneration: (input) => call({ ...input, method: 'planGeneration' }) };
|
|
5
|
+
return { check: (input) => call({ schemaVersion: 3, ...input, method: 'check' }), expand: (input) => call({ schemaVersion: 3, ...input, method: 'expand' }), planGeneration: (input) => call({ schemaVersion: 3, ...input, method: 'planGeneration' }) };
|
|
6
6
|
}
|
package/bin/lawspec.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { createCompiler } from "../api.mjs";
|
|
5
5
|
import { targets, templates, commands, setup } from "../templates.mjs";
|
|
6
6
|
import { generateExamples } from "../examples-command.mjs";
|
|
7
|
+
import { showScalar } from "../scalars.mjs";
|
|
7
8
|
import { doctor } from "../doctor.mjs";
|
|
8
9
|
import {
|
|
9
10
|
readOptional,
|
|
@@ -18,7 +19,7 @@ const options = {};
|
|
|
18
19
|
const positional = [];
|
|
19
20
|
for (let i = 0; i < args.length; i++) {
|
|
20
21
|
const arg = args[i];
|
|
21
|
-
if (["--target", "--project", "--config", "--output"].includes(arg)) {
|
|
22
|
+
if (["--target", "--project", "--config", "--output", "--machine-bits"].includes(arg)) {
|
|
22
23
|
if (!args[i + 1] || args[i + 1].startsWith("--"))
|
|
23
24
|
throw new Error(`Missing value for ${arg}`);
|
|
24
25
|
options[arg.slice(2)] = args[++i];
|
|
@@ -103,6 +104,7 @@ async function init() {
|
|
|
103
104
|
throw new Error("Target is already configured");
|
|
104
105
|
await mkdir(root, { recursive: true });
|
|
105
106
|
const buildFiles = [
|
|
107
|
+
"Cargo.toml",
|
|
106
108
|
"pom.xml",
|
|
107
109
|
"pyproject.toml",
|
|
108
110
|
"package.json",
|
|
@@ -138,6 +140,7 @@ async function init() {
|
|
|
138
140
|
await readFile(new URL("../starter.lawspec", import.meta.url), "utf8"),
|
|
139
141
|
true,
|
|
140
142
|
);
|
|
143
|
+
if (options.machineBits !== undefined) config.machineBits = options.machineBits;
|
|
141
144
|
config.targets.push({
|
|
142
145
|
language,
|
|
143
146
|
root: path.relative(configRoot, root) || ".",
|
|
@@ -151,47 +154,32 @@ async function init() {
|
|
|
151
154
|
`Configured ${language}. ${hasBuild ? "Existing build files preserved." : "Created missing project build files."}\n${setup[language]}\nNext: lawspec doctor, then lawspec generate.`,
|
|
152
155
|
);
|
|
153
156
|
}
|
|
154
|
-
function
|
|
155
|
-
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
expr.tag === "Number" ||
|
|
159
|
-
expr.tag === "StringLit" ||
|
|
160
|
-
expr.tag === "BoolLit"
|
|
161
|
-
)
|
|
162
|
-
return JSON.stringify(value);
|
|
163
|
-
if (expr.tag === "Apply")
|
|
164
|
-
return `${showExpression(value[0])} (${showExpression(value[1])})`;
|
|
165
|
-
return `(${showExpression(value[0])} . ${showExpression(value[1])})`;
|
|
157
|
+
function showAssertion(assertion) {
|
|
158
|
+
if (assertion.kind === "equal") return `${assertion.left.text} = ${assertion.right.text}`;
|
|
159
|
+
if (assertion.kind === "implies") return `${assertion.guard.text} implies ${showAssertion(assertion.body)}`;
|
|
160
|
+
return assertion.items.map(showAssertion).join(" and ");
|
|
166
161
|
}
|
|
167
162
|
function explainExamples(law) {
|
|
168
|
-
return law.
|
|
169
|
-
.
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
.map(([n, v]) => ` ${n} = ${JSON.stringify(v)}`)
|
|
174
|
-
.join("\n") +
|
|
175
|
-
"\n" +
|
|
176
|
-
ex.expectations
|
|
177
|
-
.map(
|
|
178
|
-
(e) =>
|
|
179
|
-
` expect ${showExpression(e.actual)} = ${JSON.stringify(e.expected)}`,
|
|
180
|
-
)
|
|
181
|
-
.join("\n"),
|
|
182
|
-
)
|
|
183
|
-
.join("\n");
|
|
163
|
+
return law.examples.map(ex =>
|
|
164
|
+
`\nexample ${JSON.stringify(ex.name)}\n` +
|
|
165
|
+
ex.bindings.map(b => ` ${b.name} = ${showScalar(b.value)}`).join("\n") + "\n" +
|
|
166
|
+
ex.expectations.map(e => ` expect ${showAssertion(e)}`).join("\n")
|
|
167
|
+
).join("\n");
|
|
184
168
|
}
|
|
185
169
|
async function main() {
|
|
170
|
+
if (options['machine-bits'] !== undefined) {
|
|
171
|
+
options.machineBits = Number(options['machine-bits']);
|
|
172
|
+
if (![32,64].includes(options.machineBits)) throw new Error('machineBits must be 32 or 64');
|
|
173
|
+
}
|
|
186
174
|
if (!verb || ["help", "--help", "-h"].includes(verb)) {
|
|
187
175
|
output(
|
|
188
|
-
"LawSpec 0.
|
|
176
|
+
"LawSpec 0.8.0\nUsage: lawspec init --target <language> [--project <directory>]\n lawspec check | doctor | explain <unit>::<law> | generate\n lawspec examples [--target <language>] [--output example_artifacts]\nOptions: --config <path>, --target <language>, --machine-bits <32|64>, --json\nGeneration: --dry-run, --check\nTargets: " +
|
|
189
177
|
targets.join(", "),
|
|
190
178
|
);
|
|
191
179
|
return;
|
|
192
180
|
}
|
|
193
181
|
if (verb === "--version") {
|
|
194
|
-
output("0.
|
|
182
|
+
output("0.8.0");
|
|
195
183
|
return;
|
|
196
184
|
}
|
|
197
185
|
if (positional.length > (verb === "explain" ? 1 : 0))
|
|
@@ -256,14 +244,15 @@ async function main() {
|
|
|
256
244
|
if (reports.some((r) => !r.ok)) process.exitCode = 1;
|
|
257
245
|
return;
|
|
258
246
|
}
|
|
259
|
-
|
|
247
|
+
if (config.machineBits !== undefined && ![32, 64].includes(config.machineBits)) throw new Error("machineBits must be 32 or 64");
|
|
248
|
+
const input = { sources: await sources(config), generation: config.generation, machineBits: options.machineBits ?? config.machineBits ?? 64 };
|
|
260
249
|
const compiler = await createCompiler();
|
|
261
250
|
if (verb === "check") {
|
|
262
251
|
const result = diagnostics(await compiler.check(input));
|
|
263
252
|
output(
|
|
264
253
|
options.json
|
|
265
254
|
? result
|
|
266
|
-
: `Checked ${result.laws.length}
|
|
255
|
+
: `Checked ${result.laws.length} law(s).`,
|
|
267
256
|
);
|
|
268
257
|
return;
|
|
269
258
|
}
|
|
@@ -274,7 +263,7 @@ async function main() {
|
|
|
274
263
|
.filter(
|
|
275
264
|
({ e }) => !positional[0] || `${e.owner}::${e.name}` === positional[0],
|
|
276
265
|
);
|
|
277
|
-
if (!indices.length) throw new Error("No matching
|
|
266
|
+
if (!indices.length) throw new Error("No matching law");
|
|
278
267
|
output(
|
|
279
268
|
options.json
|
|
280
269
|
? indices.map(({ e, i }) => ({ ...e, expansion: result.expansions[i] }))
|
|
@@ -302,7 +291,7 @@ async function main() {
|
|
|
302
291
|
).files,
|
|
303
292
|
);
|
|
304
293
|
const reports = await Promise.all(
|
|
305
|
-
selected.map((t, i) => doctor(t, roots[i])),
|
|
294
|
+
selected.map((t, i) => doctor(t, roots[i], artifacts[i])),
|
|
306
295
|
);
|
|
307
296
|
const failed = reports.filter((r) => !r.ok);
|
|
308
297
|
if (failed.length)
|
package/build.json
CHANGED
|
@@ -2,38 +2,94 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"compilerSources": [
|
|
4
4
|
"package.yaml",
|
|
5
|
+
"runtime/LawSpecRuntime.hs",
|
|
6
|
+
"runtime/LawSpecRuntime.java",
|
|
7
|
+
"runtime/lawspec_runtime.go",
|
|
8
|
+
"runtime/lawspec_runtime.mjs",
|
|
9
|
+
"runtime/lawspec_runtime.py",
|
|
10
|
+
"runtime/lawspec_runtime.rs",
|
|
11
|
+
"runtime/lawspec_strategies.rs",
|
|
12
|
+
"runtime/rust/Cargo.lock",
|
|
13
|
+
"runtime/rust/Cargo.toml",
|
|
5
14
|
"src/LawSpec/Api.hs",
|
|
15
|
+
"src/LawSpec/Backend.hs",
|
|
16
|
+
"src/LawSpec/Common.hs",
|
|
6
17
|
"src/LawSpec/Compile.hs",
|
|
18
|
+
"src/LawSpec/Core.hs",
|
|
19
|
+
"src/LawSpec/Core/Eval.hs",
|
|
20
|
+
"src/LawSpec/Core/Semantics.hs",
|
|
21
|
+
"src/LawSpec/Core/Validate.hs",
|
|
22
|
+
"src/LawSpec/CoreEmit.hs",
|
|
23
|
+
"src/LawSpec/CoreNativeScalarEmit.hs",
|
|
24
|
+
"src/LawSpec/CoreScalarEmit.hs",
|
|
25
|
+
"src/LawSpec/Domain.hs",
|
|
7
26
|
"src/LawSpec/Emit.hs",
|
|
27
|
+
"src/LawSpec/Eval.hs",
|
|
28
|
+
"src/LawSpec/Frontend.hs",
|
|
8
29
|
"src/LawSpec/Gen.hs",
|
|
9
30
|
"src/LawSpec/Model.hs",
|
|
10
31
|
"src/LawSpec/Parser.hs",
|
|
11
32
|
"src/LawSpec/Prelude.hs",
|
|
33
|
+
"src/LawSpec/Public.hs",
|
|
34
|
+
"src/LawSpec/Refinement.hs",
|
|
35
|
+
"src/LawSpec/RuntimeSources.hs",
|
|
36
|
+
"src/LawSpec/RustEmit.hs",
|
|
37
|
+
"src/LawSpec/Scalar.hs",
|
|
38
|
+
"src/LawSpec/Testing.hs",
|
|
12
39
|
"stack.yaml",
|
|
13
40
|
"stack.yaml.lock",
|
|
41
|
+
"tools/embed-runtimes.py",
|
|
14
42
|
"wasm/app/Exports.hs",
|
|
15
43
|
"wasm/cabal.project",
|
|
16
44
|
"wasm/cabal.project.freeze",
|
|
17
45
|
"wasm/lawspec-wasm.cabal"
|
|
18
46
|
],
|
|
19
47
|
"digests": {
|
|
20
|
-
"package.yaml": "
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
48
|
+
"package.yaml": "90923d07cf41977f62a201faf26667ae4fd76de84c6f25ce47917ac92a6a1f4b",
|
|
49
|
+
"runtime/LawSpecRuntime.hs": "1c3ca22232c64227717d836a57d375d906f748ecde6b79705944a2319ce5064d",
|
|
50
|
+
"runtime/LawSpecRuntime.java": "5637914bd491f75ad0c75fb19d1bec798164295b7bcf9d23e1549e8ebf780d01",
|
|
51
|
+
"runtime/lawspec_runtime.go": "62d2041c33760b67db619e0a803501768a505c4dfd9837bca85e158fff81fda9",
|
|
52
|
+
"runtime/lawspec_runtime.mjs": "7d0abd53fa45dd9828c10ff2ffa7876162e965e7bb4913f4c460af13e06142fa",
|
|
53
|
+
"runtime/lawspec_runtime.py": "7541353effebd133e50cd00a3f4d8999651c93ba751a6d951164b1f390ce0c15",
|
|
54
|
+
"runtime/lawspec_runtime.rs": "bf9cf5fa546346a59c85248936395fe11946974ce0bad053aeeaf4e26b6a829d",
|
|
55
|
+
"runtime/lawspec_strategies.rs": "a5dcee400574a69a5d2aa066bb8959e366728b3f21a578de84f50feadadf6641",
|
|
56
|
+
"runtime/rust/Cargo.lock": "91533e20939436e117c0f09ded695a8a05a86e79efc29fe4b4b93ad8ef1771c8",
|
|
57
|
+
"runtime/rust/Cargo.toml": "7ec66cf82dcc73a4a0b4a44d6048b6c3a340897c488aaaace9e8d4bac90b5e0a",
|
|
58
|
+
"src/LawSpec/Api.hs": "4810c05d53e370daf7520b9116c34997b7b4ba9c8d3f1a349e2afdb7b1424552",
|
|
59
|
+
"src/LawSpec/Backend.hs": "15ba04b69739e3891f73757d9cdc0a29acca6b7f7e067fd11d83d718474d0f20",
|
|
60
|
+
"src/LawSpec/Common.hs": "baa849dad0cc6227af334fdc7488389df50c0fe3a74a2ba1d84eb60c6b6edd6e",
|
|
61
|
+
"src/LawSpec/Compile.hs": "6c8bcea938f2ec3f83afb348c5ca932d96ce070f4e916fc9233a3a02ffcc89f2",
|
|
62
|
+
"src/LawSpec/Core.hs": "27ce2bb1274485cc190e10f8520118d5b381c90ac4ab6343223c46fb3cd10869",
|
|
63
|
+
"src/LawSpec/Core/Eval.hs": "87bf2acfdfff1431f1358947044c0474df40b5bc601076f5e5051e089f1d2fa4",
|
|
64
|
+
"src/LawSpec/Core/Semantics.hs": "a9d019433e3b9731f8cd0f65ec0ccc53179824bea58247b72df26760b878c2b6",
|
|
65
|
+
"src/LawSpec/Core/Validate.hs": "bd67926c872e190279f3758ad57b45c0f80d5be8381cedc8ac56a5b88b115201",
|
|
66
|
+
"src/LawSpec/CoreEmit.hs": "7ba225a262372f61edd6fe2ba0f2dbade44dfa2f767cdbcdd3f4c323eb5e9e7b",
|
|
67
|
+
"src/LawSpec/CoreNativeScalarEmit.hs": "f20382952f5854605ae617609c1ff9f5627e5acb134417cf3a67b97054844acb",
|
|
68
|
+
"src/LawSpec/CoreScalarEmit.hs": "db564e0cb07c69dee63b7dcb9cd524c30d4e7d658264d72f834adcc9fa05899d",
|
|
69
|
+
"src/LawSpec/Domain.hs": "8ad50ad3a37aacb3a429c2d7924279e2a3b973204ce3c5c04c634c1b9ada0151",
|
|
70
|
+
"src/LawSpec/Emit.hs": "9f34f5765915a2a12a66a6dca647ddaef9289372956802c6d99cf0cfec447c7a",
|
|
71
|
+
"src/LawSpec/Eval.hs": "bbaa50d3febee690c3b09ceb3f2592964cdb458dd1b4c38b6ec68a80f3e077ea",
|
|
72
|
+
"src/LawSpec/Frontend.hs": "b483732416f23960f0f03c30079a6db3ba630cf013b38fd6beede08b186585b8",
|
|
73
|
+
"src/LawSpec/Gen.hs": "f0a153e05fb0599a7cb2303dd62a269b2561738466c7211efc1927af2d5604b0",
|
|
74
|
+
"src/LawSpec/Model.hs": "75cedd730b8e8f7bff2dd39cfe3c1ed8f64e74471f6231248474957df446328f",
|
|
75
|
+
"src/LawSpec/Parser.hs": "e64356888d78f02572ff8c0a39b782248dd6a4bb9f0b1e30900f9dc12c3cd946",
|
|
27
76
|
"src/LawSpec/Prelude.hs": "b1e155c12f58e7c346551a0c65c5d5af76d045b3f1625977ca2a5d1c97cb8c46",
|
|
77
|
+
"src/LawSpec/Public.hs": "eec6c9684b3c5cb42fa882f89460104856cb02f963822b66d9b9583dc011dfeb",
|
|
78
|
+
"src/LawSpec/Refinement.hs": "9902897edc0a803c2d440cf6d5a1d44e524f94b7384ea55a6f9dcd16dbfa8957",
|
|
79
|
+
"src/LawSpec/RuntimeSources.hs": "69dadd138bb784eaebd74869d03e30079e6d73e1724179794d8ec32be178e171",
|
|
80
|
+
"src/LawSpec/RustEmit.hs": "702652cc590c5db148f35d2e591d90e45270eee47e78c31772749943509aafe7",
|
|
81
|
+
"src/LawSpec/Scalar.hs": "2907d94b72e66584b762b5ab33da64adb26f9bf84b885151ef408dc3de1a440d",
|
|
82
|
+
"src/LawSpec/Testing.hs": "a5dc1c15a45539bb72d1c7cb24cbd04888bbb8ef2f066c829654c3bdbf32c609",
|
|
28
83
|
"stack.yaml": "20ccf4d599e355e60b7aa4f814a7cd4299fe2048616cc2e6dbdc22a7bd8cec73",
|
|
29
84
|
"stack.yaml.lock": "ae222b9c81af920c56e50fa4596fa57786e7fa5a7b461390362b2b2ff63818c7",
|
|
85
|
+
"tools/embed-runtimes.py": "26dda6ecf944ac065d10165d84ecdf44f134945fabbd48492a67d2495b5faba4",
|
|
30
86
|
"wasm/app/Exports.hs": "4ecbdac8faa2449e6fc61b93c82e6278fa14374154f29b4dacf0eef433f06a47",
|
|
31
87
|
"wasm/cabal.project": "021e560afdc5eb4cb7169e7119ecb8f92c9ee170245af909c94b612941bff5cc",
|
|
32
88
|
"wasm/cabal.project.freeze": "733dbed3d2ecccb26e874fd58f136296dad772184deb1658c196b5d54a0814dc",
|
|
33
|
-
"wasm/lawspec-wasm.cabal": "
|
|
34
|
-
"npm/core.wasm": "
|
|
89
|
+
"wasm/lawspec-wasm.cabal": "22590b2a6fcd384ef8957bd179e04a055e90535f7eaaa2b5ac3d314fd0b686ed",
|
|
90
|
+
"npm/core.wasm": "09efa9e767c3d33e718a8a0909cdf23b24d158813570cc610f56e72d37a59fa3",
|
|
35
91
|
"npm/core_jsffi.js": "88d136efe92f7cff5758c8fec8d9b6bbc9707fe37741cebeeb415fe34ce3d72b",
|
|
36
|
-
"npm/api.mjs": "
|
|
37
|
-
"npm/index.d.ts": "
|
|
92
|
+
"npm/api.mjs": "7415fc057ea5f4da68f9d255ce862152d6bc45d6b0053d44f61f9110911ac64b",
|
|
93
|
+
"npm/index.d.ts": "d19ecaea0be4ca01c8667a76e1bcfe758fd200d728ef67a44ad14e1b1dd15b72"
|
|
38
94
|
}
|
|
39
95
|
}
|
package/compatibility.json
CHANGED
|
@@ -2,37 +2,120 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"targets": {
|
|
4
4
|
"java": {
|
|
5
|
-
"java": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"java": [
|
|
6
|
+
"25",
|
|
7
|
+
"26"
|
|
8
|
+
],
|
|
9
|
+
"jetCheck": [
|
|
10
|
+
"0.3.0",
|
|
11
|
+
"0.3.1"
|
|
12
|
+
],
|
|
13
|
+
"junit-jupiter": [
|
|
14
|
+
"5.14.0",
|
|
15
|
+
"5.15.0"
|
|
16
|
+
]
|
|
8
17
|
},
|
|
9
18
|
"python": {
|
|
10
|
-
"python": [
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
"python": [
|
|
20
|
+
"3.13",
|
|
21
|
+
"3.15"
|
|
22
|
+
],
|
|
23
|
+
"pytest": [
|
|
24
|
+
"8.4.0",
|
|
25
|
+
"8.5.0"
|
|
26
|
+
],
|
|
27
|
+
"hypothesis": [
|
|
28
|
+
"6.135.26",
|
|
29
|
+
"7"
|
|
30
|
+
]
|
|
13
31
|
},
|
|
14
32
|
"javascript": {
|
|
15
|
-
"fast-check": [
|
|
33
|
+
"fast-check": [
|
|
34
|
+
"4",
|
|
35
|
+
"5"
|
|
36
|
+
]
|
|
16
37
|
},
|
|
17
38
|
"typescript": {
|
|
18
|
-
"fast-check": [
|
|
19
|
-
|
|
20
|
-
|
|
39
|
+
"fast-check": [
|
|
40
|
+
"4",
|
|
41
|
+
"5"
|
|
42
|
+
],
|
|
43
|
+
"typescript": [
|
|
44
|
+
"5.9",
|
|
45
|
+
"5.10"
|
|
46
|
+
],
|
|
47
|
+
"@types/node": [
|
|
48
|
+
"22",
|
|
49
|
+
"23"
|
|
50
|
+
]
|
|
21
51
|
},
|
|
22
52
|
"go": {
|
|
23
|
-
"go": [
|
|
24
|
-
|
|
53
|
+
"go": [
|
|
54
|
+
"1.22",
|
|
55
|
+
"1.27"
|
|
56
|
+
],
|
|
57
|
+
"rapid": [
|
|
58
|
+
"1.2.0",
|
|
59
|
+
"1.2.1"
|
|
60
|
+
]
|
|
25
61
|
},
|
|
26
62
|
"haskell": {
|
|
27
|
-
"hspec": [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
63
|
+
"hspec": [
|
|
64
|
+
"2.11",
|
|
65
|
+
"2.12"
|
|
66
|
+
],
|
|
67
|
+
"hedgehog": [
|
|
68
|
+
"1.5",
|
|
69
|
+
"1.6"
|
|
70
|
+
],
|
|
71
|
+
"hspec-hedgehog": [
|
|
72
|
+
"0.3.0.0",
|
|
73
|
+
"0.3.0.1"
|
|
74
|
+
],
|
|
75
|
+
"ghc": [
|
|
76
|
+
"9.10",
|
|
77
|
+
"9.11"
|
|
78
|
+
]
|
|
31
79
|
},
|
|
32
80
|
"kotlin": {
|
|
33
|
-
"java": [
|
|
34
|
-
|
|
35
|
-
|
|
81
|
+
"java": [
|
|
82
|
+
"25",
|
|
83
|
+
"26"
|
|
84
|
+
],
|
|
85
|
+
"gradle": [
|
|
86
|
+
"9.1",
|
|
87
|
+
"9.4"
|
|
88
|
+
],
|
|
89
|
+
"kotlin": [
|
|
90
|
+
"2.3.21",
|
|
91
|
+
"2.3.22"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"rust": {
|
|
95
|
+
"rust": [
|
|
96
|
+
"1.85",
|
|
97
|
+
"2"
|
|
98
|
+
],
|
|
99
|
+
"proptest": [
|
|
100
|
+
"1.11.0",
|
|
101
|
+
"1.11.1"
|
|
102
|
+
],
|
|
103
|
+
"num-bigint": [
|
|
104
|
+
"0.4.8",
|
|
105
|
+
"0.4.9"
|
|
106
|
+
],
|
|
107
|
+
"num-rational": [
|
|
108
|
+
"0.4.2",
|
|
109
|
+
"0.4.3"
|
|
110
|
+
],
|
|
111
|
+
"num-complex": [
|
|
112
|
+
"0.4.6",
|
|
113
|
+
"0.4.7"
|
|
114
|
+
],
|
|
115
|
+
"num-traits": [
|
|
116
|
+
"0.2.19",
|
|
117
|
+
"0.2.20"
|
|
118
|
+
]
|
|
36
119
|
}
|
|
37
120
|
}
|
|
38
121
|
}
|
package/core.wasm
CHANGED
|
Binary file
|