home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pilot.zip / pilot.c < prev    next >
C/C++ Source or Header  |  1993-08-18  |  32KB  |  1,063 lines

  1. /* -------
  2.  * pilot.c -- Pilot CAI interpreter (C) Dave Taylor.
  3.  * -------
  4.  * This program is an interpreter for the Pilot CAI language as defined
  5.  * in the associated file pilot.bnf.
  6.  *
  7.  * Original program (C) Copyright 1985, Dave Taylor.
  8.  * OS/2 port, Tommi Nieminen 18-Aug-1993.
  9.  *
  10.  * Notes on OS/2 port:
  11.  * Some modification was needed to get this compiled with gcc 2.4.5 and
  12.  * emx 0.8g. Also, "EXTPROC" support was added to implement Pilot CAI
  13.  * as an external command processor (see `read.me' file for further
  14.  * information on this).
  15.  *
  16.  */
  17.  
  18. #include <stdio.h>
  19. #include <string.h>
  20. #include <ctype.h>
  21.  
  22.   /* Program name to be used in messages */
  23. #define  PRGNAME        "Pilot CAI for OS/2 v1.0"
  24.  
  25. #define  SLEN           256     /* string length    */
  26. #define  NLEN           20      /* short string     */
  27. #define  COLON          ':'     /* colon char       */
  28. #define  STRINGDELIM    '$'     /* delimit stringvar*/
  29. #define  NUMDELIM       '#'     /* delimit num var  */
  30. #ifndef  TRUE
  31. #define  TRUE           1       /* boolean true     */
  32. #define  FALSE          0       /* boolean false    */
  33. #endif
  34. #define  MAXDEPTH       10      /* subroutine depth */
  35. #define  DEFAULT_DEBUG  0       /* 0=OFF, 1=ON      */
  36.  
  37.   /* Error types */
  38. #define  NONFATAL       0
  39. #define  FATAL          1
  40.  
  41.   /* Return codes */
  42. #define  NOERROR        0
  43. #define  ERROR          1
  44.  
  45.   /* Error codes */
  46. #define  UNKNOWN_STATEMENT   1
  47. #define  BAD_IDENT           2
  48. #define  BAD_INSTRUCT        3
  49. #define  DUP_LABEL           4
  50. #define  UNKNOWN_LBL         5
  51. #define  NO_LABEL            6
  52. #define  UNEXPECT_EOF        7
  53. #define  TOO_DEEP            8
  54. #define  OUT_OF_MEM          9
  55. #define  NAME_TOO_LONG      10
  56. #define  UNDEF_VAR          11
  57. #define  BAD_NUMBER         12
  58. #define  BAD_PARENS         13
  59. #define  BAD_EXP            14
  60. #define  DIVIDE_BY_ZERO     15
  61. #define  BAD_LHS            16
  62. #define  MISSING_EQ         17
  63. #define  STRING_IN_EXP      18
  64. #define  BAD_REL_EXP        19
  65. #define  BAD_REL_OP         20
  66.  
  67. #define  PLUS           '+'
  68. #define  MINUS          '-'
  69. #define  TIMES          '*'
  70. #define  DIVIDE         '/'
  71. #define  LEFT_PAREN     '('
  72. #define  RIGHT_PAREN    ')'
  73.  
  74.   /* Character classification routines */
  75. #define end_of_line(c)  (c == '\n' || c == '\r' || c == '\0')
  76. #define addops(c)       (c == PLUS  || c == MINUS )
  77. #define mulops(c)       (c == TIMES  || c == DIVIDE )
  78. #define mathops(c)      (addops(c) || mulops(c))
  79. #define relop(c)        (c == '='  || c == '<' || c == '>')
  80. #define paren(c)        (c == LEFT_PAREN  || c == RIGHT_PAREN)
  81. #define special(c)      (mathops(c) || relop(c) || paren(c))
  82. #define stringvar(s)    (s[0] == STRINGDELIM)
  83. #define numvar(s)       (s[0] == NUMDELIM)
  84. #define whitespace(c)   (c == ' ' || c == '\t')
  85. #define valid_char(c)   (isalnum(c) || c == '_')
  86.  
  87.   /* Various one-line routines for the interpreter */
  88. #define lastch(s)           s[strlen(s) - 1]
  89. #define remove_return(s)    if (lastch(s) == '\n') lastch(s) = '\0'
  90. #define last_label()        (last->name)
  91. #define init_get_token()    line_loc = 0
  92. #define unget_token()       line_loc = last_line_loc
  93.  
  94. char *errmsg[] = {
  95.     "Unknown generic error",                       /* GENERIC ERROR     */
  96.     "Unknown statement",                           /* UNKNOWN_STATEMENT */
  97.     "Bad identifier: \"%s\"",                      /* BAD_IDENT         */
  98.     "Badly formed instruction",                    /* BAD_INSTRUCT      */
  99.     "Duplicate label \"%s\"",                      /* DUP_LABEL         */
  100.     "Unknown label \"%s\"",                        /* UNKNOWN_LBL       */
  101.     "Label \"%s\" not found in program",           /* NO_LABEL          */
  102.     "End of File during search for label \"%s\"",  /* UNEXPECTED_EOF    */
  103.     "Routine calls nested too deeply",             /* TOO_DEEP          */
  104.     "Out of memory!",                              /* OUT_OF_MEM        */
  105.     "Name \"%s\" too long",                        /* NAME_TOO_LONG     */
  106.     "Undefined variable: \"%s\"",                  /* UNDEF_VAR         */
  107.     "Invalid format for numerical input!",         /* BAD_NUMBER        */
  108.     "Badly formed expression: parenthesis",        /* BAD_PARENS        */
  109.     "Badly formed expression",                     /* BAD_EXP           */
  110.     "Attempt to divide by zero!",                  /* DIVIDE_BY_ZERO    */
  111.     "Bad left-hand-side of expression",            /* BAD_LHS           */
  112.     "Missing or misplaced \"=\" in expression",    /* MISSING_EQ        */
  113.     "String variable in numerical expression",     /* STRING_IN_EXP     */
  114.     "Bad relational expression",                   /* BAD_REL_EXP       */
  115.     "Bad relational operator: \"%s\""              /* BAD_REL_OP        */
  116. };
  117.  
  118. struct a_label {            /* The label table is a linked list */
  119.     char name[NLEN];        /*   of label name,                 */
  120.     int loc;                /*   the line number it occurs on,  */
  121.     struct a_label *next;   /*   and a link to the next element */
  122. } *label_list, *last;
  123.  
  124. struct symbol_entry {       /* The symbol table is a binary tree */
  125.     char name[NLEN];        /*   of symbol name                  */
  126.     char value[SLEN];       /*   the printable current value     */
  127.     int numvalue;           /*   the numeric value if number var */
  128.     struct symbol_entry
  129.         *left,              /*   a left subnode link             */
  130.         *right;             /*   and a right subnode link        */
  131. } *symbol_table, *symbol_node;
  132.  
  133.   /* Subroutine calls and returns */
  134. int subroutine_stack[MAXDEPTH];
  135.  
  136. FILE *fileid;                 /* input file descriptor      */
  137. char def_string[SLEN];        /* line read from stdin       */
  138. int current_line = 0,         /* line being read from file  */
  139.     line_loc,                 /* for line -> words transl.  */
  140.     last_line_loc,            /* for unget_token() routine  */
  141.     boolean = TRUE,           /* result of last match       */
  142.     boolean_cont = FALSE,     /* result of last bool test   */
  143.     nesting_level = 0,        /* how deep into 'use' calls  */
  144.     error,                    /* error during exp parsing   */
  145.     furthest_into_file = 0,   /* furthest line read in file */
  146.     debug = DEFAULT_DEBUG;    /* is debugging turned on?    */
  147.  
  148.   /* Function declarations */
  149. void init(char *fname);
  150. void wrapup(void);
  151. void parse(char *line);
  152. void type(char *line);
  153. void accept(char *line);
  154. void match(char *line);
  155. void jump(char *line);
  156. void endit(char *line);
  157. void use(char *line);
  158. void compute(char *line);
  159. int label(char *line, int loc);
  160. int raise_error(int errno, int errtype, char *arg);
  161. int in_string(char *buffer, char *pattern);
  162. int check_condition(char *line);
  163. int remove_past_colon(char *line);
  164. int break_string(char *line, char *lhs, char *op, char *rhs);
  165. void add_label(char *name);
  166. int find_label(char *name);
  167. int get_to_label(int loc, char *labelname);
  168. struct symbol_entry *add_symbol(struct symbol_entry *node, char *symbol);
  169. void print_symbol_table(struct symbol_entry *node);
  170. struct symbol_entry *find_symbol(struct symbol_entry *node, char *symbol);
  171. int substitute_vars(char *line);
  172. int relation(char *exp);
  173. int evaluate(char *exp);
  174. int expression(char *exp);
  175. int term(char *exp);
  176. int factor(char *string);
  177. char *get_token(char *line);
  178.  
  179. main(int argc, char *argv [])
  180. {
  181.     char line[SLEN];  /* Input buffer for reading file */
  182.  
  183.     if (argc == 3)
  184.         debug ++;
  185.     else if (argc != 2) {
  186.         printf("%s (C) Dave Taylor 1985.\n", PRGNAME);
  187.         printf("OS/2 port--Tommi Nieminen 18-Aug-1993.\n");
  188.         printf("\n\tUsage: [D:\\] pilot [ -d ] FILE\n");
  189.         printf("\n-d switch turns on debugging mode.\n");
  190.         exit(1);
  191.     }
  192.  
  193.       /* Open file and such */
  194.     init(argv[argc - 1]);
  195.  
  196.     while (fgets(line, SLEN, fileid) != NULL) {
  197.         remove_return(line);
  198.  
  199.           /* TN 18-Aug-1993:
  200.            * Ignore EXTPROC (note: this ignores EXTPROC anywhere,
  201.            * not just at the beginning of file)
  202.            */
  203.         if (strnicmp(line, "EXTPROC", 7) == 0)
  204.             *line = '\0';
  205.  
  206.         current_line ++;
  207.         if (debug)
  208.             printf(" %2d > %s\n", current_line, line);
  209.         if (strlen(line) > 0)
  210.             parse(line);
  211.     }
  212.  
  213.       /* Close file and such */
  214.     wrapup();
  215. }
  216.  
  217. void init(char *fname)
  218.   /* Initialize the interpreter - open file and related. */
  219. {
  220.     if ((fileid = fopen(fname, "r")) == NULL) {
  221.         fprintf(stderr, "Fatal error: Could not open \"%s\"\n", fname);
  222.         wrapup();
  223.     }
  224.  
  225.     label_list = NULL;
  226. }
  227.  
  228. void wrapup(void)
  229.   /* End of interpreter session - close file & list if debug */
  230. {
  231.     if (debug) {
  232.         printf("\nDump of symbol table:\n");
  233.         print_symbol_table(symbol_table);
  234.         printf("\nDump of label table:\n");
  235.         last = label_list;
  236.         while (last != NULL) {
  237.             printf("%-20.20s at line %d\n", last->name, last->loc);
  238.             last = last->next;
  239.         }
  240.     }
  241.  
  242.     fclose(fileid);
  243.     exit(0);
  244. }
  245.  
  246. void parse(char *line)
  247.   /* Parse line, calling the appropriate routine depending on
  248.    * the statement type.
  249.    */
  250. {
  251.     register int i = 0;
  252.     char *lineptr, buffer[SLEN];
  253.  
  254.       /* Skip leading blanks */
  255.     while (whitespace(line[i]))
  256.         i ++;
  257.  
  258.       /* ...skip first char */
  259.     lineptr = (char *) line + i + 1;
  260.  
  261.       /* Each line is new! */
  262.     error = NOERROR;
  263.  
  264.     switch (toupper(line[i])) {
  265.         case 'A' :  accept(lineptr);
  266.                     break;
  267.         case 'C' :  compute(lineptr);
  268.                     break;
  269.         case 'E' :  endit(lineptr);
  270.                     break;
  271.         case 'J' :  jump(lineptr);
  272.                     break;
  273.         case 'M' :  match(lineptr);
  274.                     break;
  275.         case 'R' :  /* Remark */
  276.                     break;
  277.         case 'T' :  type(lineptr);
  278.                     break;
  279.         case 'U' :  use(lineptr);
  280.                     break;
  281.         case '*' :  label(line, current_line);
  282.                     break;
  283.         case ':' :  sprintf(buffer, "%c%s", boolean_cont==boolean? 'Y' : 'N',
  284.                       line);
  285.                     type(buffer);
  286.                     break;
  287.         default  :  raise_error(UNKNOWN_STATEMENT, NONFATAL, line);
  288.     }
  289. }
  290.  
  291. void type(char *line)
  292.   /* Outputs the given line to the screen. */
  293. {
  294.     if (!(boolean_cont = check_condition(line)))
  295.         return;
  296.  
  297.     if (substitute_vars(line) != ERROR)
  298.         printf("%s\n", line);
  299. }
  300.  
  301. void accept(char *line)
  302.   /* This routine accepts a line of input and sets def_string
  303.    * to the value.  If a variable is specified, it uses that.
  304.    */
  305. {
  306.     struct symbol_entry *entry, *add_symbol();
  307.     char *name;
  308.     int defined_symbol = 0, value_buffer, i;
  309.  
  310.     if (!(boolean_cont = check_condition(line)))
  311.         return;
  312.  
  313.     init_get_token();
  314.  
  315.     if (strlen(line) > 0)  /* Variable name to use! */
  316.         if ((name = get_token(line)) != NULL) {
  317.             if (!stringvar(name) && !numvar(name)) {
  318.                 raise_error(BAD_IDENT, NONFATAL, name);
  319.                 return;
  320.             }
  321.             symbol_table = add_symbol(symbol_table, name);
  322.             entry = symbol_node; /* Set to new node address */
  323.             defined_symbol ++;
  324.         }
  325.  
  326.     printf("> ");
  327.  
  328.     fgets(def_string, SLEN, stdin);
  329.     remove_return(def_string);  /* Remove return, if any */
  330.  
  331.     if (numvar(name)) {  /* Special processing for number */
  332.         i = 0;
  333.         if (def_string[i] == '-')
  334.           i ++;
  335.         for (; i < strlen(def_string); i ++)
  336.             if (!isdigit(def_string[i])) {
  337.                 raise_error(BAD_NUMBER, FATAL, NULL);
  338.                 wrapup();
  339.             }
  340.  
  341.         if (defined_symbol) {  /* Do we need save this?  */
  342.             sscanf(def_string,"%d", &value_buffer);
  343.             entry->numvalue = value_buffer;    /* Keep as a number and */
  344.             strcpy(entry->value, def_string);  /* as string too!       */
  345.         }
  346.     }
  347.     else if (defined_symbol)  /* Do we need to save it? */
  348.         strcpy(entry->value, def_string);  /* Then named = default */
  349. }
  350.  
  351. void match(char *line)
  352.   /* Try to match any of the list of words or string  variables
  353.    * (delimited by spaces) in the list to the value of def_string,
  354.    * the last line input. Variables are expanded to their values.
  355.    *   Possible errors: UNDEF_VAR
  356.    */
  357. {
  358.     struct symbol_entry *entry;
  359.     char *word;
  360.  
  361.     if (!(boolean_cont = check_condition(line)))
  362.         return;
  363.  
  364.     init_get_token();
  365.  
  366.     while ((word = get_token(line)) != NULL) {
  367.         if (stringvar(word) || numvar(word)) {
  368.             if ((entry = find_symbol(symbol_table, word)) == NULL) {
  369.                 raise_error(UNDEF_VAR, NONFATAL, word);
  370.                 return;
  371.             }
  372.             else
  373.                 strcpy(word, entry->value);  /* Word = value of var */
  374.         }
  375.         if (boolean = in_string(def_string, word))
  376.             return; /* Done as soon as hit a match... */
  377.     }
  378. }
  379.  
  380. void jump(char *line)
  381.   /* Jump to the indicated label, if possible */
  382. {
  383.     if (!(boolean_cont = check_condition(line)))
  384.         return;
  385.     get_to_label(find_label((char *) line + 1), (char *) line + 1);
  386. }
  387.  
  388. void endit(char *line)
  389.   /* This marks the end of a routine or of the program */
  390. {
  391.     if (!(boolean_cont = check_condition(line)))
  392.         return;
  393.  
  394.     if (nesting_level == 0)
  395.         wrapup();  /* Done with entire program! */
  396.     else
  397.         get_to_label(subroutine_stack[-- nesting_level], NULL);
  398. }
  399.  
  400. void use(char *line)
  401.   /* Call the specified subroutine.
  402.    *   Possible errors: TOO_DEEP
  403.    */
  404. {
  405.     if (!(boolean_cont = check_condition(line)))
  406.         return;
  407.  
  408.     line = (char *) line + 1;
  409.  
  410.     if (nesting_level == MAXDEPTH) {
  411.         raise_error(TOO_DEEP, FATAL, NULL);
  412.         wrapup();
  413.     }
  414.  
  415.     subroutine_stack[nesting_level ++] = current_line;
  416.  
  417.     get_to_label(find_label(line), line);
  418. }
  419.  
  420. void compute(char *line)
  421.   /* Compute the indicated expression based on whether it's a
  422.    * numerical or string expression.
  423.    *   Possible errors: BAD_LHS, MISSING_EQ
  424.    */
  425. {
  426.     struct symbol_entry *node;
  427.     char *ident, buffer[SLEN];
  428.     int value = 0, i = 0, j = 0;
  429.  
  430.     if (!(boolean_cont = check_condition(line)))
  431.         return;
  432.  
  433.     init_get_token();
  434.  
  435.     if ((ident = get_token(line)) == NULL) {
  436.         raise_error(BAD_LHS, NONFATAL, NULL);
  437.         return;
  438.     }
  439.  
  440.     if (!stringvar(ident) && !numvar(ident)) {
  441.         raise_error(BAD_LHS, NONFATAL, NULL);
  442.         return;
  443.     }
  444.  
  445.     symbol_table = add_symbol(symbol_table, ident);
  446.       /* Keep structure to save to */
  447.     node = symbol_node;
  448.  
  449.     if ((ident = get_token(line)) == NULL) {
  450.         raise_error(MISSING_EQ, NONFATAL, NULL);
  451.         return;
  452.     }
  453.  
  454.     if (ident[0] != '=') {
  455.         raise_error(MISSING_EQ, NONFATAL, NULL);
  456.         return;
  457.     }
  458.  
  459.       /* String expression */
  460.     if (stringvar(node->name)) {
  461.           /* Get 'rest' of line for string substitution      */
  462.         for (i = line_loc; !end_of_line(line[i]); i ++)
  463.             buffer[j ++] = line[i] ++;
  464.         buffer[j] = 0;
  465.         if (substitute_vars(buffer) == ERROR)
  466.             return;
  467.         strcpy(node->value, buffer);
  468.     }
  469.       /* Numerical expression */
  470.     else {
  471.         value = evaluate(line);
  472.         if (!error) {
  473.             node->numvalue = value;
  474.             sprintf(node->value, "%d", value);
  475.         }
  476.     }
  477. }
  478.  
  479. int label(char *line, int loc)
  480.   /* Add label to label table at line number loc, but only if
  481.    * we haven't read this part of program before!
  482.    */
  483. {
  484.     if (loc > furthest_into_file) {
  485.         add_label(line);
  486.         furthest_into_file = loc;
  487.     }
  488. }
  489.  
  490. int raise_error(int errno, int errtype, char *arg)
  491.   /* Display error 'errno', type FATAL or NONFATAL, arg, if present,
  492.    * is output too.
  493.    */
  494. {
  495.     char buffer[SLEN];
  496.  
  497.     if (errtype == FATAL)
  498.         sprintf(buffer, "FATAL Error: %s\n", errmsg[errno]);
  499.     else
  500.         sprintf(buffer, "Error: %s on line %d\n", errmsg[errno],
  501.           current_line);
  502.     printf(buffer, arg);
  503.  
  504.     return(ERROR);
  505. }
  506.  
  507. int in_string(char *buffer, char *pattern)
  508.   /* Returns TRUE iff pattern occurs IN IT'S ENTIRETY in buffer. */
  509. {
  510.     register int i = 0, j = 0;
  511.  
  512.     while (buffer[i] != '\0') {
  513.         while (buffer[i ++] == pattern[j ++])
  514.             if (pattern[j] == '\0')
  515.                 return(TRUE);
  516.         i = i - j + 1;
  517.         j = 0;
  518.     }
  519.  
  520.     return(FALSE);
  521. }
  522.  
  523. int check_condition(char *line)
  524.   /* Returns non-zero iff the indicated condition (if any) is TRUE.
  525.    * This routine will also remove the part of the  line that contains
  526.    * the actual conditional and the colon.
  527.    */
  528. {
  529.     char buffer[SLEN];
  530.  
  531.        /* Save the line */
  532.     strcpy(buffer, line);
  533.  
  534.     if (!remove_past_colon(line))
  535.         return(ERROR);
  536.  
  537.       /* Relational expression */
  538.     if (buffer[0] == '(')
  539.         return(relation(buffer));
  540.       /* If boolean... */
  541.     else if (buffer[0] == 'Y')
  542.         return(boolean);
  543.       /* If not boolean */
  544.     else if (buffer[0] == 'N')
  545.         return(! boolean);
  546.       /* Always */
  547.     else
  548.         return(TRUE);
  549. }
  550.  
  551. int remove_past_colon(char *line)
  552.   /* Remove up to and including the 'colon' from input string
  553.    * Returns zero iff no colon.
  554.    *   Possible error: BAD_INSTRUCT
  555.    */
  556. {
  557.     register int i = 0, index;
  558.  
  559.     while (line[i] != COLON && line[i] != '\0')
  560.         i++;
  561.  
  562.     if (line[i] == COLON)
  563.         i ++;  /* Get past colon */
  564.       /* No colon in line!  Bad construct! */
  565.     else
  566.         return(raise_error(BAD_INSTRUCT, NONFATAL, line));
  567.  
  568.     for (index = i; line[index] != '\0'; index ++)
  569.         line[index - i] = line[index];
  570.  
  571.     line[index - i] = '\0';
  572.  
  573.     return(TRUE);
  574. }
  575.  
  576. int break_string(char *line, char *lhs, char *op, char *rhs)
  577.   /* Breaks down line into left-hand-side, relational operator and
  578.    * right-hand-side.  We need to strip out parens surrounding the
  579.    * expression too without saving them.
  580.    *   Possible errors: BAD_REL_OP, BAD_REL_EXP
  581.    */
  582. {
  583.     char *word;
  584.  
  585.       /* Initialize them all */
  586.     lhs[0] = rhs[0] = op[0] = '\0';
  587.  
  588.     init_get_token();
  589.  
  590.     if (get_token(line) == NULL)  /* No open parenthesis! */
  591.         return(raise_error(BAD_REL_EXP, NONFATAL, NULL));
  592.  
  593.       /* Get lhs ... */
  594.     while ((word = get_token(line)) != NULL && ! relop(word[0]) &&
  595.       word[0] != COLON && ! paren(word[0]))
  596.         sprintf(lhs, "%s%s", lhs, word);
  597.  
  598.     if (word == NULL)
  599.         return(raise_error(BAD_REL_EXP, NONFATAL, NULL));
  600.  
  601.     if (paren(word[0]))  /* Nonexpression relational */
  602.         return(NOERROR);
  603.  
  604.       /* Get op ... */
  605.     strcpy(op, word);
  606.  
  607.       /* Get rhs ... */
  608.     while ((word = get_token(line)) != NULL && word[0] != COLON)
  609.         sprintf(rhs, "%s%s", rhs, word);
  610.  
  611.       /* Remove last closing paren */
  612.     lastch(rhs) = '\0';
  613.  
  614.     if (word == NULL)
  615.         return(raise_error(BAD_REL_EXP, NONFATAL, NULL));
  616.     else
  617.         return(NOERROR);
  618. }
  619.  
  620. void add_label(char *name)
  621.   /* Add given label to label list at end, if not found first.
  622.    *   Possible errors: DUP_LABEL, OUT_OF_MEM
  623.    */
  624. {
  625.     struct a_label *previous;
  626.  
  627.       /* Both previous and last are set to head */
  628.     previous = last = label_list;
  629.  
  630.     while (last != NULL) {
  631.         if (strcmp(last->name, name) == 0) {
  632.             raise_error(DUP_LABEL, NONFATAL, name);
  633.             return;
  634.         }
  635.         previous = last;
  636.         last = last->next;
  637.     }
  638.  
  639.       /* At this point entry == NULL and previous == last valid entry */
  640.  
  641.     if ((last = (struct a_label *) malloc(sizeof *last)) == NULL) {
  642.           /* No memory! */
  643.         raise_error(OUT_OF_MEM, FATAL, NULL);
  644.         wrapup();
  645.     }
  646.  
  647.     strncpy(last->name, name, NLEN);
  648.     last->loc = current_line;
  649.     last->next= NULL;
  650.  
  651.     if (previous == NULL)
  652.         label_list = last;  /* First element in list */
  653.     else
  654.         previous->next = last;
  655. }
  656.  
  657. int find_label(char *name)
  658.   /* Returns line location of specified label or 0 if that label
  659.    * isn't currently in the label table.
  660.    */
  661. {
  662.     struct a_label *entry;
  663.  
  664.     entry = label_list; /* set entry to label list head */
  665.  
  666.     while (entry != NULL) {
  667.         if (strcmp(entry->name, name) == 0)
  668.             return(entry->loc);
  669.         entry = entry->next;
  670.     }
  671.  
  672.     return(0);
  673. }
  674.  
  675. int get_to_label(int loc, char *labelname)
  676.   /* Move file pointer to indicated line number.  If loc is zero this
  677.    * indicates that we need to scan FORWARD in the file, so reads
  678.    * quickly forward, adding newly encountered labels to the label
  679.    * list as encounted.  If loc is non-zero, get to specified line
  680.    * from current line in minimal movement possible.
  681.    *   Possible errors: NO_LABEL, UNEXPECT_EOF
  682.    */
  683. {
  684.     char buffer[SLEN];
  685.  
  686.       /* Forward scan */
  687.     if (loc == 0) {
  688.         if (debug)
  689.             printf("\tget_to_label(%s)\n", labelname);
  690.         while (fgets(buffer, SLEN, fileid) != NULL) {
  691.             remove_return(buffer);
  692.             current_line ++;
  693.             if (debug)
  694.                 printf("%d >> %s\n", current_line, buffer);
  695.             if (buffer[0] == '*') {
  696.                 if (label(buffer, current_line) == ERROR) {
  697.                     raise_error(UNEXPECT_EOF, FATAL, labelname);
  698.                     wrapup();
  699.                 }
  700.                 if (strcmp(labelname, last_label()) == 0)
  701.                     return;  /* Label found! */
  702.             }
  703.         }
  704.         raise_error(NO_LABEL, FATAL, labelname);
  705.         wrapup();
  706.     }
  707.       /* Get to specified line */
  708.     else {
  709.         if (loc < current_line) {  /* If before, rewind file */
  710.             rewind(fileid);
  711.             current_line = 0;
  712.         }
  713.  
  714.         while (fgets(buffer, SLEN, fileid) != NULL) {
  715.             current_line ++;
  716.             if (current_line == loc)
  717.                 return;
  718.         }
  719.         raise_error(UNEXPECT_EOF, FATAL, NULL);
  720.         wrapup();
  721.     }
  722. }
  723.  
  724. struct symbol_entry *add_symbol(struct symbol_entry *node, char *symbol)
  725.   /* This routine adds the specified symbol to the symbol table.
  726.    * The first character determines the type of the variable: '$' for
  727.    * strings and '#' for integers.
  728.    *   Possible errors: OUT_OF_MEM
  729.    */
  730. {
  731.     int cond;
  732.  
  733.     if (node == NULL) {
  734.         if ((node = (struct symbol_entry *) malloc(sizeof *node)) == NULL) {
  735.             raise_error(OUT_OF_MEM, FATAL, NULL);
  736.             wrapup();
  737.         }
  738.         strcpy(node->name, symbol);
  739.         node->value[0] = '\0';
  740.         node->numvalue = 0;
  741.         node->left  = NULL;
  742.         node->right = NULL;
  743.           /* Store address globally too, if needed! */
  744.         symbol_node = node;
  745.     }
  746.     else if ((cond = strcmp(symbol, node->name)) == 0)
  747.           /* Store address globally too, if needed! */
  748.         symbol_node = node;
  749.     else if (cond < 0)
  750.         node->left = add_symbol(node->left, symbol);
  751.     else
  752.         node->right= add_symbol(node->right, symbol);
  753.  
  754.     return(node);
  755. }
  756.  
  757. void print_symbol_table(struct symbol_entry *node)
  758.   /* Recursively lists all entries in the symbol table. Debug only */
  759. {
  760.     if (node != NULL) {
  761.         print_symbol_table(node->left);
  762.         printf("\t%-20.20s '%s'\n", node->name, node->value);
  763.         print_symbol_table(node->right);
  764.     }
  765. }
  766.  
  767. struct symbol_entry *find_symbol(struct symbol_entry *node, char *symbol)
  768.   /* Returns either NULL if the symbol is not found or the address of
  769.    * the structure containing the specified symbol.  This is a stan-
  770.    * dard recursive binary tree search...
  771.    */
  772. {
  773.     int cond;
  774.  
  775.     if (node == NULL)
  776.         return(NULL);
  777.     else if ((cond = strcmp(symbol, node->name)) == 0)
  778.         return(node); /* store if needed */
  779.     else if (cond < 0)
  780.         return(find_symbol(node->left, symbol));
  781.     else
  782.         return(find_symbol(node->right, symbol));
  783. }
  784.  
  785. int substitute_vars(char *line)
  786.    /* This routine substitutes the value for each variable it finds in
  787.     * the given line.
  788.     *   Possible error: UNDEF_VAR
  789.     */
  790. {
  791.     struct symbol_entry *entry, *find_symbol();
  792.     register int i = 0, j = 0, word_index;
  793.     char word[NLEN], buffer[SLEN];
  794.  
  795.     do {
  796.           /* While not in variable copy to buffer... */
  797.         while (line[i] != STRINGDELIM && line[i] != NUMDELIM &&
  798.           !end_of_line(line[i]))
  799.             buffer[j ++] = line[i ++];
  800.  
  801.           /* Get variable if it exists... */
  802.         word_index = 0;
  803.  
  804.         if (!end_of_line(line[i]))
  805.           /* Copy in the delimiter */
  806.         word[word_index ++] = line[i ++];
  807.  
  808.         while (!end_of_line(line[i]) && valid_char(line[i]))
  809.             word[word_index ++] = line[i ++];
  810.  
  811.           /* Have a variable? If so try to find and substitute */
  812.         if (word_index > 0) {
  813.             word[word_index] = '\0';
  814.             if ((entry = find_symbol(symbol_table, word)) == NULL)
  815.                 return(raise_error(UNDEF_VAR, NONFATAL, word));
  816.  
  817.             for (word_index = 0; word_index < strlen(entry->value);
  818.               word_index ++)
  819.                 buffer[j++] = (entry->value)[word_index];
  820.         }
  821.     } while (!end_of_line(line[i]));
  822.  
  823.     buffer[j] = '\0';
  824.       /* Copy it back in */
  825.     strcpy(line, buffer);
  826.  
  827.     return(NOERROR);
  828. }
  829.  
  830. int relation(char *exp)
  831.   /* Evaluate relational expression between a set of parenthesis.
  832.    * Returns TRUE or FALSE according to the results of the evaluation.
  833.    * If an error occurs this routine will always return FALSE.
  834.    *   Possible errors: BAD_REL_OP
  835.    */
  836. {
  837.     char word[NLEN], lhs_string[SLEN], rhs_string[SLEN];
  838.     int retval,
  839.         lhs,      /* Left hand side */
  840.         rhs;      /* Right hand side */
  841.  
  842.     if (break_string(exp, lhs_string, word, rhs_string) == ERROR)
  843.         return(FALSE);    /* Default for error */
  844.  
  845.     init_get_token();             /* New string:    */
  846.     lhs = evaluate(lhs_string);   /* left hand side */
  847.  
  848.     if (error)
  849.         return(FALSE);  /* Erroneous always fail */
  850.  
  851.     if (word[0] == '\0' && rhs_string[0] == '\0')
  852.         return(lhs != 0);  /* Accept no relation exp. */
  853.  
  854.     init_get_token();             /* New string:     */
  855.     rhs = evaluate(rhs_string);   /* right hand side */
  856.  
  857.     if (error)
  858.         return(FALSE);  /* Erroneous always fail */
  859.  
  860.       /* Compute return value   */
  861.     switch (word[0]) {
  862.         case '=' :  retval = (lhs == rhs);
  863.                     break;
  864.         case '<' :  switch (word[1]) {
  865.                         case '\0' : retval = (lhs < rhs);
  866.                                     break;
  867.                         case '>'  : retval = (lhs != rhs);
  868.                                     break;
  869.                         case '='  : retval = (lhs <= rhs);
  870.                                     break;
  871.                         default   : return(raise_error(BAD_REL_OP, NONFATAL,
  872.                                       word));
  873.                     }
  874.                     break;
  875.         case '>' :  switch (word[1]) {
  876.                         case '\0' : retval = (lhs > rhs);
  877.                                     break;
  878.                         case '='  : retval = (lhs >= rhs);
  879.                                     break;
  880.                         default   : return(raise_error(BAD_REL_OP, NONFATAL,
  881.                                       word));
  882.                     }
  883.                     break;
  884.         default  :  return(raise_error(BAD_REL_OP, NONFATAL, word));
  885.     }
  886.  
  887.     return(retval);
  888. }
  889.  
  890. int evaluate(char *exp)
  891.   /* Evaluate expression and check that we've read all the tokens
  892.    * Returns value or ERROR.
  893.    *   Possible errors: BAD_PARENS
  894.    */
  895. {
  896.     int value;
  897.  
  898.     value = expression(exp);
  899.  
  900.     if (get_token(exp) != NULL)
  901.         return((!error ++) ? raise_error(BAD_PARENS, NONFATAL, NULL) : 0);
  902.     else
  903.         return(value);
  904. }
  905.  
  906. int expression(char *exp)
  907.   /* Recursively evaluate the expression given. */
  908. {
  909.     char *word;
  910.     int val = 0;
  911.  
  912.     val = term(exp);
  913.  
  914.     if ((word = get_token(exp)) == NULL)
  915.         return(val);
  916.  
  917.     if (!addops(word[0])) {
  918.         unget_token();
  919.         return(val);
  920.     }
  921.  
  922.     while (addops(word[0])) {
  923.         if (word[0] == PLUS)
  924.             val += term(exp);
  925.         else  /* Must be MINUS */
  926.             val -= term(exp);
  927.  
  928.         if ((word = get_token(exp)) == NULL)
  929.             return(val);
  930.         if (! addops(word[0])) {
  931.             unget_token();
  932.             return(val);
  933.         }
  934.     }
  935.  
  936.     return(val);
  937. }
  938.  
  939. int term(char *exp)
  940.   /* Get a term (ie a multiply or divide) and return the results
  941.    * of computing it.
  942.    *   Possible errors: DIVIDE_BY_ZERO
  943.    */
  944. {
  945.     register int val = 0, value;
  946.     char *word;
  947.  
  948.     val = factor(exp);
  949.  
  950.     /* if ((word = get_token(exp,3)) == NULL) */
  951.     if ((word = get_token(exp)) == NULL)
  952.         return(val);
  953.  
  954.     if (!mulops(word[0])) {
  955.         unget_token();
  956.         return(val);
  957.     }
  958.  
  959.     while (mulops(word[0])) {
  960.         if (word[0] == TIMES)
  961.             val *= factor(exp);
  962.         else if ((value = factor(exp)) == 0) {
  963.             if (!error ++) {
  964.                 raise_error(DIVIDE_BY_ZERO, FATAL, NULL);
  965.                 wrapup();
  966.             }
  967.             else
  968.                 return(0);
  969.         }
  970.         else
  971.             val /= value;
  972.  
  973.         if ((word = get_token(exp)) == NULL)
  974.             return(val);
  975.  
  976.         if (!mulops(word[0])) {
  977.             unget_token();
  978.             return(val);
  979.         }
  980.     }
  981.  
  982.     return(val);
  983. }
  984.  
  985. int factor(char *string)
  986.   /* Break down a string - either another expression in parentheses,
  987.    * a specific numerical value or a variable name
  988.    *   Possible errors: BAD_EXP, BAD_PARENS, STRING_IN_EXP, UNDEF_VAR
  989.    */
  990. {
  991.     struct symbol_entry *entry, *find_symbol();
  992.     int val = 0;
  993.     char *word;
  994.  
  995.     if ((word = get_token(string)) == NULL)
  996.       return((!error ++) ? raise_error(BAD_EXP, NONFATAL, NULL) : 0);
  997.  
  998.     if (word[0] == LEFT_PAREN) {
  999.         val = expression(string);
  1000.         if ((word = get_token(string)) == NULL)
  1001.             return((!error ++) ? raise_error(BAD_PARENS, NONFATAL, NULL) : 0);
  1002.         else if (word[0] != RIGHT_PAREN)
  1003.             return((!error ++) ? raise_error(BAD_EXP, NONFATAL, NULL) : 0);
  1004.     }
  1005.     else if (stringvar(word))  /* What's THIS doing here?? */
  1006.         return((!error ++) ? raise_error(STRING_IN_EXP, NONFATAL, NULL) : 0);
  1007.     else if (numvar(word)) {
  1008.         if ((entry = find_symbol(symbol_table, word)) == NULL)
  1009.             return((!error ++) ? raise_error(UNDEF_VAR, NONFATAL, word) : 0);
  1010.         val = entry->numvalue;
  1011.     }
  1012.     else if (word[0] == '-') {  /* Minus number */
  1013.         if ((word = get_token(string)) == NULL)
  1014.             return((!error ++) ? raise_error(BAD_EXP, NONFATAL, NULL) : 0);
  1015.         val = -atoi(word);
  1016.     }
  1017.     else
  1018.         val = atoi(word);
  1019.  
  1020.     return(val);
  1021. }
  1022.  
  1023.  
  1024. char *get_token(char *line)
  1025.   /* Return the next token in the line without surrounding white spaces.
  1026.    * Return zero if at end-of-line
  1027.    */
  1028. {
  1029.     static char word[SLEN];
  1030.     register int i = 0;
  1031.  
  1032.     while (whitespace(line[line_loc]))
  1033.         line_loc ++;
  1034.  
  1035.     last_line_loc = line_loc;
  1036.  
  1037.     if (end_of_line(line[line_loc]))
  1038.         return(NULL);
  1039.  
  1040.     if (mathops(line[line_loc]) || paren(line[line_loc]))
  1041.         word[i ++] = line[line_loc ++];
  1042.     else if (relop(line[line_loc])) {
  1043.         word[i ++] = line[line_loc ++];
  1044.         if (relop(line[line_loc]) && line[line_loc] != line[line_loc-1])
  1045.             word[i++] = line[line_loc++];
  1046.     }
  1047.     else {
  1048.         while (!special(line[line_loc]) && !whitespace(line[line_loc]) &&
  1049.           !end_of_line(line[line_loc]))
  1050.             if (i == NLEN-1) {
  1051.                 word[i] = '\0';
  1052.                 raise_error(NAME_TOO_LONG, FATAL, word);
  1053.                 wrapup();
  1054.             }
  1055.             else
  1056.                 word[i ++] = line[line_loc ++];
  1057.     }
  1058.  
  1059.     word[i] = '\0';
  1060.  
  1061.     return((char *) word);
  1062. }
  1063.