Version française
Home     About     Download     Resources     Contact us    
Browse thread
Re: [Caml-list] Error Reporting
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: David McClain <barabh@q...>
Subject: Re: [Caml-list] Error Reporting
Thanks for the suggestions everybody...

I need to spend some time studying what Neel sent along. But it seems to me
that we have some misunderstandings between us about what I mean by CPS.

In a tail call, and all continuations are tail calls, there is no record of
past history -- hence no trail of continuation frames. I can easily report
the location of the present error, but there is no history to report.

but let me study what Neel sent along before I go off the wall any
further....

Cheers,

- DM

----- Original Message -----
From: "Krishnaswami, Neel" <neelk@cswcasa.com>
To: <caml-list@inria.fr>
Sent: Thursday, September 27, 2001 6:32 AM
Subject: Re: [Caml-list] Error Reporting


> David McClain [mailto:barabh@qwest.net] wrote:
> >
> > I am looking for some ideas on handling program error reporting for
> > a compiler that is built largely on CPS. Since there are no stack
> > frames to crawl for traceback, I have a first cut based on keeping a
> > finite length queue of last visited closures. But this becomes
> > problematic when there are circularities in the code, i.e.,
> > recursion. Also, I think I have way too much error tracking going on
> > at each incremental execution step and so runtime performance is cut
> > back compared to what it could be.
>
> Since you're using CPS, it seems like you have most of the infrastructure
> you need already. If you track source locations from parsing down to code
> generation, you can place a source location into each frame as you
allocate
> it. Then, when you have an error, you can simply walk back down the
> contination, emitting the source location of each frame. Since the
> continuation represents the rest of the computation, you automatically get
> the exact execution context of the erroneous statement.
>
> The overhead for this is one word per frame, plus initializing it
> at each continuation allocation. This is probably acceptable unless
> you are writing a really high-performance compiler. Also, tail-call
> optimization will make the traceback only approximate, but this is
> IME not a big deal in practice.
>
> Below is a small CPS-style lambda calculus interpreter that uses this
> idea to generate tracebacks of its evaluation. It's a treecode
> interpreter, but the ideas ought to carry over directly to bytecode too.
>
> -*-*-*-
>
> type var = string
> type pos = int
>
> (* Expressions carry the source location with them *)
>
> type expr =
>   | Lit of pos * int
>   | Var of pos * var
>   | Fun of pos * var * expr
>   | App of pos * expr * expr
>
> (*
>    Continuation frames have the position as an extra argument, so that
>    the continuation as a whole contains the backtrace info. Primitive
>    functions take the continuation as an extra argument so that they
>    can generate a backtrace on an error.
> *)
>
> type denot =
>   | Int of int
>   | Closure of env * var * expr
>   | Primop of (denot -> cont -> denot)
> and frame =
>   | KFun of pos * env * expr
>   | KArg of pos * env * denot
> and env = (var * denot) list
> and cont = frame list
>
> (*
>    Here are the guts of the machinery to generate a backtrace. We
>    need to extract a position from a frame and then just map over
>    the whole continuation to get the backtrace.
> *)
>
> exception Eval_error of string * pos list
>
> let framepos frame =
>   match frame with
>   | KFun(pos, _, _) -> pos
>   | KArg(pos, _, _) -> pos
>
> let backtrace k = List.map framepos k
>
> (*
>   We augment variable lookup so that failed lookups generate backtraces.
>   If you check statically for unbound variables this is unneccesary.
> *)
>
> let bind v d r = (v,d) :: r
>
> let lookup v r k =
>   try
>     List.assoc v r
>   with
>     Not_found -> raise (Eval_error("Unbound variable " ^ v, backtrace k))
>
>
> (*
>    In the interpreter proper, the only place where a backtrace is
>    generated is when an argument is applied to a non-function. If an
>    if-statement is added, then that should be too. Likewise, if
>    exceptions are added then an uncaught exception should generate
>    one also.
> *)
>
> let rec eval e r k =
>   match e with
>   | Lit(pos, n) ->
>       next (Int n) k
>   | Var(pos, v) ->
>       next (lookup v r k) k
>   | Fun(pos, formal, body) ->
>       next (Closure(r, formal, body)) k
>   | App(pos, f, arg) ->
>       eval f r (KFun(pos, r, arg) :: k)
> and next d k =
>   match k with
>   | [] -> d
>   | KFun(pos, r, arg) :: k' ->
>       eval arg r k'
>   | KArg(pos, r, func) :: k' ->
>       (match func with
>       | Int _ ->
>           raise (Eval_error("Not a function!", backtrace k))
>       | Closure(r, formal, body) ->
>           eval body (bind formal d r) k'
>       | Primop f ->
>           next (f d k) k')
> --
> Neel Krishnaswami
> neelk@cswcasa.com
> -------------------
> Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ:
http://caml.inria.fr/FAQ/
> To unsubscribe, mail caml-list-request@inria.fr  Archives:
http://caml.inria.fr
-------------------
Bug reports: http://caml.inria.fr/bin/caml-bugs  FAQ: http://caml.inria.fr/FAQ/
To unsubscribe, mail caml-list-request@inria.fr  Archives: http://caml.inria.fr