This is the mail archive of the guile@cygnus.com mailing list for the guile project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Better error handling for guile


Maciej Stachowiak <mstachow@mit.edu> writes:

> I have started on some code to allow this; specifically, so far I
> have written a version of scm_eval which will print the same nicely
> formatted messages in case of error that the Guile repl would, and
> allow backtraces;

I don't understand what you want to do.  I don't think that `eval' is
the right place for error reporting code.  It seems more reasonable
that this is associated with the repl loop or the error handler of the
application.

Could you please clarify?

> I think versions of gh_eval_str, gh_apply and gh_call[0-3] that do
> nice error handling would be a good addition to Guile.

If the user has installed an error handler the handler can take care
of error reporting for all of the above functions.

> Optimally, I'd like to get this in for the next release.

We're always open for including new code if we feel that it's a good
solution and in line with future Guile development.  Sometimes,
however, knowing that this is the case requires thinking, which
requires time, which is not always immediately available.

> * Top-level expressions which are not lists do not have source
> properties. This means that if I have a top-level expression like:
> 
> some-silly-variable-reference
> 
> in a file and I then load that file, the error message will be
> prefixed with "ERROR:" instead of the filename and line and column
> numbers, as would be the case for any other error in a file.

As you point out, the reason is that source properties only are
attached to pairs in the source code.

Since this is a limitation in the basic source information code, I
suggest that you simply ignore the problem until (and if) someone
solves it on the basic level.

Actually, I'm not sure that this is an important limitation, since
top-level expressions which aren't lists aren't very interesting.  (On
the other hand, random characters mistakenly put into the source file
could make it hard to load, and the user might want to easily locate
the debris.)

The purpose of source properties is to store source code information
for later use when the error occurs during evaluation of procedures.
Since the top-level-non-cons-problem occurs during loading, the right
solution would perhaps be to take care of it in the loading code.

> * On the other hand, the input file property appears to be there and
> is used for the standard input, so you get errors prefixed with
> "standard input:0:0:" instead of "ERROR:" for non-atomic expressions.

While this may not be the nicest form of information to the user, I
don't think it is wrong that the source properties tell the accurate
origin of the source.  The right solution is probably to handle this
in the display routines (printing just ERROR when the file name is
"standard input").

> * It's currently necessary to cons a list for each expression
> evaluated to make scm_m_start_stack happy. This would surely slow
> things down in batch mode.
> 
> * Using scm_m_start_stack does not seem to generalize easily to doing
> a call instead of an eval, e.g. gh_call0().

I've now separated the guts of scm_m_start_stack (which was never
meant to be used from C) into a function:

  SCM scm_start_stack (SCM id, SCM exp, SCM env)

BTW, have you noticed the function scm_internal_stack_catch?  That's
the function to use when writing an application error handler.

Here's how to do it:

------- Start of forwarded message -------
To: russell.mcmanus@gs.com
Cc: guile@cygnus.com
Subject: Re: generating a backtrace from a custom error handler.
References: <199708122232.SAA06339@nytrdc058.eq.gs.com>
Cc: mdj@nada.kth.se
From: Mikael Djurfeldt <mdj@nada.kth.se>
Date: 13 Aug 1997 16:57:02 +0200
Message-ID: <xy7oh72uo5d.fsf@mdj.nada.kth.se>

Russ McManus <mcmanr@eq.gs.com> writes:

> i have a sizeable program that uses guile as an extension
> language.  the program frequently 'calls out' to scheme, which
> is where the lion's share of the work happens.
> 
> i am trying to set up the program so that i can catch scheme
> errors and display them in the user interface, and -also- get a 
> backtrace of where the error occurred.
> 
> after an hour of reading through ice-9/boot9.scm, ice-9/debug.scm,
> backtrace.c, and stacks.c, the answer still eludes me.  perhaps
> someone can point me in the right direction?

You can use the functions scm_display_backtrace and scm_display_error
to display backtraces and errors.  These functions take a stack object
as their first argument.

The stack object represents the state of the stack when the error
occurred.  It is normally created by a call to `make-stack' and stored
in `the-last-stack' by the error handler in boot-9.scm.

If you want to be able to display backtraces from C code you have to
setup an error handler which captures the stack.  This is not trivial
since you cannot "pop" the stack before calling scm_make_stack.  This
means that should use a combination of a `catch' and a
`lazy-catch'-handler.  This is implemented by
scm_internal_stack_catch.

*** Unfortunately, this function was declared "static" and had another
*** name in all previous snapshots.  I've now corrected this, but you
*** have to download the snapshot from tomorrow in order to access
*** scm_internal_stack_catch.

Below follows an example program which handles error from C.

By using soft ports or string ports you can handle the output of
scm_display_error and scm_display_backtrace any way you want.

Best regards,
/mdj

----------------------------------------------------------------------
/* Compile with:

   gcc myrepl.c -o myrepl -lguile -lqt -lreadline -lcurses

 */

#include <guile/gh.h>
#include <readline/readline.h>

/**********************************************************************
 *
 * Small example repl which handles errors.  Uses readline.
 *
 **********************************************************************/

SCM 
my_handler (void *data, SCM tag, SCM throw_args)
{
  SCM port = scm_def_errp;

  if (scm_ilength (throw_args) >= 3)
    {
      SCM stack = SCM_CDR (scm_the_last_stack_var);
      SCM subr = SCM_CAR (throw_args);
      SCM message = SCM_CADR (throw_args);
      SCM args = SCM_CADDR (throw_args);

      scm_newline (port);
      scm_display_backtrace (stack, port, SCM_UNDEFINED, SCM_UNDEFINED);
      scm_newline (port);
      scm_display_error (stack, port, subr, message, args, SCM_EOL);
      return SCM_BOOL_F;
    }
  else
    {
      scm_gen_puts (scm_regular_string, "uncaught throw to ", port);
      scm_prin1 (tag, port, 0);
      scm_gen_puts (scm_regular_string, ": ", port);
      scm_prin1 (throw_args, port, 1);
      scm_gen_putc ('\n', port);
      exit (2);
    }
}

static char *line = (char *)NULL;

static SCM
eval_str_wrapper (void *data, SCM jmpbuf)
{
  return scm_eval_0str (data);
}

static void
inner_main (int argc, char **argv)
{
  SCM x;
  while (1)
    {
      line = readline ("> ");
      x = scm_internal_stack_catch (SCM_BOOL_T,
				    (scm_catch_body_t) eval_str_wrapper,
				    line,
				    (scm_catch_handler_t) my_handler,
				    line);
      gh_write (x);
      gh_newline ();
    }
}

int
main (int argc, char **argv)
{
  gh_enter (argc, argv, inner_main);
  return 0; /* never reached */
}

------- End of forwarded message -------