home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / CALC / CALC.ZIP / CALC.C
Encoding:
C/C++ Source or Header  |  1988-07-21  |  19.8 KB  |  956 lines

  1. #include <stdio.h>
  2.  
  3. /*
  4.  * Calculator program - 9 May 1985
  5.  * Please send corrections or remarks to:
  6.  *    Bob Brodt
  7.  *    34 Mehrhof Rd.
  8.  *    Little Ferry, NJ 07643
  9.  *    (201)-641-9582
  10.  *
  11.  * This is a simple integer arithmetic calculator program. It uses infix
  12.  * notation, i.e. 1+2*3 as opposed to "reverse polish" notation: 1 2 3 * +.
  13.  *
  14.  * CONSTANTS:
  15.  *    Numbers may be input as in C using 0x notation for hex, and a
  16.  *    leading zero for octal, everything else is assumed to be decimal.
  17.  * VARIABLES:
  18.  *    Also available are 26 registers referenced by a single letter
  19.  *    (a-z or A-Z, case is ignored).
  20.  * OPERATORS:
  21.  *    The following operators are supported (from highest precedence to
  22.  *    lowest):
  23.  *
  24.  *    ( )        associativity
  25.  *    ~        one's complement
  26.  *    * / %        mutliply, divide and modulo
  27.  *    + -        unary and binary add & subtract
  28.  *    << >>        shift left and right
  29.  *    &        bitwise AND
  30.  *    ^        bitwise exclusive OR
  31.  *    |        bitwise inclusive OR
  32.  *    =        assignment
  33.  *    ,        comma - seperates function arguments
  34.  *
  35.  *    All operators associate from left to right with the exception of
  36.  *    the assignment (=) operator.
  37.  * FUNCTIONS:
  38.  *    The calculator also has built-in function capabilties which may be
  39.  *    enhanced later:
  40.  *    base n  - set the output conversion base to n (n = 8, 10 or 16)
  41.  * ADDITIONAL FEATURES:
  42.  *    help - displays a help screen containing the above information.
  43.  *    ? - displays all of the variables and their current values.
  44.  */
  45.  
  46. /*
  47.  * Tokens
  48.  */
  49. #define T_EOL        ';'
  50. #define T_CONST        'C'
  51. #define T_SYMBOL    'S'
  52. #define T_LPAR        '('
  53. #define T_RPAR        ')'
  54. #define T_COMMA        ','
  55. #define T_ASSIGN    '='
  56. #define T_POINT        '$'
  57. #define T_MUL        '*'
  58. #define T_DIV        '/'
  59. #define T_MOD        '%'
  60. #define T_ADD        '+'
  61. #define T_SUB        '-'
  62. #define T_NEG        '_'
  63. #define T_NOT        '~'
  64. #define T_SHL        'L'
  65. #define T_SHR        'R'
  66. #define T_AND        '&'
  67. #define T_XOR        '^'
  68. #define T_IOR        '|'
  69. #define T_FUNC        'F'
  70.  
  71.  
  72. /*
  73.  * Operator/Operand stack, base ptr and top of stack ptr
  74.  */
  75. #define STAKSZ 128
  76. struct {
  77.     char o_token;
  78.     int o_value;
  79. } Opstk[ STAKSZ ];
  80. int Opbase, Opsp;
  81.  
  82. /*
  83.  * Value (working) stack, base ptr and top of stack ptr
  84.  */
  85. int Valstk[ STAKSZ ];
  86. int Valbase, Valsp;
  87.  
  88. /*
  89.  * Built-in Functions and jump table
  90.  */
  91. int f_sum(), f_base();
  92. int (*Functab[ 2 ])();
  93.  
  94. /*
  95.  * Symbol Table
  96.  */
  97. int Symbols[ 26 ];
  98.     
  99. /*
  100.  * Miscellaneous
  101.  */
  102. #define MAXLEVEL 32
  103. char Level;    /* current recursion level in calc() */
  104. char Token;    /* current input token */
  105. int Value;    /* and its value */
  106. char Eol;    /* set when end of line encountered */
  107. char *Lineptr;    /* points to next character in input line */
  108. char *Ofmt;    /* current output format (set by "base" command) */
  109. int Error;    /* set if parsing error occurred */
  110. char Nohelp;    /* set if help was given already */
  111.  
  112. char *skipws();
  113.  
  114. main()
  115. {
  116.     char line[ 1024 ], *cp;
  117.  
  118.     initialize();
  119.  
  120.     for ( ;; )
  121.     {
  122.         Error = 0;
  123.         /*
  124.          * read a line from stdin and try to parse it
  125.          */
  126.         printf( "? " );
  127.         if ( ! gets( line ) )
  128.             break;
  129.         cp = skipws( line );
  130.         if ( *cp )
  131.         {
  132.             if ( *cp == '?' )
  133.             {
  134.                 dumpvars();
  135.                 continue;
  136.             }
  137.             else if ( strcmp( cp, "quit" ) == 0 )
  138.                 break;
  139.             else if ( strcmp( cp, "help" ) == 0 )
  140.             {
  141.                 help();
  142.                 continue;
  143.             }
  144.             calc( cp );
  145.             /*
  146.              * If no parsing errors were found, print the
  147.              * results of the evaluation of the expression.
  148.              */
  149.             if ( Error && Nohelp )
  150.             {
  151.                 printf( "do you need help ? " );
  152.                 gets( line );    
  153.                 cp = skipws( line );    
  154.                 if ( toupper( *cp ) == 'Y' )
  155.                     help();
  156.             }
  157.             else if ( ! Error )
  158.             {
  159.                 printf( Ofmt, Value );
  160.                 putchar( '\n' );
  161.             }
  162.         }
  163.     }
  164.     printf( "bye bye\n" );
  165. }
  166.  
  167. initialize()
  168. {
  169.     /*
  170.      * initialize
  171.      */
  172.     Functab[0] = f_sum;
  173.     Functab[1] = f_base;
  174.         
  175.     push( 10 );
  176.     f_base();
  177.     Nohelp = 1;
  178. }
  179.  
  180. help()
  181. {
  182.     Nohelp = 0;
  183.  
  184. printf("This calculator uses infix notation i.e. 1+2*3 as opposed to\n");
  185. printf("\"reverse polish\" notation: 1 2 3 * +.\n");
  186. printf("CONSTANTS:\n");
  187. printf(" Numbers may be entered using 0x notation for hex (like in C),\n");
  188. printf(" and a leading zero for octal, everything else is assumed to be\n");
  189. printf(" decimal.\n");
  190. printf("VARIABLES:\n");
  191. printf(" Also available are 26 registers referenced by a single letter\n");
  192. printf(" (a-z or A-Z, case is ignored).\n");
  193. printf("OPERATORS:\n");
  194. printf(" The following operators are supported (from highest precedence\n");
  195. printf(" to lowest):\n");
  196. printf("\n");
  197. printf(" ( )        associativity\n");
  198. printf(" ~        one's complement\n");
  199. printf(" * / %        mutliply, divide and modulo\n");
  200. printf(" + -        unary and binary add & subtract\n");
  201. printf(" << >>        shift left and right\n");
  202. printf(" &        bitwise AND\n");
  203. printf(" ^        bitwise exclusive OR\n");
  204. printf(" |        bitwise inclusive OR\n");
  205. printf("-MORE-" );
  206.     getchar();
  207. printf(" =        assignment\n");
  208. printf(" ,        comma - seperates function arguments\n");
  209. printf("\n");
  210. printf(" All operators associate from left to right with the exception\n");
  211. printf(" of the assignment (=) operator.\n");
  212. printf("FUNCTIONS:\n");
  213. printf(" The calculator also has built-in function capabilties (which\n");
  214. printf(" may be enhanced in a later revision):\n");
  215. printf("\n");
  216. printf(" base n  - set the output conversion base to n (n = 8, 10 or 16)\n");
  217. printf("\n");
  218. printf("ADDITIONAL FEATURES:\n");
  219. printf(" help - displays a help screen containing the above information.\n");
  220. printf(" ? - displays all of the variables and their current values.\n");
  221. }
  222.  
  223. dumpvars()
  224. {
  225.     int i;
  226.  
  227.     for ( i=0; i<26; ++i )
  228.     {
  229.         if ( !(i % 8) )
  230.             printf( "\n" );
  231.         printf( "%c=", i + 'A' );
  232.         printf( Ofmt, Symbols[ i ] );
  233.         printf( "\t" );
  234.     }
  235.     printf( "\n" );
  236. }
  237.  
  238. /*************************************************************
  239. *                 EXPRESSION EVALUATOR                       *
  240. **************************************************************/
  241. /*
  242.  * NOTE: The comments make reference to "lvalues" and "rvalues". These
  243.  * are attributes of <primaries> (primaries, for the layman, are things
  244.  * like constants and variables, and parenthesized expressions. If you
  245.  * don't know what an expression is, you shouldn't be a reading this!).
  246.  * If a <primary> is an "lvalue", it means that it can usually be found on
  247.  * LEFT-HAND side of an assignment operator. "rvalues" can only be found
  248.  * on the RIGHT-HAND side of an assignment. Simply stated, only things like
  249.  * variables can be used as both "lvalues" and "rvalues", whereas things
  250.  * like constants and parenthesized expressions can only be "rvalues" since
  251.  * it wouldn't make sense to say: 12 = 5.
  252.  */
  253.  
  254.  
  255. calc( line )
  256. char *line;
  257. {
  258.     /*
  259.      * Parse and calculate an arithmetic expression pointed to
  260.      * by "line". This routine is fully re-entrant - a feature
  261.      * that is not currently used, but may come in handy later
  262.      * (for instance if a variable points to a character string
  263.      * that contains an expression to be evaluated).
  264.      */
  265.     char *savLineptr;
  266.     int savValbase, savValsp, savOpbase, savOpsp;
  267.  
  268.     if ( ++Level > MAXLEVEL )
  269.     {
  270.         err( "recursion" );
  271.     }
  272.     else
  273.     {
  274.         /*
  275.          * Save all global variables that may change if calc()
  276.          * is called recursively.
  277.          */
  278.         savLineptr = Lineptr;
  279.         savValbase = Valbase;
  280.         savValsp = Valsp;
  281.         savOpbase = Opbase;
  282.         savOpsp = Opsp;
  283.  
  284.         /*
  285.          * Set up stack base ptrs and input line ptr
  286.          */
  287.         Valbase = Valsp;
  288.         Opbase = Opsp;
  289.         Lineptr = line;
  290.  
  291.         /*
  292.          * Get the first token from input line and parse an
  293.          * expression and then evaluate it.
  294.          */
  295.         Eol = 0;
  296.  
  297.         getoken();
  298.         if ( ! Eol )
  299.         {
  300.             expression();
  301.             if ( Error <= 0 )
  302.                 Value = evaluate();
  303.         }
  304.         /*
  305.          * Restore previous values of globals
  306.          */
  307.         Lineptr = savLineptr;
  308.         Valbase = savValbase;
  309.         Valsp = savValsp;
  310.         Opbase = savOpbase;
  311.         Opsp = savOpsp;
  312.     }
  313.  
  314.     --Level;
  315. }
  316.  
  317. evaluate()
  318. {
  319.     /*
  320.      * Evaluate an expression by popping operators and operands
  321.      * from the Operator/Operand stack and performing each indicated
  322.      * operation. Only the operators starting from current base ptr
  323.      * (Opbase) to top of stack (Opsp) are evaluated, so that evaluate()
  324.      * may be called by any generation of calc().
  325.      */
  326.     int opsp, val, *ip;
  327.     char op;
  328.  
  329. #define TOS (Valstk[Valsp-1])    /* top of stack macro */
  330.  
  331.     for ( opsp=Opbase; opsp<Opsp; ++opsp )
  332.     {
  333.         op = Opstk[ opsp ].o_token;
  334.  
  335.         if ( op == T_CONST )
  336.             push( Opstk[ opsp ].o_value );
  337.         else if ( op == T_SYMBOL )
  338.             /*
  339.              * Push the address of a variable
  340.              */
  341.             push( &Symbols[ Opstk[ opsp ].o_value ] );
  342.         else if ( op == T_POINT )
  343.         {
  344.             /*
  345.              * Get the value of the integer pointed to by the
  346.              * address on top of the stack. This usually follows
  347.              * a T_SYMBOL when the symbol is not being used
  348.              * as an "lvalue".
  349.              */
  350.             ip = pop();
  351.             push( *ip );
  352.         }
  353.         else if ( op == T_ASSIGN )
  354.         {
  355.             /*
  356.              * Assignment operator: The item on top of stack is
  357.              * the "rvalue", second on stack is the "lvalue"
  358.              * (an address where to store the "rvalue"). The
  359.              * "rvalue" gets pushed back on top of the stack.
  360.              */
  361.             val = pop();
  362.             ip = pop();
  363.             push( *ip = val );
  364.         }
  365.         else if ( op == T_ADD )
  366.         {
  367.             val = pop();
  368.             TOS += val;
  369.         }
  370.         else if ( op == T_SUB )
  371.         {
  372.             val = pop();
  373.             TOS -= val;
  374.         }
  375.         else if ( op == T_NOT )
  376.             TOS = ~TOS;
  377.         else if ( op == T_NEG )
  378.             TOS = -TOS;
  379.         else if ( op == T_MUL )
  380.         {
  381.             val = pop();
  382.             TOS *= val;
  383.         }
  384.         else if ( op == T_DIV )
  385.         {
  386.             val = pop();
  387.             TOS /= val;
  388.         }
  389.         else if ( op == T_MOD )
  390.         {
  391.             val = pop();
  392.             TOS %= val;
  393.         }
  394.         else if ( op == T_SHL )
  395.         {
  396.             val = pop();
  397.             TOS <<= val;
  398.         }
  399.         else if ( op == T_SHR )
  400.         {
  401.             val = pop();
  402.             TOS >>= val;
  403.         }
  404.         else if ( op == T_AND )
  405.         {
  406.             val = pop();
  407.             TOS &= val;
  408.         }
  409.         else if ( op == T_XOR )
  410.         {
  411.             val = pop();
  412.             TOS ^= val;
  413.         }
  414.         else if ( op == T_IOR )
  415.         {
  416.             val = pop();
  417.             TOS |= val;
  418.         }
  419.         else if ( op == T_COMMA )
  420.             ;
  421.         else if ( op == T_FUNC )
  422.             push( (*Functab[ Opstk[ opsp ].o_value ])() );
  423.         else
  424.             err( "bad operator in stack: 0x%x (%c)", op, op );
  425.     }
  426.     return Valstk[ Valbase ];
  427. }
  428.  
  429. push( val )
  430. {
  431.     if ( Valsp >= STAKSZ )
  432.         err( "stack overflow" );
  433.     return Valstk[ Valsp++ ] = val;
  434. }
  435.  
  436. pop()
  437. {
  438.     if ( --Valsp < 0 )
  439.         Valsp = 0;
  440.     return Valstk[ Valsp ];
  441. }
  442.  
  443. /*************************************************************
  444. *                   EXPRESSION PARSER                        *
  445. **************************************************************/
  446. expression()
  447. {
  448.     /*
  449.      * Parse an expression. Expressions have the following syntax:
  450.      *    <expression> := <primary> <operator> <primary>
  451.      * so the first thing to look for is a primary.
  452.      */
  453.     int lvalue;
  454.  
  455.     if ( !(lvalue = primary()) )
  456.         err( "bad expression" );
  457.     else if ( Eol )
  458.     {
  459.         if ( lvalue < 0 )
  460.             generate( T_POINT, 0 );
  461.     }
  462.     else
  463.         op_prim( 0, lvalue );
  464. }
  465.  
  466. op_prim( precedence, lvalue )
  467. int precedence;    /* precedence of current <operator> */
  468. int lvalue;    /* type of current <primary>: -1 => lvalue */
  469.         /*                             0 => no <primary> (error) */
  470.         /*                             1 => rvalue */
  471. {
  472.     /*
  473.      * Parse the <operator> <primary> part of an expression.
  474.      * "precedence" is the PREVIOUS <operator>'s precedence level
  475.      * (0=low, +n=high).
  476.      */
  477.     char tkn;
  478.     int pr, lv;
  479.  
  480.     while ( ! Eol )
  481.     {
  482.         /*
  483.          * Get the precedence level of current <operator> ("pr").
  484.          * If it is greater than previous operator ("precedence"),
  485.          * get the next <primary> and do another <operator> <primary>
  486.          * NOTE: For left-to-right associativity, the condition
  487.          *     pr > precedence
  488.          * must be true. for right-to-left associativity,
  489.          *     pr >= precedence
  490.          * must be true (assignment operator only).
  491.          */
  492.  
  493.         pr = binop( Token );
  494.  
  495.         if (
  496.             (pr>precedence && pr>0) ||
  497.             (Token==T_ASSIGN && pr>=precedence)
  498.         )
  499.         {
  500.             if ( Token == T_ASSIGN )
  501.             {
  502.                 if ( lvalue > 0 )
  503.                     err( "= needs and lvalue" );
  504.             }
  505.             else if ( lvalue < 0 )
  506.                 generate( T_POINT, 0 );
  507.  
  508.             /*
  509.              * Save the operator token and do a primary.
  510.              */
  511.             tkn = Token;
  512.             getoken();
  513.             if ( ! (lv = primary()) )
  514.                 err( "missing an operand" );
  515.             /*
  516.              * Now look at the next operator. If its precedence
  517.              * is greater than this one ("tkn" above), generate
  518.              * code for it BEFORE this one.
  519.              */
  520.             lvalue = op_prim( pr, lv );
  521.  
  522.             if ( Token != T_ASSIGN && lvalue < 0 )
  523.             {
  524.                 /*
  525.                  * Next operator is not the assignment op.
  526.                  * and the current <primary> is an lvalue,
  527.                  * therefore generate a "load from address
  528.                  * on top of stack" instruction.
  529.                  */
  530.                 generate( T_POINT, 0 );
  531.                 /*
  532.                  * This makes it an rvalue now.
  533.                  */
  534.                 lvalue = 1;
  535.             }
  536.             else if ( tkn!=T_ASSIGN && Token==T_ASSIGN )
  537.             {
  538.                 /*
  539.                  * YEECH! this is the only way I know of to
  540.                  * detect errors like: a+b=c
  541.                  */
  542.                 err( "= needs an lvalue" );
  543.             }
  544.  
  545.             /*
  546.              * Generate the instruction for the current operator.
  547.              */
  548.             generate( tkn, 0 );
  549.         }
  550.         else
  551.             break;
  552.     }
  553.  
  554.     return lvalue;
  555. }
  556.  
  557. primary()
  558. {
  559.     /*
  560.      * Parse a primary. Primaries have the following syntax:
  561.      *    <primary> := <constant> |
  562.      *                 '(' <expression> ')' |
  563.      *                 <unary op> <primary> |
  564.      *                 <function> <primary>
  565.      */
  566.     int rtn;
  567.     int val;
  568.  
  569.     if ( Eol )
  570.         return 0;
  571.  
  572.     /*
  573.      * Return value:
  574.      *   -1   => the <primary> is an lvalue
  575.      *    0   => not a <primary> (usually end of line or a syntax error)
  576.      *    1   => the <primary> is an rvalue
  577.      */
  578.     rtn = 1;
  579.  
  580.     switch ( Token )
  581.     {
  582.     case T_SYMBOL:    /* a symbol */
  583.         rtn = -1;
  584.     case T_CONST:    /* a constant */
  585.         generate( Token, Value );
  586.         getoken();
  587.         break;
  588.     case T_LPAR:    /* a parenthesized expression */
  589.         getoken();
  590.         expression();
  591.         if ( Token != T_RPAR )
  592.         {
  593.             err( "missing ')'" );
  594.             rtn = 0;
  595.         }
  596.         else
  597.             getoken();
  598.         break;
  599.     case T_SUB:    /* unary - */
  600.         /*
  601.          * The lexical analyzer is not smart enough to recognize
  602.          * unary operators (+ and -), that's why we have to do
  603.          * it here
  604.          */
  605.         getoken();
  606.         expression();
  607.         generate( T_NEG, 0 );
  608.         break;
  609.     case T_NOT:    /* unary ~ */
  610.         getoken();
  611.         expression();
  612.         generate( T_NOT, 0 );
  613.         break;
  614.     case T_ADD:    /* unary + */
  615.         getoken();
  616.         expression();
  617.         break;
  618.     case T_FUNC:    /* built-in function */
  619.         val = Value;
  620.         getoken();
  621.         expression();
  622.         generate( T_FUNC, val );
  623.         break;
  624.     default:
  625.         /*
  626.          * Not a primary
  627.          */
  628.         rtn = 0;
  629.     }
  630.     return rtn;
  631. }
  632.  
  633. binop( op )
  634. char op;
  635. {
  636.     /*
  637.      * Determine if "op" is a binary operator and return its
  638.      * precedence level if so. If not, return 0.
  639.      */
  640.     switch ( op )
  641.     {
  642.     case T_COMMA:
  643.         return 1;
  644.     case T_ASSIGN:
  645.         return 2;
  646.     case T_IOR:
  647.         return 3;
  648.     case T_XOR:
  649.         return 4;
  650.     case T_AND:
  651.         return 5;
  652.     case T_SHL:
  653.     case T_SHR:
  654.         return 6;
  655.     case T_ADD:
  656.     case T_SUB:
  657.         return 7;
  658.     case T_MUL:
  659.     case T_DIV:
  660.     case T_MOD:
  661.         return 8;
  662.     case T_NOT:
  663.         return 9;
  664.     }
  665.     return 0;
  666. }
  667.  
  668. generate( tkn, val )
  669. char tkn;
  670. {
  671.     /*
  672.      * Push the given token and value onto the Operator/Operand stack.
  673.      */
  674.     if ( Opsp < STAKSZ )
  675.     {
  676.         Opstk[ Opsp ].o_token = tkn;
  677.         Opstk[ Opsp ].o_value = val;
  678.         ++Opsp;
  679.     }
  680.     else
  681.         err( "expression too complex" );
  682. }
  683.  
  684. /*************************************************************
  685. *                     LEXICAL ANALYZER                       *
  686. *************************************************************/
  687. getoken()
  688. {
  689.     /*
  690.      * Lexical Analyzer. Gets next token from the input line
  691.      * pointed to by "Lineptr" and advances "Lineptr" to next
  692.      * character. If end of input line is encountered, the
  693.      * "Eol" flag is set.
  694.      */
  695.     char *cp, buf[ 128 ];
  696.     int i;
  697.  
  698.     /*
  699.      * skip white space
  700.      */
  701.     Lineptr = skipws( Lineptr );
  702.  
  703.     if ( ! *Lineptr )
  704.     {
  705.         Eol = 1;
  706.         Token = T_EOL;
  707.     }
  708.     else if ( *Lineptr == '0' )
  709.     {
  710.         /*
  711.          * Check if it's a hex or octal constant
  712.          */
  713.         Token = T_CONST;
  714.         ++Lineptr;
  715.         if ( toupper( *Lineptr ) == 'X' )
  716.         {
  717.             ++Lineptr;
  718.             for ( cp = buf; ishexdigit( *Lineptr ); )
  719.                 *cp++ = *Lineptr++;
  720.             *cp = 0;
  721.             sscanf( buf, "%x", &Value );
  722.         }
  723.         else if ( isdigit( *Lineptr ) )
  724.         {
  725.             for ( cp = buf; isoctdigit( *Lineptr ); )
  726.                 *cp++ = *Lineptr++;
  727.             *cp = 0;
  728.             sscanf( buf, "%o", &Value );
  729.         }
  730.         else
  731.             Value = 0;
  732.     }
  733.     else if ( isdigit( *Lineptr ) )
  734.     {
  735.         /*
  736.          * It's a numeric constant, "Value" will be its value.
  737.          */
  738.         Token = T_CONST;
  739.         for ( cp = buf; isdigit( *Lineptr ); )
  740.             *cp++ = *Lineptr++;
  741.         *cp = 0;
  742.         Value = atoi( buf );
  743.     }
  744.     else if ( Value = isfunc( &Lineptr ) )
  745.     {
  746.         /*
  747.          * It's a built-in function, "Value" will be the index
  748.          * into the function jump table.
  749.          */
  750.         Token = T_FUNC;
  751.         --Value;
  752.     }
  753.     else if ( Token = isbinop( &Lineptr ) )
  754.     {
  755.         /*
  756.          * It's a binary operator
  757.          */
  758.         ;
  759.     }
  760.     else if ( isalpha( *Lineptr ) )
  761.     {
  762.         Token = T_SYMBOL;
  763.         Value = toupper( *Lineptr ) - 'A';
  764.         ++Lineptr;
  765.     }
  766.     else
  767.     {
  768.         /*
  769.          * Bad character in input line
  770.          */
  771.         err( "bad syntax: %s", Lineptr );
  772.         ++Lineptr;
  773.     }
  774.  
  775.     return Token;
  776. }
  777.  
  778. char *
  779. skipws( cp )
  780. char *cp;
  781. {
  782.     while ( *cp==' ' || *cp=='\t' )
  783.         ++cp;
  784.     return cp;
  785. }
  786.  
  787. isfunc( cpp )
  788. char **cpp;
  789. {
  790.     /*
  791.      * Check if *cpp is the name of a built-function, return the
  792.      * function jump table index+1 if so and bump *cpp to next
  793.      * character. Return 0 if not a function.
  794.      */
  795.     char *cp, *bp, buf[ 80 ];
  796.     int funcno;
  797.  
  798.     /*
  799.      * copy the name from input line buffer to a local buffer so
  800.      * we can use it to make a proper comparison to function names.
  801.      */
  802.     for ( cp=*cpp, bp=buf; isalpha( *cp ); )
  803.         *bp++ = *cp++;
  804.     *bp = 0;
  805.  
  806.     /*
  807.      * compare it to all of the function names we know about.
  808.      */
  809.     if ( strcmp( buf, "sum" ) == 0 )
  810.         funcno = 1;
  811.     else if ( strcmp( buf, "base" ) == 0 )
  812.         funcno = 2;
  813.     else
  814.         /*
  815.          * not a built-in function
  816.          */
  817.         funcno = 0;
  818.  
  819.     if ( funcno )
  820.         *cpp = cp;
  821.  
  822.     return funcno;
  823. }
  824.  
  825. isbinop( cpp )
  826. char **cpp;
  827. {
  828.     /*
  829.      * Check if *cpp is a binary operator, return its token value
  830.      * and bump *cpp to next character.
  831.      */
  832.     int tkn;
  833.     char c;
  834.  
  835.     c = **cpp;
  836.     if ( c == ',' )
  837.         tkn = T_COMMA;
  838.     else if ( c == '=' )
  839.         tkn = T_ASSIGN;
  840.     else if ( c == '<' )
  841.     {
  842.         if ( *(++(*cpp)) == '<' )
  843.             tkn = T_SHL;
  844.     }
  845.     else if ( c == '>' )
  846.     {
  847.         if ( *(++(*cpp)) == '>' )
  848.             tkn = T_SHR;
  849.     }
  850.     else if ( c == '(' )
  851.         tkn = T_LPAR;
  852.     else if ( c == ')' )
  853.         tkn = T_RPAR;
  854.     else if ( c == '*' )
  855.         tkn = T_MUL;
  856.     else if ( c == '/' )
  857.         tkn = T_DIV;
  858.     else if ( c == '%' )
  859.         tkn = T_MOD;
  860.     else if ( c == '+' )
  861.         tkn = T_ADD;
  862.     else if ( c == '-' )
  863.         tkn = T_SUB;
  864.     else if ( c == '^' )
  865.         tkn = T_XOR;
  866.     else if ( c == '&' )
  867.         tkn = T_AND;
  868.     else if ( c == '|' )
  869.         tkn = T_IOR;
  870.     else if ( c == '~' )
  871.         tkn = T_NOT;
  872.     else
  873.         tkn = 0;
  874.  
  875.     if ( tkn )
  876.         ++(*cpp);
  877.  
  878.     return tkn;
  879. }
  880.  
  881. /*************************************************************
  882. *                   BUILT-IN FUNCTIONS                       *
  883. **************************************************************/
  884. f_sum()
  885. {
  886.     /*
  887.      * usage: sum( a, b )
  888.      *    Sum all the numbers between a and b
  889.      */
  890.     int i, a, b;
  891.  
  892.     b = pop();
  893.     a = pop();
  894.     for ( i=a+1; i<=b; ++i )
  895.         a += i;
  896.  
  897.     push( a );
  898. }
  899.  
  900. f_base()
  901. {
  902.     /*
  903.      * usage: base( n )
  904.      *    Set output number base
  905.      */
  906.     switch ( pop() )
  907.     {
  908.     case 8:
  909.         Ofmt = "0%o";
  910.         break;
  911.     case 16:
  912.         Ofmt = "0x%x";
  913.         break;
  914.     case 10:
  915.     default:
  916.         Ofmt = "%d";
  917.         break;
  918.     }
  919. }
  920.  
  921.  
  922. /*************************************************************
  923. *                       MISCELLANEOUS                        *
  924. **************************************************************/
  925. err( a, b, c, d, e, f )
  926. {
  927.     if ( ! Error )
  928.     {
  929.         printf( a, b, c, d, e, f );
  930.         putchar( '\n' );
  931.         Error = 1;
  932.     }
  933. }
  934.  
  935. ishexdigit( c )
  936. char c;
  937. {
  938.     return instr( c, "0123456789abcdefABCDEF" );
  939. }
  940.  
  941. isoctdigit( c )
  942. char c;
  943. {
  944.     return instr( c, "01234567" );
  945. }
  946.  
  947. instr( c, s )
  948. char c, *s;
  949. {
  950.     while ( *s )
  951.         if ( c == *s++ )
  952.             return 1;
  953.  
  954.     return 0;
  955. }
  956.