lawspec 0.4.0 → 0.5.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 +95 -20
- package/bin/lawspec.mjs +8 -4
- package/build.json +10 -10
- package/core.wasm +0 -0
- package/examples/specs/boolean_flags.lawspec +21 -0
- package/examples/specs/parse_port.lawspec +66 -0
- package/index.d.ts +5 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**State the law once. Check it everywhere.**
|
|
4
4
|
|
|
5
|
-
LawSpec 0.
|
|
5
|
+
LawSpec 0.5 compiles reusable laws into native property tests, executable examples,
|
|
6
6
|
and implementation adapters. The compiler is Haskell, distributed as prebuilt
|
|
7
7
|
WebAssembly with a Node CLI and an asynchronous, typed JavaScript API.
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ WebAssembly with a Node CLI and an asynchronous, typed JavaScript API.
|
|
|
11
11
|
Install [LawSpec from npm](https://www.npmjs.com/package/lawspec):
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npm install --save-dev lawspec@0.
|
|
14
|
+
npm install --save-dev lawspec@0.5.0
|
|
15
15
|
npx lawspec --version
|
|
16
16
|
```
|
|
17
17
|
|
|
@@ -25,8 +25,8 @@ local dependencies so LawSpec can create its `package.json` and test script:
|
|
|
25
25
|
```sh
|
|
26
26
|
mkdir lawspec-example
|
|
27
27
|
cd lawspec-example
|
|
28
|
-
npm exec --package=lawspec@0.
|
|
29
|
-
npm install --save-dev lawspec@0.
|
|
28
|
+
npm exec --package=lawspec@0.5.0 -- lawspec init --target javascript
|
|
29
|
+
npm install --save-dev lawspec@0.5.0
|
|
30
30
|
npx lawspec check
|
|
31
31
|
npx lawspec explain 'example.atoi_codec::itoa and then atoi yields a'
|
|
32
32
|
npx lawspec doctor
|
|
@@ -52,7 +52,7 @@ properties with the selected framework's shrinking and failure reporting.
|
|
|
52
52
|
|
|
53
53
|
| Target | Build setup | Test libraries | Test command |
|
|
54
54
|
| --- | --- | --- | --- |
|
|
55
|
-
| `java` | Maven, JDK 25, release 25 | JetCheck 0.
|
|
55
|
+
| `java` | Maven, JDK 25, release 25 | JetCheck 0.3.0, JUnit Jupiter 5.14.x | `mvn test` |
|
|
56
56
|
| `python` | Python 3.13 or 3.14, pyproject | pytest 8.4.x, Hypothesis 6.135.26+ (6.x) | `python -m pytest` |
|
|
57
57
|
| `javascript` | Node 22+, npm, ESM | fast-check 4.x, node:test | `npm test` |
|
|
58
58
|
| `typescript` | Node 22+, npm, TypeScript 5.9.x, ESM | fast-check 4.x, node:test | `npm test` |
|
|
@@ -61,7 +61,7 @@ properties with the selected framework's shrinking and failure reporting.
|
|
|
61
61
|
| `kotlin` | JDK/JVM 25, Gradle 9.1–9.3, Kotlin 2.3.21 | Kotest 5.9.1 | `gradle test` |
|
|
62
62
|
|
|
63
63
|
Java 25 and Python 3.13 are the minimum baselines. New JVM releases are admitted
|
|
64
|
-
through compatibility profiles after testing; v0.
|
|
64
|
+
through compatibility profiles after testing; v0.5's current JVM profile certifies
|
|
65
65
|
25. Python templates declare `requires-python = ">=3.13"` and runtime checks
|
|
66
66
|
currently recognize 3.13 and 3.14. Kotlin templates pin Gradle's supported build
|
|
67
67
|
configuration to Kotlin 2.3.21 and target JVM 25.
|
|
@@ -159,10 +159,11 @@ for all (x :: Int32) . atoi (itoa (x)) = x
|
|
|
159
159
|
|
|
160
160
|
Reusable laws can declare typed unary function parameters and `requires Eq a`.
|
|
161
161
|
Definitions support law application, function application/composition, universal
|
|
162
|
-
quantification,
|
|
163
|
-
and `
|
|
164
|
-
|
|
165
|
-
types can also be intermediate or
|
|
162
|
+
quantification, `implies`, Boolean predicates, scalar literals, and equality.
|
|
163
|
+
Function signatures use `Int32`, `Text`, and `Bool`; generic variables are
|
|
164
|
+
supported in reusable laws. v0.5 generates quantified inputs of all three types,
|
|
165
|
+
including mixed and multiple inputs. These types can also be intermediate or
|
|
166
|
+
compared results. Functions are synchronous
|
|
166
167
|
and unary. Text literals are double-quoted, with escapes such as `\"`, `\\`,
|
|
167
168
|
`\n`, and `\t`; examples must bind each input to a literal of its declared type.
|
|
168
169
|
Text values contain Unicode scalar values; surrogate code points are rejected.
|
|
@@ -178,11 +179,85 @@ Additional primitives, external law packages, cross-unit imports beyond the
|
|
|
178
179
|
prelude, async functions, direct existing-symbol binding and browser hosting are
|
|
179
180
|
outside this release.
|
|
180
181
|
|
|
181
|
-
##
|
|
182
|
+
## Predicates and conditional laws (0.5)
|
|
183
|
+
|
|
184
|
+
A predicate is a unary function returning `Bool`. Use `true` and `false` in
|
|
185
|
+
expressions, example bindings, and expected results. A Boolean expression can
|
|
186
|
+
stand alone as a law's definition: it must evaluate to `true`.
|
|
187
|
+
|
|
188
|
+
`condition implies consequence` checks the consequence only when the condition
|
|
189
|
+
is true. Conditions must have type `Bool`; nested implications short-circuit in
|
|
190
|
+
source order. The consequence can be an equality, another implication, a Boolean
|
|
191
|
+
predicate, or a reusable law application. Quantify any inputs before using them.
|
|
192
|
+
|
|
193
|
+
```lawspec
|
|
194
|
+
unit example.parse_port
|
|
195
|
+
|
|
196
|
+
validPort :: Int32 -> Bool
|
|
197
|
+
render :: Int32 -> Text
|
|
198
|
+
parse :: Text -> Int32
|
|
199
|
+
|
|
200
|
+
law `valid ports round trip` is
|
|
201
|
+
definition is
|
|
202
|
+
`for all` (x :: Int32) .
|
|
203
|
+
validPort x implies
|
|
204
|
+
parse (render x) = x
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
example `ordinary port` is
|
|
208
|
+
x = 443
|
|
209
|
+
expect validPort x = true
|
|
210
|
+
expect render x = "443"
|
|
211
|
+
expect parse (render x) = 443
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
example `zero is rejected; the round trip is skipped` is
|
|
215
|
+
x = 0
|
|
216
|
+
expect validPort x = false
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.5.0/examples/specs/parse_port.lawspec)
|
|
222
|
+
defines valid ports as 1–65535, and covers both endpoints, ordinary ports, zero,
|
|
223
|
+
negative values, and 65536. All explicit `expect` assertions run regardless of
|
|
224
|
+
the law's condition. A false condition skips only the consequence: invalid ports
|
|
225
|
+
never reach `render` or `parse` through the law. Predicate errors still fail the
|
|
226
|
+
test; they are not treated as false.
|
|
227
|
+
|
|
228
|
+
Implication is logical implication, not generator filtering or an assumption.
|
|
229
|
+
Randomized tests still sample the full input domain and count a false condition
|
|
230
|
+
as satisfying the law. A narrow predicate may therefore exercise few or no
|
|
231
|
+
consequences during a random run. Explicit valid examples ensure the important
|
|
232
|
+
cases run, and expectations of both `true` and `false` catch always-false and
|
|
233
|
+
always-true predicate implementations.
|
|
234
|
+
|
|
235
|
+
The prelude includes `satisfies predicate` (the predicate holds for every input)
|
|
236
|
+
and `left inverse when predicate parse render` (the guarded round trip above).
|
|
237
|
+
These reusable laws preserve the condition and its lexical bindings when expanded.
|
|
238
|
+
`equivalent` can also compare two predicates, since `Bool` supports equality.
|
|
239
|
+
The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.5.0/examples/specs/boolean_flags.lawspec)
|
|
240
|
+
checks that flipping twice restores both `false` and `true`; all targets generate
|
|
241
|
+
Boolean property inputs and explicit tests for both Boolean boundary values. Java caps Boolean-only JetCheck runs at the number of
|
|
242
|
+
possible input combinations (up to 100), avoiding generator exhaustion.
|
|
243
|
+
|
|
244
|
+
In 0.5, `implies`, `true`, and `false` become reserved words. Existing 0.4 specs
|
|
245
|
+
that use those words as identifiers need renaming. The API adds `BoolLit`,
|
|
246
|
+
`Holds`, and `Implies` AST variants, Boolean literal values, and an ordered
|
|
247
|
+
`guards` array on expanded laws. `lawspec explain` prints the conditions.
|
|
248
|
+
|
|
249
|
+
Kotlin adapters now group functions in an `object` named after the unit (for
|
|
250
|
+
example, `object ParsePort` in package `example`). This allows both the port and
|
|
251
|
+
alternatives units to define `render(Int)`. When upgrading a Kotlin project,
|
|
252
|
+
move existing top-level adapter functions into the indicated object; generation
|
|
253
|
+
preserves your adapter and reports the required stub shape. Generated tests call
|
|
254
|
+
`ParsePort.validPort(...)`, `ParsePort.render(...)`, and `ParsePort.parse(...)`.
|
|
255
|
+
|
|
256
|
+
## Expected results and migration from 0.3
|
|
182
257
|
|
|
183
258
|
Every `example` must bind all quantified inputs and then include one or more
|
|
184
259
|
`expect <expression> = <literal>` assertions. The expected literal must have the
|
|
185
|
-
same `Int32` or `
|
|
260
|
+
same `Int32`, `Text`, or `Bool` type as the expression. Expressions can reference the
|
|
186
261
|
example's inputs and the unit's functions, including composed function calls.
|
|
187
262
|
Input names shadow function names within expectations, following lexical scope.
|
|
188
263
|
|
|
@@ -240,7 +315,7 @@ This expands to `for all (x :: Int32) . render (x) = referenceRender (x)`.
|
|
|
240
315
|
The example inherits the input name `x` from the prelude. Both functions are
|
|
241
316
|
user-owned adapter functions; either may delegate to your existing code.
|
|
242
317
|
|
|
243
|
-
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
318
|
+
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.5.0/examples/specs/equivalent.lawspec) compares decimal
|
|
244
319
|
renderers and two implementations that clamp negative integers to zero. For
|
|
245
320
|
JavaScript, their adapters can be:
|
|
246
321
|
|
|
@@ -255,7 +330,7 @@ The same specification generates native tests for all seven targets. The
|
|
|
255
330
|
integration suite checks both examples with matching implementations, then
|
|
256
331
|
breaks each alternative separately to verify detection. The general equivalence law alone does not establish independent correctness;
|
|
257
332
|
two implementations can share the same bug. Explicit expectations additionally
|
|
258
|
-
check the specified outputs at the supplied example inputs. Quantified inputs can be `Int32` or `
|
|
333
|
+
check the specified outputs at the supplied example inputs. Quantified inputs can be `Int32`, `Text`, or `Bool`.
|
|
259
334
|
|
|
260
335
|
## Text properties and idempotence
|
|
261
336
|
|
|
@@ -277,7 +352,7 @@ law `normalizers agree` is
|
|
|
277
352
|
end
|
|
278
353
|
```
|
|
279
354
|
|
|
280
|
-
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
355
|
+
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.5.0/examples/specs/slug.lawspec)
|
|
281
356
|
compares two implementations of ASCII-space replacement. It includes empty,
|
|
282
357
|
Unicode and escaped text. Each target uses its native string generator:
|
|
283
358
|
JetCheck `Generator.stringsOf(Generator.asciiPrintableChars())`, Hypothesis `st.text()`, fast-check `fc.string()`,
|
|
@@ -304,7 +379,7 @@ law `canonicalization reaches a fixed point` is
|
|
|
304
379
|
end
|
|
305
380
|
```
|
|
306
381
|
|
|
307
|
-
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
382
|
+
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.5.0/examples/specs/canonical_url.lawspec)
|
|
308
383
|
uses removal of **all trailing slashes** as a small fixed-point demonstration,
|
|
309
384
|
not a complete URL canonicalization algorithm. For JavaScript:
|
|
310
385
|
|
|
@@ -313,10 +388,10 @@ export const canonicalize = value => value.replace(/\/+$/, "");
|
|
|
313
388
|
```
|
|
314
389
|
|
|
315
390
|
Removing just one trailing slash fails the supplied repeated-slash example.
|
|
316
|
-
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
391
|
+
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.5.0/examples/specs/mixed_inputs.lawspec)
|
|
317
392
|
shows `Text` and `Int32` in the same quantified property and executable example.
|
|
318
|
-
The JavaScript API represents input bindings and expected values as `number | string`.
|
|
319
|
-
Each example includes `expectations: { actual: Expr; expected: number | string }[]`.
|
|
393
|
+
The JavaScript API represents input bindings and expected values as `number | string | boolean`.
|
|
394
|
+
Each example includes `expectations: { actual: Expr; expected: number | string | boolean }[]`.
|
|
320
395
|
|
|
321
396
|
## Generate all example artifacts
|
|
322
397
|
|
|
@@ -378,7 +453,7 @@ by the JS shim.
|
|
|
378
453
|
## Build and verify
|
|
379
454
|
|
|
380
455
|
For contributors working from a repository checkout, build a local archive with
|
|
381
|
-
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.
|
|
456
|
+
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.5.0.tgz`.
|
|
382
457
|
The package payload lives in `npm/`.
|
|
383
458
|
|
|
384
459
|
```sh
|
package/bin/lawspec.mjs
CHANGED
|
@@ -154,7 +154,11 @@ async function init() {
|
|
|
154
154
|
function showExpression(expr) {
|
|
155
155
|
const value = expr.contents;
|
|
156
156
|
if (expr.tag === "Var") return value;
|
|
157
|
-
if (
|
|
157
|
+
if (
|
|
158
|
+
expr.tag === "Number" ||
|
|
159
|
+
expr.tag === "StringLit" ||
|
|
160
|
+
expr.tag === "BoolLit"
|
|
161
|
+
)
|
|
158
162
|
return JSON.stringify(value);
|
|
159
163
|
if (expr.tag === "Apply")
|
|
160
164
|
return `${showExpression(value[0])} (${showExpression(value[1])})`;
|
|
@@ -181,13 +185,13 @@ function explainExamples(law) {
|
|
|
181
185
|
async function main() {
|
|
182
186
|
if (!verb || ["help", "--help", "-h"].includes(verb)) {
|
|
183
187
|
output(
|
|
184
|
-
"LawSpec 0.
|
|
188
|
+
"LawSpec 0.5.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>, --json\nGeneration: --dry-run, --check\nTargets: " +
|
|
185
189
|
targets.join(", "),
|
|
186
190
|
);
|
|
187
191
|
return;
|
|
188
192
|
}
|
|
189
193
|
if (verb === "--version") {
|
|
190
|
-
output("0.
|
|
194
|
+
output("0.5.0");
|
|
191
195
|
return;
|
|
192
196
|
}
|
|
193
197
|
if (positional.length > (verb === "explain" ? 1 : 0))
|
|
@@ -207,7 +211,7 @@ async function main() {
|
|
|
207
211
|
: result
|
|
208
212
|
.map(
|
|
209
213
|
(r) =>
|
|
210
|
-
`${r.target}: ${r.files.length} artifacts in ${r.directory}; ${r.preservedAdapters.length} user adapters preserved
|
|
214
|
+
`${r.target}: ${r.files.length} artifacts in ${r.directory}; ${r.preservedAdapters.length} user adapters preserved.${r.adapterUpdates.length ? "\nReview required adapter signatures:\n" + r.adapterUpdates.map((a) => a.path + "\n" + a.requiredAdapter).join("\n") : ""}`,
|
|
211
215
|
)
|
|
212
216
|
.join("\n") +
|
|
213
217
|
"\nInspection artifacts only; native toolchains and dependencies are not checked. Stubs must be implemented before running tests.",
|
package/build.json
CHANGED
|
@@ -17,23 +17,23 @@
|
|
|
17
17
|
"wasm/lawspec-wasm.cabal"
|
|
18
18
|
],
|
|
19
19
|
"digests": {
|
|
20
|
-
"package.yaml": "
|
|
20
|
+
"package.yaml": "deae9eb35385fd0e08aa44432ae718e7ba6bad8e8320c728018ee9d224733c8c",
|
|
21
21
|
"src/LawSpec/Api.hs": "00b1e4bc81231ebd387f10652463904084ebf9c3c4486fd2c2217a6d4dbfc157",
|
|
22
|
-
"src/LawSpec/Compile.hs": "
|
|
23
|
-
"src/LawSpec/Emit.hs": "
|
|
24
|
-
"src/LawSpec/Gen.hs": "
|
|
25
|
-
"src/LawSpec/Model.hs": "
|
|
26
|
-
"src/LawSpec/Parser.hs": "
|
|
27
|
-
"src/LawSpec/Prelude.hs": "
|
|
22
|
+
"src/LawSpec/Compile.hs": "fd696c774025cb0ec6eb29f71661ab71ac603314865ef068778ae9df1355b6cb",
|
|
23
|
+
"src/LawSpec/Emit.hs": "7f53109a18d2450313c9dc9f9ff0fe31d80f533858baf9b2c30e36b8849b416c",
|
|
24
|
+
"src/LawSpec/Gen.hs": "c4b5d4f003c52fb31983794187890ef0dad09c86a451d8e1145974605a5ca0e8",
|
|
25
|
+
"src/LawSpec/Model.hs": "148f133296be537d4e076c2716219fdafa6d2a44b26c9de4b033f87694870911",
|
|
26
|
+
"src/LawSpec/Parser.hs": "12b652dbbf174a7091609385c4564bd47b99e45329cfc1bcdaa3936901b2f966",
|
|
27
|
+
"src/LawSpec/Prelude.hs": "98cc20e242d2f70510fa09e59d0f3e387d1ebe438b4d3435ba2f32a1192cfb4e",
|
|
28
28
|
"stack.yaml": "20ccf4d599e355e60b7aa4f814a7cd4299fe2048616cc2e6dbdc22a7bd8cec73",
|
|
29
29
|
"stack.yaml.lock": "ae222b9c81af920c56e50fa4596fa57786e7fa5a7b461390362b2b2ff63818c7",
|
|
30
30
|
"wasm/app/Exports.hs": "4ecbdac8faa2449e6fc61b93c82e6278fa14374154f29b4dacf0eef433f06a47",
|
|
31
31
|
"wasm/cabal.project": "021e560afdc5eb4cb7169e7119ecb8f92c9ee170245af909c94b612941bff5cc",
|
|
32
32
|
"wasm/cabal.project.freeze": "733dbed3d2ecccb26e874fd58f136296dad772184deb1658c196b5d54a0814dc",
|
|
33
|
-
"wasm/lawspec-wasm.cabal": "
|
|
34
|
-
"npm/core.wasm": "
|
|
33
|
+
"wasm/lawspec-wasm.cabal": "4c96fa2ec9c6cb61ca16f705c23cdae8723054bb252d27d8ef58043f8e31b868",
|
|
34
|
+
"npm/core.wasm": "581d42431681aa93b64bcd775d403612974f5cb9c7350be4b07d3ed49575c18e",
|
|
35
35
|
"npm/core_jsffi.js": "88d136efe92f7cff5758c8fec8d9b6bbc9707fe37741cebeeb415fe34ce3d72b",
|
|
36
36
|
"npm/api.mjs": "d6df654600172131ac66a55b86876fc29cde2a76a0a63bbefff3183488a4837b",
|
|
37
|
-
"npm/index.d.ts": "
|
|
37
|
+
"npm/index.d.ts": "9687bedaa439c0888aa54cb94279f4fbda536122f0e55f6ab4ec3ea82ddb4283"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/core.wasm
CHANGED
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
unit example.boolean_flags
|
|
2
|
+
|
|
3
|
+
flipFlag :: Bool -> Bool
|
|
4
|
+
|
|
5
|
+
law `flipping twice restores either flag` is
|
|
6
|
+
definition is
|
|
7
|
+
`left inverse` flipFlag flipFlag
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
example `disabled becomes enabled and then disabled again` is
|
|
11
|
+
x = false
|
|
12
|
+
expect flipFlag x = true
|
|
13
|
+
expect flipFlag (flipFlag x) = false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
example `enabled becomes disabled and then enabled again` is
|
|
17
|
+
x = true
|
|
18
|
+
expect flipFlag x = false
|
|
19
|
+
expect flipFlag (flipFlag x) = true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
unit example.parse_port
|
|
2
|
+
|
|
3
|
+
validPort :: Int32 -> Bool
|
|
4
|
+
render :: Int32 -> Text
|
|
5
|
+
parse :: Text -> Int32
|
|
6
|
+
|
|
7
|
+
law `valid ports round trip` is
|
|
8
|
+
definition is
|
|
9
|
+
`for all` (x :: Int32) .
|
|
10
|
+
validPort x implies
|
|
11
|
+
parse (render x) = x
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
description is
|
|
15
|
+
"For ports in 1 through 65535, {parse} recovers the value written by {render}"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
example `ordinary port` is
|
|
19
|
+
x = 443
|
|
20
|
+
expect validPort x = true
|
|
21
|
+
expect render x = "443"
|
|
22
|
+
expect parse (render x) = 443
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
example `lowest port is accepted and round trips` is
|
|
26
|
+
x = 1
|
|
27
|
+
expect validPort x = true
|
|
28
|
+
expect render x = "1"
|
|
29
|
+
expect parse (render x) = 1
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
example `highest port is accepted and round trips` is
|
|
33
|
+
x = 65535
|
|
34
|
+
expect validPort x = true
|
|
35
|
+
expect render x = "65535"
|
|
36
|
+
expect parse (render x) = 65535
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
example `zero is rejected; rendering and parsing are skipped` is
|
|
40
|
+
x = 0
|
|
41
|
+
expect validPort x = false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
example `negative port is rejected; rendering and parsing are skipped` is
|
|
45
|
+
x = -1
|
|
46
|
+
expect validPort x = false
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
example `65536 is rejected; rendering and parsing are skipped` is
|
|
50
|
+
x = 65536
|
|
51
|
+
expect validPort x = false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
law `the same port contract via the prelude` is
|
|
56
|
+
definition is
|
|
57
|
+
`left inverse when` validPort parse render
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
example `port 8080 satisfies the inherited condition and round trips` is
|
|
61
|
+
x = 8080
|
|
62
|
+
expect validPort x = true
|
|
63
|
+
expect render x = "8080"
|
|
64
|
+
expect parse (render x) = 8080
|
|
65
|
+
end
|
|
66
|
+
end
|
package/index.d.ts
CHANGED
|
@@ -5,12 +5,12 @@ export interface Location { file: string; line: number; column: number }
|
|
|
5
5
|
export interface Diagnostic { code: string; message: string; at: Location | null }
|
|
6
6
|
export interface Artifact { path: string; content: string; ownership: 'user' | 'generated' }
|
|
7
7
|
export type Type = {tag: 'Named' | 'Variable'; contents: string} | {tag: 'Arrow'; contents: [Type, Type]};
|
|
8
|
-
export type Expr = {tag: 'Var' | 'StringLit'; contents: string} | {tag: 'Number'; contents: number} | {tag: 'Apply' | 'Compose'; contents: [Expr, Expr]};
|
|
9
|
-
export type Definition = {tag: 'Forall'; contents: [[string, Type][], Definition]} | {tag: 'Equal'; contents: [Expr, Expr]} | {tag: 'Invoke'; contents: [string, Expr[]]};
|
|
10
|
-
export interface Expectation { actual: Expr; expected: number | string }
|
|
11
|
-
export interface Example { exampleName: string; bindings: [string, number | string][]; expectations: Expectation[] }
|
|
8
|
+
export type Expr = {tag: 'Var' | 'StringLit'; contents: string} | {tag: 'Number'; contents: number} | {tag: 'BoolLit'; contents: boolean} | {tag: 'Apply' | 'Compose'; contents: [Expr, Expr]};
|
|
9
|
+
export type Definition = {tag: 'Forall'; contents: [[string, Type][], Definition]} | {tag: 'Equal'; contents: [Expr, Expr]} | {tag: 'Holds'; contents: Expr} | {tag: 'Implies'; contents: [Expr, Definition]} | {tag: 'Invoke'; contents: [string, Expr[]]};
|
|
10
|
+
export interface Expectation { actual: Expr; expected: number | string | boolean }
|
|
11
|
+
export interface Example { exampleName: string; bindings: [string, number | string | boolean][]; expectations: Expectation[] }
|
|
12
12
|
export interface Law { lawName: string; parameters: [string, Type][]; requirements: Type[]; definition: Definition; description: string; rationale: string; examples: Example[]; references: string[]; location: Location }
|
|
13
|
-
export interface Expanded { owner: string; name: string; inputs: {inputName: string; inputId: string; inputType: Type}[]; left: Expr; right: Expr; trace: string[]; original: Law }
|
|
13
|
+
export interface Expanded { owner: string; name: string; inputs: {inputName: string; inputId: string; inputType: Type}[]; left: Expr; right: Expr; guards: Expr[]; trace: string[]; original: Law }
|
|
14
14
|
export interface CheckRequest { sources: Source[] }
|
|
15
15
|
export interface GenerationRequest extends CheckRequest { target: Target; sourceDir?: string; testDir?: string }
|
|
16
16
|
export interface Result { diagnostics: Diagnostic[]; laws?: Expanded[]; expansions?: string[]; files?: Artifact[] }
|