Carl Friedrich Bolz avatar Carl Friedrich Bolz committed 9253070 Merge

merge better-errors

Comments (0)

Files changed (22)

prolog/builtin/allsolution.py

 
 
 @expose_builtin("findall", unwrap_spec=['raw', 'callable', 'raw'],
-                handles_continuation=True, needs_module=True)
-def impl_findall(engine, heap, module, template, goal, bag, scont, fcont):
+                handles_continuation=True, needs_rule=True)
+def impl_findall(engine, heap, rule, template, goal, bag, scont, fcont):
     newheap = heap.branch()
     collector = FindallContinuation(engine, template, heap, scont)
-    newscont = continuation.BodyContinuation(engine, module, collector, goal)
+    newscont = continuation.BodyContinuation(engine, rule, collector, goal)
     fcont = DoneWithFindallContinuation(engine, scont, fcont, heap, collector, bag)
     return newscont, fcont, newheap

prolog/builtin/control.py

     return scont, end_fcont, heap
 
 @expose_builtin(",", unwrap_spec=["callable", "raw"], 
-        handles_continuation=True, needs_module=True)
-def impl_and(engine, heap, module, call1, call2, scont, fcont):
+        handles_continuation=True, needs_rule=True)
+def impl_and(engine, heap, rule, call1, call2, scont, fcont):
     if not isinstance(call2, term.Var) and not isinstance(call2, term.Callable):
         return error.throw_type_error('callable', call2)
-    scont = continuation.BodyContinuation(engine, module, scont, call2)
-    return engine.call(call1, module, scont, fcont, heap)
+    scont = continuation.BodyContinuation(engine, rule, scont, call2)
+    return engine.call(call1, rule, scont, fcont, heap)
 
 class OrContinuation(continuation.FailureContinuation):
-    def __init__(self, engine, module, nextcont, orig_fcont, undoheap, altcall):
+    def __init__(self, engine, rule, nextcont, orig_fcont, undoheap, altcall):
         continuation.FailureContinuation.__init__(self, engine, nextcont, orig_fcont, undoheap)
         self.altcall = altcall
         assert undoheap is not None
-        self.module = module
+        self.rule = rule
 
     def fail(self, heap):
         heap = heap.revert_upto(self.undoheap, discard_choicepoint=True)
-        scont = continuation.BodyContinuation(self.engine, self.module, self.nextcont, self.altcall)
+        scont = continuation.BodyContinuation(self.engine, self.rule, self.nextcont, self.altcall)
         return scont, self.orig_fcont, heap
 
     def __repr__(self):
 
 
 @expose_builtin(";", unwrap_spec=["callable", "callable"],
-                handles_continuation=True, needs_module=True)
-def impl_or(engine, heap, module, call1, call2, scont, fcont):
+                handles_continuation=True, needs_rule=True)
+def impl_or(engine, heap, rule, call1, call2, scont, fcont):
     # sucks a bit to have to special-case A -> B ; C here :-(
     if call1.signature().eq(ifsig):
         assert helper.is_term(call1)
         return if_then_else(
-                engine, heap, module, scont, fcont,
+                engine, heap, rule, scont, fcont,
                 call1.argument_at(0),
                 call1.argument_at(1), call2)
     else:
-        fcont = OrContinuation(engine, module, scont, fcont, heap, call2)
-        newscont = continuation.BodyContinuation(engine, module, scont, call1)
+        fcont = OrContinuation(engine, rule, scont, fcont, heap, call2)
+        newscont = continuation.BodyContinuation(engine, rule, scont, call1)
         return newscont, fcont, heap.branch()
 
-def if_then_else(engine, heap, module, scont, fcont, if_clause, then_clause, else_clause):
-    newfcont = OrContinuation(engine, module, scont, fcont, heap, else_clause)
+def if_then_else(engine, heap, rule, scont, fcont, if_clause, then_clause, else_clause):
+    newfcont = OrContinuation(engine, rule, scont, fcont, heap, else_clause)
     newscont, fcont, heap = impl_if(
-            engine, heap, module, if_clause, then_clause, scont, newfcont, fcont)
+            engine, heap, rule, if_clause, then_clause, scont, newfcont, fcont)
     return newscont, fcont, heap.branch()
 
 @expose_builtin("->", unwrap_spec=["callable", "raw"],
-                handles_continuation=True, needs_module=True)
-def impl_if(engine, heap, module, if_clause, then_clause, scont, fcont,
+                handles_continuation=True, needs_rule=True)
+def impl_if(engine, heap, rule, if_clause, then_clause, scont, fcont,
             fcont_after_condition=None):
     if fcont_after_condition is None:
         fcont_after_condition = fcont
-    scont = continuation.BodyContinuation(engine, module, scont, then_clause)
+    scont = continuation.BodyContinuation(engine, rule, scont, then_clause)
     scont = IfScopeNotifier(engine, scont, fcont, fcont_after_condition)
-    newscont = continuation.BodyContinuation(engine, module, scont, if_clause)
+    newscont = continuation.BodyContinuation(engine, rule, scont, if_clause)
     return newscont, fcont, heap
 
 class IfScopeNotifier(continuation.CutScopeNotifier):
         return self.nextcont, self.fcont_after_condition, heap
 
 @expose_builtin(["not", "\\+"], unwrap_spec=["callable"],
-                handles_continuation=True, needs_module=True)
-def impl_not(engine, heap, module, call, scont, fcont):
-    return if_then_else(engine, heap, module, scont, fcont, call, FAILATOM, TRUEATOM)
+                handles_continuation=True, needs_rule=True)
+def impl_not(engine, heap, rule, call, scont, fcont):
+    return if_then_else(engine, heap, rule, scont, fcont, call, FAILATOM, TRUEATOM)

prolog/builtin/database.py

     if rule.signature().eq(prefixsig):
         modname, rule = unpack_modname_and_predicate(rule)
         engine.switch_module(modname)
-    engine.add_rule(rule.dereference(heap), end=end, old_modname=current_modname)
+    engine.add_rule(rule.dereference(heap), end=end)
+    engine.switch_module(current_modname)
 
 @expose_builtin("retract", unwrap_spec=["callable"], needs_module=True)
 def impl_retract(engine, heap, module, pattern):

prolog/builtin/exception.py

 # exception handling
 
 @expose_builtin("catch", unwrap_spec=["callable", "obj", "callable"],
-                handles_continuation=True, needs_module=True)
-def impl_catch(engine, heap, module, goal, catcher, recover, scont, fcont):
-    scont = continuation.CatchingDelimiter(engine, module, scont, fcont, catcher, recover, heap)
+                handles_continuation=True, needs_rule=True)
+def impl_catch(engine, heap, rule, goal, catcher, recover, scont, fcont):
+    scont = continuation.CatchingDelimiter(engine, rule, scont, fcont, catcher, recover, heap)
     scont = continuation.CutScopeNotifier(engine, scont, fcont)
-    scont = continuation.BodyContinuation(engine, module, scont, goal)
+    scont = continuation.BodyContinuation(engine, rule, scont, goal)
     #continuation.view(scont, fcont, heap)
     return scont, fcont, heap.branch()
 
-@expose_builtin("throw", unwrap_spec=["obj"], handles_continuation=True)
-def impl_throw(engine, heap, exc, scont, fcont):
-    return engine.throw(exc, scont, fcont, heap)
+@expose_builtin("throw", unwrap_spec=["obj"], needs_rule=True)
+def impl_throw(engine, heap, rule, exc):
+    raise error.CatchableError(exc)
 

prolog/builtin/metacall.py

 # meta-call predicates
 
 @expose_builtin("call", unwrap_spec=["callable"],
-                handles_continuation=True, needs_module=True)
-def impl_call(engine, heap, module, call, scont, fcont):
+                handles_continuation=True, needs_rule=True)
+def impl_call(engine, heap, rule, call, scont, fcont):
     scont = continuation.CutScopeNotifier.insert_scope_notifier(engine, scont, fcont)
-    return engine.call(call, module, scont, fcont, heap)
+    return engine.call(call, rule, scont, fcont, heap)
 
 class OnceContinuation(continuation.Continuation):
     def __init__(self, engine, nextcont, fcont):
         return self.nextcont, self.fcont, heap
 
 @expose_builtin("once", unwrap_spec=["callable"],
-                handles_continuation=True, needs_module=True)
-def impl_once(engine, heap, module, clause, scont, fcont):
+                handles_continuation=True, needs_rule=True)
+def impl_once(engine, heap, rule, clause, scont, fcont):
     scont = OnceContinuation(engine, scont, fcont)
-    return engine.call(clause, module, scont, fcont, heap)
+    return engine.call(clause, rule, scont, fcont, heap)
 

prolog/builtin/modules.py

 def impl_module_prefixing(engine, heap, modulename, 
         call, scont, fcont):
     module = engine.modulewrapper.get_module(modulename, call)
-    return engine.call(call, module, scont, fcont, heap)
+    return engine.call_in_module(call, module, scont, fcont, heap)
 
 @expose_builtin("add_library_dir", unwrap_spec=["atom"])
 def impl_add_library_dir(engine, heap, path):

prolog/builtin/register.py

         self.numargs = numargs
         self.signature = signature
 
-    def call(self, engine, query, module, scont, fcont, heap):
-        return self.function(engine, query, module, scont, fcont, heap)
-        
+    def call(self, engine, query, rule, scont, fcont, heap):
+        try:
+            return self.function(engine, query, rule, scont, fcont, heap)
+        except error.CatchableError as e:
+            e.sig_context = self.signature
+            raise
+
     def _freeze_(self):
         return True
 
     return really_expose
 
 def make_wrapper(func, name, unwrap_spec=[], handles_continuation=False,
-                   translatable=True, needs_module=False):
+                   translatable=True, needs_module=False, needs_rule=False):
+    numargs = len(unwrap_spec)
     if isinstance(name, list):
         expose_as = name
         name = name[0]
     if not name.isalnum():
         name = func.func_name
     orig_funcargs = inspect.getargs(func.func_code)[0]
-    funcname = "wrap_%s_%s" % (name, len(unwrap_spec))
-    code = ["def %s(engine, query, module, scont, fcont, heap):" % (funcname, )]
+    funcname = "wrap_%s_%s" % (name, numargs)
+    code = ["def %s(engine, query, rule, scont, fcont, heap):" % (funcname, )]
+    code.append("    module = rule.module")
     if not translatable:
         code.append("    if we_are_translated():")
         code.append("        raise error.UncatchableError('%s does not work in translated version')" % (name, ))
     if needs_module:
         subargs.insert(2, "module")
         assert orig_funcargs[2] == "module"
+    if needs_rule:
+        subargs.insert(2, "rule")
+        assert orig_funcargs[2] == "rule"
     if handles_continuation:
         subargs.append("scont")
         subargs.append("fcont")
         code.append("    return scont, fcont, heap")
     else:
         code.append("    return result")
-    miniglobals = globals().copy()
+
+    used_globals = ["helper", "error", "term", "eval_arithmetic"]
+    miniglobals = {key: globals()[key] for key in used_globals}
     miniglobals[func.func_name] = func
-    #if func.__module__[len("prolog.builtin."):] not in jit_modules:
-    #    jit.dont_look_inside(func)
     exec py.code.Source("\n".join(code)).compile() in miniglobals
     for name in expose_as:
-        l = len(unwrap_spec)
-        signature = Signature.getsignature(name, l)
-        b = Builtin(miniglobals[funcname], funcname, l, signature)
+        signature = Signature.getsignature(name, numargs)
+        b = Builtin(miniglobals[funcname], funcname, numargs, signature)
         signature.set_extra("builtin", b)
     return func

prolog/builtin/termconstruction.py

                     heap)
 
 @continuation.make_failure_continuation
-def continue_arg(Choice, engine, scont, fcont, heap, varnum, num, temarg, vararg):
+def continue_arg(Choice, engine, scont, fcont, heap, varnum, num, temarg, vararg, rule):
     if num < temarg.argument_count() - 1:
-        fcont = Choice(engine, scont, fcont, heap, varnum, num + 1, temarg, vararg)
+        fcont = Choice(engine, scont, fcont, heap, varnum, num + 1, temarg, vararg, rule)
         heap = heap.branch()
     scont = continuation.BodyContinuation(
-            engine, engine.modulewrapper.user_module, scont, term.Callable.build("=", [vararg, temarg.argument_at(num)]))
+            engine, rule, scont, term.Callable.build("=", [vararg, temarg.argument_at(num)]))
     varnum.unify(term.Number(num + 1), heap)
     return scont, fcont, heap
 
 @expose_builtin("arg", unwrap_spec=["obj", "obj", "obj"],
-handles_continuation=True)
-def impl_arg(engine, heap, first, second, third, scont, fcont):
+                needs_rule=True, handles_continuation=True)
+def impl_arg(engine, heap, rule, first, second, third, scont, fcont):
     if isinstance(second, term.Var):
         error.throw_instantiation_error()
     if isinstance(second, term.Atom):
         error.throw_type_error("compound", second)
     assert isinstance(second, term.Callable)
     if isinstance(first, term.Var):
-        return continue_arg(engine, scont, fcont, heap, first, 0, second, third)
+        return continue_arg(engine, scont, fcont, heap, first, 0, second, third, rule)
     elif isinstance(first, term.Number):
         num = first.num
         if num == 0:

prolog/interpreter/continuation.py

     rule = None
     while not scont.is_done():
         #view(scont=scont, fcont=fcont, heap=heap)
-        if isinstance(scont, RuleContinuation):
-            rule = scont._rule
-            if scont._rule.body is not None:
-                jitdriver.can_enter_jit(rule=rule, scont=scont, fcont=fcont,
-                                        heap=heap)
+        if isinstance(scont, ContinuationWithRule):
+            rule = scont.rule
+        if isinstance(scont, RuleContinuation) and rule.body is not None:
+            jitdriver.can_enter_jit(rule=rule, scont=scont, fcont=fcont,
+                                    heap=heap)
         try:
             jitdriver.jit_merge_point(rule=rule, scont=scont, fcont=fcont,
                                       heap=heap)
                 if fcont.is_done():
                     raise
             scont, fcont, heap = fcont.fail(heap)
-        except error.CatchableError, e:
-            scont, fcont, heap = scont.engine.throw(e.term, scont, fcont, heap, rule)
+        except error.CatchableError, exc:
+            scont, fcont, heap = scont.engine.throw(exc, scont, fcont, heap, rule)
         else:
             scont, fcont, heap = _process_hooks(scont, fcont, heap)
     assert isinstance(scont, DoneSuccessContinuation)
                 try:
                     mod = e.modulewrapper.get_module(module, query)
                 except error.CatchableError, err:
-                    scont, fcont, heap = scont.engine.throw(err.term, scont, fcont, heap)
+                    scont, fcont, heap = scont.engine.throw(err, scont, fcont, heap)
                     break
-                scont, fcont, heap = e.call(query, mod, scont, fcont, heap)
+                scont, fcont, heap = e.call_in_module(query, mod, scont, fcont, heap)
                 heap.add_trail_atts(attvar, module)
             hookcell = hookcell.next
             attvar.value_list = None # XXX?
 
 class Engine(object):
     def __init__(self, load_system=False):
-        self.parser = None
         self.operations = None
         self.modulewrapper = ModuleWrapper(self)
         if load_system:
     # _____________________________________________________
     # database functionality
 
-    def add_rule(self, rule, end=True, old_modname=None):
-        m = self.modulewrapper
-        if helper.is_term(rule):
-            assert isinstance(rule, Callable)
-            if rule.signature().eq(predsig):
-                rule = Rule(rule.argument_at(0), rule.argument_at(1),
-                        m.current_module)
-            else:
-                rule = Rule(rule, None, m.current_module)
-        elif isinstance(rule, Atom):
-            rule = Rule(rule, None, m.current_module)
-        else:
-            error.throw_type_error("callable", rule)
-            assert 0, "unreachable" # make annotator happy
-        signature = rule.signature        
+    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 = m.current_module.lookup(signature)
+        function = module.lookup(signature)
         function.add_rule(rule, end)
-        if old_modname is not None:
-            self.switch_module(old_modname)
+        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):
     # _____________________________________________________
     # parsing-related functionality
 
-    def _build_and_run(self, tree):
+    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(term.argument_at(0), self.modulewrapper.current_module)
+            self.run_query_in_current(term.argument_at(0))
         else:
-            self._term_expand(term)
-        return self.parser
+            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(call, self.modulewrapper.current_module)
+                self.run_query_in_current(call)
             except error.UnificationFailed:
                 v = BindingVar()
                 call = Callable.build("term_expand", [term, v])
-                self.run(call, self.modulewrapper.system)
+                self.run_query(call, self.modulewrapper.system)
             term = v.dereference(None)
-        self.add_rule(term)
+        return term
 
     def runstring(self, s, file_name=None):
         from prolog.interpreter.parsing import parse_file
-        parse_file(s, self.parser, Engine._build_and_run, self, file_name=file_name)
+        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, self.parser, file_name=file_name)
+        trees = parse_file(s, None, file_name=file_name)
         terms = builder.build_many(trees)
         return terms, builder.varname_to_var
 
 
     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)
-        driver(*self.call(query, module, continuation, fcont, Heap()))
-    run = run_query
+        continuation = BodyContinuation(self, rule, continuation, query)
+        return driver(continuation, fcont, Heap())
 
-    def call(self, query, module, scont, 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):
         signature = query.signature()        
         builtin = self.get_builtin(signature)
         if builtin is not None:
-            return BuiltinContinuation(self, module, scont, builtin, query), fcont, heap
+            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)
         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:
     @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 = exc.copy(heap, memo.CopyMemo())
+        exc_term = exc.term.copy(heap, memo.CopyMemo())
+        orig_scont = scont
         while not scont.is_done():
             if not isinstance(scont, CatchingDelimiter):
                 scont = scont.nextcont
             discard_heap = scont.heap
             heap = heap.revert_upto(discard_heap)
             try:
-                scont.catcher.unify(exc, heap)
+                scont.catcher.unify(exc_term, heap)
             except error.UnificationFailed:
                 scont = scont.nextcont
             else:
                 return self.call(
-                    scont.recover, scont.module, scont.nextcont, scont.fcont, heap)
-        raise error.UncaughtError(exc, rule_likely_source)
+                    scont.recover, scont.rule, scont.nextcont, scont.fcont, heap)
+        raise error.UncaughtError(exc_term, exc.sig_context, rule_likely_source, orig_scont)
 
 
 
 
     _dot = _dot
 
-class ContinuationWithModule(Continuation):
+class ContinuationWithRule(Continuation):
     """ This class represents continuations which need
-    to take care of the module system. """
+    to have a reference to the current rule
+    (e.g. to get at the module). """
 
-    def __init__(self, engine, module, nextcont):
+    def __init__(self, engine, nextcont, rule):
         Continuation.__init__(self, engine, nextcont)
-        self.module = module
+        assert isinstance(rule, Rule)
+        self.rule = rule
 
 def view(*objects, **names):
     from dotviewer import graphclient
         return True
 
 
-class BodyContinuation(ContinuationWithModule):
+class BodyContinuation(ContinuationWithRule):
     """ Represents a bit of Prolog code that is still to be called. """
-    def __init__(self, engine, module, nextcont, body):
-        ContinuationWithModule.__init__(self, engine, module, nextcont)
+    def __init__(self, engine, rule, nextcont, body):
+        ContinuationWithRule.__init__(self, engine, nextcont, rule)
         self.body = body
 
     def activate(self, fcont, heap):
-        return self.engine.call(self.body, self.module, self.nextcont, fcont, heap)
+        return self.engine.call(self.body, self.rule, self.nextcont, fcont, heap)
 
     def __repr__(self):
         return "<BodyContinuation %r>" % (self.body, )
 
-class BuiltinContinuation(ContinuationWithModule):
+class BuiltinContinuation(ContinuationWithRule):
     """ Represents the call to a builtin. """
-    def __init__(self, engine, module, nextcont, builtin, query):
-        ContinuationWithModule.__init__(self, engine, module, nextcont)
+    def __init__(self, engine, rule, nextcont, builtin, query):
+        ContinuationWithRule.__init__(self, engine, nextcont, rule)
         self.builtin = builtin
         self.query = query
 
     def activate(self, fcont, heap):
-        return self.builtin.call(self.engine, self.query, self.module, 
+        return self.builtin.call(self.engine, self.query, self.rule,
                 self.nextcont, fcont, heap)
 
     def __repr__(self):
                 self.query, self.rulechain)
     
 
-class RuleContinuation(Continuation):
+class RuleContinuation(ContinuationWithRule):
     """ A Continuation that represents the application of a rule, i.e.:
         - standardizing apart of the rule
         - unifying the rule head with the query
     """
 
     def __init__(self, engine, nextcont, rule, query):
-        Continuation.__init__(self, engine, nextcont)
-        self._rule = rule
+        ContinuationWithRule.__init__(self, engine, nextcont, rule)
         self.query = query
 
     def activate(self, fcont, heap):
         nextcont = self.nextcont
-        rule = jit.hint(self._rule, promote=True)
+        rule = jit.hint(self.rule, promote=True)
         nextcall = rule.clone_and_unify_head(heap, self.query)
         if nextcall is not None:
-            return self.engine.call(nextcall, self._rule.module, nextcont, fcont, heap)
+            return self.engine.call(nextcall, self.rule, nextcont, fcont, heap)
         else:
             cont = nextcont
         return cont, fcont, heap
 
     def __repr__(self):
-        return "<RuleContinuation rule=%r query=%r>" % (self._rule, self.query)
+        return "<RuleContinuation rule=%r query=%r>" % (self.rule, self.query)
 
 class CutScopeNotifier(Continuation):
     def __init__(self, engine, nextcont, fcont_after_cut):
         return self.nextcont, fcont, heap
 
 
-class CatchingDelimiter(ContinuationWithModule):
-    def __init__(self, engine, module, nextcont, fcont, catcher, recover, heap):
-        ContinuationWithModule.__init__(self, engine, module, nextcont)
+class CatchingDelimiter(ContinuationWithRule):
+    def __init__(self, engine, rule, nextcont, fcont, catcher, recover, heap):
+        ContinuationWithRule.__init__(self, engine, nextcont, rule)
         self.catcher = catcher
         self.recover = recover
         self.fcont = fcont

prolog/interpreter/error.py

+from rpython.rlib import rstring
+
 class PrologError(Exception):
     pass
 
         self.message = message
 
 class TermedError(PrologError):
-    def __init__(self, term):
+    def __init__(self, term, sig_context=None):
         self.term = term
+        self.sig_context = sig_context
 
     def get_errstr(self, engine):
         from prolog.builtin import formatting
-        from prolog.interpreter import term
+        from prolog.interpreter import term, signature
+        errorsig = signature.Signature.getsignature("error", 1)
 
         f = formatting.TermFormatter(engine, quoted=True, max_depth=20)
         f._make_reverse_op_mapping()
 
-        errorterm = self.term.argument_at(0)
+        t = self.term
+        if not isinstance(t, term.Callable) or not t.signature().eq(errorsig):
+            return "Unhandled exception: %s" % (f.format(t), )
+
+        errorterm = t.argument_at(0)
 
         if isinstance(errorterm, term.Callable):
             if errorterm.name() == "instantiation_error":
                 return "arguments not sufficiently instantiated"
-            elif errorterm.name()== "existence_error":
+            elif errorterm.name() == "existence_error":
                 if isinstance(errorterm, term.Callable):
                      return "Undefined %s: %s" % (
                         f.format(errorterm.argument_at(0)),
 
 class CatchableError(TermedError): pass
 class UncaughtError(TermedError):
-    def __init__(self, term, rule_likely_source=None):
-        TermedError.__init__(self, term)
+    def __init__(self, term, sig_context=None, rule_likely_source=None, scont=None):
+        TermedError.__init__(self, term, sig_context)
         self.rule = rule_likely_source
+        self.traceback = _construct_traceback(scont)
+
+    def format_traceback(self, engine):
+        out = ["Traceback (most recent call last):"]
+        self.traceback._format(out)
+        context = self.sig_context.string()
+        if context == "throw/1":
+            context = ""
+        else:
+            context += ": "
+        out.append("%s%s" % (context, self.get_errstr(engine)))
+        return "\n".join(out)
+
+
+class TraceFrame(object):
+    def __init__(self, rule, next=None):
+        self.rule = rule
+        self.next = next
+
+    def __repr__(self):
+        return "TraceFrame(%r, %r)" % (self.rule, self.next)
+
+    def _format(self, out):
+        rule = self.rule
+        if rule.line_range is not None:
+            if rule.line_range[0] + 1 ==  rule.line_range[1]:
+                lines = "line %s " % (rule.line_range[0], )
+            else:
+                lines = "lines %s-%s " % (rule.line_range[0] + 1, rule.line_range[1])
+        else:
+            lines = ""
+        out.append("  File \"%s\" %sin %s:%s" % (
+            rule.file_name, lines,
+            rule.module.name, rule.signature.string()))
+        source = rule.source
+        if source is not None:
+            # poor man's indent
+            out.append("    " + rstring.replace(source, "\n", "\n    "))
+        if self.next is not None:
+            self.next._format(out)
+
+def _construct_traceback(scont):
+    from prolog.interpreter.continuation import ContinuationWithRule
+    if scont is None:
+        return None
+    next = None
+    while not scont.is_done():
+        if isinstance(scont, ContinuationWithRule):
+            next = TraceFrame(scont.rule, next)
+        scont = scont.nextcont
+    return next
 
 def wrap_error(t):
     from prolog.interpreter import term

prolog/interpreter/function.py

 class Rule(object):
     _immutable_ = True
     _immutable_fields_ = ["headargs[*]"]
-    _attrs_ = ['next', 'head', 'headargs', 'contains_cut', 'body', 'size_env', 'signature', 'module']
+    _attrs_ = ['next', 'head', 'headargs', 'contains_cut', 'body', 'size_env', 'signature', 'module', 'file_name', 'line_range', 'source']
     unrolling_attrs = unroll.unrolling_iterable(_attrs_)
     
     def __init__(self, head, body, module, next = None):
             self.body = None
         self.size_env = memo.size()
         self.signature = head.signature()        
-        self._does_contain_cut()
         self.module = module
         self.next = next
+        self.file_name = "<unknown>"
+        self.line_range = None
+        self.source = None
+
+        self._does_contain_cut()
+
+    def _init_source_info(self, tree, source_info):
+        from rpython.rlib.parsing.tree import Nonterminal, Symbol
+        start_source_pos = tree.getsourcepos()
+        end_source_pos = None
+        parts = [tree]
+        while parts:
+            first = parts.pop()
+            if isinstance(first, Nonterminal):
+                parts.extend(first.children)
+            else:
+                assert isinstance(first, Symbol)
+                end_source_pos = first.getsourcepos()
+                break
+        if end_source_pos is None:
+            end_source_pos = start_source_pos
+        self.line_range = [start_source_pos.lineno, end_source_pos.lineno + 1]
+        start = start_source_pos.i
+        stop = end_source_pos.i + 1
+        assert 0 <= start <= stop
+        self.source = source_info[start:stop]
 
     def _does_contain_cut(self):
         if self.body is None:
     def __ne__(self, other):
         return not self == other
 
+def _make_toplevel_rule(module):
+    # this is a rule object that is used for error messages when running
+    # toplevel goals
+    head = Callable.build("<%s toplevel>" % (module.name, ))
+    return Rule(head, None, module)
 
 class Function(object):
     _immutable_fields_ = ["rulechain?", "meta_args?"]

prolog/interpreter/module.py

 import py
 from rpython.rlib import jit
 from prolog.interpreter.signature import Signature
-from prolog.interpreter import error
+from prolog.interpreter import error, term
 from prolog.interpreter.term import Callable, Atom
-from prolog.interpreter.function import Function
+from prolog.interpreter.function import Function, _make_toplevel_rule
 from prolog.interpreter.helper import unwrap_predicate_indicator
 
 class VersionTag(object):
         error.throw_existence_error("procedure",
             errorterm.get_prolog_signature())
 
+    def get_or_make_module(self, name):
+        module = self._get_module(name, self.version)
+        if module is not None:
+            return module
+
+
     @jit.elidable
     def _get_module(self, name, version):
         return self.modules.get(name, None)
         self.nameatom = Atom(name)
         self.functions = {}
         self.exports = []
+        self._toplevel_rule = _make_toplevel_rule(self)
 
     def add_meta_predicate(self, signature, arglist):
         func = self.lookup(signature)

prolog/interpreter/parsing.py

     parser_fact = PrologPackratParser(real_rules, "fact")
     return parser_fact
 
-def _dummyfunc(arg, tree):
+def _dummyfunc(arg, tree, source_string, file_name):
     return parser_fact
 
 def parse_file(s, parser=None, callback=_dummyfunc, arg=None, file_name=None):
     if file_name is None:
         file_name = "<unknown>" # for error messages only
     try:
-        return _parse_file(s, parser, callback, arg)
+        return _parse_file(s, parser, callback, arg, file_name)
     except ParseError, exc:
         message = exc.nice_error_message(file_name, s)
         lineno = exc.source_pos.lineno
     raise error.PrologParseError(file_name, lineno, message)
 
 
-def _parse_file(s, parser, callback, arg):
+def _parse_file(s, parser, callback, arg, file_name):
     tokens = lexer.tokenize(s)
     lines = []
     line = []
     trees = []
     for line in lines:
         tree = parser.parse(line, lazy=False)
-        if callback is not None:
-            # XXX ugh
-            parser = callback(arg, tree)
-            if parser is None:
-                parser = parser_fact
+        callback(arg, tree, s, file_name)
         trees.append(tree)
     return trees
 

prolog/interpreter/test/test_builtin.py

 def test_repeat():
     assert_true("repeat, true.")
     e = Engine()
-    py.test.raises(UnificationFailed,
-        e.run, parse_query_term("repeat, !, fail."), 
-                e.modulewrapper.user_module)
+    assert_false("repeat, !, fail.")
     # hard to test repeat differently
     e = get_engine('f :- repeat, !, fail.')
     assert_false('f.', e)

prolog/interpreter/test/test_console_output.py

         child.expect(re.escape("X = a b c."))
         child.expect(re.escape("      ^"))
         child.expect(re.escape("ParseError: expected ."))
+
+    def test_traceback(self):
+        child = self.spawn([])
+        child.expect("welcome!")
+        child.expect(">?- ")
+        child.sendline("assert((f(X) :- X is X)).")
+        child.expect("yes")
+        child.sendline("f(X).")
+        child.expect(re.escape("ERROR:"))
+        child.expect(re.escape("Traceback (most recent call last):"))
+        child.expect(re.escape('  File "<unknown>" in user:f/1'))
+        child.expect(re.escape("arguments not sufficiently instantiated"))
+

prolog/interpreter/test/test_continuation.py

         g(X, Y) :- X > 0, !, Y = a.
         g(_, b).
     """)
-    e.run(parse_query_term("g(-1, Y), Y == b, g(1, Z), Z == a."), 
-            e.modulewrapper.user_module, CheckContinuation())
+    e.run_query_in_current(parse_query_term("g(-1, Y), Y == b, g(1, Z), Z == a."), 
+                           CheckContinuation())
 
 # ___________________________________________________________________
 # integration tests
     e = get_engine("""
         f(a).
     """)
-    m = e.modulewrapper
     t, vars = get_query_and_vars("f(X).")
-    e.run(t, m.user_module)
+    e.run_query_in_current(t)
     assert vars['X'].dereference(None).name()== "a"
 
 def test_and():
         g(b, c).
         f(X, Z) :- g(X, Y), g(Y, Z).
     """)
-    m = e.modulewrapper
-    e.run(parse_query_term("f(a, c)."), m.user_module)
+    e.run_query_in_current(parse_query_term("f(a, c)."))
     t, vars = get_query_and_vars("f(X, c).")
-    e.run(t, m.user_module)
+    e.run_query_in_current(t)
     assert vars['X'].dereference(None).name()== "a"
 
 def test_and_long():
         factorial(0, succ(0)).
         factorial(succ(X), Y) :- factorial(X, Z), mul(Z, succ(X), Y).
     """)
-    m = e.modulewrapper
     def nstr(n):
         if n == 0:
             return "0"
         return "succ(%s)" % nstr(n - 1)
-    e.run(parse_query_term("num(0)."), m.user_module)
-    e.run(parse_query_term("num(succ(0))."), m.user_module)
+    e.run_query_in_current(parse_query_term("num(0)."))
+    e.run_query_in_current(parse_query_term("num(succ(0))."))
     t, vars = get_query_and_vars("num(X).")
-    e.run(t, m.user_module)
+    e.run_query_in_current(t)
     assert vars['X'].dereference(None).num == 0
-    e.run(parse_query_term("add(0, 0, 0)."), m.user_module)
-    py.test.raises(UnificationFailed, e.run, parse_query_term("""
-        add(0, 0, succ(0))."""), m.user_module)
-    e.run(parse_query_term("add(succ(0), succ(0), succ(succ(0)))."), m.user_module)
-    e.run(parse_query_term("mul(succ(0), 0, 0)."), m.user_module)
-    e.run(parse_query_term("mul(succ(succ(0)), succ(0), succ(succ(0)))."), m.user_module)
-    e.run(parse_query_term("mul(succ(succ(0)), succ(succ(0)), succ(succ(succ(succ(0)))))."), m.user_module)
-    e.run(parse_query_term("factorial(0, succ(0))."), m.user_module)
-    e.run(parse_query_term("factorial(succ(0), succ(0))."), m.user_module)
-    e.run(parse_query_term("factorial(%s, %s)." % (nstr(5), nstr(120))), m.user_module)
+    e.run_query_in_current(parse_query_term("add(0, 0, 0)."))
+    py.test.raises(UnificationFailed, e.run_query_in_current, parse_query_term("""
+        add(0, 0, succ(0))."""))
+    e.run_query_in_current(parse_query_term("add(succ(0), succ(0), succ(succ(0)))."))
+    e.run_query_in_current(parse_query_term("mul(succ(0), 0, 0)."))
+    e.run_query_in_current(parse_query_term("mul(succ(succ(0)), succ(0), succ(succ(0)))."))
+    e.run_query_in_current(parse_query_term("mul(succ(succ(0)), succ(succ(0)), succ(succ(succ(succ(0)))))."))
+    e.run_query_in_current(parse_query_term("factorial(0, succ(0))."))
+    e.run_query_in_current(parse_query_term("factorial(succ(0), succ(0))."))
+    e.run_query_in_current(parse_query_term("factorial(%s, %s)." % (nstr(5), nstr(120))))
 
 def test_or_backtrack():
     e = get_engine("""
         f(X, Y, Z) :- (g(X, Z); g(X, Z); g(Z, Y)), a(Z).
         """)
     t, vars = get_query_and_vars("f(a, b, Z).")
-    e.run(t, e.modulewrapper.user_module)
+    e.run_query_in_current(t)
     assert vars['Z'].dereference(None).name()== "a"
     f = collect_all(e, "X = 1; X = 2.")
     assert len(f) == 2
         append([],L,L).
         append([X|Y],L,[X|Z]) :- append(Y,L,Z).
     """)
-    e.run(parse_query_term("append(%s, %s, X)." % (range(30), range(10))),
-            e.modulewrapper.user_module)
-    return
-    e.run(parse_query_term("nrev(%s, X)." % (range(15), )))
-    e.run(parse_query_term("nrev(%s, %s)." % (range(8), range(7, -1, -1))))
+    e.run_query_in_current(parse_query_term("append(%s, %s, X)." % (range(30), range(10))))
+    e.run_query_in_current(parse_query_term("nrev(%s, X)." % (range(15), )))
+    e.run_query_in_current(parse_query_term("nrev(%s, %s)." % (range(8), range(7, -1, -1))))
 
 def test_indexing():
     # this test is quite a lot faster if indexing works properly. hrmrm
                                 for i in range(97, 122)]))
     t = parse_query_term("f(x, g(y)).")
     for i in range(200):
-        e.run(t, e.modulewrapper.user_module)
+        e.run_query_in_current(t)
     t = parse_query_term("f(x, g(y, a)).")
     for i in range(200):
-        py.test.raises(UnificationFailed, e.run, t, e.modulewrapper.user_module)
+        py.test.raises(UnificationFailed, e.run_query_in_current, t)
 
 def test_indexing2():
     e = get_engine("""

prolog/interpreter/test/test_error.py

 from prolog.interpreter.parsing import get_engine
 from prolog.interpreter.parsing import get_query_and_vars
 from prolog.interpreter.error import UncaughtError
+from prolog.interpreter.signature import Signature
+
+def get_uncaught_error(query, e):
+    if isinstance(query, str):
+        (query, _) = get_query_and_vars(query)
+    return pytest.raises(UncaughtError, e.run_query_in_current, query).value
+
 
 def test_errstr():
     e = get_engine("""
         f(X) :- drumandbass(X).
     """)
-    (t, vs) = get_query_and_vars("f(X).")
+    error = get_uncaught_error("f(X).", e)
+    assert error.get_errstr(e) == "Undefined procedure: drumandbass/1"
 
-    m = e.modulewrapper
-
-    info = pytest.raises(UncaughtError, e.run, t, m.user_module)
-    assert info.value.get_errstr(e) == "Undefined procedure: drumandbass/1"
+def test_errstr_user():
+    e = get_engine("""
+        f(X) :- throw(foo).
+    """)
+    error = get_uncaught_error("f(X).", e)
+    assert error.get_errstr(e) == "Unhandled exception: foo"
 
 def test_exception_knows_rule():
     e = get_engine("""
     m = e.modulewrapper
     sig = t.argument_at(0).signature()
     rule = m.user_module.lookup(sig).rulechain.next
+    error = get_uncaught_error(t, e)
+    assert error.rule is rule
 
-    info = pytest.raises(UncaughtError, e.run, t, m.user_module)
-    assert info.value.rule is rule
+def test_exception_knows_rule_toplevel():
+    # toplevel rule
+    e = get_engine("")
+    m = e.modulewrapper
+    error = get_uncaught_error("drumandbass(X).", e)
+    assert error.rule is m.current_module._toplevel_rule
 
+def test_exception_knows_rule_change_back_to_earlier_rule():
+    e = get_engine("""
+        g(a).
+        f(X) :- g(X), drumandbass(X).
+    """)
+    (t, vs) = get_query_and_vars("f(X).")
+
+    m = e.modulewrapper
+    sig = t.signature()
+    rule = m.user_module.lookup(sig).rulechain
+
+    error = get_uncaught_error(t, e)
+    assert error.rule is rule
+
+def test_exception_knows_builtin_signature():
+    e = get_engine("""
+        f(X, Y) :- atom_length(X, Y).
+    """)
+    error = get_uncaught_error("f(1, Y).", e)
+    assert error.sig_context == Signature.getsignature("atom_length", 2)
+
+def test_traceback():
+    e = get_engine("""
+        h(y).
+        g(a).
+        g(_) :- throw(foo).
+        f(X, Y) :- g(X), h(Y).
+    """)
+    error = get_uncaught_error("f(1, Y).", e)
+    sig_g = Signature.getsignature("g", 1)
+    sig_f = Signature.getsignature("f", 2)
+    m = e.modulewrapper
+    rule_f = m.user_module.lookup(sig_f).rulechain
+    rule_g = m.user_module.lookup(sig_g).rulechain.next
+    tb = error.traceback
+    assert tb.rule is rule_f
+    assert tb.next.rule is rule_g
+    assert tb.next.next is None
+
+@pytest.mark.xfail
+def test_traceback_in_if():
+    e = get_engine("""
+        h(y).
+        g(a).
+        g(_) :- throw(foo).
+        f(X, Y) :- (g(X) -> X = 1 ; X = 2), h(Y).
+    """)
+    error = get_uncaught_error("f(1, Y).", e)
+    sig_g = Signature.getsignature("g", 1)
+    sig_f = Signature.getsignature("f", 2)
+    m = e.modulewrapper
+    rule_f = m.user_module.lookup(sig_f).rulechain
+    rule_g = m.user_module.lookup(sig_g).rulechain.next
+    tb = error.traceback
+    assert tb.rule is rule_f
+    assert tb.next.rule is rule_g
+
+def test_traceback_print():
+    e = get_engine("""
+h(y).
+g(a).
+g(_) :- throw(foo).
+f(X, Y) :-
+    g(X),
+    h(Y).
+:- assert((h :- g(b), true)).
+    """)
+    error = get_uncaught_error("f(1, Y).", e)
+    s = error.format_traceback(e)
+    assert s == """\
+Traceback (most recent call last):
+  File "<unknown>" lines 5-7 in user:f/2
+    f(X, Y) :-
+        g(X),
+        h(Y).
+  File "<unknown>" line 3 in user:g/1
+    g(_) :- throw(foo).
+Unhandled exception: foo"""
+
+    error = get_uncaught_error("h.", e)
+    s = error.format_traceback(e)
+    assert s == """\
+Traceback (most recent call last):
+  File "<unknown>" in user:h/0
+  File "<unknown>" line 3 in user:g/1
+    g(_) :- throw(foo).
+Unhandled exception: foo"""
+
+def test_traceback_print_builtin():
+    e = get_engine("""
+h(y).
+g(a).
+g(_) :- _ is _.
+f(X, Y) :-
+    g(X),
+    h(Y).
+:- assert((h :- g(b), true)).
+    """)
+    error = get_uncaught_error("f(1, Y).", e)
+    s = error.format_traceback(e)
+    assert s == """\
+Traceback (most recent call last):
+  File "<unknown>" lines 5-7 in user:f/2
+    f(X, Y) :-
+        g(X),
+        h(Y).
+  File "<unknown>" line 3 in user:g/1
+    g(_) :- _ is _.
+is/2: arguments not sufficiently instantiated"""
+

prolog/interpreter/test/test_function.py

 from prolog.interpreter.term import Callable
 from prolog.interpreter.signature import Signature
 from prolog.interpreter.continuation import Engine
+from prolog.interpreter.test.tool import get_engine
 
 class C(Callable):
     def __init__(self, name):
     f.add_rule(r4, True)
     assert get_rules(rulechain) == [(C(0), C(0)), (C(1), C(2)), (C(2), C(3))]
     assert get_rules(f.rulechain) == [(C(0), C(0)), (C(1), C(2)), (C(2), C(3)), (C(15), C(-1))]
+
+def test_source_range():
+    e = get_engine("""
+f(a) :- a.
+f(b) :-
+    b.
+a.
+b.
+""")
+    func = e.modulewrapper.current_module.lookup(Signature.getsignature("f", 1))
+    assert func.rulechain.line_range == [1, 2]
+    assert func.rulechain.next.line_range == [2, 4]
+    assert func.rulechain.file_name == "<unknown>"
+    assert func.rulechain.source == "f(a) :- a."
+    assert func.rulechain.next.source == "f(b) :-\n    b."
+

prolog/interpreter/test/test_parsing.py

     assert m.modules["user"].lookup(Signature.getsignature("add_numeral", 3)).rulechain.head.argument_at(1).name() == "null"
     four = Callable.build("succ", [Callable.build("succ", [Callable.build("succ",
                 [Callable.build("succ", [Callable.build("null")])])])])
-    e.run(parse_query_term("numeral(succ(succ(null)))."), m.user_module)
+    e.run_query_in_current(parse_query_term("numeral(succ(succ(null)))."))
     term = parse_query_term(
         """add_numeral(succ(succ(null)), succ(succ(null)), X).""")
-    e.run(term, m.user_module)
+    e.run_query_in_current(term)
     hp = Heap()
     var = BindingVar().dereference(hp)
     # does not raise
     var.unify(four, hp)
     term = parse_query_term(
         """greater_than(succ(succ(succ(null))), succ(succ(null))).""")
-    e.run(term, m.user_module)
+    e.run_query_in_current(term)
 
 def test_quoted_atoms():
     t = parse_file("""

prolog/interpreter/test/test_unification.py

     hp = Heap()
     X = hp.newvar()
     c3 = Callable.build("f", [Callable.build("b"), X])
-    e.run(c3, e.modulewrapper.user_module)
+    e.run_query_in_current(c3)
     assert X.dereference(hp).name()== "b"
     query = Callable.build("f", [Callable.build("b"), Callable.build("a")]) 
-    e.run(query, e.modulewrapper.user_module)
+    e.run_query_in_current(query)
 
 
 def test_quick_unify_check():

prolog/interpreter/test/tool.py

         e = Engine()
     terms, vars = e.parse(query)
     term, = terms
-    e.run(term, e.modulewrapper.current_module)
+    e.run_query_in_current(term)
     return dict([(name, var.dereference(None))
                      for name, var in vars.iteritems()])
 
     if e is None:
         e = Engine()
     term = e.parse(query)[0][0]
-    py.test.raises(UnificationFailed, e.run, term, e.modulewrapper.current_module)
+    py.test.raises(UnificationFailed, e.run_query_in_current, term)
 
 def prolog_raises(exc, query, e=None):
     prolog_catch = "catch(((%s), fail), error(%s), true)." % (query, exc)
     terms, vars = engine.parse(s)
     term, = terms
     collector = CollectAllContinuation(engine.modulewrapper.user_module, vars)
-    py.test.raises(UnificationFailed, engine.run, term,
+    py.test.raises(UnificationFailed, engine.run_query, term,
             engine.modulewrapper.current_module, collector)
     return collector.heaps
 

prolog/interpreter/translatedmain.py

     try:
         if query is None:
             return
-        engine.run(query, engine.modulewrapper.current_module, 
+        engine.run_query_in_current(
+                query,
                 ContinueContinuation(engine, var_to_pos, printmessage))
     except error.UnificationFailed:
         printmessage("Nein\n")
-    except (error.UncaughtError, error.CatchableError), e:
+    except error.UncaughtError, e:
+        printmessage("ERROR:\n%s\n" % e.format_traceback(engine))
+    except error.CatchableError, e:
         printmessage("ERROR: %s\n" % e.get_errstr(engine))
     except error.PrologParseError, exc:
         printmessage(exc.message + "\n")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.