ChipMaster's bwBASIC This also includes history going back to v2.10. *WARN* some binary files might have been corrupted by CRLF.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3081 lines
75 KiB

  1. /***************************************************************
  2. bwb_cnd.c Conditional Expressions and Commands
  3. for Bywater BASIC Interpreter
  4. Copyright (c) 1993, Ted A. Campbell
  5. Bywater Software
  6. email: tcamp@delphi.com
  7. Copyright and Permissions Information:
  8. All U.S. and international rights are claimed by the author,
  9. Ted A. Campbell.
  10. This software is released under the terms of the GNU General
  11. Public License (GPL), which is distributed with this software
  12. in the file "COPYING". The GPL specifies the terms under
  13. which users may copy and use the software in this distribution.
  14. A separate license is available for commercial distribution,
  15. for information on which you should contact the author.
  16. ***************************************************************/
  17. /*---------------------------------------------------------------*/
  18. /* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */
  19. /* 11/1995 (eidetics@cerf.net). */
  20. /* */
  21. /* Those additionally marked with "DD" were at the suggestion of */
  22. /* Dale DePriest (daled@cadence.com). */
  23. /* */
  24. /* Version 3.00 by Howard Wulf, AF5NE */
  25. /* */
  26. /*---------------------------------------------------------------*/
  27. #include "bwbasic.h"
  28. /* declarations of functions visible to this file only */
  29. static int
  30. cnd_thenels(char *buffer, int position, int *then, int *els);
  31. static int
  32. cnd_tostep(char *buffer, int position, int *to, int *step);
  33. static int
  34. case_eval(struct exp_ese * expression, struct exp_ese * minval, struct exp_ese * maxval);
  35. static int
  36. FindTopLineOnStack(struct bwb_line * l);
  37. static int
  38. FindBottomLineOnStack(struct bwb_line * l);
  39. static struct bwb_line *
  40. FindExitLineOnStack(struct bwb_line * l);
  41. static struct bwb_line *
  42. find_NextTestInCode(struct bwb_line * l);
  43. /* FIXME: DELETE should remove DEF FN, FUNCTIONS, and SUBROUTINES in the deleted line range. */
  44. /* FIXME: add Variable Range checking, based upon type characters $, @, %, &, !, # */
  45. /* FIXME: check EOF, LOF, LOC, SEEK, such as QB45 */
  46. /* FIXME: centralize Variable/Function/Command name character checking, so we can configure it with OPTION VERSION */
  47. /*
  48. --------------------------------------------------------------------------------------------
  49. FUNCTION - END FUNCTION
  50. --------------------------------------------------------------------------------------------
  51. */
  52. struct bwb_line *
  53. bwb_FUNCTION(struct bwb_line * l)
  54. {
  55. bwx_DEBUG(__FUNCTION__);
  56. /* check current exec level */
  57. if (CURTASK exsc == 0)
  58. {
  59. /* skip over the entire function definition */
  60. return l->OtherLine->next; /* line after END SUB */
  61. }
  62. /* we are being executed via fnc_deffn() */
  63. /* if this is the first time at this SUB statement, note it */
  64. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  65. {
  66. bwb_incexec();
  67. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  68. /* find the END SUB statement */
  69. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);;
  70. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  71. {
  72. /* NOT FOUND */
  73. bwb_error("FUNCTION without END FUNCTION");
  74. return bwb_zline(l);
  75. }
  76. }
  77. adv_eos(l->buffer, &(l->position));
  78. return bwb_zline(l);
  79. }
  80. struct bwb_line *
  81. bwb_EXIT_FUNCTION(struct bwb_line * l)
  82. {
  83. struct bwb_line *next_line;
  84. bwx_DEBUG(__FUNCTION__);
  85. next_line = FindExitLineOnStack(l);
  86. if (next_line == NULL)
  87. {
  88. bwb_error("EXIT FUNCTION without FUNCTION");
  89. return bwb_zline(l);
  90. }
  91. {
  92. /* EXIT FUNCTION */
  93. struct bwb_line *r;
  94. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  95. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  96. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  97. r->position = 0;
  98. bwb_decexec();
  99. return r;
  100. }
  101. #if 0
  102. /* set the next line in the exec stack */
  103. next_line->position = 0;
  104. bwb_setexec(next_line, 0, EXEC_FUNCTION);
  105. return next_line;
  106. #endif
  107. }
  108. struct bwb_line *
  109. bwb_END_FUNCTION(struct bwb_line * l)
  110. {
  111. bwx_DEBUG(__FUNCTION__);
  112. /* check integrity of SUB commmand */
  113. if (FindBottomLineOnStack(l) == FALSE)
  114. {
  115. /* NOT FOUND */
  116. bwb_error("END FUNCTION without FUNCTION");
  117. return bwb_zline(l);
  118. }
  119. /* decrement the stack */
  120. bwb_decexec();
  121. /* and return next from old line */
  122. CURTASK excs[CURTASK exsc].line->next->position = 0;
  123. return CURTASK excs[CURTASK exsc].line->next;
  124. }
  125. /*
  126. --------------------------------------------------------------------------------------------
  127. SUB - END SUB
  128. --------------------------------------------------------------------------------------------
  129. */
  130. /***************************************************************
  131. FUNCTION: bwb_sub()
  132. DESCRIPTION: This function implements the BASIC
  133. SUB command, introducing a named
  134. subroutine.
  135. SYNTAX: SUB subroutine-name
  136. ...
  137. [ EXIT SUB ]
  138. ...
  139. END SUB
  140. ***************************************************************/
  141. struct bwb_line *
  142. bwb_SUB(struct bwb_line * l)
  143. {
  144. bwx_DEBUG(__FUNCTION__);
  145. /* check current exec level */
  146. if (CURTASK exsc == 0)
  147. {
  148. /* skip over the entire function definition */
  149. return l->OtherLine->next; /* line after END SUB */
  150. }
  151. /* we are being executed via fnc_deffn() */
  152. /* if this is the first time at this SUB statement, note it */
  153. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  154. {
  155. bwb_incexec();
  156. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  157. /* find the END SUB statement */
  158. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);;
  159. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  160. {
  161. /* NOT FOUND */
  162. bwb_error("SUB without END SUB");
  163. return bwb_zline(l);
  164. }
  165. }
  166. adv_eos(l->buffer, &(l->position));
  167. return bwb_zline(l);
  168. }
  169. /***************************************************************
  170. FUNCTION: bwb_endsub()
  171. DESCRIPTION: This C function implements the BASIC
  172. END SUB command, ending a subroutine
  173. definition. Because the command END
  174. can have multiple meanings, this function
  175. should be called from the bwb_xend()
  176. function, which should be able to identify
  177. an END SUB command.
  178. SYNTAX: END SUB
  179. ***************************************************************/
  180. struct bwb_line *
  181. bwb_EXIT_SUB(struct bwb_line * l)
  182. {
  183. struct bwb_line *next_line;
  184. bwx_DEBUG(__FUNCTION__);
  185. next_line = FindExitLineOnStack(l);
  186. if (next_line == NULL)
  187. {
  188. bwb_error("EXIT SUB without SUB");
  189. return bwb_zline(l);
  190. }
  191. {
  192. /* EXIT SUB */
  193. struct bwb_line *r;
  194. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  195. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  196. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  197. r->position = 0;
  198. bwb_decexec();
  199. return r;
  200. }
  201. #if 0
  202. /* set the next line in the exec stack */
  203. next_line->position = 0;
  204. bwb_setexec(next_line, 0, EXEC_FUNCTION);
  205. return next_line;
  206. #endif
  207. }
  208. struct bwb_line *
  209. bwb_END_SUB(struct bwb_line * l)
  210. {
  211. bwx_DEBUG(__FUNCTION__);
  212. /* check integrity of SUB commmand */
  213. if (FindBottomLineOnStack(l) == FALSE)
  214. {
  215. /* NOT FOUND */
  216. bwb_error("END SUB without SUB");
  217. return bwb_zline(l);
  218. }
  219. /* decrement the stack */
  220. bwb_decexec();
  221. /* and return next from old line */
  222. CURTASK excs[CURTASK exsc].line->next->position = 0;
  223. return CURTASK excs[CURTASK exsc].line->next;
  224. }
  225. /*
  226. --------------------------------------------------------------------------------------------
  227. IF - END IF
  228. --------------------------------------------------------------------------------------------
  229. */
  230. /***************************************************************
  231. FUNCTION: bwb_IF()
  232. DESCRIPTION: This function handles the BASIC IF
  233. statement, standard flavor.
  234. SYNTAX: IF expression THEN line [ELSE line]
  235. ***************************************************************/
  236. struct bwb_line *
  237. bwb_IF(struct bwb_line * l)
  238. {
  239. /* classic IF */
  240. int then, els;
  241. struct exp_ese *e;
  242. int tpos;
  243. char tbuf[BasicStringLengthMax + 1];
  244. int Value;
  245. bwx_DEBUG(__FUNCTION__);
  246. /* Call bwb_exp() to evaluate the condition. This should return with
  247. * position set to the "THEN" statement */
  248. e = bwb_exp(l->buffer, FALSE, &(l->position));
  249. if (ERROR_PENDING)
  250. {
  251. return bwb_zline(l);
  252. }
  253. Value = exp_getival(e);
  254. /* test for "THEN" and "ELSE" statements */
  255. cnd_thenels(l->buffer, l->position, &then, &els);
  256. /* evaluate and execute */
  257. if (Value != 0)
  258. {
  259. /* expression is TRUE */
  260. if (then == FALSE)
  261. {
  262. /* syntac error */
  263. bwb_error(err_syntax);
  264. return bwb_zline(l);
  265. }
  266. else
  267. {
  268. /* check for THEN followed by literal line number */
  269. struct bwb_line *x;
  270. tpos = then + strlen("THEN") + 1;
  271. adv_element(l->buffer, &tpos, tbuf);
  272. /* check for target label */
  273. x = find_label(tbuf);
  274. if (x != NULL)
  275. {
  276. adv_eos(l->buffer, &(l->position));
  277. x->position = 0;
  278. return x;
  279. }
  280. /* syntac error */
  281. bwb_error(err_syntax);
  282. return bwb_zline(l);
  283. }
  284. }
  285. else
  286. {
  287. /* expression is FALSE */
  288. if (els == FALSE)
  289. {
  290. /* optional */
  291. adv_eos(l->buffer, &(l->position));
  292. return bwb_zline(l);
  293. }
  294. else
  295. {
  296. /* check for ELSE followed by literal line number */
  297. struct bwb_line *x;
  298. tpos = els + strlen("ELSE") + 1;
  299. adv_element(l->buffer, &tpos, tbuf);
  300. /* check for target label */
  301. x = find_label(tbuf);
  302. if (x != NULL)
  303. {
  304. adv_eos(l->buffer, &(l->position));
  305. x->position = 0;
  306. return x;
  307. }
  308. /* syntac error */
  309. bwb_error(err_syntax);
  310. return bwb_zline(l);
  311. }
  312. }
  313. return bwb_zline(l);
  314. }
  315. /***************************************************************
  316. FUNCTION: cnd_thenelse()
  317. DESCRIPTION: This function searches through the
  318. <buffer> beginning at point <position>
  319. and attempts to find positions of THEN
  320. and ELSE statements.
  321. ***************************************************************/
  322. static int
  323. cnd_thenels(char *buffer, int position, int *then, int *els)
  324. {
  325. int loop, t_pos, b_pos, p_word;
  326. char tbuf[BasicStringLengthMax + 1];
  327. bwx_DEBUG(__FUNCTION__);
  328. /* set then and els to 0 initially */
  329. *then = *els = 0;
  330. /* loop to find words */
  331. p_word = b_pos = position;
  332. t_pos = 0;
  333. tbuf[0] = '\0';
  334. loop = TRUE;
  335. while (loop == TRUE)
  336. {
  337. char c;
  338. c = buffer[b_pos];
  339. if (c == '\0' || c == OptionCommentChar || c == ' ')
  340. {
  341. if (strncasecmp(tbuf, "THEN", (size_t) strlen("THEN")) == 0)
  342. {
  343. *then = p_word;
  344. }
  345. else
  346. if (strncasecmp(tbuf, "GOTO", (size_t) strlen("GOTO")) == 0)
  347. {
  348. *then = p_word;
  349. }
  350. else
  351. if (strncasecmp(tbuf, "ELSE", (size_t) strlen("ELSE")) == 0)
  352. {
  353. *els = p_word;
  354. }
  355. /* check for end of the line */
  356. if (c == '\0' || c == OptionCommentChar)
  357. {
  358. return TRUE;
  359. }
  360. ++b_pos;
  361. p_word = b_pos;
  362. t_pos = 0;
  363. tbuf[0] = '\0';
  364. }
  365. else
  366. {
  367. tbuf[t_pos] = c;
  368. ++b_pos;
  369. ++t_pos;
  370. tbuf[t_pos] = '\0';
  371. }
  372. }
  373. return FALSE;
  374. }
  375. /***************************************************************
  376. FUNCTION: bwb_IF_THEN()
  377. DESCRIPTION: This function handles the BASIC IF
  378. statement, structured flavor.
  379. SYNTAX: IF expression THEN
  380. ...
  381. END IF
  382. ***************************************************************/
  383. struct bwb_line *
  384. bwb_IF_THEN(struct bwb_line * l)
  385. {
  386. /* structured IF */
  387. struct exp_ese *e;
  388. struct bwb_line *else_line;
  389. struct bwb_line *endif_line;
  390. int Value;
  391. bwx_DEBUG(__FUNCTION__);
  392. /* if this is the first time at this IF statement, note it */
  393. #if 0
  394. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  395. #endif
  396. if (FindTopLineOnStack(l) == FALSE)
  397. {
  398. bwb_incexec();
  399. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  400. /* find the LOOP statement */
  401. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);;
  402. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  403. {
  404. /* NOT FOUND */
  405. bwb_error("IF without END IF");
  406. return bwb_zline(l);
  407. }
  408. }
  409. /* Call bwb_exp() to evaluate the condition. This should return with
  410. * position set to the "THEN" statement */
  411. /* FIXME: extract the expression before calling bwb_exp() - no COMMANDS should be there */
  412. e = bwb_exp(l->buffer, FALSE, &(l->position));
  413. if (ERROR_PENDING)
  414. {
  415. return bwb_zline(l);
  416. }
  417. Value = exp_getival(e);
  418. /* evaluate the expression */
  419. if (Value != 0)
  420. {
  421. CURTASK excs[CURTASK exsc].code = EXEC_IFTRUE; /* IF has been processed */
  422. /* fall thru to execute this code block */
  423. adv_eos(l->buffer, &(l->position));
  424. return bwb_zline(l);
  425. }
  426. CURTASK excs[CURTASK exsc].code = EXEC_IFFALSE; /* IF has NOT been
  427. * processed */
  428. else_line = find_NextTestInCode(l);
  429. if (else_line != NULL)
  430. {
  431. else_line->position = 0;
  432. return else_line;
  433. }
  434. endif_line = CURTASK excs[CURTASK exsc].LoopBottomLine;
  435. endif_line->position = 0;
  436. return endif_line;
  437. }
  438. /***************************************************************
  439. FUNCTION: bwb_else()
  440. DESCRIPTION: This function handles the BASIC ELSE
  441. statement.
  442. SYNTAX: ELSE
  443. ***************************************************************/
  444. struct bwb_line *
  445. bwb_ELSE(struct bwb_line * l)
  446. {
  447. struct bwb_line *next_line;
  448. bwx_DEBUG(__FUNCTION__);
  449. /* there are two legitamate ways to get here: 1. a fall thru from an
  450. * upper block, code == EXEC_IFTRUE we should jump to the END IF 2. a
  451. * jump from from an upper block, code == EXEC_IFFALSE we should
  452. * execute our code block */
  453. next_line = FindExitLineOnStack(l); /* END IF */
  454. if (next_line == NULL)
  455. {
  456. bwb_error("ELSE without IF");
  457. return bwb_zline(l);
  458. }
  459. switch (CURTASK excs[CURTASK exsc].code)
  460. {
  461. case EXEC_IFTRUE: /* IF has been processed */
  462. /* jump to the END IF */
  463. next_line->position = 0;
  464. return next_line;
  465. break;
  466. case EXEC_IFFALSE:
  467. CURTASK excs[CURTASK exsc].code = EXEC_IFTRUE; /* IF has been processed */
  468. /* fall thru to execute this code block */
  469. break;
  470. default:
  471. /* the BASIC program has jumped into the middle of a
  472. * structured command */
  473. bwb_error("ELSE without IF");
  474. break;
  475. }
  476. return bwb_zline(l);
  477. }
  478. /***************************************************************
  479. FUNCTION: bwb_elseif()
  480. DESCRIPTION: This function handles the BASIC ELSEIF
  481. statement.
  482. SYNTAX: ELSEIF
  483. ***************************************************************/
  484. struct bwb_line *
  485. bwb_ELSEIF(struct bwb_line * l)
  486. {
  487. struct bwb_line *next_line;
  488. bwx_DEBUG(__FUNCTION__);
  489. /* there are two legitamate ways to get here: 1. a fall thru from an
  490. * upper block, code == EXEC_IFTRUE we should jump to the END IF 2. a
  491. * jump from from an upper block, code == EXEC_IFFALSE we should
  492. * execute our code block */
  493. next_line = FindExitLineOnStack(l); /* END IF */
  494. if (next_line == NULL)
  495. {
  496. bwb_error("ELSEIF without IF");
  497. return bwb_zline(l);
  498. }
  499. switch (CURTASK excs[CURTASK exsc].code)
  500. {
  501. case EXEC_IFTRUE: /* IF has been processed */
  502. /* jump to the END IF */
  503. next_line->position = 0;
  504. return next_line;
  505. break;
  506. case EXEC_IFFALSE:
  507. /* execute our code block (maybe) */
  508. {
  509. struct bwb_line *else_line;
  510. struct exp_ese *e;
  511. /* Call bwb_exp() to evaluate the condition. This
  512. * should return with position set to the "THEN"
  513. * statement */
  514. e = bwb_exp(l->buffer, FALSE, &(l->position));
  515. if (ERROR_PENDING)
  516. {
  517. return bwb_zline(l);
  518. }
  519. if (exp_getival(e) != FALSE)
  520. {
  521. /* condition is TRUE: proceed to the next
  522. * line */
  523. CURTASK excs[CURTASK exsc].code = EXEC_IFTRUE; /* IF has been processed */
  524. /* fall thru to execute this code block */
  525. adv_eos(l->buffer, &(l->position));
  526. return bwb_zline(l);
  527. }
  528. /* condition is FALSE */
  529. /* proceed to next ELSE line if there is one */
  530. else_line = find_NextTestInCode(l); /* ELSEIF or ELSE */
  531. if (else_line != NULL)
  532. {
  533. else_line->position = 0;
  534. return else_line;
  535. }
  536. /* no more ELSExx lines */
  537. /* jump to the END IF */
  538. next_line->position = 0;
  539. return next_line;
  540. }
  541. break;
  542. default:
  543. /* the BASIC program has jumped into the middle of a
  544. * structured command */
  545. bwb_error("ELSEIF without IF");
  546. break;
  547. }
  548. return bwb_zline(l);
  549. }
  550. /***************************************************************
  551. FUNCTION: bwb_endif()
  552. DESCRIPTION: This function handles the BASIC END IF
  553. statement.
  554. SYNTAX: END IF
  555. ***************************************************************/
  556. struct bwb_line *
  557. bwb_END_IF(struct bwb_line * l)
  558. {
  559. bwx_DEBUG(__FUNCTION__);
  560. /* check integrity of IF commmand */
  561. if (FindBottomLineOnStack(l) == FALSE)
  562. {
  563. /* NOT FOUND */
  564. bwb_error("END IF without IF");
  565. return bwb_zline(l);
  566. }
  567. switch (CURTASK excs[CURTASK exsc].code)
  568. {
  569. case EXEC_IFTRUE: /* IF has been processed */
  570. break;
  571. case EXEC_IFFALSE:
  572. break;
  573. default:
  574. /* the BASIC program has jumped into the middle of a
  575. * structured command */
  576. bwb_error("ENDIF without IF");
  577. }
  578. /* remove IF from the stack (NEW) */
  579. bwb_decexec();
  580. bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code);
  581. return bwb_zline(l);
  582. }
  583. /*
  584. --------------------------------------------------------------------------------------------
  585. SELECT CASE - END SELECT
  586. --------------------------------------------------------------------------------------------
  587. */
  588. /***************************************************************
  589. FUNCTION: bwb_select()
  590. DESCRIPTION: This C function handles the BASIC SELECT
  591. statement.
  592. SYNTAX: SELECT CASE expression
  593. ***************************************************************/
  594. struct bwb_line *
  595. bwb_SELECT(struct bwb_line * l)
  596. {
  597. bwx_DEBUG(__FUNCTION__);
  598. bwb_error("SELECT without CASE");
  599. return bwb_zline(l);
  600. }
  601. struct bwb_line *
  602. bwb_SELECT_CASE(struct bwb_line * l)
  603. {
  604. struct exp_ese *e;
  605. bwx_DEBUG(__FUNCTION__);
  606. /* if this is the first time at this SELECT CASE statement, note it */
  607. #if 0
  608. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  609. #endif
  610. if (FindTopLineOnStack(l) == FALSE)
  611. {
  612. bwb_incexec();
  613. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  614. /* find the LOOP statement */
  615. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);
  616. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  617. {
  618. /* NOT FOUND */
  619. bwb_error("SELECT CASE without END SELECT");
  620. return bwb_zline(l);
  621. }
  622. }
  623. /* increment the level and set to EXEC_SELFALSE */
  624. CURTASK excs[CURTASK exsc].code = EXEC_SELFALSE;
  625. /* evaluate the expression at this level */
  626. e = bwb_exp(l->buffer, FALSE, &(l->position));
  627. if (ERROR_PENDING)
  628. {
  629. return bwb_zline(l);
  630. }
  631. if (e->type == STRING)
  632. {
  633. CURTASK excs[CURTASK exsc].expression.type = STRING;
  634. str_btob(&(CURTASK excs[CURTASK exsc].expression.sval),
  635. &(e->sval));
  636. }
  637. else
  638. {
  639. CURTASK excs[CURTASK exsc].expression.type = NUMBER;
  640. CURTASK excs[CURTASK exsc].expression.nval
  641. = exp_getnval(e);
  642. }
  643. /* return */
  644. adv_eos(l->buffer, &(l->position));
  645. return bwb_zline(l);
  646. }
  647. /***************************************************************
  648. FUNCTION: bwb_case()
  649. DESCRIPTION: This C function handles the BASIC CASE
  650. statement.
  651. SYNTAX: CASE constant | IF partial-expression | ELSE
  652. ***************************************************************/
  653. struct bwb_line *
  654. bwb_CASE_ELSE(struct bwb_line * l)
  655. {
  656. struct bwb_line *next_line;
  657. bwx_DEBUG(__FUNCTION__);
  658. /* there are two legitamate ways to get here: 1. a fall thru from an
  659. * upper block, code == EXEC_SELTRUE we should jump to the END SELECT
  660. * 2. a jump from from an upper block, code == EXEC_SELFALSE we
  661. * should execute our code block */
  662. next_line = FindExitLineOnStack(l); /* END SELECT */
  663. if (next_line == NULL)
  664. {
  665. bwb_error("CASE ELSE without SELECT CASE");
  666. return bwb_zline(l);
  667. }
  668. switch (CURTASK excs[CURTASK exsc].code)
  669. {
  670. case EXEC_SELTRUE: /* SELECT has been processed */
  671. /* jump to the END SELECT */
  672. next_line->position = 0;
  673. return next_line;
  674. break;
  675. case EXEC_SELFALSE:
  676. CURTASK excs[CURTASK exsc].code = EXEC_SELTRUE; /* SELECT has been
  677. * processed */
  678. /* fall thru to execute this code block */
  679. break;
  680. default:
  681. /* the BASIC program has jumped into the middle of a
  682. * structured command */
  683. bwb_error("CASE ELSE without SELECT CASE");
  684. break;
  685. }
  686. return bwb_zline(l);
  687. }
  688. struct bwb_line *
  689. bwb_CASE(struct bwb_line * l) /* TODO - FIXME */
  690. {
  691. struct bwb_line *next_line;
  692. bwx_DEBUG(__FUNCTION__);
  693. /* there are two legitamate ways to get here: 1. a fall thru from an
  694. * upper block, code == EXEC_SELTRUE we should jump to the END SELECT
  695. * 2. a jump from from an upper block, code == EXEC_SELFALSE we
  696. * should execute our code block */
  697. next_line = FindExitLineOnStack(l); /* END SELECT */
  698. if (next_line == NULL)
  699. {
  700. bwb_error("CASE without SELECT CASE");
  701. return bwb_zline(l);
  702. }
  703. switch (CURTASK excs[CURTASK exsc].code)
  704. {
  705. case EXEC_SELTRUE: /* SELECT has been processed */
  706. /* jump to the END SELECT */
  707. next_line->position = 0;
  708. return next_line;
  709. break;
  710. case EXEC_SELFALSE:
  711. /* fall thru to execute this code block (maybe) */
  712. {
  713. struct exp_ese minvalue;
  714. struct exp_ese *minval;
  715. struct bwb_line *case_line;
  716. int IsExpression;
  717. IsExpression = FALSE;
  718. minval = bwb_exp(l->buffer, FALSE, &(l->position));
  719. if (ERROR_PENDING)
  720. {
  721. return bwb_zline(l);
  722. }
  723. memcpy(&minvalue, minval, sizeof(struct exp_ese));
  724. minval = &minvalue;
  725. /* check for string value */
  726. if (minvalue.type == STRING)
  727. { /* STRING */
  728. bstring *a;
  729. bstring *b;
  730. a = &(CURTASK excs[CURTASK exsc].expression.sval);
  731. b = &(minvalue.sval);
  732. if (str_cmp(a, b) == 0)
  733. {
  734. IsExpression = TRUE;
  735. }
  736. }
  737. /* STRING */
  738. else
  739. { /* NUMBER */
  740. struct exp_ese *maxval;
  741. maxval = minval;
  742. /* not a string; advance */
  743. adv_ws(l->buffer, &(l->position));
  744. /* check for TO */
  745. if (is_eol(l->buffer, &(l->position)) != TRUE)
  746. {
  747. char tbuf[BasicStringLengthMax + 1];
  748. /* find the TO statement */
  749. adv_element(l->buffer, &(l->position), tbuf);
  750. if (strcasecmp(tbuf, "TO") != 0)
  751. {
  752. sprintf(bwb_ebuf, "CASE has inexplicable code following expression");
  753. bwb_error(bwb_ebuf);
  754. adv_eos(l->buffer, &(l->position));
  755. return bwb_zline(l);
  756. }
  757. /* now evaluate the MAX expression */
  758. maxval = bwb_exp(l->buffer, FALSE, &(l->position));
  759. if (ERROR_PENDING)
  760. {
  761. return bwb_zline(l);
  762. }
  763. }
  764. /* evaluate the expression */
  765. if (case_eval(&(CURTASK excs[CURTASK exsc].expression), minval, maxval) == TRUE)
  766. {
  767. IsExpression = TRUE;
  768. }
  769. } /* NUMBER */
  770. if (IsExpression == TRUE)
  771. {
  772. /* condition is TRUE: proceed to the next
  773. * line */
  774. CURTASK excs[CURTASK exsc].code = EXEC_SELTRUE; /* SELECT has been
  775. * processed */
  776. /* fall thru to execute this code block */
  777. adv_eos(l->buffer, &(l->position));
  778. return bwb_zline(l);
  779. }
  780. /* evaluation returns a FALSE value; find next CASExx
  781. * statement */
  782. case_line = find_NextTestInCode(l); /* CASE or CASE IF or
  783. * CASE ELSE */
  784. if (case_line != NULL)
  785. {
  786. case_line->position = 0;
  787. return case_line;
  788. }
  789. /* no more CASExx lines */
  790. /* jump to the END SELECT */
  791. next_line->position = 0;
  792. return next_line;
  793. }
  794. break;
  795. default:
  796. /* the BASIC program has jumped into the middle of a
  797. * structured command */
  798. bwb_error("CASE without SELECT CASE");
  799. break;
  800. }
  801. return bwb_zline(l);
  802. }
  803. /***************************************************************
  804. FUNCTION: bwb_caseif()
  805. DESCRIPTION: This C function handles the BASIC CASE IF
  806. statement.
  807. ***************************************************************/
  808. struct bwb_line *
  809. bwb_CASE_IS(struct bwb_line * l)
  810. {
  811. bwx_DEBUG(__FUNCTION__);
  812. return bwb_CASE_IF(l);
  813. }
  814. struct bwb_line *
  815. bwb_CASE_IF(struct bwb_line * l)
  816. {
  817. struct bwb_line *next_line;
  818. bwx_DEBUG(__FUNCTION__);
  819. /* there are two legitamate ways to get here: 1. a fall thru from an
  820. * upper block, code == EXEC_SELTRUE we should jump to the END SELECT
  821. * 2. a jump from from an upper block, code == EXEC_SELFALSE we
  822. * should execute our code block */
  823. next_line = FindExitLineOnStack(l); /* END SELECT */
  824. if (next_line == NULL)
  825. {
  826. bwb_error("CASE IF without SELECT CASE");
  827. return bwb_zline(l);
  828. }
  829. switch (CURTASK excs[CURTASK exsc].code)
  830. {
  831. case EXEC_SELTRUE: /* IF has been processed */
  832. /* jump to the END IF */
  833. next_line->position = 0;
  834. return next_line;
  835. break;
  836. case EXEC_SELFALSE:
  837. /* execute our code block (maybe) */
  838. {
  839. char tbuf[BasicStringLengthMax + 1];
  840. int position;
  841. struct exp_ese *r;
  842. struct bwb_line *case_line;
  843. if (CURTASK excs[CURTASK exsc].expression.type == NUMBER)
  844. {
  845. sprintf(tbuf, BasicNumberPrintFormat " %s",
  846. CURTASK excs[CURTASK exsc].expression.nval,
  847. &(l->buffer[l->position]));
  848. }
  849. else
  850. {
  851. bwb_error(err_mismatch);
  852. adv_eos(l->buffer, &(l->position));
  853. return bwb_zline(l);
  854. }
  855. position = 0;
  856. r = bwb_exp(tbuf, FALSE, &position);
  857. if (ERROR_PENDING)
  858. {
  859. return bwb_zline(l);
  860. }
  861. if (r->nval == TRUE)
  862. {
  863. /* condition is TRUE: proceed to the next
  864. * line */
  865. CURTASK excs[CURTASK exsc].code = EXEC_SELTRUE; /* SELECT has been
  866. * processed */
  867. /* fall thru to execute this code block */
  868. adv_eos(l->buffer, &(l->position));
  869. return bwb_zline(l);
  870. }
  871. /* condition is FALSE */
  872. /* proceed to next CASE line if there is one */
  873. case_line = find_NextTestInCode(l); /* CASE IF or CASE ELSE */
  874. if (case_line != NULL)
  875. {
  876. case_line->position = 0;
  877. return case_line;
  878. }
  879. /* no more CASExx lines */
  880. /* jump to the END SELECT */
  881. next_line->position = 0;
  882. return next_line;
  883. }
  884. break;
  885. default:
  886. /* the BASIC program has jumped into the middle of a
  887. * structured command */
  888. bwb_error("CASE IF without SELECT CASE");
  889. break;
  890. }
  891. return bwb_zline(l);
  892. }
  893. /***************************************************************
  894. FUNCTION: case_eval()
  895. DESCRIPTION: This function evaluates a case statement
  896. by comparing minimum and maximum values
  897. with a set expression. It returns either
  898. TRUE or FALSE
  899. ***************************************************************/
  900. static int
  901. case_eval(struct exp_ese * expression, struct exp_ese * minval,
  902. struct exp_ese * maxval)
  903. {
  904. bwx_DEBUG(__FUNCTION__);
  905. /* string value */
  906. if (expression->type == STRING)
  907. {
  908. bwb_error(err_mismatch);
  909. return FALSE;
  910. }
  911. /* numerical value */
  912. if ((expression->nval >= minval->nval)
  913. && (expression->nval <= maxval->nval))
  914. {
  915. return TRUE;
  916. }
  917. return FALSE;
  918. }
  919. /***************************************************************
  920. FUNCTION: bwb_endselect()
  921. DESCRIPTION: This function handles the BASIC END
  922. SELECT statement.
  923. SYNTAX: END SELECT
  924. ***************************************************************/
  925. struct bwb_line *
  926. bwb_END_SELECT(struct bwb_line * l)
  927. {
  928. bwx_DEBUG(__FUNCTION__);
  929. /* check integrity of SELECT CASE commmand */
  930. if (FindBottomLineOnStack(l) == FALSE)
  931. {
  932. /* NOT FOUND */
  933. bwb_error("END SELECT without SELECT CASE");
  934. return bwb_zline(l);
  935. }
  936. switch (CURTASK excs[CURTASK exsc].code)
  937. {
  938. case EXEC_SELTRUE: /* SELECT has been processed */
  939. break;
  940. case EXEC_SELFALSE:
  941. break;
  942. default:
  943. /* the BASIC program has jumped into the middle of a
  944. * structured command */
  945. bwb_error("END SELECT without SELECT CASE");
  946. }
  947. /* remove SELECT from the stack */
  948. bwb_decexec();
  949. bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code);
  950. return bwb_zline(l);
  951. }
  952. /*
  953. --------------------------------------------------------------------------------------------
  954. DO - LOOP
  955. --------------------------------------------------------------------------------------------
  956. */
  957. /***************************************************************
  958. FUNCTION: bwb_DO()
  959. DESCRIPTION: This C function implements the ANSI BASIC
  960. DO statement, when DO is not followed by
  961. an argument. It is called by bwb_do() in
  962. bwb_cmd.c.
  963. SYNTAX: DO ' forever
  964. ***************************************************************/
  965. struct bwb_line *
  966. bwb_DO(struct bwb_line * l)
  967. {
  968. bwx_DEBUG(__FUNCTION__);
  969. /* if this is the first time at this DO statement, note it */
  970. #if 0
  971. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  972. #endif
  973. if (FindTopLineOnStack(l) == FALSE)
  974. {
  975. bwb_incexec();
  976. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  977. /* find the LOOP statement */
  978. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);;
  979. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  980. {
  981. /* NOT FOUND */
  982. bwb_error("DO without LOOP");
  983. return bwb_zline(l);
  984. }
  985. }
  986. bwb_setexec(l, l->position, EXEC_DO);
  987. return bwb_zline(l);
  988. }
  989. /***************************************************************
  990. FUNCTION: bwb_DO_UNTIL()
  991. DESCRIPTION: This C function implements the ANSI BASIC
  992. DO UNTIL statement, when DO is not followed by
  993. an argument. It is called by bwb_do() in
  994. bwb_cmd.c.
  995. SYNTAX: DO UNTIL expression ' exit when X <> 0
  996. ***************************************************************/
  997. struct bwb_line *
  998. bwb_DO_UNTIL(struct bwb_line * l)
  999. {
  1000. struct exp_ese *e;
  1001. bwx_DEBUG(__FUNCTION__);
  1002. /* if this is the first time at this WHILE statement, note it */
  1003. #if 0
  1004. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  1005. #endif
  1006. if (FindTopLineOnStack(l) == FALSE)
  1007. {
  1008. bwb_incexec();
  1009. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  1010. /* find the UEND statement (or LOOP statement) */
  1011. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);
  1012. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  1013. {
  1014. /* NOT FOUND */
  1015. bwb_error("DO without LOOP");
  1016. return bwb_zline(l);
  1017. }
  1018. }
  1019. /*----------------------------------------------------*/
  1020. /* Expression evaluation was at the top of bwb_while, */
  1021. /* and the init portion was performed only if TRUE. */
  1022. /* The init routine should be performed regardless of */
  1023. /* expression value, else a segmentation fault can */
  1024. /* occur! (JBV) */
  1025. /*----------------------------------------------------*/
  1026. /* call bwb_exp() to interpret the expression */
  1027. e = bwb_exp(l->buffer, FALSE, &(l->position));
  1028. if (ERROR_PENDING)
  1029. {
  1030. return bwb_zline(l);
  1031. }
  1032. if (exp_getival(e) != FALSE)
  1033. {
  1034. /* EXIT DO */
  1035. struct bwb_line *r;
  1036. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1037. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1038. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1039. r->position = 0;
  1040. bwb_decexec();
  1041. return r;
  1042. }
  1043. /* DO UNTIL ... */
  1044. bwb_setexec(l, l->position, EXEC_DO);
  1045. return bwb_zline(l);
  1046. }
  1047. /***************************************************************
  1048. FUNCTION: bwb_DO_WHILE()
  1049. DESCRIPTION: This C function implements the ANSI BASIC
  1050. DO statement, when DO is not followed by
  1051. an argument. It is called by bwb_do() in
  1052. bwb_cmd.c.
  1053. SYNTAX: DO WHILE expression ' exit when X = 0
  1054. ***************************************************************/
  1055. struct bwb_line *
  1056. bwb_DO_WHILE(struct bwb_line * l)
  1057. {
  1058. struct exp_ese *e;
  1059. bwx_DEBUG(__FUNCTION__);
  1060. /* if this is the first time at this WHILE statement, note it */
  1061. #if 0
  1062. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  1063. #endif
  1064. if (FindTopLineOnStack(l) == FALSE)
  1065. {
  1066. bwb_incexec();
  1067. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  1068. /* find the LOOP statement */
  1069. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);
  1070. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  1071. {
  1072. /* NOT FOUND */
  1073. bwb_error("DO without LOOP");
  1074. return bwb_zline(l);
  1075. }
  1076. }
  1077. /*----------------------------------------------------*/
  1078. /* Expression evaluation was at the top of bwb_while, */
  1079. /* and the init portion was performed only if TRUE. */
  1080. /* The init routine should be performed regardless of */
  1081. /* expression value, else a segmentation fault can */
  1082. /* occur! (JBV) */
  1083. /*----------------------------------------------------*/
  1084. /* call bwb_exp() to interpret the expression */
  1085. e = bwb_exp(l->buffer, FALSE, &(l->position));
  1086. if (ERROR_PENDING)
  1087. {
  1088. return bwb_zline(l);
  1089. }
  1090. if (exp_getival(e) == FALSE)
  1091. {
  1092. /* EXIT DO */
  1093. struct bwb_line *r;
  1094. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1095. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1096. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1097. r->position = 0;
  1098. bwb_decexec();
  1099. return r;
  1100. }
  1101. /* DO WHILE ... */
  1102. bwb_setexec(l, l->position, EXEC_DO);
  1103. return bwb_zline(l);
  1104. }
  1105. /***************************************************************
  1106. FUNCTION: bwb_EXIT_DO()
  1107. DESCRIPTION: This function handles the BASIC EXIT
  1108. DO statement. This is a structured
  1109. programming command compatible with ANSI
  1110. BASIC. It is called from the bwb_exit()
  1111. subroutine.
  1112. ***************************************************************/
  1113. struct bwb_line *
  1114. bwb_EXIT_DO(struct bwb_line * l)
  1115. {
  1116. struct bwb_line *next_line;
  1117. bwx_DEBUG(__FUNCTION__);
  1118. next_line = FindExitLineOnStack(l);
  1119. if (next_line == NULL)
  1120. {
  1121. bwb_error("EXIT DO without DO");
  1122. return bwb_zline(l);
  1123. }
  1124. {
  1125. /* EXIT DO */
  1126. struct bwb_line *r;
  1127. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1128. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1129. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1130. r->position = 0;
  1131. bwb_decexec();
  1132. return r;
  1133. }
  1134. #if 0
  1135. next_line = next_line->next;
  1136. /* set the next line in the exec stack */
  1137. next_line->position = 0;
  1138. bwb_setexec(next_line, 0, EXEC_DO);
  1139. return next_line;
  1140. #endif
  1141. }
  1142. /***************************************************************
  1143. FUNCTION: bwb_LOOP()
  1144. DESCRIPTION: This C function implements the ANSI BASIC
  1145. LOOP statement.
  1146. SYNTAX: LOOP ' forever
  1147. ***************************************************************/
  1148. struct bwb_line *
  1149. bwb_LOOP(struct bwb_line * l)
  1150. {
  1151. bwx_DEBUG(__FUNCTION__);
  1152. /* check integrity of DO loop */
  1153. if (FindBottomLineOnStack(l) == FALSE)
  1154. {
  1155. /* NOT FOUND */
  1156. bwb_error("LOOP without DO");
  1157. return bwb_zline(l);
  1158. }
  1159. /* reset to the top of the current DO loop */
  1160. CURTASK excs[CURTASK exsc].LoopTopLine->position = 0;
  1161. bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_DO);
  1162. return CURTASK excs[CURTASK exsc].LoopTopLine;
  1163. }
  1164. /***************************************************************
  1165. FUNCTION: bwb_LOOP_UNTIL()
  1166. DESCRIPTION: This C function implements the ANSI BASIC
  1167. LOOP UNTIL statement and is called by
  1168. bwb_loop().
  1169. SYNTAX: LOOP UNTIL expression ' exit when X <> 0
  1170. ***************************************************************/
  1171. struct bwb_line *
  1172. bwb_LOOP_UNTIL(struct bwb_line * l)
  1173. {
  1174. struct exp_ese *e;
  1175. bwx_DEBUG(__FUNCTION__);
  1176. if (FindBottomLineOnStack(l) == FALSE)
  1177. {
  1178. /* NOT FOUND */
  1179. bwb_error("LOOP without DO");
  1180. return bwb_zline(l);
  1181. }
  1182. /* call bwb_exp() to interpret the expression */
  1183. e = bwb_exp(l->buffer, FALSE, &(l->position));
  1184. if (ERROR_PENDING)
  1185. {
  1186. return bwb_zline(l);
  1187. }
  1188. if (exp_getival(e) != FALSE)
  1189. {
  1190. /* EXIT DO */
  1191. struct bwb_line *r;
  1192. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1193. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1194. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1195. r->position = 0;
  1196. bwb_decexec();
  1197. return r;
  1198. }
  1199. /* loop around to DO again */
  1200. CURTASK excs[CURTASK exsc].LoopTopLine->position = 0;
  1201. bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_DO);
  1202. return CURTASK excs[CURTASK exsc].LoopTopLine;
  1203. }
  1204. /***************************************************************
  1205. FUNCTION: bwb_LOOP_WHILE()
  1206. DESCRIPTION: This C function implements the BASIC
  1207. LOOP WHILE statement and is called by
  1208. bwb_loop().
  1209. SYNTAX: LOOP WHILE expression ' exit when X = 0
  1210. ***************************************************************/
  1211. struct bwb_line *
  1212. bwb_LOOP_WHILE(struct bwb_line * l)
  1213. {
  1214. struct exp_ese *e;
  1215. bwx_DEBUG(__FUNCTION__);
  1216. if (FindBottomLineOnStack(l) == FALSE)
  1217. {
  1218. /* NOT FOUND */
  1219. bwb_error("LOOP without DO");
  1220. return bwb_zline(l);
  1221. }
  1222. /* call bwb_exp() to interpret the expression */
  1223. e = bwb_exp(l->buffer, FALSE, &(l->position));
  1224. if (ERROR_PENDING)
  1225. {
  1226. return bwb_zline(l);
  1227. }
  1228. if (exp_getival(e) == FALSE)
  1229. {
  1230. /* EXIT DO */
  1231. struct bwb_line *r;
  1232. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1233. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1234. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1235. r->position = 0;
  1236. bwb_decexec();
  1237. return r;
  1238. }
  1239. /* execute DO-LOOP again */
  1240. CURTASK excs[CURTASK exsc].LoopTopLine->position = 0;
  1241. bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_DO);
  1242. return CURTASK excs[CURTASK exsc].LoopTopLine;
  1243. }
  1244. /*
  1245. --------------------------------------------------------------------------------------------
  1246. WHILE - WEND
  1247. --------------------------------------------------------------------------------------------
  1248. */
  1249. /***************************************************************
  1250. FUNCTION: bwb_WHILE()
  1251. DESCRIPTION: This function handles the BASIC
  1252. WHILE statement.
  1253. SYNTAX: WHILE expression
  1254. ...
  1255. WEND
  1256. SYNTAX: WHILE expression ' exit when X = 0
  1257. ***************************************************************/
  1258. struct bwb_line *
  1259. bwb_WHILE(struct bwb_line * l)
  1260. {
  1261. struct exp_ese *e;
  1262. bwx_DEBUG(__FUNCTION__);
  1263. /* if this is the first time at this WHILE statement, note it */
  1264. #if 0
  1265. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  1266. #endif
  1267. if (FindTopLineOnStack(l) == FALSE)
  1268. {
  1269. bwb_incexec();
  1270. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  1271. /* find the LOOP statement */
  1272. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);
  1273. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  1274. {
  1275. /* NOT FOUND */
  1276. bwb_error("WHILE without WEND");
  1277. return bwb_zline(l);
  1278. }
  1279. }
  1280. /*----------------------------------------------------*/
  1281. /* Expression evaluation was at the top of bwb_while, */
  1282. /* and the init portion was performed only if TRUE. */
  1283. /* The init routine should be performed regardless of */
  1284. /* expression value, else a segmentation fault can */
  1285. /* occur! (JBV) */
  1286. /*----------------------------------------------------*/
  1287. /* call bwb_exp() to interpret the expression */
  1288. e = bwb_exp(l->buffer, FALSE, &(l->position));
  1289. if (ERROR_PENDING)
  1290. {
  1291. return bwb_zline(l);
  1292. }
  1293. if (exp_getival(e) == FALSE)
  1294. {
  1295. /* EXIT WHILE */
  1296. struct bwb_line *r;
  1297. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1298. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1299. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1300. r->position = 0;
  1301. bwb_decexec();
  1302. return r;
  1303. }
  1304. /* WHILE ... */
  1305. bwb_setexec(l, l->position, EXEC_WHILE);
  1306. return bwb_zline(l);
  1307. }
  1308. /***************************************************************
  1309. FUNCTION: bwb_exitwhile()
  1310. DESCRIPTION: This function handles the BASIC EXIT
  1311. WHILE statement. This is a structured
  1312. programming command compatible with ANSI
  1313. BASIC. It is called from the bwb_exit()
  1314. subroutine.
  1315. ***************************************************************/
  1316. struct bwb_line *
  1317. bwb_EXIT_WHILE(struct bwb_line * l)
  1318. {
  1319. struct bwb_line *next_line;
  1320. bwx_DEBUG(__FUNCTION__);
  1321. /* Check the integrity of the WHILE statement */
  1322. next_line = FindExitLineOnStack(l);
  1323. if (next_line == NULL)
  1324. {
  1325. bwb_error("EXIT WHILE without WHILE");
  1326. return bwb_zline(l);
  1327. }
  1328. {
  1329. /* EXIT WHILE */
  1330. struct bwb_line *r;
  1331. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1332. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1333. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1334. r->position = 0;
  1335. bwb_decexec();
  1336. return r;
  1337. }
  1338. #if 0
  1339. next_line = next_line->next;
  1340. /* set the next line in the exec stack */
  1341. next_line->position = 0;
  1342. bwb_setexec(next_line, 0, EXEC_WHILE);
  1343. return next_line;
  1344. #endif
  1345. }
  1346. /***************************************************************
  1347. FUNCTION: bwb_wend()
  1348. DESCRIPTION: This function handles the BASIC WEND
  1349. statement and the LOOP statement ending
  1350. a DO WHILE loop.
  1351. SYNTAX: WEND
  1352. LOOP
  1353. ***************************************************************/
  1354. struct bwb_line *
  1355. bwb_WEND(struct bwb_line * l)
  1356. {
  1357. bwx_DEBUG(__FUNCTION__);
  1358. /* check integrity of WHILE loop */
  1359. if (FindBottomLineOnStack(l) == FALSE)
  1360. {
  1361. /* NOT FOUND */
  1362. bwb_error("WEND without WHILE");
  1363. return bwb_zline(l);
  1364. }
  1365. /* reset to the top of the current WHILE loop */
  1366. CURTASK excs[CURTASK exsc].LoopTopLine->position = 0;
  1367. bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_WHILE);
  1368. return CURTASK excs[CURTASK exsc].LoopTopLine;
  1369. }
  1370. /*
  1371. --------------------------------------------------------------------------------------------
  1372. UNTIL - UEND
  1373. --------------------------------------------------------------------------------------------
  1374. */
  1375. /***************************************************************
  1376. FUNCTION: bwb_UNTIL()
  1377. DESCRIPTION: This function handles the BASIC
  1378. UNTIL statement.
  1379. SYNTAX: UNTIL expression ' exit when X <> 0
  1380. ...
  1381. UEND
  1382. ***************************************************************/
  1383. struct bwb_line *
  1384. bwb_UNTIL(struct bwb_line * l)
  1385. {
  1386. struct exp_ese *e;
  1387. bwx_DEBUG(__FUNCTION__);
  1388. /* if this is the first time at this WHILE statement, note it */
  1389. #if 0
  1390. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  1391. #endif
  1392. if (FindTopLineOnStack(l) == FALSE)
  1393. {
  1394. bwb_incexec();
  1395. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  1396. /* find the UEND statement (or LOOP statement) */
  1397. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);
  1398. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  1399. {
  1400. /* NOT FOUND */
  1401. bwb_error("UNTIL without UEND");
  1402. return bwb_zline(l);
  1403. }
  1404. }
  1405. /*----------------------------------------------------*/
  1406. /* Expression evaluation was at the top of bwb_while, */
  1407. /* and the init portion was performed only if TRUE. */
  1408. /* The init routine should be performed regardless of */
  1409. /* expression value, else a segmentation fault can */
  1410. /* occur! (JBV) */
  1411. /*----------------------------------------------------*/
  1412. /* call bwb_exp() to interpret the expression */
  1413. e = bwb_exp(l->buffer, FALSE, &(l->position));
  1414. if (ERROR_PENDING)
  1415. {
  1416. return bwb_zline(l);
  1417. }
  1418. if (exp_getival(e) != FALSE)
  1419. {
  1420. /* EXIT UNTIL */
  1421. struct bwb_line *r;
  1422. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1423. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1424. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1425. r->position = 0;
  1426. bwb_decexec();
  1427. return r;
  1428. }
  1429. /* UNTIL ... */
  1430. bwb_setexec(l, l->position, EXEC_UNTIL);
  1431. return bwb_zline(l);
  1432. }
  1433. /***************************************************************
  1434. FUNCTION: bwb_exituntil()
  1435. DESCRIPTION: This function handles the BASIC EXIT
  1436. UNTIL statement. This is a structured
  1437. programming command compatible with ANSI
  1438. BASIC. It is called from the bwb_exit()
  1439. subroutine.
  1440. ***************************************************************/
  1441. struct bwb_line *
  1442. bwb_EXIT_UNTIL(struct bwb_line * l)
  1443. {
  1444. struct bwb_line *next_line;
  1445. bwx_DEBUG(__FUNCTION__);
  1446. /* Check the integrity of the UNTIL statement */
  1447. next_line = FindExitLineOnStack(l);
  1448. if (next_line == NULL)
  1449. {
  1450. bwb_error("EXIT UNTIL without UNTIL");
  1451. return bwb_zline(l);
  1452. }
  1453. {
  1454. /* EXIT UNTIL */
  1455. struct bwb_line *r;
  1456. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1457. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1458. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1459. r->position = 0;
  1460. bwb_decexec();
  1461. return r;
  1462. }
  1463. #if 0
  1464. next_line = next_line->next;
  1465. /* set the next line in the exec stack */
  1466. next_line->position = 0;
  1467. bwb_setexec(next_line, 0, EXEC_UNTIL);
  1468. return next_line;
  1469. #endif
  1470. }
  1471. /***************************************************************
  1472. FUNCTION: bwb_uend()
  1473. DESCRIPTION: This function handles the BASIC UEND
  1474. statement and the LOOP statement ending
  1475. a DO UNTIL loop.
  1476. SYNTAX: UEND
  1477. ***************************************************************/
  1478. struct bwb_line *
  1479. bwb_UEND(struct bwb_line * l)
  1480. {
  1481. bwx_DEBUG(__FUNCTION__);
  1482. /* check integrity of UNTIL loop */
  1483. if (FindBottomLineOnStack(l) == FALSE)
  1484. {
  1485. /* NOT FOUND */
  1486. bwb_error("UEND without UNTIL");
  1487. return bwb_zline(l);
  1488. }
  1489. /* reset to the top of the current UNTIL loop */
  1490. CURTASK excs[CURTASK exsc].LoopTopLine->position = 0;
  1491. bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine, 0, EXEC_UNTIL);
  1492. return CURTASK excs[CURTASK exsc].LoopTopLine;
  1493. }
  1494. /*
  1495. --------------------------------------------------------------------------------------------
  1496. FOR - NEXT
  1497. --------------------------------------------------------------------------------------------
  1498. */
  1499. /***************************************************************
  1500. FUNCTION: bwb_for()
  1501. DESCRIPTION: This function handles the BASIC FOR
  1502. statement.
  1503. SYNTAX: FOR counter = start TO finish [STEP increment]
  1504. NOTE: This is controlled by the OptionVersion bitmask.
  1505. The order of expression evaluation and variable creation varies.
  1506. For example:
  1507. FUNCTION FNA( Y )
  1508. PRINT "Y="; Y
  1509. FNA = Y
  1510. END FUNCTION
  1511. FOR X = FNA(3) TO FNA(1) STEP FNA(2)
  1512. NEXT X
  1513. ANSI/ECMA;
  1514. Y= 1
  1515. Y= 2
  1516. Y= 3
  1517. X is created (if it does not exist)
  1518. X is assigned the value of 3
  1519. MICROSOFT;
  1520. X is created (if it does not exist)
  1521. Y= 3
  1522. X is assigned the value of 3
  1523. Y= 1
  1524. Y= 2
  1525. ECMA-55: Section 13.4
  1526. ...
  1527. The action of the for-statement and the next-statement is de-
  1528. fined in terms of other statements, as follows:
  1529. FOR v = initial-value TO limit STEP increment
  1530. (block)
  1531. NEXT v
  1532. is equivalent to:
  1533. LET own1 = limit
  1534. LET own2 = increment
  1535. LET v = initial-value
  1536. line1 IF (v-own1) * SGN (own2) > 0 THEN line2
  1537. (block)
  1538. LET v = v + own2
  1539. GOTO line1
  1540. line2 REM continued in sequence
  1541. ...
  1542. ***************************************************************/
  1543. struct bwb_line *
  1544. bwb_FOR(struct bwb_line * l)
  1545. {
  1546. bwx_DEBUG(__FUNCTION__);
  1547. #if 0
  1548. if (CURTASK excs[CURTASK exsc].LoopTopLine != l)
  1549. #endif
  1550. if (FindTopLineOnStack(l) == FALSE)
  1551. {
  1552. bwb_incexec();
  1553. CURTASK excs[CURTASK exsc].LoopTopLine = l;
  1554. /* find the NEXT statement */
  1555. CURTASK excs[CURTASK exsc].LoopBottomLine = find_BottomLineInCode(l);
  1556. if (CURTASK excs[CURTASK exsc].LoopBottomLine == NULL)
  1557. {
  1558. /* NOT FOUND */
  1559. bwb_error("FOR without NEXT");
  1560. return bwb_zline(l);
  1561. }
  1562. }
  1563. /* -------------------------------------------------------------------
  1564. * ------------------------------- */
  1565. if (TRUE)
  1566. {
  1567. register int n;
  1568. int e, loop;
  1569. int to, step, p;
  1570. BasicNumberType for_step; /* STEP value for FOR */
  1571. BasicNumberType for_target; /* target value for FOR */
  1572. struct exp_ese *exp;
  1573. struct bwb_variable *v;
  1574. char tbuf[BasicStringLengthMax + 1];
  1575. int VarStart; /* position in l->buffer */
  1576. int VarLength;
  1577. int InitStart;
  1578. int InitLength;
  1579. int TermStart;
  1580. int TermLength;
  1581. int StepStart;
  1582. int StepLength;
  1583. BasicNumberType Value;
  1584. /* VarStart = 0; */
  1585. /* VarLength = 0; */
  1586. InitStart = 0;
  1587. InitLength = 0;
  1588. TermStart = 0;
  1589. TermLength = 0;
  1590. StepStart = 0;
  1591. StepLength = 0;
  1592. /* get the variable name */
  1593. VarStart = l->position;
  1594. exp_getvfname(&(l->buffer[l->position]), tbuf);
  1595. VarLength = strlen(tbuf);
  1596. l->position += VarLength;
  1597. /* at this point one should find an equals sign ('=') */
  1598. adv_ws(l->buffer, &(l->position));
  1599. if (l->buffer[l->position] != '=')
  1600. {
  1601. sprintf(bwb_ebuf, "in bwb_for(): failed to find equals sign, buf <%s>",
  1602. &(l->buffer[l->position]));
  1603. bwb_error(bwb_ebuf);
  1604. return bwb_zline(l);
  1605. }
  1606. else
  1607. {
  1608. ++(l->position);
  1609. }
  1610. /* Find the TO and STEP statements */
  1611. cnd_tostep(l->buffer, l->position, &to, &step);
  1612. /* if there is no TO statement, then an error has ocurred */
  1613. if (to < 1)
  1614. {
  1615. bwb_error("FOR without TO");
  1616. return bwb_zline(l);
  1617. }
  1618. /* copy initial value to buffer and evaluate it */
  1619. InitStart = l->position;
  1620. tbuf[0] = '\0';
  1621. p = 0;
  1622. for (n = l->position; n < to; ++n)
  1623. {
  1624. tbuf[p] = l->buffer[n];
  1625. ++p;
  1626. ++l->position;
  1627. tbuf[p] = '\0';
  1628. }
  1629. InitLength = p;
  1630. /* copy target value to small buffer and evaluate it */
  1631. tbuf[0] = '\0';
  1632. p = 0;
  1633. l->position = to + 2;
  1634. if (step < 1)
  1635. {
  1636. e = strlen(l->buffer);
  1637. }
  1638. else
  1639. {
  1640. e = step - 1;
  1641. }
  1642. loop = TRUE;
  1643. n = l->position;
  1644. TermStart = l->position;
  1645. while (loop == TRUE)
  1646. {
  1647. tbuf[p] = l->buffer[n];
  1648. ++p;
  1649. ++l->position;
  1650. tbuf[p] = '\0';
  1651. if (n >= e)
  1652. {
  1653. loop = FALSE;
  1654. }
  1655. ++n;
  1656. if (l->buffer[n] == '\0' || l->buffer[n] == OptionCommentChar)
  1657. {
  1658. loop = FALSE;
  1659. }
  1660. }
  1661. TermLength = p;
  1662. /* If there is a STEP statement, copy it to a buffer and
  1663. * evaluate it */
  1664. if (step > 1)
  1665. {
  1666. tbuf[0] = '\0';
  1667. p = 0;
  1668. l->position = step + 4;
  1669. StepStart = l->position;
  1670. for (n = l->position; n < (int) strlen(l->buffer); ++n)
  1671. {
  1672. tbuf[p] = l->buffer[n];
  1673. ++p;
  1674. ++l->position;
  1675. tbuf[p] = '\0';
  1676. }
  1677. StepLength = p;
  1678. }
  1679. if (OptionFlags & OPTION_BUGS_ON)
  1680. {
  1681. /* Create Variable */
  1682. if (VarLength < 1)
  1683. {
  1684. bwb_error(err_syntax);
  1685. return bwb_zline(l);
  1686. }
  1687. strncpy(tbuf, &l->buffer[VarStart], VarLength);
  1688. tbuf[VarLength] = '\0';
  1689. v = var_find(tbuf);
  1690. /* Init */
  1691. if (InitLength < 1)
  1692. {
  1693. bwb_error(err_syntax);
  1694. return bwb_zline(l);
  1695. }
  1696. strncpy(tbuf, &l->buffer[InitStart], InitLength);
  1697. tbuf[InitLength] = '\0';
  1698. p = 0;
  1699. exp = bwb_exp(tbuf, FALSE, &p);
  1700. if (ERROR_PENDING)
  1701. {
  1702. return bwb_zline(l);
  1703. }
  1704. Value = exp_getnval(exp);
  1705. /* Assign Variable */
  1706. var_setnval(v, Value);
  1707. /* Limit */
  1708. if (TermLength < 1)
  1709. {
  1710. bwb_error(err_syntax);
  1711. return bwb_zline(l);
  1712. }
  1713. strncpy(tbuf, &l->buffer[TermStart], TermLength);
  1714. tbuf[TermLength] = '\0';
  1715. p = 0;
  1716. exp = bwb_exp(tbuf, FALSE, &p);
  1717. if (ERROR_PENDING)
  1718. {
  1719. return bwb_zline(l);
  1720. }
  1721. for_target = exp_getnval(exp);
  1722. /* Step */
  1723. if (StepLength > 0)
  1724. {
  1725. strncpy(tbuf, &l->buffer[StepStart], StepLength);
  1726. tbuf[StepLength] = '\0';
  1727. p = 0;
  1728. exp = bwb_exp(tbuf, FALSE, &p);
  1729. if (ERROR_PENDING)
  1730. {
  1731. return bwb_zline(l);
  1732. }
  1733. for_step = exp_getnval(exp);
  1734. }
  1735. else
  1736. {
  1737. for_step = 1.0;
  1738. }
  1739. }
  1740. else
  1741. {
  1742. /* Limit */
  1743. if (TermLength < 1)
  1744. {
  1745. bwb_error(err_syntax);
  1746. return bwb_zline(l);
  1747. }
  1748. strncpy(tbuf, &l->buffer[TermStart], TermLength);
  1749. tbuf[TermLength] = '\0';
  1750. p = 0;
  1751. exp = bwb_exp(tbuf, FALSE, &p);
  1752. if (ERROR_PENDING)
  1753. {
  1754. return bwb_zline(l);
  1755. }
  1756. for_target = exp_getnval(exp);
  1757. /* Step */
  1758. if (StepLength > 0)
  1759. {
  1760. strncpy(tbuf, &l->buffer[StepStart], StepLength);
  1761. tbuf[StepLength] = '\0';
  1762. p = 0;
  1763. exp = bwb_exp(tbuf, FALSE, &p);
  1764. if (ERROR_PENDING)
  1765. {
  1766. return bwb_zline(l);
  1767. }
  1768. for_step = exp_getnval(exp);
  1769. }
  1770. else
  1771. {
  1772. for_step = 1.0;
  1773. }
  1774. /* Init */
  1775. if (InitLength < 1)
  1776. {
  1777. bwb_error(err_syntax);
  1778. return bwb_zline(l);
  1779. }
  1780. strncpy(tbuf, &l->buffer[InitStart], InitLength);
  1781. tbuf[InitLength] = '\0';
  1782. p = 0;
  1783. exp = bwb_exp(tbuf, FALSE, &p);
  1784. if (ERROR_PENDING)
  1785. {
  1786. return bwb_zline(l);
  1787. }
  1788. Value = exp_getnval(exp);
  1789. /* Create Variable */
  1790. if (VarLength < 1)
  1791. {
  1792. bwb_error(err_syntax);
  1793. return bwb_zline(l);
  1794. }
  1795. strncpy(tbuf, &l->buffer[VarStart], VarLength);
  1796. tbuf[VarLength] = '\0';
  1797. v = var_find(tbuf);
  1798. /* Assign Variable */
  1799. var_setnval(v, Value);
  1800. }
  1801. CURTASK excs[CURTASK exsc].local_variable = v;
  1802. CURTASK excs[CURTASK exsc].for_step = for_step;
  1803. CURTASK excs[CURTASK exsc].for_target = for_target;
  1804. }
  1805. /* -------------------------------------------------------------------
  1806. * ------------------------------ */
  1807. bwb_setexec(l, l->position, EXEC_FOR);
  1808. /* 800 FOR I1=9 TO I1 STEP I1 ' I1=-2 */
  1809. if (TRUE)
  1810. {
  1811. int IsExit;
  1812. BasicNumberType Value;
  1813. BasicNumberType Target;
  1814. BasicNumberType Step;
  1815. IsExit = FALSE;
  1816. Value = var_getnval(CURTASK excs[CURTASK exsc].local_variable);
  1817. Target = CURTASK excs[CURTASK exsc].for_target;
  1818. Step = CURTASK excs[CURTASK exsc].for_step;
  1819. if (Step > 0)
  1820. {
  1821. /* POSITIVE */
  1822. if (Value > Target)
  1823. {
  1824. IsExit = TRUE;
  1825. }
  1826. }
  1827. else
  1828. {
  1829. /* NEGATIVE */
  1830. if (Value < Target)
  1831. {
  1832. IsExit = TRUE;
  1833. }
  1834. }
  1835. if (IsExit == TRUE)
  1836. {
  1837. /* EXIT FOR */
  1838. struct bwb_line *next_line;
  1839. next_line = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1840. bwb_decexec();
  1841. /* set the next line in the exec stack */
  1842. next_line->position = 0;
  1843. /* bwb_setexec( next_line, 0, EXEC_NORM ); *//* WRONG
  1844. * (JBV) */
  1845. bwb_setexec(next_line, 0, CURTASK excs[CURTASK exsc].code); /* JBV */
  1846. return next_line;
  1847. }
  1848. }
  1849. /* proceed with processing */
  1850. return bwb_zline(l);
  1851. }
  1852. /***************************************************************
  1853. FUNCTION: bwb_exitfor()
  1854. DESCRIPTION: This function handles the BASIC EXIT
  1855. FOR statement. This is a structured
  1856. programming command compatible with ANSI
  1857. BASIC. It is called from the bwb_exit()
  1858. subroutine.
  1859. SYNTAX: EXIT FOR
  1860. ***************************************************************/
  1861. struct bwb_line *
  1862. bwb_EXIT_FOR(struct bwb_line * l)
  1863. {
  1864. struct bwb_line *next_line;
  1865. bwx_DEBUG(__FUNCTION__);
  1866. /* Check the integrity of the FOR statement */
  1867. next_line = FindExitLineOnStack(l);
  1868. if (next_line == NULL)
  1869. {
  1870. bwb_error("EXIT FOR without FOR");
  1871. return bwb_zline(l);
  1872. }
  1873. {
  1874. /* EXIT FOR */
  1875. struct bwb_line *r;
  1876. CURTASK excs[CURTASK exsc].LoopTopLine = NULL;
  1877. r = CURTASK excs[CURTASK exsc].LoopBottomLine->next;
  1878. bwb_setexec(r, 0, CURTASK excs[CURTASK exsc - 1].code);
  1879. r->position = 0;
  1880. bwb_decexec();
  1881. return r;
  1882. }
  1883. #if 0
  1884. next_line = next_line->next;
  1885. /* set the next line in the exec stack */
  1886. next_line->position = 0;
  1887. bwb_setexec(next_line, 0, EXEC_FOR);
  1888. return next_line;
  1889. #endif
  1890. }
  1891. /***************************************************************
  1892. FUNCTION: bwb_next()
  1893. DESCRIPTION: This function handles the BASIC NEXT
  1894. statement.
  1895. SYNTAX: NEXT counter
  1896. ***************************************************************/
  1897. struct bwb_line *
  1898. bwb_NEXT(struct bwb_line * l)
  1899. {
  1900. char tbuf[BasicStringLengthMax + 1];
  1901. bwx_DEBUG(__FUNCTION__);
  1902. if (FindBottomLineOnStack(l) == FALSE)
  1903. {
  1904. /* NOT FOUND */
  1905. bwb_error("NEXT without FOR");
  1906. return bwb_zline(l);
  1907. }
  1908. /* read the argument, if there is one */
  1909. exp_getvfname(&(l->buffer[l->position]), tbuf);
  1910. if (strlen(tbuf) != 0)
  1911. {
  1912. /* NEXT variablename */
  1913. struct bwb_variable *v;
  1914. v = var_find(tbuf);
  1915. l->position += strlen(tbuf);
  1916. /* decrement or increment the value */
  1917. if (CURTASK excs[CURTASK exsc].local_variable != v)
  1918. {
  1919. /* not found */
  1920. bwb_error("NEXT without FOR");
  1921. return bwb_zline(l);
  1922. }
  1923. }
  1924. else
  1925. {
  1926. /* NEXT */
  1927. if (CURTASK excs[CURTASK exsc].local_variable == NULL)
  1928. {
  1929. /* not found */
  1930. bwb_error("NEXT without FOR");
  1931. return bwb_zline(l);
  1932. }
  1933. }
  1934. var_setnval(CURTASK excs[CURTASK exsc].local_variable,
  1935. var_getnval(CURTASK excs[CURTASK exsc].local_variable)
  1936. + CURTASK excs[CURTASK exsc].for_step);
  1937. /* check for completion of the loop */
  1938. if (CURTASK excs[CURTASK exsc].for_step > 0) /* if step is positive */
  1939. {
  1940. if (var_getnval(CURTASK excs[CURTASK exsc].local_variable)
  1941. > CURTASK excs[CURTASK exsc].for_target)
  1942. {
  1943. bwb_decexec();
  1944. bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code);
  1945. return bwb_zline(l);
  1946. }
  1947. }
  1948. else /* if step is negative */
  1949. {
  1950. if (var_getnval(CURTASK excs[CURTASK exsc].local_variable)
  1951. < CURTASK excs[CURTASK exsc].for_target)
  1952. {
  1953. bwb_decexec();
  1954. bwb_setexec(l->next, 0, CURTASK excs[CURTASK exsc].code);
  1955. return bwb_zline(l);
  1956. }
  1957. }
  1958. /* Target not reached: return to the top of the FOR loop */
  1959. bwb_setexec(CURTASK excs[CURTASK exsc].LoopTopLine,
  1960. CURTASK excs[CURTASK exsc].LoopTopLine->position, EXEC_FOR);
  1961. return CURTASK excs[CURTASK exsc].LoopTopLine;
  1962. }
  1963. /***************************************************************
  1964. FUNCTION: cnd_tostep()
  1965. DESCRIPTION: This function searches through the
  1966. <buffer> beginning at point <position>
  1967. and attempts to find positions of TO
  1968. and STEP statements.
  1969. ***************************************************************/
  1970. static int
  1971. cnd_tostep(char *buffer, int position, int *to, int *step)
  1972. {
  1973. int loop, t_pos, b_pos, p_word;
  1974. char tbuf[BasicStringLengthMax + 1];
  1975. bwx_DEBUG(__FUNCTION__);
  1976. /* set then and els to FALSE initially */
  1977. *to = *step = FALSE;
  1978. /* loop to find words */
  1979. p_word = b_pos = position;
  1980. t_pos = 0;
  1981. tbuf[0] = '\0';
  1982. loop = TRUE;
  1983. while (loop == TRUE)
  1984. {
  1985. if (buffer[b_pos] == '\0' || buffer[b_pos] == OptionCommentChar)
  1986. {
  1987. /* end of string */
  1988. return TRUE;
  1989. }
  1990. switch (buffer[b_pos])
  1991. {
  1992. case ' ': /* whitespace = end of word */
  1993. if (strncasecmp(tbuf, "TO", (size_t) strlen("TO")) == 0)
  1994. {
  1995. *to = p_word;
  1996. }
  1997. else
  1998. if (strncasecmp(tbuf, "STEP", (size_t) strlen("STEP")) == 0)
  1999. {
  2000. *step = p_word;
  2001. }
  2002. ++b_pos;
  2003. p_word = b_pos;
  2004. t_pos = 0;
  2005. tbuf[0] = '\0';
  2006. break;
  2007. default:
  2008. tbuf[t_pos] = buffer[b_pos];
  2009. ++b_pos;
  2010. ++t_pos;
  2011. tbuf[t_pos] = '\0';
  2012. break;
  2013. }
  2014. }
  2015. return TRUE;
  2016. }
  2017. /***************************************************************
  2018. FUNCTION: var_setnval()
  2019. DESCRIPTION: This function sets the value of numerical
  2020. variable v to the value of i.
  2021. ***************************************************************/
  2022. int
  2023. var_setnval(struct bwb_variable * v, BasicNumberType i)
  2024. {
  2025. bwx_DEBUG(__FUNCTION__);
  2026. switch (v->type)
  2027. {
  2028. case NUMBER:
  2029. *var_findnval(v, v->array_pos) = i;
  2030. break;
  2031. default:
  2032. sprintf(bwb_ebuf, "in var_setnval(): variable <%s> is not a number",
  2033. v->name);
  2034. bwb_error(bwb_ebuf);
  2035. }
  2036. /* successful assignment */
  2037. return TRUE;
  2038. }
  2039. static
  2040. int
  2041. FindTopLineOnStack(struct bwb_line * l)
  2042. {
  2043. /* since we are the top of the loop, we MIGHT be on the stack */
  2044. int i;
  2045. bwx_DEBUG(__FUNCTION__);
  2046. for (i = CURTASK exsc; i >= 0; i--)
  2047. {
  2048. struct bwb_line *current;
  2049. current = CURTASK excs[i].LoopTopLine;
  2050. if (current != NULL)
  2051. {
  2052. if (current == l)
  2053. {
  2054. /* FOUND */
  2055. while (CURTASK exsc > i)
  2056. {
  2057. bwb_decexec();
  2058. }
  2059. /* we are now the top item on the stack */
  2060. return TRUE;
  2061. }
  2062. /* do NOT cross a function/sub boundary */
  2063. switch (current->cmdnum)
  2064. {
  2065. case C_FUNCTION:
  2066. case C_SUB:
  2067. /* NOT FOUND */
  2068. return FALSE;
  2069. break;
  2070. }
  2071. }
  2072. }
  2073. /* NOT FOUND */
  2074. return FALSE;
  2075. }
  2076. static
  2077. int
  2078. FindBottomLineOnStack(struct bwb_line * l)
  2079. {
  2080. /* since we are the bottom of the loop, we MUST be on the stack */
  2081. bwx_DEBUG(__FUNCTION__);
  2082. while (CURTASK exsc >= 0)
  2083. {
  2084. if (CURTASK excs[CURTASK exsc].LoopBottomLine == l)
  2085. {
  2086. /* FOUND */
  2087. return TRUE;
  2088. }
  2089. bwb_decexec();
  2090. }
  2091. /* NOT FOUND */
  2092. return FALSE;
  2093. }
  2094. static struct bwb_line *
  2095. FindExitLineOnStack(struct bwb_line * l)
  2096. {
  2097. /* we are an EXIT ... command, find the bottom line on the stack */
  2098. bwx_DEBUG(__FUNCTION__);
  2099. while (CURTASK exsc >= 0)
  2100. {
  2101. struct bwb_line *current;
  2102. current = CURTASK excs[CURTASK exsc].LoopBottomLine;
  2103. if (current != NULL)
  2104. {
  2105. switch (l->cmdnum)
  2106. {
  2107. case C_EXIT_DO:
  2108. /* DO - LOOP */
  2109. switch (current->cmdnum)
  2110. {
  2111. case C_LOOP:
  2112. case C_LOOP_UNTIL:
  2113. case C_LOOP_WHILE:
  2114. return current;
  2115. break;
  2116. }
  2117. break;
  2118. case C_EXIT_WHILE:
  2119. /* WHILE - WEND */
  2120. switch (current->cmdnum)
  2121. {
  2122. case C_WEND:
  2123. return current;
  2124. break;
  2125. }
  2126. break;
  2127. case C_EXIT_UNTIL:
  2128. /* UNTIL - UEND */
  2129. switch (current->cmdnum)
  2130. {
  2131. case C_UEND:
  2132. return current;
  2133. break;
  2134. }
  2135. break;
  2136. case C_EXIT_FOR:
  2137. /* FOR - NEXT */
  2138. switch (current->cmdnum)
  2139. {
  2140. case C_NEXT:
  2141. return current;
  2142. break;
  2143. }
  2144. break;
  2145. case C_EXIT_SUB:
  2146. /* SUB - END SUB */
  2147. switch (current->cmdnum)
  2148. {
  2149. case C_END_SUB:
  2150. return current;
  2151. break;
  2152. }
  2153. break;
  2154. case C_EXIT_FUNCTION:
  2155. /* FUNCTION - END FUNCTION */
  2156. switch (current->cmdnum)
  2157. {
  2158. case C_END_FUNCTION:
  2159. return current;
  2160. break;
  2161. }
  2162. break;
  2163. case C_ELSEIF:
  2164. case C_ELSE:
  2165. /* IF - END IF */
  2166. switch (current->cmdnum)
  2167. {
  2168. case C_END_IF:
  2169. return current;
  2170. break;
  2171. }
  2172. break;
  2173. case C_CASE_IF:
  2174. case C_CASE_IS:
  2175. case C_CASE:
  2176. case C_CASE_ELSE:
  2177. /* SELECT CASE - END SELECT */
  2178. switch (current->cmdnum)
  2179. {
  2180. case C_END_SELECT:
  2181. return current;
  2182. break;
  2183. }
  2184. break;
  2185. }
  2186. }
  2187. bwb_decexec();
  2188. }
  2189. /* NOT FOUND */
  2190. return FALSE;
  2191. }
  2192. static struct bwb_line *
  2193. scan_BottomLineInCode(struct bwb_line * l)
  2194. {
  2195. /* we are the TopLine. Let's find our matching BottomLine */
  2196. struct bwb_line *current;
  2197. register int w_level;
  2198. bwx_DEBUG(__FUNCTION__);
  2199. switch (l->cmdnum)
  2200. {
  2201. case C_DEF:
  2202. case C_USER_LBL:
  2203. /* just the single line */
  2204. return l;
  2205. break;
  2206. }
  2207. /* multiple lines */
  2208. w_level = 1;
  2209. for (current = l->next; current != &CURTASK bwb_end; current = current->next)
  2210. {
  2211. switch (l->cmdnum)
  2212. {
  2213. case C_DO:
  2214. case C_DO_WHILE:
  2215. case C_DO_UNTIL:
  2216. /* DO - LOOP */
  2217. switch (current->cmdnum)
  2218. {
  2219. case C_DO:
  2220. case C_DO_WHILE:
  2221. case C_DO_UNTIL:
  2222. ++w_level;
  2223. break;
  2224. case C_LOOP:
  2225. case C_LOOP_UNTIL:
  2226. case C_LOOP_WHILE:
  2227. --w_level;
  2228. if (w_level == 0)
  2229. {
  2230. return current;
  2231. }
  2232. break;
  2233. }
  2234. break;
  2235. case C_WHILE:
  2236. /* WHILE - WEND */
  2237. switch (current->cmdnum)
  2238. {
  2239. case C_WHILE:
  2240. ++w_level;
  2241. break;
  2242. case C_WEND:
  2243. --w_level;
  2244. if (w_level == 0)
  2245. {
  2246. return current;
  2247. }
  2248. break;
  2249. }
  2250. break;
  2251. case C_UNTIL:
  2252. /* UNTIL - UEND */
  2253. switch (current->cmdnum)
  2254. {
  2255. case C_UNTIL:
  2256. ++w_level;
  2257. break;
  2258. case C_UEND:
  2259. --w_level;
  2260. if (w_level == 0)
  2261. {
  2262. return current;
  2263. }
  2264. break;
  2265. }
  2266. break;
  2267. case C_FOR:
  2268. /* FOR - NEXT */
  2269. switch (current->cmdnum)
  2270. {
  2271. case C_FOR:
  2272. ++w_level;
  2273. break;
  2274. case C_NEXT:
  2275. --w_level;
  2276. if (w_level == 0)
  2277. {
  2278. return current;
  2279. }
  2280. break;
  2281. }
  2282. break;
  2283. case C_IF_THEN:
  2284. /* IF - END IF */
  2285. switch (current->cmdnum)
  2286. {
  2287. case C_IF_THEN:
  2288. ++w_level;
  2289. break;
  2290. case C_END_IF:
  2291. --w_level;
  2292. if (w_level == 0)
  2293. {
  2294. return current;
  2295. }
  2296. break;
  2297. }
  2298. break;
  2299. case C_SELECT_CASE:
  2300. /* SELECT CASE - END SELECT */
  2301. switch (current->cmdnum)
  2302. {
  2303. case C_SELECT_CASE:
  2304. ++w_level;
  2305. break;
  2306. case C_END_SELECT:
  2307. --w_level;
  2308. if (w_level == 0)
  2309. {
  2310. return current;
  2311. }
  2312. break;
  2313. }
  2314. break;
  2315. case C_SUB:
  2316. /* SUB - END SUB */
  2317. switch (current->cmdnum)
  2318. {
  2319. case C_SUB:
  2320. ++w_level;
  2321. break;
  2322. case C_END_SUB:
  2323. --w_level;
  2324. if (w_level == 0)
  2325. {
  2326. return current;
  2327. }
  2328. break;
  2329. }
  2330. break;
  2331. case C_FUNCTION:
  2332. /* SUB - END SUB */
  2333. switch (current->cmdnum)
  2334. {
  2335. case C_FUNCTION:
  2336. ++w_level;
  2337. break;
  2338. case C_END_FUNCTION:
  2339. --w_level;
  2340. if (w_level == 0)
  2341. {
  2342. return current;
  2343. }
  2344. break;
  2345. }
  2346. break;
  2347. default:
  2348. return NULL;
  2349. break;
  2350. }
  2351. }
  2352. /* NOT FOUND */
  2353. return NULL;
  2354. }
  2355. struct bwb_line *
  2356. find_BottomLineInCode(struct bwb_line * l)
  2357. {
  2358. /* we only want to scan once */
  2359. /* bwb_scan() sets l->OtherLine to NULL to ensure MERGE and DELETE
  2360. * work correctly */
  2361. bwx_DEBUG(__FUNCTION__);
  2362. if (l->OtherLine == NULL)
  2363. {
  2364. /* we do not yet know */
  2365. l->OtherLine = scan_BottomLineInCode(l);
  2366. if (l->OtherLine != NULL)
  2367. {
  2368. /* FOUND */
  2369. /* mark the bottom line to point to us, so we can use
  2370. * this info in DELETE */
  2371. l->OtherLine->OtherLine = l;
  2372. }
  2373. }
  2374. return l->OtherLine;
  2375. }
  2376. static struct bwb_line *
  2377. find_NextTestInCode(struct bwb_line * l)
  2378. {
  2379. struct bwb_line *current;
  2380. register int w_level;
  2381. bwx_DEBUG(__FUNCTION__);
  2382. w_level = 1;
  2383. for (current = l->next; current != &CURTASK bwb_end; current = current->next)
  2384. {
  2385. switch (l->cmdnum)
  2386. {
  2387. case C_IF_THEN:
  2388. case C_ELSEIF:
  2389. switch (current->cmdnum)
  2390. {
  2391. case C_IF_THEN:
  2392. ++w_level;
  2393. break;
  2394. case C_ELSEIF:
  2395. case C_ELSE:
  2396. /* we must report only the first ELSE we
  2397. * encounter at level 1 */
  2398. if (w_level == 1)
  2399. {
  2400. return current;
  2401. }
  2402. break;
  2403. case C_END_IF:
  2404. --w_level;
  2405. if (w_level == 0)
  2406. {
  2407. return NULL;
  2408. }
  2409. break;
  2410. }
  2411. break;
  2412. case C_CASE:
  2413. case C_CASE_IF:
  2414. case C_CASE_IS:
  2415. switch (current->cmdnum)
  2416. {
  2417. case C_SELECT_CASE:
  2418. ++w_level;
  2419. break;
  2420. case C_CASE:
  2421. case C_CASE_IF:
  2422. case C_CASE_IS:
  2423. case C_CASE_ELSE:
  2424. /* we must report only the first CASE we
  2425. * encounter at level 1 */
  2426. if (w_level == 1)
  2427. {
  2428. return current;
  2429. }
  2430. break;
  2431. case C_END_SELECT:
  2432. --w_level;
  2433. if (w_level == 0)
  2434. {
  2435. return NULL;
  2436. }
  2437. }
  2438. break;
  2439. default:
  2440. return NULL;
  2441. break;
  2442. }
  2443. }
  2444. return NULL;
  2445. }
  2446. /* EOF */