зеркало из https://github.com/microsoft/ivy.git
working on target=test
This commit is contained in:
Родитель
6cce55a9b4
Коммит
63028b2ab4
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
layout: page
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
Modular specifications have uses other than verification. Imagine the
|
||||
we have interface specifications of all of the components of the
|
||||
system. By assume/guarantee reasoning, we know that if all the
|
||||
component implementations satisfy their specifications, then the
|
||||
system as a while satisfies its service specification. We don't yet
|
||||
have formal proofs yet that the component implementations are correct.
|
||||
|
||||
In this scenario we can use *compositional testing* to improve our
|
||||
confidence in the system's correctness. In this approach, we test each
|
||||
component rigorously against its formal specification. If we have high
|
||||
confidence in the correctness of all of the components, this
|
||||
confidence transfers to the system as a whole by way of our
|
||||
assume/guarantee argument. Put another way, if the system as a whole
|
||||
fails to satisfy its specification, then necessarily one of its
|
||||
components fails its specificaiton and we can discover thhis fact by
|
||||
component testing.
|
||||
|
||||
The question is, how can we test the components rigorously in a way
|
||||
that will give us high confidence of their correctness? One
|
||||
possiblility is to use a *constrained random* approach. That is, we
|
||||
automatically generate test inputs for the component in a way that
|
||||
satisfies its assumptions. We then check that the component's outputs
|
||||
satisfy its guarantees. The purpose of randomness is to avoid bias
|
||||
that might creep into a manually generated test suite or testbench.
|
||||
|
||||
Ivy can do just that. It can extract a component and a randomized test
|
||||
environment for that component. The test environment generates inputs
|
||||
for the component, calling its exported actions with input parameters
|
||||
that satisfy the component's assumptions. It also checks that all the
|
||||
components outputs (including its call-backs to imported actions)
|
||||
satisy the component's guarantees.
|
||||
|
||||
This sort of rigorous coomponent-based testing combines the advantages
|
||||
of unit testing and integration testing. Like informal unit testing,
|
||||
the method has the advantage that the component's inputs can be
|
||||
controlled directly. This gives us much more flexibility to cover the
|
||||
component's "corner cases" and to expose design errors. Unlike
|
||||
informal unit testing, however, the method uses only the component's
|
||||
specification, eliminating the possibility of human bias, and giving a
|
||||
definitive reference for evaluating the test results. Like integration
|
||||
testing, compositional testing allows us to gain confidence in the
|
||||
correctness of the system as a whole. Compositional testing can be
|
||||
much faster, hwoever, becuase it takes many fewer steps to stimulate a
|
||||
component bug through the component's interface rather than through
|
||||
the system's interface. In addition, of course, we do not have to
|
||||
execute the entire system to test it compositionally.
|
||||
|
||||
# An example
|
||||
|
||||
Let's consider a trivial example, to see how to create a test
|
||||
environment using ivy.
|
|
@ -1040,8 +1040,11 @@ def create_isolate(iso,mod = None,**kwargs):
|
|||
for name,mixins in mod.mixins.iteritems():
|
||||
for mixin in mixins:
|
||||
action1,action2 = (lookup_action(mixin,mod,a.relname) for a in mixin.args)
|
||||
mixed_name = mixin.args[1].relname
|
||||
if mixed_name in orig_exports and isinstance(mixin,ivy_ast.MixinBeforeDef):
|
||||
action1 = action1.assert_to_assume()
|
||||
mixed = ia.apply_mixin(mixin,action1,action2)
|
||||
mod.actions[mixin.args[1].relname] = mixed
|
||||
mod.actions[mixed_name] = mixed
|
||||
# find the globally exported actions (all if none specified, for compat)
|
||||
if mod.exports:
|
||||
mod.public_actions.clear()
|
||||
|
|
|
@ -691,6 +691,14 @@ class Sig(object):
|
|||
return
|
||||
del self.symbols[symbol.name]
|
||||
|
||||
def contains_symbol(self,symbol):
|
||||
if symbol.name not in self.symbols:
|
||||
return False
|
||||
sort = self.symbols[symbol.name].sort
|
||||
if isinstance(sort,UnionSort):
|
||||
return symbol.sort in sort.sorts
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
return sig_to_str(self)
|
||||
|
||||
|
|
|
@ -279,6 +279,8 @@ class init_gen : public gen {
|
|||
public:
|
||||
init_gen();
|
||||
""")
|
||||
header.append(" bool generate(" + classname + "&);\n")
|
||||
header.append(" bool execute(" + classname + "&){}\n};\n")
|
||||
impl.append("init_gen::init_gen(){\n");
|
||||
indent_level += 1
|
||||
emit_sig(impl)
|
||||
|
@ -297,13 +299,12 @@ public:
|
|||
impl.append('))");\n')
|
||||
indent_level -= 1
|
||||
impl.append("}\n");
|
||||
header.append(" bool generate(" + classname + "&);\n};\n")
|
||||
impl.append("bool init_gen::generate(" + classname + "& obj) {\n")
|
||||
indent_level += 1
|
||||
for sym in all_state_symbols():
|
||||
if slv.solver_name(sym) != None: # skip interpreted symbols
|
||||
if slv.solver_name(il.normalize_symbol(sym)) != None: # skip interpreted symbols
|
||||
global is_derived
|
||||
if sym not in is_derived:
|
||||
if sym_is_member(sym):
|
||||
emit_randomize(impl,sym)
|
||||
indent_level -= 1
|
||||
impl.append("""
|
||||
|
@ -343,74 +344,36 @@ def emit_randomize(header,symbol):
|
|||
# indent(header)
|
||||
# header.append('randomize("{}");\n'.format(slv.solver_name(symbol)))
|
||||
|
||||
def emit_action_gen(header,impl,name,action,classname):
|
||||
global indent_level
|
||||
caname = varname(name)
|
||||
upd = action.update(im.module,None)
|
||||
pre = tr.reverse_image(ilu.true_clauses(),ilu.true_clauses(),upd)
|
||||
pre_clauses = ilu.trim_clauses(pre)
|
||||
pre_clauses = ilu.and_clauses(pre_clauses,ilu.Clauses([ldf.formula.to_constraint() for ldf in im.module.definitions]))
|
||||
pre = pre_clauses.to_formula()
|
||||
syms = [x for x in ilu.used_symbols_ast(pre) if x.name not in il.sig.symbols]
|
||||
header.append("class " + caname + "_gen : public gen {\n public:\n")
|
||||
for sym in syms:
|
||||
if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx:
|
||||
declare_symbol(header,sym)
|
||||
header.append(" {}_gen();\n".format(caname))
|
||||
impl.append(caname + "_gen::" + caname + "_gen(){\n");
|
||||
indent_level += 1
|
||||
emit_sig(impl)
|
||||
for sym in syms:
|
||||
emit_decl(impl,sym)
|
||||
|
||||
indent(impl)
|
||||
impl.append('add("(assert {})");\n'.format(slv.formula_to_z3(pre).sexpr().replace('\n','\\\n')))
|
||||
indent_level -= 1
|
||||
impl.append("}\n");
|
||||
header.append(" bool generate(" + classname + "&);\n};\n");
|
||||
impl.append("bool " + caname + "_gen::generate(" + classname + "& obj) {\n push();\n")
|
||||
indent_level += 1
|
||||
pre_used = ilu.used_symbols_ast(pre)
|
||||
for sym in all_state_symbols():
|
||||
if sym in pre_used and sym not in pre_clauses.defidx: # skip symbols not used in constraint
|
||||
if slv.solver_name(sym) != None: # skip interpreted symbols
|
||||
global is_derived
|
||||
if sym not in is_derived:
|
||||
emit_set(impl,sym)
|
||||
for sym in syms:
|
||||
if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx:
|
||||
emit_randomize(impl,sym)
|
||||
impl.append("""
|
||||
bool res = solve();
|
||||
if (res) {
|
||||
""")
|
||||
indent_level += 1
|
||||
for sym in syms:
|
||||
if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx:
|
||||
emit_eval(impl,sym)
|
||||
indent_level -= 2
|
||||
impl.append("""
|
||||
}
|
||||
pop();
|
||||
obj.___ivy_gen = this;
|
||||
return res;
|
||||
}
|
||||
""")
|
||||
|
||||
def is_local_sym(sym):
|
||||
sym = il.normalize_symbol(sym)
|
||||
return not il.sig.contains_symbol(sym) and slv.solver_name(il.normalize_symbol(sym)) != None
|
||||
|
||||
def emit_action_gen(header,impl,name,action,classname):
|
||||
global indent_level
|
||||
caname = varname(name)
|
||||
upd = action.update(im.module,None)
|
||||
pre = tr.reverse_image(ilu.true_clauses(),ilu.true_clauses(),upd)
|
||||
iu.dbg('name')
|
||||
iu.dbg('pre')
|
||||
pre_clauses = ilu.trim_clauses(pre)
|
||||
iu.dbg('pre_clauses')
|
||||
pre_clauses = ilu.and_clauses(pre_clauses,ilu.Clauses([ldf.formula.to_constraint() for ldf in im.module.definitions]))
|
||||
pre = pre_clauses.to_formula()
|
||||
syms = [x for x in ilu.used_symbols_ast(pre) if x.name not in il.sig.symbols]
|
||||
used = set(ilu.used_symbols_ast(pre))
|
||||
used_names = set(varname(s) for s in used)
|
||||
for p in action.formal_params:
|
||||
if varname(p) not in used_names:
|
||||
used.add(p)
|
||||
syms = [x for x in used if is_local_sym(x)]
|
||||
iu.dbg('syms')
|
||||
header.append("class " + caname + "_gen : public gen {\n public:\n")
|
||||
for sym in syms:
|
||||
if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx:
|
||||
declare_symbol(header,sym)
|
||||
header.append(" {}_gen();\n".format(caname))
|
||||
header.append(" bool generate(" + classname + "&);\n");
|
||||
header.append(" bool execute(" + classname + "&);\n};\n");
|
||||
impl.append(caname + "_gen::" + caname + "_gen(){\n");
|
||||
indent_level += 1
|
||||
emit_sig(impl)
|
||||
|
@ -421,15 +384,15 @@ def emit_action_gen(header,impl,name,action,classname):
|
|||
impl.append('add("(assert {})");\n'.format(slv.formula_to_z3(pre).sexpr().replace('\n','\\\n')))
|
||||
indent_level -= 1
|
||||
impl.append("}\n");
|
||||
header.append(" bool generate(" + classname + "&);\n};\n");
|
||||
impl.append("bool " + caname + "_gen::generate(" + classname + "& obj) {\n push();\n")
|
||||
indent_level += 1
|
||||
pre_used = ilu.used_symbols_ast(pre)
|
||||
for sym in all_state_symbols():
|
||||
if sym in pre_used and sym not in pre_clauses.defidx: # skip symbols not used in constraint
|
||||
if slv.solver_name(sym) != None: # skip interpreted symbols
|
||||
global is_derived
|
||||
if sym not in is_derived:
|
||||
iu.dbg('sym')
|
||||
if slv.solver_name(il.normalize_symbol(sym)) != None: # skip interpreted symbols
|
||||
if sym_is_member(sym):
|
||||
iu.dbg('"got here"')
|
||||
emit_set(impl,sym)
|
||||
for sym in syms:
|
||||
if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx:
|
||||
|
@ -450,6 +413,16 @@ def emit_action_gen(header,impl,name,action,classname):
|
|||
return res;
|
||||
}
|
||||
""")
|
||||
open_scope(impl,line="bool " + caname + "_gen::execute(" + classname + "& obj)")
|
||||
code_line(impl,'std::cout << "> {}("'.format(name) + ' << "," '.join(' << {}'.format(varname(p)) for p in action.formal_params) + ' << ")" << std::endl')
|
||||
call = 'obj.{}('.format(caname) + ','.join(varname(p) for p in action.formal_params) + ')'
|
||||
if len(action.formal_returns) == 0:
|
||||
code_line(impl,call)
|
||||
else:
|
||||
code_line(impl,'std::out << "= " << ' + call)
|
||||
close_scope(impl)
|
||||
|
||||
|
||||
def emit_derived(header,impl,df,classname):
|
||||
name = df.defines().name
|
||||
sort = df.defines().sort.rng
|
||||
|
@ -727,7 +700,7 @@ def check_member_names(classname):
|
|||
raise iu.IvyError(None,'Cannot create C++ class {} with member {}.\nUse command line option classname=... to change the class name.'
|
||||
.format(classname,classname))
|
||||
|
||||
def module_to_cpp_class(classname):
|
||||
def module_to_cpp_class(classname,basename):
|
||||
check_member_names(classname)
|
||||
global is_derived
|
||||
is_derived = set()
|
||||
|
@ -747,6 +720,7 @@ def module_to_cpp_class(classname):
|
|||
header.append('extern void ivy_assume(bool,const char *);\n')
|
||||
header.append('extern void ivy_check_progress(int,int);\n')
|
||||
header.append('extern int choose(int,int);\n')
|
||||
if target.get() in ["gen","test"]:
|
||||
header.append('struct ivy_gen {virtual int choose(int rng,const char *name) = 0;};\n')
|
||||
header.append('#include <vector>\n')
|
||||
|
||||
|
@ -762,7 +736,7 @@ def module_to_cpp_class(classname):
|
|||
|
||||
header.append('class ' + classname + ' {\n public:\n')
|
||||
header.append(' std::vector<int> ___ivy_stack;\n')
|
||||
if target.get() == "gen":
|
||||
if target.get() in ["gen","test"]:
|
||||
header.append(' ivy_gen *___ivy_gen;\n')
|
||||
header.append(' int ___ivy_choose(int rng,const char *name,int id);\n')
|
||||
if target.get() != "gen":
|
||||
|
@ -770,7 +744,7 @@ def module_to_cpp_class(classname):
|
|||
header.append(' virtual void ivy_assume(bool,const char *){}\n')
|
||||
header.append(' virtual void ivy_check_progress(int,int){}\n')
|
||||
|
||||
impl = ['#include "' + classname + '.h"\n\n']
|
||||
impl = ['#include "' + basename + '.h"\n\n']
|
||||
impl.append("#include <sstream>\n")
|
||||
impl.append("#include <algorithm>\n")
|
||||
impl.append("""
|
||||
|
@ -873,7 +847,7 @@ void __deser<bool>(const std::vector<char> &inp, unsigned &pos, bool &res) {
|
|||
|
||||
""")
|
||||
|
||||
if target.get() == "repl":
|
||||
if True or target.get() == "repl":
|
||||
for sort_name in sorted(im.module.sort_destructors):
|
||||
csname = varname(sort_name)
|
||||
cfsname = classname + '::' + csname
|
||||
|
@ -899,7 +873,7 @@ void __deser<bool>(const std::vector<char> &inp, unsigned &pos, bool &res) {
|
|||
|
||||
|
||||
impl.append("int " + classname)
|
||||
if target.get() == "gen":
|
||||
if target.get() in ["gen","test"]:
|
||||
impl.append(
|
||||
"""::___ivy_choose(int rng,const char *name,int id) {
|
||||
std::ostringstream ss;
|
||||
|
@ -959,7 +933,7 @@ void __deser<bool>(const std::vector<char> &inp, unsigned &pos, bool &res) {
|
|||
for sortname in il.sig.interp:
|
||||
if sortname in il.sig.sorts:
|
||||
impl.append(' __CARD__{} = {};\n'.format(varname(sortname),csortcard(il.sig.sorts[sortname])))
|
||||
if target.get() != "gen":
|
||||
if target.get() not in ["gen","test"]:
|
||||
emit_one_initial_state(impl)
|
||||
for native in im.module.natives:
|
||||
tag = native_type(native)
|
||||
|
@ -975,14 +949,15 @@ void __deser<bool>(const std::vector<char> &inp, unsigned &pos, bool &res) {
|
|||
|
||||
impl.append('}\n')
|
||||
|
||||
if target.get() == "gen":
|
||||
emit_boilerplate1(header,impl)
|
||||
emit_init_gen(header,impl,classname)
|
||||
if target.get() in ["gen","test"]:
|
||||
sf = header if target.get() == "gen" else impl
|
||||
emit_boilerplate1(sf,impl,classname)
|
||||
emit_init_gen(sf,impl,classname)
|
||||
for name,action in im.module.actions.iteritems():
|
||||
if name in im.module.public_actions:
|
||||
emit_action_gen(header,impl,name,action,classname)
|
||||
emit_action_gen(sf,impl,name,action,classname)
|
||||
|
||||
if target.get() == "repl":
|
||||
if True or target.get() == "repl":
|
||||
|
||||
for sort_name in sorted(im.module.sort_destructors):
|
||||
destrs = im.module.sort_destructors[sort_name]
|
||||
|
@ -1028,102 +1003,107 @@ void __deser<bool>(const std::vector<char> &inp, unsigned &pos, bool &res) {
|
|||
close_scope(impl)
|
||||
|
||||
|
||||
emit_repl_imports(header,impl,classname)
|
||||
emit_repl_boilerplate1(header,impl,classname)
|
||||
if target.get() in ["repl","test"]:
|
||||
emit_repl_imports(header,impl,classname)
|
||||
emit_repl_boilerplate1(header,impl,classname)
|
||||
|
||||
for sort_name in sorted(im.module.sort_destructors):
|
||||
destrs = im.module.sort_destructors[sort_name]
|
||||
sort = im.module.sig.sorts[sort_name]
|
||||
csname = varname(sort_name)
|
||||
cfsname = classname + '::' + csname
|
||||
impl.append('template <>\n')
|
||||
open_scope(impl,line=cfsname + ' _arg<' + cfsname + '>(std::vector<ivy_value> &args, unsigned idx, int bound)')
|
||||
code_line(impl,cfsname + ' res')
|
||||
code_line(impl,'ivy_value &arg = args[idx]')
|
||||
code_line(impl,'if (arg.atom.size() || arg.fields.size() != {}) throw out_of_bounds(idx)'.format(len(destrs)))
|
||||
code_line(impl,'std::vector<ivy_value> tmp_args(1)')
|
||||
for idx,sym in enumerate(destrs):
|
||||
open_scope(impl,line='if (arg.fields[{}].is_member())'.format(idx))
|
||||
code_line(impl,'tmp_args[0] = arg.fields[{}].fields[0]'.format(idx))
|
||||
fname = memname(sym)
|
||||
code_line(impl,'if (arg.fields[{}].atom != "{}") throw out_of_bounds(idx)'.format(idx,fname))
|
||||
close_scope(impl)
|
||||
open_scope(impl,line='else')
|
||||
code_line(impl,'tmp_args[0] = arg.fields[{}]'.format(idx))
|
||||
close_scope(impl)
|
||||
vs = variables(sym.sort.dom[1:])
|
||||
for v in vs:
|
||||
open_scope(impl)
|
||||
code_line(impl,'ivy_value tmp = tmp_args[0]')
|
||||
code_line(impl,'if(tmp.atom.size() || tmp.fields.size() != {}) throw out_of_bounds(idx)'.format(csortcard(v.sort)))
|
||||
open_loop(impl,[v])
|
||||
code_line(impl,'std::vector<ivy_value> tmp_args(1)')
|
||||
code_line(impl,'tmp_args[0] = tmp.fields[{}]'.format(varname(v)))
|
||||
code_line(impl,'res.'+fname+''.join('[{}]'.format(varname(v)) for v in vs) + ' = _arg<'+ctype(sym.sort.rng,classname=classname)
|
||||
+'>(tmp_args,0,{});\n'.format(csortcard(sym.sort.rng)))
|
||||
for v in vs:
|
||||
close_loop(impl,[v])
|
||||
for sort_name in sorted(im.module.sort_destructors):
|
||||
destrs = im.module.sort_destructors[sort_name]
|
||||
sort = im.module.sig.sorts[sort_name]
|
||||
csname = varname(sort_name)
|
||||
cfsname = classname + '::' + csname
|
||||
impl.append('template <>\n')
|
||||
open_scope(impl,line=cfsname + ' _arg<' + cfsname + '>(std::vector<ivy_value> &args, unsigned idx, int bound)')
|
||||
code_line(impl,cfsname + ' res')
|
||||
code_line(impl,'ivy_value &arg = args[idx]')
|
||||
code_line(impl,'if (arg.atom.size() || arg.fields.size() != {}) throw out_of_bounds(idx)'.format(len(destrs)))
|
||||
code_line(impl,'std::vector<ivy_value> tmp_args(1)')
|
||||
for idx,sym in enumerate(destrs):
|
||||
open_scope(impl,line='if (arg.fields[{}].is_member())'.format(idx))
|
||||
code_line(impl,'tmp_args[0] = arg.fields[{}].fields[0]'.format(idx))
|
||||
fname = memname(sym)
|
||||
code_line(impl,'if (arg.fields[{}].atom != "{}") throw out_of_bounds(idx)'.format(idx,fname))
|
||||
close_scope(impl)
|
||||
code_line(impl,'return res')
|
||||
close_scope(impl)
|
||||
open_scope(impl,line='else')
|
||||
code_line(impl,'tmp_args[0] = arg.fields[{}]'.format(idx))
|
||||
close_scope(impl)
|
||||
vs = variables(sym.sort.dom[1:])
|
||||
for v in vs:
|
||||
open_scope(impl)
|
||||
code_line(impl,'ivy_value tmp = tmp_args[0]')
|
||||
code_line(impl,'if(tmp.atom.size() || tmp.fields.size() != {}) throw out_of_bounds(idx)'.format(csortcard(v.sort)))
|
||||
open_loop(impl,[v])
|
||||
code_line(impl,'std::vector<ivy_value> tmp_args(1)')
|
||||
code_line(impl,'tmp_args[0] = tmp.fields[{}]'.format(varname(v)))
|
||||
code_line(impl,'res.'+fname+''.join('[{}]'.format(varname(v)) for v in vs) + ' = _arg<'+ctype(sym.sort.rng,classname=classname)
|
||||
+'>(tmp_args,0,{});\n'.format(csortcard(sym.sort.rng)))
|
||||
for v in vs:
|
||||
close_loop(impl,[v])
|
||||
close_scope(impl)
|
||||
code_line(impl,'return res')
|
||||
close_scope(impl)
|
||||
|
||||
impl.append('template <>\n')
|
||||
open_scope(impl,line='void __deser<' + cfsname + '>(const std::vector<char> &inp, unsigned &pos, ' + cfsname + ' &res)')
|
||||
for idx,sym in enumerate(destrs):
|
||||
fname = memname(sym)
|
||||
vs = variables(sym.sort.dom[1:])
|
||||
for v in vs:
|
||||
open_loop(impl,[v])
|
||||
code_line(impl,'__deser(inp,pos,res.'+fname+''.join('[{}]'.format(varname(v)) for v in vs) + ')')
|
||||
for v in vs:
|
||||
close_loop(impl,[v])
|
||||
close_scope(impl)
|
||||
|
||||
|
||||
impl.append('template <>\n')
|
||||
open_scope(impl,line='void __deser<' + cfsname + '>(const std::vector<char> &inp, unsigned &pos, ' + cfsname + ' &res)')
|
||||
for idx,sym in enumerate(destrs):
|
||||
fname = memname(sym)
|
||||
vs = variables(sym.sort.dom[1:])
|
||||
for v in vs:
|
||||
open_loop(impl,[v])
|
||||
code_line(impl,'__deser(inp,pos,res.'+fname+''.join('[{}]'.format(varname(v)) for v in vs) + ')')
|
||||
for v in vs:
|
||||
close_loop(impl,[v])
|
||||
close_scope(impl)
|
||||
|
||||
emit_repl_boilerplate1a(header,impl,classname)
|
||||
for actname in sorted(im.module.public_actions):
|
||||
username = actname[4:] if actname.startswith("ext:") else actname
|
||||
action = im.module.actions[actname]
|
||||
getargs = ','.join('_arg<{}>(args,{},{})'.format(ctype(x.sort,classname=classname),idx,csortcard(x.sort)) for idx,x in enumerate(action.formal_params))
|
||||
thing = "ivy.methodname(getargs)"
|
||||
if action.formal_returns:
|
||||
thing = "std::cout << " + thing + " << std::endl"
|
||||
impl.append("""
|
||||
if (action == "actname") {
|
||||
check_arity(args,numargs,action);
|
||||
thing;
|
||||
}
|
||||
else
|
||||
""".replace('thing',thing).replace('actname',username).replace('methodname',varname(actname)).replace('numargs',str(len(action.formal_params))).replace('getargs',getargs))
|
||||
emit_repl_boilerplate2(header,impl,classname)
|
||||
|
||||
|
||||
impl.append("int main(int argc, char **argv){\n")
|
||||
impl.append(" if (argc != "+str(len(im.module.params)+1)+"){\n")
|
||||
impl.append(' std::cerr << "usage: {} {}\\n";\n'
|
||||
.format(classname,' '.join(map(varname,im.module.params))))
|
||||
impl.append(' exit(1);\n }\n')
|
||||
impl.append(' std::vector<std::string> args;\n')
|
||||
impl.append(' std::vector<ivy_value> arg_values(1);\n')
|
||||
impl.append(' for(int i = 1; i < argc;i++){args.push_back(argv[i]);}\n')
|
||||
for idx,s in enumerate(im.module.params):
|
||||
impl.append(' int p__'+varname(s)+';\n')
|
||||
impl.append(' try {\n')
|
||||
impl.append(' int pos = 0;\n')
|
||||
impl.append(' arg_values[{}] = parse_value(args[{}],pos);\n'.format(idx,idx))
|
||||
impl.append(' p__'+varname(s)+' = _arg<{}>(arg_values,{},{});\n'
|
||||
.format(ctype(s.sort,classname=classname),idx,csortcard(s.sort)))
|
||||
impl.append(' }\n catch(out_of_bounds &) {\n')
|
||||
impl.append(' std::cerr << "parameter {} out of bounds\\n";\n'.format(varname(s)))
|
||||
|
||||
emit_repl_boilerplate1a(header,impl,classname)
|
||||
for actname in sorted(im.module.public_actions):
|
||||
username = actname[4:] if actname.startswith("ext:") else actname
|
||||
action = im.module.actions[actname]
|
||||
getargs = ','.join('_arg<{}>(args,{},{})'.format(ctype(x.sort,classname=classname),idx,csortcard(x.sort)) for idx,x in enumerate(action.formal_params))
|
||||
thing = "ivy.methodname(getargs)"
|
||||
if action.formal_returns:
|
||||
thing = 'std::cout << "= " << ' + thing + " << std::endl"
|
||||
impl.append("""
|
||||
if (action == "actname") {
|
||||
check_arity(args,numargs,action);
|
||||
thing;
|
||||
}
|
||||
else
|
||||
""".replace('thing',thing).replace('actname',username).replace('methodname',varname(actname)).replace('numargs',str(len(action.formal_params))).replace('getargs',getargs))
|
||||
emit_repl_boilerplate2(header,impl,classname)
|
||||
|
||||
|
||||
impl.append("int main(int argc, char **argv){\n")
|
||||
impl.append(" if (argc != "+str(len(im.module.params)+1)+"){\n")
|
||||
impl.append(' std::cerr << "usage: {} {}\\n";\n'
|
||||
.format(classname,' '.join(map(varname,im.module.params))))
|
||||
impl.append(' exit(1);\n }\n')
|
||||
impl.append(' catch(syntax_error &) {\n')
|
||||
impl.append(' std::cerr << "syntax error in command argument\\n";\n')
|
||||
impl.append(' exit(1);\n }\n')
|
||||
cp = '(' + ','.join('p__'+varname(s) for s in im.module.params) + ')' if im.module.params else ''
|
||||
impl.append(' {}_repl ivy{};\n'
|
||||
.format(classname,cp))
|
||||
emit_repl_boilerplate3(header,impl,classname)
|
||||
impl.append(' std::vector<std::string> args;\n')
|
||||
impl.append(' std::vector<ivy_value> arg_values(1);\n')
|
||||
impl.append(' for(int i = 1; i < argc;i++){args.push_back(argv[i]);}\n')
|
||||
for idx,s in enumerate(im.module.params):
|
||||
impl.append(' int p__'+varname(s)+';\n')
|
||||
impl.append(' try {\n')
|
||||
impl.append(' int pos = 0;\n')
|
||||
impl.append(' arg_values[{}] = parse_value(args[{}],pos);\n'.format(idx,idx))
|
||||
impl.append(' p__'+varname(s)+' = _arg<{}>(arg_values,{},{});\n'
|
||||
.format(ctype(s.sort,classname=classname),idx,csortcard(s.sort)))
|
||||
impl.append(' }\n catch(out_of_bounds &) {\n')
|
||||
impl.append(' std::cerr << "parameter {} out of bounds\\n";\n'.format(varname(s)))
|
||||
impl.append(' exit(1);\n }\n')
|
||||
impl.append(' catch(syntax_error &) {\n')
|
||||
impl.append(' std::cerr << "syntax error in command argument\\n";\n')
|
||||
impl.append(' exit(1);\n }\n')
|
||||
cp = '(' + ','.join('p__'+varname(s) for s in im.module.params) + ')' if im.module.params else ''
|
||||
impl.append(' {}_repl ivy{};\n'
|
||||
.format(classname,cp))
|
||||
if target.get() == "test":
|
||||
emit_repl_boilerplate3test(header,impl,classname)
|
||||
else:
|
||||
emit_repl_boilerplate3(header,impl,classname)
|
||||
|
||||
|
||||
|
||||
return ''.join(header) , ''.join(impl)
|
||||
|
@ -1800,7 +1780,7 @@ int ask_ret(int bound) {
|
|||
if not imp.scope() and name in im.module.actions:
|
||||
action = im.module.actions[name]
|
||||
emit_method_decl(impl,name,action);
|
||||
impl.append('{\n std::cout << "' + name[5:] + '"')
|
||||
impl.append('{\n std::cout << "< ' + name[5:] + '"')
|
||||
if action.formal_params:
|
||||
impl.append(' << "("')
|
||||
first = True
|
||||
|
@ -2060,8 +2040,67 @@ def emit_repl_boilerplate3(header,impl,classname):
|
|||
}
|
||||
""".replace('classname',classname))
|
||||
|
||||
def emit_repl_boilerplate3test(header,impl,classname):
|
||||
impl.append("""
|
||||
init_gen my_init_gen;
|
||||
my_init_gen.generate(ivy);
|
||||
std::vector<gen *> generators;
|
||||
""")
|
||||
for actname in sorted(im.module.public_actions):
|
||||
action = im.module.actions[actname]
|
||||
impl.append(" generators.push_back(new {}_gen);\n".format(varname(actname)))
|
||||
impl.append("""
|
||||
|
||||
def emit_boilerplate1(header,impl):
|
||||
for(int cycle = 0; cycle < 100; cycle++) {
|
||||
|
||||
int rnd = rand() % (generators.size() + readers.size());
|
||||
if (rnd < generators.size()) {
|
||||
gen &g = *generators[rnd];
|
||||
if (g.generate(ivy))
|
||||
g.execute(ivy);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
fd_set rdfds;
|
||||
FD_ZERO(&rdfds);
|
||||
int maxfds = 0;
|
||||
|
||||
for (unsigned i = 0; i < readers.size(); i++) {
|
||||
reader *r = readers[i];
|
||||
int fds = r->fdes();
|
||||
FD_SET(fds,&rdfds);
|
||||
if (fds > maxfds)
|
||||
maxfds = fds;
|
||||
}
|
||||
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 1;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
int foo = select(maxfds+1,&rdfds,0,0,&timeout);
|
||||
|
||||
if (foo < 0)
|
||||
{perror("select failed"); exit(1);}
|
||||
|
||||
if (foo == 0){
|
||||
// std::cout << "TIMEOUT\\n";
|
||||
for (unsigned i = 0; i < timers.size(); i++)
|
||||
timers[i]->timeout();
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i < readers.size(); i++) {
|
||||
reader *r = readers[i];
|
||||
if (FD_ISSET(r->fdes(),&rdfds))
|
||||
r->read();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".replace('classname',classname))
|
||||
|
||||
|
||||
def emit_boilerplate1(header,impl,classname):
|
||||
header.append("""
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -2093,6 +2132,10 @@ protected:
|
|||
|
||||
|
||||
public:
|
||||
virtual bool generate(classname& obj)=0;
|
||||
virtual bool execute(classname& obj)=0;
|
||||
virtual ~gen(){}
|
||||
|
||||
z3::expr mk_apply_expr(const char *decl_name, unsigned num_args, const int *args){
|
||||
z3::func_decl decl = decls_by_name.find(decl_name)->second;
|
||||
std::vector<z3::expr> expr_args;
|
||||
|
@ -2313,10 +2356,10 @@ public:
|
|||
return eval_apply(name);
|
||||
}
|
||||
};
|
||||
""")
|
||||
""".replace('classname',classname))
|
||||
|
||||
|
||||
target = iu.EnumeratedParameter("target",["impl","gen","repl"],"gen")
|
||||
target = iu.EnumeratedParameter("target",["impl","gen","repl","test"],"gen")
|
||||
opt_classname = iu.Parameter("classname","")
|
||||
|
||||
|
||||
|
@ -2332,15 +2375,16 @@ def main():
|
|||
with im.Module():
|
||||
ivy.ivy_init()
|
||||
|
||||
classname = opt_classname.get() or im.module.name
|
||||
basename = opt_classname.get() or im.module.name
|
||||
classname = varname(basename)
|
||||
with iu.ErrorPrinter():
|
||||
header,impl = module_to_cpp_class(classname)
|
||||
header,impl = module_to_cpp_class(classname,basename)
|
||||
# print header
|
||||
# print impl
|
||||
f = open(classname+'.h','w')
|
||||
f = open(basename+'.h','w')
|
||||
f.write(header)
|
||||
f.close()
|
||||
f = open(classname+'.cpp','w')
|
||||
f = open(basename+'.cpp','w')
|
||||
f.write(impl)
|
||||
f.close()
|
||||
|
||||
|
|
|
@ -4,4 +4,11 @@
|
|||
%: %.cpp
|
||||
g++ -g -o $@ $<
|
||||
|
||||
.PRECIOUS: %.cpp
|
||||
%.test.cpp: %.ivy
|
||||
ivy_to_cpp target=test classname=$*.test $<
|
||||
|
||||
%.test: %.test.cpp
|
||||
g++ -I $(Z3DIR)/include -L $(Z3DIR)/lib -g -o $@ $< hash.cpp -lz3
|
||||
|
||||
|
||||
.PRECIOUS: %.cpp %.test.cpp
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
#lang ivy1.6
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# A module containing the axioms of total order
|
||||
#
|
||||
################################################################################
|
||||
|
||||
module total_order_axioms(t) = {
|
||||
relation (X:t < Y:t)
|
||||
property [transitivity] X:t < Y & Y < Z -> X < Z
|
||||
property [antisymmetry] ~(X:t < Y & Y < X)
|
||||
property [totality] X:t < Y | X = Y | Y < X
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# A module containing the injectivity axiom
|
||||
#
|
||||
################################################################################
|
||||
|
||||
module injectivity_axioms(f) = {
|
||||
axiom [injectivity] f(X) = f(Y) -> X = Y
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# ADT describing a totally ordered datatype
|
||||
#
|
||||
################################################################################
|
||||
|
||||
module total_order = {
|
||||
type t
|
||||
instantiate total_order_axioms(t) # t is totally ordered
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# ADT describing a ring topology.
|
||||
#
|
||||
# The module includes a ring_head and ring_tail elements, and a ring
|
||||
# total order relation.
|
||||
#
|
||||
# The module also includes get_next and get_prev actions.
|
||||
#
|
||||
# In this module, the ring topology is arbitrary and fixed.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
module ring_topology = {
|
||||
type t
|
||||
|
||||
# Axioms that ensure that t is totally ordered with head the
|
||||
# minimal element and tail the maximal element.
|
||||
|
||||
instantiate total_order_axioms(t) # t is totally ordered
|
||||
|
||||
action get_next(x:t) returns (y:t)
|
||||
|
||||
object spec = {
|
||||
after get_next {
|
||||
assert (y <= X & X <= x) | (x < y & ~ (x < Z & Z < y))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# Types, relations and functions describing state of the network
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# A totally ordered set of ids
|
||||
instance id : total_order
|
||||
|
||||
# A ring topology of nodes
|
||||
instance node : ring_topology
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# The assignments of id's to nodes
|
||||
#
|
||||
################################################################################
|
||||
|
||||
object asgn = {
|
||||
|
||||
function pid(X:node.t) : id.t # map each node to an id
|
||||
|
||||
axiom [injectivity] pid(X) = pid(Y) -> X = Y
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# The transport-layer service specification
|
||||
#
|
||||
################################################################################
|
||||
|
||||
object trans = {
|
||||
|
||||
relation sent(V:id.t, N:node.t) # The identity V is sent at node N
|
||||
init ~sent(V, N)
|
||||
|
||||
action send(dst:node.t, v:id.t)
|
||||
action recv(dst:node.t, v:id.t)
|
||||
|
||||
object spec = {
|
||||
before send {
|
||||
sent(v,dst) := true
|
||||
}
|
||||
before recv {
|
||||
assert sent(v,dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# The high-level service specification
|
||||
#
|
||||
################################################################################
|
||||
|
||||
|
||||
object serv = {
|
||||
|
||||
action elect(v:node.t) # called when v is elected leader
|
||||
|
||||
object spec = {
|
||||
before elect {
|
||||
assert asgn.pid(v) >= asgn.pid(X) # only the max pid can be elected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# The high-level protocol
|
||||
#
|
||||
################################################################################
|
||||
|
||||
module process(pid) = {
|
||||
|
||||
action async(me:node.t) = {
|
||||
call trans.send(node.get_next(me),pid(me))
|
||||
}
|
||||
|
||||
implement trans.recv(me:node.t,v:id.t) {
|
||||
if v = pid(me) { # Found a leader
|
||||
call serv.elect(me)
|
||||
}
|
||||
else if v > pid(me) { # pass message to next node
|
||||
call trans.send(node.get_next(me),v)
|
||||
}
|
||||
}
|
||||
|
||||
conjecture ~(asgn.pid(N) < asgn.pid(P) & trans.sent(asgn.pid(N),N))
|
||||
conjecture ~(asgn.pid(P) < asgn.pid(Q) & N:node.t < P & P < Q & trans.sent(asgn.pid(P),N))
|
||||
conjecture ~(asgn.pid(N) < asgn.pid(P) & N < P & P < Q & trans.sent(asgn.pid(N),Q))
|
||||
conjecture ~(asgn.pid(Q) < asgn.pid(N) & N < P & P < Q & trans.sent(asgn.pid(Q),P))
|
||||
|
||||
}
|
||||
|
||||
# instantiate one process per ring element
|
||||
|
||||
instance app: process(asgn.pid)
|
||||
|
||||
import serv.elect
|
||||
import trans.send
|
||||
export app.async
|
||||
export trans.recv
|
||||
|
||||
isolate iso_app = app with node,id,trans,serv,asgn
|
||||
|
||||
object node_impl = {
|
||||
interpret node.t -> bv[1]
|
||||
|
||||
implement node.get_next(x:node.t) returns (y:node.t) {
|
||||
y := x + 1
|
||||
}
|
||||
}
|
||||
|
||||
isolate iso_node = node, node_impl
|
||||
|
||||
object id_impl = {
|
||||
interpret id.t -> bv[1]
|
||||
}
|
||||
|
||||
isolate iso_id = id, id_impl
|
||||
|
||||
extract iso_impl = app,node_impl,id_impl,asgn
|
Загрузка…
Ссылка в новой задаче