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.
 
 
 
 
 
 

983 lines
22 KiB

  1. /***************************************************************f
  2. bwb_int.c Line Interpretation Routines
  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. #include <stdio.h>
  25. #include <ctype.h>
  26. #include "bwbasic.h"
  27. #include "bwb_mes.h"
  28. /***************************************************************
  29. FUNCTION: adv_element()
  30. DESCRIPTION: This function reads characters in <buffer>
  31. beginning at <pos> and advances past a
  32. line element, incrementing <pos> appropri-
  33. ately and returning the line element in
  34. <element>.
  35. ***************************************************************/
  36. #if ANSI_C
  37. int
  38. adv_element( char *buffer, int *pos, char *element )
  39. #else
  40. int
  41. adv_element( buffer, pos, element )
  42. char *buffer;
  43. int *pos;
  44. char *element;
  45. #endif
  46. {
  47. int loop; /* control loop */
  48. int e_pos; /* position in element buffer */
  49. int str_const; /* boolean: building a string constant */
  50. /* advance beyond any initial whitespace */
  51. adv_ws( buffer, pos );
  52. #if INTENSIVE_DEBUG
  53. sprintf( bwb_ebuf, "in adv_element(): receieved <%s>.", &( buffer[ *pos ] ));
  54. bwb_debug( bwb_ebuf );
  55. #endif
  56. /* now loop while building an element and looking for an
  57. element terminator */
  58. loop = TRUE;
  59. e_pos = 0;
  60. element[ e_pos ] = '\0';
  61. str_const = FALSE;
  62. while ( loop == TRUE )
  63. {
  64. switch( buffer[ *pos ] )
  65. {
  66. case ',': /* element terminators */
  67. case ';':
  68. #if MULTISEG_LINES
  69. case ':':
  70. #endif
  71. case '=':
  72. case ' ':
  73. case '\t':
  74. /* case '\0': */ /* Removed by JBV (found by DD) */
  75. case '\n':
  76. case '\r':
  77. if ( str_const == TRUE )
  78. {
  79. element[ e_pos ] = buffer[ *pos ];
  80. ++e_pos;
  81. ++( *pos );
  82. element[ e_pos ] = '\0';
  83. }
  84. else
  85. {
  86. return TRUE;
  87. }
  88. break;
  89. case '\0': /* Added by JBV (found by DD) */
  90. if ( str_const == TRUE ) /* termination of string constant */
  91. {
  92. element[ e_pos ] = '\"';
  93. element[ ++e_pos ] = '\0';
  94. }
  95. return TRUE;
  96. break;
  97. case '\"': /* string constant */
  98. element[ e_pos ] = buffer[ *pos ];
  99. ++e_pos;
  100. ++( *pos );
  101. element[ e_pos ] = '\0';
  102. if ( str_const == TRUE ) /* termination of string constant */
  103. {
  104. return TRUE;
  105. }
  106. else /* beginning of string constant */
  107. {
  108. str_const = TRUE;
  109. }
  110. break;
  111. default:
  112. element[ e_pos ] = buffer[ *pos ];
  113. ++e_pos;
  114. ++( *pos );
  115. element[ e_pos ] = '\0';
  116. break;
  117. }
  118. }
  119. /* This should not happen */
  120. return FALSE;
  121. }
  122. /***************************************************************
  123. FUNCTION: adv_ws()
  124. DESCRIPTION: This function reads characters in <buffer>
  125. beginning at <pos> and advances past any
  126. whitespace, incrementing <pos> appropri-
  127. ately.
  128. ***************************************************************/
  129. #if ANSI_C
  130. int
  131. adv_ws( char *buffer, int *pos )
  132. #else
  133. int
  134. adv_ws( buffer, pos )
  135. char *buffer;
  136. int *pos;
  137. #endif
  138. {
  139. int loop;
  140. loop = TRUE;
  141. while ( loop == TRUE )
  142. {
  143. switch( buffer[ *pos ] )
  144. {
  145. case ' ':
  146. case '\t':
  147. ++( *pos );
  148. break;
  149. default:
  150. return TRUE;
  151. }
  152. }
  153. /* This should not happen */
  154. return FALSE;
  155. }
  156. /***************************************************************
  157. FUNCTION: adv_eos()
  158. DESCRIPTION: This function reads characters in <buffer>
  159. beginning at <pos> and advances to the
  160. end of a segment delimited by ':',
  161. incrementing <pos> appropriately.
  162. ***************************************************************/
  163. #if MULTISEG_LINES
  164. #if ANSI_C
  165. int
  166. adv_eos( char *buffer, int *pos )
  167. #else
  168. int
  169. adv_eos( buffer, pos )
  170. char *buffer;
  171. int *pos;
  172. #endif
  173. {
  174. int loop;
  175. loop = TRUE;
  176. while ( loop == TRUE )
  177. {
  178. if ( is_eol( buffer, pos ) == TRUE )
  179. {
  180. return FALSE;
  181. }
  182. switch( buffer[ *pos ] )
  183. {
  184. case ':': /* end of segment marker */
  185. ++( *pos );
  186. return TRUE;
  187. case '\"': /* begin quoted string */
  188. ++( *pos );
  189. while ( buffer[ *pos ] != '\"' )
  190. {
  191. if ( is_eol( buffer, pos ) == TRUE )
  192. {
  193. return FALSE;
  194. }
  195. else
  196. {
  197. ++( *pos );
  198. }
  199. }
  200. break;
  201. default:
  202. ++( *pos );
  203. }
  204. }
  205. /* This should not happen */
  206. return FALSE;
  207. }
  208. #endif /* MULTISEG_LINES */
  209. /***************************************************************
  210. FUNCTION: bwb_strtoupper()
  211. DESCRIPTION: This function converts the string in
  212. <buffer> to upper-case characters.
  213. ***************************************************************/
  214. #if ANSI_C
  215. int
  216. bwb_strtoupper( char *buffer )
  217. #else
  218. int
  219. bwb_strtoupper( buffer )
  220. char *buffer;
  221. #endif
  222. {
  223. char *p;
  224. p = buffer;
  225. while ( *p != '\0' )
  226. {
  227. if ( islower( *p ) != FALSE )
  228. {
  229. *p = (char) toupper( *p );
  230. }
  231. ++p;
  232. }
  233. return TRUE;
  234. }
  235. /***************************************************************
  236. FUNCTION: line_start()
  237. DESCRIPTION: This function reads a line buffer in
  238. <buffer> beginning at the position
  239. <pos> and attempts to determine (a)
  240. the position of the line number in the
  241. buffer (returned in <lnpos>), (b) the
  242. line number at this position (returned
  243. in <lnum>), (c) the position of the
  244. BASIC command in the buffer (returned
  245. in <cmdpos>), (d) the position of this
  246. BASIC command in the command table
  247. (returned in <cmdnum>), and (e) the
  248. position of the beginning of the rest
  249. of the line (returned in <startpos>).
  250. Although <startpos> must be returned
  251. as a positive integer, the other
  252. searches may fail, in which case FALSE
  253. will be returned in their positions.
  254. <pos> is not incremented.
  255. ***************************************************************/
  256. #if ANSI_C
  257. int
  258. line_start( char *buffer, int *pos, int *lnpos, int *lnum, int *cmdpos,
  259. int *cmdnum, int *startpos )
  260. #else
  261. int
  262. line_start( buffer, pos, lnpos, lnum, cmdpos, cmdnum, startpos )
  263. char *buffer;
  264. int *pos;
  265. int *lnpos;
  266. int *lnum;
  267. int *cmdpos;
  268. int *cmdnum;
  269. int *startpos;
  270. #endif
  271. {
  272. static int position;
  273. static char *tbuf;
  274. static int init = FALSE;
  275. /* get memory for temporary buffer if necessary */
  276. if ( init == FALSE )
  277. {
  278. init = TRUE;
  279. /* Revised to CALLOC pass-thru call by JBV */
  280. if ( ( tbuf = CALLOC( MAXSTRINGSIZE + 1, sizeof( char ), "line_start")) == NULL )
  281. {
  282. #if PROG_ERRORS
  283. bwb_error( "in line_start(): failed to get memory for tbuf" );
  284. #else
  285. bwb_error( err_getmem );
  286. #endif
  287. }
  288. }
  289. #if INTENSIVE_DEBUG
  290. sprintf( bwb_ebuf, "in line_start(): pos <%d> buffer <%s>", *pos,
  291. buffer );
  292. bwb_debug( bwb_ebuf );
  293. #endif
  294. /* set initial values */
  295. *startpos = position = *pos;
  296. *cmdpos = *lnpos = *pos;
  297. *cmdnum = *lnum = -1;
  298. /* check for null line */
  299. adv_ws( buffer, &position );
  300. if ( buffer[ position ] == '\0' )
  301. {
  302. #if INTENSIVE_DEBUG
  303. bwb_debug( "in line_start(): found NULL line" );
  304. #endif
  305. *cmdnum = getcmdnum( CMD_REM );
  306. return TRUE;
  307. }
  308. /* advance beyond the first element */
  309. *lnpos = position;
  310. scan_element( buffer, &position, tbuf );
  311. adv_ws( buffer, &position );
  312. /* test for a line number in the first element */
  313. if ( is_numconst( tbuf ) == TRUE ) /* a line number */
  314. {
  315. *lnum = atoi( tbuf );
  316. *startpos = position; /* temp */
  317. *cmdpos = position;
  318. scan_element( buffer, &position, tbuf ); /* advance past next element */
  319. #if INTENSIVE_DEBUG
  320. sprintf( bwb_ebuf, "in line_start(): new element is <%s>", tbuf );
  321. bwb_debug( bwb_ebuf );
  322. #endif
  323. #if STRUCT_CMDS
  324. if ( is_label( tbuf ) == TRUE )
  325. {
  326. *cmdnum = getcmdnum( CMD_LABEL );
  327. adv_ws( buffer, &position );
  328. *startpos = position;
  329. }
  330. else if ( is_cmd( tbuf, cmdnum ) == TRUE )
  331. #else
  332. if ( is_cmd( tbuf, cmdnum ) == TRUE )
  333. #endif
  334. {
  335. adv_ws( buffer, &position );
  336. *startpos = position;
  337. }
  338. else if ( is_let( &( buffer[ *cmdpos ] ), cmdnum ) == TRUE )
  339. {
  340. *cmdpos = -1;
  341. }
  342. else
  343. {
  344. *cmdpos = *cmdnum = -1;
  345. }
  346. }
  347. /* not a line number */
  348. else
  349. {
  350. *lnum = -1;
  351. *lnpos = -1;
  352. #if INTENSIVE_DEBUG
  353. sprintf( bwb_ebuf, "in line_start(): no line number, element <%s>.",
  354. tbuf );
  355. bwb_debug( bwb_ebuf );
  356. #endif
  357. #if STRUCT_CMDS
  358. if ( is_label( tbuf ) == TRUE )
  359. {
  360. #if INTENSIVE_DEBUG
  361. sprintf( bwb_ebuf, "in line_start(): label detected <%s>.",
  362. tbuf );
  363. bwb_debug( bwb_ebuf );
  364. #endif
  365. *cmdnum = getcmdnum( CMD_LABEL );
  366. adv_ws( buffer, &position );
  367. *startpos = position;
  368. }
  369. else if ( is_cmd( tbuf, cmdnum ) == TRUE )
  370. #else
  371. if ( is_cmd( tbuf, cmdnum ) == TRUE )
  372. #endif
  373. {
  374. adv_ws( buffer, &position );
  375. *startpos = position;
  376. }
  377. else if ( is_let( &( buffer[ position ] ), cmdnum ) == TRUE )
  378. {
  379. adv_ws( buffer, &position );
  380. *cmdpos = -1;
  381. }
  382. else
  383. {
  384. *cmdpos = *cmdnum = -1;
  385. }
  386. }
  387. #if INTENSIVE_DEBUG
  388. sprintf( bwb_ebuf, "in line_start(): lnpos <%d> lnum <%d>",
  389. *lnpos, *lnum );
  390. bwb_debug( bwb_ebuf );
  391. sprintf( bwb_ebuf, "in line_start(): cmdpos <%d> cmdnum <%d> startpos <%d>",
  392. *cmdpos, *cmdnum, *startpos );
  393. bwb_debug( bwb_ebuf );
  394. #endif
  395. /* return */
  396. return TRUE;
  397. }
  398. /***************************************************************
  399. FUNCTION: is_cmd()
  400. DESCRIPTION: This function determines whether the
  401. string in 'buffer' is a BASIC command
  402. statement, returning TRUE or FALSE,
  403. and if TRUE returning the command number
  404. in the command lookup table in the
  405. integer pointed to by 'cmdnum'.
  406. ***************************************************************/
  407. #if ANSI_C
  408. int
  409. is_cmd( char *buffer, int *cmdnum )
  410. #else
  411. int
  412. is_cmd( buffer, cmdnum )
  413. char *buffer;
  414. int *cmdnum;
  415. #endif
  416. {
  417. register int n;
  418. /* Convert the command name to upper case */
  419. bwb_strtoupper( buffer );
  420. /* Go through the command table and search for a match. */
  421. for ( n = 0; n < COMMANDS; ++n )
  422. {
  423. if ( strcmp( bwb_cmdtable[ n ].name, buffer ) == 0 )
  424. {
  425. *cmdnum = n;
  426. return TRUE;
  427. }
  428. }
  429. /* No command name was found */
  430. *cmdnum = -1;
  431. return FALSE;
  432. }
  433. /***************************************************************
  434. FUNCTION: is_let()
  435. DESCRIPTION: This function tries to determine if the
  436. expression in <buffer> is a LET statement
  437. without the LET command specified.
  438. ***************************************************************/
  439. #if ANSI_C
  440. int
  441. is_let( char *buffer, int *cmdnum )
  442. #else
  443. int
  444. is_let( buffer, cmdnum )
  445. char *buffer;
  446. int *cmdnum;
  447. #endif
  448. {
  449. register int n, i;
  450. #if INTENSIVE_DEBUG
  451. sprintf( bwb_ebuf, "in is_let(): buffer <%s>", buffer );
  452. bwb_debug( bwb_ebuf );
  453. #endif
  454. /* Go through the expression and search for an assignment operator. */
  455. for ( n = 0; buffer[ n ] != '\0'; ++n )
  456. {
  457. switch( buffer[ n ] )
  458. {
  459. case '\"': /* string constant */
  460. ++n;
  461. while( buffer[ n ] != '\"' )
  462. {
  463. ++n;
  464. if ( buffer[ n ] == '\0' )
  465. {
  466. #if PROG_ERRORS
  467. sprintf( bwb_ebuf, "Incomplete string constant" );
  468. bwb_error( bwb_ebuf );
  469. #else
  470. bwb_error( err_syntax );
  471. #endif
  472. *cmdnum = -1;
  473. return FALSE;
  474. }
  475. }
  476. ++n;
  477. break;
  478. case '=':
  479. #if INTENSIVE_DEBUG
  480. sprintf( bwb_ebuf, "in is_let(): implied LET found." );
  481. bwb_debug( bwb_ebuf );
  482. #endif
  483. for ( i = 0; i < COMMANDS; ++i )
  484. {
  485. if ( strncmp( bwb_cmdtable[ i ].name, "LET", (size_t) 3 ) == 0 )
  486. {
  487. *cmdnum = i;
  488. }
  489. }
  490. return TRUE;
  491. }
  492. }
  493. /* No command name was found */
  494. *cmdnum = -1;
  495. return FALSE;
  496. }
  497. /***************************************************************
  498. FUNCTION: bwb_stripcr()
  499. DESCRIPTION: This function strips the carriage return
  500. or line-feed from the end of a string.
  501. ***************************************************************/
  502. #if ANSI_C
  503. int
  504. bwb_stripcr( char *s )
  505. #else
  506. int
  507. bwb_stripcr( s )
  508. char *s;
  509. #endif
  510. {
  511. char *p;
  512. p = s;
  513. while ( *p != 0 )
  514. {
  515. switch( *p )
  516. {
  517. case '\r':
  518. case '\n':
  519. *p = 0;
  520. return TRUE;
  521. }
  522. ++p;
  523. }
  524. *p = 0;
  525. return TRUE;
  526. }
  527. /***************************************************************
  528. FUNCTION: is_numconst()
  529. DESCRIPTION: This function reads the string in <buffer>
  530. and returns TRUE if it is a numerical
  531. constant and FALSE if it is not. At
  532. this point, only decimal (base 10)
  533. constants are detected.
  534. ***************************************************************/
  535. #if ANSI_C
  536. int
  537. is_numconst( char *buffer )
  538. #else
  539. int
  540. is_numconst( buffer )
  541. char *buffer;
  542. #endif
  543. {
  544. char *p;
  545. #if INTENSIVE_DEBUG
  546. sprintf( bwb_ebuf, "in is_numconst(): received string <%s>.", buffer );
  547. bwb_debug( bwb_ebuf );
  548. #endif
  549. /* Return FALSE for empty buffer */
  550. if ( buffer[ 0 ] == '\0' )
  551. {
  552. return FALSE;
  553. }
  554. /* else check digits */
  555. p = buffer;
  556. while( *p != '\0' )
  557. {
  558. switch( *p )
  559. {
  560. case '0':
  561. case '1':
  562. case '2':
  563. case '3':
  564. case '4':
  565. case '5':
  566. case '6':
  567. case '7':
  568. case '8':
  569. case '9':
  570. break;
  571. default:
  572. return FALSE;
  573. }
  574. ++p;
  575. }
  576. /* only numerical characters detected */
  577. return TRUE;
  578. }
  579. /***************************************************************
  580. FUNCTION: bwb_numseq()
  581. DESCRIPTION: This function reads in a sequence of
  582. numbers (e.g., "10-120"), returning
  583. the first and last numbers in the sequence
  584. in the integers pointed to by 'start' and
  585. 'end'.
  586. ***************************************************************/
  587. #if ANSI_C
  588. int
  589. bwb_numseq( char *buffer, int *start, int *end )
  590. #else
  591. int
  592. bwb_numseq( buffer, start, end )
  593. char *buffer;
  594. int *start;
  595. int *end;
  596. #endif
  597. {
  598. register int b, n;
  599. int numbers;
  600. static char *tbuf;
  601. static int init = FALSE;
  602. /* get memory for temporary buffer if necessary */
  603. if ( init == FALSE )
  604. {
  605. init = TRUE;
  606. /* Revised to CALLOC pass-thru call by JBV */
  607. if ( ( tbuf = CALLOC( MAXSTRINGSIZE + 1, sizeof( char ), "bwb_numseq")) == NULL )
  608. {
  609. #if PROG_ERRORS
  610. bwb_error( "in bwb_numseq(): failed to find memory for tbuf" );
  611. #else
  612. bwb_error( err_getmem );
  613. #endif
  614. }
  615. }
  616. if ( buffer[ 0 ] == 0 )
  617. {
  618. *start = *end = 0;
  619. return FALSE;
  620. }
  621. numbers = n = b = 0;
  622. tbuf[ 0 ] = 0;
  623. while( TRUE )
  624. {
  625. switch( buffer[ b ] )
  626. {
  627. case 0: /* end of string */
  628. case '\n':
  629. case '\r':
  630. if ( n > 0 )
  631. {
  632. if ( numbers == 0 )
  633. {
  634. *end = 0;
  635. *start = atoi( tbuf );
  636. ++numbers;
  637. }
  638. else
  639. {
  640. *end = atoi( tbuf );
  641. return TRUE;
  642. }
  643. }
  644. else
  645. {
  646. if ( numbers == 0 )
  647. {
  648. *start = *end = 0;
  649. }
  650. else if ( numbers == 1 )
  651. {
  652. *end = 0;
  653. }
  654. else if ( ( numbers == 2 ) && ( tbuf[ 0 ] == 0 ))
  655. {
  656. *end = 0;
  657. }
  658. }
  659. return TRUE;
  660. #ifdef ALLOWWHITESPACE
  661. case ' ': /* whitespace */
  662. case '\t':
  663. #endif
  664. case '-': /* or skip to next number */
  665. if ( n > 0 )
  666. {
  667. if ( numbers == 0 )
  668. {
  669. *start = atoi( tbuf );
  670. ++numbers;
  671. }
  672. else
  673. {
  674. *end = atoi( tbuf );
  675. return TRUE;
  676. }
  677. }
  678. ++b;
  679. n = 0;
  680. break;
  681. case '0':
  682. case '1':
  683. case '2':
  684. case '3':
  685. case '4':
  686. case '5':
  687. case '6':
  688. case '7':
  689. case '8':
  690. case '9':
  691. tbuf[ n ] = buffer[ b ];
  692. ++n;
  693. tbuf[ n ] = 0;
  694. ++b;
  695. break;
  696. default:
  697. #if PROG_ERRORS
  698. sprintf( bwb_ebuf,
  699. "ERROR: character <%c> unexpected in numerical sequence",
  700. buffer[ b ] );
  701. ++b;
  702. bwb_error( bwb_ebuf );
  703. #else
  704. bwb_error( err_syntax );
  705. #endif
  706. break;
  707. }
  708. }
  709. }
  710. /***************************************************************
  711. FUNCTION: bwb_freeline()
  712. DESCRIPTION: This function frees memory associated
  713. with a program line in memory.
  714. ***************************************************************/
  715. #if ANSI_C
  716. int
  717. bwb_freeline( struct bwb_line *l )
  718. #else
  719. int
  720. bwb_freeline( l )
  721. struct bwb_line *l;
  722. #endif
  723. {
  724. /* free arguments if there are any */
  725. /* Revised to FREE pass-thru calls by JBV */
  726. if (l->buffer != NULL)
  727. {
  728. FREE( l->buffer, "bwb_freeline" );
  729. l->buffer = NULL; /* JBV */
  730. }
  731. FREE( l, "bwb_freeline" );
  732. l = NULL; /* JBV */
  733. return TRUE;
  734. }
  735. /***************************************************************
  736. FUNCTION: int_qmdstr()
  737. DESCRIPTION: This function returns a string delimited
  738. by quotation marks.
  739. ***************************************************************/
  740. #if ANSI_C
  741. int
  742. int_qmdstr( char *buffer_a, char *buffer_b )
  743. #else
  744. int
  745. int_qmdstr( buffer_a, buffer_b )
  746. char *buffer_a;
  747. char *buffer_b;
  748. #endif
  749. {
  750. char *a, *b;
  751. a = buffer_a;
  752. ++a; /* advance beyond quotation mark */
  753. b = buffer_b;
  754. while( *a != '\"' )
  755. {
  756. *b = *a;
  757. ++a;
  758. ++b;
  759. *b = '\0';
  760. }
  761. return TRUE;
  762. }
  763. /***************************************************************
  764. FUNCTION: is_eol()
  765. DESCRIPTION: This function determines whether the buffer
  766. is at the end of a line.
  767. ***************************************************************/
  768. #if ANSI_C
  769. extern int
  770. is_eol( char *buffer, int *position )
  771. #else
  772. int
  773. is_eol( buffer, position )
  774. char *buffer;
  775. int *position;
  776. #endif
  777. {
  778. adv_ws( buffer, position );
  779. #if INTENSIVE_DEBUG
  780. sprintf( bwb_ebuf, "in is_eol(): character is <0x%x> = <%c>",
  781. buffer[ *position ], buffer[ *position ] );
  782. bwb_debug( bwb_ebuf );
  783. #endif
  784. switch( buffer[ *position ] )
  785. {
  786. case '\0':
  787. case '\n':
  788. case '\r':
  789. #if MULTISEG_LINES
  790. case ':':
  791. #endif
  792. return TRUE;
  793. default:
  794. return FALSE;
  795. }
  796. }