lawspec 0.4.0 → 0.6.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 +230 -21
- package/bin/lawspec.mjs +8 -4
- package/build.json +10 -10
- package/core.wasm +0 -0
- package/examples/specs/algebra.lawspec +254 -0
- package/examples/specs/boolean_flags.lawspec +21 -0
- package/examples/specs/currying.lawspec +60 -0
- package/examples/specs/parse_port.lawspec +66 -0
- package/index.d.ts +6 -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.6 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.6.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.6.0 -- lawspec init --target javascript
|
|
29
|
+
npm install --save-dev lawspec@0.6.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.6'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,11 +159,12 @@ 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
|
|
166
|
-
|
|
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.6 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 and support curried signatures with any positive
|
|
167
|
+
number of scalar arguments. 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.
|
|
169
170
|
|
|
@@ -178,11 +179,219 @@ 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
|
+
## Algebra and currying (0.6)
|
|
183
|
+
|
|
184
|
+
Version 0.6 adds algebra laws, scalar law parameters, curried signatures,
|
|
185
|
+
and conjunctions.
|
|
186
|
+
|
|
187
|
+
```lawspec
|
|
188
|
+
unit example.addition
|
|
189
|
+
|
|
190
|
+
add :: Int32 -> Int32 -> Int32
|
|
191
|
+
|
|
192
|
+
law `addition commutes` is
|
|
193
|
+
definition is
|
|
194
|
+
`commutative` add
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
example `3 plus 5 and 5 plus 3 both produce 8` is
|
|
198
|
+
x = 3
|
|
199
|
+
y = 5
|
|
200
|
+
expect add x y = 8
|
|
201
|
+
expect add y x = 8
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
law `zero is an identity on both sides` is
|
|
206
|
+
definition is
|
|
207
|
+
`identity` add 0
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
example `zero preserves 3 on either side` is
|
|
211
|
+
x = 3
|
|
212
|
+
expect add 0 x = 3
|
|
213
|
+
expect add x 0 = 3
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Arrows associate to the right and application associates to the left:
|
|
219
|
+
`f :: a -> b -> c` takes two arguments, and `f x y` means `(f x) y`.
|
|
220
|
+
A partial application such as `add 1` can be passed to a reusable unary law;
|
|
221
|
+
`sumFour 1 2` can be passed to a binary law. Partial applications also compose.
|
|
222
|
+
The compiler specializes these expressions before emission. Java, Kotlin,
|
|
223
|
+
Python, JavaScript, TypeScript and Go adapters take ordinary positional arguments
|
|
224
|
+
(`add(x, y)`); Haskell adapters use native currying (`add x y`). Argument order
|
|
225
|
+
and types are preserved, including mixtures of `Text`, `Bool` and `Int32`.
|
|
226
|
+
|
|
227
|
+
Law parameters can also be scalar values: `(e :: a)` supplies an identity and
|
|
228
|
+
`(zero :: a)` supplies an absorbing element. Pass literals directly, for example
|
|
229
|
+
`left identity` with arguments `add 0`, or `absorbing element` with `multiply 0`.
|
|
230
|
+
Functions declared by a unit take one or more scalar inputs and return a scalar;
|
|
231
|
+
reusable laws accept these curried functions, their partial applications, and
|
|
232
|
+
scalar parameters. Quantified test inputs remain scalar.
|
|
233
|
+
|
|
234
|
+
The prelude defines the following laws. Every row has an executable example in
|
|
235
|
+
[algebra.lawspec](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/algebra.lawspec), including both sides of every
|
|
236
|
+
combined law. `f` and `g` are binary operations, `inverse` is unary, and `e` and
|
|
237
|
+
`zero` are scalar parameters. All these laws require equality of the element type.
|
|
238
|
+
|
|
239
|
+
| Law and arguments | Equations checked for every quantified input |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| `commutative f` | `f x y = f y x` |
|
|
242
|
+
| `associative f` | `f (f x y) z = f x (f y z)` |
|
|
243
|
+
| `left identity f e` | `f e x = x` |
|
|
244
|
+
| `right identity f e` | `f x e = x` |
|
|
245
|
+
| `identity f e` | Both identity equations |
|
|
246
|
+
| `left absorbing element f zero` | `f zero x = zero` |
|
|
247
|
+
| `right absorbing element f zero` | `f x zero = zero` |
|
|
248
|
+
| `absorbing element f zero` | Both absorbing equations |
|
|
249
|
+
| `left distributive f g` | `f x (g y z) = g (f x y) (f x z)` |
|
|
250
|
+
| `right distributive f g` | `f (g x y) z = g (f x z) (f y z)` |
|
|
251
|
+
| `distributive f g` | Both distributive equations |
|
|
252
|
+
| `idempotent operation f` | `f x x = x` (the existing `idempotent` law is unary) |
|
|
253
|
+
| `left inverse element f inverse e` | `f (inverse x) x = e` |
|
|
254
|
+
| `right inverse element f inverse e` | `f x (inverse x) = e` |
|
|
255
|
+
| `invertible f inverse e` | Both inverse equations |
|
|
256
|
+
| `left division f divideLeft` | `f x (divideLeft x y) = y` and `divideLeft x (f x y) = y` |
|
|
257
|
+
| `right division f divideRight` | `f (divideRight x y) y = x` and `divideRight (f x y) y = x` |
|
|
258
|
+
| `divisible f divideLeft divideRight` | All four division equations |
|
|
259
|
+
| `involution f` | `f (f x) = x` |
|
|
260
|
+
|
|
261
|
+
Here **divisible** means algebraic left/right division. `divideLeft x y` solves
|
|
262
|
+
`f x result = y`; `divideRight x y` solves `f result y = x`. The subtraction
|
|
263
|
+
example deliberately uses a noncommutative operation: with `x = 3` and `y = 5`,
|
|
264
|
+
the left solution is `-2` and the right solution is `8`. Recovery is checked in
|
|
265
|
+
both directions. These are total laws; a partially defined division needs an
|
|
266
|
+
explicit domain predicate and conditional equations.
|
|
267
|
+
|
|
268
|
+
`invertible` checks the supplied inverse operation. Check `identity` and
|
|
269
|
+
`associative` as well when specifying a group. The prelude states contracts;
|
|
270
|
+
it does not supply arithmetic implementations or prove a structure from random
|
|
271
|
+
tests. The numeric examples use Int32 arithmetic modulo 2^32 so their laws hold
|
|
272
|
+
at overflow boundaries on every target. JavaScript uses `Math.imul` for products,
|
|
273
|
+
and Python explicitly wraps results into the signed Int32 range in the test
|
|
274
|
+
adapters.
|
|
275
|
+
|
|
276
|
+
Use `and` to require multiple conclusions in one law. For example:
|
|
277
|
+
|
|
278
|
+
```lawspec
|
|
279
|
+
unit example.absorption
|
|
280
|
+
multiply :: Int32 -> Int32 -> Int32
|
|
281
|
+
|
|
282
|
+
law `zero absorbs on both sides` is
|
|
283
|
+
definition is
|
|
284
|
+
`for all` (x :: Int32) .
|
|
285
|
+
multiply 0 x = 0 and multiply x 0 = 0
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
example `3 times zero and zero times 3 both produce zero` is
|
|
289
|
+
x = 3
|
|
290
|
+
expect multiply 0 x = 0
|
|
291
|
+
expect multiply x 0 = 0
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Quantification and implication extend through the following conjunction:
|
|
297
|
+
`p x implies A and B` guards both conclusions. Write `(p x implies A) and B`
|
|
298
|
+
to guard only the first. A shared guard runs once per check; false guards skip
|
|
299
|
+
their entire consequence. Every conjunct is type-checked and emitted. As with
|
|
300
|
+
existing assertions, the first failure stops that individual test. `and` is now
|
|
301
|
+
a reserved word.
|
|
302
|
+
|
|
303
|
+
[Currying examples](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/currying.lawspec) demonstrate a four-argument
|
|
304
|
+
function partially applied twice, a formatter with four heterogeneous arguments,
|
|
305
|
+
and composition after partial application. Each example states its exact outputs.
|
|
306
|
+
Run `node npm/bin/lawspec.mjs examples` after rebuilding to inspect all nine units
|
|
307
|
+
in all seven target languages (126 artifacts).
|
|
308
|
+
|
|
309
|
+
The expanded API's **`assertion` tree is authoritative**: `AssertEqual` contains
|
|
310
|
+
two expressions, `AssertImplies` contains a condition and consequence, and
|
|
311
|
+
`AssertAll` contains every conjunct. Existing `left`, `right`, and `guards`
|
|
312
|
+
fields are compatibility projections of the first conclusion only; consumers
|
|
313
|
+
checking compound laws must traverse `assertion`. Source definitions add `And`.
|
|
314
|
+
`lawspec explain` prints the full conjunction and its conditional scope.
|
|
315
|
+
|
|
316
|
+
## Predicates and conditional laws (0.5)
|
|
317
|
+
|
|
318
|
+
A predicate is a unary function returning `Bool`. Use `true` and `false` in
|
|
319
|
+
expressions, example bindings, and expected results. A Boolean expression can
|
|
320
|
+
stand alone as a law's definition: it must evaluate to `true`.
|
|
321
|
+
|
|
322
|
+
`condition implies consequence` checks the consequence only when the condition
|
|
323
|
+
is true. Conditions must have type `Bool`; nested implications short-circuit in
|
|
324
|
+
source order. The consequence can be an equality, another implication, a Boolean
|
|
325
|
+
predicate, or a reusable law application. Quantify any inputs before using them.
|
|
326
|
+
|
|
327
|
+
```lawspec
|
|
328
|
+
unit example.parse_port
|
|
329
|
+
|
|
330
|
+
validPort :: Int32 -> Bool
|
|
331
|
+
render :: Int32 -> Text
|
|
332
|
+
parse :: Text -> Int32
|
|
333
|
+
|
|
334
|
+
law `valid ports round trip` is
|
|
335
|
+
definition is
|
|
336
|
+
`for all` (x :: Int32) .
|
|
337
|
+
validPort x implies
|
|
338
|
+
parse (render x) = x
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
example `ordinary port` is
|
|
342
|
+
x = 443
|
|
343
|
+
expect validPort x = true
|
|
344
|
+
expect render x = "443"
|
|
345
|
+
expect parse (render x) = 443
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
example `zero is rejected; the round trip is skipped` is
|
|
349
|
+
x = 0
|
|
350
|
+
expect validPort x = false
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The [complete port example](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/parse_port.lawspec)
|
|
356
|
+
defines valid ports as 1–65535, and covers both endpoints, ordinary ports, zero,
|
|
357
|
+
negative values, and 65536. All explicit `expect` assertions run regardless of
|
|
358
|
+
the law's condition. A false condition skips only the consequence: invalid ports
|
|
359
|
+
never reach `render` or `parse` through the law. Predicate errors still fail the
|
|
360
|
+
test; they are not treated as false.
|
|
361
|
+
|
|
362
|
+
Implication is logical implication, not generator filtering or an assumption.
|
|
363
|
+
Randomized tests still sample the full input domain and count a false condition
|
|
364
|
+
as satisfying the law. A narrow predicate may therefore exercise few or no
|
|
365
|
+
consequences during a random run. Explicit valid examples ensure the important
|
|
366
|
+
cases run, and expectations of both `true` and `false` catch always-false and
|
|
367
|
+
always-true predicate implementations.
|
|
368
|
+
|
|
369
|
+
The prelude includes `satisfies predicate` (the predicate holds for every input)
|
|
370
|
+
and `left inverse when predicate parse render` (the guarded round trip above).
|
|
371
|
+
These reusable laws preserve the condition and its lexical bindings when expanded.
|
|
372
|
+
`equivalent` can also compare two predicates, since `Bool` supports equality.
|
|
373
|
+
The [Boolean flags example](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/boolean_flags.lawspec)
|
|
374
|
+
checks that flipping twice restores both `false` and `true`; all targets generate
|
|
375
|
+
Boolean property inputs and explicit tests for both Boolean boundary values. Java caps Boolean-only JetCheck runs at the number of
|
|
376
|
+
possible input combinations (up to 100), avoiding generator exhaustion.
|
|
377
|
+
|
|
378
|
+
In 0.5, `implies`, `true`, and `false` become reserved words. Existing 0.4 specs
|
|
379
|
+
that use those words as identifiers need renaming. The API adds `BoolLit`,
|
|
380
|
+
`Holds`, and `Implies` AST variants, Boolean literal values, and an ordered
|
|
381
|
+
`guards` array on expanded laws. `lawspec explain` prints the conditions.
|
|
382
|
+
|
|
383
|
+
Kotlin adapters now group functions in an `object` named after the unit (for
|
|
384
|
+
example, `object ParsePort` in package `example`). This allows both the port and
|
|
385
|
+
alternatives units to define `render(Int)`. When upgrading a Kotlin project,
|
|
386
|
+
move existing top-level adapter functions into the indicated object; generation
|
|
387
|
+
preserves your adapter and reports the required stub shape. Generated tests call
|
|
388
|
+
`ParsePort.validPort(...)`, `ParsePort.render(...)`, and `ParsePort.parse(...)`.
|
|
389
|
+
|
|
390
|
+
## Expected results and migration from 0.3
|
|
182
391
|
|
|
183
392
|
Every `example` must bind all quantified inputs and then include one or more
|
|
184
393
|
`expect <expression> = <literal>` assertions. The expected literal must have the
|
|
185
|
-
same `Int32` or `
|
|
394
|
+
same `Int32`, `Text`, or `Bool` type as the expression. Expressions can reference the
|
|
186
395
|
example's inputs and the unit's functions, including composed function calls.
|
|
187
396
|
Input names shadow function names within expectations, following lexical scope.
|
|
188
397
|
|
|
@@ -240,7 +449,7 @@ This expands to `for all (x :: Int32) . render (x) = referenceRender (x)`.
|
|
|
240
449
|
The example inherits the input name `x` from the prelude. Both functions are
|
|
241
450
|
user-owned adapter functions; either may delegate to your existing code.
|
|
242
451
|
|
|
243
|
-
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
452
|
+
[The complete example](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/equivalent.lawspec) compares decimal
|
|
244
453
|
renderers and two implementations that clamp negative integers to zero. For
|
|
245
454
|
JavaScript, their adapters can be:
|
|
246
455
|
|
|
@@ -255,7 +464,7 @@ The same specification generates native tests for all seven targets. The
|
|
|
255
464
|
integration suite checks both examples with matching implementations, then
|
|
256
465
|
breaks each alternative separately to verify detection. The general equivalence law alone does not establish independent correctness;
|
|
257
466
|
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 `
|
|
467
|
+
check the specified outputs at the supplied example inputs. Quantified inputs can be `Int32`, `Text`, or `Bool`.
|
|
259
468
|
|
|
260
469
|
## Text properties and idempotence
|
|
261
470
|
|
|
@@ -277,7 +486,7 @@ law `normalizers agree` is
|
|
|
277
486
|
end
|
|
278
487
|
```
|
|
279
488
|
|
|
280
|
-
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
489
|
+
The [slug example](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/slug.lawspec)
|
|
281
490
|
compares two implementations of ASCII-space replacement. It includes empty,
|
|
282
491
|
Unicode and escaped text. Each target uses its native string generator:
|
|
283
492
|
JetCheck `Generator.stringsOf(Generator.asciiPrintableChars())`, Hypothesis `st.text()`, fast-check `fc.string()`,
|
|
@@ -304,7 +513,7 @@ law `canonicalization reaches a fixed point` is
|
|
|
304
513
|
end
|
|
305
514
|
```
|
|
306
515
|
|
|
307
|
-
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
516
|
+
The [canonical URL example](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/canonical_url.lawspec)
|
|
308
517
|
uses removal of **all trailing slashes** as a small fixed-point demonstration,
|
|
309
518
|
not a complete URL canonicalization algorithm. For JavaScript:
|
|
310
519
|
|
|
@@ -313,10 +522,10 @@ export const canonicalize = value => value.replace(/\/+$/, "");
|
|
|
313
522
|
```
|
|
314
523
|
|
|
315
524
|
Removing just one trailing slash fails the supplied repeated-slash example.
|
|
316
|
-
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.
|
|
525
|
+
The [mixed-input example](https://github.com/brain-fuel/lawspec/blob/v0.6.0/examples/specs/mixed_inputs.lawspec)
|
|
317
526
|
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 }[]`.
|
|
527
|
+
The JavaScript API represents input bindings and expected values as `number | string | boolean`.
|
|
528
|
+
Each example includes `expectations: { actual: Expr; expected: number | string | boolean }[]`.
|
|
320
529
|
|
|
321
530
|
## Generate all example artifacts
|
|
322
531
|
|
|
@@ -378,7 +587,7 @@ by the JS shim.
|
|
|
378
587
|
## Build and verify
|
|
379
588
|
|
|
380
589
|
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.
|
|
590
|
+
`npm pack ./npm` and install it with `npm install --save-dev ./lawspec-0.6.0.tgz`.
|
|
382
591
|
The package payload lives in `npm/`.
|
|
383
592
|
|
|
384
593
|
```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.6.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.6.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": "25d79d741039fe21e1164d789fdf4f698f135df7e4c96c82560e4f7efe8fbdf9",
|
|
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": "64aa042ce2f0f460d691ea43fa27bf75576a49a6e852a068f62a2cf5a28e81b7",
|
|
23
|
+
"src/LawSpec/Emit.hs": "85ab85d789c4e2c98703e2768075cb1c0bed42b987c45b5060b9a9df56e8d291",
|
|
24
|
+
"src/LawSpec/Gen.hs": "c596e411790a47cb15b2945d391b9909cbec1972bf6e502ed8c069db2c67e991",
|
|
25
|
+
"src/LawSpec/Model.hs": "549f553d1cc5d65c804a5acfac0cc06e63ff10eb80674893e32fcd3c0a71a5b3",
|
|
26
|
+
"src/LawSpec/Parser.hs": "ae7c5ad29871b99441db2dae10548afafb2ae4931dd1a62208ca0f5e3545002c",
|
|
27
|
+
"src/LawSpec/Prelude.hs": "b1e155c12f58e7c346551a0c65c5d5af76d045b3f1625977ca2a5d1c97cb8c46",
|
|
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": "d0d9c395b96de75264d5dc62f7995f02b285aebc696a2cf603a62b4e280f9132",
|
|
34
|
+
"npm/core.wasm": "e90e71610ca9f58a1b1e88d65f593c74d2a5b09347a4b47802bd771c954b7e42",
|
|
35
35
|
"npm/core_jsffi.js": "88d136efe92f7cff5758c8fec8d9b6bbc9707fe37741cebeeb415fe34ce3d72b",
|
|
36
36
|
"npm/api.mjs": "d6df654600172131ac66a55b86876fc29cde2a76a0a63bbefff3183488a4837b",
|
|
37
|
-
"npm/index.d.ts": "
|
|
37
|
+
"npm/index.d.ts": "628016928dc71dc735d59d15ccde45500d9a4133e3b20c8d3c21cb9c65ccb604"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/core.wasm
CHANGED
|
Binary file
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
unit example.algebra
|
|
2
|
+
|
|
3
|
+
-- Int32 arithmetic in this example wraps modulo 2^32 on every target.
|
|
4
|
+
add :: Int32 -> Int32 -> Int32
|
|
5
|
+
multiply :: Int32 -> Int32 -> Int32
|
|
6
|
+
negateValue :: Int32 -> Int32
|
|
7
|
+
maximumValue :: Int32 -> Int32 -> Int32
|
|
8
|
+
subtractValue :: Int32 -> Int32 -> Int32
|
|
9
|
+
divideLeft :: Int32 -> Int32 -> Int32
|
|
10
|
+
divideRight :: Int32 -> Int32 -> Int32
|
|
11
|
+
|
|
12
|
+
law `addition is commutative` is
|
|
13
|
+
definition is
|
|
14
|
+
`commutative` add
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
example `3 plus 5 and 5 plus 3 both produce 8` is
|
|
18
|
+
x = 3
|
|
19
|
+
y = 5
|
|
20
|
+
expect add x y = 8
|
|
21
|
+
expect add y x = 8
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
law `addition is associative` is
|
|
26
|
+
definition is
|
|
27
|
+
`associative` add
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
example `both groupings of 3 plus 5 plus 7 produce 15` is
|
|
31
|
+
x = 3
|
|
32
|
+
y = 5
|
|
33
|
+
z = 7
|
|
34
|
+
expect add (add x y) z = 15
|
|
35
|
+
expect add x (add y z) = 15
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
law `zero is left identity for addition` is
|
|
40
|
+
definition is
|
|
41
|
+
`left identity` add 0
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
example `adding zero on left preserves 3` is
|
|
45
|
+
x = 3
|
|
46
|
+
expect add 0 x = 3
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
law `zero is right identity for addition` is
|
|
51
|
+
definition is
|
|
52
|
+
`right identity` add 0
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
example `adding zero on right preserves 3` is
|
|
56
|
+
x = 3
|
|
57
|
+
expect add x 0 = 3
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
law `zero is two-sided identity for addition` is
|
|
62
|
+
definition is
|
|
63
|
+
`identity` add 0
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
example `adding zero on both sides preserves 3` is
|
|
67
|
+
x = 3
|
|
68
|
+
expect add 0 x = 3
|
|
69
|
+
expect add x 0 = 3
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
law `zero absorbs multiplication left` is
|
|
74
|
+
definition is
|
|
75
|
+
`left absorbing element` multiply 0
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
example `zero on left of multiplication produces zero` is
|
|
79
|
+
x = 3
|
|
80
|
+
expect multiply 0 x = 0
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
law `zero absorbs multiplication right` is
|
|
85
|
+
definition is
|
|
86
|
+
`right absorbing element` multiply 0
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
example `zero on right of multiplication produces zero` is
|
|
90
|
+
x = 3
|
|
91
|
+
expect multiply x 0 = 0
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
law `zero absorbs multiplication on both sides` is
|
|
96
|
+
definition is
|
|
97
|
+
`absorbing element` multiply 0
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
example `zero on both sides of multiplication produces zero` is
|
|
101
|
+
x = 3
|
|
102
|
+
expect multiply 0 x = 0
|
|
103
|
+
expect multiply x 0 = 0
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
law `multiplication distributes over addition left` is
|
|
108
|
+
definition is
|
|
109
|
+
`left distributive` multiply add
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
example `left distribution produces 36` is
|
|
113
|
+
x = 3
|
|
114
|
+
y = 5
|
|
115
|
+
z = 7
|
|
116
|
+
expect multiply x (add y z) = 36
|
|
117
|
+
expect add (multiply x y) (multiply x z) = 36
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
law `multiplication distributes over addition right` is
|
|
122
|
+
definition is
|
|
123
|
+
`right distributive` multiply add
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
example `right distribution produces 56` is
|
|
127
|
+
x = 3
|
|
128
|
+
y = 5
|
|
129
|
+
z = 7
|
|
130
|
+
expect multiply (add x y) z = 56
|
|
131
|
+
expect add (multiply x z) (multiply y z) = 56
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
law `multiplication distributes over addition on both sides` is
|
|
136
|
+
definition is
|
|
137
|
+
`distributive` multiply add
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
example `left distribution produces 36; right distribution produces 56` is
|
|
141
|
+
x = 3
|
|
142
|
+
y = 5
|
|
143
|
+
z = 7
|
|
144
|
+
expect multiply x (add y z) = 36
|
|
145
|
+
expect add (multiply x y) (multiply x z) = 36
|
|
146
|
+
expect multiply (add x y) z = 56
|
|
147
|
+
expect add (multiply x z) (multiply y z) = 56
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
law `maximum is an idempotent operation` is
|
|
152
|
+
definition is
|
|
153
|
+
`idempotent operation` maximumValue
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
example `the maximum of 3 and itself is 3` is
|
|
157
|
+
x = 3
|
|
158
|
+
expect maximumValue x x = 3
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
law `negation supplies left additive inverses` is
|
|
163
|
+
definition is
|
|
164
|
+
`left inverse element` add negateValue 0
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
example `3 has inverse -3; the left inverse equation produces zero` is
|
|
168
|
+
x = 3
|
|
169
|
+
expect negateValue x = -3
|
|
170
|
+
expect add (negateValue x) x = 0
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
law `negation supplies right additive inverses` is
|
|
175
|
+
definition is
|
|
176
|
+
`right inverse element` add negateValue 0
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
example `3 has inverse -3; the right inverse equation produces zero` is
|
|
180
|
+
x = 3
|
|
181
|
+
expect negateValue x = -3
|
|
182
|
+
expect add x (negateValue x) = 0
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
law `negation supplies both additive inverses` is
|
|
187
|
+
definition is
|
|
188
|
+
`invertible` add negateValue 0
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
example `3 has inverse -3; the left and right inverse equation produces zero` is
|
|
192
|
+
x = 3
|
|
193
|
+
expect negateValue x = -3
|
|
194
|
+
expect add (negateValue x) x = 0
|
|
195
|
+
expect add x (negateValue x) = 0
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
law `subtraction has left division` is
|
|
200
|
+
definition is
|
|
201
|
+
`left division` subtractValue divideLeft
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
example `left solution is -2 since 3 - (-2) = 5; reversing the operation recovers 5` is
|
|
205
|
+
x = 3
|
|
206
|
+
y = 5
|
|
207
|
+
expect divideLeft x y = -2
|
|
208
|
+
expect subtractValue x (divideLeft x y) = 5
|
|
209
|
+
expect divideLeft x (subtractValue x y) = 5
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
law `subtraction has right division` is
|
|
214
|
+
definition is
|
|
215
|
+
`right division` subtractValue divideRight
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
example `right solution is 8 since 8 - 5 = 3; reversing the operation recovers 3` is
|
|
219
|
+
x = 3
|
|
220
|
+
y = 5
|
|
221
|
+
expect divideRight x y = 8
|
|
222
|
+
expect subtractValue (divideRight x y) y = 3
|
|
223
|
+
expect divideRight (subtractValue x y) y = 3
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
law `subtraction has divisible` is
|
|
228
|
+
definition is
|
|
229
|
+
`divisible` subtractValue divideLeft divideRight
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
example `left solution is -2; right solution is 8; both recovery directions hold` is
|
|
233
|
+
x = 3
|
|
234
|
+
y = 5
|
|
235
|
+
expect divideLeft x y = -2
|
|
236
|
+
expect subtractValue x (divideLeft x y) = 5
|
|
237
|
+
expect divideLeft x (subtractValue x y) = 5
|
|
238
|
+
expect divideRight x y = 8
|
|
239
|
+
expect subtractValue (divideRight x y) y = 3
|
|
240
|
+
expect divideRight (subtractValue x y) y = 3
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
law `negation is an involution` is
|
|
245
|
+
definition is
|
|
246
|
+
`involution` negateValue
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
example `negating -3 twice restores -3` is
|
|
250
|
+
x = -3
|
|
251
|
+
expect negateValue x = 3
|
|
252
|
+
expect negateValue (negateValue x) = -3
|
|
253
|
+
end
|
|
254
|
+
end
|
|
@@ -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,60 @@
|
|
|
1
|
+
unit example.currying
|
|
2
|
+
|
|
3
|
+
sumFour :: Int32 -> Int32 -> Int32 -> Int32 -> Int32
|
|
4
|
+
format :: Text -> Bool -> Int32 -> Text -> Text
|
|
5
|
+
referenceFormat :: Text -> Bool -> Int32 -> Text -> Text
|
|
6
|
+
trim :: Text -> Text
|
|
7
|
+
|
|
8
|
+
law `a four-argument function partially applied twice is commutative` is
|
|
9
|
+
definition is
|
|
10
|
+
`commutative` (sumFour 1 2)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
example `the fixed 1 and 2 plus 3 and 4 sum to 10 in either order` is
|
|
14
|
+
x = 3
|
|
15
|
+
y = 4
|
|
16
|
+
expect sumFour 1 2 x y = 10
|
|
17
|
+
expect sumFour 1 2 y x = 10
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
law `staged formatter`
|
|
22
|
+
(render :: Int32 -> Text -> Text)
|
|
23
|
+
(reference :: Int32 -> Text -> Text)
|
|
24
|
+
is
|
|
25
|
+
definition is
|
|
26
|
+
`for all` (port :: Int32) (suffix :: Text) .
|
|
27
|
+
render port suffix = reference port suffix
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
law `partial application preserves heterogeneous argument order` is
|
|
32
|
+
definition is
|
|
33
|
+
`staged formatter` (format "port:" true) (referenceFormat "port:" true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
example `port 443 with a tcp suffix formats as port:443/tcp` is
|
|
37
|
+
port = 443
|
|
38
|
+
suffix = "/tcp"
|
|
39
|
+
expect format "port:" true port suffix = "port:443/tcp"
|
|
40
|
+
expect referenceFormat "port:" true port suffix = "port:443/tcp"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
example `a disabled numeric field keeps the prefix and suffix` is
|
|
44
|
+
port = 443
|
|
45
|
+
suffix = "/tcp"
|
|
46
|
+
expect format "port:" false port suffix = "port:/tcp"
|
|
47
|
+
expect referenceFormat "port:" false port suffix = "port:/tcp"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
law `composition accepts a partially applied function` is
|
|
52
|
+
definition is
|
|
53
|
+
`equivalent` ((format "port:" true 443) . trim) ((referenceFormat "port:" true 443) . trim)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
example `trim runs before the remaining formatter argument` is
|
|
57
|
+
x = " /tcp "
|
|
58
|
+
expect ((format "port:" true 443) . trim) x = "port:443/tcp"
|
|
59
|
+
end
|
|
60
|
+
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,13 @@ 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: 'And'; contents: [Definition, 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
|
|
13
|
+
export type Assertion = {tag: 'AssertEqual'; contents: [Expr, Expr]} | {tag: 'AssertImplies'; contents: [Expr, Assertion]} | {tag: 'AssertAll'; contents: Assertion[]};
|
|
14
|
+
export interface Expanded { owner: string; name: string; inputs: {inputName: string; inputId: string; inputType: Type}[]; left: Expr; right: Expr; guards: Expr[]; assertion: Assertion; trace: string[]; original: Law }
|
|
14
15
|
export interface CheckRequest { sources: Source[] }
|
|
15
16
|
export interface GenerationRequest extends CheckRequest { target: Target; sourceDir?: string; testDir?: string }
|
|
16
17
|
export interface Result { diagnostics: Diagnostic[]; laws?: Expanded[]; expansions?: string[]; files?: Artifact[] }
|