diff --git a/Makefile.in b/Makefile.in index ae2c253..6c1fdd0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -51,12 +51,12 @@ LIBS = @READLINE_LIBS@ @LIBS@ $(ADDLIBS) HFILES = config.h es.h gc.h input.h prim.h print.h sigmsgs.h \ stdenv.h syntax.h term.h token.h var.h CFILES = access.c closure.c conv.c dict.c eval.c except.c fd.c gc.c glob.c \ - glom.c input.c heredoc.c history.c list.c main.c match.c open.c opt.c \ + glom.c input.c heredoc.c list.c main.c match.c open.c opt.c \ prim-ctl.c prim-etc.c prim-io.c prim-sys.c prim.c print.c proc.c \ readline.c sigmsgs.c signal.c split.c status.c str.c syntax.c term.c \ token.c tree.c util.c var.c vec.c version.c y.tab.c dump.c OFILES = access.o closure.o conv.o dict.o eval.o except.o fd.o gc.o glob.o \ - glom.o input.o heredoc.o history.o list.o main.o match.o open.o opt.o \ + glom.o input.o heredoc.o list.o main.o match.o open.o opt.o \ prim-ctl.o prim-etc.o prim-io.o prim-sys.o prim.o print.o proc.o \ readline.o sigmsgs.o signal.o split.o status.o str.o syntax.o term.o \ token.o tree.o util.o var.o vec.o version.o y.tab.o @@ -134,7 +134,6 @@ glob.o : glob.c es.h config.h stdenv.h gc.h glom.o : glom.c es.h config.h stdenv.h gc.h input.o : input.c es.h config.h stdenv.h input.h token.h heredoc.o : heredoc.c es.h config.h stdenv.h gc.h input.h syntax.h token.h -history.o : history.c es.h config.h stdenv.h gc.h input.h token.h list.o : list.c es.h config.h stdenv.h gc.h main.o : main.c es.h config.h stdenv.h match.o : match.c es.h config.h stdenv.h diff --git a/es.h b/es.h index 08f58b4..a291660 100644 --- a/es.h +++ b/es.h @@ -288,14 +288,13 @@ extern Boolean streq2(const char *s, const char *t1, const char *t2); /* input.c */ -extern Tree *parse(char *prompt1, char *prompt2); +extern Tree *parse(List *reader); extern Tree *parsestring(const char *str); extern Boolean isinteractive(void); extern Boolean isfromfd(void); -extern void initinput(void); extern List *runfd(int fd, const char *name, int flags); -extern List *runstring(const char *str, const char *name, int flags); +extern List *runstring(const char *str, int flags); /* eval_* flags are also understood as runflags */ #define run_interactive 4 /* -i or $0[0] = '-' */ @@ -305,27 +304,6 @@ extern List *runstring(const char *str, const char *name, int flags); #define run_lisptrees 64 /* -L and defined(LISPTREES) */ -#if HAVE_READLINE -/* readline.c */ - -extern Boolean resetterminal; -extern char *callreadline(char *prompt); - -extern void inithistory(void); - -extern void sethistory(char *file); -extern void loghistory(char *cmd); -extern void setmaxhistorylength(int length); -extern void checkhistory(void); -#endif - - -/* history.c */ -extern void newhistbuffer(void); -extern void addhistbuffer(char c); -extern char *dumphistbuffer(void); - - /* opt.c */ extern void esoptbegin(List *list, const char *caller, const char *usage, Boolean throws); diff --git a/heredoc.c b/heredoc.c index 3e1dace..2dcb667 100644 --- a/heredoc.c +++ b/heredoc.c @@ -43,7 +43,7 @@ extern Tree *snarfheredoc(Parser *p, const char *eof, Boolean quoted) { for (tree = NULL, tailp = &tree, buf = openbuffer(0);;) { int c; - print_prompt2(p); + p->input->lineno++; for (s = (unsigned char *) eof; (c = get(p)) == *s; s++) ; if (*s == '\0' && (c == '\n' || c == EOF)) { diff --git a/history.c b/history.c deleted file mode 100644 index b4f4cec..0000000 --- a/history.c +++ /dev/null @@ -1,50 +0,0 @@ -/* history.c -- control the history file ($Revision: 1.1.1.1 $) */ - -#include "es.h" -#include "gc.h" -#include "input.h" - - -/* - * constants - */ - -#define BUFSIZE ((size_t) 4096) /* buffer size to fill reads into */ - - -/* - * globals - */ - -static Buffer *histbuffer = NULL; - - -/* - * histbuffer -- build the history line during input and dump it as a gc-string - */ - - -extern void newhistbuffer(void) { - assert(histbuffer == NULL); - histbuffer = openbuffer(BUFSIZE); -} - -extern void addhistbuffer(char c) { - if (histbuffer == NULL) - return; - histbuffer = bufputc(histbuffer, c); -} - -extern char *dumphistbuffer(void) { - char *s; - size_t len; - assert(histbuffer != NULL); - - s = sealcountedbuffer(histbuffer); - histbuffer = NULL; - - len = strlen(s); - if (len > 0 && s[len - 1] == '\n') - s[len - 1] = '\0'; - return s; -} diff --git a/initial.es b/initial.es index 4551bec..88a1de3 100644 --- a/initial.es +++ b/initial.es @@ -653,10 +653,39 @@ if {~ <=$&primitives writehistory} { # The parsed code is executed only if it is non-empty, because otherwise # result gets set to zero when it should not be. -fn-%parse = $&parse fn-%batch-loop = $&batchloop fn-%is-interactive = $&isinteractive +if {~ <=$&primitives readline} { + fn-%read-line = $&readline +} { + fn %read-line prompt { + echo -n $prompt + $&read + } +} + +fn %parse { + if %is-interactive { + let (in = (); p = $*(1)) + unwind-protect { + $&parse { + let (r = <={%read-line $p}) { + in = $in $r + p = $*(2) + result $r + } + } + } { + if {!~ $#fn-%write-history 0 && !~ $#in 0} { + %write-history <={%flatten \n $in} + } + } + } { + $&parse # fall back to built-in read with no prompt + } +} + fn %interactive-loop { let (result = <=true) { catch @ e type msg { @@ -690,6 +719,7 @@ fn %interactive-loop { } } + # These functions are potentially passed to a REPL as the %dispatch # function. (For %eval-noprint, note that an empty list prepended # to a command just causes the command to be executed.) diff --git a/input.c b/input.c index 9d08753..74e291f 100644 --- a/input.c +++ b/input.c @@ -17,11 +17,6 @@ */ static Input *input = NULL; -Boolean resetterminal = FALSE; /* TODO: localize to readline code */ - -#if HAVE_READLINE -#include -#endif /* @@ -41,32 +36,56 @@ extern void yyerror(Parser *p, const char *s) { p->error = locate(p->input, s); } -/* warn -- print a warning */ -static void warn(Input *in, char *s) { - eprint("warning: %s\n", locate(in, s)); -} - /* * getting and ungetting characters */ -static int fill(Input *in); -static void cleanup(Input *in); +/* fill -- fill input buffer by running a command */ +static int fill(Parser *p) { + List *result; + char *read; + size_t nread; + Input *in = p->input; + + assert(p->buf == p->bufend); + + if (p->reader != NULL) { + result = eval(p->reader, NULL, 0); + read = str("%L\n", result, " "); + } else { + result = prim("read", NULL, 0); + RefAdd(result); + if (length(result) > 1) + eprint("%s\n", locate(in, "null character ignored")); + read = str("%L\n", result, ""); + RefRemove(result); + } + if (result == NULL) { /* eof */ + in->eof = TRUE; + return EOF; + } + if ((nread = strlen(read)) > p->buflen) { + p->bufbegin = erealloc(p->bufbegin, nread); + p->buflen = nread; + } + memcpy(p->bufbegin, read, nread); + + p->buf = p->bufbegin; + p->bufend = &p->buf[nread]; + + return *p->buf++; +} /* get -- get a character, filter out nulls */ extern int get(Parser *p) { int c; - Input *in = p->input; if (p->ungot > 0) return p->unget[--p->ungot]; - while ((c = (in->buf < in->bufend ? *in->buf++ : fill(in))) == '\0') - warn(in, "null character ignored"); - if (c != EOF) { - char buf = c; - addhistbuffer(buf); - if (p->input->runflags & run_echoinput) - ewrite(2, &buf, 1); + c = p->buf < p->bufend ? *p->buf++ : fill(p); + if (c != EOF && p->input->runflags & run_echoinput) { + char buf = (char)c; + ewrite(2, &buf, 1); } return c; } @@ -77,65 +96,22 @@ extern void unget(Parser *p, int c) { p->unget[p->ungot++] = c; } - - -static int fill(Input *in) { - long nread; - assert(in->buf == in->bufend); - - if (in->fd < 0) { - in->eof = TRUE; - return EOF; - } - -#if HAVE_READLINE - if (in->runflags & run_interactive && in->fd == 0) { - char *rlinebuf = NULL; - rl_instream = stdin; - rl_outstream = stdout; - do { - rlinebuf = callreadline(in->prompt); - } while (rlinebuf == NULL && errno == EINTR); - if (rlinebuf == NULL) - nread = 0; - else { - nread = strlen(rlinebuf) + 1; - if (in->buflen < (unsigned int)nread) { - while (in->buflen < (unsigned int)nread) - in->buflen *= 2; - in->bufbegin = erealloc(in->bufbegin, in->buflen); - } - memcpy(in->bufbegin, rlinebuf, nread - 1); - in->bufbegin[nread - 1] = '\n'; - efree(rlinebuf); - } +static void initbuf(Parser *p) { + const char *initial = p->input->str; + p->buflen = initial == NULL ? BUFSIZE : strlen(initial); + p->bufbegin = p->buf = ealloc(p->buflen); + if (initial != NULL) { + memcpy(p->buf, initial, p->buflen); + p->bufend = p->bufbegin + p->buflen; } else -#endif - do { - nread = read(in->fd, (char *) in->bufbegin, in->buflen); - SIGCHK(); - } while (nread == -1 && errno == EINTR); - - if (nread == -1) - fail("$&parse", "%s: %s", in->name == NULL ? "es" : in->name, esstrerror(errno)); - if (nread == 0) { - in->eof = TRUE; - return EOF; - } - - in->buf = in->bufbegin; - in->bufend = &in->buf[nread]; - return *in->buf++; + p->bufend = p->bufbegin; } - /* - * the input loop + * parse -- wrapper around yyparse() */ - -/* parse -- yyparse() wrapper */ -extern Tree *parse(char *pr1, char *pr2) { - int result; +extern Tree *parse(List *reader) { + int result, fd, ticket = UNREGISTERED; Parser p; void *oldpspace; @@ -146,24 +122,39 @@ extern Tree *parse(char *pr1, char *pr2) { memzero(&p, sizeof (Parser)); p.input = input; + p.reader = reader; + RefAdd(p.reader); p.space = createpspace(); oldpspace = setpspace(p.space); inityy(&p); + initbuf(&p); p.tokenbuf = ealloc(p.bufsize); - RefAdd(pr2); - input->prompt = pr1 == NULL ? NULL : pstr("%s", pr1); - input->prompt2 = pr2 == NULL ? NULL : pstr("%s", pr2); - RefRemove(pr2); -#if !HAVE_READLINE - if (input->prompt != NULL) - eprint("%s", input->prompt); -#endif + fd = (input->fd == -1) + ? eopen("/dev/null", oOpen) + : dup(input->fd); + ticket = defer_mvfd(TRUE, fd, 0); - result = yyparse(&p); + ExceptionHandler + + result = yyparse(&p); + + CatchException (e) + + undefer(ticket); + pseal(NULL); + setpspace(oldpspace); + throw(e); + EndExceptionHandler + + undefer(ticket); + + RefRemove(p.reader); assert(p.ungot == 0); + if (p.bufbegin != NULL) + efree(p.bufbegin); if (p.tokenbuf != NULL) efree(p.tokenbuf); @@ -185,6 +176,19 @@ extern Tree *parse(char *pr1, char *pr2) { RefReturn(tree); } + +/* + * the input loop + */ + +/* cleanup -- clean up after an input source */ +static void cleanup(Input *in) { + if (in->fd != -1) { + unregisterfd(&in->fd); + close(in->fd); + } +} + /* runinput -- run from an input source */ extern List *runinput(Input *in, int runflags) { volatile int flags = runflags; @@ -243,14 +247,6 @@ extern List *runinput(Input *in, int runflags) { * pushing new input sources */ -static void cleanup(Input *in) { - if (in->fd != -1) { - unregisterfd(&in->fd); - close(in->fd); - } - efree(in->bufbegin); -} - /* runfd -- run commands from a file descriptor */ extern List *runfd(int fd, const char *name, int flags) { Input in; @@ -260,9 +256,6 @@ extern List *runfd(int fd, const char *name, int flags) { in.lineno = 1; in.fd = fd; registerfd(&in.fd, TRUE); - in.buflen = BUFSIZE; - in.bufbegin = in.buf = ealloc(in.buflen); - in.bufend = in.bufbegin; in.name = (name == NULL) ? str("fd %d", fd) : name; RefAdd(in.name); @@ -273,25 +266,22 @@ extern List *runfd(int fd, const char *name, int flags) { } /* runstring -- run commands from a string */ -extern List *runstring(const char *str, const char *name, int flags) { +extern List *runstring(const char *str, int flags) { Input in; List *result; - unsigned char *buf; assert(str != NULL); memzero(&in, sizeof (Input)); in.fd = -1; in.lineno = 1; - in.name = (name == NULL) ? str : name; - in.buflen = strlen(str); - buf = ealloc(in.buflen + 1); - memcpy(buf, str, in.buflen); - in.bufbegin = in.buf = buf; - in.bufend = in.buf + in.buflen; + in.name = str; + in.str = str; RefAdd(in.name); + RefAdd(in.str); result = runinput(&in, flags); + RefRemove(in.str); RefRemove(in.name); return result; } @@ -305,7 +295,7 @@ extern Tree *parseinput(Input *in) { input = in; ExceptionHandler - result = parse(NULL, NULL); + result = parse(NULL); if (!in->eof) fail("$&parse", "more than one value in term"); CatchException (e) @@ -323,24 +313,19 @@ extern Tree *parseinput(Input *in) { extern Tree *parsestring(const char *str) { Input in; Tree *result; - unsigned char *buf; assert(str != NULL); - /* TODO: abstract out common code with runstring */ - memzero(&in, sizeof (Input)); in.fd = -1; in.lineno = 1; in.name = str; - in.buflen = strlen(str); - buf = ealloc(in.buflen + 1); - memcpy(buf, str, in.buflen); - in.bufbegin = in.buf = buf; - in.bufend = in.buf + in.buflen; + in.str = str; RefAdd(in.name); + RefAdd(in.str); result = parseinput(&in); + RefRemove(in.str); RefRemove(in.name); return result; } diff --git a/input.h b/input.h index 656ee1d..f96f72e 100644 --- a/input.h +++ b/input.h @@ -6,19 +6,11 @@ /* Input contains state that lasts longer than a $&parse. */ struct Input { - /* input buffer */ - size_t buflen; - unsigned char *buf, *bufend, *bufbegin; - - /* input metadata and flags */ - const char *name; + const char *name, *str; int lineno; int fd; - int runflags; Boolean eof; - - /* TODO: these belong in Parser */ - char *prompt, *prompt2; /* pspace-allocated */ + int runflags; }; typedef enum { NW, RW, KW } WordState; /* nonword, realword, keyword */ @@ -26,20 +18,24 @@ typedef enum { NW, RW, KW } WordState; /* nonword, realword, keyword */ /* Parser contains state that lasts for one call to $&parse or less. */ struct Parser { Input *input; - void *space; /* where the parse tree is kept in memory */ + List *reader; + void *space; /* pspace, for parser-related allocations */ /* these variables are all allocated in pspace */ Tree *tree; /* the final parse tree */ Here *hereq; /* pending here document queue */ const char *error; /* syntax error, if it exists */ + /* read buffer */ + size_t buflen; + unsigned char *buf, *bufend, *bufbegin; + /* token pushback buffer */ - int unget[MAXUNGET]; - int ungot; + int ungot, unget[MAXUNGET]; /* lexer state */ WordState ws; - Boolean newline, goterror, dollar; + Boolean dollar; size_t bufsize; char *tokenbuf; }; diff --git a/main.c b/main.c index 623ec70..3c8e941 100644 --- a/main.c +++ b/main.c @@ -210,7 +210,7 @@ int main(int argc, char **argv0) { vardef("*", NULL, argp); vardef("0", NULL, mklist(mkstr(argv[0]), NULL)); if (cmd != NULL) - status = exitstatus(runstring(cmd, NULL, runflags)); + status = exitstatus(runstring(cmd, runflags)); else status = exitstatus(runfd(0, "stdin", runflags)); diff --git a/prim-etc.c b/prim-etc.c index 60a9137..2c91f8e 100644 --- a/prim-etc.c +++ b/prim-etc.c @@ -149,54 +149,16 @@ PRIM(var) { return list; } -static void loginput(char *input) { - char *c; - List *fn = varlookup("fn-%write-history", NULL); - if (!isinteractive() || !isfromfd() || fn == NULL) - return; - for (c = input;; c++) - switch (*c) { - case '#': case '\n': return; - case ' ': case '\t': break; - default: goto writeit; - } -writeit: - gcdisable(); - Ref(List *, list, append(fn, mklist(mkstr(input), NULL))); - gcenable(); - eval(list, NULL, 0); - RefEnd(list); -} - PRIM(parse) { List *result; - Ref(char *, prompt1, NULL); - Ref(char *, prompt2, NULL); - Ref(List *, lp, list); - if (lp != NULL) { - prompt1 = getstr(lp->term); - if ((lp = lp->next) != NULL) - prompt2 = getstr(lp->term); - } - RefEnd(lp); - newhistbuffer(); - + Ref(List *, reader, list); Ref(Tree *, tree, NULL); - ExceptionHandler - tree = parse(prompt1, prompt2); - CatchException (ex) - Ref(List *, e, ex); - loginput(dumphistbuffer()); - throw(e); - RefEnd(e); - EndExceptionHandler - - loginput(dumphistbuffer()); + tree = parse(reader); result = (tree == NULL) ? NULL : mklist(mkterm(NULL, mkclosure(gcmk(nThunk, tree), NULL)), NULL); - RefEnd3(tree, prompt2, prompt1); + RefEnd2(tree, reader); return result; } @@ -303,6 +265,7 @@ PRIM(setmaxevaldepth) { RefReturn(lp); } + /* * initialization */ diff --git a/prim.h b/prim.h index 76416ca..898c04f 100644 --- a/prim.h +++ b/prim.h @@ -16,6 +16,7 @@ typedef struct { List *(*prim)(List *, int); } Prim; extern Dict *initprims_controlflow(Dict *primdict); /* prim-ctl.c */ extern Dict *initprims_io(Dict *primdict); /* prim-io.c */ extern Dict *initprims_etc(Dict *primdict); /* prim-etc.c */ +extern Dict *initprims_readline(Dict *primdict); /* prim-readline.c */ extern Dict *initprims_sys(Dict *primdict); /* prim-sys.c */ extern Dict *initprims_proc(Dict *primdict); /* proc.c */ extern Dict *initprims_access(Dict *primdict); /* access.c */ diff --git a/readline.c b/readline.c index ff58dac..1ca0242 100644 --- a/readline.c +++ b/readline.c @@ -8,8 +8,14 @@ #include #include -Boolean reloadhistory = FALSE; -static char *history; + +/* + * globals + */ + +static Boolean reloadhistory = FALSE; +static Boolean resetterminal = FALSE; +static char *history = NULL; #if 0 /* These split history file entries by timestamp, which allows readline to pick up @@ -19,6 +25,7 @@ static int history_write_timestamps = 1; static char history_comment_char = '#'; #endif + /* * history functions */ @@ -78,7 +85,16 @@ static void reload_history(void) { reloadhistory = FALSE; } +static void inithistory(void) { + static Boolean initialized = FALSE; + if (initialized) + return; + globalroot(&history); + initialized = TRUE; +} + extern void sethistory(char *file) { + inithistory(); if (reloadhistory) reload_history(); reloadhistory = TRUE; @@ -106,7 +122,7 @@ extern void checkhistory(void) { /* - * readline library functions + * readline functions */ /* quote -- teach readline how to quote a word during completion. @@ -324,12 +340,33 @@ static int es_complete_primitive(int UNUSED count, int UNUSED key) { return rl_complete_internal(rl_completion_mode(es_complete_primitive)); } -/* callreadline -- readline wrapper */ -extern char *callreadline(char *prompt0) { - char *r; - Ref(char *volatile, prompt, prompt0); - if (prompt == NULL) - prompt = ""; /* bug fix for readline 2.0 */ +static void initreadline(void) { + rl_readline_name = "es"; + + /* this word_break_characters excludes '&' due to primitive completion */ + rl_basic_word_break_characters = " \t\n`$><=;|{()}"; + rl_filename_quote_characters = " \t\n\\`'$><=;|&{()}"; + rl_basic_quote_characters = ""; + rl_special_prefixes = "$"; + + rl_completion_word_break_hook = completion_start; + rl_filename_stat_hook = unquote_for_stat; + rl_attempted_completion_function = builtin_completion; + rl_completion_display_matches_hook = display_matches; + + rl_add_funmap_entry("es-complete-filename", es_complete_filename); + rl_add_funmap_entry("es-complete-variable", es_complete_variable); + rl_add_funmap_entry("es-complete-primitive", es_complete_primitive); + rl_bind_keyseq("\033/", es_complete_filename); + rl_bind_keyseq("\033$", es_complete_variable); +} + +static void prepreadline(void) { + static Boolean initialized = FALSE; + if (!initialized) { + initreadline(); + initialized = TRUE; + } checkhistory(); if (resetterminal) { rl_reset_terminal(NULL); @@ -337,6 +374,15 @@ extern char *callreadline(char *prompt0) { } if (RL_ISSTATE(RL_STATE_INITIALIZED)) rl_reset_screen_size(); +} + +/* callreadline -- readline wrapper */ +static char *callreadline(char *prompt0) { + char *r; + Ref(char *volatile, prompt, prompt0); + prepreadline(); + if (prompt == NULL) + prompt = ""; /* bug fix for readline 2.0 */ if (!sigsetjmp(slowlabel, 1)) { slow = TRUE; r = readline(prompt); @@ -373,6 +419,13 @@ PRIM(readline) { if (list != NULL && list->next != NULL) fail("$&readline", "usage: %read-line [prompt]"); + if (!isatty(fdmap(0))) { + list = prim("read", NULL, 0); + if (length(list) <= 1) + return list; + return mklist(mkstr(str("%L", list, "")), NULL); + } + rl_instream = fdmapopen(0, "r"); ExceptionHandler rl_outstream = fdmapopen(2, "w"); @@ -444,39 +497,12 @@ PRIM(resetterminal) { return ltrue; } - -/* - * initialization - */ - extern Dict *initprims_readline(Dict *primdict) { - rl_readline_name = "es"; - - /* this word_break_characters excludes '&' due to primitive completion */ - rl_basic_word_break_characters = " \t\n`$><=;|{()}"; - rl_filename_quote_characters = " \t\n\\`'$><=;|&{()}"; - rl_basic_quote_characters = ""; - rl_special_prefixes = "$"; - - rl_completion_word_break_hook = completion_start; - rl_filename_stat_hook = unquote_for_stat; - rl_attempted_completion_function = builtin_completion; - rl_completion_display_matches_hook = display_matches; - - rl_add_funmap_entry("es-complete-filename", es_complete_filename); - rl_add_funmap_entry("es-complete-variable", es_complete_variable); - rl_add_funmap_entry("es-complete-primitive", es_complete_primitive); - rl_bind_keyseq("\033/", es_complete_filename); - rl_bind_keyseq("\033$", es_complete_variable); - - globalroot(&history); /* history file */ - X(readline); X(sethistory); X(writehistory); X(resetterminal); X(setmaxhistorylength); - return primdict; } #endif diff --git a/test/tests/parse.es b/test/tests/parse.es new file mode 100644 index 0000000..ca2c6c6 --- /dev/null +++ b/test/tests/parse.es @@ -0,0 +1,67 @@ +# tests/parse.es -- test that $&parse works with the chaos of various reader commands + +test 'parser' { + let (ex = ()) { + catch @ e { + ex = $e + } { + $&parse {throw test-exception} + } + assert {~ $ex test-exception} + } + + let ((e type msg) = ()) { + catch @ exc { + (e type msg) = $exc + } { + $&parse {result ')'} + } + assert {~ $e error && ~ $msg *'syntax error'*} \ + 'parser handles syntax error' + } + + # run these two in subshells, they cause their inputs to go "eof" + let (msg = `` \n {catch @ exc { + echo $exc + } { + $&parse {result '('} + } + }) { + assert {~ $msg 'error'*'memory exhausted'* || ~ $msg 'error'*'stack overflow'*} \ + 'parser handles infinite recursion' + } + + let (msg = `` \n { + catch @ exc { + echo 'caught' $exc + } { + let (line = 'aaaa ( bbbbb') + echo 'parsed' <={$&parse {let (l = $line) {line = (); result $l}}} + } + }) { + assert {~ $msg 'caught'*'syntax error'*} + } + + # test parsing from string while parsing from input + let (e = ()) { + catch @ exc { + e = $exc + } { + $&parse {eval result true} + } + assert {~ $e ()} + } + + # do normal cases last to see if previous ones broke anything + assert {~ <={$&parse {result 'echo >[1=2]'}} '{%dup 1 2 {echo}}'} + + # force GCs during parsing + let (lines = 'fn zoom {' 'this is one' 'let (z = a a a) {' 'this is three' '}' '}') + assert {~ <={$&parse { + let (l = ()) { + (l lines) = $lines + $&collect + result $l + } + }} '{fn-^zoom=@ *{%seq {this is one} {let(z=a a a){this is three}}}}'} +} diff --git a/test/tests/read.es b/test/tests/read.es index 605b03a..a278079 100644 --- a/test/tests/read.es +++ b/test/tests/read.es @@ -65,6 +65,7 @@ test 'fd error handling' { if {~ <=$&primitives readline} { assert {catch @ {true} {$&readline <<< '' <[0=]; false}} assert {catch @ {false} {$&readline <<< '' >[1=]; true}} - assert {catch @ {true} {$&readline <<< '' >[2=]; false}} + # Only while $&read fallback is performed + assert {catch @ {false} {$&readline <<< '' >[2=]; true}} } } diff --git a/token.c b/token.c index 0a454c3..05de5d8 100644 --- a/token.c +++ b/token.c @@ -57,23 +57,11 @@ const char dnw[] = { }; -/* print_prompt2 -- called before all continuation lines */ -extern void print_prompt2(Parser *p) { - Input *in = p->input; - in->lineno++; -#if HAVE_READLINE - in->prompt = in->prompt2; -#else - if ((p->input->runflags & run_interactive) && in->prompt2 != NULL) - eprint("%s", in->prompt2); -#endif -} - /* scanerror -- called for lexical errors */ static void scanerror(Parser *p, int c, char *s) { while (c != '\n' && c != EOF) c = get(p); - p->goterror = TRUE; + unget(p, '\n'); yyerror(p, s); } @@ -140,19 +128,9 @@ extern int yylex(YYSTYPE *y, Parser *p) { const char *meta; /* allow optimizing compilers like gcc to load these */ char *buf = p->tokenbuf; /* values into registers. */ - if (p->goterror) { - p->goterror = FALSE; - return NL; - } - /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */ meta = (p->dollar ? dnw : nw); p->dollar = FALSE; - if (p->newline) { - --p->input->lineno; /* slight space optimization; print_prompt2() always increments lineno */ - print_prompt2(p); - p->newline = FALSE; - } top: while ((c = get(p)) == ' ' || c == '\t') p->ws = NW; if (c == EOF) @@ -224,7 +202,7 @@ top: while ((c = get(p)) == ' ' || c == '\t') while ((c = get(p)) != '\'' || (c = get(p)) == '\'') { buf[i++] = c; if (c == '\n') - print_prompt2(p); + p->input->lineno++; if (c == EOF) { p->ws = NW; scanerror(p, c, "eof in quoted string"); @@ -239,7 +217,7 @@ top: while ((c = get(p)) == ' ' || c == '\t') return QWORD; case '\\': if ((c = get(p)) == '\n') { - print_prompt2(p); + p->input->lineno++; unget(p, ' '); goto top; /* Pretend it was just another space. */ } @@ -306,7 +284,6 @@ top: while ((c = get(p)) == ' ' || c == '\t') FALLTHROUGH; case '\n': p->input->lineno++; - p->newline = TRUE; p->ws = NW; return NL; case '(': @@ -402,7 +379,7 @@ top: while ((c = get(p)) == ' ' || c == '\t') } extern void inityy(Parser *p) { - p->newline = p->dollar = p->goterror = FALSE; + p->dollar = FALSE; p->ws = NW; p->bufsize = BUFSIZE; }