Skip to content
This repository was archived by the owner on Aug 20, 2021. It is now read-only.

Commit 24fab1b

Browse files
d-xoxwvvvvwx
authored andcommitted
act: add where block
allow definition of per act macro substitutions. substitution is regex based and occurs as part of klab build.
1 parent 8df2b9d commit 24fab1b

File tree

10 files changed

+129
-3
lines changed

10 files changed

+129
-3
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ media/%.pdf: media/%.md
6161
KLAB = $(CURDIR)/bin/klab
6262

6363
# doing them in this order should save time
64-
examples := $(addprefix examples/,heal multipleCalls token SafeAdd multipleInternals createContract)
64+
examples := $(addprefix examples/,heal multipleCalls token SafeAdd multipleInternals createContract where)
6565

6666
# this is now redundant
6767
build-test:

acts.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,13 @@ Lists the behaviours that this spec calls. If the listed behaviour's `_pass` pro
201201
calls
202202
DSToken.transferFrom
203203
```
204-
204+
205+
### where
206+
Allows for the definition of named expressions. All instances of the name on the LHS of the `where` binding will be expanded to the RHS as part of `klab build`. Helps to reduce duplication in specs for methods that have lots of intermediate variables.
207+
208+
```
209+
where
210+
211+
Var := X + Y
212+
```
213+

examples/where/config.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"src": {
3+
"specification": "src/specification.act.md",
4+
"smt_prelude": "src/prelude.smt2.md",
5+
"rules": [
6+
"src/lemmas.k.md"
7+
]
8+
},
9+
"implementations": {
10+
"Calculate": {
11+
"src": "src/Calculate.sol"
12+
}
13+
},
14+
"dapp_root": "dapp"
15+
}
16+

examples/where/dapp/out/dapp.sol.json

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
pragma solidity >=0.4.21;
2+
3+
contract Calculate {
4+
function add(uint x, uint y) internal pure returns (uint z) {
5+
require((z = x + y) >= x);
6+
}
7+
function sub(uint x, uint y) internal pure returns (uint z) {
8+
require((z = x - y) <= x);
9+
}
10+
function mul(uint x, uint y) internal pure returns (uint z) {
11+
require(y == 0 || (z = x * y) / y == x);
12+
}
13+
14+
function run(uint a, uint b) public pure returns (uint e) {
15+
uint c = sub(a, b);
16+
uint d = mul(c, b);
17+
e = add(d, a);
18+
}
19+
}

examples/where/src/lemmas.k.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
```k
2+
rule A -Word B <=Int A => #rangeUInt(256, A -Int B)
3+
requires #rangeUInt(256, A) andBool #rangeUInt(256, B)
4+
```
5+

examples/where/src/prelude.smt2.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
```smt2
2+
```
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
```act
2+
behaviour run of Calculate
3+
interface run(uint256 A, uint256 B)
4+
5+
iff in range uint256
6+
7+
C
8+
D
9+
E
10+
11+
iff
12+
13+
VCallValue == 0
14+
15+
where
16+
17+
C := A - B
18+
D := C * B
19+
E := D + C
20+
21+
returns E
22+
```

lib/build.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,43 @@ const parseAct = config => (act_str, silent = false) => {
430430
a.returns = returns
431431
} else if(/^returnsRaw/.test(head)) {
432432
a.returnsRaw = head.split(" ").slice(1).join(" ")
433+
} else if(/^where/.test(head)) {
434+
a.whereBindings = tail
435+
.map(l => l.trim())
436+
.filter(l => l != "")
437+
.map(l => mapInterface(a.interface, toK(l).trim()))
438+
.map(l => ({
439+
/* matches the LHS of the macro if it is:
440+
* preceded by:
441+
* - ', ' or ','
442+
* - '('
443+
* - the start of a line
444+
* - a tab
445+
* - a space
446+
* and followed by:
447+
* - ','
448+
* - ')'
449+
* - the end of a line
450+
* - a tab
451+
* - a space
452+
*
453+
* example here: https://regex101.com/r/ZubkN1/3
454+
*/
455+
"from": new RegExp(`(, ?|\\(|^|\\t| )(${l.split(":=")[0].trim()})(,|\\)|$|\\t| )`, "gm"),
456+
/*
457+
* replaces with the first capture group from the expression
458+
* above ('$1'), then the RHS of the macro (surrounded by
459+
* brackets), then the third capture group from above ('$3')
460+
*/
461+
"to": `$1(${l.split(":=")[1].trim()})$3`
462+
}))
463+
464+
// apply every where binding in turn to every other where binding
465+
for (i = 0; i < a.whereBindings.length; i++) {
466+
for (j = 0; j < a.whereBindings.length; j++) {
467+
a.whereBindings[j].to = a.whereBindings[j].to.replace(a.whereBindings[i].from, a.whereBindings[i].to);
468+
}
469+
}
433470
} else if(/^calls/.test(head)) {
434471
a.calls = tail
435472
.map(l => l.trim())
@@ -943,6 +980,7 @@ const buildActs = (config, act_proofs) => {
943980
// .filter(r => r.file_suffix)
944981
// .map(r => r.file_suffix)
945982
// .join('\n\n')
983+
rule.spec = applyWhereBindings(rule.spec, extractWhereBindings(rule.act))
946984
const _rules = [rule.spec].concat(rule.ctx.map(r => r.spec + "\n[trusted]\n"))
947985
const module = kjson.renderModule(_rules, rule.name) // TODO maybe skip this
948986
const hash = config.get_proof_hash({name: rule.name, spec: module});
@@ -979,6 +1017,21 @@ const buildActs = (config, act_proofs) => {
9791017

9801018
}
9811019

1020+
// convenience wrapper to ensure we always have something to iterate over when
1021+
// dealing with where bindings
1022+
const extractWhereBindings = (act) => {
1023+
if (typeof act.whereBindings === "undefined") return []
1024+
return act.whereBindings
1025+
}
1026+
1027+
// apply where bindings to the spec
1028+
const applyWhereBindings = (spec, bindings) => {
1029+
for (const b of bindings) {
1030+
spec = spec.replace(b.from, b.to)
1031+
}
1032+
return spec
1033+
}
1034+
9821035
module.exports = {
9831036
proofCollection,
9841037
makePrelude,

lib/storage.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
const path = require("path")
22
const marked = require("marked");
33
const Config = require("../lib/config.js");
4-
const {parseAct} = require("../lib/build.js");
54
const {
65
testPath,
76
read,

0 commit comments

Comments
 (0)