import threading
import re, sys, types
from itertools import izip, count, chain
from decorator import decorator
from contextlib import contextmanager

from redbeans import latex
from redbeans.formats import BaseFormat, Format
from redbeans.creole import tokenize
from redbeans.parser import Parser
from redbeans.tokens import *

from . import custom, db, dependencies, cache, structure
from .flavors import FORMATS, Reference
from .benchmark import benchmarking

class WikiException(Exception):
    pass

class NoContentException(WikiException):
    pass

class ElementNotFound(WikiException):
    pass

class moverride(object):
    def __init__(self, kw):
        for v in kw.values():
            assert isinstance(v, unicode), kw
        for k in kw:
            assert not k[0].isupper() and k[0] != '*', kw
        self.kw = kw
    def __enter__(self):
        self.old_moverrides = dict(eval_state.moverrides)
        eval_state.moverrides.update(self.kw)
    def __exit__(self, exc_type, exc_val, exc_tb):
        eval_state.moverrides = self.old_moverrides

class eoverride(object):
    def __init__(self, kw):
        self.kw = kw
        for k in kw:
            assert k[0].islower(), kw
            assert hasattr(kw[k], 'ename') or hasattr(kw[k], 'element') or kw[k] is None, kw
    def __enter__(self):
        self.old_eoverrides = dict(eval_state.eoverrides)
        eval_state.eoverrides.update(self.kw)
    def __exit__(self, exc_type, exc_val, exc_tb):
        eval_state.eoverrides = self.old_eoverrides
        
class poverride(object):
    def __init__(self, kw):
        for k in kw:
            assert k.startswith('*'), kw
        self.kw = kw        
    def __enter__(self):
        self.old_poverrides = dict(eval_state.poverrides)
        eval_state.poverrides.update(self.kw)
    def __exit__(self, exc_type, exc_val, exc_tb):
        eval_state.poverrides = self.old_poverrides

class context(object):
    def __init__(self, kw):
        self.kw = kw        
    def __enter__(self):
        self.old_context = dict(eval_state.context)
        eval_state.context.update(self.kw)
    def __exit__(self, exc_type, exc_val, exc_tb):
        eval_state.context = self.old_context

# Use a weird singleton becuse None and False could be valid return values.
NO_MEMO = (None,)

def get_memo_or_push(key):
    ret, restr = cache.get_memo(key + get_context_tag(), (NO_MEMO, None))
    found = False
    if ret is not NO_MEMO:
        # Check restr
        for typ, name, val in restr:
            if typ == 'eoverride':
                if eval_state.eoverrides.get(name, None) != val:
                    break
            elif typ == 'moverride':
                if eval_state.moverrides.get(name, None) != val:
                    break
            elif typ == 'poverride':
                if eval_state.poverrides.get(name, None) != val:
                    break
        else:
            found = True
    if found:
        return ret
    else:
        eval_state.memoable_stack.append(set())
        return NO_MEMO

def set_memo_and_pop(key, val):
    memoable = eval_state.memoable_stack.pop()
    restr = set()
    for typ, name in memoable:
        if typ == 'eoverride':
            restr.add(('eoverride', name,
                       eval_state.eoverrides.get(name, None)))
        elif typ == 'poverride':
            restr.add(('poverride', name,
                       eval_state.poverrides.get(name, None)))
        elif typ == 'moverride':
            restr.add(('moverride', name,
                       eval_state.moverrides.get(name, None)))
        else:
            assert False, memoable
    cache.set_memo(key + get_context_tag(), (val, restr))
    eval_state.memoable_stack[-1].update(memoable)

def recursive_get(elm, prop_name, *args, **restrictions):
    incl_elm = True
    with_owner = False
    if len(args) > 0:
        filter, = args
        # prune: include the first thing that fails the restrictions,
        #        but don't recurse into that thing.
        # exclude: exclude anything that fails the restrictions.
        # exclude!: exclude, but don't include elm itself.
        # exclude+: exclude!, but return a list of (elm, owner) instead of just
        #           a list of elm
        # invert: include only everything that would be excluded by exclude,
        #         but would've been included without restrictions.
        assert filter in ('prune', 'invert', 'exclude', 'exclude!', 'exclude+')
        if filter == 'exclude!' or filter == 'exclude+':
            if filter == 'exclude+':
                with_owner = True
            filter = 'exclude'
            incl_elm = False
    else:
        filter = 'prune'
    ret = to_python(elm, prop_name)
    new = [([elm], list(ret), False)]
    if filter != 'prune':
        # We only add things once they pass filters
        ret = []
    if filter != 'invert' and incl_elm:
        ret = [elm] + ret
    if with_owner:
        ret = [(e, elm) for e in ret]
    while len(new) > 0:
        newnew = []
        # "pruned" indicates that this element would have been pruned
        for path, l, pruned in new:
            for n in l:
                if n in path:
                    raise WikiException(
                        "Infinite %s loop in recursive_get: %s!"
                        % (prop_name, '->'.join(path+[n])))
                passed_restr = all((to_python(n, r)
                                    if n.has_propval(r) else None)
                                   == restrictions[r]
                                   for r in (unicode(sr, 'utf-8')
                                             for sr in restrictions))
                if passed_restr or filter == 'invert':
                    if (filter == 'exclude' or
                        (filter == 'invert' and (not passed_restr or pruned))):
                        # We only add it after it meets the restrictions
                        if with_owner:
                            if n.has_propval(u'subowned') and to_python(n, u'subowned'):
                                ret.append((n, path[-1]))
                            else:
                                ret.append((n, elm))
                        else:
                            ret.append(n)
                    if n.has_propval(prop_name):
                        adding = to_python(n, prop_name)
                        if filter == 'prune':
                            assert not with_owner
                            ret += adding
                    newnew.append((path+[n], adding,
                                   not passed_restr or pruned))
        new = newnew
    return ret

class RefExtractingFormat(BaseFormat):
    def __init__(self):
        self.refs = []
        self.link_toks = None
    def text(self, text):
        if self.link_toks is not None:
            self.link_toks.append(Text(text))
        return u''
    def error(self, text):
        #???
        return u''
    def start(self, t, arg=None):
        if t == LINK:
            self.link_toks = []
        elif self.link_toks is not None:
            self.link_toks.append(Start(t, arg))
        return u''
    def end(self, t, arg=None):
        if t == LINK:
            if 'ename' in arg:
                assert isinstance(arg['ename'], unicode), repr(arg)
                self.refs.append((arg['ename'], self.link_toks,
                                  arg.get('args')))
                #assert False, self.link_toks
            self.link_toks = None
        elif self.link_toks is not None:
            self.link_toks.append(End(t, arg))
        return u''
    def entity(self, t, arg=None):
        if self.link_toks is not None:
            self.link_toks.append(Entity(t, arg))
        return u''
def ref_link_func(h, sty):
    #print >>sys.stderr, "r_l_f", h, sty
    if sty == LINK and not h.startswith('/') and '://' not in h:
        args = None
        if '/' in h:
            args = h.split('/')
            h = args.pop(0)
        if '.' not in h and ' ' not in h:
            elm = get_element(h)
            if elm is not None:
                ename = elm.ename
            else:
                ename = h
            return u'', dict(ename=ename, args=args)
    return u'', {}

def get_reference_enames_with_labels(text_or_elm, propname=None, propval=None):
    if propval is not None:
        # Could be memoized
        key = 'g_r_e_w_l:%s.%s' % (propval.element.ename, propval.propname)
        ret = get_memo_or_push(key)
        if ret is not NO_MEMO:
            return ret
    
    if propname is not None:
        assert not structure.get_flavor(propname).binary
        pv = text_or_elm.get_propval(propname)
        text = unicode(pv.value, 'utf-8')
        eval_state.dependencies.addDep(pv)
    else:
        assert isinstance(text_or_elm, unicode)
        text = text_or_elm
    old_mentions = eval_state.mentions
    old_errors = eval_state.errors
    ref = RefExtractingFormat()

    with context({'in_get_refs': True}):
        eval_state.mainparser.parse(text, format=ref, link_func=ref_link_func)
    eval_state.mentions = old_mentions
    eval_state.errors = old_errors
    #print >>sys.stderr, "Refs:", ref.refs

    if propval is not None:
        set_memo_and_pop(key, ref.refs)
    
    return ref.refs

def get_reference_enames(text_or_elm, propname=None, propval=None):
    return [(e,a) for e,l,a in get_reference_enames_with_labels(text_or_elm,
                                                                propname,
                                                                propval)]

def get_references(text_or_elm, propname=None, propval=None):
    return [structure.get_element(e)
            for e,a in get_reference_enames(text_or_elm, propname, propval)]

def get_mention_enames(text_or_elm, propname=None):
    return get_eval_state_delta('mentions', text_or_elm, propname)

def get_mentions(text_or_elm, propname=None):
    """Return all elements linked to or named in this wikitext or propval."""
    return [structure.get_element(e)
            for e in get_mention_enames(text_or_elm, propname)]

def has_errors(elm, propname):
    """Return true if any errors are encountered evaluating this propval.

    This probably only works properly if the logged in user is omniscient; we
    could catch the non-wiki exceptions that can arise, but we don't."""
    try:
        return len(get_eval_state_delta('errors', elm, propname)) > 0
    except WikiException:
        return True

def has_ancestor(element, ancestor):
    if hasattr(element, 'element'):
        element = element.element
    return ancestor.is_ancestor_of(element)
def is_a(element, ancestor):
    if hasattr(element, 'element'):
        element = element.element
    return ancestor == element or ancestor.is_ancestor_of(element)

def safe_getattr(object, name, *args):
    assert '__' not in name
    return getattr(object, name, *args)

def baz_eval_wrap(arg, toPython):
    if hasattr(arg, 'ename'):
        return attrelm(arg, toPython)
    elif isinstance(arg, list):
        return [baz_eval_wrap(a, toPython) for a in arg]
    elif isinstance(arg, dict):
        return dict((baz_eval_wrap(k, toPython),
                     baz_eval_wrap(arg[k], toPython)) for k in arg)
    else:
        return arg
def baz_eval_wrap_func(func, toPython):
    @decorator
    def helper(func, *args, **kw):
        args = [baz_eval_unwrap(a) for a in args]
        kw = dict((k,baz_eval_unwrap(v)) for k,v in kw.items())
        return baz_eval_wrap(func(*args, **kw), toPython)
    return helper(func)
def baz_eval_unwrap(ret):
    if isinstance(ret, attrelm):
        return ret.element__
    elif isinstance(ret, list):
        return [baz_eval_unwrap(r) for r in ret]
    elif isinstance(ret, dict):
        return dict((baz_eval_unwrap(k), baz_eval_unwrap(ret[k])) for k in ret)
    elif isinstance(ret, str):
        return unicode(ret, 'utf-8')
    else:
        return ret

def get_context(key):
    return eval_state.context.get(key, False)
def get_context_tag():
    tag = ''
    for k in eval_state.context:
        val = eval_state.context[k]
        assert val in (True, False), eval_state.context
        if val:
            tag += '+' + k
    return tag

BAZ_EVAL_GLOBALS = dict((toPython, {"__builtins__": {},
                              "repr": repr,
                              "str": str,
                              "True": True,
                              "False": False,
                              "None": None,
                              "dict": baz_eval_wrap_func(
    lambda *args,**kw: dict(*args, **kw), toPython),
                              "any": any,
                              "all": all, 
                              "len": len,
                              "hasattr": hasattr,
                              "getattr": safe_getattr,
                              "list_props":
                              baz_eval_wrap_func(lambda e: list(e), toPython),
                              "recursive_get":
                              baz_eval_wrap_func(recursive_get, toPython),
                              "has_ancestor":
                              baz_eval_wrap_func(has_ancestor, toPython),
                              "is_a": baz_eval_wrap_func(is_a, toPython),
                              "get_references":
                              baz_eval_wrap_func(get_references, toPython),
                              "get_mentions":
                              baz_eval_wrap_func(get_mentions, toPython),
                              "has_errors":
                              baz_eval_wrap_func(has_errors, toPython),
                              "get_context": get_context
                              })
                        for toPython in (False, True))

def baz_eval(expr, toPython=True):
    if len(expr.strip()) == 0:
        return u''
    if '__' in expr:
        # Protect against hackery
        raise WikiException("The string '__' is not allowed to appear in expressions!")
    else:
        key = 'baz_eval%s:%s' % (toPython, expr)
        ret = get_memo_or_push(key)
        if ret is not NO_MEMO:
            return ret
        #print >>sys.stderr, 'baz_eval', expr
        
        elemlocs = _elemdict(toPython=toPython)
        try:
            eval_globals = dict(BAZ_EVAL_GLOBALS[toPython])
            eval_globals.update(
                {"defined": (lambda s: s in elemlocs),
                 "get": (lambda s,d=None: elemlocs.get(s,d))})
            #print >>sys.stderr, expr
            ret = eval(expr, eval_globals, elemlocs)
            ret = baz_eval_unwrap(ret)
        except AssertionError:
            raise
        except Exception,e:
            raise #!!!
            if (isinstance(e, NameError)
                and re.search("^global name '(\w+)' is not defined$",
                              e.args[0])):
                extra = " (If you're using a generator expression, try a list comprehension.)"
            else:
                extra = ''
            raise WikiException(repr(e)+extra)
        else:
            set_memo_and_pop(key, ret)
            return ret


def full_eval(expr):
    """Returns a generator for tokens representing the given baz_eval expr."""
    ret = baz_eval(expr, toPython=False)
    if isinstance(ret, basestring):
        return [Text(ret)]
    else:
        return ret

def unicode_eval(expr):
    ret = baz_eval(expr)
    if hasattr(ret, '__call__'):
        gen = ret(None, None)
        ret = tokens_text(gen)
    else:
        ret = unicode(ret)
    return ret

def tokens_text(toks):
    ret = u''
    for t in toks:
        assert t.op == TEXT, t
        ret += t.arg
    return ret

def render_allow_override(element, prop_name, default=None,
                          eoverrides={}, moverrides={}):
    # Could be an moverride.
    eval_state.memoable_stack[-1].add(('moverride', prop_name))
    for v in moverrides.values():
        assert isinstance(v, unicode)
    if prop_name in eval_state.moverrides:
        return eval_state.moverrides[prop_name]
    else:
        prop_name = get_propname(prop_name)
        eval_state.dependencies.addPropvalDep(element, prop_name)
        if element.has_propval(prop_name):
            return render_propval(element, prop_name, eoverrides=eoverrides,
                                  moverrides=moverrides)
        else:
            return default

def render_propval(element, prop_name, eoverrides={}, moverrides={},
                   content=None):
    """Render the propval as plain text, tracking dependencies."""
    for v in moverrides.values():
        assert isinstance(v, unicode)
    for k in eoverrides:
        assert k[0].islower(), eoverrides
        assert hasattr(eoverrides[k], 'ename'), eoverrides
    for k in moverrides:
        assert not k[0].isupper() and k[0] != '*', moverrides
    # TODO(xavid): theoretically this should actively be position-independent
    parser = Parser(FORMATS['txt'], macro_func, link_func, makeRestricted)
    old_eoverrides = dict(eval_state.eoverrides)
    old_moverrides = dict(eval_state.moverrides)
    old_parser = eval_state.mainparser
    eval_state.eoverrides.update(eoverrides)
    eval_state.moverrides.update(moverrides)
    eval_state.mainparser = parser
    ret = parser.render(recurse(element, prop_name, content=content))
    eval_state.eoverrides = old_eoverrides
    eval_state.moverrides = old_moverrides
    eval_state.mainparser = old_parser
    return ret

QUOTES = frozenset("'\"")
GROUPING = {'(': ')', '[': ']'}

def parse_macro_args(argstr, kw_only=False):
    """Parse an argument string into a map of names to values.

    name will be an integer for positional parameters and a string for
    keyword parameters.

    If kw_only is true, join all positional args with spaces into arg 1."""
    escaping = False
    build = ""
    name = 1
    nextname = 1
    nomore = False
    nest = []
    quote = None
    retmap = {}
    for i in xrange(len(argstr)):
        char = argstr[i]
        if len(nest) == 0 and quote is None and char.isspace():
            if build:
                retmap[name] = build
                if name == nextname:
                    nextname += 1
                build = ""
                name = nextname
                nomore = False
        else:
            if nomore:
                raise WikiException("Text after close paren in %s arg!"
                                    % name)
            build += char
            if char == '\\' and not escaping:
                escaping = True
            else:
                if (quote is None
                    and char in QUOTES):
                    quote = char
                elif char == quote and not escaping:
                    quote = None
                elif len(nest) > 0 and nest[-1] == char:
                    nest.pop()
                elif char in GROUPING:
                    nest.append(GROUPING[char])
                elif (char == '=' and len(argstr) > i+1 and argstr[i+1] != '='
                      and argstr[i-1] not in ('=','!')
                      and len(nest) == 0):
                    if isinstance(name,int) and len(build) > 1:
                        name = build[:-1]
                        build = ""
                escaping = False
    if build:
        retmap[name] = build
    return retmap
            

def _list_descendants(e):
    assert e.ename is not None
    eval_state.dependencies.addChildrenDep(e)
    # -! make more efficient, possibly move to structure
    everyone = e.get_descendants()
    for dude in everyone:
        eval_state.dependencies.addChildrenDep(dude)
    return everyone

def safesplit_tags(toks, splitters):
    retlist = [([], None)]
    macro_level = 0
    for t in toks:
        if (macro_level == 0 and t.op == ENTITY and t.style == MACRO
            and t.arg[0] in splitters):
            retlist.append(([], t.arg))
        else:
            if t.style == MACRO:
                if t.op == START:
                    macro_level += 1
                elif t.op == END:
                    macro_level -= 1
            retlist[-1][0].append(t)
    return retlist
def safesplit(toks, splitters):
    return [p[0] for p in safesplit_tags(toks, splitters)]

def get_overridden_element(ename):
    # Only allow overridden elements that start with a lowercase letter.
    # TODO(xavid): we can probably clean up lots of stuff given this new
    #              simplifying assumption.
    assert isinstance(ename, unicode), repr(ename)
    if hasattr(eval_state, 'dependencies') and ename[0].islower():
        # parent can't be overridden, for optimization reasons.
        if ename == custom.PARENT_ELEMENT:
            eval_state.dependencies.addParentDep(eval_state.me)
            return eval_state.me.get_parent()
        else:
            eval_state.memoable_stack[-1].add(('eoverride', ename))
            if ename in eval_state.eoverrides:
                return eval_state.eoverrides[ename]
            elif ename == custom.ME_ELEMENT:
                return eval_state.me
    return None
def get_element(ename):
    e = get_overridden_element(ename)
    if e is not None:
        return e
    else:
        elm = structure.get_element(ename)
        if elm is None:
            if hasattr(eval_state, 'dependencies'):
                eval_state.dependencies.addExistsDep(ename)
            return None
        return elm

def get_current_element():
    """Like get_element(u'leaf'), but allows explicit overriding by setting
    current."""
    #print >>sys.stderr, "g_c_e", eval_state.eoverrides, get_element(custom.LEAF_ELEMENT)
    if u'current' in eval_state.eoverrides:
        ret = eval_state.eoverrides[u'current']
        if ret is not None:
            return ret
    return get_element(custom.LEAF_ELEMENT)

def get_propname(pname):
    if pname[0] != '*' and pname != u'this':
        return pname
    if pname != u'this':
        eval_state.memoable_stack[-1].add(('poverride', pname))
    if pname in eval_state.poverrides:
        return eval_state.poverrides[pname]
    else:
        raise WikiException("Undefined propname '%s'!" % pname)

class Arg(object):
    def __init__(self, raw):
        self.raw = raw
    def __call__(self, args, content):
        yield Text(unicode_eval(self.raw))

def to_python(ename_or_e, pname):
    pname = get_propname(pname)
    if hasattr(ename_or_e, 'ename'):
        memo_ename = ename_or_e.ename
    elif hasattr(ename_or_e, 'element'):
        # It's a Reference, we need to include the args.
        memo_ename = u"%s/%s" % (ename_or_e.element.ename,
                                '/'.join(ename_or_e.args))
    else:
        memo_ename = get_overridden_element(ename_or_e)
        if memo_ename is None:
            memo_ename = ename_or_e
    key = 'to_python:%s.%s' % (memo_ename, pname)
    ret = get_memo_or_push(key)
    if ret is not NO_MEMO:
        return ret

    ls = list(recurse(ename_or_e, pname, to_python=True))
    #print >>sys.stderr, "to_python", ls
    assert len(ls) == 1

    set_memo_and_pop(key, ls[0])
    
    return ls[0]
def recurse(ename, pname, to_python=False, content=None, argstr=u''):
    #print >>sys.stderr, "recurse", ename, pname
    assert ename is not None
    if hasattr(ename, 'ename'):
        e = ename
        ename = e.ename
        assert hasattr(e, 'ename'), e
    elif hasattr(ename, 'element'):
        e = ename
        ename = e.element.ename
    else:
        e = get_element(ename)
        if e is None:
            raise ElementNotFound(ename)

    pname = get_propname(pname)
    assert pname is not None
    assert e is not None

    extra_mover = {}
    if hasattr(e, 'element'):
        # This is a flavors.Reference.
        for i in xrange(len(e.args)):
            extra_mover[unicode(i+1)] = e.args[i]
        e = e.element
    assert hasattr(e, 'ename'), e

    if pname == custom.ELEMENT_NAME_PROP:
        # TODO(xavid): Figure out what sort of dependency this should introduce
        if to_python:
            yield e.ename
        else:
            yield Text(e.ename)
    else:
        if hasattr(eval_state, 'dependencies'):
            assert hasattr(e, 'get_propval'), e
            eval_state.dependencies.addPropvalDep(e, pname)

        if pname == custom.NAME_PROP:
            if ename not in eval_state.mentions:
                eval_state.mentions.append(ename)

        oldme = eval_state.me
        oldeover = dict(eval_state.eoverrides)
        oldmover = dict(eval_state.moverrides)
        oldpover = dict(eval_state.poverrides)
        try:
            eval_state.me = e
            eval_state.poverrides['this'] = pname
            # Maybe update leaf
            if ename != custom.PARENT_ELEMENT:
                eval_state.eoverrides[custom.LEAF_ELEMENT] = e
            # Add macro params
            if content is not None:
                content = list(content)
                def render_content(argstr, cont):
                    newleaf = eval_state.eoverrides[custom.LEAF_ELEMENT]
                    eval_state.eoverrides[custom.LEAF_ELEMENT] = oldeover[
                        custom.LEAF_ELEMENT]
                    for t in content:
                        yield t
                    eval_state.eoverrides[custom.LEAF_ELEMENT] = newleaf
                eval_state.moverrides[u'content'] = render_content
            elif ename != custom.PARENT_ELEMENT:
                eval_state.moverrides[u'content'] = None
            if ename != custom.PARENT_ELEMENT or argstr:
                eval_state.moverrides[u'args'] = argstr
                if argstr:
                    for var,arg in parse_macro_args(argstr).items():
                        eval_state.moverrides[
                            u'arg'+unicode(var)] = Arg(arg)
            eval_state.moverrides.update(extra_mover)
            pv = e.get_propval(pname)
            if pv is None:
                raise WikiException(
                    "Element ##%s## (called as ##%s## by ##%s##) has no property ##%s##!"
                    % (e.ename if e is not None else None, ename,
                       oldme.ename if oldme is not None else None, pname))

            value = unicode(pv.value, 'utf-8')
            if to_python:
                yield structure.get_flavor(pv.propname).toPython(
                    value, eval_state.mainparser, propval=pv)
            else:
                for t in structure.get_flavor(pv.propname).tokenize(
                    value, eval_state.mainparser, propval=pv):
                    yield t
        finally:
            eval_state.me = oldme
            eval_state.poverrides = oldpover
            eval_state.eoverrides = oldeover
            eval_state.moverrides = oldmover

class nice_gen(object):
    def __init__(self, gen):
        self.__gen = gen
    def __iter__(self):
        return self
    def next(self):
        return self.__gen.next()
    def __add__(self, other):
        return chain(self, other)

class attrelm(object):
    def __init__(self, element, toPython):
        assert hasattr(element, 'ename') or hasattr(element, 'element'), element
        self.element__ = element
        self.__toPython = toPython
    def __getattr__(self, a):
        if a is None:
            raise KeyError()
        if not isinstance(a, unicode):
            a = unicode(a, 'utf-8')
        if self.__toPython:
            ret = to_python(self.element__, a)
            #print >>sys.stderr, "attrelm", self.element__, a, repr(ret), structure.get_flavor(a)
            # wrap elements we get back
            if hasattr(ret, 'ename'):
                return attrelm(ret, self.__toPython)
            elif isinstance(ret,list):
                return [attrelm(r, self.__toPython)
                        if hasattr(r, 'ename')
                        else r for r in ret]
            else:
                return ret
        else:
            return nice_gen(recurse(self.element__, a))
    def __str__(self):
        return '<@Element %s>' % self.element__.ename
    def __eq__(self, other):
        return isinstance(other,attrelm) and self.element__ == other.element__
    def __ne__(self, other):
        return not self.__eq__(other)
    def __hash__(self):
        return hash(self.element__)

class _elemdict(object):
    def __init__(self, toPython):
        self.__toPython = toPython
        self.__locals = {}
    def __contains__(self, key):
        if key in self.__locals:
            return True
        if not isinstance(key, unicode):
            key = unicode(key,'utf-8')
        if key is None:
            return False
        if not key[0].isupper():
            eval_state.memoable_stack[-1].add(('eoverride', key))
            eval_state.memoable_stack[-1].add(('moverride', key))
            if key in eval_state.eoverrides or key in eval_state.moverrides:
                return True
        if hasattr(macros, key):
            return True
        e = structure.get_element(key)
        if e is not None:
            return True
        else:
            if hasattr(eval_state, 'dependencies'):
                eval_state.dependencies.addExistsDep(key)
            return False
    def __getitem__(self,key):
        if key in self.__locals:
            return self.__locals[key]
        key = unicode(key, 'utf-8')
        if key not in self:
            raise KeyError("No element '%s' exists!"%key)
        if not key[0].isupper() and key in eval_state.moverrides:
            return eval_state.moverrides[key]
        elif key not in eval_state.eoverrides and hasattr(macros, key):
            return tokens_text(getattr(macros, key)(None, None))
        else:
            return attrelm(get_element(key), self.__toPython)
    def __setitem__(self, key, value):
        self.__locals[key] = value
    def __delitem__(self, key):
        del self.__locals[key]
    def get(self,key,default=None):
        try:
            return self[key]
        except KeyError:
            return default

# Macro-creating helpers
def environment(name, texname=None, tagname=None, texmode='environment',
                doc = ''):
    if texname is None:
        texname = name
    def env(argstr, content):
        args = parse_macro_args(argstr)
        subclasses = []
        for key,val in args.items():
            if key == 'subclass':
                subclasses.append(baz_eval(val))
            else:
                raise WikiException("Unknown arg '%s' in %s environment!"
                                % (key,name))
        if eval_state.format == FORMATS['html']:
            if tagname is not None:
                yield Literal('<%s>' % tagname)
                for t in content:
                    yield t
                yield Entity(ENV_BREAK, True)
                yield Literal('</%s>' % tagname)
            else:
                yield Literal('<div class="%s%s">' % (
                    name, ''.join(' '+c for c in subclasses)))
                for t in content:
                    yield t
                yield Entity(ENV_BREAK, True)
                yield Literal('</div>')
        elif eval_state.format == FORMATS['tex']:
            if texmode == 'environment':
                yield Literal('\\begin{%s}\n' % texname)
                for t in content:
                    yield t
                yield Entity(ENV_BREAK, True)
                yield Literal('\\end{%s}' % texname)
            else:
                yield Literal('\\%s{' % texname)
                for t in content:
                    yield t
                yield Entity(ENV_BREAK, True)
                yield Literal('}')
        else:
            for t in content:
                yield t
            yield Entity(ENV_BREAK, True)
    env.__doc__ = doc
    return env
def atom(default="", doc='', **kw):
    def ato(argstr, content=None):
        for k in kw:
            if eval_state.format == FORMATS[k]:
                yield Literal(kw[k])
                return
        else:
            yield Text(default)
    ato.__doc__ = doc
    return ato
def leafprop(pname):
    def lp(argstr):
        return recurse(custom.LEAF_ELEMENT, pname)
    return lp
def divided(name,divider,*inner):
    """A macro that turns something like <<card>>Foo<<flip/>>Bar<</card>>
    into
    <div class="card">
    <div class="front">Foo</div>
    <div class="back">Bar</div>
    </div>"""
    def div(argstr, content):
        parts = safesplit(content, (divider,))
        args = parse_macro_args(argstr)
        subclasses = []
        for key,val in args.items():
            if key == 'subclass':
                subclasses.append(baz_eval(val))
            else:
                raise WikiException("Unknown arg '%s' in %s divided!"
                                % (key,name))
        if len(parts) > len(inner):
            for e in _error("Too many %ss in a %s!"%(divider,name)):
                yield e
        else:
            if eval_state.format == FORMATS['html']:
                yield Literal('<div class="%s%s">' % (name,''.join(
                    ' '+c for c in subclasses)))
                for i in xrange(len(parts)):
                    yield Literal('<div class="%s">' % (inner[i]))
                    for t in parts[i]:
                        yield t
                    yield Literal('</div>')
                yield Literal('</div>')
            elif eval_state.format == FORMATS['tex']:
                yield Literal('\\begin{%s}\n' % (name))
                for i in xrange(len(parts)):
                    yield Literal('\\%s{' % (inner[i]))
                    for t in parts[i]:
                        yield t
                    yield Literal('}')
                yield Literal('\n\\end{%s}' % (name))
            else:
                for p in parts:
                    for t in p:
                        yield t
    return div

def inline_format(style, arg=None, doc=''):
    def fmt(argstr, content=[]):
        #if style == FOOTNOTE: assert False, eval_state.format.link_toks
        yield Start(style, arg)
        for t in content:
            yield t
        yield End(style, arg)
    fmt.__doc__ = doc
    fmt.hidden = 'latexalike'
    return fmt

def num_of_content(num):
    def num_of(argstr, content=None):
        """For references with arguments."""
        eval_state.memoable_stack[-1].add(('moverride', u'content'))
        if (u'content' in eval_state.moverrides
            and eval_state.moverrides[u'content'] is not None):
            bits = safesplit(eval_state.moverrides[u'content'](None, None),
                             'break')
            #print >>sys.stderr, "n_o_c", num, bits
            if num < len(bits):
                for t in bits[num]:
                    yield t
        else:
            raise NoContentException()
    return num_of

def illegal(name,*container):
    def ill(argstr, content=None):
        return _error('"%s" may only be used in a %s!'
                      % (name, ' or '.join('"%s"' % c for c in container)))
    ill.hidden = 'illegal'
    return ill

# Helpers used by our macros
class noescape(object):
    """Wrap a format such that it's escape method is a nop."""
    def __init__(self,format):
        self.__format = format
    def escape(self,text):
        return text
    def __getattr__(self,attr):
        return getattr(self.__format,attr)

class maxwe(object):
    def __cmp__(self,other):
        return 1
MAXWE = maxwe()

class Macros(object):
    #@staticmethod
    #def nowiki(argstr, content):
    #    """Prevent wiki markup in the content from being evaluated.
    #
    #    Unlike ##~{{{}}}##, the font style is unchanged."""
    #    return parser.escape(content)
    @staticmethod
    def par(argstr):
        """Start a new paragraph."""
        yield Entity(ENV_BREAK)
    @staticmethod
    def block(argstr, content):
        """Treat the content as its own block, separate from surroundings."""
        for t in content:
            yield t

    @staticmethod
    def format(argstr, content):
        """Include the markup, unsanitized, in the given format, with
        macros evaluated.
    
        Use like {{{<<format "html">><b>Raw HTML</b><</format>>}}}."""
        args = parse_macro_args(argstr)
        fmt = baz_eval(args[1])
        if eval_state.format == FORMATS[fmt]:
            for t in content:
                if t.op == TEXT:
                    yield Literal(t.arg)
                else:
                    yield t

    @staticmethod
    def let(argstr, content):
        """Define local names for elements or other data.

        Use like {{{<<let current=ExamplePC foo="bar">>...<</let>>}}}."""
        args = parse_macro_args(argstr)
        oldmover = dict(eval_state.moverrides)
        oldeover = dict(eval_state.eoverrides)
        for k,v in args.items():
            # TODO: come up with better evaulation logic with the lazy
            #       evaluate magic/baz_eval
            v = baz_eval(v)
            if hasattr(v, 'ename'):
                eval_state.eoverrides[k] = v
            else:
                assert isinstance(v, unicode), repr(v)
                eval_state.moverrides[k] = v
        for t in content:
            yield t
        eval_state.eoverrides = oldeover
        eval_state.moverrides = oldmover

    @staticmethod
    def plet(argstr, content):
        """Define local prop names.

        Use like {{{<<plet p="name">><<Bob.*p/>><</plet>>}}}."""
        args = parse_macro_args(argstr)
        oldpover = dict(eval_state.poverrides)
        pstubst = {}
        for k,v in args.items():
            # TODO: come up with better evaulation logic with the lazy
            #       evaluate magic/baz_eval
            v = baz_eval(v)
            psubst['*' + k] = v
        with poverride(psubst):
            for t in content:
                yield t
        
    @staticmethod
    def content(argstr):
        """When evaluating a reference, the content of the macro.

        For self-closing macros, content evaluates to None.  When not in
        reference context, raises an exception."""
        raise NoContentException()

    @staticmethod
    def _if(argstr, content):
        """Evaluate the content only if a condition is true.

        Can contain {{{<<elif another condition/>>}}} and/or {{{<<else/>>}}}.
        For example:
        {{{
        <<if leaf.gender == "male">>
          he
        <<elif leaf.gender == "female"/>>
          she
        <<else/>>
          they
        <</if>>
        }}}"""
        parttags = safesplit_tags(content, ('else', 'elif'))
        cond = argstr
        while True:
            val = baz_eval(cond)
            if val:
                break
            else:
                parttags = parttags[1:]
            if len(parttags) <= 0:
                return
            name, argstr = parttags[0][1]
            if name == 'else':
                break
            else:
                cond = argstr
        for t in parttags[0][0]:
            yield t
    # You don't need to put staticmethod() when you add things to this at runtime...
    _else = staticmethod(illegal('else','if'))
    _elif = staticmethod(illegal('elif','if'))

    @staticmethod
    def link(argstr, content=None, style=LINK):
        """Builds a link dynamically, evaluating and joining arguments.

        Use like {{{<<link "/print/" leaf.username ".pdf">>Packet<</link>>}}}
        or {{{<<link i.prefix ":" i.examplepage />>}}}."""
        #print >>sys.stderr, "LINK", repr(argstr), repr(content)
        args = parse_macro_args(argstr)
        dest = ''.join(unicode_eval(args[a]) for a in sorted(args))
        #print >>sys.stderr, "LINK dest=", dest
        if content is not None:
            yield Start(style, dest)
            for t in content:
                yield t
            yield End(style, dest)
        else:
            yield Entity(style, dest)

    @staticmethod
    def image(argstr, content=None):
        return Macros.link(argstr, content, style=IMAGE)

    @staticmethod
    def count(argstr, content=None):
        """Counts the number of elements that would be matched by foreach."""
        return Macros.foreach(argstr, content=content, count=True)

    #@staticmethod
    #def sum(argstr, content=None):
    #    total = 0
    #    for t in Macros.foreach(argstr, content=content):
    #        #assert t.op == TEXT, repr(t)
    #        if t.op == TEXT:
    #            try:
    #                total += int(t.arg)
    #            except ValueError:
    #                #assert False, repr(t.arg)
    #                pass
    #    return [Text(unicode(total))]

    @staticmethod
    def foreach(argstr, content=None, count=False):
        """Evaluate the content once for each member of some group.

        The basic form is {{{<<foreach group var ...>>...<</foreach>>}}}.
        "group" can either be an element, in which case the content is
        evaluated with var set to each of that element's descendents in
        turn, or a particular element's prop (generally of flavor references),
        in which case the content is evaluated with var set to each of the
        elements referenced in that prop value, in turn.

        Nested loops can be created in a single macro by adding
        {{{foreach group2 var2}}} after the first var name.
        You can also sum counts by doing something like:
        {{{<<foreach Object o count o.stuff i .../>>}}}.

        Parameters after the var name can be conditions filtering the
        specified group; these should generally be in parentheses.

        foreach also takes a large number of optional keyword parameters,
        which take a value after an =:

        orderBy: a prop the group members have they should be ordered by

        groupBy: the group members should be grouped by the specified
                 prop.  If the value contains a comma, the markup after
                 the comma is used as a group header.

        ifNone: markup that should be evaluated if the group is empty.

        asTree: put the evaluations of the content in a nested list
                structure reflecting the inheritance tree of the group.
                Doesn't support nested loops.

        inRowsOf: put the evaluations in a table with the specified number
                  of columns.  {{{<<break/>>}}} may be used to break an
                  evaluation into several rows.

        recursive: if the group was based on a references-flavor prop and
                   group members have that prop, evaluate the content
                   for the elements referenced in their values for the prop,
                   and so on.

        childrenOnly: use the specified element's children only, not all of
                      its descendents.
        """
        args = parse_macro_args(argstr)
        if 1 not in args or 2 not in args:
            raise WikiException("{{{foreach}}} takes at least two parameters!  See [[edit:Macros]]!")
        #print >>sys.stderr, "In foreach", args
        # We need to be able to iterate over this multiple times
        typ = args[1]
        del args[1]
        nam = args[2]
        del args[2]

        subtyps = []
        subnams = []
        next = 3
        while next in args and args[next] in ('foreach', 'count'):
            if args[next] == 'count':
                count = True
            del args[next]
            subtyps.append(args[next + 1])
            del args[next + 1]
            subnams.append(args[next + 2])
            del args[next + 2]
            next += 3

        if content is None:
            if not count:
                raise WikiException("{{{foreach}}} must have content!  See [[edit:Macros]]!")
            else:
                content = []
        content = list(content)

        restr = set()
        orderBy = []
        groupBy = None
        asTree = False
        recursive = False
        childrenOnly = False
        ifNone = None
        inRowsOf = None
        for key,arg in args.items():
            if not isinstance(key,int):
                if key.lower() == 'orderby':
                    orderBy +=arg.strip().split()
                elif key.lower() == 'groupby':
                    if ',' in arg:
                        var,groupMarkup = arg.split(',',1)
                    else:
                        var = arg
                        groupMarkup = ''
                    var = var.strip()
                    if ' ' in var:
                        var = var.split()
                        groupBy = var[0]
                        groupName = var[1]
                    else:
                        groupBy = groupName = var
                elif key.lower() == 'ifnone':
                    ifNone = baz_eval(arg)
                elif key.lower() == 'astree':
                    asTree = baz_eval(arg)
                elif key.lower() == 'recursive':
                    recursive = baz_eval(arg)
                elif key.lower() == 'childrenonly':
                    childrenOnly = baz_eval(arg)
                elif key.lower() == 'inrowsof':
                    inRowsOf = baz_eval(arg)
                else:
                    for e in _error("Unknown foreach parameter '%s'!" % key):
                        yield e
            else:
                restr.add(arg)

        # TODO(xavid): create separate "for each ancestor var" and
        #              "for var in list" syntaxes.
        list_or_elm = baz_eval(typ)
        #print >>sys.stderr, "l_o_e", typ, list_or_elm
        if not asTree:
            def get_list(list_or_elm):
                if hasattr(list_or_elm, 'ename'):
                    # Now do a traversal:
                    if childrenOnly:
                        eval_state.dependencies.addChildrenDep(list_or_elm)
                        return list_or_elm.get_children()
                    else:
                        return _list_descendants(list_or_elm)
                else:
                    assert isinstance(list_or_elm, list), repr(list_or_elm)
                    return list_or_elm
            def do_overrides(subst):
                for substnam in subst:
                    if (hasattr(subst[substnam], 'ename') or
                        hasattr(subst[substnam], 'element')):
                        eval_state.eoverrides[substnam] = subst[substnam]
                    else:
                        assert isinstance(subst[substnam], (unicode, types.FunctionType)), (subst, substnam)
                        eval_state.moverrides[substnam] = subst[substnam]
            with benchmarking('foreach get_list %s' % typ):
                lst = get_list(list_or_elm)
            nambits = nam.split(',')
            if len(nambits) == 1:
                subst_list = [{nam: e} for e in lst]
            else:
                if not all(len(nambits) == len(e) for e in lst):
                    raise WikiException("Length error unpacking for %s!" % nam)
                subst_list = [dict(zip(nambits, e)) for e in lst]
            
            old_eover = dict(eval_state.eoverrides)
            old_mover = dict(eval_state.moverrides)

            # Compute any nested loop levels
            for styp, snam in zip(subtyps, subnams):
                new_subst_list = []
                for subst in subst_list:
                    do_overrides(subst)
                    new_lst = get_list(baz_eval(styp))
                    for new_e in new_lst:
                        new_subst = dict(subst)
                        new_subst[snam] = new_e
                        new_subst_list.append(new_subst)
                subst_list = new_subst_list

            # Handle filters
            if len(restr) > 0:
                with benchmarking('foreach filtering %s' % restr):
                    filtlst = []
                    for subst in subst_list:
                        do_overrides(subst)
                        try:
                            if all(baz_eval(r) for r in restr):
                                filtlst.append(subst)
                        except NoContentException:
                            pass
                    subst_list = filtlst

            if count:
                yield Text(unicode(len(subst_list)))
            elif len(subst_list) == 0 and ifNone is not None:
                for t in tokenize(ifNone, makeRestricted):
                    yield t
            else:
                # Sort by the nam element always
                for f in reversed(orderBy):
                    subst_list.sort(key=lambda subst:
                                    to_python(subst[nambits[0]], f)
                                    if subst[nambits[0]].has_propval(f)
                                    else MAXWE)

                def stickon(subst, cont):
                    do_overrides(subst)
                    for t in cont:
                        yield t
                if groupBy:
                    groups = {}
                    for subst in subst_list:
                        e = subst[nam]
                        val = e[groupBy] if groupBy in e else MAXWE
                        if val in groups:
                            groups[val].append(subst)
                        else:
                            groups[val] = [subst]
                    for g in sorted(groups.keys()):
                        assert isinstance(g, unicode), repr(g)
                        eval_state.moverrides[groupName] = (g if g != MAXWE
                                                            else u"???")
                        if groupMarkup:
                            for t in tokenize(groupMarkup, makeRestricted):
                                yield t
                        for subst in groups[g]:
                            for t in stickon(subst, content):
                                yield t
                else:
                    if inRowsOf:
                        this_row = []
                        content_bits = safesplit(content, 'break')
                        content = content_bits[0]
                        content_bits = content_bits[1:]
                        def end_row():
                            yield End(TABLE_ROW)
                            for c in content_bits:
                                yield Start(TABLE_ROW)
                                for s in this_row:
                                    yield Start(TABLE_CELL)
                                    for t in stickon(s, c):
                                        yield t
                                    yield End(TABLE_CELL)
                                yield End(TABLE_ROW)
                            del this_row[:]
                    for subst in subst_list:
                        if inRowsOf:
                            if len(this_row) == 0:
                                yield Start(TABLE_ROW)
                            yield Start(TABLE_CELL)
                        #with benchmarking(u'foreach subst=%s' % subst):
                        for t in stickon(subst, content):
                            yield t
                        if inRowsOf:
                            yield End(TABLE_CELL)
                            this_row.append(subst)
                            if len(this_row) == inRowsOf:
                                for t in end_row():
                                    yield t
                    if inRowsOf and len(this_row) > 0:
                        for t in end_row():
                            yield t
            eval_state.eoverrides = old_eover
            eval_state.moverrides = old_mover            
        else: # asTree
            def dig(e,depth):
                assert e.ename is not None
                eval_state.dependencies.addChildrenDep(e)
                old_overrides = dict(eval_state.eoverrides)
                eval_state.eoverrides[nam] = e
                yield Start(UNORDERED_ITEM, depth)
                for t in content:
                    yield t
                yield End(UNORDERED_ITEM, depth)
                eval_state.eoverrides = old_overrides

                lst = e.getChildren()
                if count:
                    yield Text(unicode(len(lst)))
                elif len(lst) > 0:
                    for f in reversed(orderBy):
                        lst.sort(key=lambda e:e[f] if f in e else MAXWE)
                    for e in lst:
                        for y in dig(e,depth+1):
                            yield y
            assert hasattr(list_or_elm, 'ename')
            for t in dig(list_or_elm, 1):
                yield t
    _break = staticmethod(illegal('break', 'foreach', 'similar'))

    @staticmethod
    def comment(argstr, content=None):
        """Suppress any content.

        Useful for leaving comments only relevant to people editing a prop's
        value."""
        return ()

    @staticmethod
    def withheaders(argstr, content): # More descriptive name
        """Renders the (up to) four arguments as headers around the content."""
        args = parse_macro_args(argstr)
        headfoot = ["","","",""]
        for key,val in args.items():
            if key in (1,2,3,4):
                headfoot[key-1] = baz_eval(val)
            else:
                raise WikiException("Unknown arg '%s' in card!"
                                    % (key))
        cont = list(content)
        if cont:
            if eval_state.format == FORMATS['html']:
                yield Literal('''<div class="page">
<div class="lheader">%s</div>
<div class="rheader">%s</div>
<div class="lfooter">%s</div>
<div class="rfooter">%s</div>
''' % tuple(headfoot))
                for t in cont:
                    yield t
                yield Literal('''
</div>
''')
            elif eval_state.format == FORMATS['tex']:
                yield Literal(r'''\cleardoublepage
\resetnumbering
\headers{%s}{%s}{%s}{%s}
''' % tuple(headfoot))
                for t in cont:
                    yield t
                yield Literal(r'''
\cleardoublepage
''')
    
    center = staticmethod(environment('center',tagname='center',
                                      doc="""Center the content."""))
    right = staticmethod(environment('right',
                                     texname='flushright',
                                     doc="""Right-justify the content."""))
    left = staticmethod(environment('left',
                                    texname='flushleft',
                                    doc="""Left-justify the content."""))
    dbox = staticmethod(environment(
        'dbox', texmode='command',
        doc="""Put a double box around the content"""))

    figure = staticmethod(environment('figure', doc="Make a floating figure."))

    clearpage = staticmethod(atom(html=u"<hr />",tex=r'\clearpage',
                                  doc="Start a new logical page."))
    cleardoublepage = staticmethod(atom(html=u"<hr />",tex=r'\cleardoublepage',
                                        doc="Start a new piece of paper."))

    smallskip = staticmethod(atom(html=u'<div class="gap"></div>',
                              tex=r'\par\smallskip ',
                              doc="Insert a small gap."))
    medskip = staticmethod(atom(html=u'<div class="gap"></div>',
                              tex=r'\par\medskip ',
                              doc="Insert a medium gap."))
    bigskip = staticmethod(atom(html=u'<div class="gap"></div>',
                              tex=r'\par\bigskip ',
                              doc="Insert a big gap."))

    tableofcontents = staticmethod(atom(html=u'',
                                        tex=u'\tableofcontents',
                                        doc="Insert a table of contents."))

    # LaTeX-alike macro punct
    ldots = staticmethod(atom(u'\u2026', doc="Ellipsis."))

    # LaTeX-alike non-punct special chars
    ae = staticmethod(atom(u'\u00E6', doc="ae"))
    AE = staticmethod(atom(u'\u00C6', doc="AE"))

    # LaTeX-alike macros
    textbf = staticmethod(inline_format(BOLD, doc="Bold."))
    textit = staticmethod(inline_format(ITALIC, doc="Italic."))
    emph = staticmethod(inline_format(ITALIC, doc="Italic."))
    texttt = staticmethod(inline_format(MONOSPACE, doc="Monospace."))
    uline = staticmethod(inline_format(UNDERLINE, doc="Underline."))
    sout = staticmethod(inline_format(STRIKE, doc="Strike."))
    footnote = staticmethod(inline_format(FOOTNOTE, doc="Footnote."))

    Tiny = staticmethod(inline_format(SIZE, -5, doc="Small text."))
    tiny = staticmethod(inline_format(SIZE, -4, doc="Small text."))
    SMALL = staticmethod(inline_format(SIZE, -3, doc="Small text."))
    Small = staticmethod(inline_format(SIZE, -2, doc="Small text."))
    small = staticmethod(inline_format(SIZE, -1, doc="Small text."))
    normalsize = staticmethod(inline_format(SIZE, 0, doc="Normal font size."))
    large = staticmethod(inline_format(SIZE, 1, doc="Large text."))
    Large = staticmethod(inline_format(SIZE, 2, doc="Rather large text."))
    LARGE = staticmethod(inline_format(SIZE, 3, doc="Very large text."))
    huge = staticmethod(inline_format(SIZE, 4, doc="Huge text."))
    Huge = staticmethod(inline_format(SIZE, 5, doc="Very huge text."))

    @staticmethod
    def noindent(argstr, content=None):
        """No indentation."""
        if content is None:
            yield Entity(NOINDENT)
        else:
            yield Start(NOINDENT)
            for t in content:
                yield t
            yield End(NOINDENT)
    
    @staticmethod
    def _eval(argstr):
        """Evaluate the given expression and output its value.

        E.g., {{{<<eval 3 + 5/>>}}} will output 8."""
        result = unicode_eval(argstr)
        assert isinstance(result, unicode), result
        yield Text(result)

    @staticmethod
    def raw(argstr):
        """Return a macro's value as a string, unevaluated.

        Useful for args of macro-like elements."""
        argstr = argstr.strip()
        eval_state.memoable_stack[-1].add(('moverride', argstr))
        if (argstr in eval_state.moverrides
            and hasattr(eval_state.moverrides[argstr], 'raw')):
            yield Text(eval_state.moverrides[argstr].raw)
        else:
            yield Text(unicode_eval(argstr))

    @staticmethod
    def cache(argstr, content=None, editable=False):
        """Evaluate the specified prop of an element cachably.

        This means the evaluation will ignore any context, but that it can
        take advantage of caching.  Limited context can be provided in
        the form of {{{let}}}-style keyword parameters."""
        args = parse_macro_args(argstr)
        if 1 not in args: # or 2 in args:
            raise WikiException(
                "##cache## takes only one non-keyword parameter!")

        ename, prop_name = args[1].split('.')
        element = get_element(ename)
        #print >>sys.stderr, "CACHING %s.%s" % (element.ename, prop_name)
        del args[1]
        let = {}
        for k,v in args.items():
            let[k] = baz_eval(v)

        if hasattr(element, 'element'):
            # flavors.Reference
            for i in xrange(len(element.args)):
                let[str(i+1)] = element.args[i]
            element = element.element
        
        import conversion
        flat = conversion.flatten_filters({'let': let})
        
        ce = conversion.get_from_cache(element.ename, prop_name,
                                       format=eval_state.extension+flat)
        if ce is None:
            pv = element.get_propval(prop_name)
            result, deps, metadata = evaluate(pv,
                                              eval_state.extension,
                                              let=let,
                                              reentrant=True)
            if dependencies.DISCORDIA not in deps:
                conversion.cache_propval(element.ename, prop_name,
                                         eval_state.extension+flat,
                                         result, deps, metadata)
        else:
            result = ce['value']
            deps = ce['dependencies']
            metadata = ce['metadata']
            pv = None
        for d in deps:
            if d == dependencies.DISCORDIA:
                eval_state.dependencies.makeUncacheable()
                break
            elif d == dependencies.OMNISCIENCE:
                eval_state.dependencies.makeRestricted()
            else:
                assert len(d) == 3, d
                eval_state.dependencies.addRawDep(*d)
        for i in metadata['images']:
            eval_state.images.append(i)
        if editable:
            eval_state.dependencies.makeRestricted()            
            yield Literal('<div class="editable">')
        if isinstance(result, basestring):
            yield Literal(result)
        else:
            # This is a list of arbitrary objects from an ExtractingFormat
            for i in result:
                yield Literal(i)
        if editable:
            # This macro/dep should not be in bazbase
            from bazki.util import jsarg_escape
            if pv is None:
                pv = element.get_propval(prop_name)
            val = unicode(pv.value, 'utf-8')
            yield Literal(u'</div><a onclick="editize(\'%s.%s\', \'%s\', this); return false" class="editlink" id="editize-%s.%s" href="#">'
                          % (
                element.ename, prop_name,
                jsarg_escape(val),
                element.ename, prop_name))
            yield Text(u'edit')
            yield Literal('</a>')

    @staticmethod
    def editable(argstr):
        """Like {{{<<cache>>}}}, but adds an edit link."""
        editable = eval_state.format == FORMATS['html']
        for t in Macros.cache(argstr, editable=editable):
            yield t

    # TODO(xavid): combine with other foreach syntax
    @staticmethod
    def foreachpropin(argstr, content):
        """Evaluate the content once for each prop in the given element.

        Use like
        {{{<<foreachpropin Gizmo p>> * <<Gizmo.p/>><</foreachpropin>>}}}.

        Can be given an extra ##flavor=whatever## arg to limit it to
        props of a given flavor."""
        args = parse_macro_args(argstr)
        if 1 not in args or 2 not in args:
            raise WikiException(
                "##foreachpropin## takes two parameters!")
        ename = args[1]
        var = args[2]
        if 'flavor' in args:
            flavor = baz_eval(args['flavor'])
        else:
            flavor = None

        # We need to loop through content more than once
        content = list(content)

        element = get_element(ename)
        for pname in element.list_props():
            if pname != get_propname(u'this'):
                if flavor is not None:
                    if structure.get_prop(pname).flavor != flavor:
                        continue
                with poverride({'*' + var: pname}):
                    for t in content:
                        yield t

    @staticmethod
    def foreachimage(argstr, content):
        args = parse_macro_args(argstr)
        if 1 not in args or 2 not in args:
            raise WikiException("{{{foreachimage}}} takes at least two parameters!")

        old_images = eval_state.images
        eval_state.images = []

        propval = baz_eval(args[1])
        nam = args[2]
        content = list(content)

        latched_images = eval_state.images
        eval_state.images = old_images

        for i in latched_images:
            with moverride({nam: i}):
                for t in content:
                    yield t

    @staticmethod
    def sourceof(argstr):
        """Render the source of the given prop of an element.

        Use like {{{<<sourceof Gizmo.whatzit/>>}}}."""
        args = parse_macro_args(argstr)
        if 1 not in args or len(args) != 1:
            raise WikiException(
                "##sourceof## takes only one parameter!")

        ename, prop_name = args[1].split('.')
        element = get_element(ename)
        prop_name = get_propname(prop_name)

        if prop_name not in element:
            raise WikiException(
                "##%s## has no prop ##%s## to get the source of!"
                % (element.ename, prop_name))

        yield Start(CODEBLOCK)
        yield Text(unicode(element[prop_name].value, 'utf-8'))
        yield End(CODEBLOCK)

    @staticmethod
    def propname(argstr):
        """Render the propname of the given prop; useful in
        {{{<<foreachpropin>>}}}."""
        args = parse_macro_args(argstr)
        if 1 not in args or len(args) != 1:
            raise WikiException(
                "##propname## takes only one parameter!")

        prop_name = get_propname(args[1])

        yield Text(prop_name)

    @staticmethod
    def foreachmacro(argstr, content):
        """Evaluate content once for each built-in macro.

        Sets the argument to the name of the built-in macro.  Optionally,
        a second argument selects a class of macro hidden by default."""
        args = parse_macro_args(argstr)
        if 1 not in args or len(args) not in (1,2):
            raise WikiException(
                "##foreachmacro## takes one or two parameters!")
        
        var = args[1]
        expected_hidden = args[2] if 2 in args else None
        for m in dir(macros):
            if not m.startswith('_') and getattr(getattr(macros, m), 'hidden',
                                                 None) == expected_hidden:
                if m.endswith('_'):
                    m = m[:-1]
                with moverride({var: m}):
                    for t in content:
                        yield t

    @staticmethod
    def macrodoc(argstr):
        """Given a built-in macro, render its help text."""
        args = parse_macro_args(argstr)
        if 1 not in args or len(args) != 1:
            raise WikiException(
                "##macrodoc## takes only one parameter!")
        
        name = args[1]
        eval_state.memoable_stack[-1].add(('moverride', name))
        if name in eval_state.moverrides:
            name = eval_state.moverrides[name]

        if hasattr(macros, name+'_'):
            name = name+'_'
        elif not hasattr(macros, name):
            raise WikiException("No such macro ##%s##!" % name)

        doc = getattr(getattr(macros, name), '__doc__', None)
        if doc is not None:
            for t in tokenize(doc, makeRestricted):
                yield t

    @staticmethod
    def macro(argstr, content=None):
        """Evaluate the built-in macro named by the given expression."""
        argstr = argstr.strip()
        if ' ' in argstr:
            name, argstr = argstr.split(' ', 1)
        else:
            name = argstr
            argstr = ""
        eval_state.memoable_stack[-1].add(('moverride', name))
        if name in eval_state.moverrides:
            name = eval_state.moverrides[name]

        if content is not None:
            return getattr(macros, name)(argstr, content)
        else:
            return getattr(macros, name)(argstr)

    @staticmethod
    def default(argstr, content=""):
        """Special indicator macro.  Propvals that //start// with this macro
        will be considered 'abstract', and the interface will encourage
        users to override them in children.  Has no effect on evaluation."""
        for t in content:
            yield t

    @staticmethod
    def year(argstr):
        """Evauates to the current year."""
        from datetime import datetime
        yield Text(unicode(datetime.now().year))

    @staticmethod
    def label(argstr, content):
        if not get_context('in_get_refs'):
            lab = tokens_text(content)
            assert lab not in eval_state.label_map, (lab, eval_state.label_map)
            eval_state.label_map[lab] = eval_state.figure
        return ()

    @staticmethod
    def ref(argstr, content):
        lab = tokens_text(content)
        yield Entity(REF, lab)

    @staticmethod
    def subfigure(argstr, content):
        import traceback
        #traceback.print_stack()
        import conversion, translators
        ename, pname_with_filters = argstr.split('.', 1)
        filters = {}
        pname = conversion.extract_filters(pname_with_filters, filters)
        width, height = conversion.cache_and_get_dimensions(ename, pname,
                                                            filters)
        assert width is not None and height is not None, (width, height, ename,
                                                          pname, filters)
        # TODO(xavid): better understand whether and/or why we need this list()
        eval_state.subfigures.append((argstr, width, height, list(content)))
        return ()

    @staticmethod
    def table(argstr, content):
        """Start a table, with various options.

        border: include a border, default True.

        align: cell alignment, default 'left'.

        figure: Treat this as the numbered figure, with each column as
                subfigures labeled in the second row.

        font: a macro name to wrap each cell with
        """
        # TODO(xavid): way to switch into x (equal width) mode (mode: 'equal')
        args = parse_macro_args(argstr)
        border = baz_eval(args['border']) if 'border' in args else True
        align = baz_eval(args['align']) if 'align' in args else 'left'
        figure = baz_eval(args['figure']) if 'figure' in args else None
        font = baz_eval(args['font']) if 'font' in args else None

        if figure is True:
            figure = eval_state.last_figure + 1
        if not get_context('in_get_refs'):
            eval_state.last_figure = figure
        eval_state.subfigures = []

        params = {}
        if not border:
            params['border'] = False
        if align != 'left':
            params['align'] = align

        # TODO(xavid): DTWT for nested tables
        row = 0
        subfigure_ord = ord('a')
        yield Start(TABLE, params)
        for t in content:
            if t.op == END and t.style == TABLE_CELL:
                if font:
                    yield End(MACRO, font)
                eval_state.figure = None
            yield t
            if t.op == START:
                if t.style == TABLE_ROW:
                    row += 1
                elif t.style == TABLE_CELL:
                    if font:
                        yield Start(MACRO, (font, None))
                    if row == 2 and figure is not None:
                        label = u'%s%s' % (figure, unichr(subfigure_ord))
                        yield Text(u'(%s) ' % (label,))
                        eval_state.figure = label
                        subfigure_ord += 1

        if len(eval_state.subfigures) > 0:
            imgs = []
            caps = []
            specs = []
            for img, width, height, caption in eval_state.subfigures:
                imgs.append(img)
                specs.append('w{%spt}' % (width * latex.PX_TO_PT + 5))
                caps.append(caption)
            yield Start(TABLE_ROW)
            for img in imgs:
                yield Start(TABLE_CELL)
                yield Entity(IMAGE, img)
                yield End(TABLE_CELL)
            yield End(TABLE_ROW)
            yield Start(TABLE_ROW)
            for cap in caps:
                yield Start(TABLE_CELL)
                if font:
                    yield Start(MACRO, (font, None))
                label = u'%s%s' % (figure, unichr(subfigure_ord))
                yield Text(u'(%s) ' % (label,))
                eval_state.figure = label
                subfigure_ord += 1
                for t in cap:
                    yield t
                if font:
                    yield End(MACRO, font)
                eval_state.figure = None
                yield End(TABLE_CELL)
            yield End(TABLE_ROW)
            params['specs'] = specs
        del eval_state.subfigures
        
        yield End(TABLE, params)

    @staticmethod
    def tablerow(argstr, content):
        yield Start(TABLE_ROW)
        for t in content:
            yield t
        yield End(TABLE_ROW)

    @staticmethod
    def tablecell(argstr, content):
        args = parse_macro_args(argstr)
        start_dict = {}
        if 'colspan' in args:
            start_dict['colspan'] = baz_eval(args['colspan'])
        yield Start(TABLE_CELL, start_dict)
        for t in content:
            yield t
        yield End(TABLE_CELL)

    _1 = staticmethod(num_of_content(0))
    _2 = staticmethod(num_of_content(1))
    _3 = staticmethod(num_of_content(2))

def link_func(href, sty=LINK):
    implicit = False
    if (':' not in href and not href.startswith('/')) or href[0].isupper():
        if '/' not in href and ' ' not in href:
            # Normal context-based interpretation
            if (eval_state.topthis is not None
                and structure.get_prop(eval_state.topthis).visible):
                href = 'get:'+href
            else:
                href = 'edit:'+href
            implicit = True
        elif '/' in href and (' ' not in href
                              or href.find(' ') > href.find('/')):
            href = 'get:'+href
            implicit = True
    if ':' in href:
        pref,rest = href.split(':',1)
        rest = rest.strip()
        if pref in ('get','edit'):
            ext = None
            if '.' in rest:
                ename,pname = rest.split('.',1)
                if '.' in pname:
                    pname, ext = pname.rsplit('.', 1)
                    if ext[0].isdigit() and ext.endswith('in'):
                        pname = pname + '.' + ext
                        ext = None
                    else:
                        ext = '.' + ext
            else:
                ename = rest
                pname = None
            args = None
            render_func = render_propval
            if '/' in ename:
                args = ename.split('/')
                ename = args.pop(0)
                mover = {}
                for i in xrange(len(args)):
                    mover[unicode(i+1)] = args[i]
                def render_with_content(element, prop_name):
                    return render_propval(element, prop_name,
                                          moverrides=mover)
                render_func = render_with_content
            if ename in eval_state.eoverrides:
                elm = eval_state.eoverrides[ename]
                ename = elm.ename
            else:
                elm = structure.get_element(ename)
            if sty == IMAGE:
                eval_state.images.append('%s.%s' % (ename, pname))
            with eoverride({u'current': elm}):
                if pref == 'get':
                    text, info = custom.product_link(
                        ename, pname, eval_state.dependencies,
                        render_func, extension=ext, args=args)
                elif pref == 'edit':
                    text, info = custom.edit_link(
                        ename, pname, eval_state.dependencies,
                        render_func, extension=ext)
            if ename not in eval_state.mentions:
                eval_state.mentions.append(ename)
            return text, info
        elif not rest.startswith('//'):
            # Check interwiki
            iw = structure.get_element(u'InterwikiConfig')
            if iw is None:
                eval_state.dependencies.addExistsDep(u'InterwikiConfig')
            else:
                eval_state.dependencies.addDep(iw.get_propval(u'prefix'))
                eval_state.dependencies.addDep(iw.get_propval(u'url'))
                if iw.has_propval(u'prefix') and iw.has_propval(u'url'):
                    for i in _list_descendants(iw):
                        eval_state.dependencies.addDep(i.get_propval(u'prefix'))
                        if unicode(i.get_prop(u'prefix'), 'utf-8') == pref:
                            eval_state.dependencies.addDep(i.get_propval(u'url'))
                            return (href, dict(url=unicode(i.get_prop(u'url'),
                                                           'utf-8')+rest,
                                               style='external'))
    # None matched
    if href.startswith('/'):
        abshref = custom.local_link(href)
        style = 'internal'
    else:
        abshref = href
        if '://' in href:
            style = 'external'
        else:
            style = 'internal'
    return href, dict(url=abshref, style=style)

def _error(message):
    if eval_state.catch_errors:
        yield Error('[error]')
    else:
        eval_state.dependencies.makeRestricted()
        eval_state.errors.append(message)
        yield Start(ERROR)
        for t in tokenize(message, makeRestricted):
            yield t
        yield End(ERROR)

MACRO_NAME_PAT = re.compile('^([^\s#]+)(?:\#\S+)?')

def macro_func(name, argstr=u'', content=None):
    #print >>sys.stderr, "macro_func", name, argstr, content
    try:
        # Allow macros to have #foo suffix, to allow nesting
        m=MACRO_NAME_PAT.match(name)
        assert m is not None
        name = m.group(1)
        origname = name
        argstr = argstr.strip() if argstr else u''
        maced = False
        if '.' not in name and not name[0].isupper() and not argstr:
            eval_state.memoable_stack[-1].add(('moverride', name))
            overridable = True
        else:
            overridable = False
        if ('.' not in name
            and (name[0].isupper()
                 or (overridable and name not in eval_state.moverrides))
            and not hasattr(macros,name)
            and not hasattr(macros,'_'+name)):
            curr = get_current_element()
            if curr is not None:
                if (curr.has_propval(name)
                    and get_overridden_element(name) is None):
                    eval_state.dependencies.addExistsDep(name)
                    name = "%s.%s" % (curr.ename, name)
                else:
                    name += u'.'+custom.SUBSTITUTION_PROP
            else:
                name += u'.'+custom.SUBSTITUTION_PROP
            maced=True
        #print >>sys.stderr, "maced:", name
        if '.' in name:
            elm,pnam = name.strip().rsplit('.', 1)

            if '.' in elm:
                # This could be a reference, where foo.owner itself refers
                # to an element.
                elm = baz_eval(elm)
                if elm is None:
                    raise WikiException("%s evaluates to None!" % name)
            try:
                try:
                    assert elm is not None, name
                    r = recurse(elm, pnam, content=content,
                                argstr=argstr)
                    for t in r:
                        yield t
                except ElementNotFound:
                    # This hack is a DWIM for Gameki.
                    if isinstance(elm, unicode) and len(elm) >= 2 and elm[0].islower() and elm[1].isupper():
                        r = recurse(elm[1:], pnam, content=content,
                                    argstr=argstr)
                        for t in r:
                            yield t
                    else:
                        raise
            except ElementNotFound:
                if maced:
                    for e in _error(
                        "No element or macro ##%s## exists evaluating %s.%s!"
                        % (origname,
                           eval_state.eoverrides[custom.LEAF_ELEMENT].ename,
                           eval_state.poverrides['this'])):
                        yield e
                else:
                    for e in _error("No element ##%s## exists!" % elm):
                        yield e
        else:
            if (not name[0].isupper() and overridable
                and name in eval_state.moverrides):
                ove = eval_state.moverrides[name]
                if hasattr(ove, '__call__'):
                    for t in ove(argstr, content):
                        yield t
                else:
                    yield Text(ove)
                return
            if hasattr(macros,'_'+name):
                name = '_'+name
            if hasattr(macros, name):
                if content is not None:
                    gen = getattr(macros,name)(argstr=argstr,content=content)
                else:
                    gen = getattr(macros,name)(argstr=argstr)
                if not hasattr(gen, '__iter__'):
                    for e in _error(
                        'Error: {{{<<%s %s>>}}} return non-iterator "%s"!'
                        % (name, argstr, repr(gen))):
                        yield e
                    return
                for t in gen:
                    yield t
            else:
                for e in _error('Error: no such macro %s!' % name):
                    yield e
    except NoContentException:
        raise
    except WikiException,e:
        for e in _error(e.args[0]):
            yield e

# Set or add to this to change what macros are available
macros = Macros()

# These accent macrs can't be set directly
def accent(subst):
    def amac(argstr, content):
        s = tokens_text(content)
        import unicodedata
        # TODO(xavid): use XeLaTeX, which handles unicode better, and stop
        #              doing this.
        yield Text(unicodedata.normalize('NFC', s+subst))
    return amac
ACCENT_MACROS = {"'": u'\u0301',
                 '`': u'\u0300',
                 '"': u'\u0308',
                 '~': u'\u0303',
                 '=': u'\u0304',
                 }
for sym in ACCENT_MACROS:
    setattr(macros, sym, accent(ACCENT_MACROS[sym]))

# This is a bit of a hack; we could allow some state to get passed through
# AbstractMarkup.evaluate...
eval_state = threading.local()

def init_eval_state(element, propname, extension, flavor, let={},
                    catch_errors=False):
    eval_state.dependencies = dependencies.Dependencies()
    eval_state.topthis = propname
    if propname is not None:
        eval_state.dependencies.addDep(element.get_propval(propname))
    eval_state.me = element
    eval_state.eoverrides = {
        #custom.CURRENT_ELEMENT: element,
        custom.LEAF_ELEMENT: element
        }
    eval_state.moverrides = {u'args': u""}
    eval_state.poverrides = {u'this': propname}
    for k,v in let.items():
        if hasattr(v, 'ename'):
            eval_state.eoverrides[k] = v
        else:
            assert isinstance(v, unicode), let
            eval_state.moverrides[k] = v
    eval_state.images = []
    eval_state.mentions = []
    eval_state.errors = []
    eval_state.context = {}
    eval_state.memoable_stack = [set()]
    eval_state.last_figure = 0
    eval_state.label_map = {}
    
    eval_state.extension = extension
    if extension in FORMATS:
        fmt = FORMATS[extension]
        mainparser = Parser(fmt, macro_func, link_func)
        eval_state.format = fmt
        eval_state.mainparser = mainparser
    else:
        assert flavor.binary
        # binary flavors interpret a string as a parser, for great justice.
        eval_state.mainparser = extension
    eval_state.catch_errors = catch_errors

# See placeholder() in redbeans/formats.py.
PLACEHOLDER_PAT = re.compile('<<<([^>]+)>>>')

def unplaceholder(match):
    try:
        return eval_state.label_map[match.group(1)]
    except KeyError:
        assert False, (match.group(), match.group(1))
        return "???"

def evaluate(thing, extension, toPython=False, let={}, element=None,
             flavor=None, reentrant=False, catchErrors=False):
    if hasattr(thing, 'element'):
        element = thing.element
        propval = thing
        propname = propval.propname
        assert element is not None
        assert propname is not None
        assert propval.value is not None, propval
        if flavor is None:
            flavor = structure.get_flavor(propval.propname)
        if flavor.binary:
            wikitext = propval.value
        else:
            wikitext = unicode(propval.value, 'utf-8')
    else:
        propval = None
        propname = None
        wikitext = thing
        assert flavor is not None

    if not reentrant:
        assert not hasattr(eval_state, 'dependencies'), repr(eval_state.__dict__)
    else:
        old_eval_state = dict(eval_state.__dict__)

    init_eval_state(element, propname, extension, flavor, let,
                    catch_errors=catchErrors)
    try:
        desc = ("%s.%s" % (propval.element.ename, propval.propname)
                if propval is not None else 'wikitext')
        if not toPython:
            with benchmarking('evaluating '+desc):
                res = flavor.evaluate(wikitext, eval_state.mainparser,
                                      propval=propval)
            # It might not be a string for an ExtractingFormat.
            if isinstance(res, basestring):
                res = PLACEHOLDER_PAT.sub(unplaceholder, res)
        else:
            with benchmarking('python-converting '+desc):
                res = flavor.toPython(wikitext, eval_state.mainparser,
                                      propval=propval)

    except NoContentException,e:
        raise
        if toPython:
            raise
        makeRestricted()
        res = eval_state.mainparser.render([
            Text(u"Content unspecified, raw markup: "),
            Start(CODEBLOCK),
            Text(wikitext),
            End(CODEBLOCK)])
    except WikiException,e:
        if toPython:
            raise
        res = eval_state.mainparser.render(
            _error(u"Wiki Error: {{{%s}}}" % (e.args[0],)))
    finally:
        deps = eval_state.dependencies
        images = eval_state.images
        
        clear_eval_state()
        if reentrant:
            eval_state.__dict__.update(old_eval_state)

    # rendered result, dependencies, metadata
    return res, deps, {'images': images}

def clear_eval_state():
    eval_state.__dict__.clear()

def get_eval_state_delta(which, text_or_elm, propname=None):
    if propname is not None:
        assert not structure.get_flavor(propname).binary
        text = unicode(text_or_elm[propname].value, 'utf-8')
        eval_state.dependencies.addDep(text_or_elm[propname])
    else:
        assert isinstance(text_or_elm, unicode)
        text = text_or_elm
    old_mentions = eval_state.mentions
    old_errors = eval_state.errors
    setattr(eval_state, which, [])
    eval_state.mainparser.parse(text, format=Format())
    ret = getattr(eval_state, which)
    eval_state.mentions = old_mentions
    eval_state.errors = old_errors
    return ret

def get_format():
    return eval_state.format

def get_topthis():
    return eval_state.topthis

def addDeps(deps):
    eval_state.dependencies.update(deps)

def makeRestricted():
    eval_state.dependencies.makeRestricted()
