4014 lines
80 KiB
C++
4014 lines
80 KiB
C++
/*******************************************************************************
|
|
* tokenize.cpp
|
|
*
|
|
* This module implements the first part of a two part parser for the scene
|
|
* description files. This phase changes the input file into tokens.
|
|
*
|
|
* This module tokenizes the input file and sends the tokens created
|
|
* to the parser (the second stage). Tokens sent to the parser contain a
|
|
* token ID, the line number of the token, and if necessary, some data for
|
|
* the token.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
* Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
|
|
* Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
|
|
*
|
|
* POV-Ray is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* POV-Ray is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------------
|
|
* POV-Ray is based on the popular DKB raytracer version 2.12.
|
|
* DKBTrace was originally written by David K. Buck.
|
|
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
|
|
* ---------------------------------------------------------------------------
|
|
* $File: //depot/public/povray/3.x/source/backend/parser/tokenize.cpp $
|
|
* $Revision: #1 $
|
|
* $Change: 6069 $
|
|
* $DateTime: 2013/11/06 11:59:40 $
|
|
* $Author: chrisc $
|
|
*******************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
|
|
// frame.h must always be the first POV file included (pulls in platform config)
|
|
#include "backend/frame.h"
|
|
#include "base/povmsgid.h"
|
|
#include "base/stringutilities.h"
|
|
#include "backend/parser/parse.h"
|
|
#include "backend/colour/colour.h"
|
|
#include "backend/texture/texture.h"
|
|
#include "backend/math/matrices.h"
|
|
#include "backend/support/fileutil.h"
|
|
#include "backend/support/msgutil.h"
|
|
|
|
// this must be the last file included
|
|
#include "base/povdebug.h"
|
|
|
|
namespace pov
|
|
{
|
|
|
|
using namespace pov_base;
|
|
|
|
/*****************************************************************************
|
|
* Local preprocessor defines
|
|
******************************************************************************/
|
|
|
|
#define CALL(x) { if (!(x)) return (false); }
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Initialize_Tokenizer()
|
|
{
|
|
IStream *rfile = NULL;
|
|
UCS2String b;
|
|
int c;
|
|
|
|
pre_init_tokenizer ();
|
|
|
|
rfile = Locate_File(this, sceneData, sceneData->inputFile.c_str(),POV_File_Text_POV,b,true);
|
|
if(rfile != NULL)
|
|
{
|
|
Input_File->In_File = new ITextStream(b.c_str(), rfile);
|
|
sceneData->inputFile = b;
|
|
}
|
|
|
|
if (Input_File->In_File == NULL)
|
|
{
|
|
Error ("Cannot open input file.");
|
|
}
|
|
|
|
Input_File->R_Flag = false;
|
|
|
|
Got_EOF = false;
|
|
|
|
/* Init conditional stack. */
|
|
|
|
Cond_Stack = (CS_ENTRY*)POV_MALLOC(sizeof(CS_ENTRY) * COND_STACK_SIZE, "conditional stack");
|
|
|
|
Cond_Stack[0].Cond_Type = ROOT_COND;
|
|
Cond_Stack[0].Switch_Value = 0.0;
|
|
|
|
init_sym_tables();
|
|
Max_Trace_Level = MAX_TRACE_LEVEL_DEFAULT;
|
|
Had_Max_Trace_Level = false;
|
|
|
|
/* ignore any leading characters if they have character codes above 127, this
|
|
takes care of UTF-8 files with encoding info at the beginning of the file */
|
|
for(c = Echo_getc(); c > 127; c = Echo_getc())
|
|
sceneData->stringEncoding = 1; // switch to UTF-8 automatically [trf]
|
|
Echo_ungetc(c);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::pre_init_tokenizer ()
|
|
{
|
|
int i;
|
|
|
|
Token.Token_File_Pos.lineno = 0;
|
|
Token.Token_File_Pos.offset = 0;
|
|
Token.Token_Col_No = 0;
|
|
Token.Token_String = NULL;
|
|
Token.Unget_Token = false;
|
|
Token.End_Of_File = false;
|
|
Token.Data = NULL;
|
|
Token.FileHandle = NULL;
|
|
|
|
line_count = 10;
|
|
token_count = 0;
|
|
Current_Token_Count = 0;
|
|
Include_File_Index = 0;
|
|
Echo_Indx=0;
|
|
|
|
// make sure these are NULL otherwise cleanup() will crash if we terminate early
|
|
Default_Texture = NULL;
|
|
Brace_Stack = NULL;
|
|
|
|
CS_Index = 0;
|
|
Skipping = false;
|
|
Inside_Ifdef = false;
|
|
Inside_MacroDef = false;
|
|
Cond_Stack = NULL;
|
|
Table_Index = -1;
|
|
|
|
Input_File = &Include_Files[0];
|
|
Include_Files[0].In_File = NULL ;
|
|
|
|
for(i = 0; i < LAST_TOKEN; i++)
|
|
{
|
|
Conversion_Util_Table[i] = i;
|
|
if(i < FLOAT_FUNCT_TOKEN)
|
|
Conversion_Util_Table[i] = FLOAT_FUNCT_TOKEN;
|
|
else
|
|
{
|
|
if(i < VECTOR_FUNCT_TOKEN)
|
|
Conversion_Util_Table[i] = VECTOR_FUNCT_TOKEN;
|
|
else
|
|
{
|
|
if(i < COLOUR_KEY_TOKEN)
|
|
Conversion_Util_Table[i] = COLOUR_KEY_TOKEN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Terminate_Tokenizer()
|
|
{
|
|
Token.FileHandle = NULL ;
|
|
|
|
while(Table_Index >= 0)
|
|
{
|
|
Destroy_Table(Table_Index--);
|
|
}
|
|
|
|
if(Input_File->In_File != NULL)
|
|
{
|
|
delete Input_File->In_File;
|
|
Input_File->In_File = NULL;
|
|
Got_EOF = false;
|
|
}
|
|
|
|
while(Include_File_Index >= 0)
|
|
{
|
|
Input_File = &Include_Files[Include_File_Index--];
|
|
|
|
if(Input_File->In_File != NULL)
|
|
{
|
|
delete Input_File->In_File;
|
|
Input_File->In_File = NULL;
|
|
Got_EOF = false;
|
|
}
|
|
}
|
|
|
|
if(Cond_Stack != NULL)
|
|
{
|
|
for(int i = 0; i <= CS_Index; i++)
|
|
{
|
|
if((Cond_Stack[i].Cond_Type == INVOKING_MACRO_COND) && (Cond_Stack[i].Macro_Same_Flag == false))
|
|
delete Cond_Stack[i].Macro_File;
|
|
}
|
|
POV_FREE(Cond_Stack);
|
|
|
|
Cond_Stack = NULL;
|
|
}
|
|
|
|
if((String != NULL) && (String != String_Fast_Buffer))
|
|
POV_FREE(String);
|
|
String=NULL;
|
|
|
|
if((String2 != NULL) && (String2 != String_Fast_Buffer))
|
|
POV_FREE(String2);
|
|
String2=NULL;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* The main tokenizing routine. Set up the files and continue parsing
|
|
* until the end of file
|
|
*
|
|
* Read a token from the input file and store it in the Token variable.
|
|
* If the token is an INCLUDE token, then set the include file name and
|
|
* read another token.
|
|
*
|
|
* This function performs most of the work involved in tokenizing. It
|
|
* reads the first character of the token and decides which function to
|
|
* call to tokenize the rest. For simple tokens, it simply writes them
|
|
* out to the token buffer.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Get_Token ()
|
|
{
|
|
register int c,c2;
|
|
int col;
|
|
|
|
if (Token.Unget_Token)
|
|
{
|
|
Token.Unget_Token = false;
|
|
|
|
return;
|
|
}
|
|
|
|
if (Token.End_Of_File)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
|
|
while (Token.Token_Id == END_OF_FILE_TOKEN)
|
|
{
|
|
Skip_Spaces();
|
|
|
|
Token.Token_Col_No = col = Echo_Indx;
|
|
c = Echo_getc();
|
|
|
|
if (c == EOF)
|
|
{
|
|
if (Input_File->R_Flag)
|
|
{
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
Token.End_Of_File = true;
|
|
return;
|
|
}
|
|
|
|
if (Include_File_Index == 0)
|
|
{
|
|
if (CS_Index !=0)
|
|
Error("End of file reached but #end expected.");
|
|
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
|
|
Token.End_Of_File = true;
|
|
|
|
return;
|
|
}
|
|
|
|
if (Input_File->In_File == Token.FileHandle)
|
|
Token.FileHandle = NULL;
|
|
|
|
delete Input_File->In_File; /* added to fix open file buildup JLN 12/91 */
|
|
Input_File->In_File = NULL ;
|
|
Got_EOF=false;
|
|
|
|
Destroy_Table(Table_Index--);
|
|
|
|
Input_File = &Include_Files[--Include_File_Index];
|
|
if (Token.FileHandle == NULL)
|
|
Token.FileHandle = Input_File->In_File;
|
|
|
|
continue;
|
|
}
|
|
|
|
Begin_String_Fast();
|
|
|
|
String[0] = c; /* This isn't necessary but helps debugging */
|
|
|
|
String[1] = '\0';
|
|
|
|
/*String_Index = 0;*/
|
|
|
|
switch (c)
|
|
{
|
|
case '\n':
|
|
break;
|
|
|
|
case '{' :
|
|
Write_Token (LEFT_CURLY_TOKEN, col);
|
|
break;
|
|
|
|
case '}' :
|
|
Write_Token (RIGHT_CURLY_TOKEN, col);
|
|
break;
|
|
|
|
case '@' :
|
|
Write_Token (AT_TOKEN, col);
|
|
break;
|
|
|
|
case '&' :
|
|
Write_Token (AMPERSAND_TOKEN, col);
|
|
break;
|
|
|
|
case '`' :
|
|
Write_Token (BACK_QUOTE_TOKEN, col);
|
|
break;
|
|
|
|
case '\\':
|
|
Write_Token (BACK_SLASH_TOKEN, col);
|
|
break;
|
|
|
|
case '|' :
|
|
Write_Token (BAR_TOKEN, col);
|
|
break;
|
|
|
|
case ':' :
|
|
Write_Token (COLON_TOKEN, col);
|
|
break;
|
|
|
|
case ',' :
|
|
Write_Token (COMMA_TOKEN, col);
|
|
break;
|
|
|
|
case '-' :
|
|
Write_Token (DASH_TOKEN, col);
|
|
break;
|
|
|
|
case '$' :
|
|
Write_Token (DOLLAR_TOKEN, col);
|
|
break;
|
|
|
|
case '=' :
|
|
Write_Token (EQUALS_TOKEN, col);
|
|
break;
|
|
|
|
case '!' :
|
|
c2 = Echo_getc();
|
|
if (c2 == (int)'=')
|
|
{
|
|
Write_Token (REL_NE_TOKEN, col);
|
|
}
|
|
else
|
|
{
|
|
Echo_ungetc(c2);
|
|
Write_Token (EXCLAMATION_TOKEN, col);
|
|
}
|
|
break;
|
|
|
|
case '#' :
|
|
Parse_Directive(true);
|
|
/* Write_Token (HASH_TOKEN, col);*/
|
|
break;
|
|
|
|
case '^' :
|
|
Write_Token (HAT_TOKEN, col);
|
|
break;
|
|
|
|
case '<' :
|
|
c2 = Echo_getc();
|
|
if (c2 == (int)'=')
|
|
{
|
|
Write_Token (REL_LE_TOKEN, col);
|
|
}
|
|
else
|
|
{
|
|
Echo_ungetc(c2);
|
|
Write_Token (LEFT_ANGLE_TOKEN, col);
|
|
}
|
|
break;
|
|
|
|
case '(' :
|
|
Write_Token (LEFT_PAREN_TOKEN, col);
|
|
break;
|
|
|
|
case '[' :
|
|
Write_Token (LEFT_SQUARE_TOKEN, col);
|
|
break;
|
|
|
|
case '%' :
|
|
Write_Token (PERCENT_TOKEN, col);
|
|
break;
|
|
|
|
case '+' :
|
|
Write_Token (PLUS_TOKEN, col);
|
|
break;
|
|
|
|
case '?' :
|
|
Write_Token (QUESTION_TOKEN, col);
|
|
break;
|
|
|
|
case '>' :
|
|
c2 = Echo_getc();
|
|
if (c2 == (int)'=')
|
|
{
|
|
Write_Token (REL_GE_TOKEN, col);
|
|
}
|
|
else
|
|
{
|
|
Echo_ungetc(c2);
|
|
Write_Token (RIGHT_ANGLE_TOKEN, col);
|
|
}
|
|
break;
|
|
|
|
case ')' :
|
|
Write_Token (RIGHT_PAREN_TOKEN, col);
|
|
break;
|
|
|
|
case ']' :
|
|
Write_Token (RIGHT_SQUARE_TOKEN, col);
|
|
break;
|
|
|
|
case ';' : /* Parser doesn't use it, so let's ignore it */
|
|
Write_Token (SEMI_COLON_TOKEN, col);
|
|
break;
|
|
|
|
case '\'':
|
|
Write_Token (SINGLE_QUOTE_TOKEN, col);
|
|
break;
|
|
|
|
/* enable C++ style commenting */
|
|
case '/' :
|
|
c2 = Echo_getc();
|
|
if(c2 != (int) '/' && c2 != (int) '*')
|
|
{
|
|
Echo_ungetc(c2);
|
|
Write_Token (SLASH_TOKEN, col);
|
|
break;
|
|
}
|
|
if(c2 == (int)'*')
|
|
{
|
|
Parse_C_Comments();
|
|
break;
|
|
}
|
|
while(c2 != (int)'\n')
|
|
{
|
|
c2=Echo_getc();
|
|
if(c2==EOF)
|
|
{
|
|
Echo_ungetc(c2);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '*' :
|
|
Write_Token (STAR_TOKEN, col);
|
|
break;
|
|
|
|
case '~' :
|
|
Write_Token (TILDE_TOKEN, col);
|
|
break;
|
|
|
|
case '"' :
|
|
Parse_String_Literal ();
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
case '.':
|
|
Echo_ungetc(c);
|
|
if (Read_Float () != true)
|
|
return;
|
|
break;
|
|
|
|
case 'a':
|
|
case 'b':
|
|
case 'c':
|
|
case 'd':
|
|
case 'e':
|
|
case 'f':
|
|
case 'g':
|
|
case 'h':
|
|
case 'i':
|
|
case 'j':
|
|
case 'k':
|
|
case 'l':
|
|
case 'm':
|
|
case 'n':
|
|
case 'o':
|
|
case 'p':
|
|
case 'q':
|
|
case 'r':
|
|
case 's':
|
|
case 't':
|
|
case 'u':
|
|
case 'v':
|
|
case 'w':
|
|
case 'x':
|
|
case 'y':
|
|
case 'z':
|
|
|
|
case 'A':
|
|
case 'B':
|
|
case 'C':
|
|
case 'D':
|
|
case 'E':
|
|
case 'F':
|
|
case 'G':
|
|
case 'H':
|
|
case 'I':
|
|
case 'J':
|
|
case 'K':
|
|
case 'L':
|
|
case 'M':
|
|
case 'N':
|
|
case 'O':
|
|
case 'P':
|
|
case 'Q':
|
|
case 'R':
|
|
case 'S':
|
|
case 'T':
|
|
case 'U':
|
|
case 'V':
|
|
case 'W':
|
|
case 'X':
|
|
case 'Y':
|
|
case 'Z':
|
|
case '_':
|
|
Echo_ungetc(c);
|
|
Read_Symbol ();
|
|
break;
|
|
case '\t':
|
|
case '\r':
|
|
case '\032': /* Control Z - EOF on many systems */
|
|
case '\0':
|
|
break;
|
|
|
|
default:
|
|
Error("Illegal character in input file, value is %02x.", c);
|
|
break;
|
|
}
|
|
}
|
|
|
|
Current_Token_Count++;
|
|
token_count++;
|
|
|
|
if(token_count > TOKEN_OVERFLOW_RESET_COUNT) // NEVER, ever change the operator here! Other code using token_count depends on it!!! [trf]
|
|
{
|
|
token_count = 0;
|
|
|
|
if((ElapsedRealTime() - last_progress) > 1000) // update progress at most every second
|
|
{
|
|
POVMS_Object obj(kPOVObjectClass_ParserProgress);
|
|
obj.SetLong(kPOVAttrib_RealTime, ElapsedRealTime());
|
|
obj.SetLong(kPOVAttrib_CurrentTokenCount, Current_Token_Count);
|
|
RenderBackend::SendSceneOutput(sceneData->sceneId, sceneData->frontendAddress, kPOVMsgIdent_Progress, obj);
|
|
|
|
Cooperate();
|
|
|
|
last_progress = ElapsedRealTime();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Mark that the token has been put back into the input stream. The next
|
|
* call to Get_Token will return the last-read token instead of reading a
|
|
* new one from the file.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Unget_Token ()
|
|
{
|
|
Token.Unget_Token = true;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Skip over spaces in the input file.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::Skip_Spaces()
|
|
{
|
|
register int c;
|
|
|
|
while(true)
|
|
{
|
|
c = Echo_getc();
|
|
|
|
if (c == EOF)
|
|
return false;
|
|
|
|
if(!(isspace(c)))
|
|
break;
|
|
}
|
|
|
|
Echo_ungetc(c);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* C style comments with asterik and slash - CdW 8/91.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
int Parser::Parse_C_Comments()
|
|
{
|
|
register int c, c2;
|
|
bool End_Of_Comment = false;
|
|
|
|
while(!End_Of_Comment)
|
|
{
|
|
c = Echo_getc();
|
|
|
|
if(c == EOF)
|
|
Error("No */ closing comment found.");
|
|
|
|
if(c == (int) '*')
|
|
{
|
|
c2 = Echo_getc();
|
|
|
|
if(c2 != (int) '/')
|
|
Echo_ungetc(c2);
|
|
else
|
|
End_Of_Comment = true;
|
|
}
|
|
|
|
/* Check for and handle nested comments */
|
|
|
|
if(c == (int) '/')
|
|
{
|
|
c2 = Echo_getc();
|
|
|
|
if(c2 != (int) '*')
|
|
Echo_ungetc(c2);
|
|
else
|
|
Parse_C_Comments();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/* The following routines make it easier to handle strings. They stuff
|
|
characters into a string buffer one at a time making all the proper
|
|
range checks. Call Begin_String to start, Stuff_Character to put
|
|
characters in, and End_String to finish. The String variable contains
|
|
the final string. */
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
inline void Parser::Begin_String()
|
|
{
|
|
if((String != NULL) && (String != String_Fast_Buffer))
|
|
POV_FREE(String);
|
|
|
|
String = (char *)POV_MALLOC(256, "C String");
|
|
String_Buffer_Free = 256;
|
|
String_Index = 0;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
inline void Parser::Stuff_Character(int chr)
|
|
{
|
|
if(String_Buffer_Free <= 0)
|
|
{
|
|
Error("String too long.");
|
|
// This caused too many problems with buffer overflows [trf]
|
|
// String = (char *)POV_REALLOC(String, String_Index + 256, "String Literal Buffer");
|
|
// String_Buffer_Free += 256;
|
|
}
|
|
|
|
String[String_Index] = chr;
|
|
String_Buffer_Free--;
|
|
String_Index++;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
inline void Parser::End_String()
|
|
{
|
|
Stuff_Character(0);
|
|
|
|
if(String_Buffer_Free > 0)
|
|
String = (char *)POV_REALLOC(String, String_Index, "String Literal Buffer");
|
|
|
|
String_Buffer_Free = 0;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
inline void Parser::Begin_String_Fast()
|
|
{
|
|
if((String != NULL) && (String != String_Fast_Buffer))
|
|
POV_FREE(String);
|
|
|
|
String = String_Fast_Buffer;
|
|
String_Index = 0;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
inline void Parser::Stuff_Character_Fast(int chr)
|
|
{
|
|
String[String_Index & MAX_STRING_LEN_MASK] = chr;
|
|
String_Index++;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
inline void Parser::End_String_Fast()
|
|
{
|
|
Stuff_Character_Fast(0);
|
|
|
|
String_Index--; // Stuff_Character_Fast incremented this
|
|
|
|
if(String_Index != (String_Index & MAX_STRING_LEN_MASK))
|
|
Error("String too long.");
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Parse a string from the input file into a token.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Parse_String_Literal()
|
|
{
|
|
register int c;
|
|
int col = Echo_Indx;
|
|
|
|
Begin_String();
|
|
|
|
while(true)
|
|
{
|
|
c = Echo_getc();
|
|
|
|
if(c == EOF)
|
|
Error("No end quote for string.");
|
|
|
|
if(c == '\\')
|
|
{
|
|
switch(c = Echo_getc())
|
|
{
|
|
case '\n':
|
|
case '\r':
|
|
Error("Unterminated string literal.");
|
|
break;
|
|
case '\"':
|
|
c = '\"';
|
|
break;
|
|
case EOF:
|
|
Error("No end quote for string.");
|
|
break;
|
|
default:
|
|
Stuff_Character('\\');
|
|
}
|
|
|
|
Stuff_Character(c);
|
|
}
|
|
else
|
|
{
|
|
if(c != (int)'"')
|
|
Stuff_Character(c);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
End_String();
|
|
|
|
Write_Token(STRING_LITERAL_TOKEN, col);
|
|
|
|
Token.Token_String = String;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Read a float from the input file and tokenize it as one token. The phase
|
|
* variable is 0 for the first character, 1 for all subsequent characters
|
|
* up to the decimal point, 2 for all characters after the decimal
|
|
* point, 3 for the E+/- and 4 for the exponent. This helps to insure
|
|
* that the number is formatted properly. E format added 9/91 CEY
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
bool Parser::Read_Float()
|
|
{
|
|
register int c, Phase;
|
|
register bool Finished;
|
|
int col = Echo_Indx;
|
|
|
|
Finished = false;
|
|
|
|
Phase = 0;
|
|
|
|
Begin_String_Fast();
|
|
|
|
while (!Finished)
|
|
{
|
|
c = Echo_getc();
|
|
|
|
if (c == EOF)
|
|
{
|
|
Error ("Unexpected end of file.");
|
|
}
|
|
|
|
if (Phase > 1 && c == '.')
|
|
{
|
|
Error ("Unexpected additional '.' in floating-point number");
|
|
}
|
|
|
|
switch (Phase)
|
|
{
|
|
case 0:
|
|
|
|
Phase = 1;
|
|
|
|
if (isdigit(c))
|
|
{
|
|
Stuff_Character_Fast(c);
|
|
}
|
|
else
|
|
{
|
|
if (c == '.')
|
|
{
|
|
c = Echo_getc();
|
|
|
|
if (c == EOF)
|
|
{
|
|
Error ("Unexpected end of file");
|
|
}
|
|
|
|
if (isdigit(c))
|
|
{
|
|
Stuff_Character_Fast('0');
|
|
Stuff_Character_Fast('.');
|
|
Stuff_Character_Fast(c);
|
|
|
|
Phase = 2;
|
|
}
|
|
else
|
|
{
|
|
Echo_ungetc(c);
|
|
|
|
Write_Token (PERIOD_TOKEN, col);
|
|
|
|
return(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Error ("Invalid decimal number");
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
if (isdigit(c))
|
|
{
|
|
Stuff_Character_Fast(c);
|
|
}
|
|
else
|
|
{
|
|
if (c == (int) '.')
|
|
{
|
|
Stuff_Character_Fast(c); Phase = 2;
|
|
}
|
|
else
|
|
{
|
|
if ((c == 'e') || (c == 'E'))
|
|
{
|
|
Stuff_Character_Fast(c); Phase = 3;
|
|
}
|
|
else
|
|
{
|
|
Finished = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (isdigit(c))
|
|
{
|
|
Stuff_Character_Fast(c);
|
|
}
|
|
else
|
|
{
|
|
if ((c == 'e') || (c == 'E'))
|
|
{
|
|
Stuff_Character_Fast(c); Phase = 3;
|
|
}
|
|
else
|
|
{
|
|
Finished = true;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (isdigit(c) || (c == '+') || (c == '-'))
|
|
{
|
|
Stuff_Character_Fast(c); Phase = 4;
|
|
}
|
|
else
|
|
{
|
|
Finished = true;
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if (isdigit(c))
|
|
{
|
|
Stuff_Character_Fast(c);
|
|
}
|
|
else
|
|
{
|
|
Finished = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
Echo_ungetc(c);
|
|
|
|
End_String_Fast();
|
|
|
|
Write_Token (FLOAT_TOKEN, col);
|
|
|
|
if (sscanf (String, DBL_FORMAT_STRING, &Token.Token_Float) == 0)
|
|
{
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Read in a symbol from the input file. Check to see if it is a reserved
|
|
* word. If it is, write out the appropriate token. Otherwise, write the
|
|
* symbol out to the symbol table and write out an IDENTIFIER token. An
|
|
* identifier token is a token whose token number is greater than the
|
|
* highest reserved word.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Read_Symbol()
|
|
{
|
|
register int c;
|
|
int Local_Index,i,j,k;
|
|
POV_ARRAY *a;
|
|
SYM_ENTRY *Temp_Entry;
|
|
POV_PARAM *Par;
|
|
DBL val;
|
|
|
|
Begin_String_Fast();
|
|
|
|
while (true)
|
|
{
|
|
c = Echo_getc();
|
|
|
|
if (c == EOF)
|
|
{
|
|
Error ("Unexpected end of file.");
|
|
}
|
|
|
|
if (isalpha(c) || isdigit(c) || c == (int) '_')
|
|
{
|
|
Stuff_Character_Fast(c);
|
|
}
|
|
else
|
|
{
|
|
Echo_ungetc(c);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
End_String_Fast();
|
|
|
|
if (Inside_Ifdef)
|
|
{
|
|
Token.Token_Id = IDENTIFIER_TOKEN;
|
|
Token.is_array_elem = false;
|
|
|
|
return;
|
|
}
|
|
|
|
/* If its a reserved keyword, write it and return */
|
|
if ( (Temp_Entry = Find_Symbol(0,String)) != NULL)
|
|
{
|
|
Write_Token (Temp_Entry->Token_Number, Token.Token_Col_No);
|
|
return;
|
|
}
|
|
|
|
if (!Skipping)
|
|
{
|
|
/* Search tables from newest to oldest */
|
|
for (Local_Index=Table_Index; Local_Index > 0; Local_Index--)
|
|
{
|
|
/* See if it's a previously declared identifier. */
|
|
if ((Temp_Entry = Find_Symbol(Local_Index,String)) != NULL)
|
|
{
|
|
if ((Temp_Entry->Flags & (TF_DEPRECATED | TF_DEPRECATED_SHOWN)) == TF_DEPRECATED)
|
|
{
|
|
if ((Temp_Entry->Flags & TF_DEPRECATED_ONCE) != 0)
|
|
Temp_Entry->Flags |= TF_DEPRECATED_SHOWN;
|
|
Warning(0, "%s", Temp_Entry->Deprecation_Message);
|
|
}
|
|
|
|
if (Temp_Entry->Token_Number==MACRO_ID_TOKEN)
|
|
{
|
|
Token.Data = Temp_Entry->Data;
|
|
if (Ok_To_Declare)
|
|
{
|
|
Invoke_Macro();
|
|
}
|
|
else
|
|
{
|
|
Token.Token_Id=MACRO_ID_TOKEN;
|
|
Token.is_array_elem = false;
|
|
Token.NumberPtr = &(Temp_Entry->Token_Number);
|
|
Token.DataPtr = &(Temp_Entry->Data);
|
|
Write_Token (Token.Token_Id, Token.Token_Col_No);
|
|
|
|
Token.Table_Index = Local_Index;
|
|
}
|
|
return;
|
|
}
|
|
|
|
Token.Token_Id = Temp_Entry->Token_Number;
|
|
Token.is_array_elem = false;
|
|
Token.NumberPtr = &(Temp_Entry->Token_Number);
|
|
Token.DataPtr = &(Temp_Entry->Data);
|
|
|
|
while ((Token.Token_Id==PARAMETER_ID_TOKEN) ||
|
|
(Token.Token_Id==ARRAY_ID_TOKEN))
|
|
{
|
|
if (Token.Token_Id==ARRAY_ID_TOKEN)
|
|
{
|
|
Skip_Spaces();
|
|
c = Echo_getc();
|
|
Echo_ungetc(c);
|
|
|
|
if (c!='[')
|
|
{
|
|
break;
|
|
}
|
|
|
|
a = (POV_ARRAY *)(*(Token.DataPtr));
|
|
j = 0;
|
|
|
|
for (i=0; i <= a->Dims; i++)
|
|
{
|
|
GET(LEFT_SQUARE_TOKEN)
|
|
val=Parse_Float();
|
|
k=(int)(val + EPSILON);
|
|
|
|
if ((k < 0) || (val < -EPSILON))
|
|
{
|
|
Error("Negative subscript");
|
|
}
|
|
|
|
if (k >= a->Sizes[i])
|
|
{
|
|
Error("Array subscript out of range");
|
|
}
|
|
j += k * a->Mags[i];
|
|
GET(RIGHT_SQUARE_TOKEN)
|
|
}
|
|
|
|
Token.DataPtr = &(a->DataPtrs[j]);
|
|
Token.NumberPtr = &(a->Type);
|
|
Token.Token_Id = a->Type;
|
|
Token.is_array_elem = true;
|
|
if (!LValue_Ok)
|
|
{
|
|
if (*Token.DataPtr == NULL)
|
|
Error("Attempt to access uninitialized array element.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Par = (POV_PARAM *)(Temp_Entry->Data);
|
|
Token.Token_Id = *(Par->NumberPtr);
|
|
Token.is_array_elem = false;
|
|
Token.NumberPtr = Par->NumberPtr;
|
|
Token.DataPtr = Par->DataPtr;
|
|
}
|
|
}
|
|
|
|
Write_Token (Token.Token_Id, Token.Token_Col_No);
|
|
|
|
Token.Data = *(Token.DataPtr);
|
|
Token.Table_Index = Local_Index;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
Write_Token(IDENTIFIER_TOKEN, Token.Token_Col_No);
|
|
}
|
|
|
|
inline void Parser::Write_Token (TOKEN Token_Id, int col)
|
|
{
|
|
Token.Token_File_Pos = Input_File->In_File->tellg();
|
|
Token.Token_Col_No = col;
|
|
Token.FileHandle = Input_File->In_File;
|
|
Token.Token_String = String;
|
|
Token.Data = NULL;
|
|
Token.Token_Id = Conversion_Util_Table[Token_Id];
|
|
Token.Function_Id = Token_Id;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
const char *Parser::Get_Token_String (TOKEN Token_Id)
|
|
{
|
|
register int i;
|
|
|
|
for (i = 0 ; i < LAST_TOKEN ; i++)
|
|
if (Reserved_Words[i].Token_Number == Token_Id)
|
|
return (Reserved_Words[i].Token_Name);
|
|
return ("");
|
|
}
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Return a list of keywords seperated with a '\n'. The last keyword does not
|
|
* have a LF after it. The caller is responsible for freeing the memory. This
|
|
* function is intended to be used by GUI implementations that need a keyword
|
|
* list for syntax-coloring edit windows.
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
char *Parser::Get_Reserved_Words (const char *additional_words)
|
|
{
|
|
int length = 0 ;
|
|
int i ;
|
|
|
|
for (i = 0; i < LAST_TOKEN; i++)
|
|
{
|
|
if (!isalpha (Reserved_Words [i].Token_Name [0]))
|
|
continue ;
|
|
if (strchr (Reserved_Words [i].Token_Name, ' ') != NULL)
|
|
continue ;
|
|
length += (int)strlen (Reserved_Words[i].Token_Name) + 1 ;
|
|
}
|
|
|
|
length += (int)strlen (additional_words) ;
|
|
|
|
char *result = (char *) POV_MALLOC (++length, "Keyword List") ;
|
|
strcpy (result, additional_words) ;
|
|
char *s = result + strlen (additional_words) ;
|
|
|
|
for (i = 0 ; i < LAST_TOKEN ; i++)
|
|
{
|
|
if (!isalpha (Reserved_Words [i].Token_Name [0]))
|
|
continue ;
|
|
if (strchr (Reserved_Words [i].Token_Name, ' ') != NULL)
|
|
continue ;
|
|
s += sprintf (s, "%s\n", Reserved_Words[i].Token_Name) ;
|
|
}
|
|
*--s = '\0' ;
|
|
|
|
return (result) ;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
int Parser::Echo_getc()
|
|
{
|
|
register int c;
|
|
|
|
if((Input_File == NULL) || (Input_File->In_File == NULL) || (c = Input_File->In_File->getchar()) == EOF)
|
|
{
|
|
if (Got_EOF)
|
|
return EOF;
|
|
Got_EOF = true ;
|
|
Echo_Indx = 0 ;
|
|
return ('\n') ;
|
|
}
|
|
|
|
Echo_Indx++;
|
|
if(c == '\n')
|
|
Echo_Indx = 0;
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Echo_ungetc(int c)
|
|
{
|
|
if(Echo_Indx > 0)
|
|
Echo_Indx--;
|
|
|
|
Input_File->In_File->ungetchar(c);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Where_Error(POVMSObjectPtr msg)
|
|
{
|
|
// return if no filename is specified
|
|
if(Token.FileHandle == NULL)
|
|
return;
|
|
|
|
(void)POVMSUtil_SetUCS2String(msg, kPOVAttrib_FileName, Token.FileHandle->name());
|
|
(void)POVMSUtil_SetString(msg, kPOVAttrib_TokenName, Token.Token_String);
|
|
(void)POVMSUtil_SetLong(msg, kPOVAttrib_Line, Token.Token_File_Pos.lineno);
|
|
(void)POVMSUtil_SetInt(msg, kPOVAttrib_Column, Token.Token_Col_No);
|
|
if(Token.FileHandle != NULL)
|
|
(void)POVMSUtil_SetLong(msg, kPOVAttrib_FilePosition, Token.FileHandle->tellg().offset);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Where_Warning(POVMSObjectPtr msg)
|
|
{
|
|
// return if no filename is specified
|
|
if(Token.FileHandle == NULL)
|
|
return;
|
|
|
|
(void)POVMSUtil_SetUCS2String(msg, kPOVAttrib_FileName, Token.FileHandle->name());
|
|
(void)POVMSUtil_SetString(msg, kPOVAttrib_TokenName, Token.Token_String);
|
|
(void)POVMSUtil_SetLong(msg, kPOVAttrib_Line, Token.Token_File_Pos.lineno);
|
|
(void)POVMSUtil_SetInt(msg, kPOVAttrib_Column, Token.Token_Col_No);
|
|
if(Token.FileHandle != NULL)
|
|
(void)POVMSUtil_SetLong(msg, kPOVAttrib_FilePosition, Token.FileHandle->tellg().offset);
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION Parse_Directive
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR Chris Young
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Parse_Directive(int After_Hash)
|
|
{
|
|
DBL Value, Value2;
|
|
int Flag;
|
|
char *ts;
|
|
POV_MACRO *PMac=NULL;
|
|
COND_TYPE Curr_Type = Cond_Stack[CS_Index].Cond_Type;
|
|
POV_LONG Hash_Loc = Input_File->In_File->tellg().offset;
|
|
|
|
if (Curr_Type == INVOKING_MACRO_COND)
|
|
{
|
|
if (Cond_Stack[CS_Index].PMac->Macro_End==Hash_Loc)
|
|
{
|
|
Return_From_Macro();
|
|
if (--CS_Index < 0)
|
|
{
|
|
Error("Mis-matched '#end'.");
|
|
}
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!Ok_To_Declare)
|
|
{
|
|
if (After_Hash)
|
|
{
|
|
Token.Token_Id=HASH_TOKEN;
|
|
Token.is_array_elem = false;
|
|
}
|
|
Token.Unget_Token = false;
|
|
|
|
return;
|
|
}
|
|
|
|
EXPECT
|
|
CASE(IFDEF_TOKEN)
|
|
Inc_CS_Index();
|
|
|
|
if (Skipping)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
else
|
|
{
|
|
if (Parse_Ifdef_Param())
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_FALSE_COND;
|
|
Skip_Tokens(IF_FALSE_COND);
|
|
}
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(IFNDEF_TOKEN)
|
|
Inc_CS_Index();
|
|
|
|
if (Skipping)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
else
|
|
{
|
|
if (Parse_Ifdef_Param())
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_FALSE_COND;
|
|
Skip_Tokens(IF_FALSE_COND);
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
|
|
}
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(IF_TOKEN)
|
|
Inc_CS_Index();
|
|
|
|
if (Skipping)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
else
|
|
{
|
|
Value=Parse_Cond_Param();
|
|
|
|
if (fabs(Value)>EPSILON)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_FALSE_COND;
|
|
Skip_Tokens(IF_FALSE_COND);
|
|
}
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(WHILE_TOKEN)
|
|
Inc_CS_Index();
|
|
|
|
if (Skipping)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Loop_File = Input_File->In_File;
|
|
Cond_Stack[CS_Index].File_Pos = Input_File->In_File->tellg();
|
|
|
|
Value=Parse_Cond_Param();
|
|
|
|
if (fabs(Value)>EPSILON)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = WHILE_COND;
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(FOR_TOKEN)
|
|
Inc_CS_Index();
|
|
|
|
if (Skipping)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
else
|
|
{
|
|
char* Identifier = NULL;
|
|
DBL End, Step;
|
|
if (Parse_For_Param (&Identifier, &End, &Step))
|
|
{
|
|
// execute loop
|
|
Cond_Stack[CS_Index].Cond_Type = FOR_COND;
|
|
Cond_Stack[CS_Index].Loop_File = Input_File->In_File;
|
|
Cond_Stack[CS_Index].File_Pos = Input_File->In_File->tellg();
|
|
Cond_Stack[CS_Index].Loop_Identifier = Identifier;
|
|
Cond_Stack[CS_Index].For_Loop_End = End;
|
|
Cond_Stack[CS_Index].For_Loop_Step = Step;
|
|
}
|
|
else
|
|
{
|
|
// terminate loop
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(ELSE_TOKEN)
|
|
switch (Curr_Type)
|
|
{
|
|
case IF_TRUE_COND:
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
break;
|
|
|
|
case IF_FALSE_COND:
|
|
Cond_Stack[CS_Index].Cond_Type = ELSE_COND;
|
|
Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
|
|
Token.is_array_elem = false;
|
|
UNGET
|
|
break;
|
|
|
|
case CASE_TRUE_COND:
|
|
case SKIP_TIL_END_COND:
|
|
break;
|
|
|
|
case CASE_FALSE_COND:
|
|
Cond_Stack[CS_Index].Cond_Type = CASE_TRUE_COND;
|
|
if (Skipping)
|
|
{
|
|
Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
|
|
Token.is_array_elem = false;
|
|
UNGET
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Error("Mis-matched '#else'.");
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(ELSEIF_TOKEN)
|
|
switch (Curr_Type)
|
|
{
|
|
case IF_TRUE_COND:
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
break;
|
|
|
|
case IF_FALSE_COND:
|
|
Value=Parse_Cond_Param();
|
|
if (fabs(Value)>EPSILON)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
|
|
Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
|
|
Token.is_array_elem = false;
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
// nothing to do, we're staying in IF_FALSE_COND state.
|
|
}
|
|
break;
|
|
|
|
case SKIP_TIL_END_COND:
|
|
break;
|
|
|
|
default:
|
|
Error("Mis-matched '#elseif'.");
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(SWITCH_TOKEN)
|
|
Inc_CS_Index();
|
|
|
|
if (Skipping)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Switch_Value=Parse_Cond_Param();
|
|
Cond_Stack[CS_Index].Cond_Type=SWITCH_COND;
|
|
Cond_Stack[CS_Index].Switch_Case_Ok_Flag=false;
|
|
EXPECT
|
|
// NOTE: We actually expect a "#case" or "#range" here; however, this will trigger a nested call
|
|
// to Parse_Directive, so we'll encounter that CASE_TOKEN or RANGE_TOKEN here only by courtesy of the
|
|
// respective handler, which will UNGET the token and inform us via the Switch_Case_Ok_Flag
|
|
// that the CASE_TOKEN or RANGE_TOKEN we encounter here was properly preceded with a hash ("#").
|
|
CASE2(CASE_TOKEN,RANGE_TOKEN)
|
|
if (!Cond_Stack[CS_Index].Switch_Case_Ok_Flag)
|
|
Error("#switch not followed by #case or #range.");
|
|
|
|
if (Token.Token_Id==CASE_TOKEN)
|
|
{
|
|
Value=Parse_Cond_Param();
|
|
Flag = (fabs(Value-Cond_Stack[CS_Index].Switch_Value)<EPSILON);
|
|
}
|
|
else
|
|
{
|
|
Parse_Cond_Param2(&Value,&Value2);
|
|
Flag = ((Cond_Stack[CS_Index].Switch_Value >= Value) &&
|
|
(Cond_Stack[CS_Index].Switch_Value <= Value2));
|
|
}
|
|
|
|
if(Flag)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=CASE_TRUE_COND;
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=CASE_FALSE_COND;
|
|
Skip_Tokens(CASE_FALSE_COND);
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Error("#switch not followed by #case or #range.");
|
|
END_CASE
|
|
END_EXPECT
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(BREAK_TOKEN)
|
|
if (!Skipping)
|
|
Break();
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE2(CASE_TOKEN,RANGE_TOKEN)
|
|
switch(Curr_Type)
|
|
{
|
|
case CASE_TRUE_COND:
|
|
case CASE_FALSE_COND:
|
|
if (Token.Token_Id==CASE_TOKEN)
|
|
{
|
|
Value=Parse_Cond_Param();
|
|
Flag = (fabs(Value-Cond_Stack[CS_Index].Switch_Value)<EPSILON);
|
|
}
|
|
else
|
|
{
|
|
Parse_Cond_Param2(&Value,&Value2);
|
|
Flag = ((Cond_Stack[CS_Index].Switch_Value >= Value) &&
|
|
(Cond_Stack[CS_Index].Switch_Value <= Value2));
|
|
}
|
|
|
|
if(Flag && (Curr_Type==CASE_FALSE_COND))
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type=CASE_TRUE_COND;
|
|
if (Skipping)
|
|
{
|
|
Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
|
|
Token.is_array_elem = false;
|
|
UNGET
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SWITCH_COND:
|
|
Cond_Stack[CS_Index].Switch_Case_Ok_Flag=true;
|
|
UNGET
|
|
break;
|
|
|
|
case SKIP_TIL_END_COND:
|
|
break;
|
|
|
|
default:
|
|
Error("Mis-matched '#case' or '#range'.");
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(END_TOKEN)
|
|
switch (Curr_Type)
|
|
{
|
|
case INVOKING_MACRO_COND:
|
|
Return_From_Macro();
|
|
if (--CS_Index < 0)
|
|
{
|
|
Error("Mis-matched '#end'.");
|
|
}
|
|
break;
|
|
|
|
case IF_FALSE_COND:
|
|
Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
|
|
Token.is_array_elem = false;
|
|
UNGET
|
|
// FALLTHROUGH
|
|
case IF_TRUE_COND:
|
|
case ELSE_COND:
|
|
case CASE_TRUE_COND:
|
|
case CASE_FALSE_COND:
|
|
case DECLARING_MACRO_COND:
|
|
case SKIP_TIL_END_COND:
|
|
if (Curr_Type==DECLARING_MACRO_COND)
|
|
{
|
|
if ((PMac=Cond_Stack[CS_Index].PMac)!=NULL)
|
|
{
|
|
PMac->Macro_End=Hash_Loc;
|
|
}
|
|
}
|
|
if (--CS_Index < 0)
|
|
{
|
|
Error("Mis-matched '#end'.");
|
|
}
|
|
if (Skipping)
|
|
{
|
|
Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
|
|
Token.is_array_elem = false;
|
|
UNGET
|
|
}
|
|
break;
|
|
|
|
case WHILE_COND:
|
|
if (Cond_Stack[CS_Index].Loop_File != Input_File->In_File)
|
|
{
|
|
Error("#while loop did not end in file where it started.");
|
|
}
|
|
|
|
Got_EOF=false;
|
|
if (!Input_File->In_File->seekg(Cond_Stack[CS_Index].File_Pos))
|
|
{
|
|
Error("Unable to seek in input file for #while directive.");
|
|
}
|
|
|
|
Value=Parse_Cond_Param();
|
|
|
|
if (fabs(Value)<EPSILON)
|
|
{
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
break;
|
|
|
|
case FOR_COND:
|
|
if (Cond_Stack[CS_Index].Loop_File != Input_File->In_File)
|
|
{
|
|
Error("#for loop did not end in file where it started.");
|
|
}
|
|
|
|
Got_EOF=false;
|
|
if (!Input_File->In_File->seekg(Cond_Stack[CS_Index].File_Pos))
|
|
{
|
|
Error("Unable to seek in input file for #for directive.");
|
|
}
|
|
|
|
{
|
|
SYM_ENTRY* Entry = Find_Symbol(Table_Index, Cond_Stack[CS_Index].Loop_Identifier);
|
|
if ((Entry == NULL) || (Entry->Token_Number != FLOAT_ID_TOKEN))
|
|
Error ("#for loop variable must remain defined and numerical during loop.");
|
|
|
|
DBL* CurrentPtr = (DBL*)Entry->Data;
|
|
DBL End = Cond_Stack[CS_Index].For_Loop_End;
|
|
DBL Step = Cond_Stack[CS_Index].For_Loop_Step;
|
|
|
|
*CurrentPtr = *CurrentPtr + Step;
|
|
|
|
if ( ((Step > 0) && (*CurrentPtr > End + EPSILON)) ||
|
|
((Step < 0) && (*CurrentPtr < End - EPSILON)) )
|
|
{
|
|
POV_FREE(Cond_Stack[CS_Index].Loop_Identifier);
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens(SKIP_TIL_END_COND);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Error("Mis-matched '#end'.");
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE2 (DECLARE_TOKEN,LOCAL_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
EXIT
|
|
}
|
|
else
|
|
{
|
|
Parse_Declare(Token.Token_Id == LOCAL_TOKEN, After_Hash);
|
|
Curr_Type = Cond_Stack[CS_Index].Cond_Type;
|
|
if (Token.Unget_Token)
|
|
{
|
|
switch (Token.Token_Id)
|
|
{
|
|
case HASH_TOKEN:
|
|
Token.Unget_Token=false;
|
|
break;
|
|
|
|
case MACRO_ID_TOKEN:
|
|
break;
|
|
|
|
default:
|
|
EXIT
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EXIT
|
|
}
|
|
}
|
|
END_CASE
|
|
|
|
CASE (DEFAULT_TOKEN)
|
|
if ( Skipping )
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Parse_Default();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (INCLUDE_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Open_Include();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (FLOAT_FUNCT_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
EXIT
|
|
}
|
|
else
|
|
{
|
|
switch(Token.Function_Id)
|
|
{
|
|
case VERSION_TOKEN:
|
|
if (sceneData->languageVersionSet == false && token_count > 1)
|
|
sceneData->languageVersionLate = true;
|
|
sceneData->languageVersionSet = true;
|
|
Ok_To_Declare = false;
|
|
Get_Token();
|
|
if (pov_stricmp(Token.Token_String,"unofficial") == 0)
|
|
{
|
|
#if POV_RAY_IS_OFFICIAL == 1
|
|
Get_Token();
|
|
Error("This file was created for an unofficial version and\ncannot work as-is with this official version.");
|
|
#else
|
|
// PATCH AUTHORS - you should not enable any extra features unless the
|
|
// 'unofficial' keyword is set in the scene file.
|
|
#endif
|
|
}
|
|
else
|
|
Unget_Token();
|
|
sceneData->languageVersion = (int)(Parse_Float() * 100 + 0.5);
|
|
messageFactory.SetLanguageVersion(sceneData->languageVersion);
|
|
if (sceneData->explicitNoiseGenerator == false)
|
|
sceneData->noiseGenerator = sceneData->languageVersion >= 350 ? 2 : 1;
|
|
// [CLi] if assumed_gamma is not specified in a legacy (3.6.x or earlier) scene, gammaMode defaults to kPOVList_GammaMode_None;
|
|
// this is enforced later anyway after parsing, but we may need this information /now/ during parsing already
|
|
switch (sceneData->gammaMode)
|
|
{
|
|
case kPOVList_GammaMode_None:
|
|
case kPOVList_GammaMode_AssumedGamma37Implied:
|
|
if (sceneData->EffectiveLanguageVersion() < 370)
|
|
sceneData->gammaMode = kPOVList_GammaMode_None;
|
|
else
|
|
sceneData->gammaMode = kPOVList_GammaMode_AssumedGamma37Implied;
|
|
break;
|
|
case kPOVList_GammaMode_AssumedGamma36:
|
|
case kPOVList_GammaMode_AssumedGamma37:
|
|
if (sceneData->EffectiveLanguageVersion() < 370)
|
|
sceneData->gammaMode = kPOVList_GammaMode_AssumedGamma36;
|
|
else
|
|
sceneData->gammaMode = kPOVList_GammaMode_AssumedGamma37;
|
|
break;
|
|
}
|
|
Parse_Semi_Colon(false);
|
|
|
|
if (sceneData->languageVersion > OFFICIAL_VERSION_NUMBER)
|
|
{
|
|
Error("Your scene file requires POV-Ray version %g or later!\n", (DBL)(sceneData->languageVersion / 100.0));
|
|
}
|
|
|
|
Ok_To_Declare = true;
|
|
Curr_Type = Cond_Stack[CS_Index].Cond_Type;
|
|
if (Token.Unget_Token && (Token.Token_Id==HASH_TOKEN))
|
|
{
|
|
Token.Unget_Token=false;
|
|
}
|
|
else
|
|
{
|
|
EXIT
|
|
}
|
|
break;
|
|
|
|
default:
|
|
UNGET
|
|
Expectation_Error ("object or directive.");
|
|
break;
|
|
}
|
|
}
|
|
END_CASE
|
|
|
|
CASE(WARNING_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
ts=Parse_C_String();
|
|
if(strlen(ts) > 160) // intentional 160, not 128 [trf]
|
|
{
|
|
ts[124] = ts[125] = ts[126] = '.';
|
|
ts[127] = 0;
|
|
}
|
|
Warning(0, "%s", ts);
|
|
POV_FREE(ts);
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(ERROR_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
ts=Parse_C_String();
|
|
if(strlen(ts) > 160) // intentional 160, not 128 [trf]
|
|
{
|
|
ts[124] = ts[125] = ts[126] = '.';
|
|
ts[127] = 0;
|
|
}
|
|
Error("Parse halted by #error directive: %s", ts);
|
|
POV_FREE(ts);
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
/* Note: The new message driven output system does not support
|
|
generic user output to the render and statistics streams.
|
|
Both streams are now directed into the debug stream. */
|
|
CASE(RENDER_TOKEN)
|
|
CASE(STATISTICS_TOKEN)
|
|
Warning(0, "#render and #statistics streams are no longer available.\nRedirecting output to #debug stream.");
|
|
// Intentional, redirect output to debug stream.
|
|
CASE(DEBUG_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
ts=Parse_C_String();
|
|
if(strlen(ts) > 200) // intentional 200, not 160
|
|
{
|
|
ts[156] = ts[157] = ts[158] = '.';
|
|
ts[159] = 0;
|
|
}
|
|
Debug_Info("%s", ts);
|
|
POV_FREE(ts);
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(FOPEN_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Parse_Fopen();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(FCLOSE_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Parse_Fclose();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(READ_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Parse_Read();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(WRITE_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Parse_Write();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(UNDEF_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Ok_To_Declare = false;
|
|
EXPECT
|
|
CASE (IDENTIFIER_TOKEN)
|
|
Warning(0,"Attempt to undef unknown identifier");
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE2 (MACRO_ID_TOKEN, PARAMETER_ID_TOKEN)
|
|
CASE3 (FILE_ID_TOKEN, FUNCT_ID_TOKEN, VECTFUNCT_ID_TOKEN)
|
|
// These have to match Parse_Declare in parse.cpp! [trf]
|
|
CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
|
|
CASE4 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN, PIGMENT_ID_TOKEN)
|
|
CASE4 (SLOPE_MAP_ID_TOKEN, NORMAL_MAP_ID_TOKEN, TEXTURE_MAP_ID_TOKEN, COLOUR_ID_TOKEN)
|
|
CASE4 (PIGMENT_MAP_ID_TOKEN, MEDIA_ID_TOKEN, STRING_ID_TOKEN, INTERIOR_ID_TOKEN)
|
|
CASE4 (DENSITY_ID_TOKEN, ARRAY_ID_TOKEN, DENSITY_MAP_ID_TOKEN, UV_ID_TOKEN)
|
|
CASE4 (VECTOR_4D_ID_TOKEN, RAINBOW_ID_TOKEN, FOG_ID_TOKEN, SKYSPHERE_ID_TOKEN)
|
|
CASE2 (MATERIAL_ID_TOKEN, SPLINE_ID_TOKEN)
|
|
Remove_Symbol (Token.Table_Index, Token.Token_String, Token.is_array_elem, Token.DataPtr, Token.Token_Id);
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE2 (VECTOR_FUNCT_TOKEN, FLOAT_FUNCT_TOKEN)
|
|
switch(Token.Function_Id)
|
|
{
|
|
case VECTOR_ID_TOKEN:
|
|
case FLOAT_ID_TOKEN:
|
|
Remove_Symbol (Token.Table_Index, Token.Token_String, Token.is_array_elem, Token.DataPtr, Token.Token_Id);
|
|
break;
|
|
|
|
default:
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
break;
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
END_CASE
|
|
END_EXPECT
|
|
Ok_To_Declare = true;
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (MACRO_ID_TOKEN)
|
|
if (Skipping)
|
|
{
|
|
UNGET
|
|
}
|
|
else
|
|
{
|
|
Invoke_Macro();
|
|
}
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (MACRO_TOKEN)
|
|
if (!Skipping)
|
|
{
|
|
if (Inside_MacroDef)
|
|
{
|
|
Error("Cannot nest macro definitions");
|
|
}
|
|
Inside_MacroDef=true;
|
|
PMac=Parse_Macro();
|
|
Inside_MacroDef=false;
|
|
}
|
|
Inc_CS_Index();
|
|
Cond_Stack[CS_Index].Cond_Type = DECLARING_MACRO_COND;
|
|
Cond_Stack[CS_Index].PMac = PMac;
|
|
Skip_Tokens(DECLARING_MACRO_COND);
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
UNGET
|
|
EXIT
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
if (Token.Unget_Token)
|
|
{
|
|
Token.Unget_Token = false;
|
|
}
|
|
else
|
|
{
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Open_Include()
|
|
{
|
|
char *tempascii;
|
|
UCS2String temp;
|
|
UCS2String b;
|
|
|
|
if (Skip_Spaces () != true)
|
|
Error ("Expecting a string after INCLUDE.");
|
|
|
|
tempascii = Parse_C_String(true);
|
|
temp = ASCIItoUCS2String(tempascii);
|
|
POV_FREE(tempascii);
|
|
|
|
Include_File_Index++;
|
|
|
|
if (Include_File_Index >= MAX_INCLUDE_FILES)
|
|
{
|
|
Include_File_Index--;
|
|
Error ("Too many nested include files.");
|
|
}
|
|
|
|
Echo_Indx = 0;
|
|
|
|
Input_File = &Include_Files[Include_File_Index];
|
|
|
|
// must set this to NULL in case it's uninitialized - if Locate_File throws an
|
|
// exception (e.g. I/O restriction error), the parser shut-down code will attempt
|
|
// to free In_File if it's not NULL.
|
|
Input_File->In_File = NULL;
|
|
|
|
IStream *is = Locate_File(this, sceneData, temp, POV_File_Text_INC, b, true);
|
|
if(is == NULL)
|
|
{
|
|
Input_File->In_File = NULL; /* Keeps from closing failed file. */
|
|
Error ("Cannot open include file %s.", UCS2toASCIIString(temp).c_str());
|
|
}
|
|
else
|
|
Input_File->In_File = new ITextStream(b.c_str(), is);
|
|
|
|
Input_File->R_Flag=false;
|
|
|
|
Add_Sym_Table();
|
|
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Skip_Tokens(COND_TYPE cond)
|
|
{
|
|
int Temp = CS_Index;
|
|
int Prev_Skip = Skipping;
|
|
|
|
Skipping=true;
|
|
|
|
while ((CS_Index > Temp) || ((CS_Index == Temp) && (Cond_Stack[CS_Index].Cond_Type == cond)))
|
|
{
|
|
Get_Token();
|
|
}
|
|
|
|
Skipping=Prev_Skip;
|
|
|
|
if (Token.Token_Id==HASH_TOKEN)
|
|
{
|
|
Token.Token_Id=END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
Token.Unget_Token=false;
|
|
}
|
|
else
|
|
{
|
|
UNGET
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::Break()
|
|
{
|
|
int Prev_Skip = Skipping;
|
|
|
|
Skipping=true;
|
|
|
|
while ( (CS_Index > 0) &&
|
|
(Cond_Stack[CS_Index].Cond_Type != WHILE_COND) &&
|
|
(Cond_Stack[CS_Index].Cond_Type != FOR_COND) &&
|
|
(Cond_Stack[CS_Index].Cond_Type != CASE_TRUE_COND) &&
|
|
(Cond_Stack[CS_Index].Cond_Type != INVOKING_MACRO_COND) )
|
|
{
|
|
Get_Token();
|
|
}
|
|
|
|
if (CS_Index == 0)
|
|
Error ("Invalid context for #break");
|
|
|
|
if (Cond_Stack[CS_Index].Cond_Type == INVOKING_MACRO_COND)
|
|
{
|
|
Skipping=Prev_Skip;
|
|
Return_From_Macro();
|
|
--CS_Index;
|
|
return;
|
|
}
|
|
|
|
Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
|
|
Skip_Tokens (SKIP_TIL_END_COND);
|
|
|
|
Skipping=Prev_Skip;
|
|
|
|
if (Token.Token_Id==HASH_TOKEN)
|
|
{
|
|
Token.Token_Id=END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
Token.Unget_Token=false;
|
|
}
|
|
else
|
|
{
|
|
UNGET
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* get_hash_value
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Dieter Bayer
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* Calculate hash value for a given string.
|
|
*
|
|
* CHANGES
|
|
*
|
|
* Apr 1996 : Creation.
|
|
*
|
|
******************************************************************************/
|
|
|
|
int Parser::get_hash_value(const char *s)
|
|
{
|
|
unsigned int i = 0;
|
|
|
|
while (*s)
|
|
{
|
|
i = (i << 1) ^ *s++;
|
|
}
|
|
|
|
return((int)(i % SYM_TABLE_SIZE));
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* init_sym_tables
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* Chris Young
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::init_sym_tables()
|
|
{
|
|
int i;
|
|
|
|
Add_Sym_Table();
|
|
|
|
for (i = 0; i < LAST_TOKEN; i++)
|
|
{
|
|
Add_Symbol(0,Reserved_Words[i].Token_Name,Reserved_Words[i].Token_Number);
|
|
}
|
|
|
|
Add_Sym_Table();
|
|
}
|
|
|
|
void Parser::Add_Sym_Table()
|
|
{
|
|
int i;
|
|
|
|
SYM_TABLE *New;
|
|
|
|
if ((++Table_Index)==MAX_NUMBER_OF_TABLES)
|
|
{
|
|
Table_Index--;
|
|
Error("Too many nested symbol tables");
|
|
}
|
|
|
|
Tables[Table_Index]=New=(SYM_TABLE *)POV_MALLOC(sizeof(SYM_TABLE),"symbol table");
|
|
|
|
for (i = 0; i < SYM_TABLE_SIZE; i++)
|
|
{
|
|
New->Table[i] = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
void Parser::Destroy_Table(int index)
|
|
{
|
|
int i;
|
|
SYM_TABLE *Table = Tables[index];
|
|
SYM_ENTRY *Entry;
|
|
|
|
for(i = SYM_TABLE_SIZE - 1; i >= 0; i--)
|
|
{
|
|
Entry = Table->Table[i];
|
|
|
|
while(Entry)
|
|
{
|
|
Entry = Destroy_Entry(index, Entry);
|
|
}
|
|
|
|
Table->Table[i] = NULL;
|
|
}
|
|
|
|
POV_FREE(Table);
|
|
|
|
}
|
|
|
|
SYM_ENTRY *Parser::Create_Entry (int Index,const char *Name,TOKEN Number)
|
|
{
|
|
SYM_ENTRY *New;
|
|
|
|
New = (SYM_ENTRY *)POV_MALLOC(sizeof(SYM_ENTRY), "symbol table entry");
|
|
|
|
New->Token_Number = Number;
|
|
New->Data = NULL;
|
|
New->Flags = 0;
|
|
New->Deprecation_Message = NULL;
|
|
New->ref_count = 1;
|
|
if (Index != 0)
|
|
New->Token_Name = POV_STRDUP(Name);
|
|
else
|
|
New->Token_Name = const_cast<char*>(Name);
|
|
|
|
return(New);
|
|
}
|
|
|
|
void Parser::Acquire_Entry_Reference (SYM_ENTRY *Entry)
|
|
{
|
|
if (Entry == NULL)
|
|
return;
|
|
if (Entry->ref_count >= SYM_ENTRY_REF_MAX)
|
|
Error("Too many unresolved references to symbol");
|
|
Entry->ref_count ++;
|
|
}
|
|
|
|
void Parser::Release_Entry_Reference (int Index, SYM_ENTRY *Entry)
|
|
{
|
|
if (Entry == NULL)
|
|
return;
|
|
if (Entry->ref_count <= 0)
|
|
Error("Internal error: Symbol reference counter underflow");
|
|
Entry->ref_count --;
|
|
|
|
if (Entry->ref_count == 0)
|
|
{
|
|
if(Index != 0) // NB reserved words reference hard-coded token names in code segment, rather than allocated on heap
|
|
{
|
|
POV_FREE(Entry->Token_Name);
|
|
Destroy_Ident_Data (Entry->Data, Entry->Token_Number); // TODO - shouldn't this be outside the if() block?
|
|
}
|
|
if (Entry->Deprecation_Message != NULL)
|
|
POV_FREE(Entry->Deprecation_Message);
|
|
|
|
POV_FREE(Entry);
|
|
}
|
|
}
|
|
|
|
SYM_ENTRY *Parser::Destroy_Entry (int Index, SYM_ENTRY *Entry)
|
|
{
|
|
SYM_ENTRY *Next;
|
|
|
|
if(Entry == NULL)
|
|
return NULL;
|
|
|
|
// always unhook the entry from hash table (if it is still member of one)
|
|
Next = Entry->next;
|
|
Entry->next = NULL;
|
|
|
|
if (Entry->ref_count <= 0)
|
|
Error("Internal error: Symbol reference counter underflow");
|
|
Entry->ref_count --;
|
|
|
|
if (Entry->ref_count == 0)
|
|
{
|
|
if(Index != 0) // NB reserved words reference hard-coded token names in code segment, rather than allocated on heap
|
|
{
|
|
POV_FREE(Entry->Token_Name);
|
|
Destroy_Ident_Data (Entry->Data, Entry->Token_Number); // TODO - shouldn't this be outside the if() block?
|
|
}
|
|
if (Entry->Deprecation_Message != NULL)
|
|
POV_FREE(Entry->Deprecation_Message);
|
|
|
|
POV_FREE(Entry);
|
|
}
|
|
|
|
return Next;
|
|
}
|
|
|
|
|
|
void Parser::Add_Entry (int Index,SYM_ENTRY *Table_Entry)
|
|
{
|
|
int i = get_hash_value(Table_Entry->Token_Name);
|
|
|
|
Table_Entry->next = Tables[Index]->Table[i];
|
|
Tables[Index]->Table[i] = Table_Entry;
|
|
}
|
|
|
|
|
|
SYM_ENTRY *Parser::Add_Symbol (int Index,const char *Name,TOKEN Number)
|
|
{
|
|
SYM_ENTRY *New;
|
|
|
|
New = Create_Entry (Index,Name,Number);
|
|
Add_Entry(Index,New);
|
|
|
|
return(New);
|
|
}
|
|
|
|
|
|
SYM_ENTRY *Parser::Find_Symbol(int Index,const char *Name)
|
|
{
|
|
SYM_ENTRY *Entry;
|
|
|
|
int i = get_hash_value(Name);
|
|
|
|
Entry = Tables[Index]->Table[i];
|
|
|
|
while (Entry)
|
|
{
|
|
if (strcmp(Name, Entry->Token_Name) == 0)
|
|
{
|
|
return(Entry);
|
|
}
|
|
|
|
Entry = Entry->next;
|
|
}
|
|
|
|
return(Entry);
|
|
}
|
|
|
|
|
|
void Parser::Remove_Symbol (int Index, const char *Name, bool is_array_elem, void **DataPtr, int ttype)
|
|
{
|
|
if(is_array_elem == true)
|
|
{
|
|
if(DataPtr == NULL)
|
|
Error("Invalid array element!");
|
|
|
|
if(ttype == FLOAT_FUNCT_TOKEN)
|
|
ttype = FLOAT_ID_TOKEN;
|
|
else if(ttype == VECTOR_FUNCT_TOKEN)
|
|
ttype = VECTOR_ID_TOKEN;
|
|
else if(ttype == COLOUR_KEY_TOKEN)
|
|
ttype = COLOUR_ID_TOKEN;
|
|
|
|
Destroy_Ident_Data (*DataPtr, ttype);
|
|
*DataPtr = NULL;
|
|
}
|
|
else
|
|
{
|
|
SYM_ENTRY *Entry;
|
|
SYM_ENTRY **EntryPtr;
|
|
|
|
int i = get_hash_value(Name);
|
|
|
|
EntryPtr = &(Tables[Index]->Table[i]);
|
|
Entry = *EntryPtr;
|
|
|
|
while (Entry)
|
|
{
|
|
if (strcmp(Name, Entry->Token_Name) == 0)
|
|
{
|
|
*EntryPtr = Entry->next;
|
|
Destroy_Entry(Index, Entry);
|
|
return;
|
|
}
|
|
|
|
EntryPtr = &(Entry->next);
|
|
Entry = *EntryPtr;
|
|
}
|
|
|
|
Error("Tried to free undefined symbol");
|
|
}
|
|
}
|
|
|
|
void Parser::Check_Macro_Vers(void)
|
|
{
|
|
if (sceneData->languageVersion < 310)
|
|
{
|
|
Error("Macros require #version 3.1 or later but #version %x.%02d is set.",
|
|
sceneData->languageVersion / 100, sceneData->languageVersion % 100);
|
|
}
|
|
}
|
|
|
|
Parser::POV_MACRO *Parser::Parse_Macro()
|
|
{
|
|
POV_MACRO *New;
|
|
SYM_ENTRY *Table_Entry=NULL;
|
|
int Old_Ok = Ok_To_Declare;
|
|
|
|
Check_Macro_Vers();
|
|
|
|
Ok_To_Declare = false;
|
|
|
|
EXPECT
|
|
CASE (IDENTIFIER_TOKEN)
|
|
Table_Entry = Add_Symbol (1,Token.Token_String,TEMPORARY_MACRO_ID_TOKEN);
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (MACRO_ID_TOKEN)
|
|
Remove_Symbol(1,Token.Token_String,false,NULL,0);
|
|
Table_Entry = Add_Symbol (1,Token.Token_String,TEMPORARY_MACRO_ID_TOKEN);
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
New=(POV_MACRO *)POV_MALLOC(sizeof(POV_MACRO),"macro");
|
|
|
|
Table_Entry->Data=(void *)New;
|
|
|
|
New->Macro_Filename = NULL;
|
|
New->Num_Of_Pars=0;
|
|
New->Macro_Name=POV_STRDUP(Token.Token_String);
|
|
|
|
EXPECT
|
|
CASE (LEFT_PAREN_TOKEN )
|
|
EXIT
|
|
END_CASE
|
|
CASE (TEMPORARY_MACRO_ID_TOKEN)
|
|
Error( "Can't invoke a macro while declaring its parameters");
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Expectation_Error ("identifier or expression.");
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
EXPECT
|
|
CASE3 (MACRO_ID_TOKEN, IDENTIFIER_TOKEN, PARAMETER_ID_TOKEN)
|
|
CASE3 (FILE_ID_TOKEN, FUNCT_ID_TOKEN, VECTFUNCT_ID_TOKEN)
|
|
// These have to match Parse_Declare in parse.cpp! [trf]
|
|
CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
|
|
CASE4 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN, PIGMENT_ID_TOKEN)
|
|
CASE4 (SLOPE_MAP_ID_TOKEN, NORMAL_MAP_ID_TOKEN, TEXTURE_MAP_ID_TOKEN, COLOUR_ID_TOKEN)
|
|
CASE4 (PIGMENT_MAP_ID_TOKEN, MEDIA_ID_TOKEN, STRING_ID_TOKEN, INTERIOR_ID_TOKEN)
|
|
CASE4 (DENSITY_ID_TOKEN, ARRAY_ID_TOKEN, DENSITY_MAP_ID_TOKEN, UV_ID_TOKEN)
|
|
CASE4 (VECTOR_4D_ID_TOKEN, RAINBOW_ID_TOKEN, FOG_ID_TOKEN, SKYSPHERE_ID_TOKEN)
|
|
CASE2 (MATERIAL_ID_TOKEN, SPLINE_ID_TOKEN)
|
|
New->Par_Name[New->Num_Of_Pars] = POV_STRDUP(Token.Token_String);
|
|
if (++(New->Num_Of_Pars) == MAX_PARAMETER_LIST)
|
|
{
|
|
Error("Too many parameters");
|
|
}
|
|
Parse_Comma();
|
|
END_CASE
|
|
|
|
CASE2 (VECTOR_FUNCT_TOKEN, FLOAT_FUNCT_TOKEN)
|
|
switch(Token.Function_Id)
|
|
{
|
|
case VECTOR_ID_TOKEN:
|
|
case FLOAT_ID_TOKEN:
|
|
New->Par_Name[New->Num_Of_Pars] = POV_STRDUP(Token.Token_String);
|
|
if (++(New->Num_Of_Pars) == MAX_PARAMETER_LIST)
|
|
{
|
|
Error("Too many parameters");
|
|
}
|
|
Parse_Comma();
|
|
break;
|
|
|
|
default:
|
|
Expectation_Error ("identifier or expression.");
|
|
break;
|
|
}
|
|
END_CASE
|
|
|
|
CASE(RIGHT_PAREN_TOKEN)
|
|
UNGET
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(TEMPORARY_MACRO_ID_TOKEN)
|
|
Error( "Can't invoke a macro while declaring its parameters");
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Expectation_Error ("identifier or expression.");
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
Ok_To_Declare = Old_Ok;
|
|
|
|
Table_Entry->Token_Number = MACRO_ID_TOKEN;
|
|
|
|
New->Macro_Filename = UCS2_strdup(Input_File->In_File->name());
|
|
New->Macro_File_Pos = Input_File->In_File->tellg();
|
|
|
|
Check_Macro_Vers();
|
|
|
|
return (New);
|
|
}
|
|
|
|
|
|
void Parser::Invoke_Macro()
|
|
{
|
|
POV_MACRO *PMac=(POV_MACRO *)Token.Data;
|
|
SYM_ENTRY **Table_Entries=NULL;
|
|
int i,Local_Index;
|
|
|
|
if(PMac == NULL)
|
|
{
|
|
if(Token.DataPtr!=NULL)
|
|
PMac = (POV_MACRO*)(*(Token.DataPtr));
|
|
else
|
|
Error("Error in Invoke_Macro");
|
|
}
|
|
|
|
Check_Macro_Vers();
|
|
|
|
GET(LEFT_PAREN_TOKEN);
|
|
|
|
if (PMac->Num_Of_Pars > 0)
|
|
{
|
|
Table_Entries = (SYM_ENTRY **)POV_MALLOC(sizeof(SYM_ENTRY *)*PMac->Num_Of_Pars,"parameters");
|
|
|
|
/* We must parse all parameters before adding new symbol table
|
|
or adding entries. Otherwise recursion won't always work.
|
|
*/
|
|
|
|
Local_Index = Table_Index;
|
|
|
|
for (i=0; i<PMac->Num_Of_Pars; i++)
|
|
{
|
|
Table_Entries[i]=Create_Entry(1,PMac->Par_Name[i],IDENTIFIER_TOKEN);
|
|
if (!Parse_RValue(IDENTIFIER_TOKEN, &(Table_Entries[i]->Token_Number), &(Table_Entries[i]->Data), NULL, true, false, true, true, Local_Index))
|
|
{
|
|
Error("Expected %d parameters but only %d found.",PMac->Num_Of_Pars,i);
|
|
}
|
|
Parse_Comma();
|
|
}
|
|
}
|
|
|
|
GET(RIGHT_PAREN_TOKEN);
|
|
|
|
Inc_CS_Index();
|
|
Cond_Stack[CS_Index].Cond_Type = INVOKING_MACRO_COND;
|
|
|
|
Cond_Stack[CS_Index].File_Pos = Input_File->In_File->tellg();
|
|
Cond_Stack[CS_Index].Macro_Return_Name = Input_File->In_File->name();
|
|
Cond_Stack[CS_Index].PMac = PMac;
|
|
|
|
/* Gotta have new symbol table in case #local is used */
|
|
Add_Sym_Table();
|
|
|
|
if (PMac->Num_Of_Pars > 0)
|
|
{
|
|
for (i=0; i<PMac->Num_Of_Pars; i++)
|
|
{
|
|
Add_Entry(Table_Index,Table_Entries[i]);
|
|
}
|
|
|
|
POV_FREE(Table_Entries);
|
|
}
|
|
|
|
if (UCS2_strcmp(PMac->Macro_Filename,Input_File->In_File->name()))
|
|
{
|
|
UCS2String ign;
|
|
/* Not in same file */
|
|
Cond_Stack[CS_Index].Macro_Same_Flag=false;
|
|
Cond_Stack[CS_Index].Macro_File = Input_File->In_File ;
|
|
// POV_DELETE(Input_File->In_File, IStream);
|
|
Got_EOF=false;
|
|
Input_File->R_Flag=false;
|
|
IStream *is = Locate_File (this, sceneData, PMac->Macro_Filename, POV_File_Text_Macro, ign, true);
|
|
if(is == NULL)
|
|
{
|
|
Input_File->In_File = NULL; /* Keeps from closing failed file. */
|
|
Error ("Cannot open macro file '%s'.", UCS2toASCIIString(UCS2String(PMac->Macro_Filename)).c_str());
|
|
}
|
|
else
|
|
Input_File->In_File = new ITextStream(PMac->Macro_Filename, is);
|
|
}
|
|
else
|
|
{
|
|
Cond_Stack[CS_Index].Macro_Same_Flag=true;
|
|
}
|
|
|
|
Got_EOF=false;
|
|
if (!Input_File->In_File->seekg(PMac->Macro_File_Pos))
|
|
{
|
|
Error("Unable to file seek in macro.");
|
|
}
|
|
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
|
|
Check_Macro_Vers();
|
|
|
|
}
|
|
|
|
void Parser::Return_From_Macro()
|
|
{
|
|
Check_Macro_Vers();
|
|
|
|
if (!Cond_Stack[CS_Index].Macro_Same_Flag)
|
|
{
|
|
if (Token.FileHandle == Input_File->In_File)
|
|
Token.FileHandle = NULL;
|
|
delete Input_File->In_File;
|
|
Input_File->R_Flag=false;
|
|
Input_File->In_File = Cond_Stack[CS_Index].Macro_File ;
|
|
if (Token.FileHandle == NULL)
|
|
Token.FileHandle = Input_File->In_File;
|
|
}
|
|
|
|
Got_EOF=false;
|
|
|
|
if (!Input_File->In_File->seekg(Cond_Stack[CS_Index].File_Pos))
|
|
{
|
|
Error("Unable to file seek in return from macro.");
|
|
}
|
|
|
|
// Always destroy macro locals
|
|
Destroy_Table(Table_Index--);
|
|
}
|
|
|
|
void Parser::Destroy_Macro(POV_MACRO *PMac)
|
|
{
|
|
int i;
|
|
if (PMac==NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
POV_FREE(PMac->Macro_Name);
|
|
if (PMac->Macro_Filename!=NULL)
|
|
{
|
|
POV_FREE(PMac->Macro_Filename);
|
|
}
|
|
|
|
for (i=0; i < PMac->Num_Of_Pars; i++)
|
|
{
|
|
POV_FREE(PMac->Par_Name[i]);
|
|
}
|
|
|
|
POV_FREE(PMac);
|
|
}
|
|
|
|
Parser::POV_ARRAY *Parser::Parse_Array_Declare (void)
|
|
{
|
|
POV_ARRAY *New;
|
|
int i,j;
|
|
|
|
New=(POV_ARRAY *)POV_MALLOC(sizeof(POV_ARRAY),"array");
|
|
|
|
i=0;
|
|
j=1;
|
|
|
|
Ok_To_Declare = false;
|
|
|
|
EXPECT
|
|
CASE (LEFT_SQUARE_TOKEN)
|
|
if (i>4)
|
|
{
|
|
Error("Too many array dimensions");
|
|
}
|
|
New->Sizes[i]=(int)(Parse_Float() + EPSILON);
|
|
j *= New->Sizes[i];
|
|
if ( j <= 0) {
|
|
Error("Invalid dimension size for an array");
|
|
}
|
|
i++;
|
|
GET(RIGHT_SQUARE_TOKEN)
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
UNGET
|
|
EXIT
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
if ( i < 1 ) {
|
|
Error( "An array declaration must have at least one dimension");
|
|
}
|
|
|
|
New->Dims = i-1;
|
|
New->Total = j;
|
|
New->Type = EMPTY_ARRAY_TOKEN;
|
|
New->DataPtrs = (void **)POV_MALLOC(sizeof(void *)*j,"array");
|
|
|
|
j = 1;
|
|
|
|
for(i = New->Dims; i>=0; i--)
|
|
{
|
|
New->Mags[i] = j;
|
|
j *= New->Sizes[i];
|
|
}
|
|
|
|
for (i=0; i<New->Total; i++)
|
|
{
|
|
New->DataPtrs[i] = NULL;
|
|
}
|
|
|
|
EXPECT
|
|
CASE(LEFT_CURLY_TOKEN)
|
|
UNGET
|
|
Parse_Initalizer(0,0,New);
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
UNGET
|
|
EXIT
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
Ok_To_Declare = true;
|
|
return(New);
|
|
|
|
};
|
|
|
|
void Parser::Parse_Initalizer (int Sub, int Base, POV_ARRAY *a)
|
|
{
|
|
int i;
|
|
|
|
Parse_Begin();
|
|
if (Sub < a->Dims)
|
|
{
|
|
for(i=0; i < a->Sizes[Sub]; i++)
|
|
{
|
|
Parse_Initalizer(Sub+1,i*a->Mags[Sub]+Base,a);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i < a->Sizes[Sub]; i++)
|
|
{
|
|
if (!Parse_RValue (a->Type, &(a->Type), &(a->DataPtrs[Base+i]), NULL, false, false, true, false, MAX_NUMBER_OF_TABLES))
|
|
{
|
|
Error("Insufficent number of initializers");
|
|
}
|
|
Parse_Comma();
|
|
}
|
|
}
|
|
Parse_End();
|
|
Parse_Comma();
|
|
};
|
|
|
|
void Parser::Parse_Fopen(void)
|
|
{
|
|
IStream *rfile = NULL;
|
|
OStream *wfile = NULL;
|
|
DATA_FILE *New;
|
|
char *asciitemp;
|
|
UCS2String temp;
|
|
UCS2String ign;
|
|
SYM_ENTRY *Entry;
|
|
|
|
New=(DATA_FILE *)POV_MALLOC(sizeof(DATA_FILE),"user file");
|
|
New->In_File=NULL;
|
|
New->Out_File=NULL;
|
|
|
|
GET(IDENTIFIER_TOKEN)
|
|
Entry = Add_Symbol (1,Token.Token_String,FILE_ID_TOKEN);
|
|
Entry->Data=(void *)New;
|
|
|
|
asciitemp = Parse_C_String(true);
|
|
temp = ASCIItoUCS2String(asciitemp);
|
|
POV_FREE(asciitemp);
|
|
|
|
EXPECT
|
|
CASE(READ_TOKEN)
|
|
New->R_Flag = true;
|
|
rfile = Locate_File(this, sceneData, temp.c_str(), POV_File_Text_User, ign, true);
|
|
if(rfile != NULL)
|
|
New->In_File = new ITextStream(temp.c_str(), rfile);
|
|
else
|
|
New->In_File = NULL;
|
|
|
|
if(New->In_File == NULL)
|
|
Error ("Cannot open user file %s (read).", UCS2toASCIIString(temp).c_str());
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(WRITE_TOKEN)
|
|
New->R_Flag = false;
|
|
wfile = sceneData->CreateFile(GetPOVMSContext(), temp.c_str(), POV_File_Text_User, false);
|
|
if(wfile != NULL)
|
|
New->Out_File= new OTextStream(temp.c_str(), wfile);
|
|
else
|
|
New->Out_File = NULL;
|
|
|
|
if(New->Out_File == NULL)
|
|
Error ("Cannot open user file %s (write).", UCS2toASCIIString(temp).c_str());
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(APPEND_TOKEN)
|
|
New->R_Flag = false;
|
|
wfile = sceneData->CreateFile(GetPOVMSContext(), temp.c_str(), POV_File_Text_User, true);
|
|
if(wfile != NULL)
|
|
New->Out_File= new OTextStream(temp.c_str(), wfile);
|
|
else
|
|
New->Out_File = NULL;
|
|
|
|
if(New->Out_File == NULL)
|
|
Error ("Cannot open user file %s (append).", UCS2toASCIIString(temp).c_str());
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Expectation_Error("read or write");
|
|
END_CASE
|
|
END_EXPECT
|
|
}
|
|
|
|
void Parser::Parse_Fclose(void)
|
|
{
|
|
DATA_FILE *Data;
|
|
|
|
EXPECT
|
|
CASE(FILE_ID_TOKEN)
|
|
Data=(DATA_FILE *)Token.Data;
|
|
if(Data->In_File != NULL)
|
|
delete Data->In_File;
|
|
if(Data->Out_File != NULL)
|
|
delete Data->Out_File;
|
|
Got_EOF=false;
|
|
Data->In_File = NULL;
|
|
Data->Out_File = NULL;
|
|
Remove_Symbol (1,Token.Token_String,false,NULL,0);
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
EXIT
|
|
END_CASE
|
|
END_EXPECT
|
|
}
|
|
|
|
void Parser::Parse_Read()
|
|
{
|
|
DATA_FILE *User_File;
|
|
SYM_ENTRY *Temp_Entry;
|
|
int End_File=false;
|
|
char *File_Id;
|
|
|
|
GET(LEFT_PAREN_TOKEN)
|
|
|
|
GET(FILE_ID_TOKEN)
|
|
User_File=(DATA_FILE *)Token.Data;
|
|
File_Id=POV_STRDUP(Token.Token_String);
|
|
if(User_File->In_File == NULL)
|
|
Error("Cannot read from file %s because the file is open for writing only.", UCS2toASCIIString(UCS2String(User_File->Out_File->name())).c_str());
|
|
|
|
Parse_Comma(); /* Scene file comma between File_Id and 1st data ident */
|
|
|
|
LValue_Ok = true;
|
|
|
|
EXPECT
|
|
CASE (IDENTIFIER_TOKEN)
|
|
if (!End_File)
|
|
{
|
|
Temp_Entry = Add_Symbol (1,Token.Token_String,IDENTIFIER_TOKEN);
|
|
End_File=Parse_Read_Value (User_File,Token.Token_Id, &(Temp_Entry->Token_Number), &(Temp_Entry->Data));
|
|
Token.is_array_elem = false;
|
|
Parse_Comma(); /* Scene file comma between 2 idents */
|
|
}
|
|
END_CASE
|
|
|
|
CASE (STRING_ID_TOKEN)
|
|
if (!End_File)
|
|
{
|
|
End_File=Parse_Read_Value (User_File,Token.Token_Id,Token.NumberPtr,Token.DataPtr);
|
|
Token.is_array_elem = false;
|
|
Parse_Comma(); /* Scene file comma between 2 idents */
|
|
}
|
|
END_CASE
|
|
|
|
CASE2 (VECTOR_FUNCT_TOKEN,FLOAT_FUNCT_TOKEN)
|
|
switch(Token.Function_Id)
|
|
{
|
|
case VECTOR_ID_TOKEN:
|
|
case FLOAT_ID_TOKEN:
|
|
if (!End_File)
|
|
{
|
|
End_File=Parse_Read_Value (User_File,Token.Function_Id,Token.NumberPtr,Token.DataPtr);
|
|
Parse_Comma(); /* Scene file comma between 2 idents */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
break;
|
|
}
|
|
END_CASE
|
|
|
|
CASE(COMMA_TOKEN)
|
|
if (!End_File)
|
|
{
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
}
|
|
END_CASE
|
|
|
|
CASE(RIGHT_PAREN_TOKEN)
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
LValue_Ok = false;
|
|
|
|
if (End_File)
|
|
{
|
|
delete User_File->In_File;
|
|
Got_EOF=false;
|
|
User_File->In_File = NULL;
|
|
Remove_Symbol (1,File_Id,false,NULL,0);
|
|
}
|
|
POV_FREE(File_Id);
|
|
}
|
|
|
|
int Parser::Parse_Read_Value(DATA_FILE *User_File,int Previous,int *NumberPtr,void **DataPtr)
|
|
{
|
|
pov_base::ITextStream *Temp;
|
|
bool Temp_R_Flag;
|
|
DBL Val;
|
|
int End_File=false;
|
|
int i;
|
|
EXPRESS Express;
|
|
|
|
Temp = Input_File->In_File;
|
|
Temp_R_Flag = Input_File->R_Flag;
|
|
Input_File->In_File = User_File->In_File;
|
|
Input_File->R_Flag = User_File->R_Flag;
|
|
if(User_File->In_File == NULL)
|
|
Error("Cannot read from file '%s' because the file is open for writing only.", UCS2toASCIIString(UCS2String(User_File->Out_File->name())).c_str());
|
|
User_File->In_File = NULL; // take control over pointer
|
|
|
|
try
|
|
{
|
|
EXPECT
|
|
CASE3 (PLUS_TOKEN,DASH_TOKEN,FLOAT_FUNCT_TOKEN)
|
|
UNGET
|
|
Val=Parse_Signed_Float();
|
|
*NumberPtr = FLOAT_ID_TOKEN;
|
|
Test_Redefine(Previous,NumberPtr,*DataPtr);
|
|
*DataPtr = (void *) Create_Float();
|
|
*((DBL *)*DataPtr) = Val;
|
|
Parse_Comma(); /* data file comma between 2 data items */
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (LEFT_ANGLE_TOKEN)
|
|
i=1;
|
|
Express[X]=Parse_Signed_Float(); Parse_Comma();
|
|
Express[Y]=Parse_Signed_Float(); Parse_Comma();
|
|
|
|
EXPECT
|
|
CASE3 (PLUS_TOKEN,DASH_TOKEN,FLOAT_FUNCT_TOKEN)
|
|
UNGET
|
|
if (++i>4)
|
|
{
|
|
Error("Vector data too long");
|
|
}
|
|
Express[i]=Parse_Signed_Float(); Parse_Comma();
|
|
END_CASE
|
|
|
|
CASE (RIGHT_ANGLE_TOKEN)
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Expectation_Error("vector");
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
switch(i)
|
|
{
|
|
case 1:
|
|
*NumberPtr = UV_ID_TOKEN;
|
|
Test_Redefine(Previous,NumberPtr,*DataPtr);
|
|
*DataPtr = (void *) Create_UV_Vect();
|
|
Assign_UV_Vect((DBL *)*DataPtr, Express);
|
|
break;
|
|
|
|
case 2:
|
|
*NumberPtr = VECTOR_ID_TOKEN;
|
|
Test_Redefine(Previous,NumberPtr,*DataPtr);
|
|
*DataPtr = (void *) Create_Vector();
|
|
Assign_Vector((DBL *)*DataPtr, Express);
|
|
break;
|
|
|
|
case 3:
|
|
*NumberPtr = VECTOR_4D_ID_TOKEN;
|
|
Test_Redefine(Previous,NumberPtr,*DataPtr);
|
|
*DataPtr = (void *) Create_Vector_4D();
|
|
Assign_Vector_4D((DBL *)*DataPtr, Express);
|
|
break;
|
|
|
|
case 4:
|
|
*NumberPtr = COLOUR_ID_TOKEN;
|
|
Test_Redefine(Previous,NumberPtr,*DataPtr);
|
|
*DataPtr = (void *) Create_Colour();
|
|
Assign_Colour_Express((COLC*)(*DataPtr), Express); /* NK fix assign_colour bug */
|
|
break;
|
|
}
|
|
|
|
Parse_Comma(); // data file comma between 2 data items
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE(STRING_LITERAL_TOKEN)
|
|
*NumberPtr = STRING_ID_TOKEN;
|
|
Test_Redefine(Previous,NumberPtr,*DataPtr);
|
|
*DataPtr = String_To_UCS2(Token.Token_String, false);
|
|
Parse_Comma(); // data file comma between 2 data items
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (END_OF_FILE_TOKEN)
|
|
EXIT
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Expectation_Error ("float, vector, or string literal");
|
|
END_CASE
|
|
END_EXPECT
|
|
}
|
|
catch (...)
|
|
{
|
|
// re-assign the file pointers so that they are properly disposed of later on
|
|
User_File->In_File = Input_File->In_File;
|
|
Input_File->In_File = Temp;
|
|
Input_File->R_Flag = Temp_R_Flag;
|
|
throw;
|
|
}
|
|
|
|
if (Token.Token_Id==END_OF_FILE_TOKEN)
|
|
End_File = true;
|
|
|
|
Token.End_Of_File = false;
|
|
Token.Unget_Token = false;
|
|
Got_EOF = false;
|
|
User_File->In_File = Input_File->In_File; // return control over pointer
|
|
Input_File->In_File = Temp;
|
|
Input_File->R_Flag = Temp_R_Flag;
|
|
|
|
return End_File ;
|
|
}
|
|
|
|
void Parser::Parse_Write(void)
|
|
{
|
|
char *temp;
|
|
DATA_FILE *User_File;
|
|
EXPRESS Express;
|
|
int Terms;
|
|
|
|
GET(LEFT_PAREN_TOKEN)
|
|
GET(FILE_ID_TOKEN)
|
|
|
|
User_File=(DATA_FILE *)Token.Data;
|
|
if(User_File->Out_File == NULL)
|
|
Error("Cannot write to file %s because the file is open for reading only.", UCS2toASCIIString(UCS2String(User_File->In_File->name())).c_str());
|
|
|
|
Parse_Comma();
|
|
|
|
EXPECT
|
|
CASE5 (SINT8_TOKEN,SINT16BE_TOKEN,SINT16LE_TOKEN,SINT32BE_TOKEN,SINT32LE_TOKEN)
|
|
CASE3 (UINT8_TOKEN,UINT16BE_TOKEN,UINT16LE_TOKEN)
|
|
{
|
|
signed long val_min;
|
|
signed long val_max;
|
|
int num_bytes;
|
|
bool big_endian = false;
|
|
switch (Token.Token_Id)
|
|
{
|
|
case SINT8_TOKEN: val_min = -128; val_max = 127; num_bytes = 1; break; // -2^7 to 2^7-1
|
|
case UINT8_TOKEN: val_min = 0; val_max = 255; num_bytes = 1; break; // 0 to 2^8-1
|
|
case SINT16BE_TOKEN: big_endian = true; // FALLTHROUGH
|
|
case SINT16LE_TOKEN: val_min = -32768; val_max = 32767; num_bytes = 2; break; // -2^15 to 2^15-1
|
|
case UINT16BE_TOKEN: big_endian = true; // FALLTHROUGH
|
|
case UINT16LE_TOKEN: val_min = 0; val_max = 65535; num_bytes = 2; break; // 0 to 2^16-1
|
|
case SINT32BE_TOKEN: big_endian = true; // FALLTHROUGH
|
|
case SINT32LE_TOKEN: val_min = (-2147483647-1); val_max = 2147483647; num_bytes = 4; break; // -2^31 to 2^31-1 (using unconventional notation to avoid a warning with some compiler)
|
|
}
|
|
EXPECT
|
|
CASE_VECTOR
|
|
Terms = Parse_Unknown_Vector (Express);
|
|
if ((Terms >= 1) && (Terms <= 5))
|
|
{
|
|
for (int i = 0; i < Terms; i ++)
|
|
{
|
|
signed long val;
|
|
if (Express[i] <= val_min)
|
|
val = val_min; // TODO - maybe we should warn the user
|
|
else if (Express[i] >= val_max)
|
|
val = val_max; // TODO - maybe we should warn the user
|
|
else
|
|
val = (signed long)(floor(Express[i]+0.5));
|
|
for (int j = 0; j < num_bytes; j ++)
|
|
{
|
|
int bitShift = (big_endian? (num_bytes-1)-j : j) * 8;
|
|
User_File->Out_File->putraw((val >> bitShift) & 0xFF);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Expectation_Error("expression");
|
|
}
|
|
END_CASE
|
|
CASE (RIGHT_PAREN_TOKEN)
|
|
UNGET
|
|
EXIT
|
|
END_CASE
|
|
CASE (COMMA_TOKEN)
|
|
UNGET
|
|
EXIT
|
|
END_CASE
|
|
OTHERWISE
|
|
Expectation_Error("expression");
|
|
END_CASE
|
|
END_EXPECT
|
|
}
|
|
END_CASE
|
|
|
|
CASE5 (STRING_LITERAL_TOKEN,CHR_TOKEN,SUBSTR_TOKEN,STR_TOKEN,VSTR_TOKEN)
|
|
CASE5 (CONCAT_TOKEN,STRUPR_TOKEN,STRLWR_TOKEN,DATETIME_TOKEN,STRING_ID_TOKEN)
|
|
UNGET
|
|
temp=Parse_C_String();
|
|
if(strlen(temp) > 512)
|
|
{
|
|
for(char *ptr = temp; *ptr != 0; ptr++)
|
|
User_File->Out_File->printf("%c", *ptr);
|
|
}
|
|
else
|
|
User_File->Out_File->printf("%s", temp);
|
|
POV_FREE(temp);
|
|
END_CASE
|
|
|
|
CASE_VECTOR
|
|
Terms = Parse_Unknown_Vector (Express);
|
|
switch (Terms)
|
|
{
|
|
case 1:
|
|
User_File->Out_File->printf("%g",Express[X]);
|
|
break;
|
|
|
|
case 2:
|
|
User_File->Out_File->printf("<%g,%g> ",Express[U],Express[V]);
|
|
break;
|
|
|
|
case 3:
|
|
User_File->Out_File->printf("<%g,%g,%g> ",Express[X],Express[Y],Express[Z]);
|
|
break;
|
|
|
|
case 4:
|
|
User_File->Out_File->printf("<%g,%g,%g,%g> ",Express[X],Express[Y],Express[Z],Express[T]);
|
|
break;
|
|
|
|
case 5:
|
|
User_File->Out_File->printf("<%g,%g,%g,%g,%g> ",Express[X],Express[Y],Express[Z],Express[3],Express[4]);
|
|
break;
|
|
|
|
default:
|
|
Expectation_Error("expression");
|
|
}
|
|
END_CASE
|
|
|
|
CASE (RIGHT_PAREN_TOKEN)
|
|
EXIT
|
|
END_CASE
|
|
|
|
CASE (COMMA_TOKEN)
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
Expectation_Error("string");
|
|
END_CASE
|
|
END_EXPECT
|
|
}
|
|
|
|
DBL Parser::Parse_Cond_Param(void)
|
|
{
|
|
int Old_Ok = Ok_To_Declare;
|
|
int Old_Sk = Skipping;
|
|
DBL Val;
|
|
|
|
Ok_To_Declare = false;
|
|
Skipping = false;
|
|
|
|
Val=Parse_Float_Param();
|
|
|
|
Ok_To_Declare = Old_Ok;
|
|
Skipping = Old_Sk;
|
|
|
|
return(Val);
|
|
}
|
|
|
|
void Parser::Parse_Cond_Param2(DBL *V1,DBL *V2)
|
|
{
|
|
int Old_Ok = Ok_To_Declare;
|
|
int Old_Sk = Skipping;
|
|
|
|
Ok_To_Declare = false;
|
|
Skipping = false;
|
|
|
|
Parse_Float_Param2(V1,V2);
|
|
|
|
Ok_To_Declare = Old_Ok;
|
|
Skipping = Old_Sk;
|
|
}
|
|
|
|
void Parser::Inc_CS_Index(void)
|
|
{
|
|
if (++CS_Index >= COND_STACK_SIZE)
|
|
{
|
|
Error("Too many nested conditionals or macros.");
|
|
}
|
|
Cond_Stack[CS_Index].Macro_File = NULL;
|
|
Cond_Stack[CS_Index].Macro_Return_Name = NULL;
|
|
Cond_Stack[CS_Index].PMac = NULL;
|
|
Cond_Stack[CS_Index].Loop_File = NULL;
|
|
Cond_Stack[CS_Index].Loop_Identifier = NULL;
|
|
}
|
|
|
|
int Parser::Parse_Ifdef_Param (void)
|
|
{
|
|
int Local_Index;
|
|
SYM_ENTRY *Entry;
|
|
int retval = false;
|
|
int i,j,k;
|
|
register int c;
|
|
DBL val;
|
|
POV_ARRAY *a;
|
|
|
|
GET(LEFT_PAREN_TOKEN)
|
|
Inside_Ifdef=true;
|
|
Get_Token();
|
|
String2 = POV_STRDUP(String);
|
|
Inside_Ifdef=false;
|
|
|
|
/* Search tables from newest to oldest */
|
|
for (Local_Index=Table_Index; Local_Index > 0; Local_Index--)
|
|
{
|
|
Entry = Find_Symbol(Local_Index,String2);
|
|
if ( Entry != NULL)
|
|
{
|
|
Token.Token_Id = Entry->Token_Number;
|
|
Token.is_array_elem = false;
|
|
Token.NumberPtr = &(Entry->Token_Number);
|
|
Token.DataPtr = &(Entry->Data);
|
|
|
|
if ( Token.Token_Id == PARAMETER_ID_TOKEN )
|
|
{
|
|
Token.NumberPtr = ((POV_PARAM *)(Entry->Data))->NumberPtr;
|
|
Token.DataPtr = ((POV_PARAM *)(Entry->Data))->DataPtr;
|
|
}
|
|
|
|
if (Token.NumberPtr && *(Token.NumberPtr)==ARRAY_ID_TOKEN)
|
|
{
|
|
Skip_Spaces();
|
|
c = Echo_getc();
|
|
Echo_ungetc(c);
|
|
|
|
if (c!='[')
|
|
{
|
|
retval = true;
|
|
break;
|
|
}
|
|
|
|
a = (POV_ARRAY *)(*(Token.DataPtr));
|
|
j = 0;
|
|
|
|
for (i=0; i <= a->Dims; i++)
|
|
{
|
|
GET(LEFT_SQUARE_TOKEN)
|
|
val=Parse_Float();
|
|
|
|
k=(int)(val + EPSILON);
|
|
|
|
if (k<0.0)
|
|
{
|
|
Error("Negative subscript");
|
|
}
|
|
|
|
if (k>=a->Sizes[i])
|
|
{
|
|
Error("Array subscript out of range");
|
|
}
|
|
j += k * a->Mags[i];
|
|
GET(RIGHT_SQUARE_TOKEN)
|
|
}
|
|
|
|
Token.DataPtr = &(a->DataPtrs[j]);
|
|
Token.NumberPtr = &(a->Type);
|
|
Token.Token_Id = a->Type;
|
|
Token.is_array_elem = true;
|
|
|
|
retval = (*Token.DataPtr != NULL);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
retval = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
GET(RIGHT_PAREN_TOKEN)
|
|
|
|
POV_FREE(String2);
|
|
String2 = NULL;
|
|
|
|
return retval;
|
|
}
|
|
|
|
int Parser::Parse_For_Param (char** IdentifierPtr, DBL* EndPtr, DBL* StepPtr)
|
|
{
|
|
int Previous=-1;
|
|
SYM_ENTRY *Temp_Entry = NULL;
|
|
|
|
GET(LEFT_PAREN_TOKEN)
|
|
|
|
LValue_Ok = true;
|
|
|
|
EXPECT_ONE
|
|
CASE (IDENTIFIER_TOKEN)
|
|
if (Token.is_array_elem)
|
|
Error("#for loop variable must not be an array element");
|
|
Temp_Entry = Add_Symbol (Table_Index,Token.Token_String,IDENTIFIER_TOKEN);
|
|
Token.NumberPtr = &(Temp_Entry->Token_Number);
|
|
Token.DataPtr = &(Temp_Entry->Data);
|
|
Previous = Token.Token_Id;
|
|
END_CASE
|
|
|
|
CASE2 (FUNCT_ID_TOKEN, VECTFUNCT_ID_TOKEN)
|
|
if (Token.is_array_elem)
|
|
Error("#for loop variable must not be an array element");
|
|
if((!Token.is_array_elem) || (*(Token.DataPtr) != NULL))
|
|
Error("Redeclaring functions is not allowed - #undef the function first!");
|
|
// fall through
|
|
|
|
// These have to match Parse_Declare in parse.cpp!
|
|
CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
|
|
CASE4 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN, PIGMENT_ID_TOKEN)
|
|
CASE4 (SLOPE_MAP_ID_TOKEN, NORMAL_MAP_ID_TOKEN, TEXTURE_MAP_ID_TOKEN, COLOUR_ID_TOKEN)
|
|
CASE4 (PIGMENT_MAP_ID_TOKEN, MEDIA_ID_TOKEN, STRING_ID_TOKEN, INTERIOR_ID_TOKEN)
|
|
CASE4 (DENSITY_MAP_ID_TOKEN, ARRAY_ID_TOKEN, DENSITY_ID_TOKEN, UV_ID_TOKEN)
|
|
CASE4 (VECTOR_4D_ID_TOKEN, RAINBOW_ID_TOKEN, FOG_ID_TOKEN, SKYSPHERE_ID_TOKEN)
|
|
CASE2 (MATERIAL_ID_TOKEN, SPLINE_ID_TOKEN )
|
|
if (Token.is_array_elem)
|
|
Error("#for loop variable must not be an array element");
|
|
if (Token.Table_Index != Table_Index)
|
|
{
|
|
Temp_Entry = Add_Symbol (Table_Index,Token.Token_String,IDENTIFIER_TOKEN);
|
|
Token.NumberPtr = &(Temp_Entry->Token_Number);
|
|
Token.DataPtr = &(Temp_Entry->Data);
|
|
Previous = IDENTIFIER_TOKEN;
|
|
}
|
|
else
|
|
{
|
|
Previous = Token.Token_Id;
|
|
}
|
|
END_CASE
|
|
|
|
CASE (EMPTY_ARRAY_TOKEN)
|
|
if (Token.is_array_elem)
|
|
Error("#for loop variable must not be an array element");
|
|
Previous = Token.Token_Id;
|
|
END_CASE
|
|
|
|
CASE2 (VECTOR_FUNCT_TOKEN, FLOAT_FUNCT_TOKEN)
|
|
if (Token.is_array_elem)
|
|
Error("#for loop variable must not be an array element");
|
|
switch(Token.Function_Id)
|
|
{
|
|
case VECTOR_ID_TOKEN:
|
|
case FLOAT_ID_TOKEN:
|
|
if (Token.Table_Index != Table_Index)
|
|
{
|
|
Temp_Entry = Add_Symbol (Table_Index,Token.Token_String,IDENTIFIER_TOKEN);
|
|
Token.NumberPtr = &(Temp_Entry->Token_Number);
|
|
Token.DataPtr = &(Temp_Entry->Data);
|
|
}
|
|
Previous = Token.Function_Id;
|
|
break;
|
|
|
|
default:
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
break;
|
|
}
|
|
END_CASE
|
|
|
|
OTHERWISE
|
|
if (Token.is_array_elem)
|
|
Error("#for loop variable must not be an array element");
|
|
Parse_Error(IDENTIFIER_TOKEN);
|
|
END_CASE
|
|
END_EXPECT
|
|
|
|
LValue_Ok = false;
|
|
|
|
*Token.NumberPtr = FLOAT_ID_TOKEN;
|
|
Test_Redefine(Previous,Token.NumberPtr,*Token.DataPtr, true);
|
|
*Token.DataPtr = (void *) Create_Float();
|
|
DBL* CurrentPtr = ((DBL *)*Token.DataPtr);
|
|
|
|
size_t len = strlen(Token.Token_String)+1;
|
|
*IdentifierPtr = (char*)POV_MALLOC(len, "loop identifier");
|
|
memcpy(*IdentifierPtr, Token.Token_String, len);
|
|
|
|
Parse_Comma();
|
|
*CurrentPtr = Parse_Float();
|
|
Parse_Comma();
|
|
*EndPtr = Parse_Float();
|
|
Parse_Comma();
|
|
*StepPtr = Allow_Float(1.0);
|
|
|
|
if (fabs(*StepPtr) < EPSILON)
|
|
Error ("#for loop increment must be non-zero.");
|
|
|
|
GET(RIGHT_PAREN_TOKEN)
|
|
|
|
return ((*StepPtr > 0) && (*CurrentPtr < *EndPtr + EPSILON)) ||
|
|
((*StepPtr < 0) && (*CurrentPtr > *EndPtr - EPSILON));
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* FUNCTION
|
|
*
|
|
* INPUT
|
|
*
|
|
* OUTPUT
|
|
*
|
|
* RETURNS
|
|
*
|
|
* AUTHOR
|
|
*
|
|
* DESCRIPTION
|
|
*
|
|
* CHANGES
|
|
*
|
|
******************************************************************************/
|
|
|
|
void Parser::IncludeHeader(const UCS2String& temp)
|
|
{
|
|
UCS2String b;
|
|
|
|
if (temp.empty())
|
|
return;
|
|
|
|
if (++Include_File_Index >= MAX_INCLUDE_FILES)
|
|
{
|
|
Include_File_Index--;
|
|
Error ("Too many nested include files.");
|
|
}
|
|
|
|
Echo_Indx = 0;
|
|
|
|
Input_File = &Include_Files[Include_File_Index];
|
|
Input_File->In_File = NULL;
|
|
IStream *is = Locate_File (this, sceneData, temp.c_str(),POV_File_Text_INC,b,true);
|
|
if(is == NULL)
|
|
{
|
|
Input_File->In_File = NULL; /* Keeps from closing failed file. */
|
|
Error ("Cannot open include header file %s.", UCS2toASCIIString(temp).c_str());
|
|
}
|
|
else
|
|
Input_File->In_File = new ITextStream(b.c_str(), is);
|
|
|
|
Input_File->R_Flag=false;
|
|
|
|
Add_Sym_Table();
|
|
|
|
Token.Token_Id = END_OF_FILE_TOKEN;
|
|
Token.is_array_elem = false;
|
|
}
|
|
|
|
}
|