povray/source/backend/parser/tokenize.cpp
2013-11-06 13:07:19 -05:00

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;
}
}