/*************************************************************** bwb_cnd.c Conditional Expressions and Commands for Bywater BASIC Interpreter Copyright (c) 1993, Ted A. Campbell Bywater Software email: tcamp@delphi.com Copyright and Permissions Information: All U.S. and international rights are claimed by the author, Ted A. Campbell. This software is released under the terms of the GNU General Public License (GPL), which is distributed with this software in the file "COPYING". The GPL specifies the terms under which users may copy and use the software in this distribution. A separate license is available for commercial distribution, for information on which you should contact the author. ***************************************************************/ /*---------------------------------------------------------------*/ /* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */ /* 11/1995 (eidetics@cerf.net). */ /* */ /* Those additionally marked with "DD" were at the suggestion of */ /* Dale DePriest (daled@cadence.com). */ /* */ /* Version 3.00 by Howard Wulf, AF5NE */ /* */ /*---------------------------------------------------------------*/ #include "bwbasic.h" /* declarations of functions visible to this file only */ static int cnd_thenels(char *buffer, int position, int *then, int *els); static int cnd_tostep(char *buffer, int position, int *to, int *step); static int case_eval(struct exp_ese * expression, struct exp_ese * minval, struct exp_ese * maxval); static int FindTopLineOnStack(struct bwb_line * l); static int FindBottomLineOnStack(struct bwb_line * l); static struct bwb_line * FindExitLineOnStack(struct bwb_line * l); static struct bwb_line * find_NextTestInCode(struct bwb_line * l); /* FIXME: DELETE should remove DEF FN, FUNCTIONS, and SUBROUTINES in the deleted line range. */ /* FIXME: add Variable Range checking, based upon type characters $, @, %, &, !, # */ /* FIXME: check EOF, LOF, LOC, SEEK, such as QB45 */ /* FIXME: centralize Variable/Function/Command name character checking, so we can configure it with OPTION VERSION */ /* -------------------------------------------------------------------------------------------- FUNCTION - END FUNCTION -------------------------------------------------------------------------------------------- */ struct bwb_line * bwb_FUNCTION(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check current exec level */ if (CURTASK exsc == 0) { /* skip over the entire function definition */ return l->OtherLine->next; /* line after END SUB */ } /* we are being executed via fnc_deffn() */ /* if this is the first time at this SUB statement, note it */ if (CURTASK excs[CURTASK exsc].LoopTopLine != l) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the END SUB statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);; if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("FUNCTION without END FUNCTION"); return bwb_zline(l); } } adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } struct bwb_line * bwb_EXIT_FUNCTION(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); next_line = FindExitLineOnStack(l); if (next_line == NULL) { bwb_error("EXIT FUNCTION without FUNCTION"); return bwb_zline(l); } { /* EXIT FUNCTION */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } #if 0 /* set the next line in the exec stack */ next_line->position = 0; bwb_setexec(next_line, 0, EXEC_FUNCTION); return next_line; #endif } struct bwb_line * bwb_END_FUNCTION(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of SUB commmand */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("END FUNCTION without FUNCTION"); return bwb_zline(l); } /* decrement the stack */ bwb_decexec(); /* and return next from old line */ CURTASK excs[CURTASK exsc].line->next->position = 0; return CURTASK excs[CURTASK exsc].line->next; } /* -------------------------------------------------------------------------------------------- SUB - END SUB -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_sub() DESCRIPTION: This function implements the BASIC SUB command, introducing a named subroutine. SYNTAX: SUB subroutine-name ... [ EXIT SUB ] ... END SUB ***************************************************************/ struct bwb_line * bwb_SUB(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check current exec level */ if (CURTASK exsc == 0) { /* skip over the entire function definition */ return l->OtherLine->next; /* line after END SUB */ } /* we are being executed via fnc_deffn() */ /* if this is the first time at this SUB statement, note it */ if (CURTASK excs[CURTASK exsc].LoopTopLine != l) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the END SUB statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);; if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("SUB without END SUB"); return bwb_zline(l); } } adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_endsub() DESCRIPTION: This C function implements the BASIC END SUB command, ending a subroutine definition. Because the command END can have multiple meanings, this function should be called from the bwb_xend() function, which should be able to identify an END SUB command. SYNTAX: END SUB ***************************************************************/ struct bwb_line * bwb_EXIT_SUB(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); next_line = FindExitLineOnStack(l); if (next_line == NULL) { bwb_error("EXIT SUB without SUB"); return bwb_zline(l); } { /* EXIT SUB */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } #if 0 /* set the next line in the exec stack */ next_line->position = 0; bwb_setexec(next_line, 0, EXEC_FUNCTION); return next_line; #endif } struct bwb_line * bwb_END_SUB(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of SUB commmand */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("END SUB without SUB"); return bwb_zline(l); } /* decrement the stack */ bwb_decexec(); /* and return next from old line */ CURTASK excs[CURTASK exsc].line->next->position = 0; return CURTASK excs[CURTASK exsc].line->next; } /* -------------------------------------------------------------------------------------------- IF - END IF -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_IF() DESCRIPTION: This function handles the BASIC IF statement, standard flavor. SYNTAX: IF expression THEN line [ELSE line] ***************************************************************/ struct bwb_line * bwb_IF(struct bwb_line * l) { /* classic IF */ int then, els; struct exp_ese *e; int tpos; char tbuf[BasicStringLengthMax + 1]; int Value; bwx_DEBUG(__FUNCTION__); /* Call bwb_exp() to evaluate the condition. This should return with * position set to the "THEN" statement */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } Value = exp_getival(e); /* test for "THEN" and "ELSE" statements */ cnd_thenels(l->buffer, l->position, &then, &els); /* evaluate and execute */ if (Value != 0) { /* expression is TRUE */ if (then == FALSE) { /* syntac error */ bwb_error(err_syntax); return bwb_zline(l); } else { /* check for THEN followed by literal line number */ struct bwb_line *x; tpos = then + strlen("THEN") + 1; adv_element(l->buffer, &tpos, tbuf); /* check for target label */ x = find_label(tbuf); if (x != NULL) { adv_eos(l->buffer, &(l->position)); x->position = 0; return x; } /* syntac error */ bwb_error(err_syntax); return bwb_zline(l); } } else { /* expression is FALSE */ if (els == FALSE) { /* optional */ adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } else { /* check for ELSE followed by literal line number */ struct bwb_line *x; tpos = els + strlen("ELSE") + 1; adv_element(l->buffer, &tpos, tbuf); /* check for target label */ x = find_label(tbuf); if (x != NULL) { adv_eos(l->buffer, &(l->position)); x->position = 0; return x; } /* syntac error */ bwb_error(err_syntax); return bwb_zline(l); } } return bwb_zline(l); } /*************************************************************** FUNCTION: cnd_thenelse() DESCRIPTION: This function searches through the beginning at point and attempts to find positions of THEN and ELSE statements. ***************************************************************/ static int cnd_thenels(char *buffer, int position, int *then, int *els) { int loop, t_pos, b_pos, p_word; char tbuf[BasicStringLengthMax + 1]; bwx_DEBUG(__FUNCTION__); /* set then and els to 0 initially */ *then = *els = 0; /* loop to find words */ p_word = b_pos = position; t_pos = 0; tbuf[0] = '\0'; loop = TRUE; while (loop == TRUE) { char c; c = buffer[b_pos]; if (c == '\0' || c == OptionCommentChar || c == ' ') { if (strncasecmp(tbuf, "THEN", (size_t) strlen("THEN")) == 0) { *then = p_word; } else if (strncasecmp(tbuf, "GOTO", (size_t) strlen("GOTO")) == 0) { *then = p_word; } else if (strncasecmp(tbuf, "ELSE", (size_t) strlen("ELSE")) == 0) { *els = p_word; } /* check for end of the line */ if (c == '\0' || c == OptionCommentChar) { return TRUE; } ++b_pos; p_word = b_pos; t_pos = 0; tbuf[0] = '\0'; } else { tbuf[t_pos] = c; ++b_pos; ++t_pos; tbuf[t_pos] = '\0'; } } return FALSE; } /*************************************************************** FUNCTION: bwb_IF_THEN() DESCRIPTION: This function handles the BASIC IF statement, structured flavor. SYNTAX: IF expression THEN ... END IF ***************************************************************/ struct bwb_line * bwb_IF_THEN(struct bwb_line * l) { /* structured IF */ struct exp_ese *e; struct bwb_line *else_line; struct bwb_line *endif_line; int Value; bwx_DEBUG(__FUNCTION__); /* if this is the first time at this IF statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the LOOP statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);; if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("IF without END IF"); return bwb_zline(l); } } /* Call bwb_exp() to evaluate the condition. This should return with * position set to the "THEN" statement */ /* FIXME: extract the expression before calling bwb_exp() - no COMMANDS should be there */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } Value = exp_getival(e); /* evaluate the expression */ if (Value != 0) { CURTASK excs[CURTASK exsc].code = EXEC_IFTRUE; /* IF has been processed */ /* fall thru to execute this code block */ adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } CURTASK excs[CURTASK exsc].code = EXEC_IFFALSE; /* IF has NOT been * processed */ else_line = find_NextTestInCode(l); if (else_line != NULL) { else_line->position = 0; return else_line; } endif_line = CURTASK excs[CURTASK exsc].LoopBottomLine; endif_line->position = 0; return endif_line; } /*************************************************************** FUNCTION: bwb_else() DESCRIPTION: This function handles the BASIC ELSE statement. SYNTAX: ELSE ***************************************************************/ struct bwb_line * bwb_ELSE(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* there are two legitamate ways to get here: 1. a fall thru from an * upper block, code == EXEC_IFTRUE we should jump to the END IF 2. a * jump from from an upper block, code == EXEC_IFFALSE we should * execute our code block */ next_line = FindExitLineOnStack(l); /* END IF */ if (next_line == NULL) { bwb_error("ELSE without IF"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_IFTRUE: /* IF has been processed */ /* jump to the END IF */ next_line->position = 0; return next_line; break; case EXEC_IFFALSE: CURTASK excs[CURTASK exsc].code = EXEC_IFTRUE; /* IF has been processed */ /* fall thru to execute this code block */ break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("ELSE without IF"); break; } return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_elseif() DESCRIPTION: This function handles the BASIC ELSEIF statement. SYNTAX: ELSEIF ***************************************************************/ struct bwb_line * bwb_ELSEIF(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* there are two legitamate ways to get here: 1. a fall thru from an * upper block, code == EXEC_IFTRUE we should jump to the END IF 2. a * jump from from an upper block, code == EXEC_IFFALSE we should * execute our code block */ next_line = FindExitLineOnStack(l); /* END IF */ if (next_line == NULL) { bwb_error("ELSEIF without IF"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_IFTRUE: /* IF has been processed */ /* jump to the END IF */ next_line->position = 0; return next_line; break; case EXEC_IFFALSE: /* execute our code block (maybe) */ { struct bwb_line *else_line; struct exp_ese *e; /* Call bwb_exp() to evaluate the condition. This * should return with position set to the "THEN" * statement */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) != FALSE) { /* condition is TRUE: proceed to the next * line */ CURTASK excs[CURTASK exsc].code = EXEC_IFTRUE; /* IF has been processed */ /* fall thru to execute this code block */ adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } /* condition is FALSE */ /* proceed to next ELSE line if there is one */ else_line = find_NextTestInCode(l); /* ELSEIF or ELSE */ if (else_line != NULL) { else_line->position = 0; return else_line; } /* no more ELSExx lines */ /* jump to the END IF */ next_line->position = 0; return next_line; } break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("ELSEIF without IF"); break; } return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_endif() DESCRIPTION: This function handles the BASIC END IF statement. SYNTAX: END IF ***************************************************************/ struct bwb_line * bwb_END_IF(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of IF commmand */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("END IF without IF"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_IFTRUE: /* IF has been processed */ break; case EXEC_IFFALSE: break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("ENDIF without IF"); } /* remove IF from the stack (NEW) */ bwb_decexec(); bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code); return bwb_zline(l); } /* -------------------------------------------------------------------------------------------- SELECT CASE - END SELECT -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_select() DESCRIPTION: This C function handles the BASIC SELECT statement. SYNTAX: SELECT CASE expression ***************************************************************/ struct bwb_line * bwb_SELECT(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); bwb_error("SELECT without CASE"); return bwb_zline(l); } struct bwb_line * bwb_SELECT_CASE(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); /* if this is the first time at this SELECT CASE statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the LOOP statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l); if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("SELECT CASE without END SELECT"); return bwb_zline(l); } } /* increment the level and set to EXEC_SELFALSE */ CURTASK excs[CURTASK exsc].code = EXEC_SELFALSE; /* evaluate the expression at this level */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (e->type == STRING) { CURTASK excs[CURTASK exsc].expression.type = STRING; str_btob(&(CURTASK excs[CURTASK exsc].expression.sval), &(e->sval)); } else { CURTASK excs[CURTASK exsc].expression.type = NUMBER; CURTASK excs[CURTASK exsc].expression.nval = exp_getnval(e); } /* return */ adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_case() DESCRIPTION: This C function handles the BASIC CASE statement. SYNTAX: CASE constant | IF partial-expression | ELSE ***************************************************************/ struct bwb_line * bwb_CASE_ELSE(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* there are two legitamate ways to get here: 1. a fall thru from an * upper block, code == EXEC_SELTRUE we should jump to the END SELECT * 2. a jump from from an upper block, code == EXEC_SELFALSE we * should execute our code block */ next_line = FindExitLineOnStack(l); /* END SELECT */ if (next_line == NULL) { bwb_error("CASE ELSE without SELECT CASE"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_SELTRUE: /* SELECT has been processed */ /* jump to the END SELECT */ next_line->position = 0; return next_line; break; case EXEC_SELFALSE: CURTASK excs[CURTASK exsc].code = EXEC_SELTRUE; /* SELECT has been * processed */ /* fall thru to execute this code block */ break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("CASE ELSE without SELECT CASE"); break; } return bwb_zline(l); } struct bwb_line * bwb_CASE(struct bwb_line * l) /* TODO - FIXME */ { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* there are two legitamate ways to get here: 1. a fall thru from an * upper block, code == EXEC_SELTRUE we should jump to the END SELECT * 2. a jump from from an upper block, code == EXEC_SELFALSE we * should execute our code block */ next_line = FindExitLineOnStack(l); /* END SELECT */ if (next_line == NULL) { bwb_error("CASE without SELECT CASE"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_SELTRUE: /* SELECT has been processed */ /* jump to the END SELECT */ next_line->position = 0; return next_line; break; case EXEC_SELFALSE: /* fall thru to execute this code block (maybe) */ { struct exp_ese minvalue; struct exp_ese *minval; struct bwb_line *case_line; int IsExpression; IsExpression = FALSE; minval = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } memcpy(&minvalue, minval, sizeof(struct exp_ese)); minval = &minvalue; /* check for string value */ if (minvalue.type == STRING) { /* STRING */ bstring *a; bstring *b; a = &(CURTASK excs[CURTASK exsc].expression.sval); b = &(minvalue.sval); if (str_cmp(a, b) == 0) { IsExpression = TRUE; } } /* STRING */ else { /* NUMBER */ struct exp_ese *maxval; maxval = minval; /* not a string; advance */ adv_ws(l->buffer, &(l->position)); /* check for TO */ if (is_eol(l->buffer, &(l->position)) != TRUE) { char tbuf[BasicStringLengthMax + 1]; /* find the TO statement */ adv_element(l->buffer, &(l->position), tbuf); if (strcasecmp(tbuf, "TO") != 0) { sprintf(bwb_ebuf, "CASE has inexplicable code following expression"); bwb_error(bwb_ebuf); adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } /* now evaluate the MAX expression */ maxval = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } } /* evaluate the expression */ if (case_eval(&(CURTASK excs[CURTASK exsc].expression), minval, maxval) == TRUE) { IsExpression = TRUE; } } /* NUMBER */ if (IsExpression == TRUE) { /* condition is TRUE: proceed to the next * line */ CURTASK excs[CURTASK exsc].code = EXEC_SELTRUE; /* SELECT has been * processed */ /* fall thru to execute this code block */ adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } /* evaluation returns a FALSE value; find next CASExx * statement */ case_line = find_NextTestInCode(l); /* CASE or CASE IF or * CASE ELSE */ if (case_line != NULL) { case_line->position = 0; return case_line; } /* no more CASExx lines */ /* jump to the END SELECT */ next_line->position = 0; return next_line; } break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("CASE without SELECT CASE"); break; } return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_caseif() DESCRIPTION: This C function handles the BASIC CASE IF statement. ***************************************************************/ struct bwb_line * bwb_CASE_IS(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); return bwb_CASE_IF(l); } struct bwb_line * bwb_CASE_IF(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* there are two legitamate ways to get here: 1. a fall thru from an * upper block, code == EXEC_SELTRUE we should jump to the END SELECT * 2. a jump from from an upper block, code == EXEC_SELFALSE we * should execute our code block */ next_line = FindExitLineOnStack(l); /* END SELECT */ if (next_line == NULL) { bwb_error("CASE IF without SELECT CASE"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_SELTRUE: /* IF has been processed */ /* jump to the END IF */ next_line->position = 0; return next_line; break; case EXEC_SELFALSE: /* execute our code block (maybe) */ { char tbuf[BasicStringLengthMax + 1]; int position; struct exp_ese *r; struct bwb_line *case_line; if (CURTASK excs[CURTASK exsc].expression.type == NUMBER) { sprintf(tbuf, BasicNumberPrintFormat " %s", CURTASK excs[CURTASK exsc].expression.nval, &(l->buffer[l->position])); } else { bwb_error(err_mismatch); adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } position = 0; r = bwb_exp(tbuf, FALSE, &position); if (ERROR_PENDING) { return bwb_zline(l); } if (r->nval == TRUE) { /* condition is TRUE: proceed to the next * line */ CURTASK excs[CURTASK exsc].code = EXEC_SELTRUE; /* SELECT has been * processed */ /* fall thru to execute this code block */ adv_eos(l->buffer, &(l->position)); return bwb_zline(l); } /* condition is FALSE */ /* proceed to next CASE line if there is one */ case_line = find_NextTestInCode(l); /* CASE IF or CASE ELSE */ if (case_line != NULL) { case_line->position = 0; return case_line; } /* no more CASExx lines */ /* jump to the END SELECT */ next_line->position = 0; return next_line; } break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("CASE IF without SELECT CASE"); break; } return bwb_zline(l); } /*************************************************************** FUNCTION: case_eval() DESCRIPTION: This function evaluates a case statement by comparing minimum and maximum values with a set expression. It returns either TRUE or FALSE ***************************************************************/ static int case_eval(struct exp_ese * expression, struct exp_ese * minval, struct exp_ese * maxval) { bwx_DEBUG(__FUNCTION__); /* string value */ if (expression->type == STRING) { bwb_error(err_mismatch); return FALSE; } /* numerical value */ if ((expression->nval >= minval->nval) && (expression->nval <= maxval->nval)) { return TRUE; } return FALSE; } /*************************************************************** FUNCTION: bwb_endselect() DESCRIPTION: This function handles the BASIC END SELECT statement. SYNTAX: END SELECT ***************************************************************/ struct bwb_line * bwb_END_SELECT(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of SELECT CASE commmand */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("END SELECT without SELECT CASE"); return bwb_zline(l); } switch (CURTASK excs[CURTASK exsc].code) { case EXEC_SELTRUE: /* SELECT has been processed */ break; case EXEC_SELFALSE: break; default: /* the BASIC program has jumped into the middle of a * structured command */ bwb_error("END SELECT without SELECT CASE"); } /* remove SELECT from the stack */ bwb_decexec(); bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code); return bwb_zline(l); } /* -------------------------------------------------------------------------------------------- DO - LOOP -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_DO() DESCRIPTION: This C function implements the ANSI BASIC DO statement, when DO is not followed by an argument. It is called by bwb_do() in bwb_cmd.c. SYNTAX: DO ' forever ***************************************************************/ struct bwb_line * bwb_DO(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* if this is the first time at this DO statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the LOOP statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);; if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("DO without LOOP"); return bwb_zline(l); } } bwb_setexec(l, l->position, EXEC_DO); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_DO_UNTIL() DESCRIPTION: This C function implements the ANSI BASIC DO UNTIL statement, when DO is not followed by an argument. It is called by bwb_do() in bwb_cmd.c. SYNTAX: DO UNTIL expression ' exit when X <> 0 ***************************************************************/ struct bwb_line * bwb_DO_UNTIL(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); /* if this is the first time at this WHILE statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the UEND statement (or LOOP statement) */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l); if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("DO without LOOP"); return bwb_zline(l); } } /*----------------------------------------------------*/ /* Expression evaluation was at the top of bwb_while, */ /* and the init portion was performed only if TRUE. */ /* The init routine should be performed regardless of */ /* expression value, else a segmentation fault can */ /* occur! (JBV) */ /*----------------------------------------------------*/ /* call bwb_exp() to interpret the expression */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) != FALSE) { /* EXIT DO */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } /* DO UNTIL ... */ bwb_setexec(l, l->position, EXEC_DO); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_DO_WHILE() DESCRIPTION: This C function implements the ANSI BASIC DO statement, when DO is not followed by an argument. It is called by bwb_do() in bwb_cmd.c. SYNTAX: DO WHILE expression ' exit when X = 0 ***************************************************************/ struct bwb_line * bwb_DO_WHILE(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); /* if this is the first time at this WHILE statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the LOOP statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l); if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("DO without LOOP"); return bwb_zline(l); } } /*----------------------------------------------------*/ /* Expression evaluation was at the top of bwb_while, */ /* and the init portion was performed only if TRUE. */ /* The init routine should be performed regardless of */ /* expression value, else a segmentation fault can */ /* occur! (JBV) */ /*----------------------------------------------------*/ /* call bwb_exp() to interpret the expression */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) == FALSE) { /* EXIT DO */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } /* DO WHILE ... */ bwb_setexec(l, l->position, EXEC_DO); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_EXIT_DO() DESCRIPTION: This function handles the BASIC EXIT DO statement. This is a structured programming command compatible with ANSI BASIC. It is called from the bwb_exit() subroutine. ***************************************************************/ struct bwb_line * bwb_EXIT_DO(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); next_line = FindExitLineOnStack(l); if (next_line == NULL) { bwb_error("EXIT DO without DO"); return bwb_zline(l); } { /* EXIT DO */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } #if 0 next_line = next_line->next; /* set the next line in the exec stack */ next_line->position = 0; bwb_setexec(next_line, 0, EXEC_DO); return next_line; #endif } /*************************************************************** FUNCTION: bwb_LOOP() DESCRIPTION: This C function implements the ANSI BASIC LOOP statement. SYNTAX: LOOP ' forever ***************************************************************/ struct bwb_line * bwb_LOOP(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of DO loop */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("LOOP without DO"); return bwb_zline(l); } /* reset to the top of the current DO loop */ CURTASK excs[CURTASK exsc].LoopTopLine->position = 0; bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_DO); return CURTASK excs[CURTASK exsc].LoopTopLine; } /*************************************************************** FUNCTION: bwb_LOOP_UNTIL() DESCRIPTION: This C function implements the ANSI BASIC LOOP UNTIL statement and is called by bwb_loop(). SYNTAX: LOOP UNTIL expression ' exit when X <> 0 ***************************************************************/ struct bwb_line * bwb_LOOP_UNTIL(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("LOOP without DO"); return bwb_zline(l); } /* call bwb_exp() to interpret the expression */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) != FALSE) { /* EXIT DO */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } /* loop around to DO again */ CURTASK excs[CURTASK exsc].LoopTopLine->position = 0; bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_DO); return CURTASK excs[CURTASK exsc].LoopTopLine; } /*************************************************************** FUNCTION: bwb_LOOP_WHILE() DESCRIPTION: This C function implements the BASIC LOOP WHILE statement and is called by bwb_loop(). SYNTAX: LOOP WHILE expression ' exit when X = 0 ***************************************************************/ struct bwb_line * bwb_LOOP_WHILE(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("LOOP without DO"); return bwb_zline(l); } /* call bwb_exp() to interpret the expression */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) == FALSE) { /* EXIT DO */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } /* execute DO-LOOP again */ CURTASK excs[CURTASK exsc].LoopTopLine->position = 0; bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_DO); return CURTASK excs[CURTASK exsc].LoopTopLine; } /* -------------------------------------------------------------------------------------------- WHILE - WEND -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_WHILE() DESCRIPTION: This function handles the BASIC WHILE statement. SYNTAX: WHILE expression ... WEND SYNTAX: WHILE expression ' exit when X = 0 ***************************************************************/ struct bwb_line * bwb_WHILE(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); /* if this is the first time at this WHILE statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the LOOP statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l); if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("WHILE without WEND"); return bwb_zline(l); } } /*----------------------------------------------------*/ /* Expression evaluation was at the top of bwb_while, */ /* and the init portion was performed only if TRUE. */ /* The init routine should be performed regardless of */ /* expression value, else a segmentation fault can */ /* occur! (JBV) */ /*----------------------------------------------------*/ /* call bwb_exp() to interpret the expression */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) == FALSE) { /* EXIT WHILE */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } /* WHILE ... */ bwb_setexec(l, l->position, EXEC_WHILE); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_exitwhile() DESCRIPTION: This function handles the BASIC EXIT WHILE statement. This is a structured programming command compatible with ANSI BASIC. It is called from the bwb_exit() subroutine. ***************************************************************/ struct bwb_line * bwb_EXIT_WHILE(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* Check the integrity of the WHILE statement */ next_line = FindExitLineOnStack(l); if (next_line == NULL) { bwb_error("EXIT WHILE without WHILE"); return bwb_zline(l); } { /* EXIT WHILE */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } #if 0 next_line = next_line->next; /* set the next line in the exec stack */ next_line->position = 0; bwb_setexec(next_line, 0, EXEC_WHILE); return next_line; #endif } /*************************************************************** FUNCTION: bwb_wend() DESCRIPTION: This function handles the BASIC WEND statement and the LOOP statement ending a DO WHILE loop. SYNTAX: WEND LOOP ***************************************************************/ struct bwb_line * bwb_WEND(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of WHILE loop */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("WEND without WHILE"); return bwb_zline(l); } /* reset to the top of the current WHILE loop */ CURTASK excs[CURTASK exsc].LoopTopLine->position = 0; bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_WHILE); return CURTASK excs[CURTASK exsc].LoopTopLine; } /* -------------------------------------------------------------------------------------------- UNTIL - UEND -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_UNTIL() DESCRIPTION: This function handles the BASIC UNTIL statement. SYNTAX: UNTIL expression ' exit when X <> 0 ... UEND ***************************************************************/ struct bwb_line * bwb_UNTIL(struct bwb_line * l) { struct exp_ese *e; bwx_DEBUG(__FUNCTION__); /* if this is the first time at this WHILE statement, note it */ #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the UEND statement (or LOOP statement) */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l); if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("UNTIL without UEND"); return bwb_zline(l); } } /*----------------------------------------------------*/ /* Expression evaluation was at the top of bwb_while, */ /* and the init portion was performed only if TRUE. */ /* The init routine should be performed regardless of */ /* expression value, else a segmentation fault can */ /* occur! (JBV) */ /*----------------------------------------------------*/ /* call bwb_exp() to interpret the expression */ e = bwb_exp(l->buffer, FALSE, &(l->position)); if (ERROR_PENDING) { return bwb_zline(l); } if (exp_getival(e) != FALSE) { /* EXIT UNTIL */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } /* UNTIL ... */ bwb_setexec(l, l->position, EXEC_UNTIL); return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_exituntil() DESCRIPTION: This function handles the BASIC EXIT UNTIL statement. This is a structured programming command compatible with ANSI BASIC. It is called from the bwb_exit() subroutine. ***************************************************************/ struct bwb_line * bwb_EXIT_UNTIL(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* Check the integrity of the UNTIL statement */ next_line = FindExitLineOnStack(l); if (next_line == NULL) { bwb_error("EXIT UNTIL without UNTIL"); return bwb_zline(l); } { /* EXIT UNTIL */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } #if 0 next_line = next_line->next; /* set the next line in the exec stack */ next_line->position = 0; bwb_setexec(next_line, 0, EXEC_UNTIL); return next_line; #endif } /*************************************************************** FUNCTION: bwb_uend() DESCRIPTION: This function handles the BASIC UEND statement and the LOOP statement ending a DO UNTIL loop. SYNTAX: UEND ***************************************************************/ struct bwb_line * bwb_UEND(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); /* check integrity of UNTIL loop */ if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("UEND without UNTIL"); return bwb_zline(l); } /* reset to the top of the current UNTIL loop */ CURTASK excs[CURTASK exsc].LoopTopLine->position = 0; bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_UNTIL); return CURTASK excs[CURTASK exsc].LoopTopLine; } /* -------------------------------------------------------------------------------------------- FOR - NEXT -------------------------------------------------------------------------------------------- */ /*************************************************************** FUNCTION: bwb_for() DESCRIPTION: This function handles the BASIC FOR statement. SYNTAX: FOR counter = start TO finish [STEP increment] NOTE: This is controlled by the OptionVersion bitmask. The order of expression evaluation and variable creation varies. For example: FUNCTION FNA( Y ) PRINT "Y="; Y FNA = Y END FUNCTION FOR X = FNA(3) TO FNA(1) STEP FNA(2) NEXT X ANSI/ECMA; Y= 1 Y= 2 Y= 3 X is created (if it does not exist) X is assigned the value of 3 MICROSOFT; X is created (if it does not exist) Y= 3 X is assigned the value of 3 Y= 1 Y= 2 ECMA-55: Section 13.4 ... The action of the for-statement and the next-statement is de- fined in terms of other statements, as follows: FOR v = initial-value TO limit STEP increment (block) NEXT v is equivalent to: LET own1 = limit LET own2 = increment LET v = initial-value line1 IF (v-own1) * SGN (own2) > 0 THEN line2 (block) LET v = v + own2 GOTO line1 line2 REM continued in sequence ... ***************************************************************/ struct bwb_line * bwb_FOR(struct bwb_line * l) { bwx_DEBUG(__FUNCTION__); #if 0 if (CURTASK excs[CURTASK exsc].LoopTopLine != l) #endif if (FindTopLineOnStack(l) == FALSE) { bwb_incexec(); CURTASK excs[CURTASK exsc].LoopTopLine = l; /* find the NEXT statement */ CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l); if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL) { /* NOT FOUND */ bwb_error("FOR without NEXT"); return bwb_zline(l); } } /* ------------------------------------------------------------------- * ------------------------------- */ if (TRUE) { register int n; int e, loop; int to, step, p; BasicNumberType for_step; /* STEP value for FOR */ BasicNumberType for_target; /* target value for FOR */ struct exp_ese *exp; struct bwb_variable *v; char tbuf[BasicStringLengthMax + 1]; int VarStart; /* position in l->buffer */ int VarLength; int InitStart; int InitLength; int TermStart; int TermLength; int StepStart; int StepLength; BasicNumberType Value; /* VarStart = 0; */ /* VarLength = 0; */ InitStart = 0; InitLength = 0; TermStart = 0; TermLength = 0; StepStart = 0; StepLength = 0; /* get the variable name */ VarStart = l->position; exp_getvfname(&(l->buffer[l->position]), tbuf); VarLength = strlen(tbuf); l->position += VarLength; /* at this point one should find an equals sign ('=') */ adv_ws(l->buffer, &(l->position)); if (l->buffer[l->position] != '=') { sprintf(bwb_ebuf, "in bwb_for(): failed to find equals sign, buf <%s>", &(l->buffer[l->position])); bwb_error(bwb_ebuf); return bwb_zline(l); } else { ++(l->position); } /* Find the TO and STEP statements */ cnd_tostep(l->buffer, l->position, &to, &step); /* if there is no TO statement, then an error has ocurred */ if (to < 1) { bwb_error("FOR without TO"); return bwb_zline(l); } /* copy initial value to buffer and evaluate it */ InitStart = l->position; tbuf[0] = '\0'; p = 0; for (n = l->position; n < to; ++n) { tbuf[p] = l->buffer[n]; ++p; ++l->position; tbuf[p] = '\0'; } InitLength = p; /* copy target value to small buffer and evaluate it */ tbuf[0] = '\0'; p = 0; l->position = to + 2; if (step < 1) { e = strlen(l->buffer); } else { e = step - 1; } loop = TRUE; n = l->position; TermStart = l->position; while (loop == TRUE) { tbuf[p] = l->buffer[n]; ++p; ++l->position; tbuf[p] = '\0'; if (n >= e) { loop = FALSE; } ++n; if (l->buffer[n] == '\0' || l->buffer[n] == OptionCommentChar) { loop = FALSE; } } TermLength = p; /* If there is a STEP statement, copy it to a buffer and * evaluate it */ if (step > 1) { tbuf[0] = '\0'; p = 0; l->position = step + 4; StepStart = l->position; for (n = l->position; n < (int) strlen(l->buffer); ++n) { tbuf[p] = l->buffer[n]; ++p; ++l->position; tbuf[p] = '\0'; } StepLength = p; } if (OptionFlags & OPTION_BUGS_ON) { /* Create Variable */ if (VarLength < 1) { bwb_error(err_syntax); return bwb_zline(l); } strncpy(tbuf, &l->buffer[VarStart], VarLength); tbuf[VarLength] = '\0'; v = var_find(tbuf); /* Init */ if (InitLength < 1) { bwb_error(err_syntax); return bwb_zline(l); } strncpy(tbuf, &l->buffer[InitStart], InitLength); tbuf[InitLength] = '\0'; p = 0; exp = bwb_exp(tbuf, FALSE, &p); if (ERROR_PENDING) { return bwb_zline(l); } Value = exp_getnval(exp); /* Assign Variable */ var_setnval(v, Value); /* Limit */ if (TermLength < 1) { bwb_error(err_syntax); return bwb_zline(l); } strncpy(tbuf, &l->buffer[TermStart], TermLength); tbuf[TermLength] = '\0'; p = 0; exp = bwb_exp(tbuf, FALSE, &p); if (ERROR_PENDING) { return bwb_zline(l); } for_target = exp_getnval(exp); /* Step */ if (StepLength > 0) { strncpy(tbuf, &l->buffer[StepStart], StepLength); tbuf[StepLength] = '\0'; p = 0; exp = bwb_exp(tbuf, FALSE, &p); if (ERROR_PENDING) { return bwb_zline(l); } for_step = exp_getnval(exp); } else { for_step = 1.0; } } else { /* Limit */ if (TermLength < 1) { bwb_error(err_syntax); return bwb_zline(l); } strncpy(tbuf, &l->buffer[TermStart], TermLength); tbuf[TermLength] = '\0'; p = 0; exp = bwb_exp(tbuf, FALSE, &p); if (ERROR_PENDING) { return bwb_zline(l); } for_target = exp_getnval(exp); /* Step */ if (StepLength > 0) { strncpy(tbuf, &l->buffer[StepStart], StepLength); tbuf[StepLength] = '\0'; p = 0; exp = bwb_exp(tbuf, FALSE, &p); if (ERROR_PENDING) { return bwb_zline(l); } for_step = exp_getnval(exp); } else { for_step = 1.0; } /* Init */ if (InitLength < 1) { bwb_error(err_syntax); return bwb_zline(l); } strncpy(tbuf, &l->buffer[InitStart], InitLength); tbuf[InitLength] = '\0'; p = 0; exp = bwb_exp(tbuf, FALSE, &p); if (ERROR_PENDING) { return bwb_zline(l); } Value = exp_getnval(exp); /* Create Variable */ if (VarLength < 1) { bwb_error(err_syntax); return bwb_zline(l); } strncpy(tbuf, &l->buffer[VarStart], VarLength); tbuf[VarLength] = '\0'; v = var_find(tbuf); /* Assign Variable */ var_setnval(v, Value); } CURTASK excs[CURTASK exsc].local_variable = v; CURTASK excs[CURTASK exsc].for_step = for_step; CURTASK excs[CURTASK exsc].for_target = for_target; } /* ------------------------------------------------------------------- * ------------------------------ */ bwb_setexec(l, l->position, EXEC_FOR); /* 800 FOR I1=9 TO I1 STEP I1 ' I1=-2 */ if (TRUE) { int IsExit; BasicNumberType Value; BasicNumberType Target; BasicNumberType Step; IsExit = FALSE; Value = var_getnval(CURTASK excs[CURTASK exsc].local_variable); Target = CURTASK excs[CURTASK exsc].for_target; Step = CURTASK excs[CURTASK exsc].for_step; if (Step > 0) { /* POSITIVE */ if (Value > Target) { IsExit = TRUE; } } else { /* NEGATIVE */ if (Value < Target) { IsExit = TRUE; } } if (IsExit == TRUE) { /* EXIT FOR */ struct bwb_line *next_line; next_line = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_decexec(); /* set the next line in the exec stack */ next_line->position = 0; /* bwb_setexec( next_line, 0, EXEC_NORM ); *//* WRONG * (JBV) */ bwb_setexec(next_line, 0, CURTASK excs[CURTASK exsc].code); /* JBV */ return next_line; } } /* proceed with processing */ return bwb_zline(l); } /*************************************************************** FUNCTION: bwb_exitfor() DESCRIPTION: This function handles the BASIC EXIT FOR statement. This is a structured programming command compatible with ANSI BASIC. It is called from the bwb_exit() subroutine. SYNTAX: EXIT FOR ***************************************************************/ struct bwb_line * bwb_EXIT_FOR(struct bwb_line * l) { struct bwb_line *next_line; bwx_DEBUG(__FUNCTION__); /* Check the integrity of the FOR statement */ next_line = FindExitLineOnStack(l); if (next_line == NULL) { bwb_error("EXIT FOR without FOR"); return bwb_zline(l); } { /* EXIT FOR */ struct bwb_line *r; CURTASK excs[CURTASK exsc].LoopTopLine = NULL; r = CURTASK excs[CURTASK exsc].LoopBottomLine->next; bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code); r->position = 0; bwb_decexec(); return r; } #if 0 next_line = next_line->next; /* set the next line in the exec stack */ next_line->position = 0; bwb_setexec(next_line, 0, EXEC_FOR); return next_line; #endif } /*************************************************************** FUNCTION: bwb_next() DESCRIPTION: This function handles the BASIC NEXT statement. SYNTAX: NEXT counter ***************************************************************/ struct bwb_line * bwb_NEXT(struct bwb_line * l) { char tbuf[BasicStringLengthMax + 1]; bwx_DEBUG(__FUNCTION__); if (FindBottomLineOnStack(l) == FALSE) { /* NOT FOUND */ bwb_error("NEXT without FOR"); return bwb_zline(l); } /* read the argument, if there is one */ exp_getvfname(&(l->buffer[l->position]), tbuf); if (strlen(tbuf) != 0) { /* NEXT variablename */ struct bwb_variable *v; v = var_find(tbuf); l->position += strlen(tbuf); /* decrement or increment the value */ if (CURTASK excs[CURTASK exsc].local_variable != v) { /* not found */ bwb_error("NEXT without FOR"); return bwb_zline(l); } } else { /* NEXT */ if (CURTASK excs[CURTASK exsc].local_variable == NULL) { /* not found */ bwb_error("NEXT without FOR"); return bwb_zline(l); } } var_setnval(CURTASK excs[CURTASK exsc].local_variable, var_getnval(CURTASK excs[CURTASK exsc].local_variable) + CURTASK excs[CURTASK exsc].for_step); /* check for completion of the loop */ if (CURTASK excs[CURTASK exsc].for_step > 0) /* if step is positive */ { if (var_getnval(CURTASK excs[CURTASK exsc].local_variable) > CURTASK excs[CURTASK exsc].for_target) { bwb_decexec(); bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code); return bwb_zline(l); } } else /* if step is negative */ { if (var_getnval(CURTASK excs[CURTASK exsc].local_variable) < CURTASK excs[CURTASK exsc].for_target) { bwb_decexec(); bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code); return bwb_zline(l); } } /* Target not reached: return to the top of the FOR loop */ bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, CURTASK excs[CURTASK exsc].LoopTopLine->position, EXEC_FOR); return CURTASK excs[CURTASK exsc].LoopTopLine; } /*************************************************************** FUNCTION: cnd_tostep() DESCRIPTION: This function searches through the beginning at point and attempts to find positions of TO and STEP statements. ***************************************************************/ static int cnd_tostep(char *buffer, int position, int *to, int *step) { int loop, t_pos, b_pos, p_word; char tbuf[BasicStringLengthMax + 1]; bwx_DEBUG(__FUNCTION__); /* set then and els to FALSE initially */ *to = *step = FALSE; /* loop to find words */ p_word = b_pos = position; t_pos = 0; tbuf[0] = '\0'; loop = TRUE; while (loop == TRUE) { if (buffer[b_pos] == '\0' || buffer[b_pos] == OptionCommentChar) { /* end of string */ return TRUE; } switch (buffer[b_pos]) { case ' ': /* whitespace = end of word */ if (strncasecmp(tbuf, "TO", (size_t) strlen("TO")) == 0) { *to = p_word; } else if (strncasecmp(tbuf, "STEP", (size_t) strlen("STEP")) == 0) { *step = p_word; } ++b_pos; p_word = b_pos; t_pos = 0; tbuf[0] = '\0'; break; default: tbuf[t_pos] = buffer[b_pos]; ++b_pos; ++t_pos; tbuf[t_pos] = '\0'; break; } } return TRUE; } /*************************************************************** FUNCTION: var_setnval() DESCRIPTION: This function sets the value of numerical variable v to the value of i. ***************************************************************/ int var_setnval(struct bwb_variable * v, BasicNumberType i) { bwx_DEBUG(__FUNCTION__); switch (v->type) { case NUMBER: *var_findnval(v, v->array_pos) = i; break; default: sprintf(bwb_ebuf, "in var_setnval(): variable <%s> is not a number", v->name); bwb_error(bwb_ebuf); } /* successful assignment */ return TRUE; } static int FindTopLineOnStack(struct bwb_line * l) { /* since we are the top of the loop, we MIGHT be on the stack */ int i; bwx_DEBUG(__FUNCTION__); for (i = CURTASK exsc; i >= 0; i--) { struct bwb_line *current; current = CURTASK excs[i].LoopTopLine; if (current != NULL) { if (current == l) { /* FOUND */ while (CURTASK exsc > i) { bwb_decexec(); } /* we are now the top item on the stack */ return TRUE; } /* do NOT cross a function/sub boundary */ switch (current->cmdnum) { case C_FUNCTION: case C_SUB: /* NOT FOUND */ return FALSE; break; } } } /* NOT FOUND */ return FALSE; } static int FindBottomLineOnStack(struct bwb_line * l) { /* since we are the bottom of the loop, we MUST be on the stack */ bwx_DEBUG(__FUNCTION__); while (CURTASK exsc >= 0) { if (CURTASK excs[CURTASK exsc].LoopBottomLine == l) { /* FOUND */ return TRUE; } bwb_decexec(); } /* NOT FOUND */ return FALSE; } static struct bwb_line * FindExitLineOnStack(struct bwb_line * l) { /* we are an EXIT ... command, find the bottom line on the stack */ bwx_DEBUG(__FUNCTION__); while (CURTASK exsc >= 0) { struct bwb_line *current; current = CURTASK excs[CURTASK exsc].LoopBottomLine; if (current != NULL) { switch (l->cmdnum) { case C_EXIT_DO: /* DO - LOOP */ switch (current->cmdnum) { case C_LOOP: case C_LOOP_UNTIL: case C_LOOP_WHILE: return current; break; } break; case C_EXIT_WHILE: /* WHILE - WEND */ switch (current->cmdnum) { case C_WEND: return current; break; } break; case C_EXIT_UNTIL: /* UNTIL - UEND */ switch (current->cmdnum) { case C_UEND: return current; break; } break; case C_EXIT_FOR: /* FOR - NEXT */ switch (current->cmdnum) { case C_NEXT: return current; break; } break; case C_EXIT_SUB: /* SUB - END SUB */ switch (current->cmdnum) { case C_END_SUB: return current; break; } break; case C_EXIT_FUNCTION: /* FUNCTION - END FUNCTION */ switch (current->cmdnum) { case C_END_FUNCTION: return current; break; } break; case C_ELSEIF: case C_ELSE: /* IF - END IF */ switch (current->cmdnum) { case C_END_IF: return current; break; } break; case C_CASE_IF: case C_CASE_IS: case C_CASE: case C_CASE_ELSE: /* SELECT CASE - END SELECT */ switch (current->cmdnum) { case C_END_SELECT: return current; break; } break; } } bwb_decexec(); } /* NOT FOUND */ return FALSE; } static struct bwb_line * scan_BottomLineInCode(struct bwb_line * l) { /* we are the TopLine. Let's find our matching BottomLine */ struct bwb_line *current; register int w_level; bwx_DEBUG(__FUNCTION__); switch (l->cmdnum) { case C_DEF: case C_USER_LBL: /* just the single line */ return l; break; } /* multiple lines */ w_level = 1; for (current = l->next; current != &CURTASK bwb_end; current = current->next) { switch (l->cmdnum) { case C_DO: case C_DO_WHILE: case C_DO_UNTIL: /* DO - LOOP */ switch (current->cmdnum) { case C_DO: case C_DO_WHILE: case C_DO_UNTIL: ++w_level; break; case C_LOOP: case C_LOOP_UNTIL: case C_LOOP_WHILE: --w_level; if (w_level == 0) { return current; } break; } break; case C_WHILE: /* WHILE - WEND */ switch (current->cmdnum) { case C_WHILE: ++w_level; break; case C_WEND: --w_level; if (w_level == 0) { return current; } break; } break; case C_UNTIL: /* UNTIL - UEND */ switch (current->cmdnum) { case C_UNTIL: ++w_level; break; case C_UEND: --w_level; if (w_level == 0) { return current; } break; } break; case C_FOR: /* FOR - NEXT */ switch (current->cmdnum) { case C_FOR: ++w_level; break; case C_NEXT: --w_level; if (w_level == 0) { return current; } break; } break; case C_IF_THEN: /* IF - END IF */ switch (current->cmdnum) { case C_IF_THEN: ++w_level; break; case C_END_IF: --w_level; if (w_level == 0) { return current; } break; } break; case C_SELECT_CASE: /* SELECT CASE - END SELECT */ switch (current->cmdnum) { case C_SELECT_CASE: ++w_level; break; case C_END_SELECT: --w_level; if (w_level == 0) { return current; } break; } break; case C_SUB: /* SUB - END SUB */ switch (current->cmdnum) { case C_SUB: ++w_level; break; case C_END_SUB: --w_level; if (w_level == 0) { return current; } break; } break; case C_FUNCTION: /* SUB - END SUB */ switch (current->cmdnum) { case C_FUNCTION: ++w_level; break; case C_END_FUNCTION: --w_level; if (w_level == 0) { return current; } break; } break; default: return NULL; break; } } /* NOT FOUND */ return NULL; } struct bwb_line * find_BottomLineInCode(struct bwb_line * l) { /* we only want to scan once */ /* bwb_scan() sets l->OtherLine to NULL to ensure MERGE and DELETE * work correctly */ bwx_DEBUG(__FUNCTION__); if (l->OtherLine == NULL) { /* we do not yet know */ l->OtherLine = scan_BottomLineInCode(l); if (l->OtherLine != NULL) { /* FOUND */ /* mark the bottom line to point to us, so we can use * this info in DELETE */ l->OtherLine->OtherLine = l; } } return l->OtherLine; } static struct bwb_line * find_NextTestInCode(struct bwb_line * l) { struct bwb_line *current; register int w_level; bwx_DEBUG(__FUNCTION__); w_level = 1; for (current = l->next; current != &CURTASK bwb_end; current = current->next) { switch (l->cmdnum) { case C_IF_THEN: case C_ELSEIF: switch (current->cmdnum) { case C_IF_THEN: ++w_level; break; case C_ELSEIF: case C_ELSE: /* we must report only the first ELSE we * encounter at level 1 */ if (w_level == 1) { return current; } break; case C_END_IF: --w_level; if (w_level == 0) { return NULL; } break; } break; case C_CASE: case C_CASE_IF: case C_CASE_IS: switch (current->cmdnum) { case C_SELECT_CASE: ++w_level; break; case C_CASE: case C_CASE_IF: case C_CASE_IS: case C_CASE_ELSE: /* we must report only the first CASE we * encounter at level 1 */ if (w_level == 1) { return current; } break; case C_END_SELECT: --w_level; if (w_level == 0) { return NULL; } } break; default: return NULL; break; } } return NULL; } /* EOF */