Merge pull request #11 from esorton/bugfix/add-constexpr-keyword-to-arduino-ctags
[arduino-ctags.git] / flex.c
blob038ac4eb1861596aab1cf405bd1fb160163976a4
1 /*
2 * $Id: flex.c 666 2008-05-15 17:47:31Z dfishburn $
4 * Copyright (c) 2008, David Fishburn
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for Adobe languages.
10 * There are a number of different ones, but this will begin with:
11 * Flex
12 * MXML files (*.mMacromedia XML)
13 * ActionScript files (*.as)
15 * Flex 3 language reference
16 * http://livedocs.adobe.com/flex/3/langref/index.html
20 * INCLUDE FILES
22 #include "general.h" /* must always come first */
23 #include <ctype.h> /* to define isalpha () */
24 #include <setjmp.h>
25 #ifdef DEBUG
26 #include <stdio.h>
27 #endif
29 #include "debug.h"
30 #include "entry.h"
31 #include "keyword.h"
32 #include "parse.h"
33 #include "read.h"
34 #include "routines.h"
35 #include "vstring.h"
38 * MACROS
40 #define isType(token,t) (boolean) ((token)->type == (t))
41 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
44 * DATA DECLARATIONS
47 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
50 * Tracks class and function names already created
52 static stringList *ClassNames;
53 static stringList *FunctionNames;
55 /* Used to specify type of keyword.
57 typedef enum eKeywordId {
58 KEYWORD_NONE = -1,
59 KEYWORD_function,
60 KEYWORD_capital_function,
61 KEYWORD_object,
62 KEYWORD_capital_object,
63 KEYWORD_prototype,
64 KEYWORD_var,
65 KEYWORD_new,
66 KEYWORD_this,
67 KEYWORD_for,
68 KEYWORD_while,
69 KEYWORD_do,
70 KEYWORD_if,
71 KEYWORD_else,
72 KEYWORD_switch,
73 KEYWORD_try,
74 KEYWORD_catch,
75 KEYWORD_finally,
76 KEYWORD_public,
77 KEYWORD_private,
78 KEYWORD_static,
79 KEYWORD_class,
80 KEYWORD_id,
81 KEYWORD_script,
82 KEYWORD_cdata,
83 KEYWORD_mx,
84 KEYWORD_override
85 } keywordId;
87 /* Used to determine whether keyword is valid for the token language and
88 * what its ID is.
90 typedef struct sKeywordDesc {
91 const char *name;
92 keywordId id;
93 } keywordDesc;
95 typedef enum eTokenType {
96 TOKEN_UNDEFINED,
97 TOKEN_CHARACTER,
98 TOKEN_CLOSE_PAREN,
99 TOKEN_SEMICOLON,
100 TOKEN_COLON,
101 TOKEN_COMMA,
102 TOKEN_KEYWORD,
103 TOKEN_OPEN_PAREN,
104 TOKEN_OPERATOR,
105 TOKEN_IDENTIFIER,
106 TOKEN_STRING,
107 TOKEN_PERIOD,
108 TOKEN_OPEN_CURLY,
109 TOKEN_CLOSE_CURLY,
110 TOKEN_EQUAL_SIGN,
111 TOKEN_EXCLAMATION,
112 TOKEN_FORWARD_SLASH,
113 TOKEN_OPEN_SQUARE,
114 TOKEN_CLOSE_SQUARE,
115 TOKEN_OPEN_MXML,
116 TOKEN_CLOSE_MXML,
117 TOKEN_CLOSE_SGML,
118 TOKEN_LESS_THAN,
119 TOKEN_GREATER_THAN,
120 TOKEN_QUESTION_MARK
121 } tokenType;
123 typedef struct sTokenInfo {
124 tokenType type;
125 keywordId keyword;
126 vString * string;
127 vString * scope;
128 unsigned long lineNumber;
129 fpos_t filePosition;
130 int nestLevel;
131 boolean ignoreTag;
132 boolean isClass;
133 } tokenInfo;
136 * DATA DEFINITIONS
139 static langType Lang_js;
141 static jmp_buf Exception;
143 typedef enum {
144 FLEXTAG_FUNCTION,
145 FLEXTAG_CLASS,
146 FLEXTAG_METHOD,
147 FLEXTAG_PROPERTY,
148 FLEXTAG_VARIABLE,
149 FLEXTAG_MXTAG,
150 FLEXTAG_COUNT
151 } flexKind;
153 static kindOption FlexKinds [] = {
154 { TRUE, 'f', "function", "functions" },
155 { TRUE, 'c', "class", "classes" },
156 { TRUE, 'm', "method", "methods" },
157 { TRUE, 'p', "property", "properties" },
158 { TRUE, 'v', "variable", "global variables" },
159 { TRUE, 'x', "mxtag", "mxtags" }
162 static const keywordDesc FlexKeywordTable [] = {
163 /* keyword keyword ID */
164 { "function", KEYWORD_function },
165 { "Function", KEYWORD_capital_function },
166 { "object", KEYWORD_object },
167 { "Object", KEYWORD_capital_object },
168 { "prototype", KEYWORD_prototype },
169 { "var", KEYWORD_var },
170 { "new", KEYWORD_new },
171 { "this", KEYWORD_this },
172 { "for", KEYWORD_for },
173 { "while", KEYWORD_while },
174 { "do", KEYWORD_do },
175 { "if", KEYWORD_if },
176 { "else", KEYWORD_else },
177 { "switch", KEYWORD_switch },
178 { "try", KEYWORD_try },
179 { "catch", KEYWORD_catch },
180 { "finally", KEYWORD_finally },
181 { "public", KEYWORD_public },
182 { "private", KEYWORD_private },
183 { "static", KEYWORD_static },
184 { "class", KEYWORD_class },
185 { "id", KEYWORD_id },
186 { "script", KEYWORD_script },
187 { "cdata", KEYWORD_cdata },
188 { "mx", KEYWORD_mx },
189 { "override", KEYWORD_override }
193 * FUNCTION DEFINITIONS
196 /* Recursive functions */
197 static void parseFunction (tokenInfo *const token);
198 static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);
199 static boolean parseLine (tokenInfo *const token);
200 static boolean parseActionScript (tokenInfo *const token);
202 static boolean isIdentChar (const int c)
204 return (boolean)
205 (isalpha (c) || isdigit (c) || c == '$' ||
206 c == '@' || c == '_' || c == '#');
209 static void buildFlexKeywordHash (void)
211 const size_t count = sizeof (FlexKeywordTable) /
212 sizeof (FlexKeywordTable [0]);
213 size_t i;
214 for (i = 0 ; i < count ; ++i)
216 const keywordDesc* const p = &FlexKeywordTable [i];
217 addKeyword (p->name, Lang_js, (int) p->id);
221 static tokenInfo *newToken (void)
223 tokenInfo *const token = xMalloc (1, tokenInfo);
225 token->type = TOKEN_UNDEFINED;
226 token->keyword = KEYWORD_NONE;
227 token->string = vStringNew ();
228 token->scope = vStringNew ();
229 token->nestLevel = 0;
230 token->isClass = FALSE;
231 token->ignoreTag = FALSE;
232 token->lineNumber = getSourceLineNumber ();
233 token->filePosition = getInputFilePosition ();
235 return token;
238 static void deleteToken (tokenInfo *const token)
240 vStringDelete (token->string);
241 vStringDelete (token->scope);
242 eFree (token);
246 * Tag generation functions
249 static void makeConstTag (tokenInfo *const token, const flexKind kind)
251 if (FlexKinds [kind].enabled && ! token->ignoreTag )
253 const char *const name = vStringValue (token->string);
254 tagEntryInfo e;
255 initTagEntry (&e, name);
257 e.lineNumber = token->lineNumber;
258 e.filePosition = token->filePosition;
259 e.kindName = FlexKinds [kind].name;
260 e.kind = FlexKinds [kind].letter;
262 makeTagEntry (&e);
266 static void makeFlexTag (tokenInfo *const token, flexKind kind)
268 vString * fulltag;
270 if (FlexKinds [kind].enabled && ! token->ignoreTag )
272 DebugStatement (
273 debugPrintf (DEBUG_PARSE
274 , "\n makeFlexTag start: token isClass:%d scope:%s name:%s\n"
275 , token->isClass
276 , vStringValue(token->scope)
277 , vStringValue(token->string)
280 if (kind == FLEXTAG_FUNCTION && token->isClass )
282 kind = FLEXTAG_METHOD;
285 * If a scope has been added to the token, change the token
286 * string to include the scope when making the tag.
288 if ( vStringLength(token->scope) > 0 )
290 fulltag = vStringNew ();
291 vStringCopy(fulltag, token->scope);
292 vStringCatS (fulltag, ".");
293 vStringCatS (fulltag, vStringValue(token->string));
294 vStringTerminate(fulltag);
295 vStringCopy(token->string, fulltag);
296 vStringDelete (fulltag);
298 makeConstTag (token, kind);
302 static void makeClassTag (tokenInfo *const token)
304 vString * fulltag;
306 if ( ! token->ignoreTag )
308 fulltag = vStringNew ();
309 if (vStringLength (token->scope) > 0)
311 vStringCopy(fulltag, token->scope);
312 vStringCatS (fulltag, ".");
313 vStringCatS (fulltag, vStringValue(token->string));
315 else
317 vStringCopy(fulltag, token->string);
319 vStringTerminate(fulltag);
320 if ( ! stringListHas(ClassNames, vStringValue (fulltag)) )
322 stringListAdd (ClassNames, vStringNewCopy (fulltag));
323 makeFlexTag (token, FLEXTAG_CLASS);
325 vStringDelete (fulltag);
329 static void makeMXTag (tokenInfo *const token)
331 vString * fulltag;
333 if ( ! token->ignoreTag )
335 fulltag = vStringNew ();
336 if (vStringLength (token->scope) > 0)
338 vStringCopy(fulltag, token->scope);
339 vStringCatS (fulltag, ".");
340 vStringCatS (fulltag, vStringValue(token->string));
342 else
344 vStringCopy(fulltag, token->string);
346 vStringTerminate(fulltag);
347 makeFlexTag (token, FLEXTAG_MXTAG);
348 vStringDelete (fulltag);
352 static void makeFunctionTag (tokenInfo *const token)
354 vString * fulltag;
356 if ( ! token->ignoreTag )
358 fulltag = vStringNew ();
359 if (vStringLength (token->scope) > 0)
361 vStringCopy(fulltag, token->scope);
362 vStringCatS (fulltag, ".");
363 vStringCatS (fulltag, vStringValue(token->string));
365 else
367 vStringCopy(fulltag, token->string);
369 vStringTerminate(fulltag);
370 if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) )
372 stringListAdd (FunctionNames, vStringNewCopy (fulltag));
373 makeFlexTag (token, FLEXTAG_FUNCTION);
375 vStringDelete (fulltag);
380 * Parsing functions
383 static void parseString (vString *const string, const int delimiter)
385 boolean end = FALSE;
386 while (! end)
388 int c = fileGetc ();
389 if (c == EOF)
390 end = TRUE;
391 else if (c == '\\')
393 c = fileGetc(); /* This maybe a ' or ". */
394 vStringPut(string, c);
396 else if (c == delimiter)
397 end = TRUE;
398 else
399 vStringPut (string, c);
401 vStringTerminate (string);
404 /* Read a C identifier beginning with "firstChar" and places it into
405 * "name".
407 static void parseIdentifier (vString *const string, const int firstChar)
409 int c = firstChar;
410 Assert (isIdentChar (c));
413 vStringPut (string, c);
414 c = fileGetc ();
415 } while (isIdentChar (c));
416 vStringTerminate (string);
417 if (!isspace (c))
418 fileUngetc (c); /* unget non-identifier character */
421 static void readToken (tokenInfo *const token)
423 int c;
425 token->type = TOKEN_UNDEFINED;
426 token->keyword = KEYWORD_NONE;
427 vStringClear (token->string);
429 getNextChar:
432 c = fileGetc ();
433 token->lineNumber = getSourceLineNumber ();
434 token->filePosition = getInputFilePosition ();
436 while (c == '\t' || c == ' ' || c == '\n');
438 switch (c)
440 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
441 case '(': token->type = TOKEN_OPEN_PAREN; break;
442 case ')': token->type = TOKEN_CLOSE_PAREN; break;
443 case ';': token->type = TOKEN_SEMICOLON; break;
444 case ',': token->type = TOKEN_COMMA; break;
445 case '.': token->type = TOKEN_PERIOD; break;
446 case ':': token->type = TOKEN_COLON; break;
447 case '{': token->type = TOKEN_OPEN_CURLY; break;
448 case '}': token->type = TOKEN_CLOSE_CURLY; break;
449 case '=': token->type = TOKEN_EQUAL_SIGN; break;
450 case '[': token->type = TOKEN_OPEN_SQUARE; break;
451 case ']': token->type = TOKEN_CLOSE_SQUARE; break;
452 case '?': token->type = TOKEN_QUESTION_MARK; break;
454 case '\'':
455 case '"':
456 token->type = TOKEN_STRING;
457 parseString (token->string, c);
458 token->lineNumber = getSourceLineNumber ();
459 token->filePosition = getInputFilePosition ();
460 break;
462 case '\\':
463 c = fileGetc ();
464 if (c != '\\' && c != '"' && !isspace (c))
465 fileUngetc (c);
466 token->type = TOKEN_CHARACTER;
467 token->lineNumber = getSourceLineNumber ();
468 token->filePosition = getInputFilePosition ();
469 break;
471 case '/':
473 int d = fileGetc ();
474 if ( (d != '*') && /* is this the start of a comment? */
475 (d != '/') && /* is a one line comment? */
476 (d != '>') ) /* is this a close XML tag? */
478 fileUngetc (d);
479 token->type = TOKEN_FORWARD_SLASH;
480 token->lineNumber = getSourceLineNumber ();
481 token->filePosition = getInputFilePosition ();
483 else
485 if (d == '*')
489 fileSkipToCharacter ('*');
490 c = fileGetc ();
491 if (c == '/')
492 break;
493 else
494 fileUngetc (c);
495 } while (c != EOF && c != '\0');
496 goto getNextChar;
498 else if (d == '/') /* is this the start of a comment? */
500 fileSkipToCharacter ('\n');
501 goto getNextChar;
503 else if (d == '>') /* is this the start of a comment? */
505 token->type = TOKEN_CLOSE_SGML;
506 token->lineNumber = getSourceLineNumber ();
507 token->filePosition = getInputFilePosition ();
510 break;
513 case '<':
516 * An XML comment looks like this
517 * <!-- anything over multiple lines -->
519 int d = fileGetc ();
521 if ( (d != '!' ) && /* is this the start of a comment? */
522 (d != '/' ) && /* is this the start of a closing mx tag */
523 (d != 'm' ) ) /* is this the start of a mx tag */
525 fileUngetc (d);
526 token->type = TOKEN_LESS_THAN;
527 token->lineNumber = getSourceLineNumber ();
528 token->filePosition = getInputFilePosition ();
531 else
533 if (d == '!')
535 int e = fileGetc ();
536 if ( e != '-' ) /* is this the start of a comment? */
538 fileUngetc (e);
539 fileUngetc (d);
540 token->type = TOKEN_LESS_THAN;
541 token->lineNumber = getSourceLineNumber ();
542 token->filePosition = getInputFilePosition ();
544 else
546 if (e == '-')
548 int f = fileGetc ();
549 if ( f != '-' ) /* is this the start of a comment? */
551 fileUngetc (f);
552 fileUngetc (e);
553 fileUngetc (d);
554 token->type = TOKEN_LESS_THAN;
555 token->lineNumber = getSourceLineNumber ();
556 token->filePosition = getInputFilePosition ();
558 else
560 if (f == '-')
564 fileSkipToCharacter ('-');
565 c = fileGetc ();
566 if (c == '-')
568 d = fileGetc ();
569 if (d == '>')
570 break;
571 else
573 fileUngetc (d);
574 fileUngetc (c);
576 break;
578 else
579 fileUngetc (c);
580 } while (c != EOF && c != '\0');
581 goto getNextChar;
587 else if (d == 'm')
589 int e = fileGetc ();
590 if ( e != 'x' ) /* continuing an mx tag */
592 fileUngetc (e);
593 fileUngetc (d);
594 token->type = TOKEN_LESS_THAN;
595 token->lineNumber = getSourceLineNumber ();
596 token->filePosition = getInputFilePosition ();
598 else
600 if (e == 'x')
602 int f = fileGetc ();
603 if ( f != ':' ) /* is this the start of a comment? */
605 fileUngetc (f);
606 fileUngetc (e);
607 fileUngetc (d);
608 token->type = TOKEN_LESS_THAN;
609 token->lineNumber = getSourceLineNumber ();
610 token->filePosition = getInputFilePosition ();
612 else
614 if (f == ':')
616 token->type = TOKEN_OPEN_MXML;
617 token->lineNumber = getSourceLineNumber ();
618 token->filePosition = getInputFilePosition ();
624 else if (d == '/')
626 int e = fileGetc ();
627 if ( e != 'm' ) /* continuing an mx tag */
629 fileUngetc (e);
630 fileUngetc (d);
631 token->type = TOKEN_LESS_THAN;
632 token->lineNumber = getSourceLineNumber ();
633 token->filePosition = getInputFilePosition ();
635 else
637 int f = fileGetc ();
638 if ( f != 'x' ) /* continuing an mx tag */
640 fileUngetc (f);
641 fileUngetc (e);
642 token->type = TOKEN_LESS_THAN;
643 token->lineNumber = getSourceLineNumber ();
644 token->filePosition = getInputFilePosition ();
646 else
648 if (f == 'x')
650 int g = fileGetc ();
651 if ( g != ':' ) /* is this the start of a comment? */
653 fileUngetc (g);
654 fileUngetc (f);
655 fileUngetc (e);
656 token->type = TOKEN_LESS_THAN;
657 token->lineNumber = getSourceLineNumber ();
658 token->filePosition = getInputFilePosition ();
660 else
662 if (g == ':')
664 token->type = TOKEN_CLOSE_MXML;
665 token->lineNumber = getSourceLineNumber ();
666 token->filePosition = getInputFilePosition ();
674 break;
677 case '>':
678 token->type = TOKEN_GREATER_THAN;
679 token->lineNumber = getSourceLineNumber ();
680 token->filePosition = getInputFilePosition ();
681 break;
683 case '!':
684 token->type = TOKEN_EXCLAMATION;
685 /*token->lineNumber = getSourceLineNumber ();
686 token->filePosition = getInputFilePosition ();*/
687 break;
689 default:
690 if (! isIdentChar (c))
691 token->type = TOKEN_UNDEFINED;
692 else
694 parseIdentifier (token->string, c);
695 token->lineNumber = getSourceLineNumber ();
696 token->filePosition = getInputFilePosition ();
697 token->keyword = analyzeToken (token->string, Lang_js);
698 if (isKeyword (token, KEYWORD_NONE))
699 token->type = TOKEN_IDENTIFIER;
700 else
701 token->type = TOKEN_KEYWORD;
703 break;
707 static void copyToken (tokenInfo *const dest, tokenInfo *const src)
709 dest->nestLevel = src->nestLevel;
710 dest->lineNumber = src->lineNumber;
711 dest->filePosition = src->filePosition;
712 dest->type = src->type;
713 dest->keyword = src->keyword;
714 dest->isClass = src->isClass;
715 vStringCopy(dest->string, src->string);
716 vStringCopy(dest->scope, src->scope);
720 * Token parsing functions
723 static void skipArgumentList (tokenInfo *const token)
725 int nest_level = 0;
728 * Other databases can have arguments with fully declared
729 * datatypes:
730 * ( name varchar(30), text binary(10) )
731 * So we must check for nested open and closing parentheses
734 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
736 nest_level++;
737 while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
739 readToken (token);
740 if (isType (token, TOKEN_OPEN_PAREN))
742 nest_level++;
744 if (isType (token, TOKEN_CLOSE_PAREN))
746 if (nest_level > 0)
748 nest_level--;
752 readToken (token);
756 static void skipArrayList (tokenInfo *const token)
758 int nest_level = 0;
761 * Handle square brackets
762 * var name[1]
763 * So we must check for nested open and closing square brackets
766 if (isType (token, TOKEN_OPEN_SQUARE)) /* arguments? */
768 nest_level++;
769 while (! (isType (token, TOKEN_CLOSE_SQUARE) && (nest_level == 0)))
771 readToken (token);
772 if (isType (token, TOKEN_OPEN_SQUARE))
774 nest_level++;
776 if (isType (token, TOKEN_CLOSE_SQUARE))
778 if (nest_level > 0)
780 nest_level--;
784 readToken (token);
788 static void addContext (tokenInfo* const parent, const tokenInfo* const child)
790 if (vStringLength (parent->string) > 0)
792 vStringCatS (parent->string, ".");
794 vStringCatS (parent->string, vStringValue(child->string));
795 vStringTerminate(parent->string);
798 static void addToScope (tokenInfo* const token, vString* const extra)
800 if (vStringLength (token->scope) > 0)
802 vStringCatS (token->scope, ".");
804 vStringCatS (token->scope, vStringValue(extra));
805 vStringTerminate(token->scope);
809 * Scanning functions
812 static void findCmdTerm (tokenInfo *const token)
815 * Read until we find either a semicolon or closing brace.
816 * Any nested braces will be handled within.
818 while (! ( isType (token, TOKEN_SEMICOLON) ||
819 isType (token, TOKEN_CLOSE_CURLY) ) )
821 /* Handle nested blocks */
822 if ( isType (token, TOKEN_OPEN_CURLY))
824 parseBlock (token, token);
826 else if ( isType (token, TOKEN_OPEN_PAREN) )
828 skipArgumentList(token);
830 else
832 readToken (token);
837 static void parseSwitch (tokenInfo *const token)
840 * switch (expression){
841 * case value1:
842 * statement;
843 * break;
844 * case value2:
845 * statement;
846 * break;
847 * default : statement;
851 readToken (token);
853 if (isType (token, TOKEN_OPEN_PAREN))
855 skipArgumentList(token);
858 if (isType (token, TOKEN_OPEN_CURLY))
862 readToken (token);
863 } while (! (isType (token, TOKEN_CLOSE_SGML) ||
864 isType (token, TOKEN_CLOSE_MXML) ||
865 isType (token, TOKEN_CLOSE_CURLY) ||
866 isType (token, TOKEN_GREATER_THAN)) );
871 static void parseLoop (tokenInfo *const token)
874 * Handles these statements
875 * for (x=0; x<3; x++)
876 * document.write("This text is repeated three times<br>");
878 * for (x=0; x<3; x++)
880 * document.write("This text is repeated three times<br>");
883 * while (number<5){
884 * document.write(number+"<br>");
885 * number++;
888 * do{
889 * document.write(number+"<br>");
890 * number++;
892 * while (number<5);
895 if (isKeyword (token, KEYWORD_for) || isKeyword (token, KEYWORD_while))
897 readToken(token);
899 if (isType (token, TOKEN_OPEN_PAREN))
902 * Handle nameless functions, these will only
903 * be considered methods.
905 skipArgumentList(token);
908 if (isType (token, TOKEN_OPEN_CURLY))
911 * This will be either a function or a class.
912 * We can only determine this by checking the body
913 * of the function. If we find a "this." we know
914 * it is a class, otherwise it is a function.
916 parseBlock (token, token);
918 else
920 parseLine(token);
923 else if (isKeyword (token, KEYWORD_do))
925 readToken(token);
927 if (isType (token, TOKEN_OPEN_CURLY))
930 * This will be either a function or a class.
931 * We can only determine this by checking the body
932 * of the function. If we find a "this." we know
933 * it is a class, otherwise it is a function.
935 parseBlock (token, token);
937 else
939 parseLine(token);
942 readToken(token);
944 if (isKeyword (token, KEYWORD_while))
946 readToken(token);
948 if (isType (token, TOKEN_OPEN_PAREN))
951 * Handle nameless functions, these will only
952 * be considered methods.
954 skipArgumentList(token);
960 static boolean parseIf (tokenInfo *const token)
962 boolean read_next_token = TRUE;
964 * If statements have two forms
965 * if ( ... )
966 * one line;
968 * if ( ... )
969 * statement;
970 * else
971 * statement
973 * if ( ... ) {
974 * multiple;
975 * statements;
979 * if ( ... ) {
980 * return elem
983 * This example if correctly written, but the
984 * else contains only 1 statement without a terminator
985 * since the function finishes with the closing brace.
987 * function a(flag){
988 * if(flag)
989 * test(1);
990 * else
991 * test(2)
994 * TODO: Deal with statements that can optional end
995 * without a semi-colon. Currently this messes up
996 * the parsing of blocks.
997 * Need to somehow detect this has happened, and either
998 * backup a token, or skip reading the next token if
999 * that is possible from all code locations.
1003 readToken (token);
1005 if (isKeyword (token, KEYWORD_if))
1008 * Check for an "else if" and consume the "if"
1010 readToken (token);
1013 if (isType (token, TOKEN_OPEN_PAREN))
1016 * Handle nameless functions, these will only
1017 * be considered methods.
1019 skipArgumentList(token);
1022 if (isType (token, TOKEN_OPEN_CURLY))
1025 * This will be either a function or a class.
1026 * We can only determine this by checking the body
1027 * of the function. If we find a "this." we know
1028 * it is a class, otherwise it is a function.
1030 parseBlock (token, token);
1032 else
1034 findCmdTerm (token);
1037 * The IF could be followed by an ELSE statement.
1038 * This too could have two formats, a curly braced
1039 * multiline section, or another single line.
1042 if (isType (token, TOKEN_CLOSE_CURLY))
1045 * This statement did not have a line terminator.
1047 read_next_token = FALSE;
1049 else
1051 readToken (token);
1053 if (isType (token, TOKEN_CLOSE_CURLY))
1056 * This statement did not have a line terminator.
1058 read_next_token = FALSE;
1060 else
1062 if (isKeyword (token, KEYWORD_else))
1063 read_next_token = parseIf (token);
1067 return read_next_token;
1070 static void parseFunction (tokenInfo *const token)
1072 tokenInfo *const name = newToken ();
1075 * This deals with these formats
1076 * private static function ioErrorHandler( event:IOErrorEvent ):void {
1079 if ( isKeyword(token, KEYWORD_function) )
1081 readToken (token);
1084 copyToken (name, token);
1085 /* Add scope in case this is an INNER function
1086 addToScope(name, token->scope);
1089 DebugStatement (
1090 debugPrintf (DEBUG_PARSE
1091 , "\n parseFunction: token isClass:%d scope:%s name:%s\n"
1092 , token->isClass
1093 , vStringValue(token->scope)
1094 , vStringValue(token->string)
1097 DebugStatement (
1098 debugPrintf (DEBUG_PARSE
1099 , "\n parseFunction: name isClass:%d scope:%s name:%s\n"
1100 , name->isClass
1101 , vStringValue(name->scope)
1102 , vStringValue(name->string)
1106 readToken (token);
1108 if ( isType (token, TOKEN_OPEN_PAREN) )
1109 skipArgumentList(token);
1111 if ( isType (token, TOKEN_COLON) )
1114 * function fname ():ReturnType
1116 readToken (token);
1117 readToken (token);
1120 if ( isType (token, TOKEN_OPEN_CURLY) )
1122 DebugStatement (
1123 debugPrintf (DEBUG_PARSE
1124 , "\n parseFunction end: name isClass:%d scope:%s name:%s\n"
1125 , name->isClass
1126 , vStringValue(name->scope)
1127 , vStringValue(name->string)
1130 parseBlock (token, name);
1131 DebugStatement (
1132 debugPrintf (DEBUG_PARSE
1133 , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
1134 , token->isClass
1135 , vStringValue(token->scope)
1136 , vStringValue(token->string)
1139 DebugStatement (
1140 debugPrintf (DEBUG_PARSE
1141 , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
1142 , token->isClass
1143 , vStringValue(token->scope)
1144 , vStringValue(token->string)
1147 DebugStatement (
1148 debugPrintf (DEBUG_PARSE
1149 , "\n parseFunction end3: name isClass:%d scope:%s name:%s\n"
1150 , name->isClass
1151 , vStringValue(name->scope)
1152 , vStringValue(name->string)
1155 makeFunctionTag (name);
1158 findCmdTerm (token);
1160 deleteToken (name);
1163 static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
1165 boolean read_next_token = TRUE;
1166 vString * saveScope = vStringNew ();
1168 vStringClear(saveScope);
1169 vStringCopy (saveScope, token->scope);
1170 token->nestLevel++;
1171 DebugStatement (
1172 debugPrintf (DEBUG_PARSE
1173 , "\n parseBlock start: token isClass:%d scope:%s name:%s\n"
1174 , token->isClass
1175 , vStringValue(token->scope)
1176 , vStringValue(token->string)
1180 * Make this routine a bit more forgiving.
1181 * If called on an open_curly advance it
1183 if ( isType (token, TOKEN_OPEN_CURLY) &&
1184 isKeyword(token, KEYWORD_NONE) )
1185 readToken(token);
1187 if (! isType (token, TOKEN_CLOSE_CURLY))
1190 * Read until we find the closing brace,
1191 * any nested braces will be handled within
1195 if (isType (token, TOKEN_OPEN_CURLY))
1197 /* Handle nested blocks */
1198 parseBlock (token, parent);
1200 else
1203 * It is possible for a line to have no terminator
1204 * if the following line is a closing brace.
1205 * parseLine will detect this case and indicate
1206 * whether we should read an additional token.
1208 read_next_token = parseLine (token);
1212 * Always read a new token unless we find a statement without
1213 * a ending terminator
1215 if( read_next_token )
1216 readToken(token);
1219 * If we find a statement without a terminator consider the
1220 * block finished, otherwise the stack will be off by one.
1222 } while (! isType (token, TOKEN_CLOSE_CURLY) && read_next_token );
1225 vStringDelete(saveScope);
1226 token->nestLevel--;
1228 DebugStatement (
1229 debugPrintf (DEBUG_PARSE
1230 , "\n parseBlock end: token isClass:%d scope:%s name:%s\n"
1231 , token->isClass
1232 , vStringValue(token->scope)
1233 , vStringValue(token->string)
1236 return FALSE;
1239 static void parseMethods (tokenInfo *const token, tokenInfo *const class)
1241 tokenInfo *const name = newToken ();
1244 * This deals with these formats
1245 * validProperty : 2,
1246 * validMethod : function(a,b) {}
1247 * 'validMethod2' : function(a,b) {}
1248 * container.dirtyTab = {'url': false, 'title':false, 'snapshot':false, '*': false}
1253 readToken (token);
1254 if (isType (token, TOKEN_STRING) || isKeyword(token, KEYWORD_NONE))
1256 copyToken (name, token);
1258 readToken (token);
1259 if ( isType (token, TOKEN_COLON) )
1261 readToken (token);
1262 if ( isKeyword (token, KEYWORD_function) )
1264 readToken (token);
1265 if ( isType (token, TOKEN_OPEN_PAREN) )
1267 skipArgumentList(token);
1270 if (isType (token, TOKEN_OPEN_CURLY))
1272 addToScope (name, class->string);
1273 makeFlexTag (name, FLEXTAG_METHOD);
1274 parseBlock (token, name);
1277 * Read to the closing curly, check next
1278 * token, if a comma, we must loop again
1280 readToken (token);
1283 else
1285 addToScope (name, class->string);
1286 makeFlexTag (name, FLEXTAG_PROPERTY);
1289 * Read the next token, if a comma
1290 * we must loop again
1292 readToken (token);
1296 } while ( isType(token, TOKEN_COMMA) );
1298 findCmdTerm (token);
1300 deleteToken (name);
1303 static boolean parseVar (tokenInfo *const token, boolean is_public)
1305 tokenInfo *const name = newToken ();
1306 tokenInfo *const secondary_name = newToken ();
1307 vString * saveScope = vStringNew ();
1308 boolean is_terminated = TRUE;
1310 vStringClear(saveScope);
1311 vStringCopy (saveScope, token->scope);
1313 * Variables are defined as:
1314 * private static var lastFaultMessage:Date = new Date( 0 );
1315 * private static var webRequests:ArrayCollection = new ArrayCollection();
1318 if ( isKeyword(token, KEYWORD_var) )
1320 readToken(token);
1323 /* Variable name */
1324 copyToken (name, token);
1325 readToken(token);
1327 if ( isType (token, TOKEN_COLON) )
1330 * var vname ():DataType = new Date();
1331 * var vname ():DataType;
1333 readToken (token);
1334 readToken (token);
1337 while (! isType (token, TOKEN_SEMICOLON) )
1339 readToken (token);
1342 if ( isType (token, TOKEN_SEMICOLON) )
1345 * Only create variables for global scope
1347 /* if ( token->nestLevel == 0 && is_global ) */
1348 if ( is_public )
1350 if (isType (token, TOKEN_SEMICOLON))
1351 makeFlexTag (name, FLEXTAG_VARIABLE);
1355 vStringCopy(token->scope, saveScope);
1356 deleteToken (name);
1357 deleteToken (secondary_name);
1358 vStringDelete(saveScope);
1360 return is_terminated;
1363 static boolean parseClass (tokenInfo *const token)
1365 tokenInfo *const name = newToken ();
1366 vString * saveScope = vStringNew ();
1367 boolean saveIsClass = token->isClass;
1369 vStringClear(saveScope);
1370 vStringCopy (saveScope, token->scope);
1372 * Variables are defined as:
1373 * private static var lastFaultMessage:Date = new Date( 0 );
1374 * private static var webRequests:ArrayCollection = new ArrayCollection();
1377 if ( isKeyword(token, KEYWORD_class) )
1379 readToken(token);
1382 token->isClass = TRUE;
1383 /* Add class name to scope */
1384 addToScope(token, token->string);
1385 /* Class name */
1386 copyToken (name, token);
1387 readToken(token);
1389 DebugStatement (
1390 debugPrintf (DEBUG_PARSE
1391 , "\n parseClass start: token isClass:%d scope:%s name:%s\n"
1392 , token->isClass
1393 , vStringValue(token->scope)
1394 , vStringValue(token->string)
1397 if ( isType (token, TOKEN_OPEN_CURLY) )
1399 makeClassTag (name);
1400 parseBlock (token, name);
1403 DebugStatement (
1404 debugPrintf (DEBUG_PARSE
1405 , "\n parseClass end: token isClass:%d scope:%s name:%s\n"
1406 , token->isClass
1407 , vStringValue(token->scope)
1408 , vStringValue(token->string)
1411 vStringCopy(token->scope, saveScope);
1412 token->isClass = saveIsClass;
1413 deleteToken (name);
1414 vStringDelete(saveScope);
1416 return TRUE;
1419 static boolean parseStatement (tokenInfo *const token)
1421 tokenInfo *const name = newToken ();
1422 tokenInfo *const secondary_name = newToken ();
1423 vString * saveScope = vStringNew ();
1424 boolean is_public = FALSE;
1425 boolean is_class = FALSE;
1426 boolean is_terminated = TRUE;
1427 boolean is_global = FALSE;
1428 boolean is_prototype = FALSE;
1429 vString * fulltag;
1431 vStringClear(saveScope);
1432 vStringCopy (saveScope, token->scope);
1433 DebugStatement (
1434 debugPrintf (DEBUG_PARSE
1435 , "\n parseStatement: token isClass:%d scope:%s name:%s\n"
1436 , token->isClass
1437 , vStringValue(token->scope)
1438 , vStringValue(token->string)
1442 * Functions can be named or unnamed.
1443 * This deals with these formats:
1444 * Function
1445 * validFunctionOne = function(a,b) {}
1446 * testlib.validFunctionFive = function(a,b) {}
1447 * var innerThree = function(a,b) {}
1448 * var innerFour = (a,b) {}
1449 * var D2 = secondary_fcn_name(a,b) {}
1450 * var D3 = new Function("a", "b", "return a+b;");
1451 * Class
1452 * testlib.extras.ValidClassOne = function(a,b) {
1453 * this.a = a;
1455 * Class Methods
1456 * testlib.extras.ValidClassOne.prototype = {
1457 * 'validMethodOne' : function(a,b) {},
1458 * 'validMethodTwo' : function(a,b) {}
1460 * ValidClassTwo = function ()
1462 * this.validMethodThree = function() {}
1463 * // unnamed method
1464 * this.validMethodFour = () {}
1466 * Database.prototype.validMethodThree = Database_getTodaysDate;
1469 if ( isKeyword(token, KEYWORD_public) )
1471 is_public = TRUE;
1472 readToken(token);
1475 if ( isKeyword(token, KEYWORD_private) )
1477 readToken(token);
1480 if ( isKeyword(token, KEYWORD_static) )
1482 readToken(token);
1485 if (isType(token, TOKEN_KEYWORD))
1487 switch (token->keyword)
1489 case KEYWORD_for:
1490 case KEYWORD_while:
1491 case KEYWORD_do:
1492 parseLoop (token);
1493 break;
1494 case KEYWORD_if:
1495 case KEYWORD_else:
1496 case KEYWORD_try:
1497 case KEYWORD_catch:
1498 case KEYWORD_finally:
1499 /* Common semantics */
1500 is_terminated = parseIf (token);
1501 break;
1502 case KEYWORD_switch:
1503 parseSwitch (token);
1504 break;
1505 case KEYWORD_class:
1506 parseClass (token);
1507 return is_terminated;
1508 break;
1509 case KEYWORD_function:
1510 parseFunction (token);
1511 return is_terminated;
1512 break;
1513 case KEYWORD_var:
1514 parseVar (token, is_public);
1515 return is_terminated;
1516 break;
1517 default:
1518 readToken(token);
1519 break;
1523 copyToken (name, token);
1525 while (! isType (token, TOKEN_CLOSE_CURLY) &&
1526 ! isType (token, TOKEN_SEMICOLON) &&
1527 ! isType (token, TOKEN_EQUAL_SIGN) )
1529 /* Potentially the name of the function */
1530 readToken (token);
1531 if (isType (token, TOKEN_PERIOD))
1534 * Cannot be a global variable is it has dot references in the name
1536 is_global = FALSE;
1539 readToken (token);
1540 if ( isKeyword(token, KEYWORD_NONE) )
1542 if ( is_class )
1544 vStringCopy(saveScope, token->scope);
1545 addToScope(token, name->string);
1547 else
1548 addContext (name, token);
1550 else if ( isKeyword(token, KEYWORD_prototype) )
1553 * When we reach the "prototype" tag, we infer:
1554 * "BindAgent" is a class
1555 * "build" is a method
1557 * function BindAgent( repeatableIdName, newParentIdName ) {
1558 * }
1560 * CASE 1
1561 * Specified function name: "build"
1562 * BindAgent.prototype.build = function( mode ) {
1563 * ignore everything within this function
1566 * CASE 2
1567 * Prototype listing
1568 * ValidClassOne.prototype = {
1569 * 'validMethodOne' : function(a,b) {},
1570 * 'validMethodTwo' : function(a,b) {}
1574 makeClassTag (name);
1575 is_class = TRUE;
1576 is_prototype = TRUE;
1579 * There should a ".function_name" next.
1581 readToken (token);
1582 if (isType (token, TOKEN_PERIOD))
1585 * Handle CASE 1
1587 readToken (token);
1588 if ( isKeyword(token, KEYWORD_NONE) )
1590 vStringCopy(saveScope, token->scope);
1591 addToScope(token, name->string);
1593 makeFlexTag (token, FLEXTAG_METHOD);
1595 * We can read until the end of the block / statement.
1596 * We need to correctly parse any nested blocks, but
1597 * we do NOT want to create any tags based on what is
1598 * within the blocks.
1600 token->ignoreTag = TRUE;
1602 * Find to the end of the statement
1604 findCmdTerm (token);
1605 token->ignoreTag = FALSE;
1606 is_terminated = TRUE;
1607 goto cleanUp;
1610 else if (isType (token, TOKEN_EQUAL_SIGN))
1612 readToken (token);
1613 if (isType (token, TOKEN_OPEN_CURLY))
1616 * Handle CASE 2
1618 * Creates tags for each of these class methods
1619 * ValidClassOne.prototype = {
1620 * 'validMethodOne' : function(a,b) {},
1621 * 'validMethodTwo' : function(a,b) {}
1624 parseMethods(token, name);
1626 * Find to the end of the statement
1628 findCmdTerm (token);
1629 token->ignoreTag = FALSE;
1630 is_terminated = TRUE;
1631 goto cleanUp;
1635 readToken (token);
1636 } while (isType (token, TOKEN_PERIOD));
1639 if ( isType (token, TOKEN_OPEN_PAREN) )
1640 skipArgumentList(token);
1642 if ( isType (token, TOKEN_COLON) )
1645 * Functions are of this form:
1646 * function fname ():ReturnType {
1648 readToken (token);
1649 readToken (token);
1652 if ( isType (token, TOKEN_OPEN_SQUARE) )
1653 skipArrayList(token);
1657 if ( isType (token, TOKEN_CLOSE_CURLY) )
1660 * Reaching this section without having
1661 * processed an open curly brace indicates
1662 * the statement is most likely not terminated.
1664 is_terminated = FALSE;
1665 goto cleanUp;
1668 if ( isType (token, TOKEN_SEMICOLON) )
1671 * Only create variables for global scope
1673 if ( token->nestLevel == 0 && is_global )
1676 * Handles this syntax:
1677 * var g_var2;
1679 if (isType (token, TOKEN_SEMICOLON))
1680 makeFlexTag (name, FLEXTAG_VARIABLE);
1683 * Statement has ended.
1684 * This deals with calls to functions, like:
1685 * alert(..);
1687 goto cleanUp;
1690 if ( isType (token, TOKEN_EQUAL_SIGN) )
1692 readToken (token);
1694 if ( isKeyword (token, KEYWORD_function) )
1696 readToken (token);
1698 if ( isKeyword (token, KEYWORD_NONE) &&
1699 ! isType (token, TOKEN_OPEN_PAREN) )
1702 * Functions of this format:
1703 * var D2A = function theAdd(a, b)
1704 * {
1705 * return a+b;
1706 * }
1707 * Are really two separate defined functions and
1708 * can be referenced in two ways:
1709 * alert( D2A(1,2) ); // produces 3
1710 * alert( theAdd(1,2) ); // also produces 3
1711 * So it must have two tags:
1712 * D2A
1713 * theAdd
1714 * Save the reference to the name for later use, once
1715 * we have established this is a valid function we will
1716 * create the secondary reference to it.
1718 copyToken (secondary_name, token);
1719 readToken (token);
1722 if ( isType (token, TOKEN_OPEN_PAREN) )
1723 skipArgumentList(token);
1725 if (isType (token, TOKEN_OPEN_CURLY))
1728 * This will be either a function or a class.
1729 * We can only determine this by checking the body
1730 * of the function. If we find a "this." we know
1731 * it is a class, otherwise it is a function.
1733 if ( token->isClass )
1735 makeFlexTag (name, FLEXTAG_METHOD);
1736 if ( vStringLength(secondary_name->string) > 0 )
1737 makeFunctionTag (secondary_name);
1738 parseBlock (token, name);
1740 else
1742 parseBlock (token, name);
1743 makeFunctionTag (name);
1745 if ( vStringLength(secondary_name->string) > 0 )
1746 makeFunctionTag (secondary_name);
1749 * Find to the end of the statement
1751 goto cleanUp;
1755 else if (isType (token, TOKEN_OPEN_PAREN))
1758 * Handle nameless functions
1759 * this.method_name = () {}
1761 skipArgumentList(token);
1763 if (isType (token, TOKEN_OPEN_CURLY))
1766 * Nameless functions are only setup as methods.
1768 makeFlexTag (name, FLEXTAG_METHOD);
1769 parseBlock (token, name);
1772 else if (isType (token, TOKEN_OPEN_CURLY))
1775 * Creates tags for each of these class methods
1776 * ValidClassOne.prototype = {
1777 * 'validMethodOne' : function(a,b) {},
1778 * 'validMethodTwo' : function(a,b) {}
1781 parseMethods(token, name);
1782 if (isType (token, TOKEN_CLOSE_CURLY))
1785 * Assume the closing parenthesis terminates
1786 * this statements.
1788 is_terminated = TRUE;
1791 else if (isKeyword (token, KEYWORD_new))
1793 readToken (token);
1794 if ( isKeyword (token, KEYWORD_function) ||
1795 isKeyword (token, KEYWORD_capital_function) ||
1796 isKeyword (token, KEYWORD_object) ||
1797 isKeyword (token, KEYWORD_capital_object) )
1799 if ( isKeyword (token, KEYWORD_object) ||
1800 isKeyword (token, KEYWORD_capital_object) )
1801 is_class = TRUE;
1803 readToken (token);
1804 if ( isType (token, TOKEN_OPEN_PAREN) )
1805 skipArgumentList(token);
1807 if (isType (token, TOKEN_SEMICOLON))
1809 if ( token->nestLevel == 0 )
1811 if ( is_class )
1813 makeClassTag (name);
1814 } else {
1815 makeFunctionTag (name);
1821 else if (isKeyword (token, KEYWORD_NONE))
1824 * Only create variables for global scope
1826 if ( token->nestLevel == 0 && is_global )
1829 * A pointer can be created to the function.
1830 * If we recognize the function/class name ignore the variable.
1831 * This format looks identical to a variable definition.
1832 * A variable defined outside of a block is considered
1833 * a global variable:
1834 * var g_var1 = 1;
1835 * var g_var2;
1836 * This is not a global variable:
1837 * var g_var = function;
1838 * This is a global variable:
1839 * var g_var = different_var_name;
1841 fulltag = vStringNew ();
1842 if (vStringLength (token->scope) > 0)
1844 vStringCopy(fulltag, token->scope);
1845 vStringCatS (fulltag, ".");
1846 vStringCatS (fulltag, vStringValue(token->string));
1848 else
1850 vStringCopy(fulltag, token->string);
1852 vStringTerminate(fulltag);
1853 if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
1854 ! stringListHas(ClassNames, vStringValue (fulltag)) )
1856 findCmdTerm (token);
1857 if (isType (token, TOKEN_SEMICOLON))
1858 makeFlexTag (name, FLEXTAG_VARIABLE);
1860 vStringDelete (fulltag);
1864 findCmdTerm (token);
1867 * Statements can be optionally terminated in the case of
1868 * statement prior to a close curly brace as in the
1869 * document.write line below:
1871 * function checkForUpdate() {
1872 * if( 1==1 ) {
1873 * document.write("hello from checkForUpdate<br>")
1875 * return 1;
1878 if ( ! is_terminated && isType (token, TOKEN_CLOSE_CURLY))
1879 is_terminated = FALSE;
1882 cleanUp:
1883 vStringCopy(token->scope, saveScope);
1884 deleteToken (name);
1885 deleteToken (secondary_name);
1886 vStringDelete(saveScope);
1888 return is_terminated;
1891 static boolean parseLine (tokenInfo *const token)
1893 boolean is_terminated = TRUE;
1895 * Detect the common statements, if, while, for, do, ...
1896 * This is necessary since the last statement within a block "{}"
1897 * can be optionally terminated.
1899 * If the statement is not terminated, we need to tell
1900 * the calling routine to prevent reading an additional token
1901 * looking for the end of the statement.
1904 if (isType(token, TOKEN_KEYWORD))
1906 switch (token->keyword)
1908 case KEYWORD_for:
1909 case KEYWORD_while:
1910 case KEYWORD_do:
1911 parseLoop (token);
1912 break;
1913 case KEYWORD_if:
1914 case KEYWORD_else:
1915 case KEYWORD_try:
1916 case KEYWORD_catch:
1917 case KEYWORD_finally:
1918 /* Common semantics */
1919 is_terminated = parseIf (token);
1920 break;
1921 case KEYWORD_switch:
1922 parseSwitch (token);
1923 break;
1924 default:
1925 parseStatement (token);
1926 break;
1929 else
1932 * Special case where single line statements may not be
1933 * SEMICOLON terminated. parseBlock needs to know this
1934 * so that it does not read the next token.
1936 is_terminated = parseStatement (token);
1938 return is_terminated;
1941 static boolean parseCDATA (tokenInfo *const token)
1943 if (isType (token, TOKEN_LESS_THAN))
1946 * Handle these tags
1947 * <![CDATA[
1948 * ...
1949 * ]]>
1951 readToken (token);
1952 if (isType (token, TOKEN_EXCLAMATION))
1955 * Not sure why I had to comment these out, but I did.
1956 * readToken (token);
1957 * if (isType (token, TOKEN_OPEN_SQUARE))
1960 readToken (token);
1961 if (isKeyword (token, KEYWORD_cdata))
1963 readToken (token);
1964 if (isType (token, TOKEN_OPEN_SQUARE))
1966 parseActionScript (token);
1967 if (isType (token, TOKEN_CLOSE_SQUARE))
1969 readToken (token);
1970 if (isType (token, TOKEN_CLOSE_SQUARE))
1972 readToken (token);
1977 /*} Not sure */
1980 else
1982 parseActionScript (token);
1984 return TRUE;
1987 static boolean parseMXML (tokenInfo *const token)
1989 tokenInfo *const name = newToken ();
1990 tokenInfo *const type = newToken ();
1992 * Detect the common statements, if, while, for, do, ...
1993 * This is necessary since the last statement within a block "{}"
1994 * can be optionally terminated.
1996 * If the statement is not terminated, we need to tell
1997 * the calling routine to prevent reading an additional token
1998 * looking for the end of the statement.
2001 readToken (token);
2003 if (isKeyword (token, KEYWORD_script))
2006 * These tags can be of this form:
2007 * <mx:Script src="filename.as" />
2011 readToken (token);
2012 } while (! (isType (token, TOKEN_CLOSE_SGML) ||
2013 isType (token, TOKEN_CLOSE_MXML) ||
2014 isType (token, TOKEN_GREATER_THAN)) );
2016 if (isType (token, TOKEN_CLOSE_MXML))
2019 * We have found a </mx:type> tag
2020 * Finish reading the "type" and ">"
2022 readToken (token);
2023 readToken (token);
2024 goto cleanUp;
2026 if (isType (token, TOKEN_CLOSE_SGML))
2029 * We have found a <mx:Script src="filename.as" />
2031 goto cleanUp;
2035 * This is a beginning of an embedded script.
2036 * These typically are of this format:
2037 * <mx:Script>
2038 * <![CDATA[
2039 * ... ActionScript ...
2040 * ]]>
2041 * </mx:Script>
2043 readToken (token);
2044 parseCDATA (token);
2046 readToken (token);
2047 if (isType (token, TOKEN_CLOSE_MXML))
2050 * We have found a </mx:type> tag
2051 * Finish reading the "type" and ">"
2053 readToken (token);
2054 readToken (token);
2056 goto cleanUp;
2059 copyToken (type, token);
2061 readToken (token);
2064 if (isType (token, TOKEN_OPEN_MXML))
2066 parseMXML (token);
2068 else if (isKeyword (token, KEYWORD_id))
2070 /* = */
2071 readToken (token);
2072 readToken (token);
2074 copyToken (name, token);
2075 addToScope (name, type->string);
2076 makeMXTag (name);
2078 readToken (token);
2079 } while (! (isType (token, TOKEN_CLOSE_SGML) || isType (token, TOKEN_CLOSE_MXML)) );
2081 if (isType (token, TOKEN_CLOSE_MXML))
2084 * We have found a </mx:type> tag
2085 * Finish reading the "type" and ">"
2087 readToken (token);
2088 readToken (token);
2091 cleanUp:
2092 deleteToken (name);
2093 deleteToken (type);
2094 return TRUE;
2097 static boolean parseActionScript (tokenInfo *const token)
2101 readToken (token);
2103 if (isType (token, TOKEN_LESS_THAN))
2106 * Handle these tags
2107 * <![CDATA[
2108 * ...
2109 * ]]>
2111 readToken (token);
2112 if (isType (token, TOKEN_EQUAL_SIGN))
2114 if (isType (token, TOKEN_OPEN_SQUARE))
2116 readToken (token);
2117 if (isKeyword (token, KEYWORD_cdata))
2119 readToken (token);
2124 if (isType (token, TOKEN_CLOSE_SQUARE))
2127 * Handle these tags
2128 * <![CDATA[
2129 * ...
2130 * ]]>
2132 readToken (token);
2133 if (isType (token, TOKEN_CLOSE_SQUARE))
2135 readToken (token);
2136 if (isType (token, TOKEN_GREATER_THAN))
2138 return TRUE;
2142 else if (isType (token, TOKEN_CLOSE_MXML))
2145 * Read the Script> tags
2147 readToken (token);
2148 readToken (token);
2149 return TRUE;
2151 else if (isType (token, TOKEN_OPEN_MXML))
2153 parseMXML (token);
2155 else
2157 if (isType(token, TOKEN_KEYWORD))
2159 if (isKeyword (token, KEYWORD_private) ||
2160 isKeyword (token, KEYWORD_public) ||
2161 isKeyword (token, KEYWORD_override) )
2164 * Methods can be defined as:
2165 * private function f_name
2166 * public override function f_name
2167 * override private function f_name
2168 * Ignore these keywords if present.
2170 readToken (token);
2172 if (isKeyword (token, KEYWORD_private) ||
2173 isKeyword (token, KEYWORD_public) ||
2174 isKeyword (token, KEYWORD_override) )
2177 * Methods can be defined as:
2178 * private function f_name
2179 * public override function f_name
2180 * override private function f_name
2181 * Ignore these keywords if present.
2183 readToken (token);
2186 switch (token->keyword)
2188 case KEYWORD_function: parseFunction (token); break;
2189 default: parseLine (token); break;
2192 else
2194 parseLine (token);
2197 } while (TRUE);
2200 static void parseFlexFile (tokenInfo *const token)
2204 readToken (token);
2206 if (isType (token, TOKEN_OPEN_MXML))
2208 parseMXML (token);
2210 else if (isType (token, TOKEN_LESS_THAN))
2212 readToken (token);
2213 if (isType (token, TOKEN_QUESTION_MARK))
2216 * <?xml version="1.0" encoding="utf-8"?>
2218 readToken (token);
2219 while (! isType (token, TOKEN_QUESTION_MARK) )
2221 readToken (token);
2223 readToken (token);
2225 else if (isKeyword (token, KEYWORD_NONE))
2228 * This is a simple XML tag, read until the closing statement
2229 * <something .... >
2230 * </something>
2232 readToken (token);
2233 while (! isType (token, TOKEN_GREATER_THAN) )
2235 readToken (token);
2239 else
2241 parseActionScript (token);
2243 } while (TRUE);
2246 static void initialize (const langType language)
2248 Assert (sizeof (FlexKinds) / sizeof (FlexKinds [0]) == FLEXTAG_COUNT);
2249 Lang_js = language;
2250 buildFlexKeywordHash ();
2253 static void findFlexTags (void)
2255 tokenInfo *const token = newToken ();
2256 exception_t exception;
2258 ClassNames = stringListNew ();
2259 FunctionNames = stringListNew ();
2261 exception = (exception_t) (setjmp (Exception));
2262 while (exception == ExceptionNone)
2263 parseFlexFile (token);
2265 stringListDelete (ClassNames);
2266 stringListDelete (FunctionNames);
2267 ClassNames = NULL;
2268 FunctionNames = NULL;
2269 deleteToken (token);
2272 /* Create parser definition stucture */
2273 extern parserDefinition* FlexParser (void)
2275 static const char *const extensions [] = { "as", "mxml", NULL };
2276 parserDefinition *const def = parserNew ("Flex");
2277 def->extensions = extensions;
2279 * New definitions for parsing instead of regex
2281 def->kinds = FlexKinds;
2282 def->kindCount = KIND_COUNT (FlexKinds);
2283 def->parser = findFlexTags;
2284 def->initialize = initialize;
2286 return def;
2288 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */