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]

Better error handling for guile



A lot of people have requested better error messages from guile when
not in the main repl (such as when invoking callbacks from C code, or
when running guile non-interactively using the #! mechanism). 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; as well as a simple test driver program for it. I
want to use this code mainly for `scwm', but 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. Optimally, I'd like to get this in
for the next release.

The reason I am writing to the list is that I have run into the
limitations of my understanding og the debugging code. I have appended
my test program below, and I have here a list of questions and issues
to be resolved that I'd really appreicate help on from someone with a
better understanding of the debugging code:

* 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.

* 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.

* 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'm not asking for anyone to write code on this for me, but just
suggest where to go from here, i.e. suggest specific things to look at
or try. I suspect I will have to write my own scm_internal_start_stack
or something to fix problems 3 and 4, but I am not sure what to do
about 1 and 2, the way the source properties are created in the first
place and then used to get the file and position is very unclear to
me. Here's the test program I have so far, it reads expressions from
standard input and prints the results, including useful error messages
if any:

 - Maciej Stachowiak


--------------


#include <guile/gh.h>


SCM display_error;
SCM sym_start_stack;
SCM sym_backtrace;
SCM quote_eval_stack;

typedef void (*main_prog_t) (int argc, char **argv);

static void 
test_gh_launch_pad (void *closure, int argc, char **argv)
{
  main_prog_t c_main_prog = (main_prog_t) closure;

  gh_eval_str ("(primitive-load-path \"ice-9/boot-9.scm\")");
  c_main_prog (argc, argv);
  exit (0);
}

static void 
test_gh_enter (int argc, char *argv[], main_prog_t c_main_prog)
{
  scm_boot_guile (argc, argv, test_gh_launch_pad, (void *) c_main_prog);
  /* never returns */
}

int real_main(int argc, char **argv);

int
main(int argc, char **argv)
{
  test_gh_enter(argc, argv, (main_prog_t) real_main);
  return 0;
}


/* Real error handler - retrives the stack, displays the backtrace if
   appropriate, */

SCM test_handle_error (void *handler_data, SCM tag, SCM throw_args)
{
  SCM cep = scm_current_error_port();
  SCM last_stack= scm_fluid_ref(SCM_CDR(scm_the_last_stack_fluid));

#if 0
  gh_display(gh_str02scm("the-last-stack: "));
  gh_display(last_stack);
  gh_newline();
#endif  

  if (SCM_NIMP (last_stack) && SCM_STACKP (last_stack) &&
      (SCM_BOOL_T==gh_memq(sym_backtrace,
			   scm_debug_options(SCM_UNDEFINED)))) {
    scm_newline(cep);
    scm_display_backtrace (last_stack, cep, SCM_UNDEFINED, SCM_UNDEFINED);
    scm_newline(cep);
  }
  
  gh_apply(display_error, gh_cons(last_stack, 
				  gh_cons(cep, throw_args))); 
  
  scm_display (gh_str02scm("ABORT: "), cep);
  scm_write (gh_cons(tag, SCM_EOL), cep);
  scm_newline(cep);
  
  /* scm_handle_by_message_noexit(handler_data, tag, throw_args); */
  return SCM_BOOL_F;
}


static SCM
test_inner_body_eval (void *body_data)
{
  SCM expr = *(SCM *) body_data;

  return scm_m_start_stack
    (gh_list(sym_start_stack, 
	     quote_eval_stack,
	     expr, 
	     SCM_UNDEFINED),
     scm_top_level_env 
     (SCM_CDR (scm_top_level_lookup_closure_var)));
}

#define RELOC_FRAME(ptr, offset) \
  ((scm_debug_frame *) ((SCM_STACKITEM *) (ptr) + (offset)))


/* the lazy catch handler, which just saves the stack and
   re-throws. */

static SCM
test_save_stack (void *handler_data, SCM tag, SCM throw_args)
{
  scm_fluid_set_x(SCM_CDR(scm_the_last_stack_fluid), 
		  scm_make_stack(gh_list(SCM_BOOL_T, 
					 SCM_UNDEFINED)));

  return scm_throw(tag, throw_args);
}

static SCM
test_outer_body_eval (void *body_data)
{
  SCM default_lazy_handler;
  return scm_internal_lazy_catch(SCM_BOOL_T, test_inner_body_eval, body_data,
				 test_save_stack, NULL);
}

inline static SCM 
test_catching_eval (SCM expr) {
  return scm_internal_catch (SCM_BOOL_T, test_outer_body_eval, &expr,
			     test_handle_error, "scwm");
}


int
real_main(int argc, char **argv)
{
  
  display_error=scm_permanent_object(gh_lookup("display-error"));
  sym_start_stack=scm_permanent_object(gh_symbol2scm("start-stack"));
  sym_backtrace=scm_permanent_object(gh_symbol2scm("backtrace"));
  quote_eval_stack=
    scm_permanet_object(gh_list(gh_symbol2scm("quote"),
				gh_symbol2scm("eval-stack"), SCM_UNDEFINED));
  while (1) {
    SCM exp;
    SCM result;
    exp = scm_read(SCM_UNDEFINED);
    gh_newline();
    gh_display(gh_str02scm("Expression: "));
    gh_write(exp);
    gh_newline();
    result=test_catching_eval (exp);
    gh_display(gh_str02scm("Result: "));
    gh_write(result);
    gh_newline();
  }
  return(0);
}