/***************************************************************************** * "Irit" - the 3d polygonal solid modeller. * * * * Written by: Gershon Elber Ver 0.2, Mar. 1990 * ****************************************************************************** * Module to convert infix expression given as ascii stream sequence into * * a binary tree, and evaluate it. * * All the objects are handled the same but the numerical one, which is * * moved as a RealType and not as an object (only internally within this * * module) as it is frequently used and consumes much less memory this way. * *****************************************************************************/ #ifdef __MSDOS__ #include #endif /* __MSDOS__ */ #include #include #include #include #include "program.h" #include "allocate.h" #include "ctrl-brk.h" #include "graphgen.h" #include "inptprsg.h" #include "inptprsl.h" #include "objects.h" #include "overload.h" #include "windows.h" static int IPGlblILastToken; /* Globals used by parser. */ static InptPrsrEvalErrType IPGlblParseError = IPE_NO_ERR; char IPGlblCharData[LINE_LEN_LONG]; /* Used for both parse & eval. */ static FileStackStruct FileStack[FILE_STACK_SIZE]; /* Include file stack. */ static int FileStackPtr = 0; /* Operator preceeding parser stack, and stack pointer: */ static ParseTree *Stack[MAX_PARSER_STACK]; static int ParserStackPointer = 0; /* Aliases list - simple macro substitution mechanizem. */ static AliasesStruct GlobalAliasList; #ifdef DEBUG int MaxStackPointer = 0; /* Measure maximum depth of stack. */ #endif /* DEBUG */ static ParseTree *GenInputParseTree(void); static ParseTree *OperatorPrecedence(void); static int TestPreceeding(int Token1, int Token2); static int GetToken(RealType *Data); static int GetVarFuncToken(char *Token, RealType *Data); static void FlushToEndOfExpr(int FlushStdin); static int GetEchoSrc(void); static void InptPrsrUnGetC(char c); static char InptPrsrGetC(void); /***************************************************************************** * Main module routine - generate parse tree and then tries to evaluate it. * * Returns TRUE if succesfull, otherwise check IPGlblParseError/EvalError. * *****************************************************************************/ int InputParser(void) { ParseTree *PTree; if (GlblWasCtrlBrk || GlblFatalError) { GlblFatalError = GlblWasCtrlBrk = FALSE; FlushToEndOfExpr(FALSE); /* Close all include files if any. */ return TRUE; } PTree = GenInputParseTree(); /* Generate parse tree. */ if (IPGlblParseError == IPE_NO_ERR) { # ifdef DEBUG fprintf(stderr, "\nInput generated Parse tree (Max stack = %d)\n", MaxStackPointer); InptPrsrPrintTree(PTree, NULL); fprintf(stderr, "\n"); # endif /* DEBUG */ if (InptPrsrTypeCheck(PTree, 0) == ERROR_EXPR) { /* Type checking. */ InptPrsrFreeTree(PTree); /* Not needed any more. */ FlushToEndOfExpr(TRUE);/* Close all include files, & flush stdin.*/ return FALSE; } InptPrsrEvalTree(PTree, 0); /* Evaluate it. */ if (IPGlblEvalError != IPE_NO_ERR) { FlushToEndOfExpr(TRUE); /* Close include files, and flush stdin. */ return FALSE; } } else { FlushToEndOfExpr(TRUE); /* Close all include files, and flush stdin. */ return FALSE; } InptPrsrFreeTree(PTree); /* Not needed any more. */ return !(IPGlblParseError || IPGlblEvalError); } /***************************************************************************** * Routine to convert the expression from stream f into a binary tree. * * Algorithm: Using operator precedence with the following grammer: * * EXPR ::= EXPR | EXPR + EXPR | EXPR - EXPR * * EXPR ::= EXPR | EXPR * EXPR | EXPR / EXPR * * EXPR ::= EXPR | EXPR ^ EXPR * * EXPR ::= EXPR | EXPR , EXPR | EXPR = EXPR * * EXPR ::= NUMBER | -EXPR | (EXPR) | FUNCTION * * FUCTION ::= FUNC(EXPR , EXPR , ...) * * Where FUNC might be function like arithmetics (SIN, COS etc.). * * Note that FUNC might have more than one operand, seperated by ','. * * * * Note the stream is terminated by semicolon character ';'. * * * * Left associativity for +, -, *, /, ^. * * Precedence of operators is as usual: * * {unar minus} {^} {*, /} {+, -} * * * * Returns NULL if an error was found, and error is in IPGlblParseError * *****************************************************************************/ static ParseTree *GenInputParseTree(void) { ParseTree *Root; int i; IPGlblILastToken = 0; /* Used to hold last token read from stream. */ IPGlblParseError = IPE_NO_ERR; /* No errors so far ... */ Root = OperatorPrecedence(); if (IPGlblParseError) { /* Free partialy allocated tree. */ for (i = 0; i <= ParserStackPointer; i++) InptPrsrFreeTree(Stack[i]); return NULL; /* Error ! */ } else return Root; } /***************************************************************************** * Routine to allocate new ParseTree expression node: * *****************************************************************************/ ParseTree *MyExprMalloc(void) { ParseTree *p; p = (ParseTree *) MyMalloc(sizeof(ParseTree), ALLOC_OTHER); p -> Right = p -> Left = NULL; p -> NodeKind = p -> ObjType = UNDEF_OBJ; return p; } /***************************************************************************** * Routine to free one expression node: * *****************************************************************************/ void MyExprFree(ParseTree *Ptr) { MyFree((char *) Ptr, ALLOC_OTHER); } /***************************************************************************** * Routine to actually parse using operator precedence: * * Few Notes: * * 1. Parse the input with the help of GetToken routine. Input is redirected * * using the FileStack. * * 2. All tokens must be in the range of 0..999 as we use the numbers above * * it (adding 1000) to deactivate them in the handle searching (i.e. when * * they were reduced to sub.-expression). * * 3. Returns NULL pointer in case of an error (see Expr2TrG.h for errors * * 4. See "Compilers - principles, techniques and tools" by Aho, Sethi & * * Ullman, pages 207-210. * *****************************************************************************/ static ParseTree *OperatorPrecedence(void) { int Token, LowHandle, Temp1, Temp2; RealType Data; # ifdef DEBUG MaxStackPointer = 0; # endif /* DEBUG */ ParserStackPointer = 0; /* Push the start symbol on stack (node pointer points on tos): */ Stack[ParserStackPointer] = MyExprMalloc(); Stack[ParserStackPointer] -> NodeKind = TOKENSTART; Stack[ParserStackPointer] -> Right = Stack[ParserStackPointer] -> Left = NULL; Token = GetToken(&Data); /* Get one look ahead token to start with. */ do { if (IPGlblParseError) return NULL; Temp1 = ParserStackPointer; /* Find top active token (<1000). */ while (Stack[Temp1] -> NodeKind >= 1000) Temp1--; /* Now test to see if the new token completes an handle: */ if (TestPreceeding(Stack[Temp1] -> NodeKind, Token)) { switch (Token) { case CLOSPARA: if (Stack[Temp1] -> NodeKind == OPENPARA) { MyExprFree(Stack[Temp1]); /* Free open paran. */ /* If a parameter is introduced instead of function */ /* it will be reduced already against "(" and it */ /* probably was missspelled function... */ if (Stack[Temp1-1] -> NodeKind == PARAMETER+1000) { strcpy(IPGlblCharData, Stack[Temp1-1] -> U.PObj -> Name); IPGlblParseError = IP_ERR_UNDEF_FUNC; return NULL; } if (IS_NO_PARAM_FUNC(Stack[Temp1-1] -> NodeKind)) { if (ParserStackPointer-Temp1 == 1) { UpdateCharError("", Stack[Temp1-1] -> NodeKind); IPGlblParseError = IP_ERR_NO_PARAM_FUNC; return NULL; } Stack[Temp1-1] -> NodeKind += 1000; Stack[Temp1-1] -> Right = NULL; ParserStackPointer--; } else if (IS_FUNCTION(Stack[Temp1-1] -> NodeKind)) { if (ParserStackPointer-Temp1 != 1) { UpdateCharError("", Stack[Temp1-1] -> NodeKind); IPGlblParseError = IP_ERR_PARAM_FUNC; return NULL; } Stack[ParserStackPointer] -> NodeKind -= 1000; Stack[Temp1-1] -> NodeKind += 1000; Stack[Temp1-1] -> Right = Stack[ParserStackPointer]; ParserStackPointer -= 2; } else { if (ParserStackPointer-Temp1 != 1) { IPGlblParseError = IP_ERR_PARAM_MATCH; return NULL; } Stack[Temp1] = Stack[ParserStackPointer--]; } Token = GetToken(&Data); /* Get another token. */ continue; } else if (Stack[Temp1] -> NodeKind == TOKENSTART) { /* No match for this one! */ IPGlblParseError = IP_ERR_PARAM_MATCH; return NULL; } break; case TOKENEND: if (Stack[Temp1] -> NodeKind == TOKENSTART) { if (ParserStackPointer != 1) { IPGlblParseError = IP_ERR_WRONG_SYNTAX; return NULL; } InptPrsrFreeTree(Stack[Temp1]); /* The TOKENSTART. */ Stack[1] -> NodeKind -= 1000; return Stack[1]; } } Temp2 = Temp1 - 1; /* Find the lower bound of handle. */ while (Temp2 >= 0 && Stack[Temp2] -> NodeKind >= 1000) Temp2--; LowHandle = Temp2 + 1; if (LowHandle < 1) { /* No low bound was found. */ IPGlblParseError = IP_ERR_WRONG_SYNTAX; return NULL; /* We ignore data till now. */ } switch (ParserStackPointer - LowHandle + 1) { case 1: /* Its a scalar one - mark it as used (add 1000). */ switch (Stack[ParserStackPointer] -> NodeKind) { case NUMBER: case PARAMETER: case STRING: Stack[ParserStackPointer] -> NodeKind += 1000; break; default: UpdateCharError("Found ", Stack[ParserStackPointer] -> NodeKind); IPGlblParseError = IP_ERR_PARAM_EXPECT; return NULL; } break; case 2: /* Its a monadic operator - create the subtree. */ switch (Stack[ParserStackPointer-1] -> NodeKind) { case UNARMINUS: Stack[ParserStackPointer-1] -> Right = Stack[ParserStackPointer]; Stack[ParserStackPointer] -> NodeKind -= 1000; Stack[ParserStackPointer-1] -> NodeKind += 1000; ParserStackPointer--; break; case OPENPARA: IPGlblParseError = IP_ERR_PARAM_MATCH; return NULL; default: UpdateCharError("Found Operator ", Stack[ParserStackPointer-1] -> NodeKind); IPGlblParseError = IP_ERR_ONE_OPERAND; return NULL; } break; case 3: /* Its a diadic operator - create the subtree. */ switch (Stack[ParserStackPointer-1] -> NodeKind) { case PLUS: case MINUS: case MULT: case DIV: case POWER: case COMMA: case EQUAL: case COLON: Stack[ParserStackPointer-1] -> Right = Stack[ParserStackPointer]; Stack[ParserStackPointer-1] -> Left = Stack[ParserStackPointer-2]; Stack[ParserStackPointer-2] -> NodeKind -= 1000; Stack[ParserStackPointer] -> NodeKind -= 1000; Stack[ParserStackPointer-1] -> NodeKind += 1000; Stack[ParserStackPointer-2] = Stack[ParserStackPointer-1]; ParserStackPointer -= 2; break; default: UpdateCharError("Found Operator ", Stack[ParserStackPointer-1] -> NodeKind); IPGlblParseError = IP_ERR_TWO_OPERAND; return NULL; } break; default: IPGlblParseError = IP_ERR_WRONG_SYNTAX; return NULL; } } else { /* Push that token on stack - it is not handle yet. */ Stack[++ParserStackPointer] = MyExprMalloc(); # ifdef DEBUG if (MaxStackPointer < ParserStackPointer) MaxStackPointer = ParserStackPointer; # endif /* DEBUG */ if (ParserStackPointer == MAX_PARSER_STACK-1) { IPGlblParseError = IP_ERR_STACK_OV; return NULL; /* We ignore data till now. */ } Stack[ParserStackPointer] -> NodeKind = Token; Stack[ParserStackPointer] -> U.R = Data; /* We might need that. */ Stack[ParserStackPointer] -> Right = Stack[ParserStackPointer] -> Left = NULL; if (Token == PARAMETER) { if ((Stack[ParserStackPointer] -> U.PObj = GetObject(IPGlblCharData)) == NULL) { /* Its new one - allocate memory for it. */ Stack[ParserStackPointer] -> U.PObj = AllocObject(IPGlblCharData, UNDEF_OBJ, NULL); } Stack[ParserStackPointer] -> U.PObj->Count++; } else if (Token == STRING) { Stack[ParserStackPointer] -> U.PObj = AllocObject("", STRING_OBJ, NULL); strcpy(Stack[ParserStackPointer] -> U.PObj -> U.Str, IPGlblCharData); Stack[ParserStackPointer] -> U.PObj->Count++; } Token = GetToken(&Data); /* And get new token from stream. */ } } while (TRUE); } /***************************************************************************** * Routine to test precedence of two tokens. returns 0, <0 or >0 according * * to comparison results: * *****************************************************************************/ static int TestPreceeding(int Token1, int Token2) { int Preced1, Preced2; if ((Token1 >= 1000) || (Token2 >= 1000)) return FALSE; /* Ignore sub-expr. */ if (IS_FUNCTION(Token1)) Preced1 = 130; else { switch (Token1) { case COMMA: case COLON: case EQUAL: Preced1 = 30; break; case NUMBER: case PARAMETER: case STRING: Preced1 = 150; break; case PLUS: case MINUS: Preced1 = 50; break; case MULT: case DIV: Preced1 = 70; break; case POWER: Preced1 = 90; break; case UNARMINUS: Preced1 = 95; break; case OPENPARA: Preced1 = 20; break; case CLOSPARA: Preced1 = 150; break; case TOKENSTART: case TOKENEND: Preced1 = 10; break; } } if (IS_FUNCTION(Token2)) Preced2 = 120; else { switch (Token2) { case COMMA: case COLON: case EQUAL: Preced2 = 35; break; case NUMBER: case PARAMETER: case STRING: Preced2 = 140; break; case PLUS: case MINUS: Preced2 = 40; break; case MULT: case DIV: Preced2 = 60; break; case POWER: Preced2 = 80; break; case UNARMINUS: Preced2 = 100; break; case OPENPARA: Preced2 = 140; break; case CLOSPARA: Preced2 = 15; break; case TOKENSTART: case TOKENEND: Preced2 = 0; break; } } return Preced1-Preced2 > 0; } /***************************************************************************** * Routine to update the character error message according to StrMsg & Token * *****************************************************************************/ void UpdateCharError(char *StrMsg, int Token) { char *TokenChar = NULL; if (Token > 1000) Token -= 1000; if (IS_NUM_FUNCTION(Token)) TokenChar = NumFuncTable[Token-NUM_FUNC_OFFSET].FuncName; else if (IS_OBJ_FUNCTION(Token)) TokenChar = ObjFuncTable[Token-OBJ_FUNC_OFFSET].FuncName; else if (IS_GEN_FUNCTION(Token)) TokenChar = GenFuncTable[Token-GEN_FUNC_OFFSET].FuncName; else { switch (Token) { case PLUS: TokenChar = "+"; break; case MINUS: TokenChar = "-"; break; case MULT: TokenChar = "*"; break; case DIV: TokenChar = "/"; break; case POWER: TokenChar = "^"; break; case UNARMINUS: TokenChar = "(Unar) -"; break; case COMMA: TokenChar = ","; break; case EQUAL: TokenChar = "="; break; case COLON: TokenChar = ":"; break; default: sprintf(IPGlblCharData, "%s - Token %d\n", StrMsg, Token); return; } } sprintf(IPGlblCharData, "%s%s", StrMsg, TokenChar); } /***************************************************************************** * Routine to get the next token out of the expression. * * Gets the expression in S, and current position in i. * * Returns the next token found, set data to the returned value (if any), * * and update i to one char ofter the new token found. * * Note that in minus sign case, it is determined whether it is monadic or * * diadic minus by the last token - if the last token was operator or '(' * * it is monadic minus. * *****************************************************************************/ static int GetToken(RealType *Data) { int i, RetVal = 0; char c; while (isspace(c = InptPrsrGetC())); /* Skip white blanks. */ if (c == '"') { /* Its a string token - read up to next ". */ i = 0; while ((IPGlblCharData[i] = InptPrsrGetC()) != '"') { if (IPGlblCharData[i] == '\\') /* Its escape char. for next one: */ IPGlblCharData[i] = InptPrsrGetC(); i++; } IPGlblCharData[i] = 0; RetVal = STRING; } else if (isalpha(c)) { /* Is it a variable/function name? */ if (islower(c)) IPGlblCharData[i=0] = toupper(c); else IPGlblCharData[i=0] = c; while (isalpha(c = InptPrsrGetC()) || isdigit(c) || c == '_') if (islower(c)) IPGlblCharData[++i] = toupper(c); else IPGlblCharData[++i] = c; IPGlblCharData[++i] = 0; InptPrsrUnGetC(c); if ((int) strlen(IPGlblCharData) >= OBJ_NAME_LEN) { RetVal = TOKENERROR; IPGlblParseError = IP_ERR_NAME_TOO_LONG; } else { RetVal = GetVarFuncToken(IPGlblCharData, Data); } } else if (isdigit(c) || (c == '.')) { /* Is it numeric data? */ IPGlblCharData[i=0] = c; while (isdigit(c = InptPrsrGetC()) || (c == '.') || (c == 'e') || (c == 'E') || (c == 'e')) IPGlblCharData[++i] = c; /* Handle the special case of negative exponent ("111.111E-22"). */ if (c == '-' && (IPGlblCharData[i] == 'e' || IPGlblCharData[i] == 'E')) { IPGlblCharData[++i] = c; while (isdigit(c = InptPrsrGetC()) || (c == '.')) IPGlblCharData[++i] = c; } IPGlblCharData[++i] = 0; InptPrsrUnGetC(c); # ifdef DOUBLE sscanf(IPGlblCharData, "%lf", Data); # else sscanf(IPGlblCharData, "%f", Data); # endif /* DOUBLE */ RetVal = NUMBER; } else switch (c) { case '+': RetVal = PLUS; break; case '-': switch (IPGlblILastToken) { case 0: /* If first token (no last token yet). */ case PLUS: case MINUS: case MULT: case DIV: case POWER: case COMMA: case EQUAL: case COLON: case UNARMINUS: case OPENPARA: RetVal = UNARMINUS; break; default: RetVal = MINUS; break; } break; case '*': RetVal = MULT; break; case '/': RetVal = DIV; break; case '^': RetVal = POWER; break; case '(': RetVal = OPENPARA; break; case ')': RetVal = CLOSPARA; break; case '=': RetVal = EQUAL; break; case ',': RetVal = COMMA; break; case ':': RetVal = COLON; break; case ';': RetVal = TOKENEND; break; /* End of expression! */ default: RetVal = TOKENERROR; IPGlblCharData[0] = c; IPGlblCharData[1] = 0; IPGlblParseError = IP_ERR_UNDEF_TOKEN; break; } IPGlblILastToken = RetVal; return RetVal; } /***************************************************************************** * Routine to test alpha Token for match with one of the defined functions * * and returns that Token function if found one. otherwise it is assumed to * * be a variable (new or old). * * Note that although the search is linear, It is extremely fast to add new * * functions - simply add its token, its entry here, and in the parser itself.* *****************************************************************************/ static int GetVarFuncToken(char *Token, RealType *Data) { int i; char c; if (strcmp("COMMENT", Token) == 0) { /* Get first nonspace char after the COMMENT key word: */ while (isspace(c = InptPrsrGetC())); /* And read the input until this char appear again (end of comment): */ while (c != InptPrsrGetC()); return GetToken(Data); /* Return next token instead. */ } for (i = 0; i < NumFuncTableSize; i++) /* Is it Numeric function? */ if (strcmp(NumFuncTable[i].FuncName, Token) == 0) return(NumFuncTable[i].FuncToken); for (i = 0; i < ObjFuncTableSize; i++) /* Is it Object function? */ if (strcmp(ObjFuncTable[i].FuncName, Token) == 0) return(ObjFuncTable[i].FuncToken); for (i = 0; i < GenFuncTableSize; i++) /* Is it General function? */ if (strcmp(GenFuncTable[i].FuncName, Token) == 0) return(GenFuncTable[i].FuncToken); for (i = 0; i < ConstantTableSize; i++)/* Replace constant by its value. */ if (strcmp(ConstantTable[i].FuncName, Token) == 0) { sprintf(Token, "%lg", ConstantTable[i].Value); *Data = ConstantTable[i].Value; return NUMBER; } return PARAMETER; /* If not a function - it is assumed to be variable. */ } /***************************************************************************** * Routine to reset the aliases buffer to a known state. * *****************************************************************************/ void AliasReset(void) { int i; for (i = 0; i < NUM_OF_ALIASES; i++) GlobalAliasList.Aliases[i].Name = NULL; } /***************************************************************************** * Routine to update (insert, delete or print) the global alias list * *****************************************************************************/ void AliasEdit(char *Name, char *Value) { int i; char s[LINE_LEN]; if (strlen(Name) == 0) { /* Print all defined alias list. */ WndwInputWindowPutStr("Alias List:"); for (i = 0; i < NUM_OF_ALIASES; i++) if (GlobalAliasList.Aliases[i].Name != NULL) { sprintf(s, "Alias \"%s\" - \"%s\"", GlobalAliasList.Aliases[i].Name, GlobalAliasList.Aliases[i].Value); WndwInputWindowPutStr(s); } return; } if (strlen(Value) == 0) { /* Its a delete operation. */ for (i = 0; i < NUM_OF_ALIASES; i++) if (GlobalAliasList.Aliases[i].Name != NULL && stricmp(Name, GlobalAliasList.Aliases[i].Name) == 0) break; if (i 100) { WndwInputWindowPutStr("Alias expansion - 100 expansions reached created, aborted:"); return; } OldSize = strlen(GlobalAliasList.Aliases[i].Name); NewSize = strlen(GlobalAliasList.Aliases[i].Value); DiffSize = NewSize - OldSize; if (DiffSize + (int) strlen(Line) > LINE_LEN_LONG - 1) { WndwInputWindowPutStr("Alias expansion created too long line, aborted:"); return; } /* Expand/shrink line space according to Name/Value sizes*/ if (DiffSize != 0) if (NewSize > OldSize) { movmem(alias, &alias[DiffSize], strlen(alias) + 1); } else { movmem(&alias[-DiffSize], alias, strlen(&alias[-DiffSize]) + 1); } /* And copy the Value instead of name into line: */ for (j = 0; j < NewSize; j++) alias[j] = GlobalAliasList.Aliases[i].Value[j]; } } while (alias != NULL); } } static char UnGetChar; /***************************************************************************** * Routine to get the value to EchoSrc variable. * *****************************************************************************/ static int GetEchoSrc(void) { int EchoSrc; ObjectStruct *PObj = GetObject("ECHOSRC"); if (PObj == NULL || !IS_NUM_OBJ(PObj)) { WndwInputWindowPutStr("No numeric object name EchoSrc is defined"); EchoSrc = DEFAULT_ECHOSRC; } else EchoSrc = (int) (PObj -> U.R); return EchoSrc; } /***************************************************************************** * Routine to control all getchar in this module and echo it if requested * * Note it handles the FileStack and decrease it if end of file was found. * *****************************************************************************/ static char InptPrsrGetC(void) { static char Line[LINE_LEN_LONG] = "", TLine[LINE_LEN_LONG] = "", *p; static int LineLength = 0, LineCount = 0; char c; int i; if (UnGetChar == 0) { /* One level of unget char... */ if (LineCount < LineLength) { /* Is there anything in local Line? */ } else do { if (FileStackPtr == 0) { WndwInputWindowGetStr(Line, LINE_LEN_LONG); LineCount = 0; } else { sprintf(Line, "%s > ", FileStack[FileStackPtr-1].Name); LineCount = strlen(Line); if (fgets(TLine, LINE_LEN_LONG, FileStack[FileStackPtr-1].f) == NULL) { /* Its end of file - close it and update stack. */ TLine[0] = 0; fclose(FileStack[--FileStackPtr].f); } /* Strip off CR/LF/TAB. */ for (i = LineCount, p = TLine; *p != 0; p++) { if (*p == 0x09) do { Line[i++] = ' '; } while ((i - LineCount) % 8 != 0); else if (*p < ' ' || *p > '~') break; else Line[i++] = *p; } Line[i] = 0; } AliasExpand(&Line[LineCount]); /* Expand aliases in line. */ if (GetEchoSrc()) #if defined(__MSDOS__) || defined(DJGCC) WndwInputWindowPutStr(Line); #else if (FileStackPtr != 0) /* Input was from keyboard? */ WndwInputWindowPutStr(Line); #endif /* __MSDOS__ || DJGCC */ LineLength = strlen(Line); } while (LineCount >= LineLength); c = Line[LineCount++]; if (c == '#') { /* Its a comment - skip that line. */ c = ' '; /* Must return something. */ LineCount = LineLength; /* Force next time to fetch new line. */ } # ifdef DEBUG fprintf(stderr, "%c", c); # endif /* DEBUG */ } else { c = UnGetChar; UnGetChar = 0; } return c; } /***************************************************************************** * Routine to unget one char * *****************************************************************************/ static void InptPrsrUnGetC(char c) { UnGetChar = c; } /***************************************************************************** * Routine to read data up to the next end of expression marker - ';'. * *****************************************************************************/ static void FlushToEndOfExpr(int FlushStdin) { if (FileStackPtr > 0) /* Close all the open files - back to stdin. */ while (FileStackPtr) fclose(FileStack[--FileStackPtr].f); else if (FlushStdin && IPGlblILastToken != TOKENEND) while (InptPrsrGetC() != ';'); } /***************************************************************************** * Routine to push new file to read on the FileStack from INCLUDE command: * *****************************************************************************/ void FileInclude(char *PrmFileName) { int i; FILE *f; char s[LINE_LEN], FileName[LINE_LEN], c; if (FileStackPtr < FILE_STACK_SIZE) { strcpy(FileName, PrmFileName); if (strrchr(FileName, '.') == NULL) /* If no '.' in name */ { /* (nor in its path - actually a bug, but I'll skip that...). */ strcat(FileName, ".irt"); } if ((f = fopen(FileName, "r")) != NULL) { FileStack[FileStackPtr].f = f; for (i = strlen(FileName)-1; /* Isolate the file name. */ i > 0 && (c = FileName[i]) != '\\' && c != '/' && c != ':'; i--); if (i > 0) i++; strncpy(FileStack[FileStackPtr].Name, &FileName[i], FILE_NAME_LEN-1); FileStackPtr++; /* Now next char is from that file! */ } else { sprintf(s, "Cannt open file %s - ignored", FileName); WndwInputWindowPutStr(s); } } else WndwInputWindowPutStr("File nesting too deep - ignored"); } /***************************************************************************** * Routine to return parsing error if happen one, zero elsewhere * *****************************************************************************/ InptPrsrEvalErrType InptPrsrParseError(char **Message) { InptPrsrEvalErrType Temp; *Message = IPGlblCharData; Temp = IPGlblParseError; IPGlblParseError = IPE_NO_ERR; return Temp; }