1 /*
2  * lexi.c
3  *
4  * $Id: pr_lex.c,v 1.14 2007-12-14 16:41:17 sezero Exp $
5  *
6  * Copyright (C) 1996-1997  Id Software, Inc.
7  * Copyright (C) 1997 Eric Hobbs <elhobbs@comcast.net>
8  * Copyright (C) 2005-2012 O.Sezer <sezero@users.sourceforge.net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or (at
13  * your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * See the GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License along
22  * with this program; if not, write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
24  */
25 
26 
27 #include "q_stdinc.h"
28 #include "compiler.h"
29 #include "arch_def.h"
30 #include "cmdlib.h"
31 #include "qcc.h"
32 
33 const char	*pr_file_p;
34 int		pr_source_line;
35 
36 static int	pr_bracelevel;
37 
38 char		pr_token[2048];
39 token_type_t	pr_token_type;
40 type_t		*pr_immediate_type;
41 eval_t		pr_immediate;
42 
43 char		pr_immediate_string[2048];
44 
45 int		pr_error_count;
46 
47 static const char	*pr_punctuation[] =
48 {
49 	// longer symbols must be before
50 	// a shorter partial match
51 	"&&",		"||",
52 	"<=",		">=",
53 	"==",		"!=",
54 	";",		",",
55 	"!",		"*=",
56 	"*",		"/=",
57 	"/",		"(", ")",
58 	"-=",		"->",
59 	"-",		"+=",
60 	"+",		"=",
61 	"[", "]",	"{", "}",
62 	"...",		".",
63 	"<",		">",
64 	"#",		"&",
65 	"|=",		"|",
66 	"^=",		":",
67 	NULL
68 };
69 
70 // simple types.  function types are dynamically allocated
71 type_t	type_void	= {ev_void, &def_void};
72 type_t	type_string	= {ev_string, &def_string};
73 type_t	type_float	= {ev_float, &def_float};
74 type_t	type_vector	= {ev_vector, &def_vector};
75 type_t	type_entity	= {ev_entity, &def_entity};
76 type_t	type_field	= {ev_field, &def_field};
77 type_t	type_function	= {ev_function, &def_function,NULL,&type_void};
78 // type_function is a void() function used for state defs
79 type_t	type_pointer	= {ev_pointer, &def_pointer};
80 type_t	type_floatfield	= {ev_field, &def_field, NULL, &type_float};
81 
82 int	type_size[8]	= {1, 1, 1, 3, 1, 1, 1, 1};
83 
84 def_t	def_void	= {&type_void, "temp"};
85 def_t	def_string	= {&type_string, "temp"};
86 def_t	def_float	= {&type_float, "temp"};
87 def_t	def_vector	= {&type_vector, "temp"};
88 def_t	def_entity	= {&type_entity, "temp"};
89 def_t	def_field	= {&type_field, "temp"};
90 def_t	def_function	= {&type_function, "temp"};
91 def_t	def_pointer	= {&type_pointer, "temp"};
92 
93 def_t	def_ret, def_parms[MAX_PARMS];
94 
95 def_t *def_for_type[8] =
96 {
97 	&def_void, &def_string, &def_float, &def_vector,
98 	&def_entity, &def_field, &def_function, &def_pointer
99 };
100 
101 static void PR_LexWhitespace (void);
102 
103 
104 /*
105 ==============
106 PR_NewLine
107 
108 Call at start of file and when *pr_file_p == '\n'
109 ==============
110 */
PR_NewLine(void)111 void PR_NewLine (void)
112 {
113 	pr_source_line++;
114 }
115 
116 /*
117 ==============
118 PR_LexString
119 
120 Parses a quoted string
121 ==============
122 */
PR_LexString(void)123 static void PR_LexString (void)
124 {
125 	int		c;
126 	int		len;
127 
128 	len = 0;
129 	pr_file_p++;
130 	do
131 	{
132 		c = *pr_file_p++;
133 		if (!c)
134 			PR_ParseError ("EOF inside quote");
135 		if (c == '\n')
136 			PR_ParseError ("newline inside quote");
137 		if (c == '\\')
138 		{	// escape char
139 			c = *pr_file_p++;
140 			if (!c)
141 				PR_ParseError ("EOF inside quote");
142 			if (c == 'n')
143 				c = '\n';
144 			else if (c == '"')
145 				c = '"';
146 			else
147 				PR_ParseError ("Unknown escape char");
148 		}
149 		else if (c == '\"')
150 		{
151 			pr_token[len] = 0;
152 			pr_token_type = tt_immediate;
153 			pr_immediate_type = &type_string;
154 			strcpy (pr_immediate_string, pr_token);
155 			return;
156 		}
157 		pr_token[len] = c;
158 		len++;
159 	} while (1);
160 }
161 
162 /*
163 ==============
164 PR_LexNumber
165 ==============
166 */
PR_LexNumber(void)167 static float PR_LexNumber (void)
168 {
169 	int		c;
170 	int		len;
171 
172 	len = 0;
173 	c = *pr_file_p;
174 	do
175 	{
176 		pr_token[len] = c;
177 		len++;
178 		pr_file_p++;
179 		c = *pr_file_p;
180 	} while ((c >= '0' && c<= '9') || c == '.');
181 
182 	pr_token[len] = 0;
183 	return atof (pr_token);
184 }
185 
186 /*
187 ==============
188 PR_LexVector
189 
190 Parses a single quoted vector
191 ==============
192 */
PR_LexVector(void)193 static void PR_LexVector (void)
194 {
195 	int		i;
196 
197 	pr_file_p++;
198 	pr_token_type = tt_immediate;
199 	pr_immediate_type = &type_vector;
200 	for (i = 0; i < 3; i++)
201 	{
202 		pr_immediate.vector[i] = PR_LexNumber ();
203 		PR_LexWhitespace ();
204 	}
205 	if (*pr_file_p != '\'')
206 		PR_ParseError ("Bad vector");
207 	pr_file_p++;
208 }
209 
210 /*
211 ==============
212 PR_LexName
213 
214 Parses an identifier
215 ==============
216 */
PR_LexName(void)217 static void PR_LexName (void)
218 {
219 	int		c;
220 	int		len;
221 
222 	len = 0;
223 	c = *pr_file_p;
224 	do
225 	{
226 		pr_token[len] = c;
227 		len++;
228 		pr_file_p++;
229 		c = *pr_file_p;
230 	} while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'
231 						|| (c >= '0' && c <= '9'));
232 	pr_token[len] = 0;
233 	pr_token_type = tt_name;
234 }
235 
236 /*
237 ==============
238 PR_LexPunctuation
239 ==============
240 */
PR_LexPunctuation(void)241 static void PR_LexPunctuation (void)
242 {
243 	int		i;
244 	int		len;
245 	const char	*p;
246 
247 	pr_token_type = tt_punct;
248 
249 	for (i = 0; (p = pr_punctuation[i]) != NULL; i++)
250 	{
251 		len = strlen(p);
252 		if (!strncmp(p, pr_file_p, len) )
253 		{
254 			strcpy (pr_token, p);
255 			if (p[0] == '{')
256 				pr_bracelevel++;
257 			else if (p[0] == '}')
258 				pr_bracelevel--;
259 			pr_file_p += len;
260 			return;
261 		}
262 	}
263 
264 	PR_ParseError ("Unknown punctuation");
265 }
266 
267 /*
268 ==============
269 PR_LexWhitespace
270 ==============
271 */
PR_LexWhitespace(void)272 static void PR_LexWhitespace (void)
273 {
274 	int		c;
275 
276 	while (1)
277 	{
278 	// skip whitespace
279 		while ((c = *pr_file_p) <= ' ')
280 		{
281 			if (c == '\n')
282 			{
283 				PR_NewLine ();
284 				pr_file_p++;
285 				if (*pr_file_p == 0)	// EOF
286 					return;
287 			}
288 			else
289 			{
290 				if (c == 0)
291 					return;		// EOF
292 				pr_file_p++;
293 			}
294 		}
295 
296 	// skip // comments
297 		if (c == '/' && pr_file_p[1] == '/')
298 		{
299 			while (*pr_file_p && *pr_file_p != '\n')
300 				pr_file_p++;
301 			PR_NewLine();
302 			if (*pr_file_p == '\n')	// not when EOF
303 				pr_file_p++;
304 			continue;
305 		}
306 
307 	// skip /* */ comments
308 		if (c == '/' && pr_file_p[1] == '*')
309 		{
310 			pr_file_p += 2;
311 			do
312 			{
313 				if (pr_file_p[0] == '\n')
314 					PR_NewLine();
315 				if (pr_file_p[0] == 0 || pr_file_p[1] == 0)
316 					PR_ParseError("EOF inside comment");
317 				pr_file_p++;
318 			} while (pr_file_p[-1] != '*' || pr_file_p[0] != '/');
319 			pr_file_p++;
320 			continue;
321 		}
322 
323 		break;	// a real character has been found
324 	}
325 }
326 
327 //============================================================================
328 
329 #define	MAX_FRAMES	256
330 
331 static char	pr_framemacros[MAX_FRAMES][16];
332 static int		pr_nummacros;
333 
PR_ClearGrabMacros(void)334 void PR_ClearGrabMacros (void)
335 {
336 	pr_nummacros = 0;
337 }
338 
PR_FindMacro(void)339 static void PR_FindMacro (void)
340 {
341 	int		i;
342 
343 	for (i = 0; i < pr_nummacros; i++)
344 	{
345 		if (!strcmp (pr_token, pr_framemacros[i]))
346 		{
347 			sprintf (pr_token, "%d", i);
348 			pr_token_type = tt_immediate;
349 			pr_immediate_type = &type_float;
350 			pr_immediate._float = i;
351 			return;
352 		}
353 	}
354 	PR_ParseError ("Unknown frame macro $%s", pr_token);
355 }
356 
357 // just parses text, returning false if an eol is reached
PR_SimpleGetToken(void)358 static qboolean PR_SimpleGetToken (void)
359 {
360 	int		c;
361 	int		i;
362 
363 // skip whitespace
364 	while ((c = *pr_file_p) <= ' ')
365 	{
366 		if (c == '\n' || c == 0)
367 			return false;
368 		pr_file_p++;
369 	}
370 
371 	i = 0;
372 	while ((c = *pr_file_p) > ' ' && c != ',' && c != ';')
373 	{
374 		pr_token[i] = c;
375 		i++;
376 		pr_file_p++;
377 	}
378 	pr_token[i] = 0;
379 	return true;
380 }
381 
PR_ParseFrame(void)382 static void PR_ParseFrame (void)
383 {
384 	while (PR_SimpleGetToken ())
385 	{
386 		strcpy (pr_framemacros[pr_nummacros], pr_token);
387 		pr_nummacros++;
388 	}
389 }
390 
391 /*
392 ==============
393 PR_LexGrab
394 
395 Deals with counting sequence numbers and replacing frame macros
396 ==============
397 */
PR_LexGrab(void)398 static void PR_LexGrab (void)
399 {
400 	pr_file_p++;	// skip the $
401 	if (!PR_SimpleGetToken ())
402 		PR_ParseError ("hanging $");
403 
404 // check for $frame
405 	if (!strcmp (pr_token, "frame"))
406 	{
407 		PR_ParseFrame ();
408 		PR_Lex ();
409 	}
410 // ignore other known $commands
411 	else if (!strcmp (pr_token, "cd") ||
412 		 !strcmp (pr_token, "origin") ||
413 		 !strcmp (pr_token, "base") ||
414 		 !strcmp (pr_token, "flags") ||
415 		 !strcmp (pr_token, "scale") ||
416 		 !strcmp (pr_token, "skin"))
417 	{	// skip to end of line
418 		while (PR_SimpleGetToken ())
419 			;
420 		PR_Lex ();
421 	}
422 // look for a frame name macro
423 	else
424 	{
425 		PR_FindMacro ();
426 	}
427 }
428 
429 //============================================================================
430 
431 /*
432 ==============
433 PR_Lex
434 
435 Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type
436 ==============
437 */
PR_Lex(void)438 void PR_Lex (void)
439 {
440 	int		c;
441 
442 	pr_token[0] = 0;
443 
444 	if (!pr_file_p)
445 	{
446 		pr_token_type = tt_eof;
447 		return;
448 	}
449 
450 	PR_LexWhitespace ();
451 
452 	c = *pr_file_p;
453 
454 	if (!c)
455 	{
456 		pr_token_type = tt_eof;
457 		return;
458 	}
459 
460 // handle quoted strings as a unit
461 	if (c == '\"')
462 	{
463 		PR_LexString ();
464 		return;
465 	}
466 
467 // handle quoted vectors as a unit
468 	if (c == '\'')
469 	{
470 		PR_LexVector ();
471 		return;
472 	}
473 
474 // if the first character is a valid identifier, parse until a non-id
475 // character is reached
476 	if ((c >= '0' && c <= '9') || ( c == '-' && pr_file_p[1] >= '0' && pr_file_p[1] <= '9'))
477 	{
478 		pr_token_type = tt_immediate;
479 		pr_immediate_type = &type_float;
480 		pr_immediate._float = PR_LexNumber ();
481 		return;
482 	}
483 
484 	if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' )
485 	{
486 		PR_LexName ();
487 		return;
488 	}
489 
490 	if (c == '$')
491 	{
492 		PR_LexGrab ();
493 		return;
494 	}
495 
496 // parse symbol strings until a non-symbol is found
497 	PR_LexPunctuation ();
498 }
499 
500 //=============================================================================
501 
502 /*
503 =============
504 PR_Expect
505 
506 Issues an error if the current token isn't equal to string
507 Gets the next token
508 =============
509 */
PR_Expect(const char * string)510 void PR_Expect (const char *string)
511 {
512 	if (strcmp (string, pr_token))
513 		PR_ParseError ("expected %s, found %s", string, pr_token);
514 	PR_Lex ();
515 }
516 
517 /*
518 =============
519 PR_Check
520 
521 Returns true and gets the next token if the current token equals string
522 Returns false and does nothing otherwise
523 =============
524 */
PR_Check(const char * string)525 qboolean PR_Check (const char *string)
526 {
527 	if (strcmp (string, pr_token))
528 		return false;
529 
530 	PR_Lex ();
531 	return true;
532 }
533 
534 /*
535 ============
536 PR_ParseName
537 
538 Checks to see if the current token is a valid name
539 ============
540 */
PR_ParseName(void)541 const char *PR_ParseName (void)
542 {
543 	static char	ident[MAX_NAME];
544 
545 	if (pr_token_type != tt_name)
546 		PR_ParseError ("not a name %s", pr_token);
547 	if (strlen(pr_token) >= MAX_NAME-1)
548 		PR_ParseError ("name too long");
549 	strcpy (ident, pr_token);
550 	PR_Lex ();
551 
552 	return ident;
553 }
554 
555 /*
556 ============
557 PR_FindType
558 
559 Returns a preexisting complex type that matches the parm, or allocates
560 a new one and copies it out.
561 ============
562 */
PR_FindType(type_t * type)563 static type_t *PR_FindType (type_t *type)
564 {
565 	def_t	*def;
566 	type_t	*check;
567 	int	i;
568 
569 	for (check = pr.types; check; check = check->next)
570 	{
571 		if (check->type != type->type ||
572 		    check->aux_type != type->aux_type ||
573 		    check->num_parms != type->num_parms)
574 		{
575 			continue;
576 		}
577 		for (i = 0; i < type->num_parms; i++)
578 		{
579 			if (check->parm_types[i] != type->parm_types[i])
580 				break;
581 		}
582 		if (i == type->num_parms)
583 		{
584 			return check;
585 		}
586 	}
587 
588 // allocate a new one
589 	check = (type_t *) SafeMalloc (sizeof (*check));
590 	*check = *type;
591 	check->next = pr.types;
592 	pr.types = check;
593 
594 // allocate a generic def for the type, so fields can reference it
595 	def = (def_t *) SafeMalloc (sizeof(def_t));
596 	def->name = "COMPLEX TYPE";
597 	def->type = check;
598 	check->def = def;
599 	return check;
600 }
601 
602 /*
603 ============
604 PR_SkipToSemicolon
605 
606 For error recovery, also pops out of nested braces
607 ============
608 */
PR_SkipToSemicolon(void)609 void PR_SkipToSemicolon (void)
610 {
611 	do
612 	{
613 		if (!pr_bracelevel && PR_Check (";"))
614 			return;
615 		PR_Lex ();
616 	} while (pr_token[0]);	// eof will return a null token
617 }
618 
619 /*
620 ============
621 PR_ParseType
622 
623 Parses a variable type, including field and functions types
624 ============
625 */
626 char	pr_parm_names[MAX_PARMS][MAX_NAME];
627 
PR_ParseType(void)628 type_t *PR_ParseType (void)
629 {
630 	const char	*name;
631 	type_t	newtype;
632 	type_t	*type;
633 
634 	if (PR_Check ("."))
635 	{
636 		memset (&newtype, 0, sizeof(newtype));
637 		newtype.type = ev_field;
638 		newtype.aux_type = PR_ParseType ();
639 		return PR_FindType (&newtype);
640 	}
641 
642 	if (!strcmp (pr_token, "float"))
643 		type = &type_float;
644 	else if (!strcmp (pr_token, "vector"))
645 		type = &type_vector;
646 	else if (!strcmp (pr_token, "entity"))
647 		type = &type_entity;
648 	else if (!strcmp (pr_token, "string"))
649 		type = &type_string;
650 	else if (!strcmp (pr_token, "void"))
651 		type = &type_void;
652 	else
653 	{
654 		PR_ParseError ("\"%s\" is not a type", pr_token);
655 		return NULL; /* silence compiler */
656 	}
657 	PR_Lex ();
658 
659 	if (!PR_Check ("("))
660 		return type;
661 
662 // function type
663 	memset (&newtype, 0, sizeof(newtype));
664 	newtype.type = ev_function;
665 	newtype.aux_type = type;	// return type
666 	newtype.num_parms = 0;
667 	if (!PR_Check (")"))
668 	{
669 		if (PR_Check ("..."))
670 			newtype.num_parms = -1;	// variable args
671 		else
672 		{
673 			do
674 			{
675 				type = PR_ParseType ();
676 				name = PR_ParseName ();
677 				strcpy (pr_parm_names[newtype.num_parms], name);
678 				newtype.parm_types[newtype.num_parms] = type;
679 				newtype.num_parms++;
680 			} while (PR_Check (","));
681 		}
682 
683 		PR_Expect (")");
684 	}
685 
686 	return PR_FindType (&newtype);
687 }
688 
689