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