1 /*******************************************************************************
2  * tokenize.cpp
3  *
4  * This module implements the first part of a two part parser for the scene
5  * description files.  This phase changes the input file into tokens.
6  *
7  * This module tokenizes the input file and sends the tokens created
8  * to the parser (the second stage).  Tokens sent to the parser contain a
9  * token ID, the line number of the token, and if necessary, some data for
10  * the token.
11  *
12  * ---------------------------------------------------------------------------
13  * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
14  * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
15  *
16  * POV-Ray is free software: you can redistribute it and/or modify
17  * it under the terms of the GNU Affero General Public License as
18  * published by the Free Software Foundation, either version 3 of the
19  * License, or (at your option) any later version.
20  *
21  * POV-Ray is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU Affero General Public License for more details.
25  *
26  * You should have received a copy of the GNU Affero General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  * ---------------------------------------------------------------------------
29  * POV-Ray is based on the popular DKB raytracer version 2.12.
30  * DKBTrace was originally written by David K. Buck.
31  * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
32  * ---------------------------------------------------------------------------
33  * $File: //depot/public/povray/3.x/source/backend/parser/tokenize.cpp $
34  * $Revision: #1 $
35  * $Change: 6069 $
36  * $DateTime: 2013/11/06 11:59:40 $
37  * $Author: chrisc $
38  *******************************************************************************/
39 
40 #include <ctype.h>
41 
42 // frame.h must always be the first POV file included (pulls in platform config)
43 #include "backend/frame.h"
44 #include "base/povmsgid.h"
45 #include "base/stringutilities.h"
46 #include "backend/parser/parse.h"
47 #include "backend/colour/colour.h"
48 #include "backend/texture/texture.h"
49 #include "backend/math/matrices.h"
50 #include "backend/support/fileutil.h"
51 #include "backend/support/msgutil.h"
52 
53 // this must be the last file included
54 #include "base/povdebug.h"
55 
56 namespace pov
57 {
58 
59 using namespace pov_base;
60 
61 /*****************************************************************************
62 * Local preprocessor defines
63 ******************************************************************************/
64 
65 #define CALL(x) { if (!(x)) return (false); }
66 
67 /*****************************************************************************
68 *
69 * FUNCTION
70 *
71 * INPUT
72 *
73 * OUTPUT
74 *
75 * RETURNS
76 *
77 * AUTHOR
78 *
79 * DESCRIPTION
80 *
81 * CHANGES
82 *
83 ******************************************************************************/
84 
Initialize_Tokenizer()85 void Parser::Initialize_Tokenizer()
86 {
87 	IStream *rfile = NULL;
88 	UCS2String b;
89 	int c;
90 
91 	pre_init_tokenizer ();
92 
93 	rfile = Locate_File(this, sceneData, sceneData->inputFile.c_str(),POV_File_Text_POV,b,true);
94 	if(rfile != NULL)
95 	{
96 		Input_File->In_File = new ITextStream(b.c_str(), rfile);
97 		sceneData->inputFile = b;
98 	}
99 
100 	if (Input_File->In_File == NULL)
101 	{
102 		Error ("Cannot open input file.");
103 	}
104 
105 	Input_File->R_Flag = false;
106 
107 	Got_EOF  = false;
108 
109 	/* Init conditional stack. */
110 
111 	Cond_Stack = (CS_ENTRY*)POV_MALLOC(sizeof(CS_ENTRY) * COND_STACK_SIZE, "conditional stack");
112 
113 	Cond_Stack[0].Cond_Type    = ROOT_COND;
114 	Cond_Stack[0].Switch_Value = 0.0;
115 
116 	init_sym_tables();
117 	Max_Trace_Level = MAX_TRACE_LEVEL_DEFAULT;
118 	Had_Max_Trace_Level = false;
119 
120 	/* ignore any leading characters if they have character codes above 127, this
121 	   takes care of UTF-8 files with encoding info at the beginning of the file */
122 	for(c = Echo_getc(); c > 127; c = Echo_getc())
123 		sceneData->stringEncoding = 1; // switch to UTF-8 automatically [trf]
124 	Echo_ungetc(c);
125 }
126 
127 
128 
129 /*****************************************************************************
130 *
131 * FUNCTION
132 *
133 * INPUT
134 *
135 * OUTPUT
136 *
137 * RETURNS
138 *
139 * AUTHOR
140 *
141 * DESCRIPTION
142 *
143 * CHANGES
144 *
145 ******************************************************************************/
146 
pre_init_tokenizer()147 void Parser::pre_init_tokenizer ()
148 {
149 	int i;
150 
151 	Token.Token_File_Pos.lineno = 0;
152 	Token.Token_File_Pos.offset = 0;
153 	Token.Token_Col_No = 0;
154 	Token.Token_String  = NULL;
155 	Token.Unget_Token   = false;
156 	Token.End_Of_File   = false;
157 	Token.Data = NULL;
158 	Token.FileHandle = NULL;
159 
160 	line_count = 10;
161 	token_count = 0;
162 	Current_Token_Count = 0;
163 	Include_File_Index = 0;
164 	Echo_Indx=0;
165 
166 	// make sure these are NULL otherwise cleanup() will crash if we terminate early
167 	Default_Texture = NULL;
168 	Brace_Stack = NULL;
169 
170 	CS_Index            = 0;
171 	Skipping            = false;
172 	Inside_Ifdef        = false;
173 	Inside_MacroDef     = false;
174 	Cond_Stack          = NULL;
175 	Table_Index         = -1;
176 
177 	Input_File = &Include_Files[0];
178 	Include_Files[0].In_File = NULL ;
179 
180 	for(i = 0; i < LAST_TOKEN; i++)
181 	{
182 		Conversion_Util_Table[i] = i;
183 		if(i < FLOAT_FUNCT_TOKEN)
184 			Conversion_Util_Table[i] = FLOAT_FUNCT_TOKEN;
185 		else
186 		{
187 			if(i < VECTOR_FUNCT_TOKEN)
188 				Conversion_Util_Table[i] = VECTOR_FUNCT_TOKEN;
189 			else
190 			{
191 				if(i < COLOUR_KEY_TOKEN)
192 					Conversion_Util_Table[i] = COLOUR_KEY_TOKEN;
193 			}
194 		}
195 	}
196 }
197 
198 
199 /*****************************************************************************
200 *
201 * FUNCTION
202 *
203 * INPUT
204 *
205 * OUTPUT
206 *
207 * RETURNS
208 *
209 * AUTHOR
210 *
211 * DESCRIPTION
212 *
213 * CHANGES
214 *
215 ******************************************************************************/
216 
Terminate_Tokenizer()217 void Parser::Terminate_Tokenizer()
218 {
219 	Token.FileHandle = NULL ;
220 
221 	while(Table_Index >= 0)
222 	{
223 		Destroy_Table(Table_Index--);
224 	}
225 
226 	if(Input_File->In_File != NULL)
227 	{
228 		delete Input_File->In_File;
229 		Input_File->In_File = NULL;
230 		Got_EOF = false;
231 	}
232 
233 	while(Include_File_Index >= 0)
234 	{
235 		Input_File = &Include_Files[Include_File_Index--];
236 
237 		if(Input_File->In_File != NULL)
238 		{
239 			delete Input_File->In_File;
240 			Input_File->In_File = NULL;
241 			Got_EOF = false;
242 		}
243 	}
244 
245 	if(Cond_Stack != NULL)
246 	{
247 		for(int i = 0; i <= CS_Index; i++)
248 		{
249 			if((Cond_Stack[i].Cond_Type == INVOKING_MACRO_COND) && (Cond_Stack[i].Macro_Same_Flag == false))
250 				delete Cond_Stack[i].Macro_File;
251 		}
252 		POV_FREE(Cond_Stack);
253 
254 		Cond_Stack = NULL;
255 	}
256 
257 	if((String != NULL) && (String != String_Fast_Buffer))
258 		POV_FREE(String);
259 	String=NULL;
260 
261 	if((String2 != NULL) && (String2 != String_Fast_Buffer))
262 		POV_FREE(String2);
263 	String2=NULL;
264 }
265 
266 
267 
268 /*****************************************************************************
269 *
270 * FUNCTION
271 *
272 * INPUT
273 *
274 * OUTPUT
275 *
276 * RETURNS
277 *
278 * AUTHOR
279 *
280 * DESCRIPTION
281 *
282 *   The main tokenizing routine.  Set up the files and continue parsing
283 *   until the end of file
284 *
285 *   Read a token from the input file and store it in the Token variable.
286 *   If the token is an INCLUDE token, then set the include file name and
287 *   read another token.
288 *
289 *   This function performs most of the work involved in tokenizing.  It
290 *   reads the first character of the token and decides which function to
291 *   call to tokenize the rest.  For simple tokens, it simply writes them
292 *   out to the token buffer.
293 *
294 * CHANGES
295 *
296 ******************************************************************************/
297 
Get_Token()298 void Parser::Get_Token ()
299 {
300 	int c,c2;
301 	int col;
302 
303 	if (Token.Unget_Token)
304 	{
305 		Token.Unget_Token = false;
306 
307 		return;
308 	}
309 
310 	if (Token.End_Of_File)
311 	{
312 		return;
313 	}
314 
315 	Token.Token_Id = END_OF_FILE_TOKEN;
316 	Token.is_array_elem = false;
317 
318 	while (Token.Token_Id == END_OF_FILE_TOKEN)
319 	{
320 		Skip_Spaces();
321 
322 		Token.Token_Col_No = col = Echo_Indx;
323 		c = Echo_getc();
324 
325 		if (c == EOF)
326 		{
327 			if (Input_File->R_Flag)
328 			{
329 				Token.Token_Id = END_OF_FILE_TOKEN;
330 				Token.is_array_elem = false;
331 				Token.End_Of_File = true;
332 				return;
333 			}
334 
335 			if (Include_File_Index == 0)
336 			{
337 				if (CS_Index !=0)
338 					Error("End of file reached but #end expected.");
339 
340 				Token.Token_Id = END_OF_FILE_TOKEN;
341 				Token.is_array_elem = false;
342 
343 				Token.End_Of_File = true;
344 
345 				return;
346 			}
347 
348 			if (Input_File->In_File == Token.FileHandle)
349 				Token.FileHandle = NULL;
350 
351 			delete Input_File->In_File; /* added to fix open file buildup JLN 12/91 */
352 			Input_File->In_File = NULL ;
353 			Got_EOF=false;
354 
355 			Destroy_Table(Table_Index--);
356 
357 			Input_File = &Include_Files[--Include_File_Index];
358 			if (Token.FileHandle == NULL)
359 				Token.FileHandle = Input_File->In_File;
360 
361 			continue;
362 		}
363 
364 		Begin_String_Fast();
365 
366 		String[0] = c; /* This isn't necessary but helps debugging */
367 
368 		String[1] = '\0';
369 
370 		/*String_Index = 0;*/
371 
372 		switch (c)
373 		{
374 			case '\n':
375 				break;
376 
377 			case '{' :
378 				Write_Token (LEFT_CURLY_TOKEN, col);
379 				break;
380 
381 			case '}' :
382 				Write_Token (RIGHT_CURLY_TOKEN, col);
383 				break;
384 
385 			case '@' :
386 				Write_Token (AT_TOKEN, col);
387 				break;
388 
389 			case '&' :
390 				Write_Token (AMPERSAND_TOKEN, col);
391 				break;
392 
393 			case '`' :
394 				Write_Token (BACK_QUOTE_TOKEN, col);
395 				break;
396 
397 			case '\\':
398 				Write_Token (BACK_SLASH_TOKEN, col);
399 				break;
400 
401 			case '|' :
402 				Write_Token (BAR_TOKEN, col);
403 				break;
404 
405 			case ':' :
406 				Write_Token (COLON_TOKEN, col);
407 				break;
408 
409 			case ',' :
410 				Write_Token (COMMA_TOKEN, col);
411 				break;
412 
413 			case '-' :
414 				Write_Token (DASH_TOKEN, col);
415 				break;
416 
417 			case '$' :
418 				Write_Token (DOLLAR_TOKEN, col);
419 				break;
420 
421 			case '=' :
422 				Write_Token (EQUALS_TOKEN, col);
423 				break;
424 
425 			case '!' :
426 				c2 = Echo_getc();
427 				if (c2 == (int)'=')
428 				{
429 					Write_Token (REL_NE_TOKEN, col);
430 				}
431 				else
432 				{
433 					Echo_ungetc(c2);
434 					Write_Token (EXCLAMATION_TOKEN, col);
435 				}
436 				break;
437 
438 			case '#' :
439 				Parse_Directive(true);
440 				/* Write_Token (HASH_TOKEN, col);*/
441 				break;
442 
443 			case '^' :
444 				Write_Token (HAT_TOKEN, col);
445 				break;
446 
447 			case '<' :
448 				c2 = Echo_getc();
449 				if (c2 == (int)'=')
450 				{
451 					Write_Token (REL_LE_TOKEN, col);
452 				}
453 				else
454 				{
455 					Echo_ungetc(c2);
456 					Write_Token (LEFT_ANGLE_TOKEN, col);
457 				}
458 				break;
459 
460 			case '(' :
461 				Write_Token (LEFT_PAREN_TOKEN, col);
462 				break;
463 
464 			case '[' :
465 				Write_Token (LEFT_SQUARE_TOKEN, col);
466 				break;
467 
468 			case '%' :
469 				Write_Token (PERCENT_TOKEN, col);
470 				break;
471 
472 			case '+' :
473 				Write_Token (PLUS_TOKEN, col);
474 				break;
475 
476 			case '?' :
477 				Write_Token (QUESTION_TOKEN, col);
478 				break;
479 
480 			case '>' :
481 				c2 = Echo_getc();
482 				if (c2 == (int)'=')
483 				{
484 					Write_Token (REL_GE_TOKEN, col);
485 				}
486 				else
487 				{
488 					Echo_ungetc(c2);
489 					Write_Token (RIGHT_ANGLE_TOKEN, col);
490 				}
491 				break;
492 
493 			case ')' :
494 				Write_Token (RIGHT_PAREN_TOKEN, col);
495 				break;
496 
497 			case ']' :
498 				Write_Token (RIGHT_SQUARE_TOKEN, col);
499 				break;
500 
501 			case ';' : /* Parser doesn't use it, so let's ignore it */
502 				Write_Token (SEMI_COLON_TOKEN, col);
503 				break;
504 
505 			case '\'':
506 				Write_Token (SINGLE_QUOTE_TOKEN, col);
507 				break;
508 
509 				/* enable C++ style commenting */
510 			case '/' :
511 				c2 = Echo_getc();
512 				if(c2 != (int) '/' && c2 != (int) '*')
513 				{
514 					Echo_ungetc(c2);
515 					Write_Token (SLASH_TOKEN, col);
516 					break;
517 				}
518 				if(c2 == (int)'*')
519 				{
520 					Parse_C_Comments();
521 					break;
522 				}
523 				while(c2 != (int)'\n')
524 				{
525 					c2=Echo_getc();
526 					if(c2==EOF)
527 					{
528 						Echo_ungetc(c2);
529 						break;
530 					}
531 				}
532 				break;
533 
534 			case '*' :
535 				Write_Token (STAR_TOKEN, col);
536 				break;
537 
538 			case '~' :
539 				Write_Token (TILDE_TOKEN, col);
540 				break;
541 
542 			case '"' :
543 				Parse_String_Literal ();
544 				break;
545 
546 			case '0':
547 			case '1':
548 			case '2':
549 			case '3':
550 			case '4':
551 			case '5':
552 			case '6':
553 			case '7':
554 			case '8':
555 			case '9':
556 			case '.':
557 				Echo_ungetc(c);
558 				if (Read_Float () != true)
559 					return;
560 				break;
561 
562 			case 'a':
563 			case 'b':
564 			case 'c':
565 			case 'd':
566 			case 'e':
567 			case 'f':
568 			case 'g':
569 			case 'h':
570 			case 'i':
571 			case 'j':
572 			case 'k':
573 			case 'l':
574 			case 'm':
575 			case 'n':
576 			case 'o':
577 			case 'p':
578 			case 'q':
579 			case 'r':
580 			case 's':
581 			case 't':
582 			case 'u':
583 			case 'v':
584 			case 'w':
585 			case 'x':
586 			case 'y':
587 			case 'z':
588 
589 			case 'A':
590 			case 'B':
591 			case 'C':
592 			case 'D':
593 			case 'E':
594 			case 'F':
595 			case 'G':
596 			case 'H':
597 			case 'I':
598 			case 'J':
599 			case 'K':
600 			case 'L':
601 			case 'M':
602 			case 'N':
603 			case 'O':
604 			case 'P':
605 			case 'Q':
606 			case 'R':
607 			case 'S':
608 			case 'T':
609 			case 'U':
610 			case 'V':
611 			case 'W':
612 			case 'X':
613 			case 'Y':
614 			case 'Z':
615 			case '_':
616 				Echo_ungetc(c);
617 				Read_Symbol ();
618 				break;
619 			case '\t':
620 			case '\r':
621 			case '\032':   /* Control Z - EOF on many systems */
622 			case '\0':
623 				break;
624 
625 			default:
626 				Error("Illegal character in input file, value is %02x.", c);
627 				break;
628 		}
629 	}
630 
631 	Current_Token_Count++;
632 	token_count++;
633 
634 	if(token_count > TOKEN_OVERFLOW_RESET_COUNT) // NEVER, ever change the operator here! Other code using token_count depends on it!!! [trf]
635 	{
636 		token_count = 0;
637 
638 		if((ElapsedRealTime() - last_progress) > 1000) // update progress at most every second
639 		{
640 			POVMS_Object obj(kPOVObjectClass_ParserProgress);
641 			obj.SetLong(kPOVAttrib_RealTime, ElapsedRealTime());
642 			obj.SetLong(kPOVAttrib_CurrentTokenCount, Current_Token_Count);
643 			RenderBackend::SendSceneOutput(sceneData->sceneId, sceneData->frontendAddress, kPOVMsgIdent_Progress, obj);
644 
645 			Cooperate();
646 
647 			last_progress = ElapsedRealTime();
648 		}
649 	}
650 }
651 
652 
653 
654 /*****************************************************************************
655 *
656 * FUNCTION
657 *
658 * INPUT
659 *
660 * OUTPUT
661 *
662 * RETURNS
663 *
664 * AUTHOR
665 *
666 * DESCRIPTION
667 *
668 *   Mark that the token has been put back into the input stream.  The next
669 *   call to Get_Token will return the last-read token instead of reading a
670 *   new one from the file.
671 *
672 * CHANGES
673 *
674 ******************************************************************************/
675 
Unget_Token()676 void Parser::Unget_Token ()
677 {
678 	Token.Unget_Token = true;
679 }
680 
681 
682 
683 /*****************************************************************************
684 *
685 * FUNCTION
686 *
687 * INPUT
688 *
689 * OUTPUT
690 *
691 * RETURNS
692 *
693 * AUTHOR
694 *
695 * DESCRIPTION
696 *
697 *   Skip over spaces in the input file.
698 *
699 * CHANGES
700 *
701 ******************************************************************************/
702 
Skip_Spaces()703 bool Parser::Skip_Spaces()
704 {
705 	int c;
706 
707 	while(true)
708 	{
709 		c = Echo_getc();
710 
711 		if (c == EOF)
712 			return false;
713 
714 		if(!(isspace(c)))
715 			break;
716 	}
717 
718 	Echo_ungetc(c);
719 
720 	return true;
721 }
722 
723 
724 
725 /*****************************************************************************
726 *
727 * FUNCTION
728 *
729 * INPUT
730 *
731 * OUTPUT
732 *
733 * RETURNS
734 *
735 * AUTHOR
736 *
737 * DESCRIPTION
738 *
739 *   C style comments with asterik and slash - CdW 8/91.
740 *
741 * CHANGES
742 *
743 ******************************************************************************/
744 
Parse_C_Comments()745 int Parser::Parse_C_Comments()
746 {
747 	int c, c2;
748 	bool End_Of_Comment = false;
749 
750 	while(!End_Of_Comment)
751 	{
752 		c = Echo_getc();
753 
754 		if(c == EOF)
755 			Error("No */ closing comment found.");
756 
757 		if(c == (int) '*')
758 		{
759 			c2 = Echo_getc();
760 
761 			if(c2 != (int) '/')
762 				Echo_ungetc(c2);
763 			else
764 				End_Of_Comment = true;
765 		}
766 
767 		/* Check for and handle nested comments */
768 
769 		if(c == (int) '/')
770 		{
771 			c2 = Echo_getc();
772 
773 			if(c2 != (int) '*')
774 				Echo_ungetc(c2);
775 			else
776 				Parse_C_Comments();
777 		}
778 	}
779 
780 	return true;
781 }
782 
783 
784 
785 /* The following routines make it easier to handle strings.  They stuff
786    characters into a string buffer one at a time making all the proper
787    range checks.  Call Begin_String to start, Stuff_Character to put
788    characters in, and End_String to finish.  The String variable contains
789    the final string. */
790 
791 /*****************************************************************************
792 *
793 * FUNCTION
794 *
795 * INPUT
796 *
797 * OUTPUT
798 *
799 * RETURNS
800 *
801 * AUTHOR
802 *
803 * DESCRIPTION
804 *
805 * CHANGES
806 *
807 ******************************************************************************/
808 
Begin_String()809 inline void Parser::Begin_String()
810 {
811 	if((String != NULL) && (String != String_Fast_Buffer))
812 		POV_FREE(String);
813 
814 	String = (char *)POV_MALLOC(256, "C String");
815 	String_Buffer_Free = 256;
816 	String_Index = 0;
817 }
818 
819 
820 
821 /*****************************************************************************
822  *
823  * FUNCTION
824  *
825  * INPUT
826  *
827  * OUTPUT
828  *
829  * RETURNS
830  *
831  * AUTHOR
832  *
833  * DESCRIPTION
834  *
835  * CHANGES
836  *
837 ******************************************************************************/
838 
Stuff_Character(int chr)839 inline void Parser::Stuff_Character(int chr)
840 {
841 	if(String_Buffer_Free <= 0)
842 	{
843 		Error("String too long.");
844 // This caused too many problems with buffer overflows [trf]
845 //		String = (char *)POV_REALLOC(String, String_Index + 256, "String Literal Buffer");
846 //		String_Buffer_Free += 256;
847 	}
848 
849 	String[String_Index] = chr;
850 	String_Buffer_Free--;
851 	String_Index++;
852 }
853 
854 
855 
856 /*****************************************************************************
857 *
858 * FUNCTION
859 *
860 * INPUT
861 *
862 * OUTPUT
863 *
864 * RETURNS
865 *
866 * AUTHOR
867 *
868 * DESCRIPTION
869 *
870 * CHANGES
871 *
872 ******************************************************************************/
873 
End_String()874 inline void Parser::End_String()
875 {
876 	Stuff_Character(0);
877 
878 	if(String_Buffer_Free > 0)
879 		String = (char *)POV_REALLOC(String, String_Index, "String Literal Buffer");
880 
881 	String_Buffer_Free = 0;
882 }
883 
884 
885 
886 /*****************************************************************************
887 *
888 * FUNCTION
889 *
890 * INPUT
891 *
892 * OUTPUT
893 *
894 * RETURNS
895 *
896 * AUTHOR
897 *
898 * DESCRIPTION
899 *
900 * CHANGES
901 *
902 ******************************************************************************/
903 
Begin_String_Fast()904 inline void Parser::Begin_String_Fast()
905 {
906 	if((String != NULL) && (String != String_Fast_Buffer))
907 		POV_FREE(String);
908 
909 	String = String_Fast_Buffer;
910 	String_Index = 0;
911 }
912 
913 
914 
915 /*****************************************************************************
916  *
917  * FUNCTION
918  *
919  * INPUT
920  *
921  * OUTPUT
922  *
923  * RETURNS
924  *
925  * AUTHOR
926  *
927  * DESCRIPTION
928  *
929  * CHANGES
930  *
931 ******************************************************************************/
932 
Stuff_Character_Fast(int chr)933 inline void Parser::Stuff_Character_Fast(int chr)
934 {
935 	String[String_Index & MAX_STRING_LEN_MASK] = chr;
936 	String_Index++;
937 }
938 
939 
940 
941 /*****************************************************************************
942 *
943 * FUNCTION
944 *
945 * INPUT
946 *
947 * OUTPUT
948 *
949 * RETURNS
950 *
951 * AUTHOR
952 *
953 * DESCRIPTION
954 *
955 * CHANGES
956 *
957 ******************************************************************************/
958 
End_String_Fast()959 inline void Parser::End_String_Fast()
960 {
961 	Stuff_Character_Fast(0);
962 
963 	String_Index--; // Stuff_Character_Fast incremented this
964 
965 	if(String_Index != (String_Index & MAX_STRING_LEN_MASK))
966 		Error("String too long.");
967 }
968 
969 
970 
971 /*****************************************************************************
972  *
973  * FUNCTION
974  *
975  * INPUT
976  *
977  * OUTPUT
978  *
979  * RETURNS
980  *
981  * AUTHOR
982  *
983  * DESCRIPTION
984  *
985  *   Parse a string from the input file into a token.
986  *
987  * CHANGES
988  *
989 ******************************************************************************/
990 
Parse_String_Literal()991 void Parser::Parse_String_Literal()
992 {
993 	int c;
994 	int col = Echo_Indx;
995 
996 	Begin_String();
997 
998 	while(true)
999 	{
1000 		c = Echo_getc();
1001 
1002 		if(c == EOF)
1003 			Error("No end quote for string.");
1004 
1005 		if(c == '\\')
1006 		{
1007 			switch(c = Echo_getc())
1008 			{
1009 				case '\n':
1010 				case '\r':
1011 					Error("Unterminated string literal.");
1012 					break;
1013 				case '\"':
1014 					c = '\"';
1015 					break;
1016 				case EOF:
1017 					Error("No end quote for string.");
1018 					break;
1019 				default:
1020 					Stuff_Character('\\');
1021 			}
1022 
1023 			Stuff_Character(c);
1024 		}
1025 		else
1026 		{
1027 			if(c != (int)'"')
1028 				Stuff_Character(c);
1029 			else
1030 				break;
1031 		}
1032 	}
1033 
1034 	End_String();
1035 
1036 	Write_Token(STRING_LITERAL_TOKEN, col);
1037 
1038 	Token.Token_String = String;
1039 }
1040 
1041 
1042 
1043 /*****************************************************************************
1044 *
1045 * FUNCTION
1046 *
1047 * INPUT
1048 *
1049 * OUTPUT
1050 *
1051 * RETURNS
1052 *
1053 * AUTHOR
1054 *
1055 * DESCRIPTION
1056 *
1057 *   Read a float from the input file and tokenize it as one token. The phase
1058 *   variable is 0 for the first character, 1 for all subsequent characters
1059 *   up to the decimal point, 2 for all characters after the decimal
1060 *   point, 3 for the E+/- and 4 for the exponent.  This helps to insure
1061 *   that the number is formatted properly. E format added 9/91 CEY
1062 *
1063 * CHANGES
1064 *
1065 ******************************************************************************/
1066 
Read_Float()1067 bool Parser::Read_Float()
1068 {
1069 	int c, Phase;
1070 	bool Finished;
1071 	int col = Echo_Indx;
1072 
1073 	Finished = false;
1074 
1075 	Phase = 0;
1076 
1077 	Begin_String_Fast();
1078 
1079 	while (!Finished)
1080 	{
1081 		c = Echo_getc();
1082 
1083 		if (c == EOF)
1084 		{
1085 			Error ("Unexpected end of file.");
1086 		}
1087 
1088 		if (Phase > 1 && c == '.')
1089 		{
1090 			Error ("Unexpected additional '.' in floating-point number");
1091 		}
1092 
1093 		switch (Phase)
1094 		{
1095 			case 0:
1096 
1097 				Phase = 1;
1098 
1099 				if (isdigit(c))
1100 				{
1101 					Stuff_Character_Fast(c);
1102 				}
1103 				else
1104 				{
1105 					if (c == '.')
1106 					{
1107 						c = Echo_getc();
1108 
1109 						if (c == EOF)
1110 						{
1111 							Error ("Unexpected end of file");
1112 						}
1113 
1114 						if (isdigit(c))
1115 						{
1116 							Stuff_Character_Fast('0');
1117 							Stuff_Character_Fast('.');
1118 							Stuff_Character_Fast(c);
1119 
1120 							Phase = 2;
1121 						}
1122 						else
1123 						{
1124 							Echo_ungetc(c);
1125 
1126 							Write_Token (PERIOD_TOKEN, col);
1127 
1128 							return(true);
1129 						}
1130 					}
1131 					else
1132 					{
1133 						Error ("Invalid decimal number");
1134 					}
1135 				}
1136 
1137 				break;
1138 
1139 			case 1:
1140 				if (isdigit(c))
1141 				{
1142 					Stuff_Character_Fast(c);
1143 				}
1144 				else
1145 				{
1146 					if (c == (int) '.')
1147 					{
1148 						Stuff_Character_Fast(c); Phase = 2;
1149 					}
1150 					else
1151 					{
1152 						if ((c == 'e') || (c == 'E'))
1153 						{
1154 							Stuff_Character_Fast(c); Phase = 3;
1155 						}
1156 						else
1157 						{
1158 							Finished = true;
1159 						}
1160 					}
1161 				}
1162 
1163 				break;
1164 
1165 			case 2:
1166 
1167 				if (isdigit(c))
1168 				{
1169 					Stuff_Character_Fast(c);
1170 				}
1171 				else
1172 				{
1173 					if ((c == 'e') || (c == 'E'))
1174 					{
1175 						Stuff_Character_Fast(c); Phase = 3;
1176 					}
1177 					else
1178 					{
1179 						Finished = true;
1180 					}
1181 				}
1182 
1183 				break;
1184 
1185 			case 3:
1186 
1187 				if (isdigit(c) || (c == '+') || (c == '-'))
1188 				{
1189 					Stuff_Character_Fast(c); Phase = 4;
1190 				}
1191 				else
1192 				{
1193 					Finished = true;
1194 				}
1195 
1196 				break;
1197 
1198 			case 4:
1199 
1200 				if (isdigit(c))
1201 				{
1202 					Stuff_Character_Fast(c);
1203 				}
1204 				else
1205 				{
1206 					Finished = true;
1207 				}
1208 
1209 				break;
1210 		}
1211 	}
1212 
1213 	Echo_ungetc(c);
1214 
1215 	End_String_Fast();
1216 
1217 	Write_Token (FLOAT_TOKEN, col);
1218 
1219 	if (sscanf (String, DBL_FORMAT_STRING, &Token.Token_Float) == 0)
1220 	{
1221 		return (false);
1222 	}
1223 
1224 	return (true);
1225 }
1226 
1227 
1228 
1229 /*****************************************************************************
1230 *
1231 * FUNCTION
1232 *
1233 * INPUT
1234 *
1235 * OUTPUT
1236 *
1237 * RETURNS
1238 *
1239 * AUTHOR
1240 *
1241 * DESCRIPTION
1242 *
1243 *   Read in a symbol from the input file. Check to see if it is a reserved
1244 *   word. If it is, write out the appropriate token. Otherwise, write the
1245 *   symbol out to the symbol table and write out an IDENTIFIER token. An
1246 *   identifier token is a token whose token number is greater than the
1247 *   highest reserved word.
1248 *
1249 * CHANGES
1250 *
1251 ******************************************************************************/
1252 
Read_Symbol()1253 void Parser::Read_Symbol()
1254 {
1255 	int c;
1256 	int Local_Index,i,j,k;
1257 	POV_ARRAY *a;
1258 	SYM_ENTRY *Temp_Entry;
1259 	POV_PARAM *Par;
1260 	DBL val;
1261 
1262 	Begin_String_Fast();
1263 
1264 	while (true)
1265 	{
1266 		c = Echo_getc();
1267 
1268 		if (c == EOF)
1269 		{
1270 			Error ("Unexpected end of file.");
1271 		}
1272 
1273 		if (isalpha(c) || isdigit(c) || c == (int) '_')
1274 		{
1275 			Stuff_Character_Fast(c);
1276 		}
1277 		else
1278 		{
1279 			Echo_ungetc(c);
1280 
1281 			break;
1282 		}
1283 	}
1284 
1285 	End_String_Fast();
1286 
1287 	if (Inside_Ifdef)
1288 	{
1289 		Token.Token_Id = IDENTIFIER_TOKEN;
1290 		Token.is_array_elem = false;
1291 
1292 		return;
1293 	}
1294 
1295 	/* If its a reserved keyword, write it and return */
1296 	if ( (Temp_Entry = Find_Symbol(0,String)) != NULL)
1297 	{
1298 		Write_Token (Temp_Entry->Token_Number, Token.Token_Col_No);
1299 		return;
1300 	}
1301 
1302 	if (!Skipping)
1303 	{
1304 		/* Search tables from newest to oldest */
1305 		for (Local_Index=Table_Index; Local_Index > 0; Local_Index--)
1306 		{
1307 			/* See if it's a previously declared identifier. */
1308 			if ((Temp_Entry = Find_Symbol(Local_Index,String)) != NULL)
1309 			{
1310 				if ((Temp_Entry->Flags & (TF_DEPRECATED | TF_DEPRECATED_SHOWN)) == TF_DEPRECATED)
1311 				{
1312 					if ((Temp_Entry->Flags & TF_DEPRECATED_ONCE) != 0)
1313 						Temp_Entry->Flags |= TF_DEPRECATED_SHOWN;
1314 					Warning(0, "%s", Temp_Entry->Deprecation_Message);
1315 				}
1316 
1317 				if (Temp_Entry->Token_Number==MACRO_ID_TOKEN)
1318 				{
1319 					Token.Data = Temp_Entry->Data;
1320 					if (Ok_To_Declare)
1321 					{
1322 						Invoke_Macro();
1323 					}
1324 					else
1325 					{
1326 						Token.Token_Id=MACRO_ID_TOKEN;
1327 						Token.is_array_elem = false;
1328 						Token.NumberPtr = &(Temp_Entry->Token_Number);
1329 						Token.DataPtr   = &(Temp_Entry->Data);
1330 						Write_Token (Token.Token_Id, Token.Token_Col_No);
1331 
1332 						Token.Table_Index = Local_Index;
1333 					}
1334 					return;
1335 				}
1336 
1337 				Token.Token_Id  =   Temp_Entry->Token_Number;
1338 				Token.is_array_elem = false;
1339 				Token.NumberPtr = &(Temp_Entry->Token_Number);
1340 				Token.DataPtr   = &(Temp_Entry->Data);
1341 
1342 				while ((Token.Token_Id==PARAMETER_ID_TOKEN) ||
1343 				       (Token.Token_Id==ARRAY_ID_TOKEN))
1344 				{
1345 					if (Token.Token_Id==ARRAY_ID_TOKEN)
1346 					{
1347 						Skip_Spaces();
1348 						c = Echo_getc();
1349 						Echo_ungetc(c);
1350 
1351 						if (c!='[')
1352 						{
1353 							break;
1354 						}
1355 
1356 						a = (POV_ARRAY *)(*(Token.DataPtr));
1357 						j = 0;
1358 
1359 						for (i=0; i <= a->Dims; i++)
1360 						{
1361 							GET(LEFT_SQUARE_TOKEN)
1362 							val=Parse_Float();
1363 							k=(int)(val + EPSILON);
1364 
1365 							if ((k < 0) || (val < -EPSILON))
1366 							{
1367 								Error("Negative subscript");
1368 							}
1369 
1370 							if (k >= a->Sizes[i])
1371 							{
1372 								Error("Array subscript out of range");
1373 							}
1374 							j += k * a->Mags[i];
1375 							GET(RIGHT_SQUARE_TOKEN)
1376 						}
1377 
1378 						Token.DataPtr   = &(a->DataPtrs[j]);
1379 						Token.NumberPtr = &(a->Type);
1380 						Token.Token_Id = a->Type;
1381 						Token.is_array_elem = true;
1382 						if (!LValue_Ok)
1383 						{
1384 							if (*Token.DataPtr == NULL)
1385 								Error("Attempt to access uninitialized array element.");
1386 						}
1387 					}
1388 					else
1389 					{
1390 						Par             = (POV_PARAM *)(Temp_Entry->Data);
1391 						Token.Token_Id  = *(Par->NumberPtr);
1392 						Token.is_array_elem = false;
1393 						Token.NumberPtr = Par->NumberPtr;
1394 						Token.DataPtr   = Par->DataPtr;
1395 					}
1396 				}
1397 
1398 				Write_Token (Token.Token_Id, Token.Token_Col_No);
1399 
1400 				Token.Data        = *(Token.DataPtr);
1401 				Token.Table_Index = Local_Index;
1402 				return;
1403 			}
1404 		}
1405 	}
1406 
1407 	Write_Token(IDENTIFIER_TOKEN, Token.Token_Col_No);
1408 }
1409 
Write_Token(TOKEN Token_Id,int col)1410 inline void Parser::Write_Token (TOKEN Token_Id, int col)
1411 {
1412 	Token.Token_File_Pos = Input_File->In_File->tellg();
1413 	Token.Token_Col_No   = col;
1414 	Token.FileHandle     = Input_File->In_File;
1415 	Token.Token_String   = String;
1416 	Token.Data           = NULL;
1417 	Token.Token_Id       = Conversion_Util_Table[Token_Id];
1418 	Token.Function_Id    = Token_Id;
1419 }
1420 
1421 
1422 /*****************************************************************************
1423 *
1424 * FUNCTION
1425 *
1426 * INPUT
1427 *
1428 * OUTPUT
1429 *
1430 * RETURNS
1431 *
1432 * AUTHOR
1433 *
1434 * DESCRIPTION
1435 *
1436 * CHANGES
1437 *
1438 ******************************************************************************/
1439 
Get_Token_String(TOKEN Token_Id)1440 const char *Parser::Get_Token_String (TOKEN Token_Id)
1441 {
1442 	int i;
1443 
1444 	for (i = 0 ; i < LAST_TOKEN ; i++)
1445 		if (Reserved_Words[i].Token_Number == Token_Id)
1446 			return (Reserved_Words[i].Token_Name);
1447 	return ("");
1448 }
1449 
1450 
1451 
1452 
1453 /*****************************************************************************
1454 *
1455 * FUNCTION
1456 *
1457 * INPUT
1458 *
1459 * OUTPUT
1460 *
1461 * RETURNS
1462 *
1463 * AUTHOR
1464 *
1465 * DESCRIPTION
1466 *
1467 *   Return a list of keywords seperated with a '\n'. The last keyword does not
1468 *   have a LF after it. The caller is responsible for freeing the memory. This
1469 *   function is intended to be used by GUI implementations that need a keyword
1470 *   list for syntax-coloring edit windows.
1471 *
1472 * CHANGES
1473 *
1474 ******************************************************************************/
1475 
Get_Reserved_Words(const char * additional_words)1476 char *Parser::Get_Reserved_Words (const char *additional_words)
1477 {
1478 	int length = 0 ;
1479 	int i ;
1480 
1481 	for (i = 0; i < LAST_TOKEN; i++)
1482 	{
1483 		if (!isalpha (Reserved_Words [i].Token_Name [0]))
1484 			continue ;
1485 		if (strchr (Reserved_Words [i].Token_Name, ' ') != NULL)
1486 			continue ;
1487 		length += (int)strlen (Reserved_Words[i].Token_Name) + 1 ;
1488 	}
1489 
1490 	length += (int)strlen (additional_words) ;
1491 
1492 	char *result = (char *) POV_MALLOC (++length, "Keyword List") ;
1493 	strcpy (result, additional_words) ;
1494 	char *s = result + strlen (additional_words) ;
1495 
1496 	for (i = 0 ; i < LAST_TOKEN ; i++)
1497 	{
1498 		if (!isalpha (Reserved_Words [i].Token_Name [0]))
1499 			continue ;
1500 		if (strchr (Reserved_Words [i].Token_Name, ' ') != NULL)
1501 			continue ;
1502 		s += sprintf (s, "%s\n", Reserved_Words[i].Token_Name) ;
1503 	}
1504 	*--s = '\0' ;
1505 
1506 	return (result) ;
1507 }
1508 
1509 
1510 /*****************************************************************************
1511 *
1512 * FUNCTION
1513 *
1514 * INPUT
1515 *
1516 * OUTPUT
1517 *
1518 * RETURNS
1519 *
1520 * AUTHOR
1521 *
1522 * DESCRIPTION
1523 *
1524 * CHANGES
1525 *
1526 ******************************************************************************/
1527 
Echo_getc()1528 int Parser::Echo_getc()
1529 {
1530 	int c;
1531 
1532 	if((Input_File == NULL) || (Input_File->In_File == NULL) || (c = Input_File->In_File->getchar()) == EOF)
1533 	{
1534 		if (Got_EOF)
1535 			return EOF;
1536 		Got_EOF = true ;
1537 		Echo_Indx = 0 ;
1538 		return ('\n') ;
1539 	}
1540 
1541 	Echo_Indx++;
1542 	if(c == '\n')
1543 		Echo_Indx = 0;
1544 
1545 	return c;
1546 }
1547 
1548 
1549 
1550 /*****************************************************************************
1551 *
1552 * FUNCTION
1553 *
1554 * INPUT
1555 *
1556 * OUTPUT
1557 *
1558 * RETURNS
1559 *
1560 * AUTHOR
1561 *
1562 * DESCRIPTION
1563 *
1564 * CHANGES
1565 *
1566 ******************************************************************************/
1567 
Echo_ungetc(int c)1568 void Parser::Echo_ungetc(int c)
1569 {
1570 	if(Echo_Indx > 0)
1571 		Echo_Indx--;
1572 
1573 	Input_File->In_File->ungetchar(c);
1574 }
1575 
1576 
1577 /*****************************************************************************
1578 *
1579 * FUNCTION
1580 *
1581 * INPUT
1582 *
1583 * OUTPUT
1584 *
1585 * RETURNS
1586 *
1587 * AUTHOR
1588 *
1589 * DESCRIPTION
1590 *
1591 * CHANGES
1592 *
1593 ******************************************************************************/
1594 
Where_Error(POVMSObjectPtr msg)1595 void Parser::Where_Error(POVMSObjectPtr msg)
1596 {
1597 	// return if no filename is specified
1598 	if(Token.FileHandle == NULL)
1599 		return;
1600 
1601 	(void)POVMSUtil_SetUCS2String(msg, kPOVAttrib_FileName, Token.FileHandle->name());
1602 	(void)POVMSUtil_SetString(msg, kPOVAttrib_TokenName, Token.Token_String);
1603 	(void)POVMSUtil_SetLong(msg, kPOVAttrib_Line, Token.Token_File_Pos.lineno);
1604 	(void)POVMSUtil_SetInt(msg, kPOVAttrib_Column, Token.Token_Col_No);
1605 	if(Token.FileHandle != NULL)
1606 		(void)POVMSUtil_SetLong(msg, kPOVAttrib_FilePosition, Token.FileHandle->tellg().offset);
1607 }
1608 
1609 
1610 /*****************************************************************************
1611 *
1612 * FUNCTION
1613 *
1614 * INPUT
1615 *
1616 * OUTPUT
1617 *
1618 * RETURNS
1619 *
1620 * AUTHOR
1621 *
1622 * DESCRIPTION
1623 *
1624 * CHANGES
1625 *
1626 ******************************************************************************/
1627 
Where_Warning(POVMSObjectPtr msg)1628 void Parser::Where_Warning(POVMSObjectPtr msg)
1629 {
1630 	// return if no filename is specified
1631 	if(Token.FileHandle == NULL)
1632 		return;
1633 
1634 	(void)POVMSUtil_SetUCS2String(msg, kPOVAttrib_FileName, Token.FileHandle->name());
1635 	(void)POVMSUtil_SetString(msg, kPOVAttrib_TokenName, Token.Token_String);
1636 	(void)POVMSUtil_SetLong(msg, kPOVAttrib_Line, Token.Token_File_Pos.lineno);
1637 	(void)POVMSUtil_SetInt(msg, kPOVAttrib_Column, Token.Token_Col_No);
1638 	if(Token.FileHandle != NULL)
1639 		(void)POVMSUtil_SetLong(msg, kPOVAttrib_FilePosition, Token.FileHandle->tellg().offset);
1640 }
1641 
1642 
1643 
1644 /*****************************************************************************
1645 *
1646 * FUNCTION    Parse_Directive
1647 *
1648 * INPUT
1649 *
1650 * OUTPUT
1651 *
1652 * RETURNS
1653 *
1654 * AUTHOR      Chris Young
1655 *
1656 * DESCRIPTION
1657 *
1658 * CHANGES
1659 *
1660 ******************************************************************************/
1661 
Parse_Directive(int After_Hash)1662 void Parser::Parse_Directive(int After_Hash)
1663 {
1664 	DBL Value, Value2;
1665 	int Flag;
1666 	char *ts;
1667 	POV_MACRO *PMac=NULL;
1668 	COND_TYPE Curr_Type = Cond_Stack[CS_Index].Cond_Type;
1669 	POV_LONG Hash_Loc = Input_File->In_File->tellg().offset;
1670 
1671 	if (Curr_Type == INVOKING_MACRO_COND)
1672 	{
1673 		if (Cond_Stack[CS_Index].PMac->Macro_End==Hash_Loc)
1674 		{
1675 			Return_From_Macro();
1676 			if (--CS_Index < 0)
1677 			{
1678 				Error("Mis-matched '#end'.");
1679 			}
1680 			Token.Token_Id = END_OF_FILE_TOKEN;
1681 			Token.is_array_elem = false;
1682 
1683 			return;
1684 		}
1685 	}
1686 
1687 	if (!Ok_To_Declare)
1688 	{
1689 		if (After_Hash)
1690 		{
1691 			Token.Token_Id=HASH_TOKEN;
1692 			Token.is_array_elem = false;
1693 		}
1694 		Token.Unget_Token = false;
1695 
1696 		return;
1697 	}
1698 
1699 	EXPECT
1700 		CASE(IFDEF_TOKEN)
1701 			Inc_CS_Index();
1702 
1703 			if (Skipping)
1704 			{
1705 				Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1706 				Skip_Tokens(SKIP_TIL_END_COND);
1707 			}
1708 			else
1709 			{
1710 				if (Parse_Ifdef_Param())
1711 				{
1712 					Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
1713 				}
1714 				else
1715 				{
1716 					Cond_Stack[CS_Index].Cond_Type=IF_FALSE_COND;
1717 					Skip_Tokens(IF_FALSE_COND);
1718 				}
1719 			}
1720 			EXIT
1721 		END_CASE
1722 
1723 		CASE(IFNDEF_TOKEN)
1724 			Inc_CS_Index();
1725 
1726 			if (Skipping)
1727 			{
1728 				Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1729 				Skip_Tokens(SKIP_TIL_END_COND);
1730 			}
1731 			else
1732 			{
1733 				if (Parse_Ifdef_Param())
1734 				{
1735 					Cond_Stack[CS_Index].Cond_Type=IF_FALSE_COND;
1736 					Skip_Tokens(IF_FALSE_COND);
1737 				}
1738 				else
1739 				{
1740 					Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
1741 				}
1742 			}
1743 			EXIT
1744 		END_CASE
1745 
1746 		CASE(IF_TOKEN)
1747 			Inc_CS_Index();
1748 
1749 			if (Skipping)
1750 			{
1751 				Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1752 				Skip_Tokens(SKIP_TIL_END_COND);
1753 			}
1754 			else
1755 			{
1756 				Value=Parse_Cond_Param();
1757 
1758 				if (fabs(Value)>EPSILON)
1759 				{
1760 					Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
1761 				}
1762 				else
1763 				{
1764 					Cond_Stack[CS_Index].Cond_Type=IF_FALSE_COND;
1765 					Skip_Tokens(IF_FALSE_COND);
1766 				}
1767 			}
1768 			EXIT
1769 		END_CASE
1770 
1771 		CASE(WHILE_TOKEN)
1772 			Inc_CS_Index();
1773 
1774 			if (Skipping)
1775 			{
1776 				Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1777 				Skip_Tokens(SKIP_TIL_END_COND);
1778 			}
1779 			else
1780 			{
1781 				Cond_Stack[CS_Index].Loop_File = Input_File->In_File;
1782 				Cond_Stack[CS_Index].File_Pos  = Input_File->In_File->tellg();
1783 
1784 				Value=Parse_Cond_Param();
1785 
1786 				if (fabs(Value)>EPSILON)
1787 				{
1788 					Cond_Stack[CS_Index].Cond_Type = WHILE_COND;
1789 				}
1790 				else
1791 				{
1792 					Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1793 					Skip_Tokens(SKIP_TIL_END_COND);
1794 				}
1795 			}
1796 			EXIT
1797 		END_CASE
1798 
1799 		CASE(FOR_TOKEN)
1800 			Inc_CS_Index();
1801 
1802 			if (Skipping)
1803 			{
1804 				Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1805 				Skip_Tokens(SKIP_TIL_END_COND);
1806 			}
1807 			else
1808 			{
1809 				char* Identifier = NULL;
1810 				DBL End, Step;
1811 				if (Parse_For_Param (&Identifier, &End, &Step))
1812 				{
1813 					// execute loop
1814 					Cond_Stack[CS_Index].Cond_Type = FOR_COND;
1815 					Cond_Stack[CS_Index].Loop_File = Input_File->In_File;
1816 					Cond_Stack[CS_Index].File_Pos  = Input_File->In_File->tellg();
1817 					Cond_Stack[CS_Index].Loop_Identifier = Identifier;
1818 					Cond_Stack[CS_Index].For_Loop_End = End;
1819 					Cond_Stack[CS_Index].For_Loop_Step = Step;
1820 				}
1821 				else
1822 				{
1823 					// terminate loop
1824 					Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1825 					Skip_Tokens(SKIP_TIL_END_COND);
1826 				}
1827 			}
1828 			EXIT
1829 		END_CASE
1830 
1831 		CASE(ELSE_TOKEN)
1832 			switch (Curr_Type)
1833 			{
1834 				case IF_TRUE_COND:
1835 					Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1836 					Skip_Tokens(SKIP_TIL_END_COND);
1837 					break;
1838 
1839 				case IF_FALSE_COND:
1840 					Cond_Stack[CS_Index].Cond_Type = ELSE_COND;
1841 					Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
1842 					Token.is_array_elem = false;
1843 					UNGET
1844 					break;
1845 
1846 				case CASE_TRUE_COND:
1847 				case SKIP_TIL_END_COND:
1848 					break;
1849 
1850 				case CASE_FALSE_COND:
1851 					Cond_Stack[CS_Index].Cond_Type = CASE_TRUE_COND;
1852 					if (Skipping)
1853 					{
1854 						Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
1855 						Token.is_array_elem = false;
1856 						UNGET
1857 					}
1858 					break;
1859 
1860 				default:
1861 					Error("Mis-matched '#else'.");
1862 			}
1863 			EXIT
1864 		END_CASE
1865 
1866 		CASE(ELSEIF_TOKEN)
1867 			switch (Curr_Type)
1868 			{
1869 				case IF_TRUE_COND:
1870 					Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1871 					Skip_Tokens(SKIP_TIL_END_COND);
1872 					break;
1873 
1874 				case IF_FALSE_COND:
1875 					Value=Parse_Cond_Param();
1876 					if (fabs(Value)>EPSILON)
1877 					{
1878 						Cond_Stack[CS_Index].Cond_Type=IF_TRUE_COND;
1879 						Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
1880 						Token.is_array_elem = false;
1881 						UNGET
1882 					}
1883 					else
1884 					{
1885 						// nothing to do, we're staying in IF_FALSE_COND state.
1886 					}
1887 					break;
1888 
1889 				case SKIP_TIL_END_COND:
1890 					break;
1891 
1892 				default:
1893 					Error("Mis-matched '#elseif'.");
1894 			}
1895 			EXIT
1896 		END_CASE
1897 
1898 		CASE(SWITCH_TOKEN)
1899 			Inc_CS_Index();
1900 
1901 			if (Skipping)
1902 			{
1903 				Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
1904 				Skip_Tokens(SKIP_TIL_END_COND);
1905 			}
1906 			else
1907 			{
1908 				Cond_Stack[CS_Index].Switch_Value=Parse_Cond_Param();
1909 				Cond_Stack[CS_Index].Cond_Type=SWITCH_COND;
1910 				Cond_Stack[CS_Index].Switch_Case_Ok_Flag=false;
1911 				EXPECT
1912 					// NOTE: We actually expect a "#case" or "#range" here; however, this will trigger a nested call
1913 					// to Parse_Directive, so we'll encounter that CASE_TOKEN or RANGE_TOKEN here only by courtesy of the
1914 					// respective handler, which will UNGET the token and inform us via the Switch_Case_Ok_Flag
1915 					// that the CASE_TOKEN or RANGE_TOKEN we encounter here was properly preceded with a hash ("#").
1916 					CASE2(CASE_TOKEN,RANGE_TOKEN)
1917 						if (!Cond_Stack[CS_Index].Switch_Case_Ok_Flag)
1918 							Error("#switch not followed by #case or #range.");
1919 
1920 						if (Token.Token_Id==CASE_TOKEN)
1921 						{
1922 							Value=Parse_Cond_Param();
1923 							Flag = (fabs(Value-Cond_Stack[CS_Index].Switch_Value)<EPSILON);
1924 						}
1925 						else
1926 						{
1927 							Parse_Cond_Param2(&Value,&Value2);
1928 							Flag = ((Cond_Stack[CS_Index].Switch_Value >= Value) &&
1929 							        (Cond_Stack[CS_Index].Switch_Value <= Value2));
1930 						}
1931 
1932 						if(Flag)
1933 						{
1934 							Cond_Stack[CS_Index].Cond_Type=CASE_TRUE_COND;
1935 						}
1936 						else
1937 						{
1938 							Cond_Stack[CS_Index].Cond_Type=CASE_FALSE_COND;
1939 							Skip_Tokens(CASE_FALSE_COND);
1940 						}
1941 						EXIT
1942 					END_CASE
1943 
1944 					OTHERWISE
1945 						Error("#switch not followed by #case or #range.");
1946 					END_CASE
1947 				END_EXPECT
1948 			}
1949 			EXIT
1950 		END_CASE
1951 
1952 		CASE(BREAK_TOKEN)
1953 			if (!Skipping)
1954 				Break();
1955 			EXIT
1956 		END_CASE
1957 
1958 		CASE2(CASE_TOKEN,RANGE_TOKEN)
1959 			switch(Curr_Type)
1960 			{
1961 				case CASE_TRUE_COND:
1962 				case CASE_FALSE_COND:
1963 					if (Token.Token_Id==CASE_TOKEN)
1964 					{
1965 						Value=Parse_Cond_Param();
1966 						Flag = (fabs(Value-Cond_Stack[CS_Index].Switch_Value)<EPSILON);
1967 					}
1968 					else
1969 					{
1970 						Parse_Cond_Param2(&Value,&Value2);
1971 						Flag = ((Cond_Stack[CS_Index].Switch_Value >= Value) &&
1972 						        (Cond_Stack[CS_Index].Switch_Value <= Value2));
1973 					}
1974 
1975 					if(Flag && (Curr_Type==CASE_FALSE_COND))
1976 					{
1977 						Cond_Stack[CS_Index].Cond_Type=CASE_TRUE_COND;
1978 						if (Skipping)
1979 						{
1980 							Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
1981 							Token.is_array_elem = false;
1982 							UNGET
1983 						}
1984 					}
1985 					break;
1986 
1987 				case SWITCH_COND:
1988 					Cond_Stack[CS_Index].Switch_Case_Ok_Flag=true;
1989 					UNGET
1990 					break;
1991 
1992 				case SKIP_TIL_END_COND:
1993 					break;
1994 
1995 				default:
1996 					Error("Mis-matched '#case' or '#range'.");
1997 			}
1998 			EXIT
1999 		END_CASE
2000 
2001 		CASE(END_TOKEN)
2002 			switch (Curr_Type)
2003 			{
2004 				case INVOKING_MACRO_COND:
2005 					Return_From_Macro();
2006 					if (--CS_Index < 0)
2007 					{
2008 						Error("Mis-matched '#end'.");
2009 					}
2010 					break;
2011 
2012 				case IF_FALSE_COND:
2013 					Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
2014 					Token.is_array_elem = false;
2015 					UNGET
2016 					// FALLTHROUGH
2017 				case IF_TRUE_COND:
2018 				case ELSE_COND:
2019 				case CASE_TRUE_COND:
2020 				case CASE_FALSE_COND:
2021 				case DECLARING_MACRO_COND:
2022 				case SKIP_TIL_END_COND:
2023 					if (Curr_Type==DECLARING_MACRO_COND)
2024 					{
2025 						if ((PMac=Cond_Stack[CS_Index].PMac)!=NULL)
2026 						{
2027 							PMac->Macro_End=Hash_Loc;
2028 						}
2029 					}
2030 					if (--CS_Index < 0)
2031 					{
2032 						Error("Mis-matched '#end'.");
2033 					}
2034 					if (Skipping)
2035 					{
2036 						Token.Token_Id=HASH_TOKEN; /*insures Skip_Token takes notice*/
2037 						Token.is_array_elem = false;
2038 						UNGET
2039 					}
2040 					break;
2041 
2042 				case WHILE_COND:
2043 					if (Cond_Stack[CS_Index].Loop_File != Input_File->In_File)
2044 					{
2045 						Error("#while loop did not end in file where it started.");
2046 					}
2047 
2048 					Got_EOF=false;
2049 					if (!Input_File->In_File->seekg(Cond_Stack[CS_Index].File_Pos))
2050 					{
2051 						Error("Unable to seek in input file for #while directive.");
2052 					}
2053 
2054 					Value=Parse_Cond_Param();
2055 
2056 					if (fabs(Value)<EPSILON)
2057 					{
2058 						Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
2059 						Skip_Tokens(SKIP_TIL_END_COND);
2060 					}
2061 					break;
2062 
2063 				case FOR_COND:
2064 					if (Cond_Stack[CS_Index].Loop_File != Input_File->In_File)
2065 					{
2066 						Error("#for loop did not end in file where it started.");
2067 					}
2068 
2069 					Got_EOF=false;
2070 					if (!Input_File->In_File->seekg(Cond_Stack[CS_Index].File_Pos))
2071 					{
2072 						Error("Unable to seek in input file for #for directive.");
2073 					}
2074 
2075 					{
2076 						SYM_ENTRY* Entry = Find_Symbol(Table_Index, Cond_Stack[CS_Index].Loop_Identifier);
2077 						if ((Entry == NULL) || (Entry->Token_Number != FLOAT_ID_TOKEN))
2078 							Error ("#for loop variable must remain defined and numerical during loop.");
2079 
2080 						DBL* CurrentPtr = (DBL*)Entry->Data;
2081 						DBL  End        = Cond_Stack[CS_Index].For_Loop_End;
2082 						DBL  Step       = Cond_Stack[CS_Index].For_Loop_Step;
2083 
2084 						*CurrentPtr = *CurrentPtr + Step;
2085 
2086 						if ( ((Step > 0) && (*CurrentPtr > End + EPSILON)) ||
2087 						     ((Step < 0) && (*CurrentPtr < End - EPSILON)) )
2088 						{
2089 							POV_FREE(Cond_Stack[CS_Index].Loop_Identifier);
2090 							Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
2091 							Skip_Tokens(SKIP_TIL_END_COND);
2092 						}
2093 					}
2094 					break;
2095 
2096 				default:
2097 					Error("Mis-matched '#end'.");
2098 			}
2099 			EXIT
2100 		END_CASE
2101 
2102 		CASE2 (DECLARE_TOKEN,LOCAL_TOKEN)
2103 			if (Skipping)
2104 			{
2105 				UNGET
2106 				EXIT
2107 			}
2108 			else
2109 			{
2110 				Parse_Declare(Token.Token_Id == LOCAL_TOKEN, After_Hash);
2111 				Curr_Type = Cond_Stack[CS_Index].Cond_Type;
2112 				if (Token.Unget_Token)
2113 				{
2114 					switch (Token.Token_Id)
2115 					{
2116 						case HASH_TOKEN:
2117 							Token.Unget_Token=false;
2118 							break;
2119 
2120 						case MACRO_ID_TOKEN:
2121 							break;
2122 
2123 						default:
2124 							EXIT
2125 					}
2126 				}
2127 				else
2128 				{
2129 					EXIT
2130 				}
2131 			}
2132 		END_CASE
2133 
2134 		CASE (DEFAULT_TOKEN)
2135 			if ( Skipping )
2136 			{
2137 				UNGET
2138 			}
2139 			else
2140 			{
2141 				Parse_Default();
2142 			}
2143 			EXIT
2144 		END_CASE
2145 
2146 		CASE (INCLUDE_TOKEN)
2147 			if (Skipping)
2148 			{
2149 				UNGET
2150 			}
2151 			else
2152 			{
2153 				Open_Include();
2154 			}
2155 			EXIT
2156 		END_CASE
2157 
2158 		CASE (FLOAT_FUNCT_TOKEN)
2159 			if (Skipping)
2160 			{
2161 				UNGET
2162 				EXIT
2163 			}
2164 			else
2165 			{
2166 				switch(Token.Function_Id)
2167 				{
2168 					case VERSION_TOKEN:
2169 						if (sceneData->languageVersionSet == false && token_count > 1)
2170 							sceneData->languageVersionLate = true;
2171 						sceneData->languageVersionSet = true;
2172 						Ok_To_Declare = false;
2173 						Get_Token();
2174 						if (pov_stricmp(Token.Token_String,"unofficial") == 0)
2175 						{
2176 #if POV_RAY_IS_OFFICIAL == 1
2177 							Get_Token();
2178 							Error("This file was created for an unofficial version and\ncannot work as-is with this official version.");
2179 #else
2180 							// PATCH AUTHORS - you should not enable any extra features unless the
2181 							// 'unofficial' keyword is set in the scene file.
2182 #endif
2183 						}
2184 						else
2185 							Unget_Token();
2186 						sceneData->languageVersion = (int)(Parse_Float() * 100 + 0.5);
2187 						messageFactory.SetLanguageVersion(sceneData->languageVersion);
2188 						if (sceneData->explicitNoiseGenerator == false)
2189 							sceneData->noiseGenerator = sceneData->languageVersion >= 350 ? 2 : 1;
2190 						// [CLi] if assumed_gamma is not specified in a legacy (3.6.x or earlier) scene, gammaMode defaults to kPOVList_GammaMode_None;
2191 						// this is enforced later anyway after parsing, but we may need this information /now/ during parsing already
2192 						switch (sceneData->gammaMode)
2193 						{
2194 							case kPOVList_GammaMode_None:
2195 							case kPOVList_GammaMode_AssumedGamma37Implied:
2196 								if (sceneData->EffectiveLanguageVersion() < 370)
2197 									sceneData->gammaMode = kPOVList_GammaMode_None;
2198 								else
2199 									sceneData->gammaMode = kPOVList_GammaMode_AssumedGamma37Implied;
2200 								break;
2201 							case kPOVList_GammaMode_AssumedGamma36:
2202 							case kPOVList_GammaMode_AssumedGamma37:
2203 								if (sceneData->EffectiveLanguageVersion() < 370)
2204 									sceneData->gammaMode = kPOVList_GammaMode_AssumedGamma36;
2205 								else
2206 									sceneData->gammaMode = kPOVList_GammaMode_AssumedGamma37;
2207 								break;
2208 						}
2209 						Parse_Semi_Colon(false);
2210 
2211 						if (sceneData->languageVersion > OFFICIAL_VERSION_NUMBER)
2212 						{
2213 							Error("Your scene file requires POV-Ray version %g or later!\n", (DBL)(sceneData->languageVersion / 100.0));
2214 						}
2215 
2216 						Ok_To_Declare = true;
2217 						Curr_Type = Cond_Stack[CS_Index].Cond_Type;
2218 						if (Token.Unget_Token && (Token.Token_Id==HASH_TOKEN))
2219 						{
2220 							Token.Unget_Token=false;
2221 						}
2222 						else
2223 						{
2224 							EXIT
2225 						}
2226 						break;
2227 
2228 					default:
2229 						UNGET
2230 						Expectation_Error ("object or directive.");
2231 						break;
2232 				}
2233 			}
2234 		END_CASE
2235 
2236 		CASE(WARNING_TOKEN)
2237 			if (Skipping)
2238 			{
2239 				UNGET
2240 			}
2241 			else
2242 			{
2243 				ts=Parse_C_String();
2244 				if(strlen(ts) > 160) // intentional 160, not 128 [trf]
2245 				{
2246 					ts[124] = ts[125] = ts[126] = '.';
2247 					ts[127] = 0;
2248 				}
2249 				Warning(0, "%s", ts);
2250 				POV_FREE(ts);
2251 			}
2252 			EXIT
2253 		END_CASE
2254 
2255 		CASE(ERROR_TOKEN)
2256 			if (Skipping)
2257 			{
2258 				UNGET
2259 			}
2260 			else
2261 			{
2262 				ts=Parse_C_String();
2263 				if(strlen(ts) > 160) // intentional 160, not 128 [trf]
2264 				{
2265 					ts[124] = ts[125] = ts[126] = '.';
2266 					ts[127] = 0;
2267 				}
2268 				Error("Parse halted by #error directive: %s", ts);
2269 				POV_FREE(ts);
2270 			}
2271 			EXIT
2272 		END_CASE
2273 
2274 /* Note: The new message driven output system does not support
2275    generic user output to the render and statistics streams.
2276    Both streams are now directed into the debug stream. */
2277 		CASE(RENDER_TOKEN)
2278 		CASE(STATISTICS_TOKEN)
2279 				Warning(0, "#render and #statistics streams are no longer available.\nRedirecting output to #debug stream.");
2280 			// Intentional, redirect output to debug stream.
2281 		CASE(DEBUG_TOKEN)
2282 			if (Skipping)
2283 			{
2284 				UNGET
2285 			}
2286 			else
2287 			{
2288 				ts=Parse_C_String();
2289 				if(strlen(ts) > 200) // intentional 200, not 160
2290 				{
2291 					ts[156] = ts[157] = ts[158] = '.';
2292 					ts[159] = 0;
2293 				}
2294 				Debug_Info("%s", ts);
2295 				POV_FREE(ts);
2296 			}
2297 			EXIT
2298 		END_CASE
2299 
2300 		CASE(FOPEN_TOKEN)
2301 			if (Skipping)
2302 			{
2303 				UNGET
2304 			}
2305 			else
2306 			{
2307 				Parse_Fopen();
2308 			}
2309 			EXIT
2310 		END_CASE
2311 
2312 		CASE(FCLOSE_TOKEN)
2313 			if (Skipping)
2314 			{
2315 				UNGET
2316 			}
2317 			else
2318 			{
2319 				Parse_Fclose();
2320 			}
2321 			EXIT
2322 		END_CASE
2323 
2324 		CASE(READ_TOKEN)
2325 			if (Skipping)
2326 			{
2327 				UNGET
2328 			}
2329 			else
2330 			{
2331 				Parse_Read();
2332 			}
2333 			EXIT
2334 		END_CASE
2335 
2336 		CASE(WRITE_TOKEN)
2337 			if (Skipping)
2338 			{
2339 				UNGET
2340 			}
2341 			else
2342 			{
2343 				Parse_Write();
2344 			}
2345 			EXIT
2346 		END_CASE
2347 
2348 		CASE(UNDEF_TOKEN)
2349 			if (Skipping)
2350 			{
2351 				UNGET
2352 			}
2353 			else
2354 			{
2355 				Ok_To_Declare = false;
2356 				EXPECT
2357 					CASE (IDENTIFIER_TOKEN)
2358 						Warning(0,"Attempt to undef unknown identifier");
2359 						EXIT
2360 					END_CASE
2361 
2362 					CASE2 (MACRO_ID_TOKEN, PARAMETER_ID_TOKEN)
2363 					CASE3 (FILE_ID_TOKEN,  FUNCT_ID_TOKEN, VECTFUNCT_ID_TOKEN)
2364 					// These have to match Parse_Declare in parse.cpp! [trf]
2365 					CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
2366 					CASE4 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN, PIGMENT_ID_TOKEN)
2367 					CASE4 (SLOPE_MAP_ID_TOKEN, NORMAL_MAP_ID_TOKEN, TEXTURE_MAP_ID_TOKEN, COLOUR_ID_TOKEN)
2368 					CASE4 (PIGMENT_MAP_ID_TOKEN, MEDIA_ID_TOKEN, STRING_ID_TOKEN, INTERIOR_ID_TOKEN)
2369 					CASE4 (DENSITY_ID_TOKEN, ARRAY_ID_TOKEN, DENSITY_MAP_ID_TOKEN, UV_ID_TOKEN)
2370 					CASE4 (VECTOR_4D_ID_TOKEN,  RAINBOW_ID_TOKEN, FOG_ID_TOKEN, SKYSPHERE_ID_TOKEN)
2371 					CASE2 (MATERIAL_ID_TOKEN, SPLINE_ID_TOKEN)
2372 						Remove_Symbol (Token.Table_Index, Token.Token_String, Token.is_array_elem, Token.DataPtr, Token.Token_Id);
2373 						EXIT
2374 					END_CASE
2375 
2376 					CASE2 (VECTOR_FUNCT_TOKEN, FLOAT_FUNCT_TOKEN)
2377 						switch(Token.Function_Id)
2378 						{
2379 							case VECTOR_ID_TOKEN:
2380 							case FLOAT_ID_TOKEN:
2381 								Remove_Symbol (Token.Table_Index, Token.Token_String, Token.is_array_elem, Token.DataPtr, Token.Token_Id);
2382 								break;
2383 
2384 							default:
2385 								Parse_Error(IDENTIFIER_TOKEN);
2386 								break;
2387 						}
2388 						EXIT
2389 					END_CASE
2390 
2391 					OTHERWISE
2392 						Parse_Error(IDENTIFIER_TOKEN);
2393 					END_CASE
2394 				END_EXPECT
2395 				Ok_To_Declare = true;
2396 			}
2397 			EXIT
2398 		END_CASE
2399 
2400 		CASE (MACRO_ID_TOKEN)
2401 			if (Skipping)
2402 			{
2403 				UNGET
2404 			}
2405 			else
2406 			{
2407 				Invoke_Macro();
2408 			}
2409 			EXIT
2410 		END_CASE
2411 
2412 		CASE (MACRO_TOKEN)
2413 			if (!Skipping)
2414 			{
2415 				if (Inside_MacroDef)
2416 				{
2417 					Error("Cannot nest macro definitions");
2418 				}
2419 				Inside_MacroDef=true;
2420 				PMac=Parse_Macro();
2421 				Inside_MacroDef=false;
2422 			}
2423 			Inc_CS_Index();
2424 			Cond_Stack[CS_Index].Cond_Type = DECLARING_MACRO_COND;
2425 			Cond_Stack[CS_Index].PMac      = PMac;
2426 			Skip_Tokens(DECLARING_MACRO_COND);
2427 			EXIT
2428 		END_CASE
2429 
2430 		OTHERWISE
2431 			UNGET
2432 			EXIT
2433 		END_CASE
2434 	END_EXPECT
2435 
2436 	if (Token.Unget_Token)
2437 	{
2438 		Token.Unget_Token = false;
2439 	}
2440 	else
2441 	{
2442 		Token.Token_Id = END_OF_FILE_TOKEN;
2443 		Token.is_array_elem = false;
2444 	}
2445 }
2446 
2447 
2448 /*****************************************************************************
2449 *
2450 * FUNCTION
2451 *
2452 * INPUT
2453 *
2454 * OUTPUT
2455 *
2456 * RETURNS
2457 *
2458 * AUTHOR
2459 *
2460 * DESCRIPTION
2461 *
2462 * CHANGES
2463 *
2464 ******************************************************************************/
2465 
Open_Include()2466 void Parser::Open_Include()
2467 {
2468 	char *tempascii;
2469 	UCS2String temp;
2470 	UCS2String b;
2471 
2472 	if (Skip_Spaces () != true)
2473 		Error ("Expecting a string after INCLUDE.");
2474 
2475 	tempascii = Parse_C_String(true);
2476 	temp = ASCIItoUCS2String(tempascii);
2477 	POV_FREE(tempascii);
2478 
2479 	Include_File_Index++;
2480 
2481 	if (Include_File_Index >= MAX_INCLUDE_FILES)
2482 	{
2483 		Include_File_Index--;
2484 		Error ("Too many nested include files.");
2485 	}
2486 
2487 	Echo_Indx = 0;
2488 
2489 	Input_File = &Include_Files[Include_File_Index];
2490 
2491 	// must set this to NULL in case it's uninitialized - if Locate_File throws an
2492 	// exception (e.g. I/O restriction error), the parser shut-down code will attempt
2493 	// to free In_File if it's not NULL.
2494 	Input_File->In_File = NULL;
2495 
2496 	IStream *is = Locate_File(this, sceneData, temp, POV_File_Text_INC, b, true);
2497 	if(is == NULL)
2498 	{
2499 		Input_File->In_File = NULL;  /* Keeps from closing failed file. */
2500 		Error ("Cannot open include file %s.", UCS2toASCIIString(temp).c_str());
2501 	}
2502 	else
2503 		Input_File->In_File = new ITextStream(b.c_str(), is);
2504 
2505 	Input_File->R_Flag=false;
2506 
2507 	Add_Sym_Table();
2508 
2509 	Token.Token_Id = END_OF_FILE_TOKEN;
2510 	Token.is_array_elem = false;
2511 
2512 }
2513 
2514 
2515 
2516 /*****************************************************************************
2517 *
2518 * FUNCTION
2519 *
2520 * INPUT
2521 *
2522 * OUTPUT
2523 *
2524 * RETURNS
2525 *
2526 * AUTHOR
2527 *
2528 * DESCRIPTION
2529 *
2530 * CHANGES
2531 *
2532 ******************************************************************************/
2533 
Skip_Tokens(COND_TYPE cond)2534 void Parser::Skip_Tokens(COND_TYPE cond)
2535 {
2536 	int Temp      = CS_Index;
2537 	int Prev_Skip = Skipping;
2538 
2539 	Skipping=true;
2540 
2541 	while ((CS_Index > Temp) || ((CS_Index == Temp) && (Cond_Stack[CS_Index].Cond_Type == cond)))
2542 	{
2543 		Get_Token();
2544 	}
2545 
2546 	Skipping=Prev_Skip;
2547 
2548 	if (Token.Token_Id==HASH_TOKEN)
2549 	{
2550 		Token.Token_Id=END_OF_FILE_TOKEN;
2551 		Token.is_array_elem = false;
2552 		Token.Unget_Token=false;
2553 	}
2554 	else
2555 	{
2556 		UNGET
2557 	}
2558 }
2559 
2560 
2561 /*****************************************************************************
2562 *
2563 * FUNCTION
2564 *
2565 * INPUT
2566 *
2567 * OUTPUT
2568 *
2569 * RETURNS
2570 *
2571 * AUTHOR
2572 *
2573 * DESCRIPTION
2574 *
2575 * CHANGES
2576 *
2577 ******************************************************************************/
2578 
Break()2579 void Parser::Break()
2580 {
2581 	int Prev_Skip = Skipping;
2582 
2583 	Skipping=true;
2584 
2585 	while ( (CS_Index > 0) &&
2586 	        (Cond_Stack[CS_Index].Cond_Type != WHILE_COND) &&
2587 	        (Cond_Stack[CS_Index].Cond_Type != FOR_COND) &&
2588 	        (Cond_Stack[CS_Index].Cond_Type != CASE_TRUE_COND) &&
2589 	        (Cond_Stack[CS_Index].Cond_Type != INVOKING_MACRO_COND) )
2590 	{
2591 		Get_Token();
2592 	}
2593 
2594 	if (CS_Index == 0)
2595 		Error ("Invalid context for #break");
2596 
2597 	if (Cond_Stack[CS_Index].Cond_Type == INVOKING_MACRO_COND)
2598 	{
2599 		Skipping=Prev_Skip;
2600 		Return_From_Macro();
2601 		--CS_Index;
2602 		return;
2603 	}
2604 
2605 	Cond_Stack[CS_Index].Cond_Type = SKIP_TIL_END_COND;
2606 	Skip_Tokens (SKIP_TIL_END_COND);
2607 
2608 	Skipping=Prev_Skip;
2609 
2610 	if (Token.Token_Id==HASH_TOKEN)
2611 	{
2612 		Token.Token_Id=END_OF_FILE_TOKEN;
2613 		Token.is_array_elem = false;
2614 		Token.Unget_Token=false;
2615 	}
2616 	else
2617 	{
2618 		UNGET
2619 	}
2620 }
2621 
2622 
2623 /*****************************************************************************
2624 *
2625 * FUNCTION
2626 *
2627 *   get_hash_value
2628 *
2629 * INPUT
2630 *
2631 * OUTPUT
2632 *
2633 * RETURNS
2634 *
2635 * AUTHOR
2636 *
2637 *   Dieter Bayer
2638 *
2639 * DESCRIPTION
2640 *
2641 *   Calculate hash value for a given string.
2642 *
2643 * CHANGES
2644 *
2645 *   Apr 1996 : Creation.
2646 *
2647 ******************************************************************************/
2648 
get_hash_value(const char * s)2649 int Parser::get_hash_value(const char *s)
2650 {
2651 	unsigned int i = 0;
2652 
2653 	while (*s)
2654 	{
2655 		i = (i << 1) ^ *s++;
2656 	}
2657 
2658 	return((int)(i % SYM_TABLE_SIZE));
2659 }
2660 
2661 
2662 
2663 /*****************************************************************************
2664 *
2665 * FUNCTION
2666 *
2667 *   init_sym_tables
2668 *
2669 * INPUT
2670 *
2671 * OUTPUT
2672 *
2673 * RETURNS
2674 *
2675 * AUTHOR
2676 *
2677 *   Chris Young
2678 *
2679 * DESCRIPTION
2680 *
2681 * CHANGES
2682 *
2683 ******************************************************************************/
2684 
init_sym_tables()2685 void Parser::init_sym_tables()
2686 {
2687 	int i;
2688 
2689 	Add_Sym_Table();
2690 
2691 	for (i = 0; i < LAST_TOKEN; i++)
2692 	{
2693 		Add_Symbol(0,Reserved_Words[i].Token_Name,Reserved_Words[i].Token_Number);
2694 	}
2695 
2696 	Add_Sym_Table();
2697 }
2698 
Add_Sym_Table()2699 void Parser::Add_Sym_Table()
2700 {
2701 	int i;
2702 
2703 	SYM_TABLE *New;
2704 
2705 	if ((++Table_Index)==MAX_NUMBER_OF_TABLES)
2706 	{
2707 		Table_Index--;
2708 		Error("Too many nested symbol tables");
2709 	}
2710 
2711 	Tables[Table_Index]=New=(SYM_TABLE *)POV_MALLOC(sizeof(SYM_TABLE),"symbol table");
2712 
2713 	for (i = 0; i < SYM_TABLE_SIZE; i++)
2714 	{
2715 		New->Table[i] = NULL;
2716 	}
2717 
2718 }
2719 
Destroy_Table(int index)2720 void Parser::Destroy_Table(int index)
2721 {
2722 	int i;
2723 	SYM_TABLE *Table = Tables[index];
2724 	SYM_ENTRY *Entry;
2725 
2726 	for(i = SYM_TABLE_SIZE - 1; i >= 0; i--)
2727 	{
2728 		Entry = Table->Table[i];
2729 
2730 		while(Entry)
2731 		{
2732 			Entry = Destroy_Entry(index, Entry);
2733 		}
2734 
2735 		Table->Table[i] = NULL;
2736 	}
2737 
2738 	POV_FREE(Table);
2739 
2740 }
2741 
Create_Entry(int Index,const char * Name,TOKEN Number)2742 SYM_ENTRY *Parser::Create_Entry (int Index,const char *Name,TOKEN Number)
2743 {
2744 	SYM_ENTRY *New;
2745 
2746 	New = (SYM_ENTRY *)POV_MALLOC(sizeof(SYM_ENTRY), "symbol table entry");
2747 
2748 	New->Token_Number        = Number;
2749 	New->Data                = NULL;
2750 	New->Flags               = 0;
2751 	New->Deprecation_Message = NULL;
2752 	New->ref_count           = 1;
2753 	if (Index != 0)
2754 		New->Token_Name = POV_STRDUP(Name);
2755 	else
2756 		New->Token_Name = const_cast<char*>(Name);
2757 
2758 	return(New);
2759 }
2760 
Acquire_Entry_Reference(SYM_ENTRY * Entry)2761 void Parser::Acquire_Entry_Reference (SYM_ENTRY *Entry)
2762 {
2763 	if (Entry == NULL)
2764 		return;
2765 	if (Entry->ref_count >= SYM_ENTRY_REF_MAX)
2766 		Error("Too many unresolved references to symbol");
2767 	Entry->ref_count ++;
2768 }
2769 
Release_Entry_Reference(int Index,SYM_ENTRY * Entry)2770 void Parser::Release_Entry_Reference (int Index, SYM_ENTRY *Entry)
2771 {
2772 	if (Entry == NULL)
2773 		return;
2774 	if (Entry->ref_count <= 0)
2775 		Error("Internal error: Symbol reference counter underflow");
2776 	Entry->ref_count --;
2777 
2778 	if (Entry->ref_count == 0)
2779 	{
2780 		if(Index != 0) // NB reserved words reference hard-coded token names in code segment, rather than allocated on heap
2781 		{
2782 			POV_FREE(Entry->Token_Name);
2783 			Destroy_Ident_Data (Entry->Data, Entry->Token_Number); // TODO - shouldn't this be outside the if() block?
2784 		}
2785 		if (Entry->Deprecation_Message != NULL)
2786 			POV_FREE(Entry->Deprecation_Message);
2787 
2788 		POV_FREE(Entry);
2789 	}
2790 }
2791 
Destroy_Entry(int Index,SYM_ENTRY * Entry)2792 SYM_ENTRY *Parser::Destroy_Entry (int Index, SYM_ENTRY *Entry)
2793 {
2794 	SYM_ENTRY *Next;
2795 
2796 	if(Entry == NULL)
2797 		return NULL;
2798 
2799 	// always unhook the entry from hash table (if it is still member of one)
2800 	Next = Entry->next;
2801 	Entry->next = NULL;
2802 
2803 	if (Entry->ref_count <= 0)
2804 		Error("Internal error: Symbol reference counter underflow");
2805 	Entry->ref_count --;
2806 
2807 	if (Entry->ref_count == 0)
2808 	{
2809 		if(Index != 0) // NB reserved words reference hard-coded token names in code segment, rather than allocated on heap
2810 		{
2811 			POV_FREE(Entry->Token_Name);
2812 			Destroy_Ident_Data (Entry->Data, Entry->Token_Number); // TODO - shouldn't this be outside the if() block?
2813 		}
2814 		if (Entry->Deprecation_Message != NULL)
2815 			POV_FREE(Entry->Deprecation_Message);
2816 
2817 		POV_FREE(Entry);
2818 	}
2819 
2820 	return Next;
2821 }
2822 
2823 
Add_Entry(int Index,SYM_ENTRY * Table_Entry)2824 void Parser::Add_Entry (int Index,SYM_ENTRY *Table_Entry)
2825 {
2826 	int i = get_hash_value(Table_Entry->Token_Name);
2827 
2828 	Table_Entry->next       = Tables[Index]->Table[i];
2829 	Tables[Index]->Table[i] = Table_Entry;
2830 }
2831 
2832 
Add_Symbol(int Index,const char * Name,TOKEN Number)2833 SYM_ENTRY *Parser::Add_Symbol (int Index,const char *Name,TOKEN Number)
2834 {
2835 	SYM_ENTRY *New;
2836 
2837 	New = Create_Entry (Index,Name,Number);
2838 	Add_Entry(Index,New);
2839 
2840 	return(New);
2841 }
2842 
2843 
Find_Symbol(int Index,const char * Name)2844 SYM_ENTRY *Parser::Find_Symbol(int Index,const char *Name)
2845 {
2846 	SYM_ENTRY *Entry;
2847 
2848 	int i = get_hash_value(Name);
2849 
2850 	Entry = Tables[Index]->Table[i];
2851 
2852 	while (Entry)
2853 	{
2854 		if (strcmp(Name, Entry->Token_Name) == 0)
2855 		{
2856 			return(Entry);
2857 		}
2858 
2859 		Entry = Entry->next;
2860 	}
2861 
2862 	return(Entry);
2863 }
2864 
2865 
Remove_Symbol(int Index,const char * Name,bool is_array_elem,void ** DataPtr,int ttype)2866 void Parser::Remove_Symbol (int Index, const char *Name, bool is_array_elem, void **DataPtr, int ttype)
2867 {
2868 	if(is_array_elem == true)
2869 	{
2870 		if(DataPtr == NULL)
2871 			Error("Invalid array element!");
2872 
2873 		if(ttype == FLOAT_FUNCT_TOKEN)
2874 			ttype = FLOAT_ID_TOKEN;
2875 		else if(ttype == VECTOR_FUNCT_TOKEN)
2876 			ttype = VECTOR_ID_TOKEN;
2877 		else if(ttype == COLOUR_KEY_TOKEN)
2878 			ttype = COLOUR_ID_TOKEN;
2879 
2880 		Destroy_Ident_Data (*DataPtr, ttype);
2881 		*DataPtr = NULL;
2882 	}
2883 	else
2884 	{
2885 		SYM_ENTRY *Entry;
2886 		SYM_ENTRY **EntryPtr;
2887 
2888 		int i = get_hash_value(Name);
2889 
2890 		EntryPtr = &(Tables[Index]->Table[i]);
2891 		Entry    = *EntryPtr;
2892 
2893 		while (Entry)
2894 		{
2895 			if (strcmp(Name, Entry->Token_Name) == 0)
2896 			{
2897 				*EntryPtr = Entry->next;
2898 				Destroy_Entry(Index, Entry);
2899 				return;
2900 			}
2901 
2902 			EntryPtr = &(Entry->next);
2903 			Entry    = *EntryPtr;
2904 		}
2905 
2906 		Error("Tried to free undefined symbol");
2907 	}
2908 }
2909 
Check_Macro_Vers(void)2910 void Parser::Check_Macro_Vers(void)
2911 {
2912 	if (sceneData->languageVersion < 310)
2913 	{
2914 		Error("Macros require #version 3.1 or later but #version %x.%02d is set.",
2915 		       sceneData->languageVersion / 100, sceneData->languageVersion % 100);
2916 	}
2917 }
2918 
Parse_Macro()2919 Parser::POV_MACRO *Parser::Parse_Macro()
2920 {
2921 	POV_MACRO *New;
2922 	SYM_ENTRY *Table_Entry=NULL;
2923 	int Old_Ok = Ok_To_Declare;
2924 
2925 	Check_Macro_Vers();
2926 
2927 	Ok_To_Declare = false;
2928 
2929 	EXPECT
2930 		CASE (IDENTIFIER_TOKEN)
2931 			Table_Entry = Add_Symbol (1,Token.Token_String,TEMPORARY_MACRO_ID_TOKEN);
2932 			EXIT
2933 		END_CASE
2934 
2935 		CASE (MACRO_ID_TOKEN)
2936 			Remove_Symbol(1,Token.Token_String,false,NULL,0);
2937 			Table_Entry = Add_Symbol (1,Token.Token_String,TEMPORARY_MACRO_ID_TOKEN);
2938 			EXIT
2939 		END_CASE
2940 
2941 		OTHERWISE
2942 			Parse_Error(IDENTIFIER_TOKEN);
2943 		END_CASE
2944 	END_EXPECT
2945 
2946 	New=(POV_MACRO *)POV_MALLOC(sizeof(POV_MACRO),"macro");
2947 
2948 	Table_Entry->Data=(void *)New;
2949 
2950 	New->Macro_Filename = NULL;
2951 	New->Num_Of_Pars=0;
2952 	New->Macro_Name=POV_STRDUP(Token.Token_String);
2953 
2954 	EXPECT
2955 		CASE (LEFT_PAREN_TOKEN )
2956 			EXIT
2957 		END_CASE
2958 		CASE (TEMPORARY_MACRO_ID_TOKEN)
2959 			Error( "Can't invoke a macro while declaring its parameters");
2960 		END_CASE
2961 
2962 		OTHERWISE
2963 			Expectation_Error ("identifier or expression.");
2964 		END_CASE
2965 	END_EXPECT
2966 
2967 	EXPECT
2968 		CASE3 (MACRO_ID_TOKEN, IDENTIFIER_TOKEN, PARAMETER_ID_TOKEN)
2969 		CASE3 (FILE_ID_TOKEN,  FUNCT_ID_TOKEN, VECTFUNCT_ID_TOKEN)
2970 		// These have to match Parse_Declare in parse.cpp! [trf]
2971 		CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
2972 		CASE4 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN, PIGMENT_ID_TOKEN)
2973 		CASE4 (SLOPE_MAP_ID_TOKEN, NORMAL_MAP_ID_TOKEN, TEXTURE_MAP_ID_TOKEN, COLOUR_ID_TOKEN)
2974 		CASE4 (PIGMENT_MAP_ID_TOKEN, MEDIA_ID_TOKEN, STRING_ID_TOKEN, INTERIOR_ID_TOKEN)
2975 		CASE4 (DENSITY_ID_TOKEN, ARRAY_ID_TOKEN, DENSITY_MAP_ID_TOKEN, UV_ID_TOKEN)
2976 		CASE4 (VECTOR_4D_ID_TOKEN,  RAINBOW_ID_TOKEN, FOG_ID_TOKEN, SKYSPHERE_ID_TOKEN)
2977 		CASE2 (MATERIAL_ID_TOKEN, SPLINE_ID_TOKEN)
2978 			New->Par_Name[New->Num_Of_Pars] = POV_STRDUP(Token.Token_String);
2979 			if (++(New->Num_Of_Pars) == MAX_PARAMETER_LIST)
2980 			{
2981 				Error("Too many parameters");
2982 			}
2983 			Parse_Comma();
2984 		END_CASE
2985 
2986 		CASE2 (VECTOR_FUNCT_TOKEN, FLOAT_FUNCT_TOKEN)
2987 			switch(Token.Function_Id)
2988 			{
2989 				case VECTOR_ID_TOKEN:
2990 				case FLOAT_ID_TOKEN:
2991 					New->Par_Name[New->Num_Of_Pars] = POV_STRDUP(Token.Token_String);
2992 					if (++(New->Num_Of_Pars) == MAX_PARAMETER_LIST)
2993 					{
2994 						Error("Too many parameters");
2995 					}
2996 					Parse_Comma();
2997 					break;
2998 
2999 				default:
3000 					Expectation_Error ("identifier or expression.");
3001 					break;
3002 			}
3003 		END_CASE
3004 
3005 		CASE(RIGHT_PAREN_TOKEN)
3006 			UNGET
3007 			EXIT
3008 		END_CASE
3009 
3010 		CASE(TEMPORARY_MACRO_ID_TOKEN)
3011 			Error( "Can't invoke a macro while declaring its parameters");
3012 		END_CASE
3013 
3014 		OTHERWISE
3015 			Expectation_Error ("identifier or expression.");
3016 		END_CASE
3017 	END_EXPECT
3018 
3019 	Ok_To_Declare = Old_Ok;
3020 
3021 	Table_Entry->Token_Number = MACRO_ID_TOKEN;
3022 
3023 	New->Macro_Filename = UCS2_strdup(Input_File->In_File->name());
3024 	New->Macro_File_Pos = Input_File->In_File->tellg();
3025 
3026 	Check_Macro_Vers();
3027 
3028 	return (New);
3029 }
3030 
3031 
Invoke_Macro()3032 void Parser::Invoke_Macro()
3033 {
3034 	POV_MACRO *PMac=(POV_MACRO *)Token.Data;
3035 	SYM_ENTRY **Table_Entries=NULL;
3036 	int i,Local_Index;
3037 
3038 	if(PMac == NULL)
3039 	{
3040 		if(Token.DataPtr!=NULL)
3041 			PMac = (POV_MACRO*)(*(Token.DataPtr));
3042 		else
3043 			Error("Error in Invoke_Macro");
3044 	}
3045 
3046 	Check_Macro_Vers();
3047 
3048 	GET(LEFT_PAREN_TOKEN);
3049 
3050 	if (PMac->Num_Of_Pars > 0)
3051 	{
3052 		Table_Entries = (SYM_ENTRY **)POV_MALLOC(sizeof(SYM_ENTRY *)*PMac->Num_Of_Pars,"parameters");
3053 
3054 		/* We must parse all parameters before adding new symbol table
3055 		   or adding entries.  Otherwise recursion won't always work.
3056 		 */
3057 
3058 		Local_Index = Table_Index;
3059 
3060 		for (i=0; i<PMac->Num_Of_Pars; i++)
3061 		{
3062 			Table_Entries[i]=Create_Entry(1,PMac->Par_Name[i],IDENTIFIER_TOKEN);
3063 			if (!Parse_RValue(IDENTIFIER_TOKEN, &(Table_Entries[i]->Token_Number), &(Table_Entries[i]->Data), NULL, true, false, true, true, Local_Index))
3064 			{
3065 				Error("Expected %d parameters but only %d found.",PMac->Num_Of_Pars,i);
3066 			}
3067 			Parse_Comma();
3068 		}
3069 	}
3070 
3071 	GET(RIGHT_PAREN_TOKEN);
3072 
3073 	Inc_CS_Index();
3074 	Cond_Stack[CS_Index].Cond_Type = INVOKING_MACRO_COND;
3075 
3076 	Cond_Stack[CS_Index].File_Pos          = Input_File->In_File->tellg();
3077 	Cond_Stack[CS_Index].Macro_Return_Name = Input_File->In_File->name();
3078 	Cond_Stack[CS_Index].PMac              = PMac;
3079 
3080 	/* Gotta have new symbol table in case #local is used */
3081 	Add_Sym_Table();
3082 
3083 	if (PMac->Num_Of_Pars > 0)
3084 	{
3085 		for (i=0; i<PMac->Num_Of_Pars; i++)
3086 		{
3087 			Add_Entry(Table_Index,Table_Entries[i]);
3088 		}
3089 
3090 		POV_FREE(Table_Entries);
3091 	}
3092 
3093 	if (UCS2_strcmp(PMac->Macro_Filename,Input_File->In_File->name()))
3094 	{
3095 		UCS2String ign;
3096 		/* Not in same file */
3097 		Cond_Stack[CS_Index].Macro_Same_Flag=false;
3098 		Cond_Stack[CS_Index].Macro_File = Input_File->In_File ;
3099 //  POV_DELETE(Input_File->In_File, IStream);
3100 		Got_EOF=false;
3101 		Input_File->R_Flag=false;
3102 		IStream *is = Locate_File (this, sceneData, PMac->Macro_Filename, POV_File_Text_Macro, ign, true);
3103 		if(is == NULL)
3104 		{
3105 			Input_File->In_File = NULL;  /* Keeps from closing failed file. */
3106 			Error ("Cannot open macro file '%s'.", UCS2toASCIIString(UCS2String(PMac->Macro_Filename)).c_str());
3107 		}
3108 		else
3109 			Input_File->In_File = new ITextStream(PMac->Macro_Filename, is);
3110 	}
3111 	else
3112 	{
3113 		Cond_Stack[CS_Index].Macro_Same_Flag=true;
3114 	}
3115 
3116 	Got_EOF=false;
3117 	if (!Input_File->In_File->seekg(PMac->Macro_File_Pos))
3118 	{
3119 		Error("Unable to file seek in macro.");
3120 	}
3121 
3122 	Token.Token_Id = END_OF_FILE_TOKEN;
3123 	Token.is_array_elem = false;
3124 
3125 	Check_Macro_Vers();
3126 
3127 }
3128 
Return_From_Macro()3129 void Parser::Return_From_Macro()
3130 {
3131 	Check_Macro_Vers();
3132 
3133 	if (!Cond_Stack[CS_Index].Macro_Same_Flag)
3134 	{
3135 		if (Token.FileHandle == Input_File->In_File)
3136 			Token.FileHandle = NULL;
3137 		delete Input_File->In_File;
3138 		Input_File->R_Flag=false;
3139 		Input_File->In_File = Cond_Stack[CS_Index].Macro_File ;
3140 		if (Token.FileHandle == NULL)
3141 			Token.FileHandle = Input_File->In_File;
3142 	}
3143 
3144 	Got_EOF=false;
3145 
3146 	if (!Input_File->In_File->seekg(Cond_Stack[CS_Index].File_Pos))
3147 	{
3148 		Error("Unable to file seek in return from macro.");
3149 	}
3150 
3151 	// Always destroy macro locals
3152 	Destroy_Table(Table_Index--);
3153 }
3154 
Destroy_Macro(POV_MACRO * PMac)3155 void Parser::Destroy_Macro(POV_MACRO *PMac)
3156 {
3157 	int i;
3158 	if (PMac==NULL)
3159 	{
3160 		return;
3161 	}
3162 
3163 	POV_FREE(PMac->Macro_Name);
3164 	if (PMac->Macro_Filename!=NULL)
3165 	{
3166 		POV_FREE(PMac->Macro_Filename);
3167 	}
3168 
3169 	for (i=0; i < PMac->Num_Of_Pars; i++)
3170 	{
3171 		POV_FREE(PMac->Par_Name[i]);
3172 	}
3173 
3174 	POV_FREE(PMac);
3175 }
3176 
Parse_Array_Declare(void)3177 Parser::POV_ARRAY *Parser::Parse_Array_Declare (void)
3178 {
3179 	POV_ARRAY *New;
3180 	int i,j;
3181 
3182 	New=(POV_ARRAY *)POV_MALLOC(sizeof(POV_ARRAY),"array");
3183 
3184 	i=0;
3185 	j=1;
3186 
3187 	Ok_To_Declare = false;
3188 
3189 	EXPECT
3190 		CASE (LEFT_SQUARE_TOKEN)
3191 			if (i>4)
3192 			{
3193 				Error("Too many array dimensions");
3194 			}
3195 			New->Sizes[i]=(int)(Parse_Float() + EPSILON);
3196 			j *= New->Sizes[i];
3197 			if ( j <= 0) {
3198 				Error("Invalid dimension size for an array");
3199 			}
3200 			i++;
3201 			GET(RIGHT_SQUARE_TOKEN)
3202 		END_CASE
3203 
3204 		OTHERWISE
3205 			UNGET
3206 			EXIT
3207 		END_CASE
3208 	END_EXPECT
3209 
3210 	if ( i < 1 ) {
3211 		Error( "An array declaration must have at least one dimension");
3212 	}
3213 
3214 	New->Dims     = i-1;
3215 	New->Total    = j;
3216 	New->Type     = EMPTY_ARRAY_TOKEN;
3217 	New->DataPtrs = (void **)POV_MALLOC(sizeof(void *)*j,"array");
3218 
3219 	j = 1;
3220 
3221 	for(i = New->Dims; i>=0; i--)
3222 	{
3223 		New->Mags[i] = j;
3224 		j *= New->Sizes[i];
3225 	}
3226 
3227 	for (i=0; i<New->Total; i++)
3228 	{
3229 		New->DataPtrs[i] = NULL;
3230 	}
3231 
3232 	EXPECT
3233 		CASE(LEFT_CURLY_TOKEN)
3234 			UNGET
3235 				Parse_Initalizer(0,0,New);
3236 			EXIT
3237 		END_CASE
3238 
3239 		OTHERWISE
3240 			UNGET
3241 			EXIT
3242 		END_CASE
3243 	END_EXPECT
3244 
3245 	Ok_To_Declare = true;
3246 	return(New);
3247 
3248 };
3249 
Parse_Initalizer(int Sub,int Base,POV_ARRAY * a)3250 void Parser::Parse_Initalizer (int Sub, int Base, POV_ARRAY *a)
3251 {
3252 	int i;
3253 
3254 	Parse_Begin();
3255 	if (Sub < a->Dims)
3256 	{
3257 		for(i=0; i < a->Sizes[Sub]; i++)
3258 		{
3259 			Parse_Initalizer(Sub+1,i*a->Mags[Sub]+Base,a);
3260 		}
3261 	}
3262 	else
3263 	{
3264 		for(i=0; i < a->Sizes[Sub]; i++)
3265 		{
3266 			if (!Parse_RValue (a->Type, &(a->Type), &(a->DataPtrs[Base+i]), NULL, false, false, true, false, MAX_NUMBER_OF_TABLES))
3267 			{
3268 				Error("Insufficent number of initializers");
3269 			}
3270 			Parse_Comma();
3271 		}
3272 	}
3273 	Parse_End();
3274 	Parse_Comma();
3275 };
3276 
Parse_Fopen(void)3277 void Parser::Parse_Fopen(void)
3278 {
3279 	IStream *rfile = NULL;
3280 	OStream *wfile = NULL;
3281 	DATA_FILE *New;
3282 	char *asciitemp;
3283 	UCS2String temp;
3284 	UCS2String ign;
3285 	SYM_ENTRY *Entry;
3286 
3287 	New=(DATA_FILE *)POV_MALLOC(sizeof(DATA_FILE),"user file");
3288 	New->In_File=NULL;
3289 	New->Out_File=NULL;
3290 
3291 	GET(IDENTIFIER_TOKEN)
3292 	Entry = Add_Symbol (1,Token.Token_String,FILE_ID_TOKEN);
3293 	Entry->Data=(void *)New;
3294 
3295 	asciitemp = Parse_C_String(true);
3296 	temp = ASCIItoUCS2String(asciitemp);
3297 	POV_FREE(asciitemp);
3298 
3299 	EXPECT
3300 		CASE(READ_TOKEN)
3301 			New->R_Flag = true;
3302 			rfile = Locate_File(this, sceneData, temp.c_str(), POV_File_Text_User, ign, true);
3303 			if(rfile != NULL)
3304 				New->In_File = new ITextStream(temp.c_str(), rfile);
3305 			else
3306 				New->In_File = NULL;
3307 
3308 			if(New->In_File == NULL)
3309 				Error ("Cannot open user file %s (read).", UCS2toASCIIString(temp).c_str());
3310 			EXIT
3311 		END_CASE
3312 
3313 		CASE(WRITE_TOKEN)
3314 			New->R_Flag = false;
3315 			wfile = sceneData->CreateFile(GetPOVMSContext(), temp.c_str(), POV_File_Text_User, false);
3316 			if(wfile != NULL)
3317 				New->Out_File= new OTextStream(temp.c_str(), wfile);
3318 			else
3319 				New->Out_File = NULL;
3320 
3321 			if(New->Out_File == NULL)
3322 				Error ("Cannot open user file %s (write).", UCS2toASCIIString(temp).c_str());
3323 			EXIT
3324 		END_CASE
3325 
3326 		CASE(APPEND_TOKEN)
3327 			New->R_Flag = false;
3328 			wfile = sceneData->CreateFile(GetPOVMSContext(), temp.c_str(), POV_File_Text_User, true);
3329 			if(wfile != NULL)
3330 				New->Out_File= new OTextStream(temp.c_str(), wfile);
3331 			else
3332 				New->Out_File = NULL;
3333 
3334 			if(New->Out_File == NULL)
3335 				Error ("Cannot open user file %s (append).", UCS2toASCIIString(temp).c_str());
3336 			EXIT
3337 		END_CASE
3338 
3339 		OTHERWISE
3340 			Expectation_Error("read or write");
3341 		END_CASE
3342 	END_EXPECT
3343 }
3344 
Parse_Fclose(void)3345 void Parser::Parse_Fclose(void)
3346 {
3347 	DATA_FILE *Data;
3348 
3349 	EXPECT
3350 		CASE(FILE_ID_TOKEN)
3351 			Data=(DATA_FILE *)Token.Data;
3352 			if(Data->In_File != NULL)
3353 				delete Data->In_File;
3354 			if(Data->Out_File != NULL)
3355 				delete Data->Out_File;
3356 			Got_EOF=false;
3357 			Data->In_File = NULL;
3358 			Data->Out_File = NULL;
3359 			Remove_Symbol (1,Token.Token_String,false,NULL,0);
3360 			EXIT
3361 		END_CASE
3362 
3363 		OTHERWISE
3364 			EXIT
3365 		END_CASE
3366 	END_EXPECT
3367 }
3368 
Parse_Read()3369 void Parser::Parse_Read()
3370 {
3371 	DATA_FILE *User_File;
3372 	SYM_ENTRY *Temp_Entry;
3373 	int End_File=false;
3374 	char *File_Id;
3375 
3376 	GET(LEFT_PAREN_TOKEN)
3377 
3378 	GET(FILE_ID_TOKEN)
3379 	User_File=(DATA_FILE *)Token.Data;
3380 	File_Id=POV_STRDUP(Token.Token_String);
3381 	if(User_File->In_File == NULL)
3382 		Error("Cannot read from file %s because the file is open for writing only.", UCS2toASCIIString(UCS2String(User_File->Out_File->name())).c_str());
3383 
3384 	Parse_Comma(); /* Scene file comma between File_Id and 1st data ident */
3385 
3386 	LValue_Ok = true;
3387 
3388 	EXPECT
3389 		CASE (IDENTIFIER_TOKEN)
3390 			if (!End_File)
3391 			{
3392 				Temp_Entry = Add_Symbol (1,Token.Token_String,IDENTIFIER_TOKEN);
3393 				End_File=Parse_Read_Value (User_File,Token.Token_Id, &(Temp_Entry->Token_Number), &(Temp_Entry->Data));
3394 				Token.is_array_elem = false;
3395 				Parse_Comma(); /* Scene file comma between 2 idents */
3396 			}
3397 		END_CASE
3398 
3399 		CASE (STRING_ID_TOKEN)
3400 			if (!End_File)
3401 			{
3402 				End_File=Parse_Read_Value (User_File,Token.Token_Id,Token.NumberPtr,Token.DataPtr);
3403 				Token.is_array_elem = false;
3404 				Parse_Comma(); /* Scene file comma between 2 idents */
3405 			}
3406 		END_CASE
3407 
3408 		CASE2 (VECTOR_FUNCT_TOKEN,FLOAT_FUNCT_TOKEN)
3409 			switch(Token.Function_Id)
3410 			{
3411 				case VECTOR_ID_TOKEN:
3412 				case FLOAT_ID_TOKEN:
3413 					if (!End_File)
3414 					{
3415 						End_File=Parse_Read_Value (User_File,Token.Function_Id,Token.NumberPtr,Token.DataPtr);
3416 						Parse_Comma(); /* Scene file comma between 2 idents */
3417 					}
3418 					break;
3419 
3420 				default:
3421 					Parse_Error(IDENTIFIER_TOKEN);
3422 					break;
3423 			}
3424 		END_CASE
3425 
3426 		CASE(COMMA_TOKEN)
3427 			if (!End_File)
3428 			{
3429 				Parse_Error(IDENTIFIER_TOKEN);
3430 			}
3431 		END_CASE
3432 
3433 		CASE(RIGHT_PAREN_TOKEN)
3434 			EXIT
3435 		END_CASE
3436 
3437 		OTHERWISE
3438 			Parse_Error(IDENTIFIER_TOKEN);
3439 		END_CASE
3440 	END_EXPECT
3441 
3442 	LValue_Ok = false;
3443 
3444 	if (End_File)
3445 	{
3446 		delete User_File->In_File;
3447 		Got_EOF=false;
3448 		User_File->In_File = NULL;
3449 		Remove_Symbol (1,File_Id,false,NULL,0);
3450 	}
3451 	POV_FREE(File_Id);
3452 }
3453 
Parse_Read_Value(DATA_FILE * User_File,int Previous,int * NumberPtr,void ** DataPtr)3454 int Parser::Parse_Read_Value(DATA_FILE *User_File,int Previous,int *NumberPtr,void **DataPtr)
3455 {
3456 	pov_base::ITextStream *Temp;
3457 	bool Temp_R_Flag;
3458 	DBL Val;
3459 	int End_File=false;
3460 	int i;
3461 	EXPRESS Express;
3462 
3463 	Temp = Input_File->In_File;
3464 	Temp_R_Flag = Input_File->R_Flag;
3465 	Input_File->In_File = User_File->In_File;
3466 	Input_File->R_Flag = User_File->R_Flag;
3467 	if(User_File->In_File == NULL)
3468 		Error("Cannot read from file '%s' because the file is open for writing only.", UCS2toASCIIString(UCS2String(User_File->Out_File->name())).c_str());
3469 	User_File->In_File = NULL; // take control over pointer
3470 
3471 	try
3472 	{
3473 		EXPECT
3474 			CASE3 (PLUS_TOKEN,DASH_TOKEN,FLOAT_FUNCT_TOKEN)
3475 				UNGET
3476 				Val=Parse_Signed_Float();
3477 				*NumberPtr = FLOAT_ID_TOKEN;
3478 				Test_Redefine(Previous,NumberPtr,*DataPtr);
3479 				*DataPtr   = (void *) Create_Float();
3480 				*((DBL *)*DataPtr) = Val;
3481 				Parse_Comma(); /* data file comma between 2 data items  */
3482 				EXIT
3483 			END_CASE
3484 
3485 			CASE (LEFT_ANGLE_TOKEN)
3486 				i=1;
3487 				Express[X]=Parse_Signed_Float();  Parse_Comma();
3488 				Express[Y]=Parse_Signed_Float();  Parse_Comma();
3489 
3490 				EXPECT
3491 					CASE3 (PLUS_TOKEN,DASH_TOKEN,FLOAT_FUNCT_TOKEN)
3492 						UNGET
3493 						if (++i>4)
3494 						{
3495 							Error("Vector data too long");
3496 						}
3497 						Express[i]=Parse_Signed_Float(); Parse_Comma();
3498 					END_CASE
3499 
3500 					CASE (RIGHT_ANGLE_TOKEN)
3501 						EXIT
3502 					END_CASE
3503 
3504 					OTHERWISE
3505 						Expectation_Error("vector");
3506 					END_CASE
3507 				END_EXPECT
3508 
3509 				switch(i)
3510 				{
3511 					case 1:
3512 						*NumberPtr = UV_ID_TOKEN;
3513 						Test_Redefine(Previous,NumberPtr,*DataPtr);
3514 						*DataPtr   = (void *) Create_UV_Vect();
3515 						Assign_UV_Vect((DBL *)*DataPtr, Express);
3516 						break;
3517 
3518 					case 2:
3519 						*NumberPtr = VECTOR_ID_TOKEN;
3520 						Test_Redefine(Previous,NumberPtr,*DataPtr);
3521 						*DataPtr   = (void *) Create_Vector();
3522 						Assign_Vector((DBL *)*DataPtr, Express);
3523 						break;
3524 
3525 					case 3:
3526 						*NumberPtr = VECTOR_4D_ID_TOKEN;
3527 						Test_Redefine(Previous,NumberPtr,*DataPtr);
3528 						*DataPtr   = (void *) Create_Vector_4D();
3529 						Assign_Vector_4D((DBL *)*DataPtr, Express);
3530 						break;
3531 
3532 					case 4:
3533 						*NumberPtr    = COLOUR_ID_TOKEN;
3534 						Test_Redefine(Previous,NumberPtr,*DataPtr);
3535 						*DataPtr      = (void *) Create_Colour();
3536 						Assign_Colour_Express((COLC*)(*DataPtr), Express); /* NK fix assign_colour bug */
3537 						break;
3538 				}
3539 
3540 				Parse_Comma(); // data file comma between 2 data items
3541 				EXIT
3542 			END_CASE
3543 
3544 			CASE(STRING_LITERAL_TOKEN)
3545 				*NumberPtr = STRING_ID_TOKEN;
3546 				Test_Redefine(Previous,NumberPtr,*DataPtr);
3547 				*DataPtr   = String_To_UCS2(Token.Token_String, false);
3548 				Parse_Comma(); // data file comma between 2 data items
3549 				EXIT
3550 			END_CASE
3551 
3552 			CASE (END_OF_FILE_TOKEN)
3553 				EXIT
3554 			END_CASE
3555 
3556 			OTHERWISE
3557 				Expectation_Error ("float, vector, or string literal");
3558 			END_CASE
3559 		END_EXPECT
3560 	}
3561 	catch (...)
3562 	{
3563 		// re-assign the file pointers so that they are properly disposed of later on
3564 		User_File->In_File = Input_File->In_File;
3565 		Input_File->In_File = Temp;
3566 		Input_File->R_Flag = Temp_R_Flag;
3567 		throw;
3568 	}
3569 
3570 	if (Token.Token_Id==END_OF_FILE_TOKEN)
3571 		End_File = true;
3572 
3573 	Token.End_Of_File = false;
3574 	Token.Unget_Token = false;
3575 	Got_EOF = false;
3576 	User_File->In_File = Input_File->In_File; // return control over pointer
3577 	Input_File->In_File = Temp;
3578 	Input_File->R_Flag = Temp_R_Flag;
3579 
3580 	return End_File ;
3581 }
3582 
Parse_Write(void)3583 void Parser::Parse_Write(void)
3584 {
3585 	char *temp;
3586 	DATA_FILE *User_File;
3587 	EXPRESS Express;
3588 	int Terms;
3589 
3590 	GET(LEFT_PAREN_TOKEN)
3591 	GET(FILE_ID_TOKEN)
3592 
3593 	User_File=(DATA_FILE *)Token.Data;
3594 	if(User_File->Out_File == NULL)
3595 		Error("Cannot write to file %s because the file is open for reading only.", UCS2toASCIIString(UCS2String(User_File->In_File->name())).c_str());
3596 
3597 	Parse_Comma();
3598 
3599 	EXPECT
3600 		CASE5 (SINT8_TOKEN,SINT16BE_TOKEN,SINT16LE_TOKEN,SINT32BE_TOKEN,SINT32LE_TOKEN)
3601 		CASE3 (UINT8_TOKEN,UINT16BE_TOKEN,UINT16LE_TOKEN)
3602 			{
3603 				signed long val_min;
3604 				signed long val_max;
3605 				int  num_bytes;
3606 				bool big_endian = false;
3607 				switch (Token.Token_Id)
3608 				{
3609 					case SINT8_TOKEN:                                        val_min =            -128; val_max =        127; num_bytes = 1; break; // -2^7  to 2^7-1
3610 					case UINT8_TOKEN:                                        val_min =               0; val_max =        255; num_bytes = 1; break; //  0    to 2^8-1
3611 					case SINT16BE_TOKEN: big_endian = true; // FALLTHROUGH
3612 					case SINT16LE_TOKEN:                                     val_min =          -32768; val_max =      32767; num_bytes = 2; break; // -2^15 to 2^15-1
3613 					case UINT16BE_TOKEN: big_endian = true; // FALLTHROUGH
3614 					case UINT16LE_TOKEN:                                     val_min =               0; val_max =      65535; num_bytes = 2; break; //  0    to 2^16-1
3615 					case SINT32BE_TOKEN: big_endian = true; // FALLTHROUGH
3616 					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)
3617 				}
3618 				EXPECT
3619 					CASE_VECTOR
3620 						Terms = Parse_Unknown_Vector (Express);
3621 						if ((Terms >= 1) && (Terms <= 5))
3622 						{
3623 							for (int i = 0; i < Terms; i ++)
3624 							{
3625 								signed long val;
3626 								if (Express[i] <= val_min)
3627 									val = val_min; // TODO - maybe we should warn the user
3628 								else if (Express[i] >= val_max)
3629 									val = val_max; // TODO - maybe we should warn the user
3630 								else
3631 									val = (signed long)(floor(Express[i]+0.5));
3632 								for (int j = 0; j < num_bytes; j ++)
3633 								{
3634 									int bitShift = (big_endian? (num_bytes-1)-j : j) * 8;
3635 									User_File->Out_File->putraw((val >> bitShift) & 0xFF);
3636 								}
3637 							}
3638 						}
3639 						else
3640 						{
3641 							Expectation_Error("expression");
3642 						}
3643 					END_CASE
3644 					CASE (RIGHT_PAREN_TOKEN)
3645 						UNGET
3646 						EXIT
3647 					END_CASE
3648 					CASE (COMMA_TOKEN)
3649 						UNGET
3650 						EXIT
3651 					END_CASE
3652 					OTHERWISE
3653 						Expectation_Error("expression");
3654 					END_CASE
3655 				END_EXPECT
3656 			}
3657 		END_CASE
3658 
3659 		CASE5 (STRING_LITERAL_TOKEN,CHR_TOKEN,SUBSTR_TOKEN,STR_TOKEN,VSTR_TOKEN)
3660 		CASE5 (CONCAT_TOKEN,STRUPR_TOKEN,STRLWR_TOKEN,DATETIME_TOKEN,STRING_ID_TOKEN)
3661 			UNGET
3662 			temp=Parse_C_String();
3663 			if(strlen(temp) > 512)
3664 			{
3665 				for(char *ptr = temp; *ptr != 0; ptr++)
3666 					User_File->Out_File->printf("%c", *ptr);
3667 			}
3668 			else
3669 				User_File->Out_File->printf("%s", temp);
3670 			POV_FREE(temp);
3671 		END_CASE
3672 
3673 		CASE_VECTOR
3674 			Terms = Parse_Unknown_Vector (Express);
3675 			switch (Terms)
3676 			{
3677 				case 1:
3678 					User_File->Out_File->printf("%g",Express[X]);
3679 					break;
3680 
3681 				case 2:
3682 					User_File->Out_File->printf("<%g,%g> ",Express[U],Express[V]);
3683 					break;
3684 
3685 				case 3:
3686 					User_File->Out_File->printf("<%g,%g,%g> ",Express[X],Express[Y],Express[Z]);
3687 					break;
3688 
3689 				case 4:
3690 					User_File->Out_File->printf("<%g,%g,%g,%g> ",Express[X],Express[Y],Express[Z],Express[T]);
3691 					break;
3692 
3693 				case 5:
3694 					User_File->Out_File->printf("<%g,%g,%g,%g,%g> ",Express[X],Express[Y],Express[Z],Express[3],Express[4]);
3695 					break;
3696 
3697 				default:
3698 					Expectation_Error("expression");
3699 			}
3700 		END_CASE
3701 
3702 		CASE (RIGHT_PAREN_TOKEN)
3703 			EXIT
3704 		END_CASE
3705 
3706 		CASE (COMMA_TOKEN)
3707 		END_CASE
3708 
3709 		OTHERWISE
3710 			Expectation_Error("string");
3711 		END_CASE
3712 	END_EXPECT
3713 }
3714 
Parse_Cond_Param(void)3715 DBL Parser::Parse_Cond_Param(void)
3716 {
3717 	int Old_Ok = Ok_To_Declare;
3718 	int Old_Sk = Skipping;
3719 	DBL Val;
3720 
3721 	Ok_To_Declare = false;
3722 	Skipping      = false;
3723 
3724 	Val=Parse_Float_Param();
3725 
3726 	Ok_To_Declare = Old_Ok;
3727 	Skipping      = Old_Sk;
3728 
3729 	return(Val);
3730 }
3731 
Parse_Cond_Param2(DBL * V1,DBL * V2)3732 void Parser::Parse_Cond_Param2(DBL *V1,DBL *V2)
3733 {
3734 	int Old_Ok = Ok_To_Declare;
3735 	int Old_Sk = Skipping;
3736 
3737 	Ok_To_Declare = false;
3738 	Skipping      = false;
3739 
3740 	Parse_Float_Param2(V1,V2);
3741 
3742 	Ok_To_Declare = Old_Ok;
3743 	Skipping      = Old_Sk;
3744 }
3745 
Inc_CS_Index(void)3746 void Parser::Inc_CS_Index(void)
3747 {
3748 	if (++CS_Index >= COND_STACK_SIZE)
3749 	{
3750 		Error("Too many nested conditionals or macros.");
3751 	}
3752 	Cond_Stack[CS_Index].Macro_File = NULL;
3753 	Cond_Stack[CS_Index].Macro_Return_Name = NULL;
3754 	Cond_Stack[CS_Index].PMac = NULL;
3755 	Cond_Stack[CS_Index].Loop_File = NULL;
3756 	Cond_Stack[CS_Index].Loop_Identifier = NULL;
3757 }
3758 
Parse_Ifdef_Param(void)3759 int Parser::Parse_Ifdef_Param (void)
3760 {
3761 	int Local_Index;
3762 	SYM_ENTRY *Entry;
3763 	int retval = false;
3764 	int i,j,k;
3765 	int c;
3766 	DBL val;
3767 	POV_ARRAY *a;
3768 
3769 	GET(LEFT_PAREN_TOKEN)
3770 	Inside_Ifdef=true;
3771 	Get_Token();
3772 	String2 = POV_STRDUP(String);
3773 	Inside_Ifdef=false;
3774 
3775 	/* Search tables from newest to oldest */
3776 	for (Local_Index=Table_Index; Local_Index > 0; Local_Index--)
3777 	{
3778 		Entry = Find_Symbol(Local_Index,String2);
3779 		if ( Entry != NULL)
3780 		{
3781 			Token.Token_Id  =   Entry->Token_Number;
3782 			Token.is_array_elem = false;
3783 			Token.NumberPtr = &(Entry->Token_Number);
3784 			Token.DataPtr   = &(Entry->Data);
3785 
3786 			if ( Token.Token_Id == PARAMETER_ID_TOKEN )
3787 			{
3788 				Token.NumberPtr = ((POV_PARAM *)(Entry->Data))->NumberPtr;
3789 				Token.DataPtr   = ((POV_PARAM *)(Entry->Data))->DataPtr;
3790 			}
3791 
3792 			if (Token.NumberPtr && *(Token.NumberPtr)==ARRAY_ID_TOKEN)
3793 			{
3794 				Skip_Spaces();
3795 				c = Echo_getc();
3796 				Echo_ungetc(c);
3797 
3798 				if (c!='[')
3799 				{
3800 					retval = true;
3801 					break;
3802 				}
3803 
3804 				a = (POV_ARRAY *)(*(Token.DataPtr));
3805 				j = 0;
3806 
3807 				for (i=0; i <= a->Dims; i++)
3808 				{
3809 					GET(LEFT_SQUARE_TOKEN)
3810 					val=Parse_Float();
3811 
3812 					k=(int)(val + EPSILON);
3813 
3814 					if (k<0.0)
3815 					{
3816 						Error("Negative subscript");
3817 					}
3818 
3819 					if (k>=a->Sizes[i])
3820 					{
3821 						Error("Array subscript out of range");
3822 					}
3823 					j += k * a->Mags[i];
3824 					GET(RIGHT_SQUARE_TOKEN)
3825 				}
3826 
3827 				Token.DataPtr   = &(a->DataPtrs[j]);
3828 				Token.NumberPtr = &(a->Type);
3829 				Token.Token_Id = a->Type;
3830 				Token.is_array_elem = true;
3831 
3832 				retval = (*Token.DataPtr != NULL);
3833 				break;
3834 			}
3835 			else
3836 			{
3837 				retval = true;
3838 				break;
3839 			}
3840 		}
3841 	}
3842 
3843 	GET(RIGHT_PAREN_TOKEN)
3844 
3845 	POV_FREE(String2);
3846 	String2 = NULL;
3847 
3848 	return retval;
3849 }
3850 
Parse_For_Param(char ** IdentifierPtr,DBL * EndPtr,DBL * StepPtr)3851 int Parser::Parse_For_Param (char** IdentifierPtr, DBL* EndPtr, DBL* StepPtr)
3852 {
3853 	int Previous=-1;
3854 	SYM_ENTRY *Temp_Entry = NULL;
3855 
3856 	GET(LEFT_PAREN_TOKEN)
3857 
3858 	LValue_Ok = true;
3859 
3860 	EXPECT_ONE
3861 		CASE (IDENTIFIER_TOKEN)
3862 			if (Token.is_array_elem)
3863 				Error("#for loop variable must not be an array element");
3864 			Temp_Entry = Add_Symbol (Table_Index,Token.Token_String,IDENTIFIER_TOKEN);
3865 			Token.NumberPtr = &(Temp_Entry->Token_Number);
3866 			Token.DataPtr = &(Temp_Entry->Data);
3867 			Previous = Token.Token_Id;
3868 		END_CASE
3869 
3870 		CASE2 (FUNCT_ID_TOKEN, VECTFUNCT_ID_TOKEN)
3871 			if (Token.is_array_elem)
3872 				Error("#for loop variable must not be an array element");
3873 			if((!Token.is_array_elem) || (*(Token.DataPtr) != NULL))
3874 				Error("Redeclaring functions is not allowed - #undef the function first!");
3875 			// fall through
3876 
3877 		// These have to match Parse_Declare in parse.cpp!
3878 		CASE4 (TNORMAL_ID_TOKEN, FINISH_ID_TOKEN, TEXTURE_ID_TOKEN, OBJECT_ID_TOKEN)
3879 		CASE4 (COLOUR_MAP_ID_TOKEN, TRANSFORM_ID_TOKEN, CAMERA_ID_TOKEN, PIGMENT_ID_TOKEN)
3880 		CASE4 (SLOPE_MAP_ID_TOKEN, NORMAL_MAP_ID_TOKEN, TEXTURE_MAP_ID_TOKEN, COLOUR_ID_TOKEN)
3881 		CASE4 (PIGMENT_MAP_ID_TOKEN, MEDIA_ID_TOKEN, STRING_ID_TOKEN, INTERIOR_ID_TOKEN)
3882 		CASE4 (DENSITY_MAP_ID_TOKEN, ARRAY_ID_TOKEN, DENSITY_ID_TOKEN, UV_ID_TOKEN)
3883 		CASE4 (VECTOR_4D_ID_TOKEN, RAINBOW_ID_TOKEN, FOG_ID_TOKEN, SKYSPHERE_ID_TOKEN)
3884 		CASE2 (MATERIAL_ID_TOKEN, SPLINE_ID_TOKEN )
3885 			if (Token.is_array_elem)
3886 				Error("#for loop variable must not be an array element");
3887 			if (Token.Table_Index != Table_Index)
3888 			{
3889 				Temp_Entry = Add_Symbol (Table_Index,Token.Token_String,IDENTIFIER_TOKEN);
3890 				Token.NumberPtr = &(Temp_Entry->Token_Number);
3891 				Token.DataPtr   = &(Temp_Entry->Data);
3892 				Previous        = IDENTIFIER_TOKEN;
3893 			}
3894 			else
3895 			{
3896 				Previous        = Token.Token_Id;
3897 			}
3898 		END_CASE
3899 
3900 		CASE (EMPTY_ARRAY_TOKEN)
3901 			if (Token.is_array_elem)
3902 				Error("#for loop variable must not be an array element");
3903 			Previous = Token.Token_Id;
3904 		END_CASE
3905 
3906 		CASE2 (VECTOR_FUNCT_TOKEN, FLOAT_FUNCT_TOKEN)
3907 			if (Token.is_array_elem)
3908 				Error("#for loop variable must not be an array element");
3909 			switch(Token.Function_Id)
3910 			{
3911 				case VECTOR_ID_TOKEN:
3912 				case FLOAT_ID_TOKEN:
3913 					if (Token.Table_Index != Table_Index)
3914 					{
3915 						Temp_Entry = Add_Symbol (Table_Index,Token.Token_String,IDENTIFIER_TOKEN);
3916 						Token.NumberPtr = &(Temp_Entry->Token_Number);
3917 						Token.DataPtr   = &(Temp_Entry->Data);
3918 					}
3919 					Previous           = Token.Function_Id;
3920 					break;
3921 
3922 				default:
3923 					Parse_Error(IDENTIFIER_TOKEN);
3924 					break;
3925 			}
3926 		END_CASE
3927 
3928 		OTHERWISE
3929 			if (Token.is_array_elem)
3930 				Error("#for loop variable must not be an array element");
3931 			Parse_Error(IDENTIFIER_TOKEN);
3932 		END_CASE
3933 	END_EXPECT
3934 
3935 	LValue_Ok = false;
3936 
3937 	*Token.NumberPtr = FLOAT_ID_TOKEN;
3938 	Test_Redefine(Previous,Token.NumberPtr,*Token.DataPtr, true);
3939 	*Token.DataPtr   = (void *) Create_Float();
3940 	DBL* CurrentPtr = ((DBL *)*Token.DataPtr);
3941 
3942 	size_t len = strlen(Token.Token_String)+1;
3943 	*IdentifierPtr = (char*)POV_MALLOC(len, "loop identifier");
3944 	memcpy(*IdentifierPtr, Token.Token_String, len);
3945 
3946 	Parse_Comma();
3947 	*CurrentPtr = Parse_Float();
3948 	Parse_Comma();
3949 	*EndPtr = Parse_Float();
3950 	Parse_Comma();
3951 	*StepPtr = Allow_Float(1.0);
3952 
3953 	if (fabs(*StepPtr) < EPSILON)
3954 		Error ("#for loop increment must be non-zero.");
3955 
3956 	GET(RIGHT_PAREN_TOKEN)
3957 
3958 	return ((*StepPtr > 0) && (*CurrentPtr < *EndPtr + EPSILON)) ||
3959 	       ((*StepPtr < 0) && (*CurrentPtr > *EndPtr - EPSILON));
3960 }
3961 
3962 /*****************************************************************************
3963 *
3964 * FUNCTION
3965 *
3966 * INPUT
3967 *
3968 * OUTPUT
3969 *
3970 * RETURNS
3971 *
3972 * AUTHOR
3973 *
3974 * DESCRIPTION
3975 *
3976 * CHANGES
3977 *
3978 ******************************************************************************/
3979 
IncludeHeader(const UCS2String & temp)3980 void Parser::IncludeHeader(const UCS2String& temp)
3981 {
3982 	UCS2String b;
3983 
3984 	if (temp.empty())
3985 		return;
3986 
3987 	if (++Include_File_Index >= MAX_INCLUDE_FILES)
3988 	{
3989 		Include_File_Index--;
3990 		Error ("Too many nested include files.");
3991 	}
3992 
3993 	Echo_Indx = 0;
3994 
3995 	Input_File = &Include_Files[Include_File_Index];
3996 	Input_File->In_File = NULL;
3997 	IStream *is = Locate_File (this, sceneData, temp.c_str(),POV_File_Text_INC,b,true);
3998 	if(is == NULL)
3999 	{
4000 		Input_File->In_File = NULL;  /* Keeps from closing failed file. */
4001 		Error ("Cannot open include header file %s.", UCS2toASCIIString(temp).c_str());
4002 	}
4003 	else
4004 		Input_File->In_File = new ITextStream(b.c_str(), is);
4005 
4006 	Input_File->R_Flag=false;
4007 
4008 	Add_Sym_Table();
4009 
4010 	Token.Token_Id = END_OF_FILE_TOKEN;
4011 	Token.is_array_elem = false;
4012 }
4013 
4014 }
4015