home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / IRIT / IRITS.ZIP / INPTPRSR.C < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-05  |  34.3 KB  |  1,131 lines

  1. /*****************************************************************************
  2. *   "Irit" - the 3d polygonal solid modeller.                     *
  3. *                                         *
  4. * Written by:  Gershon Elber                Ver 0.2, Mar. 1990   *
  5. ******************************************************************************
  6. *   Module to convert infix expression given as    ascii stream sequence into   *
  7. * a binary tree, and evaluate it.                         *
  8. *   All the objects are handled the same but the numerical one, which is     *
  9. * moved as a RealType and not as an object (only internally within this         *
  10. * module) as it is frequently used and consumes much less memory this way.   *
  11. *****************************************************************************/
  12.  
  13. #ifdef __MSDOS__
  14. #include <alloc.h>
  15. #include <graphics.h>
  16. #endif /* __MSDOS__ */
  17.  
  18. #include <stdio.h>
  19. #include <ctype.h>
  20. #include <math.h>
  21. #include <string.h>
  22. #include "program.h"
  23. #include "objects.h"
  24. #include "allocatg.h"
  25. #include "windowsg.h"
  26. #include "inptprsg.h"
  27. #include "inptprsl.h"
  28. #include "overload.h"
  29. #include "ctrl-brk.h"
  30.  
  31. #ifndef __MSDOS__
  32. #include "xgraphic.h"
  33. #endif /* __MSDOS__ */
  34.  
  35.  
  36. static int IPGlblILastToken, IPGlblParseError;    /* Globals used by parser. */
  37. char IPGlblCharData[LINE_LEN_LONG];          /* Used for both parse & eval. */
  38. static FileStackStruct FileStack[FILE_STACK_SIZE];    /* Include file stack. */
  39. static int FileStackPtr = 0;
  40.  
  41. /* Operator preceeding parser stack, and stack pointer: */
  42. static ParseTree *Stack[MAX_PARSER_STACK];
  43. static int ParserStackPointer = 0;
  44.  
  45. /* Aliases list - simple macro substitution mechanizem. */
  46. static AliasesStruct GlobalAliasList;
  47.  
  48. #ifdef DEBUG
  49.     int MaxStackPointer = 0;          /* Measure maximum depth of stack. */
  50. #endif /* DEBUG */
  51.  
  52. static struct ParseTree *GenInputParseTree(void);
  53. static ParseTree *OperatorPrecedence(void);
  54. static int TestPreceeding(int Token1, int Token2);
  55. static int GetToken(RealType *Data);
  56. static int GetVarFuncToken(char *Token, RealType *Data);
  57. static void FlushToEndOfExpr(int FlushStdin);
  58. static void InptPrsrUnGetC(char c);
  59. static char InptPrsrGetC(void);
  60.  
  61. /*****************************************************************************
  62. *   Main module routine - generate parse tree and then tries to evaluate it. *
  63. * Returns TRUE if succesfull, otherwise check IPGlblParseError/EvalError.    *
  64. *****************************************************************************/
  65. int InputParser(void)
  66. {
  67.     struct ParseTree *PTree;
  68.  
  69.     if (WasCtrlBrk || GlblFatalError) {
  70.     GlblFatalError = WasCtrlBrk = FALSE;
  71.     FlushToEndOfExpr(FALSE);      /* Close all include files if any. */
  72.     return TRUE;
  73.     }
  74.  
  75.     PTree = GenInputParseTree();             /* Generate parse tree. */
  76.  
  77.     if (IPGlblParseError == 0) {
  78. #    ifdef DEBUG
  79.         fprintf(stderr, "\nInput generated Parse tree (Max stack = %d)\n",
  80.                             MaxStackPointer);
  81.         InptPrsrPrintTree(PTree, NULL);
  82.         fprintf(stderr, "\n");
  83. #    endif /* DEBUG */
  84.     if (InptPrsrTypeCheck(PTree, 0) == ERROR_EXPR) {   /* Type checking. */
  85.         InptPrsrFreeTree(PTree);             /* Not needed any more. */
  86.         FlushToEndOfExpr(TRUE);/* Close all include files, & flush stdin.*/
  87.         return FALSE;
  88.     }
  89.  
  90.     InptPrsrEvalTree(PTree, 0);                 /* Evaluate it. */
  91.     if (IPGlblEvalError != 0) {
  92.         FlushToEndOfExpr(TRUE); /* Close include files, and flush stdin. */
  93.         return FALSE;
  94.     }
  95.     }
  96.     else {
  97.     FlushToEndOfExpr(TRUE); /* Close all include files, and flush stdin. */
  98.     return FALSE;
  99.     }
  100.  
  101.     InptPrsrFreeTree(PTree);                 /* Not needed any more. */
  102.  
  103.     return !(IPGlblParseError || IPGlblEvalError);
  104. }
  105.  
  106. /*****************************************************************************
  107. *   Routine to convert the expression from stream f into a binary tree.      *
  108. * Algorithm: Using operator precedence with the following grammer:           *
  109. * EXPR    ::= EXPR    |  EXPR + EXPR    |  EXPR - EXPR                       *
  110. * EXPR    ::= EXPR    |  EXPR * EXPR    |  EXPR / EXPR                       *
  111. * EXPR    ::= EXPR    |  EXPR ^ EXPR                                         *
  112. * EXPR    ::= EXPR    |  EXPR , EXPR    |  EXPR = EXPR                       *
  113. * EXPR    ::= NUMBER  |  -EXPR          |  (EXPR)        |  FUNCTION         *
  114. * FUCTION ::= FUNC(EXPR , EXPR , ...)                         *
  115. * Where FUNC might be function like arithmetics (SIN, COS etc.).         *
  116. * Note that FUNC might have more than one operand, seperated by ','.         *
  117. *                                                                            *
  118. * Note the stream is terminated by semicolon character ';'.             *
  119. *                                                                            *
  120. * Left associativity for +, -, *, /, ^.                                      *
  121. * Precedence of operators is as usual:                                       *
  122. *     <Highest> {unar minus}   {^}   {*, /}   {+, -} <Lowest>             *
  123. *                                                                            *
  124. * Returns NULL if an error was found, and error is in IPGlblParseError       *
  125. *****************************************************************************/
  126. static ParseTree *GenInputParseTree(void)
  127. {
  128.     ParseTree *Root;
  129.     int i;
  130.  
  131.     IPGlblILastToken = 0;    /* Used to hold last token read from stream. */
  132.     IPGlblParseError = 0;                 /* No errors so far ... */
  133.  
  134.     Root = OperatorPrecedence();
  135.  
  136.     if (IPGlblParseError) {
  137.     for (i=0; i<=ParserStackPointer; i++)/* Free partialy allocated tree.*/
  138.         InptPrsrFreeTree(Stack[i]);
  139.     return (ParseTree *) NULL;                  /* Error ! */
  140.     }
  141.     else return Root;
  142. }
  143.  
  144. /*****************************************************************************
  145. *  Routine to allocate new ParseTree expression node:                 *
  146. *****************************************************************************/
  147. ParseTree *MyExprMalloc(void)
  148. {
  149.     ParseTree *p;
  150.  
  151.     p = (ParseTree *) MyMalloc(sizeof(ParseTree), OTHER_TYPE);
  152.     p -> Right = p -> Left = NULL;
  153.     return p;
  154. }
  155.  
  156. /*****************************************************************************
  157. *  Routine to free one expression node:                         *
  158. *****************************************************************************/
  159. void MyExprFree(ParseTree *Ptr)
  160. {
  161.     MyFree((char *) Ptr, OTHER_TYPE);
  162. }
  163.  
  164. /*****************************************************************************
  165. *   Routine to actually parse using operator precedence:                     *
  166. * Few Notes:                                                                 *
  167. * 1. Parse the input with the help of GetToken routine. Input is redirected  *
  168. *    using the FileStack.                             *
  169. * 2. All tokens must be in the range of 0..999 as we use the numbers above   *
  170. *    it (adding 1000) to deactivate them in the handle searching (i.e. when  *
  171. *    they were reduced to sub.-expression).                                  *
  172. * 3. Returns NULL pointer in case of an error (see Expr2TrG.h for errors     *
  173. * 4. See "Compilers - principles, techniques and tools" by Aho, Sethi &      *
  174. *    Ullman,   pages 207-210.                                                *
  175. *****************************************************************************/
  176. static ParseTree *OperatorPrecedence(void)
  177. {
  178.     int Token, LowHandle, Temp1, Temp2;
  179.     RealType Data;
  180.  
  181. #   ifdef DEBUG
  182.     MaxStackPointer = 0;
  183. #   endif /* DEBUG */
  184.  
  185.     ParserStackPointer = 0;
  186.  
  187.     /* Push the start symbol on stack (node pointer points on tos): */
  188.     Stack[ParserStackPointer] = MyExprMalloc();
  189.     Stack[ParserStackPointer] -> NodeKind = TOKENSTART;
  190.     Stack[ParserStackPointer] -> Right =
  191.     Stack[ParserStackPointer] -> Left = NULL;
  192.  
  193.     Token = GetToken(&Data);      /* Get one look ahead token to start with. */
  194.  
  195.     do {
  196.         if (IPGlblParseError) return NULL;
  197.  
  198.         Temp1 = ParserStackPointer;       /* Find top active token (<1000). */
  199.         while (Stack[Temp1] -> NodeKind >= 1000) Temp1--;
  200.         /* Now test to see if the new token completes an handle: */
  201.         if (TestPreceeding(Stack[Temp1] -> NodeKind, Token)) {
  202.             switch (Token) {
  203.         case CLOSPARA:
  204.                     if (Stack[Temp1] -> NodeKind == OPENPARA) {
  205.             MyExprFree(Stack[Temp1]);     /* Free open paran. */
  206.             /* If a parameter is introduced instead of function  */
  207.             /* it will be reduced already against "(" and it     */
  208.             /* probably was missspelled function...             */
  209.                         if (Stack[Temp1-1] -> NodeKind == PARAMETER+1000) {
  210.                 strcpy(IPGlblCharData,
  211.                     Stack[Temp1-1] -> U.PObj -> Name);
  212.                 IPGlblParseError = IP_ERR_UndefFunc;
  213.                 return NULL;
  214.             }
  215.  
  216.                         switch (Stack[Temp1-1] -> NodeKind) {
  217.                 case ARCCOS:  /* If it is of the form Func(Expr) */
  218.                 case ARCSIN:  /* Then reduce it directly to that */
  219.                 case ARCTAN2: /* function, else (default) reduce */
  220.                 case ARCTAN:  /* to sub-expression.              */
  221.                 case COS:
  222.                 case EXP:
  223.                 case FABS:
  224.                 case LN:
  225.                 case LOG:
  226.                 case SIN:
  227.                 case SQRT:
  228.                 case TAN:
  229.                 case CPOLY:
  230.                 case AREA:
  231.                 case VOLUME:
  232.                 case TIME:
  233.  
  234.                 case VECTOR:
  235.                 case ROTX:
  236.                 case ROTY:
  237.                 case ROTZ:
  238.                 case TRANS:
  239.                 case SCALE:
  240.                 case BOX:
  241.                 case GBOX:
  242.                 case CONE:
  243.                 case CYLIN:
  244.                 case SPHERE:
  245.                 case TORUS:
  246.                 case PLANE:
  247.                 case POLY:
  248.                 case CROSSEC:
  249.                 case SURFREV:
  250.                 case EXTRUDE:
  251.                 case LIST:
  252.                 case LOAD:
  253.                 case CONVEX:
  254.  
  255.                 case VIEW:
  256.                 case DIR:
  257.                 case CHDIR:
  258.                 case NORMAL:
  259.                 case INCLUDE:
  260.                 case GDUMP:
  261.                 case MDUMP:
  262.                 case FREEOBJ:
  263.                 case INTERACT:
  264.                 case IFCOND:
  265.                 case FORLOOP:
  266.                 case PRHELP:
  267.                 case PAUSE:
  268.                 case ALIAS:
  269.                 case BEEP:
  270.                 case EDIT:
  271.                 case LOGFILE:
  272.                 case COLOR:
  273.  
  274.                 if (ParserStackPointer-Temp1 != 1) {
  275.                     UpdateCharError("",
  276.                     Stack[Temp1-1] -> NodeKind);
  277.                     IPGlblParseError = IP_ERR_ParamFunc;
  278.                     return NULL;
  279.                 }
  280.                 Stack[ParserStackPointer] -> NodeKind -= 1000;
  281.                 Stack[Temp1-1] -> NodeKind += 1000;
  282.                     Stack[Temp1-1] -> Right =
  283.                         Stack[ParserStackPointer];
  284.                 ParserStackPointer -= 2;
  285.                 break;
  286.  
  287.                 case EXIT:   /* Special case of non param. func. */
  288.                 case VARLIST:
  289.                 case SYSTEM:
  290.  
  291.                 if (ParserStackPointer-Temp1 == 1) {
  292.                     UpdateCharError("",
  293.                     Stack[Temp1-1] -> NodeKind);
  294.                     IPGlblParseError = IP_ERR_NoParamFunc;
  295.                     return NULL;
  296.                 }
  297.                 Stack[Temp1-1] -> NodeKind += 1000;
  298.                     Stack[Temp1-1] -> Right = NULL;
  299.                 ParserStackPointer --;
  300.                 break;
  301.  
  302.                 default:
  303.                 if (ParserStackPointer-Temp1 != 1) {
  304.                     IPGlblParseError = IP_ERR_ParaMatch;
  305.                     return NULL;
  306.                 }
  307.                                 Stack[Temp1] = Stack[ParserStackPointer--];
  308.                 break;
  309.             }
  310.                         Token = GetToken(&Data);       /* Get another token. */
  311.                         continue;
  312.             }
  313.             else if (Stack[Temp1] -> NodeKind == TOKENSTART) {
  314.             /* No match for this one! */
  315.                         IPGlblParseError = IP_ERR_ParaMatch;
  316.             return NULL;
  317.             }
  318.             break;
  319.                 case TOKENEND:
  320.                     if (Stack[Temp1] -> NodeKind == TOKENSTART) {
  321.                         if (ParserStackPointer != 1) {
  322.                             IPGlblParseError = IP_ERR_WrongSyntax;
  323.                 return NULL;
  324.             }
  325.             InptPrsrFreeTree(Stack[Temp1]);      /* The TOKENSTART. */
  326.             Stack[1] -> NodeKind -= 1000;
  327.             return Stack[1];
  328.             }
  329.         }
  330.  
  331.             Temp2 = Temp1-1;          /* Find the lower bound of handle. */
  332.             while (Stack[Temp2] -> NodeKind >= 1000) Temp2--;
  333.             LowHandle = Temp2 + 1;
  334.             if (LowHandle < 1) {                  /* No low bound was found. */
  335.                 IPGlblParseError = IP_ERR_WrongSyntax;
  336.             return NULL;             /* We ignore data till now. */
  337.             }
  338.         switch (ParserStackPointer - LowHandle + 1) {
  339.         case 1: /* Its a scalar one - mark it as used (add 1000). */
  340.             switch (Stack[ParserStackPointer] -> NodeKind) {
  341.             case NUMBER:
  342.             case PARAMETER:
  343.             case STRING:
  344.                     Stack[ParserStackPointer] -> NodeKind += 1000;
  345.                 break;
  346.             default:
  347.                 UpdateCharError("Found ",
  348.                 Stack[ParserStackPointer] -> NodeKind);
  349.                 IPGlblParseError = IP_ERR_ParamExpect;
  350.                 return NULL;
  351.             }
  352.             break;
  353.         case 2: /* Its a monadic operator - create the subtree. */
  354.             switch (Stack[ParserStackPointer-1] -> NodeKind) {
  355.                 case UNARMINUS:
  356.                     Stack[ParserStackPointer-1] -> Right =
  357.                         Stack[ParserStackPointer];
  358.                     Stack[ParserStackPointer] -> NodeKind -= 1000;
  359.                     Stack[ParserStackPointer-1] -> NodeKind += 1000;
  360.                     ParserStackPointer--;
  361.                     break;
  362.                 case OPENPARA:
  363.                 IPGlblParseError = IP_ERR_ParaMatch;
  364.                 return NULL;
  365.                 default:
  366.                 UpdateCharError("Found Operator ",
  367.                 Stack[ParserStackPointer-1] -> NodeKind);
  368.                 IPGlblParseError = IP_ERR_OneOperand;
  369.                 return NULL;
  370.             }
  371.             break;
  372.         case 3: /* Its a diadic operator - create the subtree. */
  373.             switch (Stack[ParserStackPointer-1] -> NodeKind) {
  374.                 case PLUS:
  375.                 case MINUS:
  376.                 case MULT:
  377.                 case DIV:
  378.                 case POWER:
  379.                 case COMMA:
  380.                 case EQUAL:
  381.                 case COLON:
  382.                     Stack[ParserStackPointer-1] -> Right =
  383.                                   Stack[ParserStackPointer];
  384.                             Stack[ParserStackPointer-1] -> Left =
  385.                                   Stack[ParserStackPointer-2];
  386.                     Stack[ParserStackPointer-2] -> NodeKind -= 1000;
  387.                     Stack[ParserStackPointer] -> NodeKind -= 1000;
  388.                     Stack[ParserStackPointer-1] -> NodeKind += 1000;
  389.                     Stack[ParserStackPointer-2] =
  390.                         Stack[ParserStackPointer-1];
  391.                     ParserStackPointer -= 2;
  392.                             break;
  393.                         default:
  394.                 UpdateCharError("Found Operator ",
  395.                 Stack[ParserStackPointer-1] -> NodeKind);
  396.                 IPGlblParseError = IP_ERR_TwoOperand;
  397.                 return NULL;
  398.             }
  399.             break;
  400.         default:
  401.             IPGlblParseError = IP_ERR_WrongSyntax;
  402.             return NULL;
  403.         }
  404.         }
  405.         else {         /* Push that token on stack - it is not handle yet. */
  406.         Stack[++ParserStackPointer] = MyExprMalloc();
  407.  
  408. #        ifdef DEBUG
  409.         if (MaxStackPointer < ParserStackPointer)
  410.             MaxStackPointer = ParserStackPointer;
  411. #        endif /* DEBUG */
  412.  
  413.             if (ParserStackPointer == MAX_PARSER_STACK-1) {
  414.                 IPGlblParseError = IP_ERR_StackOV;
  415.         return NULL;             /* We ignore data till now. */
  416.         }
  417.             Stack[ParserStackPointer] -> NodeKind = Token;
  418.             Stack[ParserStackPointer] -> U.R = Data;  /* We might need that. */
  419.         Stack[ParserStackPointer] -> Right =
  420.         Stack[ParserStackPointer] -> Left = (ParseTree *) NULL;
  421.         if (Token == PARAMETER) {
  422.         if ((Stack[ParserStackPointer] -> U.PObj =
  423.                 GetObject(IPGlblCharData)) == NULL) {
  424.             /* Its new one - allocate memory for it. */
  425.             Stack[ParserStackPointer] -> U.PObj =
  426.             AllocObject(IPGlblCharData, UNDEF_OBJ, NULL);
  427.         }
  428.         }
  429.         else
  430.         if (Token == STRING) {
  431.         Stack[ParserStackPointer] -> U.PObj =
  432.             AllocObject("", STRING_OBJ, NULL);
  433.         strcpy(Stack[ParserStackPointer] -> U.PObj -> U.Str,
  434.                IPGlblCharData);
  435.         }
  436.             Token = GetToken(&Data);       /* And get new token from stream. */
  437.     }
  438.     }
  439.     while (TRUE);
  440. }
  441.  
  442. /*****************************************************************************
  443. *   Routine to test precedence of two tokens. returns 0, <0 or >0 according  *
  444. * to comparison results:                                                     *
  445. *****************************************************************************/
  446. static int TestPreceeding(int Token1, int Token2)
  447. {
  448.     int Preced1, Preced2;
  449.  
  450.     if ((Token1 >= 1000) || (Token2 >= 1000))
  451.     return FALSE;                     /* Ignore sub-expr. */
  452.  
  453.     switch (Token1) {
  454.     case ARCCOS:
  455.     case ARCSIN:
  456.     case ARCTAN:
  457.     case ARCTAN2:
  458.     case COS:
  459.     case EXP:
  460.     case FABS:
  461.     case LN:
  462.     case LOG:
  463.     case SIN:
  464.     case SQRT:
  465.     case TAN:
  466.     case CPOLY:
  467.     case AREA:
  468.     case VOLUME:
  469.     case TIME:
  470.  
  471.     case VECTOR:
  472.     case ROTX:
  473.     case ROTY:
  474.     case ROTZ:
  475.     case TRANS:
  476.     case SCALE:
  477.     case BOX:
  478.     case GBOX:
  479.     case CONE:
  480.     case CYLIN:
  481.     case SPHERE:
  482.     case TORUS:
  483.     case PLANE:
  484.     case POLY:
  485.     case CROSSEC:
  486.     case SURFREV:
  487.     case EXTRUDE:
  488.     case LIST:
  489.     case LOAD:
  490.     case CONVEX:
  491.  
  492.     case EXIT:
  493.     case VIEW:
  494.     case DIR:
  495.     case CHDIR:
  496.     case NORMAL:
  497.     case INCLUDE:
  498.     case GDUMP:
  499.     case MDUMP:
  500.     case FREEOBJ:
  501.     case INTERACT:
  502.     case PAUSE:
  503.     case IFCOND:
  504.     case FORLOOP:
  505.     case PRHELP:
  506.     case VARLIST:
  507.     case ALIAS:
  508.     case BEEP:
  509.     case EDIT:
  510.     case SYSTEM:
  511.     case LOGFILE:
  512.     case COLOR:
  513.         Preced1 =130; break;
  514.     case COMMA:
  515.     case COLON:
  516.     case EQUAL:
  517.         Preced1 = 30; break;
  518.     case NUMBER:
  519.     case PARAMETER:
  520.     case STRING:
  521.         Preced1 =150; break;
  522.     case PLUS:
  523.     case MINUS:
  524.         Preced1 = 50; break;
  525.     case MULT:
  526.     case DIV:
  527.         Preced1 = 70; break;
  528.     case POWER:
  529.         Preced1 = 90; break;
  530.     case UNARMINUS:
  531.         Preced1 = 95; break;
  532.     case OPENPARA:
  533.         Preced1 = 20; break;
  534.     case CLOSPARA:
  535.         Preced1 =150; break;
  536.     case TOKENSTART:
  537.     case TOKENEND:
  538.         Preced1 = 10; break;
  539.     }
  540.  
  541.     switch (Token2) {
  542.     case ARCCOS:
  543.     case ARCSIN:
  544.     case ARCTAN:
  545.     case ARCTAN2:
  546.     case COS:
  547.     case EXP:
  548.     case FABS:
  549.     case LN:
  550.     case LOG:
  551.     case SIN:
  552.     case SQRT:
  553.     case TAN:
  554.     case CPOLY:
  555.     case AREA:
  556.     case VOLUME:
  557.     case TIME:
  558.  
  559.     case VECTOR:
  560.     case ROTX:
  561.     case ROTY:
  562.     case ROTZ:
  563.     case TRANS:
  564.     case SCALE:
  565.     case BOX:
  566.     case GBOX:
  567.     case CONE:
  568.     case CYLIN:
  569.     case SPHERE:
  570.     case TORUS:
  571.     case PLANE:
  572.     case POLY:
  573.     case CROSSEC:
  574.     case SURFREV:
  575.     case EXTRUDE:
  576.     case LIST:
  577.     case LOAD:
  578.     case CONVEX:
  579.  
  580.     case EXIT:
  581.     case VIEW:
  582.     case DIR:
  583.     case CHDIR:
  584.     case NORMAL:
  585.     case INCLUDE:
  586.     case GDUMP:
  587.     case MDUMP:
  588.     case FREEOBJ:
  589.     case INTERACT:
  590.     case PAUSE:
  591.     case IFCOND:
  592.     case FORLOOP:
  593.     case PRHELP:
  594.     case VARLIST:
  595.     case ALIAS:
  596.     case BEEP:
  597.     case EDIT:
  598.     case SYSTEM:
  599.     case LOGFILE:
  600.     case COLOR:
  601.         Preced2 =120; break;
  602.     case COMMA:
  603.     case COLON:
  604.     case EQUAL:
  605.         Preced2 = 35; break;
  606.     case NUMBER:
  607.     case PARAMETER:
  608.     case STRING:
  609.         Preced2 =140; break;
  610.     case PLUS:
  611.     case MINUS:
  612.         Preced2 = 40; break;
  613.     case MULT:
  614.     case DIV:
  615.         Preced2 = 60; break;
  616.     case POWER:
  617.         Preced2 = 80; break;
  618.     case UNARMINUS:
  619.         Preced2 =100; break;
  620.     case OPENPARA:
  621.         Preced2 =140; break;
  622.     case CLOSPARA:
  623.         Preced2 = 15; break;
  624.     case TOKENSTART:
  625.     case TOKENEND:
  626.         Preced2 =  0; break;
  627.     }
  628.  
  629.     return Preced1-Preced2 > 0;
  630. }
  631.  
  632. /*****************************************************************************
  633. *  Routine to update the character error message according to StrMsg & Token *
  634. *****************************************************************************/
  635. void UpdateCharError(char *StrMsg, int Token)
  636. {
  637.     char *TokenChar = NULL;
  638.  
  639.     if (Token > 1000) Token -= 1000;
  640.  
  641.     switch (Token) {
  642.     case ARCSIN:
  643.     case ARCCOS:
  644.     case ARCTAN:
  645.     case ARCTAN2:
  646.     case COS:
  647.     case EXP:
  648.     case FABS:
  649.     case LN:
  650.     case LOG:
  651.     case SIN:
  652.     case SQRT:
  653.     case TAN:
  654.     case CPOLY:
  655.     case AREA:
  656.     case VOLUME:
  657.     case TIME:
  658.             TokenChar = NumFuncTable[Token-NUM_FUNC_OFFSET].FuncName;
  659.         break;
  660.     case VECTOR:
  661.     case ROTX:
  662.     case ROTY:
  663.     case ROTZ:
  664.     case TRANS:
  665.     case SCALE:
  666.     case BOX:
  667.     case GBOX:
  668.     case CONE:
  669.     case CYLIN:
  670.     case SPHERE:
  671.     case TORUS:
  672.     case PLANE:
  673.     case POLY:
  674.     case CROSSEC:
  675.     case SURFREV:
  676.     case EXTRUDE:
  677.     case LIST:
  678.     case LOAD:
  679.     case CONVEX:
  680.             TokenChar = ObjFuncTable[Token-OBJ_FUNC_OFFSET].FuncName;
  681.             break;
  682.     case EXIT:
  683.     case VIEW:
  684.         case DIR:
  685.     case CHDIR:
  686.     case NORMAL:
  687.     case INCLUDE:
  688.     case GDUMP:
  689.     case MDUMP:
  690.     case FREEOBJ:
  691.     case INTERACT:
  692.     case PAUSE:
  693.     case IFCOND:
  694.     case FORLOOP:
  695.     case PRHELP:
  696.     case VARLIST:
  697.     case ALIAS:
  698.     case BEEP:
  699.     case EDIT:
  700.     case SYSTEM:
  701.     case LOGFILE:
  702.     case COLOR:
  703.             TokenChar = GenFuncTable[Token-GEN_FUNC_OFFSET].FuncName;
  704.             break;
  705.     case PLUS:
  706.             TokenChar = "+";
  707.             break;
  708.     case MINUS:
  709.             TokenChar = "-";
  710.             break;
  711.     case MULT:
  712.             TokenChar = "*";
  713.             break;
  714.     case DIV:
  715.             TokenChar = "/";
  716.             break;
  717.     case POWER:
  718.             TokenChar = "^";
  719.         break;
  720.     case UNARMINUS:
  721.             TokenChar = "(Unar) -";
  722.         break;
  723.     case COMMA:
  724.         TokenChar = ",";
  725.         break;
  726.     case EQUAL:
  727.             TokenChar = "=";
  728.         break;
  729.     case COLON:
  730.             TokenChar = ":";
  731.         break;
  732.     default:
  733.             sprintf(IPGlblCharData, "%s - Token %d\n", StrMsg, Token);
  734.             return;
  735.     }
  736.     sprintf(IPGlblCharData, "%s%s", StrMsg, TokenChar);
  737. }
  738.  
  739. /*****************************************************************************
  740. *   Routine to get the next token out of the expression.                     *
  741. * Gets the expression in S, and current position in i.                       *
  742. * Returns the next token found, set data to the returned value (if any),     *
  743. * and update i to one char ofter the new token found.                        *
  744. *   Note that in minus sign case, it is determined whether it is monadic or  *
  745. * diadic minus by the last token - if the last token was operator or '('     *
  746. * it is monadic minus.                                                       *
  747. *****************************************************************************/
  748. static int GetToken(RealType *Data)
  749. {
  750.     int i, RetVal = 0;
  751.     char c;
  752.  
  753.     while (isspace(c = InptPrsrGetC()));           /* Skip white blanks. */
  754.  
  755.     if (c == '"') {          /* Its a string token - read up to next ". */
  756.     i = 0;
  757.     while ((IPGlblCharData[i] = InptPrsrGetC()) != '"') {
  758.         if (IPGlblCharData[i] == '\\') /* Its escape char. for next one: */
  759.         IPGlblCharData[i] = InptPrsrGetC();
  760.         i++;
  761.     }
  762.     IPGlblCharData[i] = 0;
  763.     RetVal = STRING;
  764.     }
  765.     else if (isalpha(c)) {          /* Is it a variable/function name? */
  766.     if (islower(c)) IPGlblCharData[i=0] = toupper(c);
  767.     else IPGlblCharData[i=0] = c;
  768.  
  769.     while (isalpha(c = InptPrsrGetC()) || isdigit(c) || c == '_')
  770.         if (islower(c)) IPGlblCharData[++i] = toupper(c);
  771.         else IPGlblCharData[++i] = c;
  772.     IPGlblCharData[++i] = 0;
  773.     InptPrsrUnGetC(c);
  774.  
  775.     if (strlen(IPGlblCharData) >= OBJ_NAME_LEN) {
  776.         RetVal = TOKENERROR;
  777.         IPGlblParseError = IP_ERR_NameTooLong;
  778.     }
  779.     else {
  780.         RetVal = GetVarFuncToken(IPGlblCharData, Data);
  781.     }
  782.     }
  783.     else if (isdigit(c) || (c == '.')) {          /* Is it numeric data? */
  784.     IPGlblCharData[i=0] = c;
  785.  
  786.     while (isdigit(c = InptPrsrGetC()) || (c == '.') ||
  787.                     (c == 'e') || (c == 'E') || (c == 'e'))
  788.         IPGlblCharData[++i] = c;
  789.     /* Handle the special case of negative exponent ("111.111E-22"). */
  790.     if (c == '-' && (IPGlblCharData[i] == 'e' ||
  791.              IPGlblCharData[i] == 'E')) {
  792.         IPGlblCharData[++i] = c;
  793.         while (isdigit(c = InptPrsrGetC()) || (c == '.'))
  794.         IPGlblCharData[++i] = c;
  795.     }
  796.     IPGlblCharData[++i] = 0;
  797.  
  798.     InptPrsrUnGetC(c);
  799.  
  800. #    ifdef DOUBLE
  801.         sscanf(IPGlblCharData, "%lf", Data);
  802. #    else
  803.         sscanf(IPGlblCharData, "%f", Data);
  804. #    endif /* DOUBLE */
  805.  
  806.         RetVal = NUMBER;
  807.     }
  808.     else switch (c) {
  809.     case '+': RetVal = PLUS; break;
  810.     case '-': switch (IPGlblILastToken) {
  811.                case 0:          /* If first token (no last token yet). */
  812.                case PLUS:
  813.                case MINUS:
  814.                case MULT:
  815.                case DIV:
  816.                case POWER:
  817.                case COMMA:
  818.                case EQUAL:
  819.                case COLON:
  820.                case UNARMINUS:
  821.                case OPENPARA:
  822.                RetVal = UNARMINUS; break;
  823.                default:
  824.                            RetVal = MINUS; break;
  825.           }
  826.           break;
  827.     case '*': RetVal = MULT; break;
  828.     case '/': RetVal = DIV; break;
  829.     case '^': RetVal = POWER; break;;
  830.     case '(': RetVal = OPENPARA; break;
  831.     case ')': RetVal = CLOSPARA; break;
  832.     case '=': RetVal = EQUAL; break;
  833.     case ',': RetVal = COMMA; break;
  834.     case ':': RetVal = COLON; break;
  835.     case ';': RetVal = TOKENEND; break;           /* End of expression! */
  836.     default:
  837.         RetVal = TOKENERROR;
  838.         IPGlblParseError = IP_ERR_UndefToken;
  839.             break;
  840.     }
  841.  
  842.     IPGlblILastToken = RetVal;
  843.  
  844.     return RetVal;
  845. }
  846.  
  847. /*****************************************************************************
  848. *   Routine to test alpha Token for match with one of the defined functions  *
  849. * and returns that Token function if found one. otherwise it is assumed to   *
  850. * be a variable (new or old).                             *
  851. * Note that although the search is linear, It is extremely fast to add new   *
  852. * functions - simply add its token, its entry here, and in the parser itself.*
  853. *****************************************************************************/
  854. static int GetVarFuncToken(char *Token, RealType *Data)
  855. {
  856.     int i;
  857.     char c;
  858.  
  859.     if (strcmp("COMMENT", Token) == 0) {
  860.     /* Get first nonspace char after the COMMENT key word: */
  861.     while (isspace(c = InptPrsrGetC()));
  862.     /* And read the input until this char appear again (end of comment): */
  863.     while (c != InptPrsrGetC());
  864.  
  865.     return GetToken(Data);               /* Return next token instead. */
  866.     }
  867.  
  868.     for (i=0; i<NumFuncTableSize; i++)         /* Is it Numeric function ? */
  869.     if (strcmp(NumFuncTable[i].FuncName, Token) == 0)
  870.         return(NumFuncTable[i].FuncToken);
  871.     for (i=0; i<ObjFuncTableSize; i++)          /* Is it Object function ? */
  872.     if (strcmp(ObjFuncTable[i].FuncName, Token) == 0)
  873.         return(ObjFuncTable[i].FuncToken);
  874.     for (i=0; i<GenFuncTableSize; i++)         /* Is it General function ? */
  875.     if (strcmp(GenFuncTable[i].FuncName, Token) == 0)
  876.         return(GenFuncTable[i].FuncToken);
  877.  
  878.     for (i=0; i<ConstantTableSize; i++)       /* Replace constant by its value. */
  879.     if (strcmp(ConstantTable[i].FuncName, Token) == 0) {
  880.         sprintf(Token, "%lg", ConstantTable[i].Value);
  881.         *Data = ConstantTable[i].Value;
  882.         return NUMBER;
  883.     }
  884.  
  885.     return PARAMETER;   /* If not a function - it is assumed to be variable. */
  886. }
  887.  
  888. /*****************************************************************************
  889. *   Routine to reset the aliases buffer to a known state.             *
  890. *****************************************************************************/
  891. void AliasReset(void)
  892. {
  893.     int i;
  894.  
  895.     for (i=0; i<NUM_OF_ALIASES; i++)
  896.     GlobalAliasList.Aliases[i].Name = NULL;
  897. }
  898.  
  899. /*****************************************************************************
  900. *   Routine to update (insert, delete or print) the global alias list         *
  901. *****************************************************************************/
  902. void AliasEdit(char *Name, char *Value)
  903. {
  904.     int i;
  905.     char s[LINE_LEN];
  906.  
  907.     if (strlen(Name) == 0) {            /* Print all defined alias list. */
  908.     WndwInputWindowPutStrFS("Alias List:", RED, TRUE);
  909.     for (i=0; i<NUM_OF_ALIASES; i++)
  910.         if (GlobalAliasList.Aliases[i].Name != NULL) {
  911.         sprintf(s, "Alias \"%s\" - \"%s\"",
  912.             GlobalAliasList.Aliases[i].Name,
  913.             GlobalAliasList.Aliases[i].Value);
  914.         WndwInputWindowPutStrFS(s, RED, FALSE);
  915.         }
  916.     return;
  917.     }
  918.  
  919.     if (strlen(Value) == 0) {              /* Its a delete operation. */
  920.     for (i=0; i<NUM_OF_ALIASES; i++)
  921.         if (stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break;
  922.     if (i<NUM_OF_ALIASES) {         /* Found alias to delete, so delete it. */
  923.         MyFree(GlobalAliasList.Aliases[i].Name, OTHER_TYPE);
  924.         MyFree(GlobalAliasList.Aliases[i].Value, OTHER_TYPE);
  925.         GlobalAliasList.Aliases[i].Name = NULL;
  926.     }
  927.     else WndwInputWindowPutStr("Alias not found, ignored", RED);
  928.     }
  929.     else {/* Its an insert operation - test for old one, otherwise make new. */
  930.     for (i=0; i<NUM_OF_ALIASES; i++)
  931.         if (stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break;
  932.     if (i<NUM_OF_ALIASES) {         /* Found alias to replace - replace it. */
  933.         MyFree(GlobalAliasList.Aliases[i].Value, OTHER_TYPE);
  934.         GlobalAliasList.Aliases[i].Value =
  935.         MyMalloc(strlen(Value) + 1, OTHER_TYPE);
  936.         strcpy(GlobalAliasList.Aliases[i].Value, Value);
  937.     }
  938.     else {               /* Find empty slot and insert as new one. */
  939.         for (i=0; i<NUM_OF_ALIASES; i++)
  940.         if (GlobalAliasList.Aliases[i].Name == NULL) break;
  941.         if (i<NUM_OF_ALIASES) {           /* Found empty slot - use it. */
  942.         GlobalAliasList.Aliases[i].Name =
  943.             MyMalloc(strlen(Name) + 1, OTHER_TYPE);
  944.         strcpy(GlobalAliasList.Aliases[i].Name, Name);
  945.         GlobalAliasList.Aliases[i].Value =
  946.             MyMalloc(strlen(Value) + 1, OTHER_TYPE);
  947.         strcpy(GlobalAliasList.Aliases[i].Value, Value);
  948.         }
  949.         else WndwInputWindowPutStr("Aliases buffer full, ignored", RED);
  950.     }
  951.     }
  952. }
  953.  
  954. /*****************************************************************************
  955. *   Routine to expand aliases of the given line using the global defined     *
  956. * alias list as saved in GlobalAliasList.                     *
  957. *****************************************************************************/
  958. void AliasExpand(char *Line)
  959. {
  960.     int i, j, OldSize, NewSize, DiffSize, Count = 0;
  961.     char *alias;
  962.  
  963.     for (i=0; i<NUM_OF_ALIASES; i++)
  964.     if (GlobalAliasList.Aliases[i].Name != NULL) {
  965.         do {
  966.         if ((alias = strstr(Line, GlobalAliasList.Aliases[i].Name)) !=
  967.             NULL) {
  968.             if (Count++ > 100) {
  969.             WndwInputWindowPutStr("Alias expansion - 100 expansions reached created, aborted:",
  970.                 RED);
  971.             return;
  972.             }
  973.             OldSize = strlen(GlobalAliasList.Aliases[i].Name);
  974.             NewSize = strlen(GlobalAliasList.Aliases[i].Value);
  975.             DiffSize = NewSize - OldSize;
  976.             if (DiffSize + strlen(Line) > LINE_LEN_LONG - 1) {
  977.             WndwInputWindowPutStr("Alias expansion created too long line, aborted:",
  978.                 RED);
  979.             return;
  980.             }
  981.             /* Expand/shrink line space according to Name/Value sizes*/
  982.             if (DiffSize != 0)
  983.             if (NewSize > OldSize) {
  984.                 movmem(alias, &alias[DiffSize], strlen(alias) + 1);
  985.             }
  986.             else {
  987.                 movmem(&alias[-DiffSize], alias,
  988.                 strlen(&alias[-DiffSize]) + 1);
  989.             }
  990.             /* And copy the Value instead of name into line: */
  991.             for (j=0; j<NewSize; j++) alias[j] =
  992.             GlobalAliasList.Aliases[i].Value[j];
  993.         }
  994.         }
  995.         while (alias != NULL);
  996.     }
  997.  
  998. }
  999.  
  1000. static char UnGetChar;
  1001.  
  1002. /*****************************************************************************
  1003. *   Routine to control all getchar in this module and echo it if requested   *
  1004. * Note it handles the FileStack and decrease it if end of file was found.    *
  1005. *****************************************************************************/
  1006. static char InptPrsrGetC(void)
  1007. {
  1008.     static char Line[LINE_LEN_LONG] = "";
  1009.     static int LineLength = 0, LineCount = 0;
  1010.     char c;
  1011. #ifndef __MSDOS__
  1012.     int i;
  1013. #endif /* __MSDOS__ */
  1014.  
  1015.     if (UnGetChar == 0) {               /* One level of unget char... */
  1016.     if (LineCount < LineLength) {     /* Is there anything in local Line? */
  1017.     }
  1018.     else do {
  1019.         if (FileStackPtr == 0) {
  1020.         WndwInputWindowGetStr(Line, LINE_LEN_LONG);
  1021.         LineCount = 0;
  1022.         }
  1023.         else {
  1024.         sprintf(Line, "%s > ", FileStack[FileStackPtr-1].Name);
  1025.         LineCount = strlen(Line);
  1026.         if (fgets(&Line[LineCount], LINE_LEN_LONG-20,
  1027.                     FileStack[FileStackPtr-1].f) == NULL) {
  1028.             /* Its end of file - close it and update stack. */
  1029.             Line[0] = 0;
  1030.             fclose(FileStack[--FileStackPtr].f);
  1031.         }
  1032.         }
  1033.         AliasExpand(&Line[LineCount]);    /* Expand the aliases in line. */
  1034.  
  1035.         /* Line len. changes by Wndw routine - strip off CR/LF/TAB.      */
  1036. #ifdef __MSDOS__
  1037.         WndwInputWindowPutStr(Line, NO_COLOR);
  1038. #else
  1039.         if (FileStackPtr != 0)         /* Input was from keyboard? */
  1040.         WndwInputWindowPutStr(Line, NO_COLOR);
  1041.         {
  1042.         for (i=0; i<strlen(Line); i++)
  1043.             if (Line[i] == TAB) Line[i] = ' ';    /* Strip off tabs... */
  1044.         for (i=strlen(Line); isspace(Line[--i]););   /* Strip CR/LF. */
  1045.         Line[i+1] = 0;
  1046.         }
  1047. #endif /* __MSDOS__ */
  1048.         LineLength = strlen(Line);            
  1049.     } while (LineCount >= LineLength);
  1050.     c = Line[LineCount++];
  1051.     if (c == '#') {              /* Its a comment - skip that line. */
  1052.             c = ' ';                   /* Must return something. */
  1053.             LineCount = LineLength;    /* Force next time to fetch new line. */
  1054.     }
  1055. #    ifdef DEBUG
  1056.         fprintf(stderr, "%c", c);
  1057. #    endif /* DEBUG */
  1058.     }
  1059.     else {
  1060.     c = UnGetChar;
  1061.     UnGetChar = 0;
  1062.     }
  1063.  
  1064.     return c;
  1065. }
  1066.  
  1067. /*****************************************************************************
  1068. *   Routine to unget one char                             *
  1069. *****************************************************************************/
  1070. static void InptPrsrUnGetC(char c)
  1071. {
  1072.     UnGetChar = c;
  1073. }
  1074.  
  1075. /*****************************************************************************
  1076. *   Routine to read data up to the next end of expression marker - ';'.         *
  1077. *****************************************************************************/
  1078. static void FlushToEndOfExpr(int FlushStdin)
  1079. {
  1080.     if (FileStackPtr > 0)    /* Close all the open files - back to stdin. */
  1081.     while (FileStackPtr) fclose(FileStack[--FileStackPtr].f);
  1082.     else if (FlushStdin && IPGlblILastToken != TOKENEND)
  1083.         while (InptPrsrGetC() != ';');
  1084.  
  1085. }
  1086.  
  1087. /*****************************************************************************
  1088. *   Routine to push new file to read on the FileStack from INCLUDE command:  *
  1089. *****************************************************************************/
  1090. void FileInclude(char *FileName)
  1091. {
  1092.     int i;
  1093.     FILE *f;
  1094.     char s[LINE_LEN], c;
  1095.  
  1096.     if (FileStackPtr < FILE_STACK_SIZE) {
  1097.     if (strrchr(FileName, '.') == NULL)            /* If no '.' in name */
  1098.     {      /* (nor in its path - actually a bug, but I'll skip that...). */
  1099.         strcat(FileName, ".irt");
  1100.     }
  1101.     if ((f = fopen(FileName, "r")) != NULL) {
  1102.         FileStack[FileStackPtr].f = f;
  1103.         for (i=strlen(FileName)-1;           /* Isolate the file name. */
  1104.          i > 0 && (c = FileName[i]) != '\\' && c != '/' && c != ':';
  1105.          i--);
  1106.         if (i > 0) i++;
  1107.         strncpy(FileStack[FileStackPtr].Name, &FileName[i],
  1108.                             FILE_NAME_LEN-1);
  1109.         FileStackPtr++;         /* Now next char is from that file! */
  1110.     }
  1111.     else {
  1112.         sprintf(s, "Cannt open file %s - ignored\n", FileName);
  1113.         WndwInputWindowPutStr(s, RED);
  1114.     }
  1115.     }
  1116.     else WndwInputWindowPutStr("File nesting too deep - ignored\n", RED);
  1117. }
  1118.  
  1119. /*****************************************************************************
  1120. *   Routine to return parsing error if happen one, zero    elsewhere         *
  1121. *****************************************************************************/
  1122. int InptPrsrParseError(char **Message)
  1123. {
  1124.     int    Temp;
  1125.  
  1126.     *Message = IPGlblCharData;
  1127.     Temp = IPGlblParseError;
  1128.     IPGlblParseError = 0;
  1129.     return Temp;
  1130. }
  1131.