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:
12 * MXML files (*.mMacromedia XML)
13 * ActionScript files (*.as)
15 * Flex 3 language reference
16 * http://livedocs.adobe.com/flex/3/langref/index.html
22 #include "general.h" /* must always come first */
23 #include <ctype.h> /* to define isalpha () */
40 #define isType(token,t) (boolean) ((token)->type == (t))
41 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
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
{
60 KEYWORD_capital_function
,
62 KEYWORD_capital_object
,
87 /* Used to determine whether keyword is valid for the token language and
90 typedef struct sKeywordDesc
{
95 typedef enum eTokenType
{
123 typedef struct sTokenInfo
{
128 unsigned long lineNumber
;
139 static langType Lang_js
;
141 static jmp_buf Exception
;
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
)
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]);
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 ();
238 static void deleteToken (tokenInfo
*const token
)
240 vStringDelete (token
->string
);
241 vStringDelete (token
->scope
);
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
);
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
;
266 static void makeFlexTag (tokenInfo
*const token
, flexKind kind
)
270 if (FlexKinds
[kind
].enabled
&& ! token
->ignoreTag
)
273 debugPrintf (DEBUG_PARSE
274 , "\n makeFlexTag start: token isClass:%d scope:%s name:%s\n"
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
)
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
));
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
)
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
));
344 vStringCopy(fulltag
, token
->string
);
346 vStringTerminate(fulltag
);
347 makeFlexTag (token
, FLEXTAG_MXTAG
);
348 vStringDelete (fulltag
);
352 static void makeFunctionTag (tokenInfo
*const token
)
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
));
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
);
383 static void parseString (vString
*const string
, const int delimiter
)
393 c
= fileGetc(); /* This maybe a ' or ". */
394 vStringPut(string
, c
);
396 else if (c
== delimiter
)
399 vStringPut (string
, c
);
401 vStringTerminate (string
);
404 /* Read a C identifier beginning with "firstChar" and places it into
407 static void parseIdentifier (vString
*const string
, const int firstChar
)
410 Assert (isIdentChar (c
));
413 vStringPut (string
, c
);
415 } while (isIdentChar (c
));
416 vStringTerminate (string
);
418 fileUngetc (c
); /* unget non-identifier character */
421 static void readToken (tokenInfo
*const token
)
425 token
->type
= TOKEN_UNDEFINED
;
426 token
->keyword
= KEYWORD_NONE
;
427 vStringClear (token
->string
);
433 token
->lineNumber
= getSourceLineNumber ();
434 token
->filePosition
= getInputFilePosition ();
436 while (c
== '\t' || c
== ' ' || c
== '\n');
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;
456 token
->type
= TOKEN_STRING
;
457 parseString (token
->string
, c
);
458 token
->lineNumber
= getSourceLineNumber ();
459 token
->filePosition
= getInputFilePosition ();
464 if (c
!= '\\' && c
!= '"' && !isspace (c
))
466 token
->type
= TOKEN_CHARACTER
;
467 token
->lineNumber
= getSourceLineNumber ();
468 token
->filePosition
= getInputFilePosition ();
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? */
479 token
->type
= TOKEN_FORWARD_SLASH
;
480 token
->lineNumber
= getSourceLineNumber ();
481 token
->filePosition
= getInputFilePosition ();
489 fileSkipToCharacter ('*');
495 } while (c
!= EOF
&& c
!= '\0');
498 else if (d
== '/') /* is this the start of a comment? */
500 fileSkipToCharacter ('\n');
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 ();
516 * An XML comment looks like this
517 * <!-- anything over multiple lines -->
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 */
526 token
->type
= TOKEN_LESS_THAN
;
527 token
->lineNumber
= getSourceLineNumber ();
528 token
->filePosition
= getInputFilePosition ();
536 if ( e
!= '-' ) /* is this the start of a comment? */
540 token
->type
= TOKEN_LESS_THAN
;
541 token
->lineNumber
= getSourceLineNumber ();
542 token
->filePosition
= getInputFilePosition ();
549 if ( f
!= '-' ) /* is this the start of a comment? */
554 token
->type
= TOKEN_LESS_THAN
;
555 token
->lineNumber
= getSourceLineNumber ();
556 token
->filePosition
= getInputFilePosition ();
564 fileSkipToCharacter ('-');
580 } while (c
!= EOF
&& c
!= '\0');
590 if ( e
!= 'x' ) /* continuing an mx tag */
594 token
->type
= TOKEN_LESS_THAN
;
595 token
->lineNumber
= getSourceLineNumber ();
596 token
->filePosition
= getInputFilePosition ();
603 if ( f
!= ':' ) /* is this the start of a comment? */
608 token
->type
= TOKEN_LESS_THAN
;
609 token
->lineNumber
= getSourceLineNumber ();
610 token
->filePosition
= getInputFilePosition ();
616 token
->type
= TOKEN_OPEN_MXML
;
617 token
->lineNumber
= getSourceLineNumber ();
618 token
->filePosition
= getInputFilePosition ();
627 if ( e
!= 'm' ) /* continuing an mx tag */
631 token
->type
= TOKEN_LESS_THAN
;
632 token
->lineNumber
= getSourceLineNumber ();
633 token
->filePosition
= getInputFilePosition ();
638 if ( f
!= 'x' ) /* continuing an mx tag */
642 token
->type
= TOKEN_LESS_THAN
;
643 token
->lineNumber
= getSourceLineNumber ();
644 token
->filePosition
= getInputFilePosition ();
651 if ( g
!= ':' ) /* is this the start of a comment? */
656 token
->type
= TOKEN_LESS_THAN
;
657 token
->lineNumber
= getSourceLineNumber ();
658 token
->filePosition
= getInputFilePosition ();
664 token
->type
= TOKEN_CLOSE_MXML
;
665 token
->lineNumber
= getSourceLineNumber ();
666 token
->filePosition
= getInputFilePosition ();
678 token
->type
= TOKEN_GREATER_THAN
;
679 token
->lineNumber
= getSourceLineNumber ();
680 token
->filePosition
= getInputFilePosition ();
684 token
->type
= TOKEN_EXCLAMATION
;
685 /*token->lineNumber = getSourceLineNumber ();
686 token->filePosition = getInputFilePosition ();*/
690 if (! isIdentChar (c
))
691 token
->type
= TOKEN_UNDEFINED
;
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
;
701 token
->type
= TOKEN_KEYWORD
;
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
)
728 * Other databases can have arguments with fully declared
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? */
737 while (! (isType (token
, TOKEN_CLOSE_PAREN
) && (nest_level
== 0)))
740 if (isType (token
, TOKEN_OPEN_PAREN
))
744 if (isType (token
, TOKEN_CLOSE_PAREN
))
756 static void skipArrayList (tokenInfo
*const token
)
761 * Handle square brackets
763 * So we must check for nested open and closing square brackets
766 if (isType (token
, TOKEN_OPEN_SQUARE
)) /* arguments? */
769 while (! (isType (token
, TOKEN_CLOSE_SQUARE
) && (nest_level
== 0)))
772 if (isType (token
, TOKEN_OPEN_SQUARE
))
776 if (isType (token
, TOKEN_CLOSE_SQUARE
))
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
);
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
);
837 static void parseSwitch (tokenInfo
*const token
)
840 * switch (expression){
847 * default : statement;
853 if (isType (token
, TOKEN_OPEN_PAREN
))
855 skipArgumentList(token
);
858 if (isType (token
, TOKEN_OPEN_CURLY
))
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>");
884 * document.write(number+"<br>");
889 * document.write(number+"<br>");
895 if (isKeyword (token
, KEYWORD_for
) || isKeyword (token
, KEYWORD_while
))
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
);
923 else if (isKeyword (token
, KEYWORD_do
))
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
);
944 if (isKeyword (token
, KEYWORD_while
))
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
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.
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.
1005 if (isKeyword (token
, KEYWORD_if
))
1008 * Check for an "else if" and consume the "if"
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
);
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
;
1053 if (isType (token
, TOKEN_CLOSE_CURLY
))
1056 * This statement did not have a line terminator.
1058 read_next_token
= FALSE
;
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
) )
1084 copyToken (name
, token
);
1085 /* Add scope in case this is an INNER function
1086 addToScope(name, token->scope);
1090 debugPrintf (DEBUG_PARSE
1091 , "\n parseFunction: token isClass:%d scope:%s name:%s\n"
1093 , vStringValue(token
->scope
)
1094 , vStringValue(token
->string
)
1098 debugPrintf (DEBUG_PARSE
1099 , "\n parseFunction: name isClass:%d scope:%s name:%s\n"
1101 , vStringValue(name
->scope
)
1102 , vStringValue(name
->string
)
1108 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1109 skipArgumentList(token
);
1111 if ( isType (token
, TOKEN_COLON
) )
1114 * function fname ():ReturnType
1120 if ( isType (token
, TOKEN_OPEN_CURLY
) )
1123 debugPrintf (DEBUG_PARSE
1124 , "\n parseFunction end: name isClass:%d scope:%s name:%s\n"
1126 , vStringValue(name
->scope
)
1127 , vStringValue(name
->string
)
1130 parseBlock (token
, name
);
1132 debugPrintf (DEBUG_PARSE
1133 , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
1135 , vStringValue(token
->scope
)
1136 , vStringValue(token
->string
)
1140 debugPrintf (DEBUG_PARSE
1141 , "\n parseFunction end2: token isClass:%d scope:%s name:%s\n"
1143 , vStringValue(token
->scope
)
1144 , vStringValue(token
->string
)
1148 debugPrintf (DEBUG_PARSE
1149 , "\n parseFunction end3: name isClass:%d scope:%s name:%s\n"
1151 , vStringValue(name
->scope
)
1152 , vStringValue(name
->string
)
1155 makeFunctionTag (name
);
1158 findCmdTerm (token
);
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
);
1172 debugPrintf (DEBUG_PARSE
1173 , "\n parseBlock start: token isClass:%d scope:%s name:%s\n"
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
) )
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
);
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
)
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
);
1229 debugPrintf (DEBUG_PARSE
1230 , "\n parseBlock end: token isClass:%d scope:%s name:%s\n"
1232 , vStringValue(token
->scope
)
1233 , vStringValue(token
->string
)
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}
1254 if (isType (token
, TOKEN_STRING
) || isKeyword(token
, KEYWORD_NONE
))
1256 copyToken (name
, token
);
1259 if ( isType (token
, TOKEN_COLON
) )
1262 if ( isKeyword (token
, KEYWORD_function
) )
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
1285 addToScope (name
, class->string
);
1286 makeFlexTag (name
, FLEXTAG_PROPERTY
);
1289 * Read the next token, if a comma
1290 * we must loop again
1296 } while ( isType(token
, TOKEN_COMMA
) );
1298 findCmdTerm (token
);
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
) )
1324 copyToken (name
, token
);
1327 if ( isType (token
, TOKEN_COLON
) )
1330 * var vname ():DataType = new Date();
1331 * var vname ():DataType;
1337 while (! isType (token
, TOKEN_SEMICOLON
) )
1342 if ( isType (token
, TOKEN_SEMICOLON
) )
1345 * Only create variables for global scope
1347 /* if ( token->nestLevel == 0 && is_global ) */
1350 if (isType (token
, TOKEN_SEMICOLON
))
1351 makeFlexTag (name
, FLEXTAG_VARIABLE
);
1355 vStringCopy(token
->scope
, saveScope
);
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
) )
1382 token
->isClass
= TRUE
;
1383 /* Add class name to scope */
1384 addToScope(token
, token
->string
);
1386 copyToken (name
, token
);
1390 debugPrintf (DEBUG_PARSE
1391 , "\n parseClass start: token isClass:%d scope:%s name:%s\n"
1393 , vStringValue(token
->scope
)
1394 , vStringValue(token
->string
)
1397 if ( isType (token
, TOKEN_OPEN_CURLY
) )
1399 makeClassTag (name
);
1400 parseBlock (token
, name
);
1404 debugPrintf (DEBUG_PARSE
1405 , "\n parseClass end: token isClass:%d scope:%s name:%s\n"
1407 , vStringValue(token
->scope
)
1408 , vStringValue(token
->string
)
1411 vStringCopy(token
->scope
, saveScope
);
1412 token
->isClass
= saveIsClass
;
1414 vStringDelete(saveScope
);
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
;
1431 vStringClear(saveScope
);
1432 vStringCopy (saveScope
, token
->scope
);
1434 debugPrintf (DEBUG_PARSE
1435 , "\n parseStatement: token isClass:%d scope:%s name:%s\n"
1437 , vStringValue(token
->scope
)
1438 , vStringValue(token
->string
)
1442 * Functions can be named or unnamed.
1443 * This deals with these formats:
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;");
1452 * testlib.extras.ValidClassOne = function(a,b) {
1456 * testlib.extras.ValidClassOne.prototype = {
1457 * 'validMethodOne' : function(a,b) {},
1458 * 'validMethodTwo' : function(a,b) {}
1460 * ValidClassTwo = function ()
1462 * this.validMethodThree = function() {}
1464 * this.validMethodFour = () {}
1466 * Database.prototype.validMethodThree = Database_getTodaysDate;
1469 if ( isKeyword(token
, KEYWORD_public
) )
1475 if ( isKeyword(token
, KEYWORD_private
) )
1480 if ( isKeyword(token
, KEYWORD_static
) )
1485 if (isType(token
, TOKEN_KEYWORD
))
1487 switch (token
->keyword
)
1498 case KEYWORD_finally
:
1499 /* Common semantics */
1500 is_terminated
= parseIf (token
);
1502 case KEYWORD_switch
:
1503 parseSwitch (token
);
1507 return is_terminated
;
1509 case KEYWORD_function
:
1510 parseFunction (token
);
1511 return is_terminated
;
1514 parseVar (token
, is_public
);
1515 return is_terminated
;
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 */
1531 if (isType (token
, TOKEN_PERIOD
))
1534 * Cannot be a global variable is it has dot references in the name
1540 if ( isKeyword(token
, KEYWORD_NONE
) )
1544 vStringCopy(saveScope
, token
->scope
);
1545 addToScope(token
, name
->string
);
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 ) {
1561 * Specified function name: "build"
1562 * BindAgent.prototype.build = function( mode ) {
1563 * ignore everything within this function
1568 * ValidClassOne.prototype = {
1569 * 'validMethodOne' : function(a,b) {},
1570 * 'validMethodTwo' : function(a,b) {}
1574 makeClassTag (name
);
1576 is_prototype
= TRUE
;
1579 * There should a ".function_name" next.
1582 if (isType (token
, TOKEN_PERIOD
))
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
;
1610 else if (isType (token
, TOKEN_EQUAL_SIGN
))
1613 if (isType (token
, TOKEN_OPEN_CURLY
))
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
;
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 {
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
;
1668 if ( isType (token
, TOKEN_SEMICOLON
) )
1671 * Only create variables for global scope
1673 if ( token
->nestLevel
== 0 && is_global
)
1676 * Handles this syntax:
1679 if (isType (token
, TOKEN_SEMICOLON
))
1680 makeFlexTag (name
, FLEXTAG_VARIABLE
);
1683 * Statement has ended.
1684 * This deals with calls to functions, like:
1690 if ( isType (token
, TOKEN_EQUAL_SIGN
) )
1694 if ( isKeyword (token
, KEYWORD_function
) )
1698 if ( isKeyword (token
, KEYWORD_NONE
) &&
1699 ! isType (token
, TOKEN_OPEN_PAREN
) )
1702 * Functions of this format:
1703 * var D2A = function theAdd(a, b)
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:
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
);
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
);
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
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
1788 is_terminated
= TRUE
;
1791 else if (isKeyword (token
, KEYWORD_new
))
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
) )
1804 if ( isType (token
, TOKEN_OPEN_PAREN
) )
1805 skipArgumentList(token
);
1807 if (isType (token
, TOKEN_SEMICOLON
))
1809 if ( token
->nestLevel
== 0 )
1813 makeClassTag (name
);
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:
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
));
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() {
1873 * document.write("hello from checkForUpdate<br>")
1878 if ( ! is_terminated
&& isType (token
, TOKEN_CLOSE_CURLY
))
1879 is_terminated
= FALSE
;
1883 vStringCopy(token
->scope
, saveScope
);
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
)
1917 case KEYWORD_finally
:
1918 /* Common semantics */
1919 is_terminated
= parseIf (token
);
1921 case KEYWORD_switch
:
1922 parseSwitch (token
);
1925 parseStatement (token
);
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
))
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))
1961 if (isKeyword (token
, KEYWORD_cdata
))
1964 if (isType (token
, TOKEN_OPEN_SQUARE
))
1966 parseActionScript (token
);
1967 if (isType (token
, TOKEN_CLOSE_SQUARE
))
1970 if (isType (token
, TOKEN_CLOSE_SQUARE
))
1982 parseActionScript (token
);
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.
2003 if (isKeyword (token
, KEYWORD_script
))
2006 * These tags can be of this form:
2007 * <mx:Script src="filename.as" />
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 ">"
2026 if (isType (token
, TOKEN_CLOSE_SGML
))
2029 * We have found a <mx:Script src="filename.as" />
2035 * This is a beginning of an embedded script.
2036 * These typically are of this format:
2039 * ... ActionScript ...
2047 if (isType (token
, TOKEN_CLOSE_MXML
))
2050 * We have found a </mx:type> tag
2051 * Finish reading the "type" and ">"
2059 copyToken (type
, token
);
2064 if (isType (token
, TOKEN_OPEN_MXML
))
2068 else if (isKeyword (token
, KEYWORD_id
))
2074 copyToken (name
, token
);
2075 addToScope (name
, type
->string
);
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 ">"
2097 static boolean
parseActionScript (tokenInfo
*const token
)
2103 if (isType (token
, TOKEN_LESS_THAN
))
2112 if (isType (token
, TOKEN_EQUAL_SIGN
))
2114 if (isType (token
, TOKEN_OPEN_SQUARE
))
2117 if (isKeyword (token
, KEYWORD_cdata
))
2124 if (isType (token
, TOKEN_CLOSE_SQUARE
))
2133 if (isType (token
, TOKEN_CLOSE_SQUARE
))
2136 if (isType (token
, TOKEN_GREATER_THAN
))
2142 else if (isType (token
, TOKEN_CLOSE_MXML
))
2145 * Read the Script> tags
2151 else if (isType (token
, TOKEN_OPEN_MXML
))
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.
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.
2186 switch (token
->keyword
)
2188 case KEYWORD_function
: parseFunction (token
); break;
2189 default: parseLine (token
); break;
2200 static void parseFlexFile (tokenInfo
*const token
)
2206 if (isType (token
, TOKEN_OPEN_MXML
))
2210 else if (isType (token
, TOKEN_LESS_THAN
))
2213 if (isType (token
, TOKEN_QUESTION_MARK
))
2216 * <?xml version="1.0" encoding="utf-8"?>
2219 while (! isType (token
, TOKEN_QUESTION_MARK
) )
2225 else if (isKeyword (token
, KEYWORD_NONE
))
2228 * This is a simple XML tag, read until the closing statement
2233 while (! isType (token
, TOKEN_GREATER_THAN
) )
2241 parseActionScript (token
);
2246 static void initialize (const langType language
)
2248 Assert (sizeof (FlexKinds
) / sizeof (FlexKinds
[0]) == FLEXTAG_COUNT
);
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
);
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
;
2288 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */