home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pilot / pilot.c < prev    next >
Text File  |  1986-01-27  |  27KB  |  985 lines

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