📄 front.c
字号:
/**********Copyright 1990 Regents of the University of California. All rights reserved.Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group**********//* * The front-end command loop. */#include "spice.h"#include "util.h"#include "cpdefs.h"#include "suffix.h"static char *doblock();static void docommand();static void dodump();static struct control *findlabel();static wordlist *getcommand();static void pwlist();/* Return values from doblock(). I am assuming that nobody will use these * characters in a string. */#define NORMAL '\001'#define BROKEN '\002'#define CONTINUED '\003'#define NORMAL_STR "\001"#define BROKEN_STR "\002"#define CONTINUED_STR "\003"/* Are we waiting for a command? This lets (void) signal handling be more clever. */bool cp_cwait = false; char *cp_csep = ";";bool cp_dounixcom = false;/* Stuff to do control structures. We keep a history (seperate from the * cshpar history, for now at least) of commands and their event numbers, * with a block considered as a statement. In a goto, the first word in * co_text is where to go, likewise for label. For conditional controls, * we have to call ft_getpnames and ft_evaluate each time, since the * dvec pointers will change... Also we should do variable and backquote * substitution each time... */struct control { int co_type; /* One of CO_* ... */ wordlist *co_cond; /* if, while, dowhile */ char *co_foreachvar; /* foreach */ int co_numtimes; /* repeat, break & continue levels */ wordlist *co_text; /* Ordinary text and foreach values. */ struct control *co_parent; /* If this is inside a block. */ struct control *co_children; /* The contents of this block. */ struct control *co_elseblock; /* For if-then-else. */ struct control *co_next; struct control *co_prev;} ;#define CO_UNFILLED 0#define CO_STATEMENT 1#define CO_WHILE 2#define CO_DOWHILE 3#define CO_IF 4#define CO_FOREACH 5#define CO_BREAK 6#define CO_CONTINUE 7#define CO_LABEL 8#define CO_GOTO 9#define CO_REPEAT 10/* We have to keep the control structures in a stack, so that when we do * a 'source', we can push a fresh set onto the top... Actually there have * to be two stacks -- one for the pointer to the list of control structs, * and one for the 'current command' pointer... */#define CONTROLSTACKSIZE 256 /* Better be enough. */static struct control *control[CONTROLSTACKSIZE], *cend[CONTROLSTACKSIZE];static int stackp = 0;/* If there is an argument, give this to cshpar to use instead of stdin. In * a few places, we call cp_evloop again if it returns 1 and exit (or close * a file) if it returns 0... Because of the way sources are done, we can't * allow the control structures to get blown away every time we return -- * probably every time we type source at the keyboard and every time a * source returns to keyboard input is ok though -- use ft_controlreset. */static char *noredirect[] = { "stop", NULL } ; /* Only one?? */intcp_evloop(string) char *string;{ wordlist *wlist, *ww; struct control *x; char *i; int nn;#define newblock cend[stackp]->co_children = alloc(struct control); \ ZERO(cend[stackp]->co_children,struct control), \ cend[stackp]->co_children->co_parent = cend[stackp]; \ cend[stackp] = cend[stackp]->co_children; \ cend[stackp]->co_type = CO_UNFILLED; for (;;) { wlist = getcommand(string); if (wlist == NULL) { /* End of file or end of user input. */ if (cend[stackp]->co_parent && !string) { cp_resetcontrol(); continue; } else return (0); } if ((wlist->wl_word == NULL) || (*wlist->wl_word == '\0')) { /* User just typed return. */ if (string) return (1); else { cp_event--; continue; } } /* Just a check... */ for (ww = wlist; ww; ww = ww->wl_next) if (!ww->wl_word) { fprintf(cp_err, "cp_evloop: Internal Error: NULL word pointer\n"); continue; } /* Add this to the control structure list. If cend->co_type * is CO_UNFILLED, the last line was the beginning of a * block, and this is the unfilled first statement. */ if (cend[stackp] && (cend[stackp]->co_type != CO_UNFILLED)) { cend[stackp]->co_next = alloc(struct control); ZERO(cend[stackp]->co_next, struct control); cend[stackp]->co_next->co_prev = cend[stackp]; cend[stackp]->co_next->co_parent = cend[stackp]->co_parent; cend[stackp] = cend[stackp]->co_next; } else if (!cend[stackp]) { control[stackp] = cend[stackp] = alloc(struct control); ZERO(cend[stackp], struct control); } if (eq(wlist->wl_word, "while")) { cend[stackp]->co_type = CO_WHILE; cend[stackp]->co_cond = wlist->wl_next; if (!cend[stackp]->co_cond) { fprintf(stderr, "Error: missing while condition.\n"); } newblock; } else if (eq(wlist->wl_word, "dowhile")) { cend[stackp]->co_type = CO_DOWHILE; cend[stackp]->co_cond = wlist->wl_next; if (!cend[stackp]->co_cond) { fprintf(stderr, "Error: missing dowhile condition.\n"); } newblock; } else if (eq(wlist->wl_word, "repeat")) { cend[stackp]->co_type = CO_REPEAT; if (!wlist->wl_next) { cend[stackp]->co_numtimes = -1; } else { char *s; double *dd; wlist = cp_variablesubst(cp_bquote( cp_doglob(wl_copy(wlist)))); s = wlist->wl_next->wl_word; dd = ft_numparse(&s, false); if (dd) { if (*dd < 0) { fprintf(cp_err, "Error: can't repeat a negative number of times\n"); *dd = 0.0; } cend[stackp]->co_numtimes = (int) *dd; } else fprintf(cp_err, "Error: bad repeat argument %s\n", wlist->wl_next->wl_word); } newblock; } else if (eq(wlist->wl_word, "if")) { cend[stackp]->co_type = CO_IF; cend[stackp]->co_cond = wlist->wl_next; if (!cend[stackp]->co_cond) { fprintf(stderr, "Error: missing if condition.\n"); } newblock; } else if (eq(wlist->wl_word, "foreach")) { cend[stackp]->co_type = CO_FOREACH; if (wlist->wl_next) { wlist = wlist->wl_next; cend[stackp]->co_foreachvar = copy(wlist->wl_word); wlist = wlist->wl_next; } else fprintf(stderr, "Error: missing foreach variable.\n"); wlist = cp_doglob(wlist); cend[stackp]->co_text = wl_copy(wlist); newblock; } else if (eq(wlist->wl_word, "label")) { cend[stackp]->co_type = CO_LABEL; if (wlist->wl_next) { cend[stackp]->co_text = wl_copy(wlist->wl_next); /* I think of everything, don't I? */ cp_addkword(CT_LABEL, wlist->wl_next->wl_word); if (wlist->wl_next->wl_next) fprintf(cp_err, "Warning: ignored extra junk after label.\n"); } else fprintf(stderr, "Error: missing label.\n"); } else if (eq(wlist->wl_word, "goto")) { /* Incidentally, this won't work if the values 1 and * 2 ever get to be valid character pointers -- I * think it's reasonably safe to assume they aren't... */ cend[stackp]->co_type = CO_GOTO; if (wlist->wl_next) { cend[stackp]->co_text = wl_copy(wlist->wl_next); if (wlist->wl_next->wl_next) fprintf(cp_err, "Warning: ignored extra junk after goto.\n"); } else fprintf(stderr, "Error: missing label.\n"); } else if (eq(wlist->wl_word, "continue")) { cend[stackp]->co_type = CO_CONTINUE; if (wlist->wl_next) { cend[stackp]->co_numtimes = scannum(wlist-> wl_next->wl_word); if (wlist->wl_next->wl_next) fprintf(cp_err, "Warning: ignored extra junk after continue %d.\n", cend[stackp]->co_numtimes); } else cend[stackp]->co_numtimes = 1; } else if (eq(wlist->wl_word, "break")) { cend[stackp]->co_type = CO_BREAK; if (wlist->wl_next) { cend[stackp]->co_numtimes = scannum(wlist-> wl_next->wl_word); if (wlist->wl_next->wl_next) fprintf(cp_err, "Warning: ignored extra junk after break %d.\n", cend[stackp]->co_numtimes); } else cend[stackp]->co_numtimes = 1; } else if (eq(wlist->wl_word, "end")) { /* Throw away this thing. */ if (!cend[stackp]->co_parent) { fprintf(stderr, "Error: no block to end.\n"); cend[stackp]->co_type = CO_UNFILLED; } else if (cend[stackp]->co_prev) { cend[stackp]->co_prev->co_next = NULL; x = cend[stackp]; cend[stackp] = cend[stackp]->co_parent; tfree(x); } else { x = cend[stackp]; cend[stackp] = cend[stackp]->co_parent; cend[stackp]->co_children = NULL; tfree(x); } } else if (eq(wlist->wl_word, "else")) { if (!cend[stackp]->co_parent || (cend[stackp]->co_parent->co_type != CO_IF)) { fprintf(stderr, "Error: misplaced else.\n"); cend[stackp]->co_type = CO_UNFILLED; } else { if (cend[stackp]->co_prev) cend[stackp]->co_prev->co_next = NULL; else cend[stackp]->co_parent->co_children = NULL; cend[stackp]->co_parent->co_elseblock = cend[stackp]; cend[stackp]->co_prev = NULL; } } else { cend[stackp]->co_type = CO_STATEMENT; cend[stackp]->co_text = wlist; } if (!cend[stackp]->co_parent) { x = cend[stackp]; /* We have to toss this do-while loop in here so * that gotos at the top level will work. */ do { i = doblock(x, &nn); switch (*i) { case NORMAL: break; case BROKEN: fprintf(cp_err, "Error: break not in loop or too many break levels given\n"); break; case CONTINUED: fprintf(cp_err, "Error: continue not in loop or too many continue levels given\n"); break; default: x = findlabel(i, control[stackp]); if (!x) fprintf(cp_err, "Error: label %s not found\n", i); } if (x) x = x->co_next; } while (x); } if (string) return (1); /* The return value is irrelevant. */ }}/* Execute a block. There can be a number of return values from this routine. * NORMAL indicates a normal termination * BROKEN indicates a break -- if the caller is a breakable loop, * terminate it, otherwise pass the break upwards * CONTINUED indicates a continue -- if the caller is a continuable loop, * continue, else pass the continue upwards * Any other return code is considered a pointer to a string which is * a label somewhere -- if this label is present in the block, * goto it, otherwise pass it up. Note that this prevents jumping * into a loop, which is good. * Note that here is where we expand variables, ``, and globs for controls. * The 'num' argument is used by break n and continue n. */static char *doblock(bl, num) struct control *bl; int *num;{ struct control *ch, *cn = NULL; wordlist *wl; char *i; int nn; switch (bl->co_type) { case CO_WHILE: while (bl->co_cond && cp_istrue(bl->co_cond)) { for (ch = bl->co_children; ch; ch = cn) { cn = ch->co_next; i = doblock(ch, &nn); switch (*i) { case NORMAL: break; case BROKEN: /* Break. */ if (nn < 2) return (NORMAL_STR); else { *num = nn - 1; return (BROKEN_STR); } case CONTINUED: /* Continue. */ if (nn < 2) { cn = NULL; break; } else { *num = nn - 1; return (CONTINUED_STR); } default: cn = findlabel(i, bl->co_children); if (!cn) return (i); } } } break; case CO_DOWHILE: do { for (ch = bl->co_children; ch; ch = cn) { cn = ch->co_next; i = doblock(ch, &nn); switch (*i) { case NORMAL: break; case BROKEN: /* Break. */ if (nn < 2) return (NORMAL_STR); else { *num = nn - 1; return (BROKEN_STR); } case CONTINUED: /* Continue. */ if (nn < 2) { cn = NULL; break; } else { *num = nn - 1; return (CONTINUED_STR); } default: cn = findlabel(i, bl->co_children); if (!cn) return (i); } } } while (bl->co_cond && cp_istrue(bl->co_cond)); break; case CO_REPEAT: while ((bl->co_numtimes > 0) || (bl->co_numtimes == -1)) { if (bl->co_numtimes != -1) bl->co_numtimes--; for (ch = bl->co_children; ch; ch = cn) { cn = ch->co_next; i = doblock(ch, &nn); switch (*i) { case NORMAL: break; case BROKEN: /* Break. */ if (nn < 2) return (NORMAL_STR); else { *num = nn - 1; return (BROKEN_STR); } case CONTINUED: /* Continue. */ if (nn < 2) { cn = NULL; break; } else { *num = nn - 1; return (CONTINUED_STR); } default: cn = findlabel(i, bl->co_children); if (!cn) return (i); } } } break; case CO_IF: if (bl->co_cond && cp_istrue(bl->co_cond)) { for (ch = bl->co_children; ch; ch = cn) { cn = ch->co_next; i = doblock(ch, &nn); if (*i > 2) { cn = findlabel(i, bl->co_children); if (!cn) return (i); } else if (*i != NORMAL) { *num = nn; return (i); } } } else { for (ch = bl->co_elseblock; ch; ch = cn) { cn = ch->co_next; i = doblock(ch, &nn); if (*i > 2) { cn = findlabel(i, bl->co_elseblock); if (!cn)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -