def expose_builtin(*args, **kwargs):
    def really_expose(func):
        return make_wrapper(func, *args, **kwargs)
    return really_expose class Engine(object):
    def __init__(self, load_system=False):
        self.operations = None
        self.modulewrapper = ModuleWrapper(self)
        if load_system:
            self.modulewrapper.init_system_module()
        
        from prolog.builtin.statistics import Clocks
        self.clocks = Clocks()
        self.clocks.startup()
        self.streamwrapper = StreamWrapper()

    def _freeze_(self):
        return True

    # _____________________________________________________
    # database functionality

    def add_rule(self, ruleterm, end=True):
        module = self.modulewrapper.current_module
        rule = self.make_rule(ruleterm, module)

        signature = rule.signature
        if self.get_builtin(signature):
            error.throw_permission_error(
                "modify", "static_procedure", rule.head.get_prolog_signature())

        function = module.lookup(signature)
        function.add_rule(rule, end)
        return rule

    def make_rule(self, ruleterm, module):
        if helper.is_term(ruleterm):
            assert isinstance(ruleterm, Callable)
            if ruleterm.signature().eq(predsig):
                return Rule(ruleterm.argument_at(0), ruleterm.argument_at(1), module)
            else:
                return Rule(ruleterm, None, module)
        elif isinstance(ruleterm, Atom):
            return Rule(ruleterm, None, module)
        else:
            error.throw_type_error("callable", ruleterm)

    @jit.elidable_promote('all')
    def get_builtin(self, signature):
        from prolog import builtin # for the side-effects
        return signature.get_extra("builtin")


    # _____________________________________________________
    # parsing-related functionality

    def _build_and_run(self, tree, source_string, file_name):
        assert self is not None # for the annotator (!)
        from prolog.interpreter.parsing import TermBuilder
        builder = TermBuilder()
        term = builder.build_query(tree)
        if isinstance(term, Callable) and term.signature().eq(callsig):
            self.run_query_in_current(term.argument_at(0))
        else:
            term = self._term_expand(term)
            rule = self.add_rule(term)
            rule.file_name = file_name
            rule._init_source_info(tree, source_string)

    def _term_expand(self, term):
        if self.modulewrapper.system is not None:
            v = BindingVar()
            call = Callable.build("term_expand", [term, v])
            try:
                self.run_query_in_current(call)
            except error.UnificationFailed:
                v = BindingVar()
                call = Callable.build("term_expand", [term, v])
                self.run_query(call, self.modulewrapper.system)
            term = v.dereference(None)
        return term

    def runstring(self, s, file_name=None):
        from prolog.interpreter.parsing import parse_file
        parse_file(s, None, Engine._build_and_run, self, file_name=file_name)

    def parse(self, s, file_name=None):
        from prolog.interpreter.parsing import parse_file, TermBuilder
        builder = TermBuilder()
        trees = parse_file(s, None, file_name=file_name)
        terms = builder.build_many(trees)
        return terms, builder.varname_to_var

    def getoperations(self):
        from prolog.interpreter.parsing import default_operations
        if self.operations is None:
            return default_operations
        return self.operations

    # _____________________________________________________
    # Prolog execution

    def run_query(self, query, module, continuation=None):
        assert isinstance(module, Module)
        rule = module._toplevel_rule
        fcont = DoneFailureContinuation(self)
        if continuation is None:
            continuation = CutScopeNotifier(self, DoneSuccessContinuation(self), fcont)
        continuation = BodyContinuation(self, rule, continuation, query)
        return driver(continuation, fcont, Heap())

    def run_query_in_current(self, query, continuation=None):
        module = self.modulewrapper.current_module
        return self.run_query(query, module, continuation)

    def call(self, query, rule, scont, fcont, heap):
        if isinstance(query, Var):
            query = query.dereference(heap)
        if not isinstance(query, Callable):
            if isinstance(query, Var):
                raise error.throw_instantiation_error()
            raise error.throw_type_error('callable', query)
        signature = query.signature()        
        builtin = self.get_builtin(signature)
        if builtin is not None:
            return BuiltinContinuation(self, rule, scont, builtin, query), fcont, heap

        # do a real call
        module = rule.module
        function = self._get_function(signature, module, query)
        query = function.add_meta_prefixes(query, module.nameatom)
        startrulechain = jit.hint(function.rulechain, promote=True)
        rulechain = startrulechain.find_applicable_rule(query)
        if rulechain is None:
            raise error.UnificationFailed
        scont, fcont, heap = _make_rule_conts(self, scont, fcont, heap, query, rulechain)
        return scont, fcont, heap

    def call_in_module(self, query, module, scont, fcont, heap):
        return self.call(query, module._toplevel_rule, scont, fcont, heap)

    def _get_function(self, signature, module, query): 
        function = module.lookup(signature)
        if function.rulechain is None and self.modulewrapper.system is not None:
            function = self.modulewrapper.system.lookup(signature)
        if function.rulechain is None:
            return error.throw_existence_error(
                    "procedure", query.get_prolog_signature())
        return function

    # _____________________________________________________
    # module handling

    def switch_module(self, modulename):
        m = self.modulewrapper
        try:
            m.current_module = m.modules[modulename]
        except KeyError:
            module = Module(modulename)
            m.modules[modulename] = module
            m.current_module = module

    # _____________________________________________________
    # error handling

    @jit.unroll_safe
    def throw(self, exc, scont, fcont, heap, rule_likely_source=None):
        from prolog.interpreter import memo
        exc_term = exc.term
        # copy to make sure that variables in the exception that are
        # backtracked by the revert_upto below have the right value.
        exc_term = exc.term.copy(heap, memo.CopyMemo())
        orig_scont = scont
        while not scont.is_done():
            if not isinstance(scont, CatchingDelimiter):
                scont = scont.nextcont
                continue
            discard_heap = scont.heap
            heap = heap.revert_upto(discard_heap)
            try:
                scont.catcher.unify(exc_term, heap)
            except error.UnificationFailed:
                scont = scont.nextcont
            else:
                return self.call(
                    scont.recover, scont.rule, scont.nextcont, scont.fcont, heap)
        raise error.UncaughtError(exc_term, exc.sig_context, rule_likely_source, orig_scont)



    def __freeze__(self):
        return True class Heap(object):
    def __init__(self, prev=None):
        self.trail_var = INIT_TRAIL_VAR
        debug.make_sure_not_resized(self.trail_var)
        self.trail_binding = INIT_TRAIL_BINDING
        debug.make_sure_not_resized(self.trail_binding)
        self.i = 0
        self.trail_attrs = None
        self.prev = prev
        self.discarded = False
        self.hook = None

    # _____________________________________________________
    # interface that term.py uses
    def _find_not_discarded(self):
        while self is not None and self.discarded:
            self = self.prev
        return self

    def add_trail_atts(self, attvar, attr_name):
        if self._is_created_in_self(attvar):
            return
        value, index = attvar.get_attribute(attr_name)
        self._add_entry_trail_attrs(attvar, index, value)

    def trail_new_attr(self, attvar, index, value):
        if self._is_created_in_self(attvar):
            return
        self._add_entry_trail_attrs(attvar, index, value)

    def _add_entry_trail_attrs(self, attvar, index, value):
        entry = (attvar, index, value)
        if self.trail_attrs is None:
            self.trail_attrs = [entry]
        else:
            self.trail_attrs.append(entry)

    def add_trail(self, var):
        """ Remember the current state of a variable to be able to backtrack it
        to that state. Usually called just before a variable changes. """
        # if the variable doesn't exist before the last choice point, don't
        # trail it (variable shunting)
        if self._is_created_in_self(var):
            return
        i = self.i
        if i >= len(self.trail_var):
            assert i == len(self.trail_var)
            self._double_size()
        self.trail_var[i] = var
        self.trail_binding[i] = var.binding
        self.i = i + 1

    def _is_created_in_self(self, var):
        created_in = var.created_after_choice_point
        if self is created_in: # fast path
            return True
        if created_in is not None and created_in.discarded:
            # unroll _find_not_discarded once for better jittability
            created_in = created_in.prev
            if created_in is not None and created_in.discarded:
                created_in = created_in._find_not_discarded()
            var.created_after_choice_point = created_in
        return self is created_in

    def _double_size(self):
        l = len(self.trail_var)
        if l == 0:
            self.trail_var = [None, None]
            self.trail_binding = [None, None]
        elif l == 1:
            assert 0, "cannot happen"
        else:
            self.trail_var = self.trail_var + [None] * l
            self.trail_binding = self.trail_binding + [None] * l

    def newvar(self):
        """ Make a new variable. Should return a Var instance, possibly with
        interesting attributes set that e.g. add_trail can inspect."""
        result = BindingVar()
        result.created_after_choice_point = self
        return result

    def new_attvar(self):
        result = AttVar()
        result.created_after_choice_point = self
        return result

    def newvar_in_term(self, parent, index):
        from prolog.interpreter.term import var_in_term_classes
        return self.newvar() # disabled for now
        result = var_in_term_classes[index](parent)
        result.created_after_choice_point = self
        return result

    # _____________________________________________________

    def branch(self):
        """ Branch of a heap for a choice point. The return value is the new
        heap that should be used from now on, self is the heap that can be
        backtracked to."""
        res = Heap(self)
        return res

    @jit.unroll_safe
    def revert_upto(self, heap, discard_choicepoint=False):
        """ Revert to the heap corresponding to a choice point. The return
        value is the new heap that should be used."""
        previous = self
        while self is not heap:
            if self is None:
                break
            self._revert()
            previous = self
            self = self.prev
        if discard_choicepoint:
            return heap
        return previous

    @jit.look_inside_iff(lambda self: self.i < UNROLL_SIZE)
    def _revert(self):
        i = jit.promote(self.i) - 1
        while i >= 0:
            v = self.trail_var[i]
            assert v is not None
            v.binding = self.trail_binding[i]
            self.trail_var[i] = None
            self.trail_binding[i] = None
            i -= 1
        self.i = 0

        if self.trail_attrs is not None:
            for i in range(len(self.trail_attrs) - 1, -1, -1):
                attvar, index, value = self.trail_attrs[i]
                attvar.reset_field(index, value)

        self.trail_attrs = None
        self.hook = None

    def discard(self, current_heap):
        """ Remove a heap that is no longer needed (usually due to a cut) from
        a chain of frames. """
        self.discarded = True
        if current_heap.prev is self:
            current_heap._discard_try_remove_current_trail(self)
            if current_heap.trail_attrs is not None:
                current_heap._discard_try_remove_current_trail_attvars(self)

            # move the variable bindings from the discarded heap to the current
            # heap
            self._discard_move_bindings_to_current(current_heap)

            if self.trail_attrs is not None:
                if current_heap.trail_attrs is not None:
                    current_heap.trail_attrs.extend(self.trail_attrs)
                else:
                    current_heap.trail_attrs = self.trail_attrs

            current_heap.prev = self.prev
            self.trail_var = None
            self.trail_binding = None
            self.trail_attrs = None
            self.i = -1
            self.prev = current_heap
        else:
            return self
        return current_heap


    @jit.look_inside_iff(lambda self, discarded_heap:
            self.i < UNROLL_SIZE)
    def _discard_try_remove_current_trail(self, discarded_heap):
        targetpos = 0
        # check whether variables in the current heap no longer need to be
        # traced, because they originate in the discarded heap
        for i in range(jit.promote(self.i)):
            var = self.trail_var[i]
            binding = self.trail_binding[i]
            if var.created_after_choice_point is discarded_heap:
                var.created_after_choice_point = discarded_heap.prev
                self.trail_var[i] = None
                self.trail_binding[i] = None
            else:
                self.trail_var[targetpos] = var
                self.trail_binding[targetpos] = binding
                targetpos += 1
        self.i = targetpos

    def _discard_try_remove_current_trail_attvars(self, discarded_heap):
        trail_attrs = []
        targetpos = 0
        for var, attr, value in self.trail_attrs:
            if var.created_after_choice_point is discarded_heap:
                var.created_after_choice_point = discarded_heap.prev
            else:
                trail_attrs[targetpos] = (var, attr, value)
        if not trail_attrs:
            trail_attrs = None
        self.trail_attrs = trail_attrs


    @jit.look_inside_iff(lambda self, current_heap:
            self.i < UNROLL_SIZE)
    def _discard_move_bindings_to_current(self, current_heap):
        for i in range(jit.promote(self.i)):
            var = self.trail_var[i]
            currbinding = var.binding
            binding = self.trail_binding[i]

            var.binding = binding
            current_heap.add_trail(var)
            var.binding = currbinding

    def __repr__(self):
        return "<Heap %r trailed vars>" % (self.i, )

    def _dot(self, seen):
        if self in seen:
            return
        seen.add(self)
        yield '%s [label="%r", shape=octagon]' % (id(self), self)
        if self.prev:
            yield "%s -> %s [label=prev]" % (id(self), id(self.prev))
            for line in self.prev._dot(seen):
                yield line

    # methods to for the hook chain

    def add_hook(self, attvar):
        self.hook = HookCell(attvar, self.hook) class PrologError(Exception):
class UncatchableError(PrologError):
class PrologParseError(PrologError):
class TermedError(PrologError):
class CatchableError(TermedError): pass
class UncaughtError(TermedError):
class TraceFrame(object):
class UnificationFailed(PrologError):
    def __init__(self, message):
    def __init__(self, file_name, line_number, message):
    def __init__(self, term, sig_context=None):
    def get_errstr(self, engine):
    def __init__(self, term, sig_context=None, rule_likely_source=None, scont=None):
    def format_traceback(self, engine):
    def __init__(self, rule, next=None):
    def __repr__(self):
    def _format(self, out):
def _construct_traceback(scont):
def wrap_error(t):
def throw_syntax_error(msg):
def throw_import_error(modulename, signature):
def throw_existence_error(object_type, obj):
def throw_instantiation_error(obj = None):
def throw_representation_error(signature, msg):
def throw_type_error(valid_type, obj):
def throw_domain_error(valid_domain, obj):
def throw_permission_error(operation, permission_type, obj):
def throw_evaluation_error(error): DEBUG = False
OPTIMIZED_TERM_SIZE_MAX = 10
    TYPE_STANDARD_ORDER = 0
    TYPE_STANDARD_ORDER = 2
    TYPE_STANDARD_ORDER = 1
    TYPE_STANDARD_ORDER = 1
    TYPE_STANDARD_ORDER = 1
    TYPE_STANDARD_ORDER = 3
            TYPE_STANDARD_ORDER = Atom.TYPE_STANDARD_ORDER
            TYPE_STANDARD_ORDER = Term.TYPE_STANDARD_ORDER
        TYPE_STANDARD_ORDER = Term.TYPE_STANDARD_ORDER
def debug_print(*args):
    def dereference(self, heap):
    def copy(self, heap, memo):
    def copy_standardize_apart(self, heap, env):
    def copy_standardize_apart_as_child_of(self, heap, env, parent, index):
    def unify_and_standardize_apart(self, other, heap, env):
    def enumerate_vars(self, memo):
    def unify(self, other, heap, occurs_check=False):
    def _unify_derefed(self, other, heap, occurs_check=False):
    def contains_var(self, var, heap):
    def eval_arithmetic(self, engine):
    def cmp_standard_order(self, other, heap):
    def quick_unify_check(self, other):
    def __init__(self):
    def unify(self, other, heap, occurs_check=False):
    def _unify_potential_recursion(self, next, other, heap, occurs_check):
    def getbinding(self):
    def _unify_derefed(self, other, heap, occurs_check=False):
    def dereference(self, heap):
    def copy(self, heap, memo):
    def enumerate_vars(self, memo):
    def contains_var(self, var, heap):
    def __repr__(self):
    def eval_arithmetic(self, engine):
    def cmp_standard_order(self, other, heap):
    def __init__(self):
    def getbinding(self):
    def setvalue(self, value, heap):
    def _unify_potential_recursion(self, next, other, heap, occurs_check):
    def __init__(self, parent):
    def init(self, parent):
    def getbinding(self):
    def dereference(self, heap):
    def setvalue(self, value, heap):
    def _setvalue_in_parent(self, value):
    def __repr__(self):
def make_var_in_term_class(index):
        def __init__(self, parent):
        def _setvalue_in_parent(self, value):
    def __init__(self):
    def get_index(self, attname):
    def with_extra_attribute(self, attname):
    def get_attname_at_index(self, index):
    def __init__(self):
    def _unify_derefed(self, other, heap, occurs_check=False):
    def setvalue(self, value, heap):
    def __repr__(self):
    def copy(self, heap, memo):
    def add_attribute(self, attname, attribute):
    def del_attribute(self, attname):
    def get_attribute(self, attname):
    def reset_field(self, index, value):
    def get_attribute_index(self, attname):
    def is_empty(self):
    def __init__(self, index):
    def copy_standardize_apart(self, heap, env):
    def copy_standardize_apart_as_child_of(self, heap, env, parent, index):
    def unify_and_standardize_apart(self, other, heap, env):
    def dereference(self, heap):
    def __repr__(self):
    def dereference(self, heap):
    def copy(self, heap, memo):
    def copy_standardize_apart(self, heap, memo):
    def unify(self, other, heap, occurs_check=False):
    def basic_unify(self, other, heap, occurs_check):
    def _unify_derefed(self, other, heap, occurs_check=False):
    def unify_and_standardize_apart(self, other, heap, env):
    def basic_unify_and_standardize_apart(self, other, heap, env):
    def enumerate_vars(self, memo):
    def __init__(self):
    def name(self):
    def signature(self):
    def get_prolog_signature(self):
    def arguments(self):
    def argument_at(self, i):
    def argument_count(self):
    def basic_unify(self, other, heap, occurs_check):
    def basic_unify_and_standardize_apart(self, other, heap, env):
    def copy(self, heap, memo):
    def copy_standardize_apart(self, heap, env):
    def enumerate_vars(self, memo):
    def _copy_term(self, copy_individual, heap, *extraargs):
    def contains_var(self, var, heap):
    def cmp_standard_order(self, other, heap):
    def eval_arithmetic(self, engine):
    def build(term_name, args=None, signature=None, heap=None, cache=True):
    def _find_specialized_class(term_name, numargs):
    def __repr__(self):
    def quick_unify_check(self, other):
    def set_argument_at(self, i, arg):
    def __init__(self, name, signature=None):
    def __str__(self):
    def __repr__(self):
    def newatom(name, signature=None):
    def eval_arithmetic(self, engine):
    def arguments(self):
    def argument_at(self, i):
    def argument_count(self):
    def name(self):
    def signature(self):
    def basic_unify_and_standardize_apart(self, other, heap, env):
    def eval_arithmetic(self, engine):
    def __init__(self, val):
    def basic_unify(self, other, heap, occurs_check):
    def __str__(self):
    def __repr__(self):
    def cmp_standard_order(self, other, heap):
    def quick_unify_check(self, other):
    def __init__(self, value):
    def basic_unify(self, other, heap, occurs_check):
    def __str__(self):
    def __repr__(self):
    def cmp_standard_order(self, other, heap):
    def __init__(self, floatval):
    def basic_unify(self, other, heap, occurs_check):
    def __str__(self):
    def __repr__(self):
    def eval_arithmetic(self, engine):
    def cmp_standard_order(self, other, heap):
def _term_copy(obj, i, heap, memo):
def _term_copy_standardize_apart(obj, i, heap, env):
def _term_enumerate_vars(obj, i, _, memo):
def _term_unify_and_standardize_apart(obj, i, heap, other, memo):
    def __init__(self, term_name, args, signature):
    def __repr__(self):
    def __str__(self):
    def arguments(self):
    def argument_at(self, i):
    def argument_count(self):
    def signature(self):
def rcmp(a, b): # RPython does not support cmp...
def bigint_cmp(a, b):
def cmp_standard_order(obj1, obj2, heap):
def generate_class(cname, fname, n_args, immutable=True):
        def __init__(self, term_name, args, signature):
        def name(self):
        def signature(self):
        def _make_new(self, name, signature):
            def _make_new_mutable(self, name, signature):
def generate_abstract_class(n_args, immutable=True):
        def __init__(self, term_name, args, signature):
        def _init_values(self, args):
        def _make_new(self, name, signature):
        def arguments(self):
        def argument_at(self, i):
            def set_argument_at(self, i, arg):
        def argument_count(self):
        def quick_unify_check(self, other):
        def basic_unify_and_standardize_apart(self, other, heap, env):
        def copy_standardize_apart(self, heap, env):
        def basic_unify(self, other, heap, occurs_check):
        def _copy_term(self, copy_individual, heap, *extraargs):
def generate_generic_class(n_args, immutable=True):
        def __init__(self, term_name, args, signature):
        def _make_new(self, name, signature):
            def _make_new_mutable(self, name, signature):
        def signature(self):
class PrologObject(object):
class Var(PrologObject):
class BindingVar(Var):
class VarInTerm(Var):
    class VarInTermN(VarInTerm):
class AttMap(object):
class AttVar(BindingVar):
class NumberedVar(PrologObject):
class NonVar(PrologObject):
class Callable(NonVar):
class MutableCallable(Callable):
class Atom(Callable):
class Numeric(NonVar):
class Number(Numeric):#, UnboxedValue):
class BigInt(Numeric):
class Float(Numeric):
class Term(Callable):
    class specific_class(parent):
    class abstract_callable(base):
    class generic_callable(parent): class PrologStream(object):
    def __init__(self, stream):
        self.stream = stream
        self.alias = "$stream_%d" % self.fd()

    def fd(self):   
        return self.stream.try_to_find_file_descriptor()

    def seek(self, offset, how):
        self.stream.seek(offset, how)

    def close(self):
        self.stream.close()

    def tell(self):
        return self.stream.tell() class PrologInputStream(PrologStream):
    def __init__(self, stream):
        PrologStream.__init__(self, stream)

    def read(self, n):
        if self.fd() == 0:
            s = fdopen_as_stream(1, "w", False)
            s.write("|: ")
        return self.stream.read(n) class PrologOutputStream(PrologStream):
    def __init__(self, fd):
        PrologStream.__init__(self, fd)

    def write(self, data):
        self.stream.write(data) def wrap_list(python_list):
def unwrap_list(prolog_list):
def is_callable(var, engine):
def ensure_callable(var):
def unwrap_int(obj):
def unwrap_atom(obj):
def unwrap_predicate_indicator(predicate):
def unwrap_stream(engine, obj):
def unwrap_instream(engine, obj):
def unwrap_outstream(engine, obj):
def ensure_atomic(obj):
def is_atomic(obj):
def is_term(obj):
def convert_to_str(obj):
def is_numeric(obj): class TermFormatter(object):
    def __init__(self, engine, quoted=False, max_depth=0,
                 ignore_ops=False):
        self.engine = engine
        self.quoted = quoted
        self.max_depth = max_depth
        self.ignore_ops = ignore_ops
        self.curr_depth = 0
        self._make_reverse_op_mapping()
        self.var_to_number = {}
    
    def from_option_list(engine, options):
        # XXX add numbervars support
        quoted = False
        max_depth = 0
        ignore_ops = False
        number_vars = False
        for option in options:
            if (not helper.is_term(option) or (isinstance(option, Callable) and option.argument_count() != 1)):
                error.throw_domain_error('write_option', option)
            assert isinstance(option, Callable)
            arg = option.argument_at(0)
            if option.name()== "max_depth":
                try:
                    max_depth = helper.unwrap_int(arg)
                except error.CatchableError:
                    error.throw_domain_error('write_option', option)
            elif (not isinstance(arg, Atom) or
                (arg.name()!= "true" and arg.name()!= "false")):
                error.throw_domain_error('write_option', option)
                assert 0, "unreachable"
            elif option.name()== "quoted":
                quoted = arg.name()== "true"
            elif option.name()== "ignore_ops":
                ignore_ops = arg.name()== "true"
        return TermFormatter(engine, quoted, max_depth, ignore_ops)
    from_option_list = staticmethod(from_option_list)

    def format(self, term):
        self.curr_depth += 1
        term = term.dereference(None)
        if self.max_depth > 0 and self.curr_depth > self.max_depth:
            return "..."
        if isinstance(term, Atom):
            return self.format_atom(term.name())
        elif isinstance(term, Number):
            return self.format_number(term)
        elif isinstance(term, Float):
            return self.format_float(term)
        elif helper.is_term(term):
            assert isinstance(term, Callable)
            return self.format_term(term)
        elif isinstance(term, AttVar):
            return self.format_attvar(term)
        elif isinstance(term, Var):
            return self.format_var(term)
        elif isinstance(term, PrologStream):
            return self.format_stream(term)
        else:
            return '?'

    def format_atom(self, s):
        from rpython.rlib.parsing.deterministic import LexerError
        if self.quoted:
            try:
                tokens = parsing.lexer.tokenize(s)
                if (len(tokens) == 1 and tokens[0].name == 'ATOM' and
                    tokens[0].source == s):
                    return s
            except LexerError:
                pass
            return "'%s'" % (s, )
        return s

    def format_number(self, num):
        return str(num.num)

    def format_float(self, num):
        return str(num.floatval)

    def format_attvar(self, attvar):
        l = []
        if attvar.value_list is not None:
            for name, index in attvar.attmap.indexes.iteritems():
                value = attvar.value_list[index]
                if value is not None:
                    l.append("put_attr(%s, %s, %s)" % (self.format_var(attvar),
                            name, self.format(value)))
        return "\n".join(l)

    def format_var(self, var):
        try:
            num = self.var_to_number[var]
        except KeyError:
            num = self.var_to_number[var] = len(self.var_to_number)
        return "_G%s" % (num, )

    def format_term_normally(self, term):
        return "%s(%s)" % (self.format_atom(term.name()),
                           ", ".join([self.format(a) for a in term.arguments()]))

    def format_term(self, term):
        if self.ignore_ops:
            return self.format_term_normally(term)
        else:
            return self.format_with_ops(term)[1]

    def format_stream(self, stream):
        return "'$stream'(%d)" % stream.fd()

    def format_with_ops(self, term):
        if not helper.is_term(term):
            return (0, self.format(term))
        assert isinstance(term, Callable)
        if term.signature().eq(conssig):
            result = ["["]
            while helper.is_term(term) and isinstance(term, Callable) and term.signature().eq(conssig):
                first = term.argument_at(0)
                second = term.argument_at(1).dereference(None)
                result.append(self.format(first))
                result.append(", ")
                term = second
            if isinstance(term, Atom) and term.signature().eq(nilsig):
                result[-1] = "]"
            else:
                result[-1] = "|"
                result.append(self.format(term))
                result.append("]")
            return (0, "".join(result))
        if term.signature().eq(tuplesig):
            result = ["("]
            while helper.is_term(term) and isinstance(term, Callable) and term.signature().eq(tuplesig):
                first = term.argument_at(0)
                second = term.argument_at(1)
                result.append(self.format(first))
                result.append(", ")
                term = second
            result.append(self.format(term))
            result.append(")")
            return (0, "".join(result))
        if (term.argument_count(), term.name()) not in self.op_mapping:
            return (0, self.format_term_normally(term))
        form, prec = self.op_mapping[(term.argument_count(), term.name())]
        result = []
        assert 0 <= term.argument_count() <= 2
        curr_index = 0
        for c in form:
            if c == "f":
                result.append(self.format_atom(term.name()))
            else:
                childprec, child = self.format_with_ops(term.argument_at(curr_index))
                parentheses = (c == "x" and childprec >= prec or
                               c == "y" and childprec > prec)
                if parentheses:
                    result.append("(")
                    result.append(child)
                    result.append(")")
                else:
                    result.append(child)
                curr_index += 1
        assert curr_index == term.argument_count()
        return (prec, "".join(result))

    def _make_reverse_op_mapping(self):
        m = {}
        for prec, allops in self.engine.getoperations():
            for form, ops in allops:
                for op in ops:
                    m[len(form) - 1, op] = (form, prec)
        self.op_mapping = m
import os
from prolog.builtin.register import expose_builtin
from prolog.interpreter.continuation import Engine
from prolog.interpreter.heap import Heap
from prolog.interpreter import error
from prolog.interpreter import term
from prolog.interpreter.stream import PrologStream, PrologInputStream, \
PrologOutputStream
from prolog.interpreter import helper
from prolog.builtin.formatting import TermFormatter
from rpython.rlib.streamio import fdopen_as_stream, open_file_as_stream
from rpython.rlib import rstring
    from prolog.interpreter.parsing import parse_query_term


rwa = {"read": "r", "write": "w", "append": "a"}
seek_mode = {"bof": os.SEEK_SET, "current": os.SEEK_CUR, "eof": os.SEEK_END}

def make_option_dict(options):
    opts = {}
    for option in options:
        if isinstance(option, term.Var):
            error.throw_instantiation_error()
        if isinstance(option, term.Numeric):
            error.throw_domain_error("stream_option", option)
        if isinstance(option, term.Callable) and option.argument_count() == 1:
            arg0 = option.argument_at(0)
            if isinstance(arg0, term.Atom):
                opts[option.name()] = arg0.name()
    return opts

@expose_builtin("open", unwrap_spec=["atom", "atom", "obj", "list"])
def impl_open_options(engine, heap, srcpath, mode, stream, options):
    if not isinstance(stream, term.Var):
        error.throw_type_error("variable", stream)
    opts = make_option_dict(options)
    mode = rwa.get(mode, None)
    if mode is None:
        error.throw_domain_error("io_mode", term.Callable.build(
                "mode not supported"))
    else:
        buffering = opts.get("buffer", "full")
        if buffering == "full":
            bufmode = -1
        elif buffering == "line":
            bufmode = 1
        elif buffering == "false":
            bufmode = 0
        else:
            error.throw_domain_error("buffering", term.Callable.build(buffering))
            assert 0, "unreachable"

        try:
            if mode == "r":

                prolog_stream = PrologInputStream(open_file_as_stream(