def METHOD_NAME(self, e, call_arg=False):
    etyp = type(e)
    if etyp is LoopIR.Read:
        buf = self.env[e.name]
        if call_arg or isinstance(buf, (int, bool)):
            return buf
        else:
            idx = (0,) if len(e.idx) == 0 else tuple(self.METHOD_NAME(a) for a in e.idx)
            return buf[idx]
    elif etyp is LoopIR.Const:
        return e.val
    elif etyp is LoopIR.USub:
        return -self.METHOD_NAME(e.arg)
    elif etyp is LoopIR.BinOp:
        lhs, rhs = self.METHOD_NAME(e.lhs), self.METHOD_NAME(e.rhs)
        if e.op == "+":
            return lhs + rhs
        elif e.op == "-":
            return lhs - rhs
        elif e.op == "*":
            return lhs * rhs
        elif e.op == "/":  # is this right?
            if isinstance(lhs, int):
                return (lhs + rhs - 1) // rhs
            else:
                return lhs / rhs
        elif e.op == "%":
            return lhs % rhs
        elif e.op == "==":
            return lhs == rhs
        elif e.op == "<":
            return lhs < rhs
        elif e.op == ">":
            return lhs > rhs
        elif e.op == "<=":
            return lhs <= rhs
        elif e.op == ">=":
            return lhs >= rhs
        elif e.op == "and":
            return lhs and rhs
        elif e.op == "or":
            return lhs or rhs
    elif etyp is LoopIR.BuiltIn:
        args = [self.METHOD_NAME(a) for a in e.args]
        return e.f.interpret(args)
    else:
        assert False, "bad case"