xref: /Universal-ctags/parsers/flex.c (revision 24b256e375a5a9f63df9154e3a1c2bbd0909a47f)
1 /*
2  *	 Copyright (c) 2008, David Fishburn
3  *
4  *	 This source code is released for free distribution under the terms of the
5  *	 GNU General Public License version 2 or (at your option) any later version.
6  *
7  *	 This module contains functions for generating tags for Adobe languages.
8  *	 There are a number of different ones, but this will begin with:
9  *	     Flex
10  *	         MXML files (*.mMacromedia XML)
11  *	         ActionScript files (*.as)
12  *
13  *	 The ActionScript code was copied from the JavaScript parser, with some
14  *	 adaptations e.g. for classes and type specifiers.
15  *
16  *	 Flex 3 language reference
17  *		 http://livedocs.adobe.com/flex/3/langref/index.html
18  * 		 https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/language-elements.html
19  * 		 https://www.adobe.com/devnet/actionscript/learning/as3-fundamentals/packages.html
20  */
21 
22 /*
23  *	 INCLUDE FILES
24  */
25 #include "general.h"	/* must always come first */
26 #include <ctype.h>	/* to define isalpha () */
27 #ifdef DEBUG
28 #include <stdio.h>
29 #endif
30 
31 #include "debug.h"
32 #include "entry.h"
33 #include "keyword.h"
34 #include "parse.h"
35 #include "read.h"
36 #include "routines.h"
37 #include "vstring.h"
38 #include "strlist.h"
39 
40 /*
41  *	 MACROS
42  */
43 #define isType(token,t)		(bool) ((token)->type == (t))
44 #define isKeyword(token,k)	(bool) ((token)->keyword == (k))
45 #define isEOF(token) (isType ((token), TOKEN_EOF))
46 #define isIdentChar(c) \
47 	(isalpha (c) || isdigit (c) || (c) == '$' || \
48 		(c) == '@' || (c) == '_' || (c) == '#' || \
49 		(c) >= 0x80)
50 
51 /*
52  *	 DATA DECLARATIONS
53  */
54 
55 /*
56  * Tracks class and function names already created
57  */
58 static stringList *ClassNames;
59 static stringList *FunctionNames;
60 
61 /*	Used to specify type of keyword.
62 */
63 enum eKeywordId {
64 	KEYWORD_function,
65 	KEYWORD_capital_function,
66 	KEYWORD_object,
67 	KEYWORD_capital_object,
68 	KEYWORD_prototype,
69 	KEYWORD_var,
70 	KEYWORD_const,
71 	KEYWORD_new,
72 	KEYWORD_this,
73 	KEYWORD_for,
74 	KEYWORD_while,
75 	KEYWORD_do,
76 	KEYWORD_if,
77 	KEYWORD_else,
78 	KEYWORD_switch,
79 	KEYWORD_try,
80 	KEYWORD_catch,
81 	KEYWORD_finally,
82 	KEYWORD_return,
83 	KEYWORD_public,
84 	KEYWORD_private,
85 	KEYWORD_protected,
86 	KEYWORD_internal,
87 	KEYWORD_final,
88 	KEYWORD_native,
89 	KEYWORD_dynamic,
90 	KEYWORD_class,
91 	KEYWORD_interface,
92 	KEYWORD_package,
93 	KEYWORD_extends,
94 	KEYWORD_static,
95 	KEYWORD_implements,
96 	KEYWORD_get,
97 	KEYWORD_set,
98 	KEYWORD_import,
99 	KEYWORD_id,
100 	KEYWORD_name,
101 	KEYWORD_script,
102 	KEYWORD_cdata,
103 	KEYWORD_mx,
104 	KEYWORD_fx,
105 	KEYWORD_override
106 };
107 typedef int keywordId; /* to allow KEYWORD_NONE */
108 
109 typedef enum eTokenType {
110 	TOKEN_UNDEFINED,
111 	TOKEN_EOF,
112 	TOKEN_CHARACTER,
113 	TOKEN_CLOSE_PAREN,
114 	TOKEN_SEMICOLON,
115 	TOKEN_COLON,
116 	TOKEN_COMMA,
117 	TOKEN_KEYWORD,
118 	TOKEN_OPEN_PAREN,
119 	TOKEN_IDENTIFIER,
120 	TOKEN_STRING,
121 	TOKEN_PERIOD,
122 	TOKEN_OPEN_CURLY,
123 	TOKEN_CLOSE_CURLY,
124 	TOKEN_EQUAL_SIGN,
125 	TOKEN_EXCLAMATION,
126 	TOKEN_FORWARD_SLASH,
127 	TOKEN_OPEN_SQUARE,
128 	TOKEN_CLOSE_SQUARE,
129 	TOKEN_OPEN_MXML,
130 	TOKEN_CLOSE_MXML,
131 	TOKEN_CLOSE_SGML,
132 	TOKEN_LESS_THAN,
133 	TOKEN_GREATER_THAN,
134 	TOKEN_QUESTION_MARK,
135 	TOKEN_OPEN_NAMESPACE,
136 	TOKEN_POSTFIX_OPERATOR,
137 	TOKEN_STAR,
138 	TOKEN_BINARY_OPERATOR
139 } tokenType;
140 
141 typedef struct sTokenInfo {
142 	tokenType		type;
143 	keywordId		keyword;
144 	vString *		string;
145 	vString *		scope;
146 	unsigned long 	lineNumber;
147 	MIOPos 			filePosition;
148 	int				nestLevel;
149 	bool			ignoreTag;
150 	bool			isClass;
151 } tokenInfo;
152 
153 /*
154  *	DATA DEFINITIONS
155  */
156 static tokenType LastTokenType;
157 static tokenInfo *NextToken;
158 
159 static langType Lang_flex;
160 
161 typedef enum {
162 	FLEXTAG_FUNCTION,
163 	FLEXTAG_CLASS,
164 	FLEXTAG_INTERFACE,
165 	FLEXTAG_PACKAGE,
166 	FLEXTAG_METHOD,
167 	FLEXTAG_PROPERTY,
168 	FLEXTAG_VARIABLE,
169 	FLEXTAG_LOCALVAR,
170 	FLEXTAG_CONST,
171 	FLEXTAG_IMPORT,
172 	FLEXTAG_MXTAG,
173 	FLEXTAG_COUNT
174 } flexKind;
175 
176 typedef enum {
177 	FLEX_IMPORT_ROLE_IMPORTED,
178 } flexImportRole;
179 
180 static roleDefinition FlexImportRoles [] = {
181 	{ true, "import", "imports" },
182 };
183 
184 static kindDefinition FlexKinds [] = {
185 	{ true,  'f', "function",	  "functions"		   },
186 	{ true,  'c', "class",		  "classes"			   },
187 	{ true,  'i', "interface",	  "interfaces"		   },
188 	{ true,  'P', "package",	  "packages"		   },
189 	{ true,  'm', "method",		  "methods"			   },
190 	{ true,  'p', "property",	  "properties"		   },
191 	{ true,  'v', "variable",	  "global variables"   },
192 	{ false, 'l', "localvar",	  "local variables"   },
193 	{ true,  'C', "constant",	  "constants"		   },
194 	{ true,  'I', "import",		  "imports",
195 	  .referenceOnly = true, ATTACH_ROLES (FlexImportRoles) },
196 	{ true,  'x', "mxtag",		  "mxtags" 			   }
197 };
198 
199 /*	Used to determine whether keyword is valid for the token language and
200  *	what its ID is.
201  */
202 static const keywordTable FlexKeywordTable [] = {
203 	/* keyword		keyword ID */
204 	{ "function",	KEYWORD_function			},
205 	{ "Function",	KEYWORD_capital_function	},
206 	{ "object",		KEYWORD_object				},
207 	{ "Object",		KEYWORD_capital_object		},
208 	{ "prototype",	KEYWORD_prototype			},
209 	{ "var",		KEYWORD_var					},
210 	{ "const",		KEYWORD_const				},
211 	{ "new",		KEYWORD_new					},
212 	{ "this",		KEYWORD_this				},
213 	{ "for",		KEYWORD_for					},
214 	{ "while",		KEYWORD_while				},
215 	{ "do",			KEYWORD_do					},
216 	{ "if",			KEYWORD_if					},
217 	{ "else",		KEYWORD_else				},
218 	{ "switch",		KEYWORD_switch				},
219 	{ "try",		KEYWORD_try					},
220 	{ "catch",		KEYWORD_catch				},
221 	{ "finally",	KEYWORD_finally				},
222 	{ "return",		KEYWORD_return				},
223 	{ "public",		KEYWORD_public				},
224 	{ "private",	KEYWORD_private				},
225 	{ "protected",	KEYWORD_protected			},
226 	{ "internal",	KEYWORD_internal			},
227 	{ "final",		KEYWORD_final				},
228 	{ "native",		KEYWORD_native				},
229 	{ "dynamic",	KEYWORD_dynamic				},
230 	{ "class",		KEYWORD_class				},
231 	{ "interface",	KEYWORD_interface			},
232 	{ "package",	KEYWORD_package				},
233 	{ "extends",	KEYWORD_extends				},
234 	{ "static",		KEYWORD_static				},
235 	{ "implements",	KEYWORD_implements			},
236 	{ "get",		KEYWORD_get					},
237 	{ "set",		KEYWORD_set					},
238 	{ "import",		KEYWORD_import				},
239 	{ "id",			KEYWORD_id					},
240 	{ "name",		KEYWORD_name				},
241 	{ "script",		KEYWORD_script				},
242 	{ "cdata",		KEYWORD_cdata				},
243 	{ "mx",			KEYWORD_mx					},
244 	{ "fx",			KEYWORD_fx					},
245 	{ "override",	KEYWORD_override			}
246 };
247 
248 /*
249  *	 FUNCTION DEFINITIONS
250  */
251 
252 /* Recursive functions */
253 static void parseFunction (tokenInfo *const token);
254 static bool parseBlock (tokenInfo *const token, const vString *const parentScope);
255 static bool parseLine (tokenInfo *const token);
256 static bool parseActionScript (tokenInfo *const token, bool readNext);
257 static bool parseMXML (tokenInfo *const token);
258 
newToken(void)259 static tokenInfo *newToken (void)
260 {
261 	tokenInfo *const token = xMalloc (1, tokenInfo);
262 
263 	token->type			= TOKEN_UNDEFINED;
264 	token->keyword		= KEYWORD_NONE;
265 	token->string		= vStringNew ();
266 	token->scope		= vStringNew ();
267 	token->nestLevel	= 0;
268 	token->isClass		= false;
269 	token->ignoreTag	= false;
270 	token->lineNumber   = getInputLineNumber ();
271 	token->filePosition = getInputFilePosition ();
272 
273 	return token;
274 }
275 
deleteToken(tokenInfo * const token)276 static void deleteToken (tokenInfo *const token)
277 {
278 	vStringDelete (token->string);
279 	vStringDelete (token->scope);
280 	eFree (token);
281 }
282 
copyToken(tokenInfo * const dest,tokenInfo * const src,bool const include_non_read_info)283 static void copyToken (tokenInfo *const dest, tokenInfo *const src,
284                        bool const include_non_read_info)
285 {
286 	dest->lineNumber = src->lineNumber;
287 	dest->filePosition = src->filePosition;
288 	dest->type = src->type;
289 	dest->keyword = src->keyword;
290 	dest->isClass = src->isClass;
291 	vStringCopy(dest->string, src->string);
292 	if (include_non_read_info)
293 	{
294 		dest->nestLevel = src->nestLevel;
295 		vStringCopy(dest->scope, src->scope);
296 	}
297 }
298 
299 /*
300  *	 Tag generation functions
301  */
302 
buildQualifiedName(const tokenInfo * const token)303 static vString *buildQualifiedName (const tokenInfo *const token)
304 {
305 	vString *qualified = vStringNew ();
306 
307 	if (vStringLength (token->scope) > 0)
308 	{
309 		vStringCopy (qualified, token->scope);
310 		vStringPut (qualified, '.');
311 	}
312 	vStringCat (qualified, token->string);
313 
314 	return qualified;
315 }
316 
makeConstTag(tokenInfo * const token,const flexKind kind)317 static void makeConstTag (tokenInfo *const token, const flexKind kind)
318 {
319 	if (FlexKinds [kind].enabled && ! token->ignoreTag )
320 	{
321 		const char *const name = vStringValue (token->string);
322 		tagEntryInfo e;
323 		int role = ROLE_DEFINITION_INDEX;
324 
325 		if (kind == FLEXTAG_IMPORT)
326 			role = FLEX_IMPORT_ROLE_IMPORTED;
327 
328 		initRefTagEntry (&e, name, kind, role);
329 
330 		e.lineNumber   = token->lineNumber;
331 		e.filePosition = token->filePosition;
332 
333 		if ( vStringLength(token->scope) > 0 )
334 		{
335 			/* FIXME: proper parent type */
336 			flexKind parent_kind = FLEXTAG_CLASS;
337 
338 			/*
339 			 * If we're creating a function (and not a method),
340 			 * guess we're inside another function
341 			 */
342 			if (kind == FLEXTAG_FUNCTION)
343 				parent_kind = FLEXTAG_FUNCTION;
344 			/* mxtags can only be nested inside other mxtags */
345 			else if (kind == FLEXTAG_MXTAG)
346 				parent_kind = kind;
347 
348 			e.extensionFields.scopeKindIndex = parent_kind;
349 			e.extensionFields.scopeName = vStringValue (token->scope);
350 		}
351 
352 		makeTagEntry (&e);
353 
354 		/* make qualified tags for compatibility if requested */
355 		if (isXtagEnabled (XTAG_QUALIFIED_TAGS))
356 		{
357 			vString *qualified = buildQualifiedName (token);
358 
359 			markTagExtraBit (&e, XTAG_QUALIFIED_TAGS);
360 			e.name = vStringValue (qualified);
361 			makeTagEntry (&e);
362 			vStringDelete (qualified);
363 		}
364 	}
365 }
366 
makeFlexTag(tokenInfo * const token,flexKind kind)367 static void makeFlexTag (tokenInfo *const token, flexKind kind)
368 {
369 	if (FlexKinds [kind].enabled && ! token->ignoreTag )
370 	{
371 	DebugStatement (
372 			debugPrintf (DEBUG_PARSE
373 				, "\n makeFlexTag start: token isClass:%d  scope:%s  name:%s\n"
374 				, token->isClass
375 				, vStringValue(token->scope)
376 				, vStringValue(token->string)
377 				);
378 			);
379 		if (kind == FLEXTAG_FUNCTION && token->isClass )
380 		{
381 			kind = FLEXTAG_METHOD;
382 		}
383 		makeConstTag (token, kind);
384 	}
385 }
386 
makeClassTag(tokenInfo * const token)387 static void makeClassTag (tokenInfo *const token)
388 {
389 	if ( ! token->ignoreTag )
390 	{
391 		vString *fulltag = buildQualifiedName (token);
392 
393 		if ( ! stringListHas(ClassNames, vStringValue (fulltag)) )
394 		{
395 			stringListAdd (ClassNames, vStringNewCopy (fulltag));
396 			makeFlexTag (token, FLEXTAG_CLASS);
397 		}
398 		vStringDelete (fulltag);
399 	}
400 }
401 
makeMXTag(tokenInfo * const token)402 static void makeMXTag (tokenInfo *const token)
403 {
404 	if ( ! token->ignoreTag )
405 	{
406 		makeFlexTag (token, FLEXTAG_MXTAG);
407 	}
408 }
409 
makeFunctionTag(tokenInfo * const token)410 static void makeFunctionTag (tokenInfo *const token)
411 {
412 	if ( ! token->ignoreTag )
413 	{
414 		vString *fulltag = buildQualifiedName (token);
415 
416 		if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) )
417 		{
418 			stringListAdd (FunctionNames, vStringNewCopy (fulltag));
419 			makeFlexTag (token, FLEXTAG_FUNCTION);
420 		}
421 		vStringDelete (fulltag);
422 	}
423 }
424 
425 /*
426  *	 Parsing functions
427  */
428 
parseString(vString * const string,const int delimiter)429 static void parseString (vString *const string, const int delimiter)
430 {
431 	bool end = false;
432 	while (! end)
433 	{
434 		int c = getcFromInputFile ();
435 		if (c == EOF)
436 			end = true;
437 		else if (c == '\\')
438 		{
439 			c = getcFromInputFile(); /* This maybe a ' or ". */
440 			vStringPut(string, c);
441 		}
442 		else if (c == delimiter)
443 			end = true;
444 		else
445 			vStringPut (string, c);
446 	}
447 }
448 
449 /*	Read a C identifier beginning with "firstChar" and places it into
450  *	"name".
451  */
parseIdentifier(vString * const string,const int firstChar)452 static void parseIdentifier (vString *const string, const int firstChar)
453 {
454 	int c = firstChar;
455 	Assert (isIdentChar (c));
456 	do
457 	{
458 		vStringPut (string, c);
459 		c = getcFromInputFile ();
460 	} while (isIdentChar (c));
461 	ungetcToInputFile (c);		/* unget non-identifier character */
462 }
463 
readTokenFull(tokenInfo * const token,bool include_newlines)464 static void readTokenFull (tokenInfo *const token, bool include_newlines)
465 {
466 	int c;
467 	int i;
468 	bool newline_encountered = false;
469 
470 	/* if we've got a token held back, emit it */
471 	if (NextToken)
472 	{
473 		copyToken (token, NextToken, false);
474 		deleteToken (NextToken);
475 		NextToken = NULL;
476 		return;
477 	}
478 
479 	token->type			= TOKEN_UNDEFINED;
480 	token->keyword		= KEYWORD_NONE;
481 	vStringClear (token->string);
482 
483 getNextChar:
484 	i = 0;
485 	do
486 	{
487 		c = getcFromInputFile ();
488 		if (include_newlines && (c == '\r' || c == '\n'))
489 			newline_encountered = true;
490 		i++;
491 	}
492 	while (c == '\t' || c == ' ' || c == '\r' || c == '\n');
493 
494 	token->lineNumber   = getInputLineNumber ();
495 	token->filePosition = getInputFilePosition ();
496 
497 	switch (c)
498 	{
499 		case EOF: token->type = TOKEN_EOF;					break;
500 		case '(': token->type = TOKEN_OPEN_PAREN;			break;
501 		case ')': token->type = TOKEN_CLOSE_PAREN;			break;
502 		case ';': token->type = TOKEN_SEMICOLON;			break;
503 		case ',': token->type = TOKEN_COMMA;				break;
504 		case '.': token->type = TOKEN_PERIOD;				break;
505 		case ':': token->type = TOKEN_COLON;				break;
506 		case '{': token->type = TOKEN_OPEN_CURLY;			break;
507 		case '}': token->type = TOKEN_CLOSE_CURLY;			break;
508 		case '=': token->type = TOKEN_EQUAL_SIGN;			break;
509 		case '[': token->type = TOKEN_OPEN_SQUARE;			break;
510 		case ']': token->type = TOKEN_CLOSE_SQUARE;			break;
511 		case '?': token->type = TOKEN_QUESTION_MARK;		break;
512 
513 		case '+':
514 		case '-':
515 			{
516 				int d = getcFromInputFile ();
517 				if (d == c) /* ++ or -- */
518 					token->type = TOKEN_POSTFIX_OPERATOR;
519 				else
520 				{
521 					ungetcToInputFile (d);
522 					token->type = TOKEN_BINARY_OPERATOR;
523 				}
524 				break;
525 			}
526 
527 		case '*':
528 			token->type = TOKEN_STAR;
529 			break;
530 		case '%':
531 		case '^':
532 		case '|':
533 		case '&':
534 			token->type = TOKEN_BINARY_OPERATOR;
535 			break;
536 
537 		case '\'':
538 		case '"':
539 				  token->type = TOKEN_STRING;
540 				  parseString (token->string, c);
541 				  token->lineNumber = getInputLineNumber ();
542 				  token->filePosition = getInputFilePosition ();
543 				  break;
544 
545 		case '\\':
546 				  c = getcFromInputFile ();
547 				  if (c != '\\'  && c != '"'  &&  !isspace (c))
548 					  ungetcToInputFile (c);
549 				  token->type = TOKEN_CHARACTER;
550 				  token->lineNumber = getInputLineNumber ();
551 				  token->filePosition = getInputFilePosition ();
552 				  break;
553 
554 		case '/':
555 				  {
556 					  int d = getcFromInputFile ();
557 					  if ( (d != '*') &&		/* is this the start of a comment? */
558 							  (d != '/') &&		/* is a one line comment? */
559 							  (d != '>') )		/* is this a close XML tag? */
560 					  {
561 						  ungetcToInputFile (d);
562 						  token->type = TOKEN_FORWARD_SLASH;
563 						  token->lineNumber = getInputLineNumber ();
564 						  token->filePosition = getInputFilePosition ();
565 					  }
566 					  else
567 					  {
568 						  if (d == '*')
569 						  {
570 							  skipToCharacterInInputFile2('*', '/');
571 							  goto getNextChar;
572 						  }
573 						  else if (d == '/')	/* is this the start of a comment?  */
574 						  {
575 							  skipToCharacterInInputFile ('\n');
576 							  /* if we care about newlines, put it back so it is seen */
577 							  if (include_newlines)
578 								  ungetcToInputFile ('\n');
579 							  goto getNextChar;
580 						  }
581 						  else if (d == '>')	/* is this the start of a comment?  */
582 						  {
583 							  token->type = TOKEN_CLOSE_SGML;
584 							  token->lineNumber = getInputLineNumber ();
585 							  token->filePosition = getInputFilePosition ();
586 						  }
587 					  }
588 					  break;
589 				  }
590 
591 		case '<':
592 				  {
593 					  /*
594 					   * An XML comment looks like this
595 					   *   <!-- anything over multiple lines -->
596 					   */
597 					  int d = getcFromInputFile ();
598 
599 					  if ( (d != '!' )  && 		/* is this the start of a comment? */
600 					       (d != '/' )  &&	 	/* is this the start of a closing mx tag */
601 					       (d != 'm' )  &&  	/* is this the start of a mx tag */
602 					       (d != 'f' )  &&  	/* is this the start of a fx tag */
603 					       (d != 's' )    ) 	/* is this the start of a spark tag */
604 					  {
605 						  ungetcToInputFile (d);
606 						  token->type = TOKEN_LESS_THAN;
607 						  token->lineNumber = getInputLineNumber ();
608 						  token->filePosition = getInputFilePosition ();
609 						  break;
610 					  }
611 					  else
612 					  {
613 						  if (d == '!')
614 						  {
615 							  int e = getcFromInputFile ();
616 							  if ( e != '-' ) 		/* is this the start of a comment? */
617 							  {
618 								  ungetcToInputFile (e);
619 								  ungetcToInputFile (d);
620 								  token->type = TOKEN_LESS_THAN;
621 								  token->lineNumber = getInputLineNumber ();
622 								  token->filePosition = getInputFilePosition ();
623 							  }
624 							  else
625 							  {
626 								  if (e == '-')
627 								  {
628 									  int f = getcFromInputFile ();
629 									  if ( f != '-' ) 		/* is this the start of a comment? */
630 									  {
631 										  ungetcToInputFile (f);
632 										  ungetcToInputFile (e);
633 										  ungetcToInputFile (d);
634 										  token->type = TOKEN_LESS_THAN;
635 										  token->lineNumber = getInputLineNumber ();
636 										  token->filePosition = getInputFilePosition ();
637 									  }
638 									  else
639 									  {
640 										  if (f == '-')
641 										  {
642 											  do
643 											  {
644 												  skipToCharacterInInputFile ('-');
645 												  c = getcFromInputFile ();
646 												  if (c == '-')
647 												  {
648 													  d = getcFromInputFile ();
649 													  if (d == '>')
650 														  break;
651 													  else
652 													  {
653 														  ungetcToInputFile (d);
654 														  ungetcToInputFile (c);
655 													  }
656 													  break;
657 												  }
658 												  else
659 													  ungetcToInputFile (c);
660 											  } while (c != EOF && c != '\0');
661 											  goto getNextChar;
662 										  }
663 									  }
664 								  }
665 							  }
666 						  }
667 						  else if (d == 'm' || d == 'f' || d == 's' )
668 						  {
669 							  int e = getcFromInputFile ();
670 							  if ( (d == 'm' || d == 'f') && e != 'x' ) 		/* continuing an mx or fx tag */
671 							  {
672 								  ungetcToInputFile (e);
673 								  ungetcToInputFile (d);
674 								  token->type = TOKEN_LESS_THAN;
675 								  token->lineNumber = getInputLineNumber ();
676 								  token->filePosition = getInputFilePosition ();
677 								  break;
678 							  }
679 							  else
680 							  {
681 								  if ( (d == 'm' || d == 'f') && e == 'x' )
682 								  {
683 									  int f = getcFromInputFile ();
684 									  if ( f != ':' ) 		/* start of the tag */
685 									  {
686 										  ungetcToInputFile (f);
687 										  ungetcToInputFile (e);
688 										  ungetcToInputFile (d);
689 										  token->type = TOKEN_LESS_THAN;
690 										  token->lineNumber = getInputLineNumber ();
691 										  token->filePosition = getInputFilePosition ();
692 										  break;
693 									  }
694 									  else
695 									  {
696 										  token->type = TOKEN_OPEN_MXML;
697 										  token->lineNumber = getInputLineNumber ();
698 										  token->filePosition = getInputFilePosition ();
699 										  break;
700 									  }
701 								  }
702 								  if ( d == 's' && e == ':')    /* continuing a spark tag */
703 								  {
704 									  token->type = TOKEN_OPEN_MXML;
705 									  token->lineNumber = getInputLineNumber ();
706 									  token->filePosition = getInputFilePosition ();
707 									  break;
708 								  }
709 								  else
710 								  {
711 									  ungetcToInputFile (e);
712 									  ungetcToInputFile (d);
713 									  token->type = TOKEN_LESS_THAN;
714 									  token->lineNumber = getInputLineNumber ();
715 									  token->filePosition = getInputFilePosition ();
716 									  break;
717 								  }
718 							  }
719 						  }
720 						  else if (d == '/')
721 						  {
722 							  int e = getcFromInputFile ();
723 							  if ( !(e == 'm' || e == 'f' || e == 's' ))
724 							  {
725 								  ungetcToInputFile (e);
726 								  ungetcToInputFile (d);
727 								  token->type = TOKEN_LESS_THAN;
728 								  token->lineNumber = getInputLineNumber ();
729 								  token->filePosition = getInputFilePosition ();
730 								  break;
731 							  }
732 							  else
733 							  {
734 								  int f = getcFromInputFile ();
735 								  if ( (e == 'm' || e == 'f') && f != 'x' ) 		/* continuing an mx or fx tag */
736 								  {
737 									  ungetcToInputFile (f);
738 									  ungetcToInputFile (e);
739 									  token->type = TOKEN_LESS_THAN;
740 									  token->lineNumber = getInputLineNumber ();
741 									  token->filePosition = getInputFilePosition ();
742 									  break;
743 								  }
744 								  else
745 								  {
746 									  if (f == 'x')
747 									  {
748 										  int g = getcFromInputFile ();
749 										  if ( g != ':' ) 		/* is this the start of a comment? */
750 										  {
751 											  ungetcToInputFile (g);
752 											  ungetcToInputFile (f);
753 											  ungetcToInputFile (e);
754 											  token->type = TOKEN_LESS_THAN;
755 											  token->lineNumber = getInputLineNumber ();
756 											  token->filePosition = getInputFilePosition ();
757 											  break;
758 										  }
759 										  else
760 										  {
761 											  token->type = TOKEN_CLOSE_MXML;
762 											  token->lineNumber = getInputLineNumber ();
763 											  token->filePosition = getInputFilePosition ();
764 											  break;
765 										  }
766 									  }
767 									  if ( e == 's' && f == ':')    /* continuing a spark tag */
768 									  {
769 										  token->type = TOKEN_CLOSE_MXML;
770 										  token->lineNumber = getInputLineNumber ();
771 										  token->filePosition = getInputFilePosition ();
772 										  break;
773 									  }
774 									  else
775 									  {
776 										  ungetcToInputFile (f);
777 										  ungetcToInputFile (e);
778 										  token->type = TOKEN_LESS_THAN;
779 										  token->lineNumber = getInputLineNumber ();
780 										  token->filePosition = getInputFilePosition ();
781 										  break;
782 									  }
783 								  }
784 							  }
785 						  }
786 					  }
787 					  break;
788 				  }
789 
790 		case '>':
791 				  token->type = TOKEN_GREATER_THAN;
792 				  token->lineNumber = getInputLineNumber ();
793 				  token->filePosition = getInputFilePosition ();
794 				  break;
795 
796 		case '!':
797 				  token->type = TOKEN_EXCLAMATION;
798 				  /*token->lineNumber = getInputLineNumber ();
799 				  token->filePosition = getInputFilePosition ();*/
800 				  break;
801 
802 		default:
803 				  if (! isIdentChar (c))
804 					  token->type = TOKEN_UNDEFINED;
805 				  else
806 				  {
807 					  parseIdentifier (token->string, c);
808 					  token->lineNumber = getInputLineNumber ();
809 					  token->filePosition = getInputFilePosition ();
810 					  token->keyword = lookupCaseKeyword (vStringValue (token->string), Lang_flex);
811 					  if (isKeyword (token, KEYWORD_NONE))
812 						  token->type = TOKEN_IDENTIFIER;
813 					  else
814 						  token->type = TOKEN_KEYWORD;
815 				  }
816 				  break;
817 	}
818 
819 	if (include_newlines && newline_encountered)
820 	{
821 		/* This isn't strictly correct per the standard, but following the
822 		 * real rules means understanding all statements, and that's not
823 		 * what the parser currently does.  What we do here is a guess, by
824 		 * avoiding inserting semicolons that would make the statement on
825 		 * the left or right obviously invalid.  Hopefully this should not
826 		 * have false negatives (e.g. should not miss insertion of a semicolon)
827 		 * but might have false positives (e.g. it will wrongfully emit a
828 		 * semicolon sometimes, i.e. for the newline in "foo\n(bar)").
829 		 * This should however be mostly harmless as we only deal with
830 		 * newlines in specific situations where we know a false positive
831 		 * wouldn't hurt too bad. */
832 
833 		/* these already end a statement, so no need to duplicate it */
834 		#define IS_STMT_SEPARATOR(t) ((t) == TOKEN_SEMICOLON    || \
835 		                              (t) == TOKEN_EOF          || \
836 		                              (t) == TOKEN_COMMA        || \
837 		                              (t) == TOKEN_OPEN_CURLY)
838 		/* these cannot be the start or end of a statement */
839 		#define IS_BINARY_OPERATOR(t) ((t) == TOKEN_EQUAL_SIGN      || \
840 		                               (t) == TOKEN_COLON           || \
841 		                               (t) == TOKEN_PERIOD          || \
842 		                               (t) == TOKEN_STAR            || \
843 		                               (t) == TOKEN_FORWARD_SLASH   || \
844 		                               (t) == TOKEN_QUESTION_MARK   || \
845 		                               (t) == TOKEN_LESS_THAN       || \
846 		                               (t) == TOKEN_GREATER_THAN    || \
847 		                               (t) == TOKEN_BINARY_OPERATOR)
848 
849 		if (! IS_STMT_SEPARATOR(LastTokenType) &&
850 		    ! IS_STMT_SEPARATOR(token->type) &&
851 		    ! IS_BINARY_OPERATOR(LastTokenType) &&
852 		    ! IS_BINARY_OPERATOR(token->type) &&
853 		    /* these cannot be followed by a semicolon */
854 		    ! (LastTokenType == TOKEN_OPEN_PAREN ||
855 		       LastTokenType == TOKEN_OPEN_SQUARE))
856 		{
857 			/* hold the token... */
858 			Assert (NextToken == NULL);
859 			NextToken = newToken ();
860 			copyToken (NextToken, token, false);
861 
862 			/* ...and emit a semicolon instead */
863 			token->type		= TOKEN_SEMICOLON;
864 			token->keyword	= KEYWORD_NONE;
865 			vStringClear (token->string);
866 		}
867 
868 		#undef IS_STMT_SEPARATOR
869 		#undef IS_BINARY_OPERATOR
870 	}
871 
872 	LastTokenType = token->type;
873 }
874 
readToken(tokenInfo * const token)875 static void readToken (tokenInfo *const token)
876 {
877 	readTokenFull (token, false);
878 }
879 
880 /*
881  *	 Token parsing functions
882  */
883 
skipArgumentList(tokenInfo * const token,bool include_newlines)884 static void skipArgumentList (tokenInfo *const token, bool include_newlines)
885 {
886 	int nest_level = 0;
887 
888 	if (isType (token, TOKEN_OPEN_PAREN))	/* arguments? */
889 	{
890 		nest_level++;
891 		while (nest_level > 0 && ! isType (token, TOKEN_EOF))
892 		{
893 			readToken (token);
894 			if (isType (token, TOKEN_OPEN_PAREN))
895 				nest_level++;
896 			else if (isType (token, TOKEN_CLOSE_PAREN))
897 				nest_level--;
898 		}
899 		readTokenFull (token, include_newlines);
900 	}
901 }
902 
skipArrayList(tokenInfo * const token,bool include_newlines)903 static void skipArrayList (tokenInfo *const token, bool include_newlines)
904 {
905 	int nest_level = 0;
906 
907 	/*
908 	 * Handle square brackets
909 	 *	 var name[1]
910 	 * So we must check for nested open and closing square brackets
911 	 */
912 
913 	if (isType (token, TOKEN_OPEN_SQUARE))	/* arguments? */
914 	{
915 		nest_level++;
916 		while (nest_level > 0 && ! isType (token, TOKEN_EOF))
917 		{
918 			readToken (token);
919 			if (isType (token, TOKEN_OPEN_SQUARE))
920 				nest_level++;
921 			else if (isType (token, TOKEN_CLOSE_SQUARE))
922 				nest_level--;
923 		}
924 		readTokenFull (token, include_newlines);
925 	}
926 }
927 
addContext(tokenInfo * const parent,const tokenInfo * const child)928 static void addContext (tokenInfo* const parent, const tokenInfo* const child)
929 {
930 	if (vStringLength (parent->string) > 0)
931 	{
932 		vStringPut (parent->string, '.');
933 	}
934 	vStringCat (parent->string, child->string);
935 }
936 
addToScope(tokenInfo * const token,const vString * const extra)937 static void addToScope (tokenInfo* const token, const vString* const extra)
938 {
939 	if (vStringLength (token->scope) > 0)
940 	{
941 		vStringPut (token->scope, '.');
942 	}
943 	vStringCat (token->scope, extra);
944 }
945 
946 /*
947  *	 Scanning functions
948  */
949 
findCmdTerm(tokenInfo * const token,bool include_newlines,bool include_commas)950 static bool findCmdTerm (tokenInfo *const token, bool include_newlines,
951 						 bool include_commas)
952 {
953 	/*
954 	 * Read until we find either a semicolon or closing brace.
955 	 * Any nested braces will be handled within.
956 	 */
957 	while (! isType (token, TOKEN_SEMICOLON) &&
958 		   ! isType (token, TOKEN_CLOSE_CURLY) &&
959 		   ! (include_commas && isType (token, TOKEN_COMMA)) &&
960 		   ! isType (token, TOKEN_EOF))
961 	{
962 		/* Handle nested blocks */
963 		if ( isType (token, TOKEN_OPEN_CURLY))
964 		{
965 			parseBlock (token, NULL);
966 			readTokenFull (token, include_newlines);
967 		}
968 		else if ( isType (token, TOKEN_OPEN_PAREN) )
969 		{
970 			skipArgumentList(token, include_newlines);
971 		}
972 		else if ( isType (token, TOKEN_OPEN_SQUARE) )
973 		{
974 			skipArrayList(token, include_newlines);
975 		}
976 		else
977 		{
978 			readTokenFull (token, include_newlines);
979 		}
980 	}
981 
982 	return isType (token, TOKEN_SEMICOLON);
983 }
984 
parseSwitch(tokenInfo * const token)985 static void parseSwitch (tokenInfo *const token)
986 {
987 	/*
988 	 * switch (expression) {
989 	 * case value1:
990 	 *	   statement;
991 	 *	   break;
992 	 * case value2:
993 	 *	   statement;
994 	 *	   break;
995 	 * default : statement;
996 	 * }
997 	 */
998 
999 	readToken (token);
1000 
1001 	if (isType (token, TOKEN_OPEN_PAREN))
1002 	{
1003 		skipArgumentList(token, false);
1004 	}
1005 
1006 	if (isType (token, TOKEN_OPEN_CURLY))
1007 	{
1008 		parseBlock (token, NULL);
1009 	}
1010 }
1011 
parseLoop(tokenInfo * const token)1012 static bool parseLoop (tokenInfo *const token)
1013 {
1014 	/*
1015 	 * Handles these statements
1016 	 *	   for (x=0; x<3; x++)
1017 	 *		   document.write("This text is repeated three times<br>");
1018 	 *
1019 	 *	   for (x=0; x<3; x++)
1020 	 *	   {
1021 	 *		   document.write("This text is repeated three times<br>");
1022 	 *	   }
1023 	 *
1024 	 *	   while (number<5){
1025 	 *		   document.write(number+"<br>");
1026 	 *		   number++;
1027 	 *	   }
1028 	 *
1029 	 *	   do{
1030 	 *		   document.write(number+"<br>");
1031 	 *		   number++;
1032 	 *	   }
1033 	 *	   while (number<5);
1034 	 */
1035 	bool is_terminated = true;
1036 
1037 	if (isKeyword (token, KEYWORD_for) || isKeyword (token, KEYWORD_while))
1038 	{
1039 		readToken(token);
1040 
1041 		if (isType (token, TOKEN_OPEN_PAREN))
1042 		{
1043 			skipArgumentList(token, false);
1044 		}
1045 
1046 		if (isType (token, TOKEN_OPEN_CURLY))
1047 		{
1048 			parseBlock (token, NULL);
1049 		}
1050 		else
1051 		{
1052 			is_terminated = parseLine(token);
1053 		}
1054 	}
1055 	else if (isKeyword (token, KEYWORD_do))
1056 	{
1057 		readToken(token);
1058 
1059 		if (isType (token, TOKEN_OPEN_CURLY))
1060 		{
1061 			parseBlock (token, NULL);
1062 		}
1063 		else
1064 		{
1065 			is_terminated = parseLine(token);
1066 		}
1067 
1068 		if (is_terminated)
1069 			readToken(token);
1070 
1071 		if (isKeyword (token, KEYWORD_while))
1072 		{
1073 			readToken(token);
1074 
1075 			if (isType (token, TOKEN_OPEN_PAREN))
1076 			{
1077 				skipArgumentList(token, true);
1078 			}
1079 			if (! isType (token, TOKEN_SEMICOLON))
1080 			{
1081 				/* oddly enough, `do {} while (0) var foo = 42` is perfectly
1082 				 * valid AS, so explicitly handle the remaining of the line
1083 				 * for the sake of the root scope handling (as parseActionScript()
1084 				 * always advances a token not to ever get stuck) */
1085 				is_terminated = parseLine(token);
1086 			}
1087 		}
1088 	}
1089 
1090 	return is_terminated;
1091 }
1092 
parseIf(tokenInfo * const token)1093 static bool parseIf (tokenInfo *const token)
1094 {
1095 	bool read_next_token = true;
1096 	/*
1097 	 * If statements have two forms
1098 	 *	   if ( ... )
1099 	 *		   one line;
1100 	 *
1101 	 *	   if ( ... )
1102 	 *		  statement;
1103 	 *	   else
1104 	 *		  statement
1105 	 *
1106 	 *	   if ( ... ) {
1107 	 *		  multiple;
1108 	 *		  statements;
1109 	 *	   }
1110 	 *
1111 	 *
1112 	 *	   if ( ... ) {
1113 	 *		  return elem
1114 	 *	   }
1115 	 *
1116 	 *     This example if correctly written, but the
1117 	 *     else contains only 1 statement without a terminator
1118 	 *     since the function finishes with the closing brace.
1119 	 *
1120      *     function a(flag){
1121      *         if(flag)
1122      *             test(1);
1123      *         else
1124      *             test(2)
1125      *     }
1126 	 *
1127 	 * TODO:  Deal with statements that can optional end
1128 	 *		  without a semi-colon.  Currently this messes up
1129 	 *		  the parsing of blocks.
1130 	 *		  Need to somehow detect this has happened, and either
1131 	 *		  backup a token, or skip reading the next token if
1132 	 *		  that is possible from all code locations.
1133 	 *
1134 	 */
1135 
1136 	readToken (token);
1137 
1138 	if (isKeyword (token, KEYWORD_if))
1139 	{
1140 		/*
1141 		 * Check for an "else if" and consume the "if"
1142 		 */
1143 		readToken (token);
1144 	}
1145 
1146 	if (isType (token, TOKEN_OPEN_PAREN))
1147 	{
1148 		skipArgumentList(token, false);
1149 	}
1150 
1151 	if (isType (token, TOKEN_OPEN_CURLY))
1152 	{
1153 		parseBlock (token, NULL);
1154 	}
1155 	else
1156 	{
1157 		/* The next token should only be read if this statement had its own
1158 		 * terminator */
1159 		read_next_token = findCmdTerm (token, true, false);
1160 	}
1161 	return read_next_token;
1162 }
1163 
parseImport(tokenInfo * const token)1164 static bool parseImport (tokenInfo *const token)
1165 {
1166 	if (! isKeyword (token, KEYWORD_import))
1167 		return false;
1168 
1169 	readToken (token);
1170 
1171 	if (isType (token, TOKEN_IDENTIFIER))
1172 	{
1173 		tokenInfo *const name = newToken ();
1174 
1175 		copyToken (name, token, true);
1176 		readToken (token);
1177 		while (isType (token, TOKEN_PERIOD))
1178 		{
1179 			vStringPut (name->string, '.');
1180 			readToken (token);
1181 			if (isType (token, TOKEN_IDENTIFIER))
1182 				vStringCat (name->string, token->string);
1183 			else if (isType (token, TOKEN_STAR))
1184 				vStringPut (name->string, '*');
1185 			if (isType (token, TOKEN_IDENTIFIER) || isType (token, TOKEN_STAR))
1186 				readToken (token);
1187 		}
1188 
1189 		makeFlexTag (name, FLEXTAG_IMPORT);
1190 		deleteToken (name);
1191 	}
1192 
1193 	return isType (token, TOKEN_SEMICOLON);
1194 }
1195 
parseFunction(tokenInfo * const token)1196 static void parseFunction (tokenInfo *const token)
1197 {
1198 	tokenInfo *const name = newToken ();
1199 	flexKind kind = FLEXTAG_FUNCTION;
1200 
1201 	/*
1202 	 * This deals with these formats
1203      *     private static function ioErrorHandler( event:IOErrorEvent ):void {
1204      *     public function get prop():String {}
1205      *     public function set prop(param:String):void {}
1206 	 */
1207 
1208 	if ( isKeyword(token, KEYWORD_function) )
1209 	{
1210 		readToken (token);
1211 	}
1212 
1213 	/* getter and setter */
1214 	if (isKeyword (token, KEYWORD_get) ||
1215 		isKeyword (token, KEYWORD_set))
1216 	{
1217 		kind = FLEXTAG_PROPERTY;
1218 		readToken (token);
1219 	}
1220 
1221 	copyToken (name, token, true);
1222 	/* Add scope in case this is an INNER function
1223 	addToScope(name, token->scope);
1224 	*/
1225 
1226 	DebugStatement (
1227 			debugPrintf (DEBUG_PARSE
1228 				, "\n parseFunction: token isClass:%d  scope:%s  name:%s\n"
1229 				, token->isClass
1230 				, vStringValue(token->scope)
1231 				, vStringValue(token->string)
1232 				);
1233 			);
1234 	DebugStatement (
1235 			debugPrintf (DEBUG_PARSE
1236 				, "\n parseFunction: name isClass:%d  scope:%s  name:%s\n"
1237 				, name->isClass
1238 				, vStringValue(name->scope)
1239 				, vStringValue(name->string)
1240 				);
1241 			);
1242 
1243 	readToken (token);
1244 
1245 	if ( isType (token, TOKEN_OPEN_PAREN) )
1246 		skipArgumentList(token, false);
1247 
1248 	if ( isType (token, TOKEN_COLON) )
1249 	{
1250 		/*
1251 		 *   function fname ():ReturnType
1252 		 */
1253 		readToken (token);
1254 		if (isType (token, TOKEN_IDENTIFIER))
1255 			readToken (token);
1256 	}
1257 
1258 	if ( isType (token, TOKEN_OPEN_CURLY) )
1259 	{
1260 		DebugStatement (
1261 				debugPrintf (DEBUG_PARSE
1262 					, "\n parseFunction end: name isClass:%d  scope:%s  name:%s\n"
1263 					, name->isClass
1264 					, vStringValue(name->scope)
1265 					, vStringValue(name->string)
1266 					);
1267 				);
1268 		parseBlock (token, name->string);
1269 		DebugStatement (
1270 				debugPrintf (DEBUG_PARSE
1271 					, "\n parseFunction end2: token isClass:%d  scope:%s  name:%s\n"
1272 					, token->isClass
1273 					, vStringValue(token->scope)
1274 					, vStringValue(token->string)
1275 					);
1276 				);
1277 		DebugStatement (
1278 				debugPrintf (DEBUG_PARSE
1279 					, "\n parseFunction end2: token isClass:%d  scope:%s  name:%s\n"
1280 					, token->isClass
1281 					, vStringValue(token->scope)
1282 					, vStringValue(token->string)
1283 					);
1284 				);
1285 		DebugStatement (
1286 				debugPrintf (DEBUG_PARSE
1287 					, "\n parseFunction end3: name isClass:%d  scope:%s  name:%s\n"
1288 					, name->isClass
1289 					, vStringValue(name->scope)
1290 					, vStringValue(name->string)
1291 					);
1292 				);
1293 		if (kind == FLEXTAG_FUNCTION)
1294 			makeFunctionTag (name);
1295 		else
1296 			makeFlexTag (name, kind);
1297 	}
1298 
1299 	findCmdTerm (token, false, false);
1300 
1301 	deleteToken (name);
1302 }
1303 
1304 /* Parses a block surrounded by curly braces.
1305  * @p parentScope is the scope name for this block, or NULL for unnamed scopes */
parseBlock(tokenInfo * const token,const vString * const parentScope)1306 static bool parseBlock (tokenInfo *const token, const vString *const parentScope)
1307 {
1308 	bool read_next_token = true;
1309 	vString * saveScope = vStringNew ();
1310 
1311 	vStringCopy (saveScope, token->scope);
1312 	if (parentScope)
1313 	{
1314 		addToScope (token, parentScope);
1315 		token->nestLevel++;
1316 	}
1317 
1318 	DebugStatement (
1319 			debugPrintf (DEBUG_PARSE
1320 				, "\n parseBlock start: token isClass:%d  scope:%s  name:%s\n"
1321 				, token->isClass
1322 				, vStringValue(token->scope)
1323 				, vStringValue(token->string)
1324 				);
1325 			);
1326 	/*
1327 	 * Make this routine a bit more forgiving.
1328 	 * If called on an open_curly advance it
1329 	 */
1330 	if (isType (token, TOKEN_OPEN_CURLY))
1331 		readToken(token);
1332 
1333 	if (! isType (token, TOKEN_CLOSE_CURLY))
1334 	{
1335 		/*
1336 		 * Read until we find the closing brace,
1337 		 * any nested braces will be handled within
1338 		 */
1339 		do
1340 		{
1341 			if (isType (token, TOKEN_OPEN_CURLY))
1342 			{
1343 				/* Handle nested blocks */
1344 				parseBlock (token, NULL);
1345 			}
1346 			else
1347 			{
1348 				/*
1349 				 * It is possible for a line to have no terminator
1350 				 * if the following line is a closing brace.
1351 				 * parseLine will detect this case and indicate
1352 				 * whether we should read an additional token.
1353 				 */
1354 				read_next_token = parseLine (token);
1355 			}
1356 
1357 			/*
1358 			 * Always read a new token unless we find a statement without
1359 			 * a ending terminator
1360 			 */
1361 			if( read_next_token )
1362 				readToken(token);
1363 
1364 			/*
1365 			 * If we find a statement without a terminator consider the
1366 			 * block finished, otherwise the stack will be off by one.
1367 			 */
1368 		} while (! isType (token, TOKEN_EOF) &&
1369 				 ! isType (token, TOKEN_CLOSE_CURLY) && read_next_token);
1370 	}
1371 
1372 	vStringCopy(token->scope, saveScope);
1373 	vStringDelete(saveScope);
1374 	if (parentScope)
1375 		token->nestLevel--;
1376 
1377 	DebugStatement (
1378 			debugPrintf (DEBUG_PARSE
1379 				, "\n parseBlock end: token isClass:%d  scope:%s  name:%s\n"
1380 				, token->isClass
1381 				, vStringValue(token->scope)
1382 				, vStringValue(token->string)
1383 				);
1384 			);
1385 	return false;
1386 }
1387 
parseMethods(tokenInfo * const token,const tokenInfo * const class)1388 static void parseMethods (tokenInfo *const token, const tokenInfo *const class)
1389 {
1390 	tokenInfo *const name = newToken ();
1391 	vString *saveScope = vStringNew ();
1392 
1393 	vStringCopy (saveScope, token->scope);
1394 	addToScope (token, class->string);
1395 
1396 	/*
1397 	 * This deals with these formats
1398 	 *	   validProperty  : 2,
1399 	 *	   validMethod    : function(a,b) {}
1400 	 *	   'validMethod2' : function(a,b) {}
1401      *     container.dirtyTab = {'url': false, 'title':false, 'snapshot':false, '*': false}
1402 	 */
1403 
1404 	do
1405 	{
1406 		readToken (token);
1407 		if (isType (token, TOKEN_CLOSE_CURLY))
1408 		{
1409 			goto cleanUp;
1410 		}
1411 
1412 		if (isType (token, TOKEN_STRING) || isKeyword(token, KEYWORD_NONE))
1413 		{
1414 			copyToken (name, token, true);
1415 
1416 			readToken (token);
1417 			if ( isType (token, TOKEN_COLON) )
1418 			{
1419 				readToken (token);
1420 				if ( isKeyword (token, KEYWORD_function) )
1421 				{
1422 					readToken (token);
1423 					if ( isType (token, TOKEN_OPEN_PAREN) )
1424 					{
1425 						skipArgumentList(token, false);
1426 					}
1427 
1428 					if (isType (token, TOKEN_OPEN_CURLY))
1429 					{
1430 						makeFlexTag (name, FLEXTAG_METHOD);
1431 						parseBlock (token, name->string);
1432 
1433 						/*
1434 						 * Read to the closing curly, check next
1435 						 * token, if a comma, we must loop again
1436 						 */
1437 						readToken (token);
1438 					}
1439 				}
1440 				else
1441 				{
1442 						makeFlexTag (name, FLEXTAG_PROPERTY);
1443 
1444 						/*
1445 						 * Read the next token, if a comma
1446 						 * we must loop again
1447 						 */
1448 						readToken (token);
1449 				}
1450 			}
1451 		}
1452 	} while ( isType(token, TOKEN_COMMA));
1453 
1454 	findCmdTerm (token, false, false);
1455 
1456 cleanUp:
1457 	vStringCopy (token->scope, saveScope);
1458 	vStringDelete (saveScope);
1459 	deleteToken (name);
1460 }
1461 
parseVar(tokenInfo * const token,bool is_public)1462 static bool parseVar (tokenInfo *const token, bool is_public)
1463 {
1464 	tokenInfo *const name = newToken ();
1465 	bool is_terminated = true;
1466 	flexKind kind = is_public ? FLEXTAG_VARIABLE : FLEXTAG_LOCALVAR;
1467 
1468 	/*
1469 	 * Variables are defined as:
1470 	 *     private static var lastFaultMessage:Date = new Date( 0 );
1471 	 *     private static var webRequests:ArrayCollection = new ArrayCollection();
1472 	 */
1473 
1474 	if ( isKeyword(token, KEYWORD_var) )
1475 	{
1476 		readToken(token);
1477 	}
1478 	else if (isKeyword(token, KEYWORD_const))
1479 	{
1480 		kind = FLEXTAG_CONST;
1481 		readToken(token);
1482 	}
1483 
1484 	/* Variable name */
1485 	copyToken (name, token, true);
1486 	readToken(token);
1487 
1488 	if ( isType (token, TOKEN_COLON) )
1489 	{
1490 		/*
1491 		 *   var vname ():DataType = new Date();
1492 		 *   var vname ():DataType;
1493 		 */
1494 		readToken (token);
1495 		if (isType (token, TOKEN_IDENTIFIER))
1496 			readToken (token);
1497 	}
1498 
1499 	is_terminated = findCmdTerm (token, true, false);
1500 
1501 	if ( isType (token, TOKEN_SEMICOLON) )
1502 	{
1503 		makeFlexTag (name, kind);
1504 	}
1505 
1506 	deleteToken (name);
1507 
1508 	return is_terminated;
1509 }
1510 
parsePackage(tokenInfo * const token)1511 static void parsePackage (tokenInfo *const token)
1512 {
1513 	tokenInfo *name = NULL;
1514 
1515 	if (isKeyword (token, KEYWORD_package))
1516 		readToken(token);
1517 
1518 	/* name is optional and can be qualified */
1519 	if (isType (token, TOKEN_IDENTIFIER))
1520 	{
1521 		name = newToken ();
1522 		copyToken (name, token, true);
1523 		readToken (token);
1524 
1525 		while (isType (token, TOKEN_PERIOD))
1526 		{
1527 			vStringPut (name->string, '.');
1528 			readToken (token);
1529 			if (isType (token, TOKEN_IDENTIFIER))
1530 			{
1531 				vStringCat (name->string, token->string);
1532 				readToken (token);
1533 			}
1534 		}
1535 	}
1536 
1537 	if (isType (token, TOKEN_OPEN_CURLY))
1538 	{
1539 		if (name)
1540 			makeFlexTag (name, FLEXTAG_PACKAGE);
1541 		parseBlock (token, name ? name->string : NULL);
1542 	}
1543 
1544 	if (name)
1545 		deleteToken (name);
1546 }
1547 
parseClass(tokenInfo * const token)1548 static bool parseClass (tokenInfo *const token)
1549 {
1550 	tokenInfo *const name = newToken ();
1551 	bool saveIsClass = token->isClass;
1552 
1553 	/*
1554 	 * Variables are defined as:
1555 	 *     private static var lastFaultMessage:Date = new Date( 0 );
1556 	 *     private static var webRequests:ArrayCollection = new ArrayCollection();
1557 	 */
1558 
1559 	if ( isKeyword(token, KEYWORD_class) )
1560 	{
1561 		readToken(token);
1562 	}
1563 
1564 	token->isClass = true;
1565 	/* Class name */
1566 	copyToken (name, token, true);
1567 	readToken(token);
1568 
1569 	DebugStatement (
1570 			debugPrintf (DEBUG_PARSE
1571 				, "\n parseClass start: token isClass:%d  scope:%s  name:%s\n"
1572 				, token->isClass
1573 				, vStringValue(token->scope)
1574 				, vStringValue(token->string)
1575 				);
1576 			);
1577 
1578 	if (isKeyword (token, KEYWORD_extends))
1579 	{
1580 		readToken (token);
1581 		if (isType (token, TOKEN_IDENTIFIER))
1582 			readToken (token);
1583 	}
1584 
1585 	if (isKeyword (token, KEYWORD_implements))
1586 	{
1587 		do
1588 		{
1589 			readToken (token);
1590 			if (isType (token, TOKEN_IDENTIFIER))
1591 				readToken (token);
1592 		}
1593 		while (isType (token, TOKEN_COMMA));
1594 	}
1595 
1596 	if ( isType (token, TOKEN_OPEN_CURLY) )
1597 	{
1598 		makeClassTag (name);
1599 		parseBlock (token, name->string);
1600 	}
1601 
1602 	DebugStatement (
1603 			debugPrintf (DEBUG_PARSE
1604 				, "\n parseClass end: token isClass:%d  scope:%s  name:%s\n"
1605 				, token->isClass
1606 				, vStringValue(token->scope)
1607 				, vStringValue(token->string)
1608 				);
1609 			);
1610 	token->isClass = saveIsClass;
1611 	deleteToken (name);
1612 
1613 	return true;
1614 }
1615 
parseInterface(tokenInfo * const token)1616 static void parseInterface (tokenInfo *const token)
1617 {
1618 	tokenInfo *const name = newToken ();
1619 	bool saveIsClass = token->isClass;
1620 
1621 	if (isKeyword(token, KEYWORD_interface))
1622 		readToken(token);
1623 
1624 	token->isClass = true;
1625 	/* interface name */
1626 	copyToken (name, token, true);
1627 	readToken (token);
1628 
1629 	/* interfaces can extend multiple interfaces */
1630 	if (isKeyword (token, KEYWORD_extends))
1631 	{
1632 		do
1633 		{
1634 			readToken (token);
1635 			if (isType (token, TOKEN_IDENTIFIER))
1636 				readToken (token);
1637 		}
1638 		while (isType (token, TOKEN_COMMA));
1639 	}
1640 
1641 	if (isType (token, TOKEN_OPEN_CURLY))
1642 	{
1643 		makeFlexTag (name, FLEXTAG_INTERFACE);
1644 		parseBlock (token, name->string);
1645 	}
1646 
1647 	token->isClass = saveIsClass;
1648 	deleteToken (name);
1649 }
1650 
parseStatement(tokenInfo * const token)1651 static bool parseStatement (tokenInfo *const token)
1652 {
1653 	tokenInfo *const name = newToken ();
1654 	tokenInfo *const secondary_name = newToken ();
1655 	vString * saveScope = vStringNew ();
1656 	bool is_public = false;
1657 	bool is_class = false;
1658 	bool is_terminated = true;
1659 	bool is_global = false;
1660 	/* bool is_prototype = false; */
1661 	vString *	fulltag;
1662 
1663 	vStringCopy (saveScope, token->scope);
1664 	DebugStatement (
1665 			debugPrintf (DEBUG_PARSE
1666 				, "\n parseStatement: token isClass:%d  scope:%s  name:%s\n"
1667 				, token->isClass
1668 				, vStringValue(token->scope)
1669 				, vStringValue(token->string)
1670 				);
1671 			);
1672 	/*
1673 	 * Functions can be named or unnamed.
1674 	 * This deals with these formats:
1675 	 * Function
1676 	 *	   validFunctionOne = function(a,b) {}
1677 	 *	   testlib.validFunctionFive = function(a,b) {}
1678 	 *	   var innerThree = function(a,b) {}
1679 	 *	   var innerFour = (a,b) {}
1680 	 *	   var D2 = secondary_fcn_name(a,b) {}
1681 	 *	   var D3 = new Function("a", "b", "return a+b;");
1682 	 * Class
1683 	 *	   testlib.extras.ValidClassOne = function(a,b) {
1684 	 *		   this.a = a;
1685 	 *	   }
1686 	 * Class Methods
1687 	 *	   testlib.extras.ValidClassOne.prototype = {
1688 	 *		   'validMethodOne' : function(a,b) {},
1689 	 *		   'validMethodTwo' : function(a,b) {}
1690 	 *	   }
1691      *     ValidClassTwo = function ()
1692      *     {
1693      *         this.validMethodThree = function() {}
1694      *         // unnamed method
1695      *         this.validMethodFour = () {}
1696      *     }
1697 	 *	   Database.prototype.validMethodThree = Database_getTodaysDate;
1698 	 */
1699 
1700 	/* skip attributes */
1701 	while (isKeyword (token, KEYWORD_public) ||
1702 	       isKeyword (token, KEYWORD_protected) ||
1703 	       isKeyword (token, KEYWORD_private) ||
1704 	       isKeyword (token, KEYWORD_override) ||
1705 	       isKeyword (token, KEYWORD_static) ||
1706 	       isKeyword (token, KEYWORD_internal) ||
1707 	       isKeyword (token, KEYWORD_native) ||
1708 	       isKeyword (token, KEYWORD_dynamic) ||
1709 	       isKeyword (token, KEYWORD_final))
1710 	{
1711 		if (isKeyword(token, KEYWORD_public))
1712 			is_public = true;
1713 
1714 		readToken (token);
1715 	}
1716 
1717 	if (isType(token, TOKEN_KEYWORD))
1718 	{
1719 		switch (token->keyword)
1720 		{
1721 			case KEYWORD_for:
1722 			case KEYWORD_while:
1723 			case KEYWORD_do:
1724 				is_terminated = parseLoop (token);
1725 				break;
1726 			case KEYWORD_if:
1727 			case KEYWORD_else:
1728 			case KEYWORD_try:
1729 			case KEYWORD_catch:
1730 			case KEYWORD_finally:
1731 				/* Common semantics */
1732 				is_terminated = parseIf (token);
1733 				break;
1734 			case KEYWORD_switch:
1735 				parseSwitch (token);
1736 				break;
1737 			case KEYWORD_package:
1738 				parsePackage (token);
1739 				goto cleanUp;
1740 				break;
1741 			case KEYWORD_class:
1742 				parseClass (token);
1743 				goto cleanUp;
1744 				break;
1745 			case KEYWORD_interface:
1746 				parseInterface (token);
1747 				goto cleanUp;
1748 				break;
1749 			case KEYWORD_function:
1750 				parseFunction (token);
1751 				goto cleanUp;
1752 				break;
1753 			case KEYWORD_var:
1754 			case KEYWORD_const:
1755 				is_terminated = parseVar (token, is_public);
1756 				goto cleanUp;
1757 				break;
1758 			default:
1759 				readToken(token);
1760 				break;
1761 		}
1762 	}
1763 
1764 nextVar:
1765 	copyToken (name, token, true);
1766 
1767 	while (! isType (token, TOKEN_CLOSE_CURLY) &&
1768 	       ! isType (token, TOKEN_SEMICOLON)   &&
1769 	       ! isType (token, TOKEN_EQUAL_SIGN)  &&
1770 	       ! isType (token, TOKEN_COMMA)       &&
1771 	       ! isType (token, TOKEN_EOF))
1772 	{
1773 		if (isType (token, TOKEN_OPEN_CURLY))
1774 			parseBlock (token, NULL);
1775 
1776 		/* Potentially the name of the function */
1777 		if (isType (token, TOKEN_PERIOD))
1778 		{
1779 			/*
1780 			 * Cannot be a global variable is it has dot references in the name
1781 			 */
1782 			is_global = false;
1783 			/* Assume it's an assignment to a global name (e.g. a class) using
1784 			 * its fully qualified name, so strip the scope.
1785 			 * FIXME: resolve the scope so we can make more than an assumption. */
1786 			vStringClear (token->scope);
1787 			vStringClear (name->scope);
1788 			do
1789 			{
1790 				readToken (token);
1791 				if (! isType(token, TOKEN_KEYWORD))
1792 				{
1793 					if ( is_class )
1794 					{
1795 						addToScope(token, name->string);
1796 					}
1797 					else
1798 						addContext (name, token);
1799 
1800 					readToken (token);
1801 				}
1802 				else if ( isKeyword(token, KEYWORD_prototype) )
1803 				{
1804 					/*
1805 					 * When we reach the "prototype" tag, we infer:
1806 					 *     "BindAgent" is a class
1807 					 *     "build"     is a method
1808 					 *
1809 					 * function BindAgent( repeatableIdName, newParentIdName ) {
1810 					 * }
1811 					 *
1812 					 * CASE 1
1813 					 * Specified function name: "build"
1814 					 *     BindAgent.prototype.build = function( mode ) {
1815 					 *     	  ignore everything within this function
1816 					 *     }
1817 					 *
1818 					 * CASE 2
1819 					 * Prototype listing
1820 					 *     ValidClassOne.prototype = {
1821 					 *         'validMethodOne' : function(a,b) {},
1822 					 *         'validMethodTwo' : function(a,b) {}
1823 					 *     }
1824 					 *
1825 					 */
1826 					makeClassTag (name);
1827 					is_class = true;
1828 					/* is_prototype = true; */
1829 
1830 					/*
1831 					 * There should a ".function_name" next.
1832 					 */
1833 					readToken (token);
1834 					if (isType (token, TOKEN_PERIOD))
1835 					{
1836 						/*
1837 						 * Handle CASE 1
1838 						 */
1839 						readToken (token);
1840 						if (! isType(token, TOKEN_KEYWORD))
1841 						{
1842 							addToScope(token, name->string);
1843 
1844 							makeFlexTag (token, FLEXTAG_METHOD);
1845 							/*
1846 							 * We can read until the end of the block / statement.
1847 							 * We need to correctly parse any nested blocks, but
1848 							 * we do NOT want to create any tags based on what is
1849 							 * within the blocks.
1850 							 */
1851 							token->ignoreTag = true;
1852 							/*
1853 							 * Find to the end of the statement
1854 							 */
1855 							findCmdTerm (token, false, false);
1856 							token->ignoreTag = false;
1857 							is_terminated = true;
1858 							goto cleanUp;
1859 						}
1860 					}
1861 					else if (isType (token, TOKEN_EQUAL_SIGN))
1862 					{
1863 						readToken (token);
1864 						if (isType (token, TOKEN_OPEN_CURLY))
1865 						{
1866 							/*
1867 							 * Handle CASE 2
1868 							 *
1869 							 * Creates tags for each of these class methods
1870 							 *     ValidClassOne.prototype = {
1871 							 *         'validMethodOne' : function(a,b) {},
1872 							 *         'validMethodTwo' : function(a,b) {}
1873 							 *     }
1874 							 */
1875 							parseMethods(token, name);
1876 							/*
1877 							 * Find to the end of the statement
1878 							 */
1879 							findCmdTerm (token, false, false);
1880 							token->ignoreTag = false;
1881 							is_terminated = true;
1882 							goto cleanUp;
1883 						}
1884 					}
1885 				}
1886 				else
1887 					readToken (token);
1888 			} while (isType (token, TOKEN_PERIOD));
1889 		}
1890 		else
1891 			readTokenFull (token, true);
1892 
1893 		if ( isType (token, TOKEN_OPEN_PAREN) )
1894 			skipArgumentList(token, false);
1895 
1896 		if ( isType (token, TOKEN_COLON) )
1897 		{
1898 			/*
1899 			 * Functions are of this form:
1900 			 *   function fname ():ReturnType {
1901 			 */
1902 			readToken (token);
1903 			if (isType (token, TOKEN_IDENTIFIER))
1904 				readToken (token);
1905 		}
1906 
1907 		if ( isType (token, TOKEN_OPEN_SQUARE) )
1908 			skipArrayList(token, false);
1909 	}
1910 
1911 	if ( isType (token, TOKEN_CLOSE_CURLY) )
1912 	{
1913 		/*
1914 		 * Reaching this section without having
1915 		 * processed an open curly brace indicates
1916 		 * the statement is most likely not terminated.
1917 		 */
1918 		is_terminated = false;
1919 		goto cleanUp;
1920 	}
1921 
1922 	if ( isType (token, TOKEN_SEMICOLON) ||
1923 	     isType (token, TOKEN_EOF) ||
1924 	     isType (token, TOKEN_COMMA) )
1925 	{
1926 		/*
1927 		 * Only create variables for global scope
1928 		 */
1929 		if ( token->nestLevel == 0 && is_global )
1930 		{
1931 			/*
1932 			 * Handles this syntax:
1933 			 *	   var g_var2;
1934 			 */
1935 			makeFlexTag (name, FLEXTAG_VARIABLE);
1936 		}
1937 		/*
1938 		 * Statement has ended.
1939 		 * This deals with calls to functions, like:
1940 		 *     alert(..);
1941 		 */
1942 		if (isType (token, TOKEN_COMMA))
1943 		{
1944 			readToken (token);
1945 			goto nextVar;
1946 		}
1947 		goto cleanUp;
1948 	}
1949 
1950 	if ( isType (token, TOKEN_EQUAL_SIGN) )
1951 	{
1952 		readToken (token);
1953 
1954 		if ( isKeyword (token, KEYWORD_function) )
1955 		{
1956 			readToken (token);
1957 
1958 			if (! isType (token, TOKEN_KEYWORD) &&
1959 				! isType (token, TOKEN_OPEN_PAREN))
1960 			{
1961 				/*
1962 				 * Functions of this format:
1963 				 *	   var D2A = function theAdd(a, b)
1964 				 *	   {
1965 				 *		  return a+b;
1966 				 *	   }
1967 				 * Are really two separate defined functions and
1968 				 * can be referenced in two ways:
1969 				 *	   alert( D2A(1,2) );			  // produces 3
1970 				 *	   alert( theAdd(1,2) );		  // also produces 3
1971 				 * So it must have two tags:
1972 				 *	   D2A
1973 				 *	   theAdd
1974 				 * Save the reference to the name for later use, once
1975 				 * we have established this is a valid function we will
1976 				 * create the secondary reference to it.
1977 				 */
1978 				copyToken (secondary_name, token, true);
1979 				readToken (token);
1980 			}
1981 
1982 			if ( isType (token, TOKEN_OPEN_PAREN) )
1983 				skipArgumentList(token, false);
1984 
1985 			if (isType (token, TOKEN_OPEN_CURLY))
1986 			{
1987 				/*
1988 				 * This will be either a function or a class.
1989 				 * We can only determine this by checking the body
1990 				 * of the function.  If we find a "this." we know
1991 				 * it is a class, otherwise it is a function.
1992 				 */
1993 				if ( token->isClass )
1994 				{
1995 					makeFlexTag (name, FLEXTAG_METHOD);
1996 					if ( vStringLength(secondary_name->string) > 0 )
1997 						makeFunctionTag (secondary_name);
1998 					parseBlock (token, name->string);
1999 				}
2000 				else
2001 				{
2002 					parseBlock (token, name->string);
2003 					makeFunctionTag (name);
2004 
2005 					if ( vStringLength(secondary_name->string) > 0 )
2006 						makeFunctionTag (secondary_name);
2007 				}
2008 			}
2009 		}
2010 		else if (isType (token, TOKEN_OPEN_PAREN))
2011 		{
2012 			/*
2013 			 * Handle nameless functions
2014 			 *     this.method_name = () {}
2015 			 */
2016 			skipArgumentList(token, false);
2017 
2018 			if (isType (token, TOKEN_OPEN_CURLY))
2019 			{
2020 				/*
2021 				 * Nameless functions are only setup as methods.
2022 				 */
2023 				makeFlexTag (name, FLEXTAG_METHOD);
2024 				parseBlock (token, name->string);
2025 			}
2026 		}
2027 		else if (isType (token, TOKEN_OPEN_CURLY))
2028 		{
2029 			/*
2030 			 * Creates tags for each of these class methods
2031 			 *     ValidClassOne.prototype = {
2032 			 *         'validMethodOne' : function(a,b) {},
2033 			 *         'validMethodTwo' : function(a,b) {}
2034 			 *     }
2035 			 */
2036 			parseMethods(token, name);
2037 			/* Here we should be at the end of the block, on the close curly.
2038 			 * If so, read the next token not to confuse that close curly with
2039 			 * the end of the current statement. */
2040 			if (isType (token, TOKEN_CLOSE_CURLY))
2041 			{
2042 				readTokenFull(token, true);
2043 				is_terminated = isType (token, TOKEN_SEMICOLON);
2044 			}
2045 		}
2046 		else if (isKeyword (token, KEYWORD_new))
2047 		{
2048 			readToken (token);
2049 			if ( isKeyword (token, KEYWORD_function) ||
2050 					isKeyword (token, KEYWORD_capital_function) ||
2051 					isKeyword (token, KEYWORD_object) ||
2052 					isKeyword (token, KEYWORD_capital_object) )
2053 			{
2054 				if ( isKeyword (token, KEYWORD_object) ||
2055 						isKeyword (token, KEYWORD_capital_object) )
2056 					is_class = true;
2057 
2058 				readToken (token);
2059 				if ( isType (token, TOKEN_OPEN_PAREN) )
2060 					skipArgumentList(token, true);
2061 
2062 				if (isType (token, TOKEN_SEMICOLON))
2063 				{
2064 					if ( token->nestLevel == 0 )
2065 					{
2066 						if ( is_class )
2067 						{
2068 							makeClassTag (name);
2069 						} else {
2070 							makeFunctionTag (name);
2071 						}
2072 					}
2073 				}
2074 				else if (isType (token, TOKEN_CLOSE_CURLY))
2075 					is_terminated = false;
2076 			}
2077 		}
2078 		else if (! isType (token, TOKEN_KEYWORD))
2079 		{
2080 			/*
2081 			 * Only create variables for global scope
2082 			 */
2083 			if ( token->nestLevel == 0 && is_global )
2084 			{
2085 				/*
2086 				 * A pointer can be created to the function.
2087 				 * If we recognize the function/class name ignore the variable.
2088 				 * This format looks identical to a variable definition.
2089 				 * A variable defined outside of a block is considered
2090 				 * a global variable:
2091 				 *	   var g_var1 = 1;
2092 				 *	   var g_var2;
2093 				 * This is not a global variable:
2094 				 *	   var g_var = function;
2095 				 * This is a global variable:
2096 				 *	   var g_var = different_var_name;
2097 				 */
2098 				fulltag = vStringNew ();
2099 				if (vStringLength (token->scope) > 0)
2100 				{
2101 					vStringCopy(fulltag, token->scope);
2102 					vStringPut (fulltag, '.');
2103 					vStringCat (fulltag, token->string);
2104 				}
2105 				else
2106 				{
2107 					vStringCopy(fulltag, token->string);
2108 				}
2109 				if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
2110 						! stringListHas(ClassNames, vStringValue (fulltag)) )
2111 				{
2112 					makeFlexTag (name, FLEXTAG_VARIABLE);
2113 				}
2114 				vStringDelete (fulltag);
2115 			}
2116 		}
2117 	}
2118 	/* if we aren't already at the cmd end, advance to it and check whether
2119 	 * the statement was terminated */
2120 	if (! isType (token, TOKEN_CLOSE_CURLY) &&
2121 	    ! isType (token, TOKEN_SEMICOLON))
2122 	{
2123 		/*
2124 		 * Statements can be optionally terminated in the case of
2125 		 * statement prior to a close curly brace as in the
2126 		 * document.write line below:
2127 		 *
2128 		 * function checkForUpdate() {
2129 		 *	   if( 1==1 ) {
2130 		 *		   document.write("hello from checkForUpdate<br>")
2131 		 *	   }
2132 		 *	   return 1;
2133 		 * }
2134 		 */
2135 		is_terminated = findCmdTerm (token, true, true);
2136 		/* if we're at a comma, try and read a second var */
2137 		if (isType (token, TOKEN_COMMA))
2138 		{
2139 			readToken (token);
2140 			goto nextVar;
2141 		}
2142 	}
2143 
2144 cleanUp:
2145 	vStringCopy(token->scope, saveScope);
2146 	deleteToken (name);
2147 	deleteToken (secondary_name);
2148 	vStringDelete(saveScope);
2149 
2150 	return is_terminated;
2151 }
2152 
parseLine(tokenInfo * const token)2153 static bool parseLine (tokenInfo *const token)
2154 {
2155 	bool is_terminated = true;
2156 	/*
2157 	 * Detect the common statements, if, while, for, do, ...
2158 	 * This is necessary since the last statement within a block "{}"
2159 	 * can be optionally terminated.
2160 	 *
2161 	 * If the statement is not terminated, we need to tell
2162 	 * the calling routine to prevent reading an additional token
2163 	 * looking for the end of the statement.
2164 	 */
2165 
2166 	if (isType(token, TOKEN_KEYWORD))
2167 	{
2168 		switch (token->keyword)
2169 		{
2170 			case KEYWORD_for:
2171 			case KEYWORD_while:
2172 			case KEYWORD_do:
2173 				is_terminated = parseLoop (token);
2174 				break;
2175 			case KEYWORD_if:
2176 			case KEYWORD_else:
2177 			case KEYWORD_try:
2178 			case KEYWORD_catch:
2179 			case KEYWORD_finally:
2180 				/* Common semantics */
2181 				is_terminated = parseIf (token);
2182 				break;
2183 			case KEYWORD_switch:
2184 				parseSwitch (token);
2185 				break;
2186 			case KEYWORD_return:
2187 				readToken (token);
2188 				is_terminated = parseLine (token);
2189 				break;
2190 			case KEYWORD_function:
2191 				parseFunction (token);
2192 				break;
2193 			case KEYWORD_import:
2194 				is_terminated = parseImport (token);
2195 				/* to properly support unterminated imports at top level,
2196 				 * recurse here because parseActionScript() will *always*
2197 				 * advance to avoid ever getting stuck. */
2198 				if (! is_terminated)
2199 					return parseLine (token);
2200 				break;
2201 			default:
2202 				is_terminated = parseStatement (token);
2203 				break;
2204 		}
2205 	}
2206 	else
2207 	{
2208 		/*
2209 		 * Special case where single line statements may not be
2210 		 * SEMICOLON terminated.  parseBlock needs to know this
2211 		 * so that it does not read the next token.
2212 		 */
2213 		is_terminated = parseStatement (token);
2214 	}
2215 	return is_terminated;
2216 }
2217 
parseCDATA(tokenInfo * const token)2218 static bool parseCDATA (tokenInfo *const token)
2219 {
2220 	if (isType (token, TOKEN_LESS_THAN))
2221 	{
2222 		/*
2223 		 * Handle these tags
2224 		 * <![CDATA[
2225 		 *    ...
2226 		 * ]]>
2227 		 */
2228 		readToken (token);
2229 		if (isType (token, TOKEN_EXCLAMATION))
2230 		{
2231 			readToken (token);
2232 			if (isType (token, TOKEN_OPEN_SQUARE))
2233 			{
2234 				readToken (token);
2235 				if (isKeyword (token, KEYWORD_cdata))
2236 				{
2237 					readToken (token);
2238 					if (isType (token, TOKEN_OPEN_SQUARE))
2239 					{
2240 						parseActionScript (token, true);
2241 						if (isType (token, TOKEN_CLOSE_SQUARE))
2242 						{
2243 							readToken (token);
2244 							if (isType (token, TOKEN_CLOSE_SQUARE))
2245 							{
2246 								readToken (token);
2247 							}
2248 						}
2249 					}
2250 				}
2251 			}
2252 		}
2253 	}
2254 	else
2255 	{
2256 		parseActionScript (token, false);
2257 	}
2258 	return true;
2259 }
2260 
parseNamespace(tokenInfo * const token)2261 static bool parseNamespace (tokenInfo *const token)
2262 {
2263 	/*
2264 	 * If we have found a <, we know it is not a TOKEN_OPEN_MXML
2265 	 * but it could potentially be a different namespace.
2266 	 * This means it will also have a closing tag, which will
2267 	 * mess up the parser if we do not properly recurse
2268 	 * through these tags.
2269 	 */
2270 
2271 	if (isType (token, TOKEN_LESS_THAN))
2272 	{
2273 		readToken (token);
2274 	}
2275 
2276 	/*
2277 	 * Check if we have reached a other namespace tag
2278 	 *   <views:Object ... />
2279 	 * or
2280 	 *   <views:Object ... >
2281 	 *   </views:Object>
2282 	 */
2283 	if (isType (token, TOKEN_IDENTIFIER))
2284 	{
2285 		readToken (token);
2286 		if (isType (token, TOKEN_COLON))
2287 		{
2288 			readToken (token);
2289 			if ( ! isType (token, TOKEN_IDENTIFIER))
2290 			{
2291 				return true;
2292 			}
2293 		}
2294 		else
2295 		{
2296 			return true;
2297 		}
2298 	}
2299 	else
2300 	{
2301 		return true;
2302 	}
2303 
2304 	/*
2305 	 * Confirmed we are inside a namespace tag, so
2306 	 * process it until the close tag.
2307 	 *
2308 	 * But also check for new tags, which will either
2309 	 * be recursive namespaces or MXML tags
2310 	 */
2311 	do
2312 	{
2313 		if (isType (token, TOKEN_LESS_THAN))
2314 		{
2315 			parseNamespace (token);
2316 			readToken (token);
2317 		}
2318 		if (isType (token, TOKEN_OPEN_MXML))
2319 		{
2320 			parseMXML (token);
2321 		}
2322 		else
2323 		{
2324 			readToken (token);
2325 		}
2326 	} while (! (isType (token, TOKEN_CLOSE_SGML) ||
2327 		    isType (token, TOKEN_CLOSE_MXML) ||
2328 		    isEOF (token)) );
2329 	return true;
2330 }
2331 
parseMXML(tokenInfo * const token)2332 static bool parseMXML (tokenInfo *const token)
2333 {
2334 	tokenInfo *const name = newToken ();
2335 	tokenInfo *const type = newToken ();
2336 	bool inside_attributes = true;
2337 	/*
2338 	 * Detect the common statements, if, while, for, do, ...
2339 	 * This is necessary since the last statement within a block "{}"
2340 	 * can be optionally terminated.
2341 	 *
2342 	 * If the statement is not terminated, we need to tell
2343 	 * the calling routine to prevent reading an additional token
2344 	 * looking for the end of the statement.
2345 	 */
2346 
2347 	readToken (token);
2348 
2349 	if (isKeyword (token, KEYWORD_script))
2350 	{
2351 		/*
2352 		 * These tags can be of this form:
2353 		 * <mx:Script src="filename.as" />
2354 		 */
2355 		do
2356 		{
2357 			readToken (token);
2358 		} while (! (isType (token, TOKEN_CLOSE_SGML)   ||
2359 			    isType (token, TOKEN_CLOSE_MXML)   ||
2360 			    isType (token, TOKEN_GREATER_THAN) ||
2361 			    isEOF (token)) );
2362 
2363 		if (isType (token, TOKEN_CLOSE_MXML))
2364 		{
2365 			/*
2366 			 * We have found a </mx:type> tag
2367 			 * Finish reading the "type" and ">"
2368 			 */
2369 			readToken (token);
2370 			readToken (token);
2371 			goto cleanUp;
2372 		}
2373 		if (isType (token, TOKEN_CLOSE_SGML))
2374 		{
2375 			/*
2376 			 * We have found a <mx:Script src="filename.as" />
2377 			 */
2378 			goto cleanUp;
2379 		}
2380 
2381 		/*
2382 		 * This is a beginning of an embedded script.
2383 		 * These typically are of this format:
2384 		 *    <mx:Script>
2385 		 *        <![CDATA[
2386 		 *        ... ActionScript ...
2387 		 *        ]]>
2388 		 *    </mx:Script>
2389 		 */
2390 		readToken (token);
2391 		parseCDATA (token);
2392 
2393 		readToken (token);
2394 		if (isType (token, TOKEN_CLOSE_MXML))
2395 		{
2396 			/*
2397 			 * We have found a </mx:type> tag
2398 			 * Finish reading the "type" and ">"
2399 			 */
2400 			readToken (token);
2401 			readToken (token);
2402 		}
2403 		goto cleanUp;
2404 	}
2405 
2406 	copyToken (type, token, true);
2407 
2408 	readToken (token);
2409 	do
2410 	{
2411 		if (isType (token, TOKEN_GREATER_THAN))
2412 		{
2413 			inside_attributes = false;
2414 		}
2415 		if (isType (token, TOKEN_LESS_THAN))
2416 		{
2417 			parseNamespace (token);
2418 			readToken (token);
2419 		}
2420 		else if (isType (token, TOKEN_OPEN_MXML))
2421 		{
2422 			parseMXML (token);
2423 			readToken (token);
2424 		}
2425 		else if (inside_attributes && (isKeyword (token, KEYWORD_id) || isKeyword (token, KEYWORD_name)))
2426 		{
2427 			if (vStringLength(name->string) == 0 )
2428 			{
2429 				/*
2430 				 * If we have already created the tag based on either "name"
2431 				 * or "id" do not do it again.
2432 				 */
2433 				readToken (token);
2434 				readToken (token);
2435 
2436 				copyToken (name, token, true);
2437 				addToScope (name, type->string);
2438 				makeMXTag (name);
2439 			}
2440 			else
2441 			{
2442 				readToken (token);
2443 			}
2444 		}
2445 		else
2446 		{
2447 			readToken (token);
2448 		}
2449 	} while (! (isType (token, TOKEN_CLOSE_SGML) ||
2450 		    isType (token, TOKEN_CLOSE_MXML) ||
2451 		    isEOF (token)) );
2452 
2453 	if (isType (token, TOKEN_CLOSE_MXML))
2454 	{
2455 		/*
2456 		 * We have found a </mx:type> tag
2457 		 * Finish reading the "type" and ">"
2458 		 */
2459 		readToken (token);
2460 		readToken (token);
2461 	}
2462 
2463 cleanUp:
2464 	deleteToken (name);
2465 	deleteToken (type);
2466 	return true;
2467 }
2468 
parseActionScript(tokenInfo * const token,bool readNext)2469 static bool parseActionScript (tokenInfo *const token, bool readNext)
2470 {
2471 	LastTokenType = TOKEN_UNDEFINED;
2472 
2473 	do
2474 	{
2475 		if (! readNext)
2476 			readNext = true;
2477 		else
2478 			readToken (token);
2479 
2480 		if (isType (token, TOKEN_LESS_THAN))
2481 		{
2482 			/*
2483 			 * Handle these tags
2484 			 * <![CDATA[
2485 			 *    ...
2486 			 * ]]>
2487 			 */
2488 			readToken (token);
2489 			if (isType (token, TOKEN_EQUAL_SIGN))
2490 			{
2491 				if (isType (token, TOKEN_OPEN_SQUARE))
2492 				{
2493 					readToken (token);
2494 					if (isKeyword (token, KEYWORD_cdata))
2495 					{
2496 						readToken (token);
2497 					}
2498 				}
2499 			}
2500 		}
2501 		if (isType (token, TOKEN_CLOSE_SQUARE))
2502 		{
2503 			/*
2504 			 * Handle these tags
2505 			 * <![CDATA[
2506 			 *    ...
2507 			 * ]]>
2508 			 */
2509 			readToken (token);
2510 			if (isType (token, TOKEN_CLOSE_SQUARE))
2511 			{
2512 				readToken (token);
2513 				if (isType (token, TOKEN_GREATER_THAN))
2514 				{
2515 					return true;
2516 				}
2517 			}
2518 		}
2519 		else if (isType (token, TOKEN_CLOSE_MXML))
2520 		{
2521 			/*
2522 			 * Read the Script> tags
2523 			 */
2524 			readToken (token);
2525 			readToken (token);
2526 			return true;
2527 		}
2528 		else if (isType (token, TOKEN_OPEN_MXML))
2529 		{
2530 			parseMXML (token);
2531 		}
2532 		else
2533 		{
2534 			parseLine (token);
2535 		}
2536 	} while (!isEOF (token));
2537 	return true;
2538 }
2539 
parseFlexFile(tokenInfo * const token)2540 static void parseFlexFile (tokenInfo *const token)
2541 {
2542 	do
2543 	{
2544 		readToken (token);
2545 
2546 		if (isType (token, TOKEN_OPEN_MXML))
2547 		{
2548 			parseMXML (token);
2549 		}
2550 		else if (isType (token, TOKEN_LESS_THAN))
2551 		{
2552 			readToken (token);
2553 			if (isType (token, TOKEN_QUESTION_MARK))
2554 			{
2555 				/*
2556 				 * <?xml version="1.0" encoding="utf-8"?>
2557 				 */
2558 				readToken (token);
2559 				while (! (isType (token, TOKEN_QUESTION_MARK) || isEOF (token)))
2560 				{
2561 					readToken (token);
2562 				}
2563 				readToken (token);
2564 			}
2565 			else if (isKeyword (token, KEYWORD_NONE))
2566 			{
2567 				/*
2568 				 * This is a simple XML tag, read until the closing statement
2569 				 * <something .... >
2570 				 * </something>
2571 				 */
2572 				readToken (token);
2573 				while (! (isType (token, TOKEN_GREATER_THAN) || isEOF (token)))
2574 				{
2575 					readToken (token);
2576 				}
2577 			}
2578 		}
2579 		else
2580 		{
2581 			parseActionScript (token, false);
2582 		}
2583 	} while (!isEOF (token));
2584 }
2585 
initialize(const langType language)2586 static void initialize (const langType language)
2587 {
2588 	Assert (ARRAY_SIZE (FlexKinds) == FLEXTAG_COUNT);
2589 	Lang_flex = language;
2590 }
2591 
findFlexTags(void)2592 static void findFlexTags (void)
2593 {
2594 	tokenInfo *const token = newToken ();
2595 
2596 	NextToken = NULL;
2597 	ClassNames = stringListNew ();
2598 	FunctionNames = stringListNew ();
2599 
2600 	parseFlexFile (token);
2601 
2602 	stringListDelete (ClassNames);
2603 	stringListDelete (FunctionNames);
2604 	ClassNames = NULL;
2605 	FunctionNames = NULL;
2606 	deleteToken (token);
2607 }
2608 
2609 /* Create parser definition structure */
FlexParser(void)2610 extern parserDefinition* FlexParser (void)
2611 {
2612 	static const char *const extensions [] = { "as", "mxml", NULL };
2613 	parserDefinition *const def = parserNew ("Flex");
2614 	def->extensions = extensions;
2615 	/*
2616 	 * New definitions for parsing instead of regex
2617 	 */
2618 	def->kindTable	= FlexKinds;
2619 	def->kindCount	= ARRAY_SIZE (FlexKinds);
2620 	def->parser		= findFlexTags;
2621 	def->initialize = initialize;
2622 	def->keywordTable = FlexKeywordTable;
2623 	def->keywordCount = ARRAY_SIZE (FlexKeywordTable);
2624 
2625 	return def;
2626 }
2627