def read_module(f, nested=False): import ivy_logic_parser import ivy_parser header = f.readline() s = '\n' + f.read() # newline at beginning to preserve line numbers header = string.strip(header) if header.startswith('#lang ivy'): version = header[len('#lang ivy'):] old_version = iu.get_string_version() iu.set_string_version(version) if version != old_version: if nested: raise IvyError(None, '#lang ivy{} expected'.format(old_version)) # print "version: {}, old_version: {}".format(version,old_version) clear_rules('ivy_logic_parser') clear_rules('ivy_parser') reload(ivy_logic_parser) reload(ivy_parser) ivy_parser.importer = import_module decls = parse(s, nested) elif header == '//lang dafny1': decls = dc.parse_to_ivy(s) else: err = IvyError(None, 'file must begin with "#lang ivyN.N"') err.lineno = 1 if iu.filename: err.filename = iu.filename raise err return decls
def instantiate(self,inst): try: self.domain.schemata[inst.relname].instantiate(inst.args) except LookupError: raise IvyError(inst,"{} undefined in instantiation".format(inst.relname)) except IndexError: raise IvyError(inst,"wrong number of parameters in instantiation")
def interpret(self, thing): sig = self.domain.sig interp = sig.interp if isinstance(thing.formula.args[1], ivy_ast.NativeType): lhs = thing.formula.args[0].rep if lhs in interp or lhs in self.domain.native_types: raise IvyError(thing, "{} is already interpreted".format(lhs)) self.domain.native_types[lhs] = thing.formula.args[1] return lhs, rhs = (a.rep for a in thing.formula.args) self.domain.interps[lhs].append(thing) if lhs in self.domain.native_types: raise IvyError(thing, "{} is already interpreted".format(lhs)) if lhs in interp: if interp[lhs] != rhs: raise IvyError(thing, "{} is already interpreted".format(lhs)) return if isinstance(rhs, ivy_ast.Range): interp[lhs] = ivy_logic.EnumeratedSort(lhs, [ "{}:{}".format(i, lhs) for i in range(int(rhs.lo), int(rhs.hi) + 1) ]) return for x, y, z in zip([sig.sorts, sig.symbols], [slv.is_solver_sort, slv.is_solver_op], ['sort', 'symbol']): if lhs in x: if not y(rhs): raise IvyError(thing, "{} not a native {}".format(rhs, z)) interp[lhs] = rhs return raise IvyUndefined(thing, lhs)
def read_module(f,nested=False): import ivy_logic_parser import ivy_parser header = f.readline() s = '\n' + f.read() # newline at beginning to preserve line numbers header = string.strip(header) if header.startswith('#lang ivy'): version = header[len('#lang ivy'):] old_version = iu.get_string_version() iu.set_string_version(version) if version != old_version: if nested: raise IvyError(None,'#lang ivy{} expected'.format(old_version)) # print "version: {}, old_version: {}".format(version,old_version) clear_rules('ivy_logic_parser') clear_rules('ivy_parser') reload(ivy_logic_parser) reload(ivy_parser) ivy_parser.importer = import_module decls = parse(s,nested) elif header == '//lang dafny1': decls = dc.parse_to_ivy(s) else: err = IvyError(None,'file must begin with "#lang ivyN.N"') err.lineno = 1 if iu.filename: err.filename = iu.filename raise err return decls
def apply_mixin(decl, action1, action2): assert hasattr(action1, 'lineno') assert hasattr(action2, 'lineno') name1, name2 = (a.relname for a in decl.args) if len(action1.formal_params) != len(action2.formal_params): raise IvyError( decl, "mixin {} has wrong number of input parameters for {}".format( name1, name2)) if len(action1.formal_returns) != len(action2.formal_returns): raise IvyError( decl, "mixin {} has wrong number of output parameters for {}".format( name1, name2)) formals1, formals2 = (a.formal_params + a.formal_returns for a in (action1, action2)) for x, y in zip(formals1, formals2): if x.sort != y.sort: raise IvyError( decl, "parameter {} of mixin {} has wrong sort".format( str(x), name1)) subst = dict(zip(formals1, formals2)) action1_renamed = substitute_constants_ast(action1, subst) # print "action1_renamed: {}".format(action1_renamed) if isinstance(decl, MixinAfterDef): res = concat_actions(action2, action1_renamed) else: res = concat_actions(action1_renamed, action2) res.lineno = action1.lineno res.formal_params = action2.formal_params res.formal_returns = action2.formal_returns return res
def compile_assign(self): code = [] local_syms = [] with ExprContext(code, local_syms): if isinstance(self.args[0], ivy_ast.Tuple): args = [sortify_with_inference(a) for a in self.args] if not isinstance(args[1], ivy_ast.Tuple) or len( args[0].args) != len(args[1].args): raise IvyError(self, "wrong number of values in assignment") for lhs, rhs in zip(args[0].args, args[1].args): code.append(AssignAction(lhs, rhs)) else: with top_sort_as_default(): args = [a.compile() for a in self.args] if isinstance(args[1], ivy_ast.Tuple): raise IvyError(self, "wrong number of values in assignment") with ASTContext(self): teq = sort_infer(Equals(*args)) args = list(teq.args) code.append(AssignAction(*args)) for c in code: c.lineno = self.lineno if len(code) == 1: return code[0] res = LocalAction(*(local_syms + [Sequence(*code)])) res.lineno = self.lineno return res
def pull_args(args, num, sym, top): if len(args) < num: raise IvyError(None, 'not enough arguments to {}'.format(sym)) if top and len(args) > num: raise IvyError(None, 'too many arguments to {}'.format(sym)) res = args[:num] del args[:num] return res
def action_update(self, domain, pvars): lhs, rhs = self.args n = lhs.rep # Handle the hierarchical case if n in domain.hierarchy: asgns = [ postfix_atoms_ast(self, Atom(x, [])) for x in domain.hierarchy[n] ] res = unzip_append( [asgn.action_update(domain, pvars) for asgn in asgns]) return res # If the lhs application is partial, make it total by adding parameters xtra = len(lhs.rep.sort.dom) - len(lhs.args) if xtra < 0: raise IvyError(self, "too many parameters in assignment to " + lhs.rep) if xtra > 0: extend = sym_placeholders(lhs.rep)[-xtra:] extend = variables_distinct_list_ast(extend, self) # get unused variables lhs = add_parameters_ast(lhs, extend) # Assignment of individual to a boolean is a special case if is_individual_ast(rhs) and not is_individual_ast(lhs): rhs = eq_atom(extend[-1], add_parameters_ast(rhs, extend[0:-1])) else: rhs = add_parameters_ast(rhs, extend) type_check(domain, rhs) if is_individual_ast(lhs) != is_individual_ast(rhs): # print type(lhs.rep) # print str(lhs.rep) # print type(lhs.rep.sort) # print "lhs: %s: %s" % (lhs,type(lhs)) # print "rhs: %s: %s" % (rhs,type(rhs)) raise IvyError(self, "sort mismatch in assignment to {}".format(lhs.rep)) new_n = new(n) args = lhs.args dlhs = new_n(*sym_placeholders(n)) vs = dlhs.args eqs = [ eq_atom(v, a) for (v, a) in zip(vs, args) if not isinstance(a, Variable) ] rn = dict( (a.rep, v) for v, a in zip(vs, args) if isinstance(a, Variable)) drhs = substitute_ast(rhs, rn) if eqs: drhs = Ite(And(*eqs), drhs, n(*dlhs.args)) new_clauses = Clauses([], [Definition(dlhs, drhs)]) # print "assign new_clauses = {}".format(new_clauses) return ([n], new_clauses, false_clauses())
def _find_sort(type_name): if allow_unsorted: if type_name == 'S': return lg.TopS return lg.UninterpretedSort(type_name) try: return sig.sorts[type_name] except KeyError: if type_name == 'S': if iu.get_numeric_version() <= [1, 2]: return default_sort() raise IvyError(None, "unspecified type") raise IvyError(None, "unknown type: {}".format(type_name))
def apply_actuals(self, domain, pvars, v): assert hasattr(v, 'formal_params'), v actual_params = self.args[0].args actual_returns = self.args[1:] # formal_params = [s.prefix('_') for s in v.formal_params] # rename to prevent capture # formal_returns = [s.prefix('_') for s in v.formal_returns] # rename to prevent capture # subst = dict(zip(v.formal_params+v.formal_returns, formal_params+formal_returns)) vocab = list(symbols_asts(actual_params + actual_returns)) subst = distinct_obj_renaming(v.formal_params + v.formal_returns, vocab) for s, t in list(subst.iteritems()): subst[old(s)] = old(t) # print "apply_actuals: subst: {}".format(subst) formal_params = [subst[s] for s in v.formal_params] # rename to prevent capture formal_returns = [subst[s] for s in v.formal_returns ] # rename to prevent capture v = substitute_constants_ast(v, subst) # print "formal_params: {}".format(formal_params) # print "formal_returns: {}".format(formal_returns) # print "substituted called action: {}".format(v) if len(formal_params) != len(actual_params): raise IvyError(self, "wrong number of input parameters") if len(formal_returns) != len(actual_returns): raise IvyError(self, "wrong number of output parameters") for x, y in zip(formal_params, actual_params): if x.sort != y.sort and not domain.is_variant(x.sort, y.sort): raise IvyError( self, "value for input parameter {} has wrong sort".format(x)) for x, y in zip(formal_returns, actual_returns): if x.sort != y.sort and not domain.is_variant(y.sort, x.sort): raise IvyError( self, "value for output parameter {} has wrong sort".format(x)) input_asgns = [ AssignAction(x, y) for x, y in zip(formal_params, actual_params) ] output_asgns = [ AssignAction(y, x) for x, y in zip(formal_returns, actual_returns) ] res = Sequence(*(input_asgns + [BindOldsAction(v)] + output_asgns)) res = res.int_update(domain, pvars) # print "call update: {}".format(res) res = hide(formal_params + formal_returns, res) # print "after hide: {}".format(res) return res
def attribute(self, a): lhs, rhs = a.args if len(lhs.args) != 0: raise IvyError(a, 'attribute names may not have parameters') fields = lhs.rep.split(iu.ivy_compose_character) oname = iu.ivy_compose_character.join(fields[:-1]) oname = 'this' if oname == '' else oname aname = fields[-1] if oname not in self.mod.actions and oname not in self.mod.hierarchy: raise IvyError( a, '"{}" does not name an action or object'.format(oname)) if aname not in defined_attributes: raise IvyError( a, '"{}" does not name a defined attribute'.format(aname)) self.mod.attributes[lhs.rep] = rhs
def __exit__(self,exc_type, exc_val, exc_tb): if isinstance(exc_val,ivy_logic.Error): # assert False raise IvyError(self.ast,str(exc_val)) if exc_type == IvyError and exc_val.lineno == None and hasattr(self.ast,'lineno'): exc_val.lineno = self.ast.lineno return False # don't block any exceptions
def subactions(self) : if isinstance(self.args[0],ivy_ast.Some): ps = list(self.args[0].params()) fmla = self.args[0].fmla() vs = [Variable('V{}'.format(idx),x.sort) for idx,x in enumerate(ps)] subst = dict((c,v) for c,v in zip(ps,vs)) sfmla = substitute_constants_ast(fmla,subst) if isinstance(self.args[0],ivy_ast.SomeMinMax): idx = self.args[0].index() if idx not in ps: ltsym = Symbol('<',RelationSort([idx.sort,idx.sort])) operator = lambda x,y: Not(ltsym(x,y)) ivar = substitute_constants_ast(idx,subst) comp = operator(ivar,idx) if isinstance(self.args[0],ivy_ast.SomeMin) else operator(idx,ivar) fmla = And(fmla,Implies(sfmla,comp)) else : leqsym = Symbol('<=',RelationSort([idx.sort,idx.sort])) operator = lambda x,y: And(leqsym(x,y),Not(Equals(x,y))) ivar = next(v for p,v in zip(ps,vs) if p == idx) comp = operator(ivar,idx) if isinstance(self.args[0],ivy_ast.SomeMin) else operator(idx,ivar) fmla = And(fmla,Not(And(sfmla,comp))) if_part = LocalAction(*(ps+[Sequence(AssumeAction(fmla),self.args[1])])) else_action = self.args[2] if len(self.args) >= 3 else Sequence() else_part = Sequence(AssumeAction(Not(sfmla)),else_action) # iu.dbg('if_part') # iu.dbg('else_part') else: if not is_boolean(self.args[0]): raise IvyError(self,'condition must be boolean') if_part = Sequence(AssumeAction(self.args[0]),self.args[1]) else_action = self.args[2] if len(self.args) >= 3 else Sequence() else_part = Sequence(AssumeAction(dual_formula(self.args[0])),else_action) return if_part,else_part
def compile_inline_call(self, args): params, returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [cmpl_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = cmpl_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) with ASTContext(self): if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})". format(len(args), len(params))) args = [ sort_infer_contravariant(a, cmpl_sort(p.sort)) for a, p in zip(args, params) ] expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res()
def destructor(self, v): sym = self.individual(v) dom = sym.sort.dom if not dom: raise IvyError(v, "A destructor must have at least one parameter") self.domain.destructor_sorts[sym.name] = dom[0] self.domain.sort_destructors[dom[0].name].append(sym)
def __exit__(self, exc_type, exc_val, exc_tb): if isinstance(exc_val,ivy_logic.Error): raise IvyError(self.ast, str(exc_val)) if exc_type == IvyError: if exc_val.source_location.line is None and hasattr(self.ast, 'lineno'): exc_val.source_location = self.ast.lineno return False # don't block any exceptions
def compile_app(self): args = [a.compile() for a in self.args] # handle action calls in rhs of assignment if expr_context and top_context and self.rep in top_context.actions: returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [find_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol( 'loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = find_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res() return (ivy_logic.Equals if self.rep == '=' else ivy_logic.find_polymorphic_symbol(self.rep))(*args)
def compile_app(self): args = [a.compile() for a in self.args] # handle action calls in rhs of assignment if expr_context and top_context and self.rep in top_context.actions: params, returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [cmpl_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol( 'loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = cmpl_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) with ASTContext(self): if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})". format(len(args), len(params))) args = [ sort_infer(a, cmpl_sort(p.sort)) for a, p in zip(args, params) ] expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res() return (ivy_logic.Equals if self.rep == '=' else ivy_logic.find_polymorphic_symbol(self.rep))(*args)
def sort_infer(term): res = concretize_sorts(term) for x in chain(lu.used_variables(res),lu.used_constants(res)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): raise IvyError(None,"cannot infer sort of {}".format(x)) # print "sort_infer: res = {!r}".format(res) return res
def compile_assign(self): code = [] local_syms = [] with ExprContext(code,local_syms): args = [sortify_with_inference(a) for a in self.args] if isinstance(args[0],ivy_ast.Tuple): if not isinstance(args[1],ivy_ast.Tuple) or len(args[0].args) != len(args[1].args): raise IvyError(self,"wrong number of values in assignment"); for lhs,rhs in zip(args[0].args,args[1].args): code.append(AssignAction(lhs,rhs)) elif isinstance(args[1],ivy_ast.Tuple): raise IvyError(self,"wrong number of values in assignment"); else: code.append(AssignAction(*args)) if len(code) == 1: return code[0] return LocalAction(*(local_syms + [Sequence(*code)]))
def interpret(self,thing): lhs,rhs = (a.rep for a in thing.args) sig = self.domain.sig interp = sig.interp if lhs in interp: if interp[lhs] != rhs: raise IvyError(thing,"{} is already interpreted".format(lhs)) return for x,y,z in zip([sig.sorts,sig.symbols], [slv.sorts,[x for x in slv.relations] + [x for x in slv.functions]], ['sort','symbol']): if lhs in x: if rhs not in y: raise IvyError(thing,"{} not a native {}".format(rhs,z)) interp[lhs] = rhs return raise IvyUndefined(thing,lhs)
def find_symbol(symbol_name,throw=True): if allow_unsorted: return Symbol(symbol_name,lg.TopS) try: # print "find symbol: {!r}".format(symbol_name) s = sig.symbols[symbol_name] # print "find symbol: {} : {}".format(s,s.sort) return sig.symbols[symbol_name] except KeyError: if symbol_name == '=': return equals if not throw: return None else: if symbol_name in sig.sorts: IvyError(None,"type {} used where a function or individual symbol is expected".format(symbol_name)) raise IvyError(None,"unknown symbol: {}".format(symbol_name))
def init(self, s): s = sortify_with_inference(s) # print "s:{}".format(s) type_check(self.ag.domain, s) c = formula_to_clauses_tseitin(s) if not c: raise IvyError(ax, "initial condition must be a clause") self.ag.init_cond = and_clauses(self.ag.init_cond, c)
def get_callee(self): global context name = self.args[0].rep v = context.get(name) # print "v: {}".format(v) if not v: raise IvyError(self,"no value for {}".format(name)) return v
def _find_sort(type_name): if allow_unsorted: if type_name == 'S': return lg.TopS return lg.UninterpretedSort(type_name) try: return sig.sorts[type_name] except KeyError: if type_name == 'S': return default_sort() raise IvyError(None,"unknown type: {}".format(type_name))
def check_concretely_sorted(term, no_error=False, unsorted_var_names=()): for x in chain(lu.used_variables(term), lu.used_constants(term)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): if x.name not in unsorted_var_names: if no_error: raise lg.SortError raise IvyError( None, "cannot infer sort of {} in {}".format(x, repr(term)))
def type_check(domain,ast): for atom in apps_ast(ast): arity = len(atom.args) correct_arity = get_correct_arity(domain,atom) if arity != correct_arity and not(atom.rep == '-' and arity == 1): # print "atom: {} : {}".format(atom,type(atom)) raise IvyError(atom, "wrong number of arguments to {}: got {}, expecting {}." .format(atom.rep,arity,correct_arity))
def ivy_compile(ag, decls): with ag.domain.sig: ag.init_cond = true_clauses() for name in decls.defined: ag.domain.add_to_hierarchy(name) with TopContext(collect_actions(decls.decls)): IvyDomainSetup(ag.domain)(decls) IvyARGSetup(ag)(decls) ag.domain.macros = decls.macros if not ag.states: ac = ag.context with ac: if ag.predicates: if not ag.init_cond.is_true(): raise IvyError( None, "init and state declarations are not compatible") for n, p in ag.predicates.iteritems(): s = eval_state_facts(p) if s is not None: s.label = n else: ac.new_state(ag.init_cond) ag.domain.type_check() # try instantiating all the actions to type check them for name, action in ag.actions.iteritems(): # print "checking: {} = {}".format(name,action) type_check_action(action, ag.domain, ag.states[0].in_scope) if not hasattr(action, 'lineno'): print "no lineno: {}".format(name) assert hasattr(action, 'formal_params'), action iso = isolate.get() if iso: isolate_component(ag, iso) else: # apply all the mixins in no particular order for name, mixins in ag.mixins.iteritems(): for mixin in mixins: action1, action2 = (lookup_action(mixin, ag, a.relname) for a in mixin.args) mixed = ivy_actions.mixin_before(mixin, action1, action2) ag.actions[mixin.args[1].relname] = mixed # find the globally exported actions (all if none specified, for compat) if ag.exports: ag.public_actions = set() for e in ag.exports: if not e.scope(): # global export ag.public_actions.add(e.exported()) else: for a in ag.actions: ag.public_actions.add(a) print "actions:" for x, y in ag.actions.iteritems(): print iu.pretty("action {} = {}".format(x, y)) ivy_logic.sig = ag.domain.sig # TODO: make this an environment
def default_sort(): ds = sig._default_sort if ds != None: return ds if not iu.get_numeric_version() <= [1, 2]: raise IvyError(None, 'unspecified type') ds = lg.UninterpretedSort('S') add_sort(ds) sig._default_sort = ds return ds
def import_module(name): fname = name + '.ivy' f = open(fname, 'r') if not f: raise IvyError(None, "module {} not found in current directory".format(name)) with iu.SourceFile(fname): mod = read_module(f, nested=True) return mod
def ivy_new(filename=None): # d = Interp() if filename: f = open(filename, 'r') if not f: raise IvyError(None, "not found: %s" % filename) ivy_load_file(f) ag = AnalysisGraph() return ag