sasagawa888 / eisl

ISLisp interpreter/compiler
Other
281 stars 23 forks source link

Bug: Unterminated s-expression triggers abort or resource error #208

Closed wasamasa closed 2 years ago

wasamasa commented 2 years ago
(defun open-ended ()
$ eisl -r -l bug.lsp                                              (git)-[master]-
around here line=1 column=0           
Illegal right parenthesis at READ file end 
debug mode ?(help)
>>
Uncaught exception Exit interpreter raised at main.c:427
aborting...
zsh: IOT instruction (core dumped)  eisl -r -l bug.lsp
$ eisl -r -s bug.lsp                                              (git)-[master]-
around here line=1 column=0                                                         
Illegal right parenthesis at READ file end 
[...]
Illegal right parenthesis at READ file end
resource error M&S FRESHCELL NIL
eisl.h:296: I(res != ((void *)0)) failed; dumping core
zsh: IOT instruction (core dumped)  eisl -r -s bug.lsp

One observation I've made so far is that the behavior of eisl when using the -s or -l switches is far less stable than when used interactively. Is there a reason for this?

poldy commented 2 years ago

For your first failure, and I think the ultimate reason why -s and -l are giving trouble, is that the exception handlers aren't installed until after command-line args (like these) are handled. You can see this in the structure of the main function which is something like

main() {
  handle_cmd_line_opts();
  do {
    TRY {
      while (1) {
        print(eval(read()));
      }
    }
    EXCEPT (Restart_Repl); /* Reinit the REPL */
    EXCEPT (Exit_Interp) quit = true; /* Exit loop */
  } while (!quit);
}

(TRY and EXCEPT are defined by https://github.com/drh/cii , and documented in the "Except" section of http://cii.s3.amazonaws.com/book/pdf/quickref.pdf . They're just a readable wrapper around setjmp/longjmp.)

-l and -s all are processed in the handle_cmd_line_opts pseudocode. One way to fix these type of issues once-and-for-all would be to introduce an outside exception handler?

main() {
  TRY {
    handle_cmd_line_opts();
    do {
      TRY {
        while (1) {
          print(eval(read()));
        }
      }
      EXCEPT (Restart_Repl); /* Reinit the REPL */
      EXCEPT (Exit_Interp) quit = true; /* Exit loop */
    } while (!quit);
  }
  EXCEPT(...); /* Don't try to recover, exit cleanly */
}

The second failure isn't so clear to me, it seems to be an infinite recursion calling readlist() in main.c, until we run out of resources.

sasagawa888 commented 2 years ago

Improved. Please test.

poldy commented 2 years ago

Looks much better to me now. I get clean exit for both previously-failing test cases.

It's only a minor quibble, but returning 0 from main, or as an argument to exit(), usually means "no problem". 1 could be returned for "generic error" (EXIT_FAILURE in ANSI C stdlib.h) or BSD & macOS have a file /usr/include/sysexits.h with better codes, if Linux also has this they could be used instead.

sasagawa888 commented 2 years ago

Thank you. I improved it.

wasamasa commented 2 years ago

Thanks, works now.