def visit_stmt(self, node, *args, **kargs): # Save the previous code callback. self.push() # Handle this node. result = self.dispatch(node, transform_bypass = True, *args, **kargs) if result is None and self.transform_ignorenone: result = [node] else: result = dka.listifyValue(result) # Handle loop nodes. if dka.hasnodetype(node, {For, While, PatWhile}): # Loop nodes can't be replaced by multiple statements, # since that would screw up our code insertion. assert(len(result) == 1) r = result[0] entrycode = self.code_callback(self.CCB_LOOP_ENTRY) # For pattern matches aren't allowed to change during the loop, # so we need only insert code to execute upon entering the loop. if not dka.hasnodetype(node, For): repeatcode = self.code_callback(self.CCB_LOOP_REPEAT) r.body.stmtlist.extend(repeatcode) newcode = entrycode + result # Handle normal nodes. else: precode = self.code_callback(self.CCB_NORMAL) newcode = precode + result # Restore the previous code callback. self.pop() return newcode
def visit_BinOp(self, node): self.generic_visit(node) op = node.op if dka.hasnodetype(op, {dha.In, dha.NotIn}): if dka.hasnodetype(node.right, dha.Name): ssym = node.right.id if not isfield(ssym): return node attr = ssym.attr cont, val = matchpair(node.left) has = dha.HasAttr(cont, attr) matches = dha.BinOp(dha.Attribute(dka.copy(cont), attr), dha.Eq(), val) cond = dha.BoolOp(dha.And(), [has, matches]) if dka.hasnodetype(op, dha.NotIn): cond = dha.Not(cond) return cond return node
def isPatCase(node): """Test If case type.""" if dka.hasnodetype(node, PatCase): return True elif dka.hasnodetype(node, CondCase): return False else: assert()
def isAttrEnum(node): """Again, this is an implementation detail, testing whether we are dealing with AttrEnums or regular Enums.""" if dka.hasnodetype(node, {Enum, NegEnum}): return False elif dka.hasnodetype(node, {AttrEnum, NegAttrEnum}): return True else: assert()
def visit_RefUpdate(self, node, ind): # Should enforce this check someplace else. assert(dka.hasnodetype(node.op, {UpAdd, UpRemove})) return ('{}{} ref{} {}\n'.format( '\t' * ind, self.sym_text(node, node.target), '++' if dka.hasnodetype(node.op, UpAdd) else '--', self.visit(node.value, ind)))
def isAddUpdate(node): """Test update type.""" dka.assertnodetype(node, SetUpdate) if dka.hasnodetype(node.op, UpAdd): return True elif dka.hasnodetype(node.op, UpRemove): return False else: assert()
def isBoundPat(node): if dka.hasnodetype(node, PatVar): return isBoundPatVar(node) elif dka.hasnodetype(node, PatTuple): return all(isBoundPat(elt) for elt in node.elts) elif dka.hasnodetype(node, PatExpr): return True else: assert()
def isPatMatchName(node): """Test PatMatch type. This is really an implementation detail, to allow the same bit of code to work across different stages of the transformation.""" if dka.hasnodetype(node, PatMatchName): return True elif dka.hasnodetype(node, PatMatch): return False else: assert()
def hasWildcards(pattern): """Return whether a pattern contains any wildcards.""" dka.assertnodetype(pattern, dha.pattern) if dka.hasnodetype(pattern, dha.PatIgnore): return True elif dka.hasnodetype(pattern, dha.PatTuple): return any(hasWildcards(e) for e in pattern.elts) else: return False
def patmatchToNormal(patmatch): target = patmatch.target if dka.hasnodetype(patmatch, PatMatchName): iter = Name(patmatch.id) elif dka.hasnodetype(patmatch, PatMatchLookup): iter = Lookup(patmatch.id, dka.copy(patmatch.key)) else: assert() return PatMatch(target, iter)
def patmatch_helper(self, patmatch): target = patmatch.target if dka.hasnodetype(patmatch, dha.PatMatchName): iter = dha.Name(patmatch.id) elif dka.hasnodetype(patmatch, dha.PatMatchLookup): iter = dha.Lookup(patmatch.id, dka.copy(patmatch.key)) else: assert () return target, iter
def visit_BinOp(self, node): if dka.hasnodetype(node.op, {dha.Add, dha.Sub, dha.Mult, dha.Div, dha.Mod}): return ast.BinOp(self.visit(node.left), self.visit(node.op), self.visit(node.right)) elif dka.hasnodetype(node.op, {dha.Eq, dha.NotEq, dha.Lt, dha.LtE, dha.Gt, dha.GtE, dha.In, dha.NotIn}): return ast.Compare(self.visit(node.left), [self.visit(node.op)], [self.visit(node.right)]) else: assert()
def tuplematcher(left, right, bindings): if (dka.hasnodetype(left, dha.PatTuple) and dka.hasnodetype(right, dha.Tuple) and len(left.elts) == len(right.elts)): return all(tuplematcher(a, b, bindings) for a, b in zip(left.elts, right.elts)) elif dka.hasnodetype(left, dha.PatVar): if dka.hasnodetype(right, dha.Name): bindings[left.id] = right.id return True else: return False else: return False
def visit_UnaryOp(self, node): if dka.hasnodetype(node.op, dha.NotEmpty): return ast.Call(ast.Name('bool', ast.Load()), [self.visit(node.operand)], [], None, None) elif dka.hasnodetype(node.op, dha.Any): return ast.Call(ast.Attribute(self.visit(node.operand), 'any', ast.Load()), [], [], None, None) else: return ast.UnaryOp(self.visit(node.op), self.visit(node.operand))
def visit_SetUpdate(self, node): dka.assertnodetype(node.op, {dha.UpAdd, dha.UpRemove}) op = 'add' if dka.hasnodetype(node.op, dha.UpAdd) \ else 'remove' return ast.Call(ast.Attribute(ast.Name(node.target.name, ast.Load()), op, ast.Load()), [self.visit(node.value)], [], None, None)
def visit_RefUpdate(self, node): dka.assertnodetype(node.op, {dha.UpAdd, dha.UpRemove}) func = 'incref' if dka.hasnodetype(node.op, dha.UpAdd) \ else 'decref' return ast.Call(ast.Attribute(ast.Name(node.target.name, ast.Load()), func, ast.Load()), [self.visit(node.value)], [], None, None)
def patmatch_helper(self, patmatch): target = self.visit(patmatch.target) if dka.hasnodetype(patmatch, dha.PatMatch): iter = patmatch.iter elif dka.hasnodetype(patmatch, dha.PatMatchName): iter = dha.Name(patmatch.id) elif dka.hasnodetype(patmatch, dha.PatMatchLookup): iter = dha.Lookup(patmatch.id, patmatch.key) else: assert() iter = self.visit(iter) return target, iter
def visit_UnaryOp(self, node): self.generic_visit(node) op = node.op operand = node.operand if dka.hasnodetype(op, dha.NotEmpty): if dka.hasnodetype(operand, dha.Lookup): if isforwardmember(operand.id): return dha.UnaryOp(dha.NotEmpty(), operand.key) elif dka.hasnodetype(op, dha.Any): if dka.hasnodetype(operand, dha.Lookup): if isforwardmember(operand.id): return dha.UnaryOp(dha.Any(), operand.key) return node
def visit_BinOp(self, node): self.generic_visit(node) op = node.op if dka.hasnodetype(op, {dha.In, dha.NotIn}): if dka.hasnodetype(node.right, dha.Name): ssym = node.right.id if isfield(ssym): return node if ismember(ssym): cont, val = matchpair(node.left) return dha.BinOp(val, op, cont) return node
def sym_text(self, node, sym): # Determine symbol type. typestr, kind = (('V', KIND_VSYM) if isinstance(sym, dha.VSymbol) else ('F', KIND_FSYM) if isinstance(sym, dha.FSymbol) else ('X', None)) # Get symbol name and address string. symname = str(sym) symaddr = hex(id(sym)) # Find the defining scope for the symbol. defscope = None ctxtscope = getNodeScope(node) if ctxtscope is not None and kind is not None: val = ctxtscope.lookupSymbolAndScope(sym.name, kind) if val is not None: defscope, sym = val defscopeid = hex(id(defscope)) if defscope is not None else 'None' # If the symbol is a function, *and* it has a proper Scope child # (as opposed to a Symtab child), include its id as well. if dka.hasnodetype(node, dha.FunctionDef): if nodeHasScope(node): ownscopeid = '__' + hex(id(getNodeScope(node))) else: ownscopeid = '__None' else: ownscopeid = '' # If this is an enumvar, and we know whether it is bound or unbound, say which it is. if dka.hasnodetype(node, dha.PatVar): boundedness = ('?' if node.bnd is dha.P_UNKNOWN else '=' if node.bnd is dha.P_BOUND else '*' if node.bnd is dha.P_UNBOUND else '!?!') else: boundedness = '' return ((boundedness if self.show_boundedness else '') + symname + (('_' + typestr) if self.show_typestr else '') + (('_' + symaddr) if self.show_symaddr else '') + (('$' + defscopeid) if self.show_defscopeid else '') + (ownscopeid if self.show_ownscopeid else ''))
def visit_SetUpdate(self, node): op = node.op mod = op.mod # No change to addition and removal updates. if dka.hasnodetype(op, {dha.UpAdd, dha.UpRemove}): return elif dka.hasnodetype(op, {dha.UpUnion, dha.UpDiff}): isunion = dka.hasnodetype(op, dha.UpUnion) return genUnionDiffCode(node.target, node.value.id, isunion, mod) elif dka.hasnodetype(op, dha.UpClear): return genClearCode(node.target, mod) elif dka.hasnodetype(op, dha.UpCopy): return genCopyCode(node.target, node.value.id, mod) else: assert()
def visit_UnaryOp(self, node): self.generic_visit(node) op = node.op operand = node.operand if dka.hasnodetype(op, dha.NotEmpty): if dka.hasnodetype(operand, dha.Lookup): if isforwardfield(operand.id): attr = operand.id.host.attr has = dha.HasAttr(operand.key, attr) return has elif dka.hasnodetype(op, dha.Any): if dka.hasnodetype(operand, dha.Lookup): if isforwardfield(operand.id): attr = operand.id.host.attr return dha.Attribute(operand.key, attr) return node
def enums_helper(self, enums): generators = [] for enum in enums: if dka.hasnodetype(enum, dha.EnumFor): target = self.visit(enum.target) iter = self.visit(enum.iter) generators.append(ast.comprehension(target, iter, [])) elif dka.hasnodetype(enum, dha.EnumIf): assert(len(generators) > 0) target = self.visit(enum.target) iter = self.visit(enum.iter) generators[-1].ifs.append( ast.Compare(target, [ast.In()], [iter])) else: assert() return generators
def visit_SetUpdate(self, node, ind): if not dka.hasnodetype(node.op, UpClear): return ('{}{} {} {}\n'.format( '\t' * ind, self.sym_text(node, node.target), self.visit(node.op, ind), self.visit(node.value, ind))) else: return ('{}{} {}\n'.format( '\t' * ind, self.visit(node.op, ind), self.sym_text(node, node.target)))
def visit_PatMatch(self, node): self.generic_visit(node) if (dka.hasnodetype(node.iter, dha.Name) and dha.isfixset(node.iter.id)): elem = self.visit(node.target) return dha.PatMatchName(elem, node.iter.id) cont = dha.valueToPattern(node.iter, dha.P_BOUND) elem = self.visit(node.target) return dha.PatMatchName(dha.PatTuple([cont, elem]), self.getMemSym(elem._t))
def visit_If(self, node, ind): result = '' def el(c): return '' if c is node.cases[0] else 'el' for c in node.cases: if dka.hasnodetype(c, CondCase): result += ('{}{}if {}:\n{}'.format( '\t' * ind, el(c), self.visit(c.cond, ind), self.visit(c.body, ind))) elif dka.hasnodetype(c, PatCase): result += ('{}{}if {}:\n{}'.format( '\t' * ind, el(c), self.visit(c.match, ind), self.visit(c.body, ind))) elif dka.hasnodetype(c, ApplCase): result += ('{}{}if {} appliesto {}:\n{}'.format( '\t' * ind, el(c), self.visit(c.target, ind), self.visit(c.value, ind), self.visit(c.body, ind))) else: assert() if node.orelse: result += ('{}else:\n{}'.format( '\t' * ind, self.visit(node.orelse, ind))) return result
def visit_Compare(self, node): # Make sure only simple comparisons are used. if len(node.comparators) > 1: raise PythonToDARhiError('Complex comparison used') # Turn In, Not In into Match nodes. op = self.visit(node.ops[0]) if dka.hasnodetype(op, {dha.In, dha.NotIn}): target = self.pattern_helper(node.left) iter = self.visit(node.comparators[0]) if self.objectdomain: code = dha.Match(dha.PatMatch(target, iter), dka.Symtab()) else: dka.assertnodetype(iter, dha.Name) code = dha.Match(dha.PatMatchName(target, iter.id), dka.Symtab()) if dka.hasnodetype(op, {dha.NotIn}): code = dha.UnaryOp(dha.Not(), code) return code else: left = self.visit(node.left) right = self.visit(node.comparators[0]) return dha.BinOp(left, op, right)
def visit_If(self, node): self.marker = node self.generic_visit(node) if not (len(node.cases) == 1 and dka.hasnodetype(node.cases[0], dha.ApplCase)): return case = node.cases[0] wipepat = wipePattern(dka.copy(case.target)) assign = dha.Assign(dka.copy(case.target), dka.copy(case.value)) bodycode = eliminatedecomp(assign, case.body.stmtlist) check = dha.CompatTuple(wipepat, dka.copy(case.value)) newcase = dha.CondCase(check, dha.Block(bodycode)) code = dha.If([newcase], node.orelse) return code
def visit_Expr(self, node): # An "is" expression is an invariant definition. if isinstance(node.value, ast.Compare): cm = node.value if (isinstance(cm.left, ast.Name) and len(cm.ops) == 1 and isinstance(cm.ops[0], ast.Is)): sym = genCompSym(cm.left.id) value = self.visit(cm.comparators[0]) if not dka.hasnodetype(value, dha.RelSetComp): raise PythonToDARhiError('Invalid invariant definition') return dha.InvDef(sym, value) else: raise PythonToDARhiError('Standalone expression statement') # Otherwise, an expression node should be a call to a procedure. # Check that it's a call. if not isinstance(node.value, ast.Call): raise PythonToDARhiError('Standalone expression statement') call = node.value # Special attributed calls are used to express set updates. if isinstance(call.func, ast.Attribute): updatemap = {'add': dha.UpAdd, 'remove': dha.UpRemove, 'union': dha.UpUnion, 'diff': dha.UpDiff, 'clear': dha.UpClear, 'copy': dha.UpCopy} isclear = call.func.attr == 'clear' if (isinstance(call.func.value, ast.Name) and call.func.attr in updatemap): if len(call.args) != (0 if isclear else 1): raise PythonToDARhiError('Invalid number of arguments to set update') mod = dha.UP_STRICT if self.strict else dha.UP_NONSTRICT return dha.SetUpdate(genVSym(call.func.value.id), updatemap[call.func.attr](mod), self.visit(call.args[0]) if not isclear else dha.Num(0)) return self.call_helper(call, isstmt=True)
def visit_For(self, node): self.generic_visit(node) match = node.match if dka.hasnodetype(match, dha.PatMatch): return ssym = match.id if isfield(ssym): attr = ssym.attr elif isfieldaux(ssym): attr = ssym.host.attr else: assert() if isforwardfield(ssym): cont, val = match.key, match.target has = dha.HasAttr(cont, attr) bind = dha.Assign(val, dha.Attribute(dka.copy(cont), attr)) newbody = dha.Block([bind] + node.body.stmtlist) return dha.If([dha.CondCase(has, newbody)], node.orelse) else: newmatch = dha.patmatchToNormal(node.match) return dha.For(newmatch, node.body, node.orelse)