def query(self, atom): """Query a relation on the compiled theory""" self.compiler.substitutes_constants_in_array(atom.args) if atom.table not in self.compiler.typed_tables: raise base.Z3NotWellFormed( "Unknown relation {}".format(atom.table)) atom.types = self.compiler.typed_tables[atom.table] if len(atom.types) != len(atom.args): raise base.Z3NotWellFormed( "Arity of predicate inconsistency in {}".format(atom)) for i in moves.xrange(len(atom.types)): atom.args[i].type = atom.types[i] ast_vars = list(OrderedDict.fromkeys([ arg for arg in atom.args if isinstance(arg, ast.Variable) ])) vars = {} self.compiler.project = None query = self.compile_atom(vars, atom, {}) if vars != {}: compiled_vars = [vars[ast_var.full_id()] for ast_var in ast_vars] query = z3.Exists(compiled_vars, query) self.context.query(query) types = [self.datasource.types[ast_var.type] for ast_var in ast_vars] variables = [ast_var.id for ast_var in ast_vars] raw_answer = self.context.get_answer() logging.getLogger().debug("Raw answer:\n%s", raw_answer) answer = z3r.z3_to_array(raw_answer, types) return variables, answer
def extract_equal(eq): """Transform equals in a triple: var index, value, mask""" if isinstance(eq.children()[0], z3.BitVecNumRef): rhs = eq.children()[0] lhs = eq.children()[1] else: rhs = eq.children()[1] lhs = eq.children()[0] if z3.is_var(lhs): return ResultItem( var=z3.get_var_index(lhs), value=rhs, mask=None) else: kind = lhs.decl().kind() if kind == z3.Z3_OP_EXTRACT: [high, low] = lhs.params() sort = lhs.children()[0].sort() val = rhs.as_long() << low mask = (1 << (high + 1)) - (1 << low) return ResultItem( var=z3.get_var_index(lhs.children()[0]), value=z3.BitVecVal(val, sort), mask=z3.BitVecVal(mask, sort)) else: raise base.Z3NotWellFormed( "Bad lhs for equal {}".format(eq))
def flatten(self, atom, fields): """Replace named arguments with positional args. Knowing the columns in use for extensible tables, column names are replaced by positions as regular tables. """ dict_arg = {} def new_var(): """Create a new var with unique name""" self.var_count += 1 return ast.Variable("::{}".format(self.var_count)) for (label, arg) in zip(atom.labels, atom.args): if label in dict_arg: raise base.Z3NotWellFormed( "Duplicate label '{}' in atom {}".format(label, atom)) dict_arg[label] = arg atom.args = [ dict_arg[label] if label in dict_arg else new_var() for label in fields ] # Hide labels for pretty printing. atom.labels = None
def extract_and(item, types): """Extract a row""" kind = item.decl().kind() if kind == z3.Z3_OP_AND: return split_cubes(item.children(), types) elif kind == z3.Z3_OP_EQ or kind == z3.Z3_OP_NOT: return split_cubes([item], types) else: raise base.Z3NotWellFormed( "Bad result {}: {}".format(item, kind))
def get_field_from_cache(field): """Get a field compilation functions for csv access""" type_name, _ = accessor.fields[field] type_field = self.types[type_name] try: pos = index.index(field) except ValueError: raise base.Z3NotWellFormed( "Field {} was not saved for table {}".format( field, table_name)) return (type_field.to_z3, lambda row: type_field.unmarshall(row[pos]), type_field.marshall)
def find_base_relations(self): """Extracts base relations of the theory""" aux_counter = 0 new_rules = [] for rule in self.rules: if self.datasource.is_extensible(rule.head): raise base.Z3NotWellFormed( "No base predicate allowed in head: " + rule.head.table) for i, atom in enumerate(rule.body): if not self.datasource.is_extensible(atom): continue fields = self.extensible_tables.setdefault(atom.table, []) if atom.labels is None: raise base.Z3NotWellFormed( "No labels for extensible atom {}".format(atom)) for label in atom.labels: if label not in fields: fields.append(label) if atom.negated: new_table = "_negated_%d" % aux_counter aux_counter += 1 new_atom = ast.Atom(new_table, atom.args, negated=True) var_row = [ ast.Variable("V%d" % i) for i in range(len(atom.args)) ] atom_head = ast.Atom(new_table, var_row) atom_body = ast.Atom(atom.table, var_row, labels=atom.labels) new_rule = ast.Rule(atom_head, [atom_body]) new_rules.append(new_rule) rule.body[i] = new_atom self.rules.extend(new_rules) for fields in self.extensible_tables.values(): fields.sort() for rule in self.rules: for atom in rule.body: if self.datasource.is_extensible(atom): self.flatten(atom, self.extensible_tables[atom.table])
def substitutes_constants_in_array(self, args): """Substitute constants in arguments arrays""" for i in moves.range(len(args)): oarg = args[i] if isinstance(oarg, ast.Constant): arg = self.constants.get(args[i].name, None) if arg is None: raise base.Z3NotWellFormed("Unknown constant: {}".format( args[i].name)) args[i] = copy.deepcopy(arg) elif isinstance(args[i], ast.Operation): nb_vars = operations.OPERATIONS[args[i].operation].ty_vars args[i].var_types = [None] * nb_vars self.substitutes_constants_in_array(args[i].args)
def z3_to_array(expr, types): """Compiles back a Z3 result to a matrix of values""" kind = expr.decl().kind() if kind == z3.Z3_OP_OR: return [extract_and(item, types) for item in expr.children()] elif kind == z3.Z3_OP_AND: return [split_cubes(expr.children(), types)] elif kind == z3.Z3_OP_EQ or kind == z3.Z3_OP_NOT: return [split_cubes([expr], types)] elif kind == z3.Z3_OP_FALSE: return False elif kind == z3.Z3_OP_TRUE: return True else: raise base.Z3NotWellFormed("Bad result {}: {}".format(expr, kind))
def compile_expr(self, variables, expr, env): """Compile an expression to Z3""" if isinstance(expr, (ast.NumConstant, ast.StringConstant, ast.BoolConstant, ast.IpConstant)): return self.datasource.types[expr.type].to_z3(expr.val) elif isinstance(expr, ast.Variable): full_id = expr.full_id() if full_id in env: return env[full_id] if full_id in variables: return variables[full_id] expr_type = self.datasource.types[expr.type].type() var = z3.Const(expr.id, expr_type) variables[full_id] = var return var elif isinstance(expr, ast.Operation): operator = operations.OPERATIONS[expr.operation].z3 return operator( *(self.compile_expr(variables, arg, env) for arg in expr.args)) else: raise base.Z3NotWellFormed( "cannot proceed with {}".format(expr))