lawspec 0.5.0 → 0.7.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 +85 -0
- package/PRIMITIVES.md +150 -0
- package/README.md +174 -21
- package/REFINEMENTS.md +158 -0
- package/bin/lawspec.mjs +24 -7
- package/build.json +37 -11
- package/core.wasm +0 -0
- package/examples/specs/algebra.lawspec +254 -0
- package/examples/specs/currying.lawspec +60 -0
- package/examples/specs/refinements.lawspec +75 -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 +1 -1
- package/index.d.ts +21 -10
- package/package.json +2 -2
- package/scalars.mjs +13 -0
- package/templates.mjs +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
unit example.refinements
|
|
2
|
+
|
|
3
|
+
refinement Between (T :: Type) (minimum :: T) (maximum :: T)
|
|
4
|
+
requires Ordered T is
|
|
5
|
+
(value :: T where minimum <= value && value <= maximum)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
refinement Positive (T :: Type) requires Integer T is
|
|
9
|
+
(value :: T where value > 0)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
refinement AdditionOverflows (T :: Type) (left :: T)
|
|
13
|
+
requires Integer T Bounded T is
|
|
14
|
+
(right :: T where left + right > T.max)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add :: (x :: Int8)
|
|
18
|
+
-> (y :: AdditionOverflows Int8 x)
|
|
19
|
+
-> (result :: Integer where result == x + y)
|
|
20
|
+
successor :: (x :: Int8) -> (result :: Integer where result == x + 1)
|
|
21
|
+
count :: (text :: Text where prelude.length text > 0)
|
|
22
|
+
-> (result :: Integer where result == prelude.length text)
|
|
23
|
+
preserve :: (x :: UInt64) -> (result :: Integer where result == x)
|
|
24
|
+
positive :: (x :: Positive Int8) -> (result :: Positive Int8 where result == x)
|
|
25
|
+
|
|
26
|
+
law `maximum input` is
|
|
27
|
+
definition is `for all` (x :: Int8) . successor x = x + 1 end
|
|
28
|
+
example `beyond Int8` is x = 127 expect successor x = 128 end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
law `overflow pairs` is
|
|
32
|
+
definition is
|
|
33
|
+
`for all` (x :: Int8) (y :: AdditionOverflows Int8 x) .
|
|
34
|
+
add x y = x + y
|
|
35
|
+
end
|
|
36
|
+
example `smallest overflowing sum` is x = 127 y = 1 expect add x y = 128 end
|
|
37
|
+
example `largest sum` is x = 127 y = 127 expect add x y = 254 end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
law `bounded input` is
|
|
41
|
+
definition is `for all` (x :: Between Int8 1 3) . positive x = x end
|
|
42
|
+
example `middle` is x = 2 expect positive x = 2 end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
law `large integer` is
|
|
46
|
+
definition is `for all` (x :: UInt64) . preserve x = x end
|
|
47
|
+
example `maximum UInt64` is x = 18446744073709551615 expect preserve x = 18446744073709551615 end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
abstractEcho :: (x :: Integer) -> (result :: Integer where result == x)
|
|
51
|
+
|
|
52
|
+
law `nested presence` is
|
|
53
|
+
definition is `for all` (x :: Optional (Nullable (Positive Int8))) . x = x end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
law `finite positive floats` is
|
|
57
|
+
definition is
|
|
58
|
+
`for all` (x :: Float32 where prelude.isFinite x && x > 0) .
|
|
59
|
+
prelude.isFinite x
|
|
60
|
+
end
|
|
61
|
+
example `positive` is x = 0.5 expect x = 0.5 end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
law `raw byte length` is
|
|
65
|
+
definition is `for all` (x :: Bytes where prelude.length x == 3) . prelude.length x = 3 end
|
|
66
|
+
example `arbitrary octets` is x = bytes([0,128,255]) expect prelude.length x = 3 end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
law `guarded predicate arithmetic` is
|
|
70
|
+
definition is `for all` (x :: Int8) (y :: Int8 where x != 0 && y > 10 / x) . y > 10 / x end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
law `machine bound` is
|
|
74
|
+
definition is `for all` (x :: IntSize where x == IntSize.max) . x = IntSize.max end
|
|
75
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
unit example.scalar_adapters
|
|
2
|
+
echoChar :: Char -> Char
|
|
3
|
+
echoCodePoint :: CodePoint -> CodePoint
|
|
4
|
+
echoCodeUnit :: CodeUnit16 -> CodeUnit16
|
|
5
|
+
echoBytes :: Bytes -> Bytes
|
|
6
|
+
echoComplex :: Complex64 -> Complex64
|
|
7
|
+
successor :: Int8 -> BigInt
|
|
8
|
+
narrow :: Int8 -> Int8
|
|
9
|
+
addDecimal :: Decimal -> Decimal -> Decimal
|
|
10
|
+
sameSymbol :: Symbol -> Symbol -> Bool
|
|
11
|
+
echoRaw :: Utf16Text -> Utf16Text
|
|
12
|
+
echoPresence :: Optional (Nullable Int8) -> Optional (Nullable Int8)
|
|
13
|
+
finish :: Unit -> Unit
|
|
14
|
+
preserveBig :: UInt64 -> UInt64
|
|
15
|
+
machineEcho :: IntSize -> IntSize
|
|
16
|
+
|
|
17
|
+
law `promoted successor` is
|
|
18
|
+
definition is `for all` (x :: Int8) . successor x = x + 1 end
|
|
19
|
+
example `no overflow` is x = 127 expect successor x = 128 end
|
|
20
|
+
end
|
|
21
|
+
law `checked bridge` is
|
|
22
|
+
definition is `for all` (x :: Int8) . narrow (x + 0) = x end
|
|
23
|
+
example `maximum` is x = 127 expect narrow (x + 0) = 127 end
|
|
24
|
+
end
|
|
25
|
+
law `exact decimal adapter` is
|
|
26
|
+
definition is `for all` (x :: Decimal) (y :: Decimal) . addDecimal x y = x + y end
|
|
27
|
+
example `tenths` is x = 0.1 y = 0.2 expect addDecimal x y = 0.3 end
|
|
28
|
+
end
|
|
29
|
+
law `symbol identity adapter` is
|
|
30
|
+
definition is `for all` (x :: Symbol) (y :: Symbol) . sameSymbol x x and sameSymbol y y end
|
|
31
|
+
example `same description distinct identities` is
|
|
32
|
+
x = symbol("a", "same") y = symbol("b", "same")
|
|
33
|
+
expect sameSymbol x y = false
|
|
34
|
+
expect sameSymbol x x = true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
law `raw adapter` is
|
|
38
|
+
definition is `for all` (x :: Utf16Text) . echoRaw x = x end
|
|
39
|
+
example `lone surrogate` is x = utf16([55296]) expect echoRaw x = utf16([55296]) end
|
|
40
|
+
end
|
|
41
|
+
law `presence adapter` is
|
|
42
|
+
definition is `for all` (x :: Optional (Nullable Int8)) . echoPresence x = x end
|
|
43
|
+
example `missing` is x = undefined expect echoPresence x = undefined end
|
|
44
|
+
example `null present` is x = optional(null) expect echoPresence x = optional(null) end
|
|
45
|
+
end
|
|
46
|
+
law `unit adapter` is
|
|
47
|
+
definition is `for all` (x :: Unit) . finish x = unitValue end
|
|
48
|
+
example `completed` is x = unitValue expect finish x = unitValue end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
law `no safe-number truncation` is
|
|
52
|
+
definition is `for all` (x :: UInt64) . preserveBig x = x end
|
|
53
|
+
example `unsigned maximum` is x = 18446744073709551615 expect preserveBig x = 18446744073709551615 end
|
|
54
|
+
end
|
|
55
|
+
law `machine profile binding` is
|
|
56
|
+
definition is `for all` (x :: IntSize) . machineEcho x = x end
|
|
57
|
+
example `portable width` is x = 2147483647 expect machineEcho x = 2147483647 end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
law `Char native bridge` is
|
|
61
|
+
definition is `for all` (x :: Char) . echoChar x = x end
|
|
62
|
+
example `native value` is x = char(128512) expect echoChar x = char(128512) end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
law `CodePoint native bridge` is
|
|
66
|
+
definition is `for all` (x :: CodePoint) . echoCodePoint x = x end
|
|
67
|
+
example `native value` is x = codePoint(55296) expect echoCodePoint x = codePoint(55296) end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
law `CodeUnit16 native bridge` is
|
|
71
|
+
definition is `for all` (x :: CodeUnit16) . echoCodeUnit x = x end
|
|
72
|
+
example `native value` is x = codeUnit16(65535) expect echoCodeUnit x = codeUnit16(65535) end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
law `Bytes native bridge` is
|
|
76
|
+
definition is `for all` (x :: Bytes) . echoBytes x = x end
|
|
77
|
+
example `native value` is x = bytes([0,128,255]) expect echoBytes x = bytes([0,128,255]) end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
law `Complex64 native bridge` is
|
|
81
|
+
definition is `for all` (x :: Complex64) . prelude.isNaN (prelude.real (echoComplex x)) = prelude.isNaN (prelude.real x) end
|
|
82
|
+
example `native value` is x = complex64(1,2) expect echoComplex x = complex64(1,2) end
|
|
83
|
+
end
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
unit example.scalar_catalog
|
|
2
|
+
|
|
3
|
+
law `Bool representation` is
|
|
4
|
+
definition is `for all` (x :: Bool) . x = x end
|
|
5
|
+
example `explicit value` is x = true expect x = true end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
law `Int8 representation` is
|
|
9
|
+
definition is `for all` (x :: Int8) . x = x end
|
|
10
|
+
example `explicit value` is x = -128 expect x = -128 end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
law `Int16 representation` is
|
|
14
|
+
definition is `for all` (x :: Int16) . x = x end
|
|
15
|
+
example `explicit value` is x = 32767 expect x = 32767 end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
law `Int32 representation` is
|
|
19
|
+
definition is `for all` (x :: Int32) . x = x end
|
|
20
|
+
example `explicit value` is x = 2147483647 expect x = 2147483647 end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
law `Int64 representation` is
|
|
24
|
+
definition is `for all` (x :: Int64) . x = x end
|
|
25
|
+
example `explicit value` is x = 9223372036854775807 expect x = 9223372036854775807 end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
law `UInt8 representation` is
|
|
29
|
+
definition is `for all` (x :: UInt8) . x = x end
|
|
30
|
+
example `explicit value` is x = 255 expect x = 255 end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
law `UInt16 representation` is
|
|
34
|
+
definition is `for all` (x :: UInt16) . x = x end
|
|
35
|
+
example `explicit value` is x = 65535 expect x = 65535 end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
law `UInt32 representation` is
|
|
39
|
+
definition is `for all` (x :: UInt32) . x = x end
|
|
40
|
+
example `explicit value` is x = 4294967295 expect x = 4294967295 end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
law `UInt64 representation` is
|
|
44
|
+
definition is `for all` (x :: UInt64) . x = x end
|
|
45
|
+
example `explicit value` is x = 18446744073709551615 expect x = 18446744073709551615 end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
law `IntSize representation` is
|
|
49
|
+
definition is `for all` (x :: IntSize) . x = x end
|
|
50
|
+
example `explicit value` is x = 2147483647 expect x = 2147483647 end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
law `UIntSize representation` is
|
|
54
|
+
definition is `for all` (x :: UIntSize) . x = x end
|
|
55
|
+
example `explicit value` is x = 4294967295 expect x = 4294967295 end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
law `UIntPtr representation` is
|
|
59
|
+
definition is `for all` (x :: UIntPtr) . x = x end
|
|
60
|
+
example `explicit value` is x = 4294967295 expect x = 4294967295 end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
law `BigInt representation` is
|
|
64
|
+
definition is `for all` (x :: BigInt) . x = x end
|
|
65
|
+
example `explicit value` is x = -1234567890123456789012345678901234567890 expect x = -1234567890123456789012345678901234567890 end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
law `BigUInt representation` is
|
|
69
|
+
definition is `for all` (x :: BigUInt) . x = x end
|
|
70
|
+
example `explicit value` is x = 1234567890123456789012345678901234567890 expect x = 1234567890123456789012345678901234567890 end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
law `Decimal representation` is
|
|
74
|
+
definition is `for all` (x :: Decimal) . x = x end
|
|
75
|
+
example `explicit value` is x = 0.000000000000000000000000000000000001 expect x = 0.000000000000000000000000000000000001 end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
law `Rational representation` is
|
|
79
|
+
definition is `for all` (x :: Rational) . x = x end
|
|
80
|
+
example `explicit value` is x = rational(-7, 11) expect x = rational(-7, 11) end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
law `Float32 representation` is
|
|
84
|
+
definition is `for all` (x :: Float32) . prelude.isNaN x = prelude.isNaN x end
|
|
85
|
+
example `explicit value` is x = float32Bits("80000000") expect x = float32Bits("80000000") end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
law `Float64 representation` is
|
|
89
|
+
definition is `for all` (x :: Float64) . prelude.isNaN x = prelude.isNaN x end
|
|
90
|
+
example `explicit value` is x = float64Bits("7ff0000000000000") expect x = float64Bits("7ff0000000000000") end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
law `Complex64 representation` is
|
|
94
|
+
definition is `for all` (x :: Complex64) . prelude.isNaN (prelude.real x) = prelude.isNaN (prelude.real x) end
|
|
95
|
+
example `explicit value` is x = complex64(1, -2) expect x = complex64(1, -2) end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
law `Complex128 representation` is
|
|
99
|
+
definition is `for all` (x :: Complex128) . prelude.isNaN (prelude.real x) = prelude.isNaN (prelude.real x) end
|
|
100
|
+
example `explicit value` is x = complex128(0.5, -3.5) expect x = complex128(0.5, -3.5) end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
law `Char representation` is
|
|
104
|
+
definition is `for all` (x :: Char) . x = x end
|
|
105
|
+
example `explicit value` is x = char(128512) expect x = char(128512) end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
law `CodePoint representation` is
|
|
109
|
+
definition is `for all` (x :: CodePoint) . x = x end
|
|
110
|
+
example `explicit value` is x = codePoint(55296) expect x = codePoint(55296) end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
law `CodeUnit16 representation` is
|
|
114
|
+
definition is `for all` (x :: CodeUnit16) . x = x end
|
|
115
|
+
example `explicit value` is x = codeUnit16(65535) expect x = codeUnit16(65535) end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
law `Text representation` is
|
|
119
|
+
definition is `for all` (x :: Text) . x = x end
|
|
120
|
+
example `explicit value` is x = "λ日本語😀" expect x = "λ日本語😀" end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
law `CodePointText representation` is
|
|
124
|
+
definition is `for all` (x :: CodePointText) . x = x end
|
|
125
|
+
example `explicit value` is x = codePoints([55296, 1114111]) expect x = codePoints([55296, 1114111]) end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
law `Utf16Text representation` is
|
|
129
|
+
definition is `for all` (x :: Utf16Text) . x = x end
|
|
130
|
+
example `explicit value` is x = utf16([55296, 0, 65535]) expect x = utf16([55296, 0, 65535]) end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
law `Bytes representation` is
|
|
134
|
+
definition is `for all` (x :: Bytes) . x = x end
|
|
135
|
+
example `explicit value` is x = bytes([0, 128, 255]) expect x = bytes([0, 128, 255]) end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
law `Symbol representation` is
|
|
139
|
+
definition is `for all` (x :: Symbol) . x = x end
|
|
140
|
+
example `explicit value` is x = symbol("catalog", "same") expect x = symbol("catalog", "same") end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
law `Unit representation` is
|
|
144
|
+
definition is `for all` (x :: Unit) . x = x end
|
|
145
|
+
example `explicit value` is x = unitValue expect x = unitValue end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
law `Null representation` is
|
|
149
|
+
definition is `for all` (x :: Null) . x = x end
|
|
150
|
+
example `explicit value` is x = null expect x = null end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
law `Undefined representation` is
|
|
154
|
+
definition is `for all` (x :: Undefined) . x = x end
|
|
155
|
+
example `explicit value` is x = undefined expect x = undefined end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
law `Nullable Int8 representation` is
|
|
159
|
+
definition is `for all` (x :: Nullable Int8) . x = x end
|
|
160
|
+
example `explicit value` is x = nullable(127) expect x = nullable(127) end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
law `Optional Int8 representation` is
|
|
164
|
+
definition is `for all` (x :: Optional Int8) . x = x end
|
|
165
|
+
example `explicit value` is x = undefined expect x = undefined end
|
|
166
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
unit example.scalars
|
|
2
|
+
|
|
3
|
+
law `integer promotion` is
|
|
4
|
+
definition is `for all` (x :: Int8) . x + 1 = 1 + x end
|
|
5
|
+
example `overflow is preserved` is
|
|
6
|
+
x = 127
|
|
7
|
+
expect x + 1 = 128
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
law `unsigned precision` is
|
|
12
|
+
definition is `for all` (x :: UInt64) . x + 0 = x end
|
|
13
|
+
example `maximum` is
|
|
14
|
+
x = 18446744073709551615
|
|
15
|
+
expect x + 1 = 18446744073709551616
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
law `decimal exactness` is
|
|
20
|
+
definition is `for all` (x :: Decimal) . x + 0.1 = 0.1 + x end
|
|
21
|
+
example `tenths` is
|
|
22
|
+
x = 0.1
|
|
23
|
+
expect x + 0.2 = 0.3
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
law `exact division` is
|
|
28
|
+
definition is `for all` (x :: Int16) . x / 2 = x / 2 end
|
|
29
|
+
example `one half` is
|
|
30
|
+
x = 1
|
|
31
|
+
expect x / 2 = rational(1, 2)
|
|
32
|
+
expect prelude.quot -7 3 = -2
|
|
33
|
+
expect prelude.rem -7 3 = -1
|
|
34
|
+
expect prelude.round rational(125, 100) 1 = 1.2
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
law `IEEE classification` is
|
|
39
|
+
definition is `for all` (x :: Float32) . prelude.isNaN x = prelude.isNaN x end
|
|
40
|
+
example `NaN` is
|
|
41
|
+
x = float32Bits("7fc00000")
|
|
42
|
+
expect prelude.isNaN x = true
|
|
43
|
+
end
|
|
44
|
+
example `negative zero` is
|
|
45
|
+
x = float32Bits("80000000")
|
|
46
|
+
expect prelude.isNegativeZero x = true
|
|
47
|
+
expect x = (0 :: Float32)
|
|
48
|
+
end
|
|
49
|
+
example `infinity` is
|
|
50
|
+
x = float32Bits("7f800000")
|
|
51
|
+
expect prelude.isInfinite x = true
|
|
52
|
+
expect prelude.Float32 0.1 = (0.1 :: Float32)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
law `complex arithmetic` is
|
|
57
|
+
definition is `for all` (x :: Unit) . complex64(1, 2) * complex64(3, 4) = complex64(-5, 10) end
|
|
58
|
+
example `product` is
|
|
59
|
+
x = unitValue
|
|
60
|
+
expect complex64(1, 2) * complex64(3, 4) = complex64(-5, 10)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
law `raw units survive` is
|
|
65
|
+
definition is `for all` (x :: Utf16Text) . x = x end
|
|
66
|
+
example `unpaired surrogate` is
|
|
67
|
+
x = utf16([55296, 0, 56320])
|
|
68
|
+
expect x = utf16([55296, 0, 56320])
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
law `arbitrary bytes` is
|
|
73
|
+
definition is `for all` (x :: Bytes) . x = x end
|
|
74
|
+
example `octets` is
|
|
75
|
+
x = bytes([0, 128, 255])
|
|
76
|
+
expect x = bytes([0, 128, 255])
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
law `symbol fixture identity` is
|
|
81
|
+
definition is `for all` (x :: Symbol) . x = x end
|
|
82
|
+
example `same reference` is
|
|
83
|
+
x = symbol("first", "description")
|
|
84
|
+
expect x = symbol("first", "description")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
law `nested absence` is
|
|
89
|
+
definition is `for all` (x :: Optional (Nullable Int8)) . x = x end
|
|
90
|
+
example `undefined` is
|
|
91
|
+
x = undefined
|
|
92
|
+
expect x = undefined
|
|
93
|
+
end
|
|
94
|
+
example `present null` is
|
|
95
|
+
x = optional(null)
|
|
96
|
+
expect x = optional(null)
|
|
97
|
+
end
|
|
98
|
+
example `present integer` is
|
|
99
|
+
x = optional(nullable(7))
|
|
100
|
+
expect x = optional(nullable(7))
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
law `supplementary scalar` is
|
|
105
|
+
definition is `for all` (x :: Char) . x = x end
|
|
106
|
+
example `emoji` is
|
|
107
|
+
x = char(128512)
|
|
108
|
+
expect x = char(128512)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
law `surrogate code point` is
|
|
113
|
+
definition is `for all` (x :: CodePointText) . x = x end
|
|
114
|
+
example `surrogate` is
|
|
115
|
+
x = codePoints([55296, 128512])
|
|
116
|
+
expect x = codePoints([55296, 128512])
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
law `explicit float rounding` is
|
|
121
|
+
definition is `for all` (x :: Unit) . x = x end
|
|
122
|
+
example `IEEE halfway cases` is
|
|
123
|
+
x = unitValue
|
|
124
|
+
expect prelude.Complex128 complex64(1, 2) = complex128(1, 2)
|
|
125
|
+
expect prelude.Complex64 complex128(1.25, -2.5) = complex64(1.25, -2.5)
|
|
126
|
+
expect prelude.Float32 rational(16777217, 16777216) = float32Bits("3f800000")
|
|
127
|
+
expect prelude.Float32 rational(1208925891672223212634113, 1208925819614629174706176) = float32Bits("3f800001")
|
|
128
|
+
expect prelude.Float64 rational(9007199254740993, 9007199254740992) = float64Bits("3ff0000000000000")
|
|
129
|
+
expect prelude.Float64 rational(1606938044258990453947923680586147734807949174969684883144705, 1606938044258990275541962092341162602522202993782792835301376) = float64Bits("3ff0000000000001")
|
|
130
|
+
expect prelude.Float32 rational(1, 1427247692705959881058285969449495136382746624) = float32Bits("00000000")
|
|
131
|
+
expect prelude.Float32 rational(1125899906842625, 1606938044258990275541962092341162602522202993782792835301376) = float32Bits("00000001")
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
law `short circuit exact failures` is
|
|
136
|
+
definition is `for all` (x :: Unit) . false implies 1 / 0 = rational(1, 2) end
|
|
137
|
+
example `guard prevents division` is x = unitValue expect x = unitValue end
|
|
138
|
+
end
|
package/examples-command.mjs
CHANGED
|
@@ -21,7 +21,7 @@ export async function generateExamples(options) {
|
|
|
21
21
|
const compiler = await createCompiler();
|
|
22
22
|
const artifacts = [];
|
|
23
23
|
for (const target of selected) {
|
|
24
|
-
const result = await compiler.planGeneration({ sources, target });
|
|
24
|
+
const result = await compiler.planGeneration({ sources, target, machineBits: options.machineBits ?? 64 });
|
|
25
25
|
if (result.diagnostics.length)
|
|
26
26
|
throw Object.assign(new Error("Bundled examples failed to compile"), {
|
|
27
27
|
diagnostics: result.diagnostics,
|
package/index.d.ts
CHANGED
|
@@ -3,16 +3,27 @@ export type Target = 'java' | 'python' | 'javascript' | 'typescript' | 'go' | 'h
|
|
|
3
3
|
export interface Source { path: string; content: string }
|
|
4
4
|
export interface Location { file: string; line: number; column: number }
|
|
5
5
|
export interface Diagnostic { code: string; message: string; at: Location | null }
|
|
6
|
-
export interface Artifact { path: string; content: string; ownership: 'user' | 'generated' }
|
|
7
|
-
export type Type = {tag: 'Named' | 'Variable'; contents: string} | {tag: 'Arrow'; contents: [Type, Type]};
|
|
8
|
-
export
|
|
9
|
-
export type
|
|
10
|
-
export interface
|
|
11
|
-
export interface
|
|
12
|
-
export interface
|
|
13
|
-
export interface
|
|
14
|
-
export interface
|
|
6
|
+
export interface Artifact { path: string; content: string; ownership: 'user' | 'generated'; placement: 'source' | 'test' }
|
|
7
|
+
export type Type = {tag: 'Named' | 'Variable'; contents: string} | {tag: 'Arrow'; contents: [Type, Type]} | {tag: 'Applied'; contents: ['Nullable' | 'Optional', Type]} | {tag:'Refined'; contents:[string, Type, Expr | null]} | {tag:'Qualified'; contents:[Constraint[],Type]} | {tag:'CheckedType'; contents:[Expr[],Type]} | {tag:'RefinementApp'; contents:[string,RefinementArgument[]]};
|
|
8
|
+
export interface Constraint {tag:'Capability'; contents:['Eq'|'Integer'|'Ordered'|'Bounded',Type]}
|
|
9
|
+
export type RefinementArgument = {tag:'TypeArgument'; contents:Type} | {tag:'ValueArgument'; contents:Expr};
|
|
10
|
+
export interface Refinement {refinementName:string; refinementParameters:[string,Type][]; refinementRequirements:Constraint[]; refinementBody:Type}
|
|
11
|
+
export interface Contract {contractName:string; contractArguments:[string,Type][]; contractResult:[string,Type]; contractPreconditions:Expr[]; contractPostconditions:Expr[]}
|
|
12
|
+
export interface Generation {cases:number; maxAttempts:number; maxShrinks:number; exhaustiveLimit:number}
|
|
13
|
+
export interface Input {inputName:string; inputId:string; inputType:Type; inputRefinements:Expr[]}
|
|
14
|
+
export interface DomainPlan {domainInput:Input; domainBounds:[string,Expr][]}
|
|
15
|
+
export type IntegerType = 'Int8' | 'Int16' | 'Int32' | 'Int64' | 'UInt8' | 'UInt16' | 'UInt32' | 'UInt64' | 'IntSize' | 'UIntSize' | 'UIntPtr' | 'Integer' | 'BigInt' | 'BigUInt';
|
|
16
|
+
export type ScalarValue = {type: IntegerType; value: string} | {type: 'Bool'; value: boolean} | {type: 'Decimal'; coefficient: string; exponent: string} | {type: 'Rational'; numerator: string; denominator: string} | {type: 'Float32' | 'Float64'; bits: string} | {type: 'Complex64' | 'Complex128'; real: ScalarValue; imaginary: ScalarValue} | {type: 'Char' | 'CodePoint' | 'CodeUnit16'; value: number} | {type: 'Text' | 'CodePointText' | 'Utf16Text' | 'Bytes'; units: number[]} | {type: 'Symbol'; id: string; description: string} | {type: 'Unit' | 'Null' | 'Undefined'} | {type: 'Nullable' | 'Optional'; value: ScalarValue | null};
|
|
17
|
+
export type Expr = {tag: 'Var' | 'StringLit'; contents: string} | {tag: 'Number'; contents: string} | {tag: 'DecimalNumber'; contents: [string, string]} | {tag: 'BoolLit'; contents: boolean} | {tag: 'Apply' | 'Compose'; contents: [Expr, Expr]} | {tag: 'ScalarLit'; contents: ScalarValue} | {tag: 'Annotate'; contents: [Expr, Type]} | {tag: 'Unary'; contents: [string, Expr]} | {tag: 'Binary'; contents: [string, Expr, Expr]} | {tag:'TypeBound'; contents:['min'|'max',Type]};
|
|
18
|
+
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[]]};
|
|
19
|
+
export interface Expectation { actual: Expr; expected: ScalarValue }
|
|
20
|
+
export interface Example { exampleName: string; bindings: [string, ScalarValue][]; expectations: Expectation[] }
|
|
21
|
+
export interface Law { lawName: string; parameters: [string, Type][]; requirements: Constraint[]; definition: Definition; description: string; rationale: string; examples: Example[]; references: string[]; location: Location }
|
|
22
|
+
export type Assertion = {tag: 'AssertEqual'; contents: [Expr, Expr]} | {tag: 'AssertImplies'; contents: [Expr, Assertion]} | {tag: 'AssertAll'; contents: Assertion[]};
|
|
23
|
+
export interface Expanded { owner: string; name: string; inputs: Input[]; left: Expr; right: Expr; guards: Expr[]; assertion: Assertion; trace: string[]; original: Law; typedExpressions: TypedExpr[]; propertyKind: 'law'|'contract'; generation:Generation; generationPlan:DomainPlan[] }
|
|
24
|
+
export interface TypedExpr { expressionType: Type; expression: Expr; operands: TypedExpr[]; requiredConversion: Type | null }
|
|
25
|
+
export interface CheckRequest { sources: Source[]; schemaVersion?: 2; machineBits?: 32 | 64; generation?:Partial<Generation> }
|
|
15
26
|
export interface GenerationRequest extends CheckRequest { target: Target; sourceDir?: string; testDir?: string }
|
|
16
|
-
export interface Result { diagnostics: Diagnostic[]; laws?: Expanded[]; expansions?: string[]; files?: Artifact[] }
|
|
27
|
+
export interface Result { schemaVersion: 2; machineBits?: 32 | 64; generation?:Generation; refinements?:{owner:string; declaration:Refinement}[]; contracts?:{owner:string; contract:Contract}[]; diagnostics: Diagnostic[]; laws?: Expanded[]; expansions?: string[]; files?: Artifact[] }
|
|
17
28
|
export interface Compiler { check(input: CheckRequest): Promise<Result>; expand(input: CheckRequest): Promise<Result>; planGeneration(input: GenerationRequest): Promise<Result> }
|
|
18
29
|
export function createCompiler(): Promise<Compiler>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lawspec",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "State the law once. Check it everywhere.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"bin": { "lawspec": "bin/lawspec.mjs" },
|
|
9
9
|
"exports": { ".": { "types": "./index.d.ts", "import": "./api.mjs" } },
|
|
10
10
|
"types": "index.d.ts",
|
|
11
|
-
"files": ["*.mjs", "*.json", "index.d.ts", "bin", "core.wasm", "core_jsffi.js", "README.md", "LICENSE", "starter.lawspec", "examples"],
|
|
11
|
+
"files": ["*.mjs", "*.json", "index.d.ts", "bin", "core.wasm", "core_jsffi.js", "README.md", "PRIMITIVES.md", "REFINEMENTS.md", "API-MIGRATION.md", "LICENSE", "starter.lawspec", "examples"],
|
|
12
12
|
"scripts": { "test": "node --test test/*.test.mjs" }
|
|
13
13
|
}
|
package/scalars.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Human-readable, lossless API v2 scalar display.
|
|
2
|
+
export function showScalar(v) {
|
|
3
|
+
if (!v || typeof v !== 'object') throw new TypeError('Expected an API v2 scalar');
|
|
4
|
+
if ('coefficient' in v) return `${v.coefficient}e${v.exponent}`;
|
|
5
|
+
if ('numerator' in v) return `rational(${v.numerator}, ${v.denominator})`;
|
|
6
|
+
if ('bits' in v) return `${v.type === 'Float32' ? 'float32Bits' : 'float64Bits'}(${JSON.stringify(v.bits)})`;
|
|
7
|
+
if ('real' in v) return `${v.type}(${showScalar(v.real)}, ${showScalar(v.imaginary)})`;
|
|
8
|
+
if ('units' in v) return v.type === 'Text' ? JSON.stringify(v.units.map(c => String.fromCodePoint(c)).join('')) : `${v.type}([${v.units.join(', ')}])`;
|
|
9
|
+
if (v.type === 'Symbol') return `symbol(${JSON.stringify(v.id)}, ${JSON.stringify(v.description)})`;
|
|
10
|
+
if (v.type === 'Optional' || v.type === 'Nullable') return v.value === null ? (v.type === 'Optional' ? 'undefined' : 'null') : `${v.type.toLowerCase()}(${showScalar(v.value)})`;
|
|
11
|
+
if ('value' in v) return String(v.value);
|
|
12
|
+
return {Unit:'unitValue', Null:'null', Undefined:'undefined'}[v.type] ?? v.type;
|
|
13
|
+
}
|
package/templates.mjs
CHANGED
|
@@ -100,7 +100,7 @@ export function templates(target) {
|
|
|
100
100
|
case "haskell":
|
|
101
101
|
return {
|
|
102
102
|
"stack.yaml": "snapshot: lts-24.58\npackages: [.]\n",
|
|
103
|
-
"package.yaml": `name: lawspec-example\nversion: 0.1.0\ndependencies: [base, text]\nlibrary:\n source-dirs: src\ntests:\n laws:\n main: Spec.hs\n source-dirs: test\n dependencies: [lawspec-example, hspec, hedgehog, hspec-hedgehog]\n build-tools: [hspec-discover]\n`,
|
|
103
|
+
"package.yaml": `name: lawspec-example\nversion: 0.1.0\ndependencies: [base, text, bytestring]\nlibrary:\n source-dirs: src\ntests:\n laws:\n main: Spec.hs\n source-dirs: test\n dependencies: [lawspec-example, hspec, hedgehog, hspec-hedgehog]\n build-tools: [hspec-discover]\n`,
|
|
104
104
|
"test/Spec.hs": "{-# OPTIONS_GHC -F -pgmF hspec-discover #-}\n",
|
|
105
105
|
};
|
|
106
106
|
case "kotlin":
|