aboutsummaryrefslogtreecommitdiffstats
path: root/eval.c
diff options
context:
space:
mode:
authorArnold D. Robbins <arnold@skeeve.com>2011-08-10 22:36:00 +0300
committerArnold D. Robbins <arnold@skeeve.com>2011-08-10 22:36:00 +0300
commitb8c993f5a867f38fa9edd343d7d90b4b36800be2 (patch)
tree75d48551001252a7b78469634d5f5dc11b2a6754 /eval.c
parent0d1a4f152fa45eda08b5877e4989c3250beb280e (diff)
downloadegawk-b8c993f5a867f38fa9edd343d7d90b4b36800be2.tar.gz
egawk-b8c993f5a867f38fa9edd343d7d90b4b36800be2.tar.bz2
egawk-b8c993f5a867f38fa9edd343d7d90b4b36800be2.zip
BEGINFILE/ENDFILE code redone.
Diffstat (limited to 'eval.c')
-rw-r--r--eval.c378
1 files changed, 257 insertions, 121 deletions
diff --git a/eval.c b/eval.c
index ea45efd9..0a4a7386 100644
--- a/eval.c
+++ b/eval.c
@@ -30,7 +30,7 @@ extern double pow(double x, double y);
extern double modf(double x, double *yp);
extern double fmod(double x, double y);
NODE **fcall_list;
-long fcall_count;
+long fcall_count = 0;
int currule = 0;
IOBUF *curfile = NULL; /* current data file */
int exiting = FALSE;
@@ -678,7 +678,7 @@ pop_frame()
#endif
}
#else /* not PROFILING or DEBUGGING */
-#define push_frame(p) /* nothing */
+#define push_frame(p) /* nothing */
#define pop_frame() /* nothing */
#endif
@@ -690,7 +690,6 @@ pop_frame()
void
dump_fcall_stack(FILE *fp)
{
-
NODE *f, *func;
long i = 0;
@@ -1150,12 +1149,6 @@ r_get_lhs(NODE *n, int reference)
case Node_var:
break;
-#if 0
- case Node_builtin:
- /* in gawk for a while */
- fatal(_("assignment is not allowed to result of builtin function"));
-#endif
-
default:
cant_happen();
}
@@ -1242,7 +1235,7 @@ calc_exp(AWKNUM x1, AWKNUM x2)
/* setup_frame --- setup new frame for function call */
-static void
+static INSTRUCTION *
setup_frame(INSTRUCTION *pc)
{
NODE *r = NULL;
@@ -1329,18 +1322,23 @@ setup_frame(INSTRUCTION *pc)
DEREF(r);
}
+ frame_ptr->vname = source; /* save current source */
+
push_frame(frame_ptr);
/* save current frame in stack */
PUSH(frame_ptr);
+
/* setup new frame */
getnode(frame_ptr);
frame_ptr->type = Node_frame;
frame_ptr->stack = sp;
+ frame_ptr->prev_frame_size = (stack_ptr - stack_bottom); /* size of the previous stack frame */
frame_ptr->func_node = f;
frame_ptr->vname = NULL;
+ frame_ptr->reti = pc; /* on return execute pc->nexti */
- frame_ptr->reti = (unsigned long) pc; /* on return execute pc->nexti */
+ return f->code_ptr;
}
@@ -1369,13 +1367,18 @@ restore_frame(NODE *fp)
}
if (frame_ptr->stack != NULL)
efree(frame_ptr->stack);
- ri = (INSTRUCTION *) frame_ptr->reti; /* execution in calling frame
- * resumes from ri->nexti.
- */
+ ri = frame_ptr->reti; /* execution in calling frame
+ * resumes from ri->nexti.
+ */
freenode(frame_ptr);
pop_frame();
+ /* restore frame */
frame_ptr = fp;
+ /* restore source */
+ source = fp->vname;
+ fp->vname = NULL;
+
return ri->nexti;
}
@@ -1395,33 +1398,40 @@ free_arrayfor(NODE *r)
freenode(r);
}
-/* unwind_stack --- pop the runtime stack */
-void
-unwind_stack(STACK_ITEM *sp_bottom)
+/* unwind_stack --- pop items off the run-time stack;
+ * 'n' is the # of items left in the stack.
+ */
+
+INSTRUCTION *
+unwind_stack(long n)
{
NODE *r;
+ INSTRUCTION *cp = NULL;
+ STACK_ITEM *sp;
- while (stack_ptr >= sp_bottom) {
- r = POP();
- switch (r->type) {
- case Node_instruction:
- freenode(r);
- break;
+ if (stack_empty())
+ return NULL;
+
+ sp = stack_bottom + n;
+
+ if (stack_ptr < sp)
+ return NULL;
+ while (r = POP()) {
+ switch (r->type) {
case Node_frame:
- (void) restore_frame(r);
- source = frame_ptr->vname;
+ cp = restore_frame(r);
break;
-
case Node_arrayfor:
free_arrayfor(r);
break;
-
case Node_val:
DEREF(r);
break;
-
+ case Node_instruction:
+ freenode(r);
+ break;
default:
if (in_main_context())
fatal(_("unwind_stack: unexpected type `%s'"),
@@ -1435,8 +1445,19 @@ unwind_stack(STACK_ITEM *sp_bottom)
*/
break;
}
+
+ if (stack_ptr < sp)
+ break;
}
-}
+ return cp;
+}
+
+
+/* pop_fcall --- pop off the innermost frame */
+#define pop_fcall() unwind_stack(frame_ptr->prev_frame_size)
+
+/* pop the run-time stack */
+#define pop_stack() (void) unwind_stack(0)
/*
@@ -1563,6 +1584,75 @@ POP_CODE()
}
+/* Implementation of BEGINFILE and ENDFILE requires saving an execution
+ * state and the ability to return to that state. The state is
+ * defined by the instruction triggering the BEGINFILE/ENDFILE rule, the
+ * run-time stack, the rule and the source file. The source line is available in
+ * the instruction and hence is not considered a part of the execution state.
+ */
+
+
+typedef struct exec_state {
+ struct exec_state *next;
+
+ INSTRUCTION *cptr; /* either getline (Op_K_getline) or the
+ * implicit "open-file, read-record" loop (Op_newfile).
+ */
+
+ int rule; /* rule for the INSTRUCTION */
+
+ long stack_size; /* For this particular usage, it is sufficient to save
+ * only the size of the call stack. We do not
+ * store the actual stack pointer to avoid problems
+ * in case the stack gets realloc-ed.
+ */
+
+ const char *source; /* source file for the INSTRUCTION */
+} EXEC_STATE;
+
+static EXEC_STATE exec_state_stack;
+
+/* push_exec_state --- save an execution state on stack */
+
+static void
+push_exec_state(INSTRUCTION *cp, int rule, char *src, STACK_ITEM *sp)
+{
+ EXEC_STATE *es;
+
+ emalloc(es, EXEC_STATE *, sizeof(EXEC_STATE), "push_exec_state");
+ es->rule = rule;
+ es->cptr = cp;
+ es->stack_size = (sp - stack_bottom) + 1;
+ es->source = src;
+ es->next = exec_state_stack.next;
+ exec_state_stack.next = es;
+}
+
+
+/* pop_exec_state --- pop one execution state off the stack */
+
+static INSTRUCTION *
+pop_exec_state(int *rule, char **src, long *sz)
+{
+ INSTRUCTION *cp;
+ EXEC_STATE *es;
+
+ es = exec_state_stack.next;
+ if (es == NULL)
+ return NULL;
+ cp = es->cptr;
+ if (rule != NULL)
+ *rule = es->rule;
+ if (src != NULL)
+ *src = (char *) es->source;
+ if (sz != NULL)
+ *sz = es->stack_size;
+ exec_state_stack.next = es->next;
+ efree(es);
+ return cp;
+}
+
+
/*
* r_interpret:
* code is a list of instructions to run. returns the exit value
@@ -1598,6 +1688,7 @@ r_interpret(INSTRUCTION *code)
#endif
int stdio_problem = FALSE;
+
if (args_array == NULL)
emalloc(args_array, NODE **, (max_args + 2)*sizeof(NODE *), "r_interpret");
else
@@ -2227,7 +2318,7 @@ post:
/*
* Actual array for use in lint warning
* in Op_arrayfor_incr
- */
+ */
list[num_elems] = array;
arrayfor:
@@ -2383,10 +2474,8 @@ match_re:
/* save current frame along with source */
func_call:
- frame_ptr->vname = source; /* save current source */
- setup_frame(pc);
-
- ni = f->code_ptr; /* function code */
+ ni = setup_frame(pc);
+
if (ni->opcode == Op_ext_func) {
/* dynamically set source and line numbers for an extension builtin. */
ni->source_file = source;
@@ -2401,29 +2490,11 @@ func_call:
case Op_K_return:
m = POP_SCALAR(); /* return value */
- r = POP();
- while (r->type != Node_frame) {
- switch (r->type) {
- case Node_arrayfor:
- free_arrayfor(r);
- break;
- case Node_val:
- DEREF(r);
- break;
- case Node_instruction:
- freenode(r);
- break;
- default:
- break;
- }
- r = POP();
- }
-
- ni = restore_frame(r);
- source = frame_ptr->vname;
-
+ ni = pop_fcall();
+
/* put the return value back on stack */
PUSH(m);
+
JUMPTO(ni);
case Op_K_getline_redir:
@@ -2439,13 +2510,20 @@ func_call:
if (currule == BEGINFILE || currule == ENDFILE)
fatal(_("non-redirected `getline' invalid inside `%s' rule"),
ruletab[currule]);
+
do {
int ret;
- ret = nextfile(&curfile, FALSE);
+ ret = nextfile(& curfile, FALSE);
if (ret <= 0)
r = do_getline(pc->into_var, curfile);
else {
- PUSH_CODE(pc);
+
+ /* Save execution state so that we can return to it
+ * from Op_after_beginfile or Op_after_endfile.
+ */
+
+ push_exec_state(pc, currule, source, stack_ptr);
+
if (curfile == NULL)
JUMPTO((pc + 1)->target_endfile);
else
@@ -2457,84 +2535,125 @@ func_call:
break;
case Op_after_endfile:
- ni = POP_CODE();
+ /* Find the execution state to return to */
+ ni = pop_exec_state(& currule, & source, NULL);
+
assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline);
JUMPTO(ni);
case Op_after_beginfile:
- after_beginfile(&curfile);
- ni = POP_CODE();
+ after_beginfile(& curfile);
+
+ /* Find the execution state to return to */
+ ni = pop_exec_state(& currule, & source, NULL);
+
+ assert(ni->opcode == Op_newfile || ni->opcode == Op_K_getline);
if (ni->opcode == Op_K_getline
|| curfile == NULL /* skipping directory argument */
)
JUMPTO(ni);
- PUSH_CODE(ni); /* for use in Op_K_nextfile and Op_get_record */
- break; /* Op_get_record */
+
+ break; /* read a record, Op_get_record */
case Op_newfile:
{
int ret;
- ret = nextfile(&curfile, FALSE);
- if (ret < 0)
- JUMPTO(pc->target_jmp); /* end block or Op_atexit */
- else if (ret > 0) {
- PUSH_CODE(pc);
- if (curfile == NULL)
- JUMPTO(pc->target_endfile);
- break; /* beginfile block */
- } else
- PUSH_CODE(pc);
- /* fall through */
+
+ ret = nextfile(& curfile, FALSE);
+
+ if (ret < 0) /* end of input */
+ JUMPTO(pc->target_jmp); /* end block or Op_atexit */
+
+ if (ret == 0) /* read a record */
+ JUMPTO((pc + 1)->target_get_record);
+
+ /* ret > 0 */
+ /* Save execution state for use in Op_after_beginfile or Op_after_endfile. */
+
+ push_exec_state(pc, currule, source, stack_ptr);
+
+ if (curfile == NULL) /* EOF */
+ JUMPTO(pc->target_endfile);
+ /* else
+ execute beginfile block */
}
+ break;
- case Op_get_record:
- if (curfile == NULL) { /* from getline without redirection */
- ni = POP_CODE(); /* Op_newfile */
- ni = ni->target_jmp; /* end_block or Op_atexit */
- } else if (inrec(curfile) == 0)
- break; /* prog(rule) block */
- else
- ni = POP_CODE(); /* Op_newfile */
- JUMPTO(ni);
+ case Op_get_record:
+ {
+ int errcode = 0;
+
+ ni = pc->target_newfile;
+ if (curfile == NULL) {
+ /* from non-redirected getline, e.g.:
+ * {
+ * while (getline > 0) ;
+ * }
+ */
+
+ ni = ni->target_jmp; /* end_block or Op_atexit */
+ JUMPTO(ni);
+ }
+
+ if (inrec(curfile, & errcode) != 0) {
+ if (errcode > 0 && (do_traditional || ! pc->has_endfile))
+ fatal(_("error reading input file `%s': %s"),
+ curfile->name, strerror(errcode));
+
+ JUMPTO(ni);
+ } /* else
+ prog (rule) block */
+ }
+ break;
case Op_K_nextfile:
+ {
+ int ret;
+
if (currule != Rule && currule != BEGINFILE)
- fatal(_("`nextfile' cannot be called from a `%s' rule"), ruletab[currule]);
- (void) nextfile(&curfile, TRUE);
- while (currule == BEGINFILE) {
- r = POP();
- switch (r->type) {
- case Node_instruction:
- ni = r->code_ptr;
- freenode(r);
- if (ni->opcode == Op_newfile || ni->opcode == Op_K_getline)
- JUMPTO(ni);
- break;
- case Node_frame:
- (void) restore_frame(r);
- source = frame_ptr->vname;
- break;
- case Node_arrayfor:
- free_arrayfor(r);
- break;
- case Node_val:
- DEREF(r);
- break;
- default:
- break;
- }
- }
+ fatal(_("`nextfile' cannot be called from a `%s' rule"),
+ ruletab[currule]);
- assert(currule != BEGINFILE);
- unwind_stack(stack_bottom + 1); /* don't pop Op_newfile */
- JUMPTO(pc->target_endfile); /* endfile block */
+ ret = nextfile(& curfile, TRUE); /* skip current file */
- case Op_K_exit:
- if (currule == END)
- ni = pc->target_atexit;
- else
- ni = pc->target_end;
+ if (currule == BEGINFILE) {
+ long stack_size;
+
+ ni = pop_exec_state(& currule, & source, & stack_size);
+
+ assert(ni->opcode == Op_K_getline || ni->opcode == Op_newfile);
+
+ /* pop stack returning to the state of Op_K_getline or Op_newfile. */
+ unwind_stack(stack_size);
+
+ if (ret == 0) {
+ /* There was an error opening the file;
+ * don't run ENDFILE block(s).
+ */
+
+ JUMPTO(ni);
+ } else {
+ /* do run ENDFILE block(s) first. */
+
+ /* Execution state to return to in Op_after_endfile. */
+ push_exec_state(ni, currule, source, stack_ptr);
+
+ JUMPTO(pc->target_endfile);
+ }
+ } /* else
+ Start over with the first rule. */
+
+ /* empty the run-time stack to avoid memory leak */
+ pop_stack();
+
+ /* Push an execution state for Op_after_endfile to return to */
+ push_exec_state(pc->target_newfile, currule, source, stack_ptr);
+ JUMPTO(pc->target_endfile);
+ }
+ break;
+
+ case Op_K_exit:
exiting = TRUE;
POP_NUMBER(x1);
exit_val = (int) x1;
@@ -2546,19 +2665,36 @@ func_call:
/* else
just pass anything else on through */
#endif
- /* jump to either the first end_block instruction
- * or to Op_atexit
+
+ if (currule == BEGINFILE || currule == ENDFILE) {
+
+ /* Find the rule of the saved execution state (Op_K_getline/Op_newfile).
+ * This is needed to prevent multiple execution of any END rules:
+ * gawk 'BEGINFILE { exit(1) } \
+ * END { while (getline > 0); }' in1 in2
+ */
+
+ (void) pop_exec_state(& currule, & source, NULL);
+ }
+
+ pop_stack(); /* empty stack, don't leak memory */
+
+ /* Jump to either the first END block instruction
+ * or to Op_atexit.
*/
- unwind_stack(stack_bottom);
+
+ if (currule == END)
+ ni = pc->target_atexit;
+ else
+ ni = pc->target_end;
JUMPTO(ni);
case Op_K_next:
if (currule != Rule)
fatal(_("`next' cannot be called from a `%s' rule"), ruletab[currule]);
- /* jump to Op_get_record */
- unwind_stack(stack_bottom + 1); /* don't pop Op_newfile */
- JUMPTO(pc->target_jmp);
+ pop_stack();
+ JUMPTO(pc->target_jmp); /* Op_get_record, read next record */
case Op_pop:
#if defined(GAWKDEBUG) || defined(ARRAYDEBUG)