home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / alde_c / misc / zed / calc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-01-07  |  19.7 KB  |  957 lines

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