Spyro synthesizes provably sound, most-precise specifications from given function definitions.
The artifact requires dependencies if you try to run on your local machine
- python (version >= 3.6), including
- numpy (only for
run_benchmarks.py)
- numpy (only for
- Sketch (version >= 1.7.6)
- Note: The tar-ball in the Sketch wiki is 1.7.5, which may not work
You should add sketch-frontend directory to the environment variable $PATH.
Alternatively, you may set path to the Sketch binary, by editing config file:
SKETCH_PATH=<PATH TO SKETCH-FRONTENT>/sketch
To run spyro on default setting, run python3 spyro.py <PATH-TO-INPUT-FILE>.
This will synthesize minimized properties from input file, and print the result to stdout.
infiles: Input files. Use concatenation of all files as the input code.outfile: Output file. Default isstdout-v, --verbose: Print descriptive messages, and leave all the temporary files.--write-log: Write trace log if enabled.--timeout: Timeout of each query to Sketch. Default is 300s.--disable-min: Disable formula minimization.--keep-neg-may: Disable freezing negative examples.--num-atom-max: Number of disjuncts. Default is 3.--inline-bnd: Number of inlining/unrolling. Default is 5.
Spyro[Sketch] takes one or more files as input and treats those files as concatenated into a single input file. Each set of input file must contain exactly one definition for var, relation, generator, and example. The following is an example of each section for list reverse function.
// Input and output variables
var {
list l;
list lout;
}
// Target functions that we aim to synthesize specifications
// The last argument to the function is output
relation {
reverse(l, lout);
}
// The DSL for the specifictions.
// It uses the top predicate as a grammar for each disjunct.
// The maximum number of disjuncts are provided by the option "--num-atom-max"
// compare is macro for { == | != | <= | >= | < | > }
// ??(n) denotes arbitrary positive integer of n bits
// Provide only input arguments to function call
generator {
boolean AP -> is_empty(L) | !is_empty(L)
| equal_list(L, L) | !equal_list(L, L)
| compare(S, S + ??(1));
int S -> len(L) | 0 ;
list L -> l | lout ;
}
// recursive constructor for each type
// Provide only input arguments to function call
// integer is chosen from arbitrary positive or negative 3-bits integer
example {
int -> ??(3) | -1 * ??(3) ;
list -> nil() | cons(int, list);
}
// The return value of function is passed by reference
void reverse(list l, ref list ret) {
if (l == null) {
ret = null;
} else {
list tl_reverse;
reverse(l.tl, tl_reverse);
snoc(tl_reverse, l.hd, ret);
}
}
The synthesize properties are given as a code that returns a Boolean value, where the value is stored in the variable out.
It means that the value stored in out must always be true.
For example, the following is synthesized properties of application1/list/reverse.sp:
Property 0
bit var_50 = 0;
equal_list(lout, l, var_50);
int var_52 = 0;
len(l, var_52);
bit out_s1 = var_50 || (var_52 > 1);
out = out_s1;
Property 1
int var_102 = 0;
len(lout, var_102);
int var_102_0 = 0;
len(l, var_102_0);
out = var_102 == var_102_0;
The property 0 means
The property 1 means