ctags.rb 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +2 -0
- data/Rakefile +23 -0
- data/ctags.rb.gemspec +23 -0
- data/ext/.gitignore +3 -0
- data/ext/extconf.rb +15 -0
- data/ext/vendor/exuberant-ctags/.gitignore +6 -0
- data/ext/vendor/exuberant-ctags/.indent.pro +31 -0
- data/ext/vendor/exuberant-ctags/COPYING +340 -0
- data/ext/vendor/exuberant-ctags/EXTENDING.html +386 -0
- data/ext/vendor/exuberant-ctags/FAQ +371 -0
- data/ext/vendor/exuberant-ctags/INSTALL +215 -0
- data/ext/vendor/exuberant-ctags/INSTALL.oth +73 -0
- data/ext/vendor/exuberant-ctags/MAINTAINERS +88 -0
- data/ext/vendor/exuberant-ctags/Makefile.in +222 -0
- data/ext/vendor/exuberant-ctags/NEWS +871 -0
- data/ext/vendor/exuberant-ctags/README +73 -0
- data/ext/vendor/exuberant-ctags/ant.c +42 -0
- data/ext/vendor/exuberant-ctags/argproc.c +505 -0
- data/ext/vendor/exuberant-ctags/args.c +274 -0
- data/ext/vendor/exuberant-ctags/args.h +63 -0
- data/ext/vendor/exuberant-ctags/asm.c +387 -0
- data/ext/vendor/exuberant-ctags/asp.c +328 -0
- data/ext/vendor/exuberant-ctags/awk.c +81 -0
- data/ext/vendor/exuberant-ctags/basic.c +203 -0
- data/ext/vendor/exuberant-ctags/beta.c +321 -0
- data/ext/vendor/exuberant-ctags/c.c +2932 -0
- data/ext/vendor/exuberant-ctags/cobol.c +50 -0
- data/ext/vendor/exuberant-ctags/config.h.in +277 -0
- data/ext/vendor/exuberant-ctags/configure +7704 -0
- data/ext/vendor/exuberant-ctags/configure.ac +532 -0
- data/ext/vendor/exuberant-ctags/ctags.1 +1186 -0
- data/ext/vendor/exuberant-ctags/ctags.h +28 -0
- data/ext/vendor/exuberant-ctags/ctags.html +2087 -0
- data/ext/vendor/exuberant-ctags/ctags.spec +40 -0
- data/ext/vendor/exuberant-ctags/debug.c +113 -0
- data/ext/vendor/exuberant-ctags/debug.h +70 -0
- data/ext/vendor/exuberant-ctags/descrip.mms +68 -0
- data/ext/vendor/exuberant-ctags/dosbatch.c +42 -0
- data/ext/vendor/exuberant-ctags/e_amiga.h +24 -0
- data/ext/vendor/exuberant-ctags/e_djgpp.h +47 -0
- data/ext/vendor/exuberant-ctags/e_mac.h +143 -0
- data/ext/vendor/exuberant-ctags/e_msoft.h +76 -0
- data/ext/vendor/exuberant-ctags/e_os2.h +37 -0
- data/ext/vendor/exuberant-ctags/e_qdos.h +34 -0
- data/ext/vendor/exuberant-ctags/e_riscos.h +58 -0
- data/ext/vendor/exuberant-ctags/e_vms.h +31 -0
- data/ext/vendor/exuberant-ctags/eiffel.c +1352 -0
- data/ext/vendor/exuberant-ctags/entry.c +847 -0
- data/ext/vendor/exuberant-ctags/entry.h +103 -0
- data/ext/vendor/exuberant-ctags/erlang.c +189 -0
- data/ext/vendor/exuberant-ctags/flex.c +2243 -0
- data/ext/vendor/exuberant-ctags/fortran.c +2197 -0
- data/ext/vendor/exuberant-ctags/general.h +127 -0
- data/ext/vendor/exuberant-ctags/get.c +669 -0
- data/ext/vendor/exuberant-ctags/get.h +50 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/all-wcprops +47 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/entries +112 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/README.txt.svn-base +5 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regcomp.c.svn-base +3818 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.c.svn-base +74 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.h.svn-base +575 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.c.svn-base +1713 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.h.svn-base +773 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regexec.c.svn-base +4338 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/README.txt +5 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regcomp.c +3818 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex.c +74 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex.h +575 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.c +1713 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.h +773 -0
- data/ext/vendor/exuberant-ctags/gnu_regex/regexec.c +4338 -0
- data/ext/vendor/exuberant-ctags/html.c +49 -0
- data/ext/vendor/exuberant-ctags/jscript.c +1572 -0
- data/ext/vendor/exuberant-ctags/keyword.c +258 -0
- data/ext/vendor/exuberant-ctags/keyword.h +34 -0
- data/ext/vendor/exuberant-ctags/lisp.c +139 -0
- data/ext/vendor/exuberant-ctags/lregex.c +704 -0
- data/ext/vendor/exuberant-ctags/lua.c +133 -0
- data/ext/vendor/exuberant-ctags/mac.c +273 -0
- data/ext/vendor/exuberant-ctags/magic.diff +21 -0
- data/ext/vendor/exuberant-ctags/main.c +584 -0
- data/ext/vendor/exuberant-ctags/main.h +32 -0
- data/ext/vendor/exuberant-ctags/maintainer.mak +476 -0
- data/ext/vendor/exuberant-ctags/make.c +217 -0
- data/ext/vendor/exuberant-ctags/matlab.c +44 -0
- data/ext/vendor/exuberant-ctags/mk_bc3.mak +46 -0
- data/ext/vendor/exuberant-ctags/mk_bc5.mak +49 -0
- data/ext/vendor/exuberant-ctags/mk_djg.mak +18 -0
- data/ext/vendor/exuberant-ctags/mk_manx.mak +65 -0
- data/ext/vendor/exuberant-ctags/mk_mingw.mak +31 -0
- data/ext/vendor/exuberant-ctags/mk_mpw.mak +130 -0
- data/ext/vendor/exuberant-ctags/mk_mvc.mak +40 -0
- data/ext/vendor/exuberant-ctags/mk_os2.mak +104 -0
- data/ext/vendor/exuberant-ctags/mk_qdos.mak +100 -0
- data/ext/vendor/exuberant-ctags/mk_sas.mak +63 -0
- data/ext/vendor/exuberant-ctags/mkinstalldirs +40 -0
- data/ext/vendor/exuberant-ctags/ocaml.c +1842 -0
- data/ext/vendor/exuberant-ctags/options.c +1842 -0
- data/ext/vendor/exuberant-ctags/options.h +155 -0
- data/ext/vendor/exuberant-ctags/parse.c +677 -0
- data/ext/vendor/exuberant-ctags/parse.h +129 -0
- data/ext/vendor/exuberant-ctags/parsers.h +63 -0
- data/ext/vendor/exuberant-ctags/pascal.c +267 -0
- data/ext/vendor/exuberant-ctags/perl.c +382 -0
- data/ext/vendor/exuberant-ctags/php.c +237 -0
- data/ext/vendor/exuberant-ctags/python.c +771 -0
- data/ext/vendor/exuberant-ctags/qdos.c +106 -0
- data/ext/vendor/exuberant-ctags/read.c +569 -0
- data/ext/vendor/exuberant-ctags/read.h +116 -0
- data/ext/vendor/exuberant-ctags/readtags.c +959 -0
- data/ext/vendor/exuberant-ctags/readtags.h +252 -0
- data/ext/vendor/exuberant-ctags/rexx.c +39 -0
- data/ext/vendor/exuberant-ctags/routines.c +891 -0
- data/ext/vendor/exuberant-ctags/routines.h +134 -0
- data/ext/vendor/exuberant-ctags/ruby.c +408 -0
- data/ext/vendor/exuberant-ctags/scheme.c +111 -0
- data/ext/vendor/exuberant-ctags/sh.c +115 -0
- data/ext/vendor/exuberant-ctags/slang.c +41 -0
- data/ext/vendor/exuberant-ctags/sml.c +212 -0
- data/ext/vendor/exuberant-ctags/sort.c +230 -0
- data/ext/vendor/exuberant-ctags/sort.h +32 -0
- data/ext/vendor/exuberant-ctags/source.mak +122 -0
- data/ext/vendor/exuberant-ctags/sql.c +2112 -0
- data/ext/vendor/exuberant-ctags/strlist.c +281 -0
- data/ext/vendor/exuberant-ctags/strlist.h +54 -0
- data/ext/vendor/exuberant-ctags/tcl.c +116 -0
- data/ext/vendor/exuberant-ctags/tex.c +524 -0
- data/ext/vendor/exuberant-ctags/verilog.c +340 -0
- data/ext/vendor/exuberant-ctags/vhdl.c +835 -0
- data/ext/vendor/exuberant-ctags/vim.c +636 -0
- data/ext/vendor/exuberant-ctags/vstring.c +232 -0
- data/ext/vendor/exuberant-ctags/vstring.h +85 -0
- data/ext/vendor/exuberant-ctags/yacc.c +40 -0
- data/lib/ctags/exuberant.rb +45 -0
- data/lib/ctags/version.rb +3 -0
- data/lib/ctags.rb +6 -0
- data/test/test_ctags.rb +24 -0
- metadata +233 -0
|
@@ -0,0 +1,1572 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* $Id: jscript.c 666 2008-05-15 17:47:31Z dfishburn $
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2003, Darren Hiebert
|
|
5
|
+
*
|
|
6
|
+
* This source code is released for free distribution under the terms of the
|
|
7
|
+
* GNU General Public License.
|
|
8
|
+
*
|
|
9
|
+
* This module contains functions for generating tags for JavaScript language
|
|
10
|
+
* files.
|
|
11
|
+
*
|
|
12
|
+
* This is a good reference for different forms of the function statement:
|
|
13
|
+
* http://www.permadi.com/tutorial/jsFunc/
|
|
14
|
+
* Another good reference:
|
|
15
|
+
* http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* INCLUDE FILES
|
|
20
|
+
*/
|
|
21
|
+
#include "general.h" /* must always come first */
|
|
22
|
+
#include <ctype.h> /* to define isalpha () */
|
|
23
|
+
#include <setjmp.h>
|
|
24
|
+
#ifdef DEBUG
|
|
25
|
+
#include <stdio.h>
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
#include "debug.h"
|
|
29
|
+
#include "entry.h"
|
|
30
|
+
#include "keyword.h"
|
|
31
|
+
#include "parse.h"
|
|
32
|
+
#include "read.h"
|
|
33
|
+
#include "routines.h"
|
|
34
|
+
#include "vstring.h"
|
|
35
|
+
|
|
36
|
+
/*
|
|
37
|
+
* MACROS
|
|
38
|
+
*/
|
|
39
|
+
#define isType(token,t) (boolean) ((token)->type == (t))
|
|
40
|
+
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* DATA DECLARATIONS
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* Tracks class and function names already created
|
|
50
|
+
*/
|
|
51
|
+
static stringList *ClassNames;
|
|
52
|
+
static stringList *FunctionNames;
|
|
53
|
+
|
|
54
|
+
/* Used to specify type of keyword.
|
|
55
|
+
*/
|
|
56
|
+
typedef enum eKeywordId {
|
|
57
|
+
KEYWORD_NONE = -1,
|
|
58
|
+
KEYWORD_function,
|
|
59
|
+
KEYWORD_capital_function,
|
|
60
|
+
KEYWORD_object,
|
|
61
|
+
KEYWORD_capital_object,
|
|
62
|
+
KEYWORD_prototype,
|
|
63
|
+
KEYWORD_var,
|
|
64
|
+
KEYWORD_new,
|
|
65
|
+
KEYWORD_this,
|
|
66
|
+
KEYWORD_for,
|
|
67
|
+
KEYWORD_while,
|
|
68
|
+
KEYWORD_do,
|
|
69
|
+
KEYWORD_if,
|
|
70
|
+
KEYWORD_else,
|
|
71
|
+
KEYWORD_switch,
|
|
72
|
+
KEYWORD_try,
|
|
73
|
+
KEYWORD_catch,
|
|
74
|
+
KEYWORD_finally
|
|
75
|
+
} keywordId;
|
|
76
|
+
|
|
77
|
+
/* Used to determine whether keyword is valid for the token language and
|
|
78
|
+
* what its ID is.
|
|
79
|
+
*/
|
|
80
|
+
typedef struct sKeywordDesc {
|
|
81
|
+
const char *name;
|
|
82
|
+
keywordId id;
|
|
83
|
+
} keywordDesc;
|
|
84
|
+
|
|
85
|
+
typedef enum eTokenType {
|
|
86
|
+
TOKEN_UNDEFINED,
|
|
87
|
+
TOKEN_CHARACTER,
|
|
88
|
+
TOKEN_CLOSE_PAREN,
|
|
89
|
+
TOKEN_SEMICOLON,
|
|
90
|
+
TOKEN_COLON,
|
|
91
|
+
TOKEN_COMMA,
|
|
92
|
+
TOKEN_KEYWORD,
|
|
93
|
+
TOKEN_OPEN_PAREN,
|
|
94
|
+
TOKEN_OPERATOR,
|
|
95
|
+
TOKEN_IDENTIFIER,
|
|
96
|
+
TOKEN_STRING,
|
|
97
|
+
TOKEN_PERIOD,
|
|
98
|
+
TOKEN_OPEN_CURLY,
|
|
99
|
+
TOKEN_CLOSE_CURLY,
|
|
100
|
+
TOKEN_EQUAL_SIGN,
|
|
101
|
+
TOKEN_FORWARD_SLASH,
|
|
102
|
+
TOKEN_OPEN_SQUARE,
|
|
103
|
+
TOKEN_CLOSE_SQUARE
|
|
104
|
+
} tokenType;
|
|
105
|
+
|
|
106
|
+
typedef struct sTokenInfo {
|
|
107
|
+
tokenType type;
|
|
108
|
+
keywordId keyword;
|
|
109
|
+
vString * string;
|
|
110
|
+
vString * scope;
|
|
111
|
+
unsigned long lineNumber;
|
|
112
|
+
fpos_t filePosition;
|
|
113
|
+
int nestLevel;
|
|
114
|
+
boolean ignoreTag;
|
|
115
|
+
} tokenInfo;
|
|
116
|
+
|
|
117
|
+
/*
|
|
118
|
+
* DATA DEFINITIONS
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
static langType Lang_js;
|
|
122
|
+
|
|
123
|
+
static jmp_buf Exception;
|
|
124
|
+
|
|
125
|
+
typedef enum {
|
|
126
|
+
JSTAG_FUNCTION,
|
|
127
|
+
JSTAG_CLASS,
|
|
128
|
+
JSTAG_METHOD,
|
|
129
|
+
JSTAG_PROPERTY,
|
|
130
|
+
JSTAG_VARIABLE,
|
|
131
|
+
JSTAG_COUNT
|
|
132
|
+
} jsKind;
|
|
133
|
+
|
|
134
|
+
static kindOption JsKinds [] = {
|
|
135
|
+
{ TRUE, 'f', "function", "functions" },
|
|
136
|
+
{ TRUE, 'c', "class", "classes" },
|
|
137
|
+
{ TRUE, 'm', "method", "methods" },
|
|
138
|
+
{ TRUE, 'p', "property", "properties" },
|
|
139
|
+
{ TRUE, 'v', "variable", "global variables" }
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
static const keywordDesc JsKeywordTable [] = {
|
|
143
|
+
/* keyword keyword ID */
|
|
144
|
+
{ "function", KEYWORD_function },
|
|
145
|
+
{ "Function", KEYWORD_capital_function },
|
|
146
|
+
{ "object", KEYWORD_object },
|
|
147
|
+
{ "Object", KEYWORD_capital_object },
|
|
148
|
+
{ "prototype", KEYWORD_prototype },
|
|
149
|
+
{ "var", KEYWORD_var },
|
|
150
|
+
{ "new", KEYWORD_new },
|
|
151
|
+
{ "this", KEYWORD_this },
|
|
152
|
+
{ "for", KEYWORD_for },
|
|
153
|
+
{ "while", KEYWORD_while },
|
|
154
|
+
{ "do", KEYWORD_do },
|
|
155
|
+
{ "if", KEYWORD_if },
|
|
156
|
+
{ "else", KEYWORD_else },
|
|
157
|
+
{ "switch", KEYWORD_switch },
|
|
158
|
+
{ "try", KEYWORD_try },
|
|
159
|
+
{ "catch", KEYWORD_catch },
|
|
160
|
+
{ "finally", KEYWORD_finally }
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/*
|
|
164
|
+
* FUNCTION DEFINITIONS
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
/* Recursive functions */
|
|
168
|
+
static void parseFunction (tokenInfo *const token);
|
|
169
|
+
static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent);
|
|
170
|
+
static boolean parseLine (tokenInfo *const token, boolean is_inside_class);
|
|
171
|
+
|
|
172
|
+
static boolean isIdentChar (const int c)
|
|
173
|
+
{
|
|
174
|
+
return (boolean)
|
|
175
|
+
(isalpha (c) || isdigit (c) || c == '$' ||
|
|
176
|
+
c == '@' || c == '_' || c == '#');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
static void buildJsKeywordHash (void)
|
|
180
|
+
{
|
|
181
|
+
const size_t count = sizeof (JsKeywordTable) /
|
|
182
|
+
sizeof (JsKeywordTable [0]);
|
|
183
|
+
size_t i;
|
|
184
|
+
for (i = 0 ; i < count ; ++i)
|
|
185
|
+
{
|
|
186
|
+
const keywordDesc* const p = &JsKeywordTable [i];
|
|
187
|
+
addKeyword (p->name, Lang_js, (int) p->id);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
static tokenInfo *newToken (void)
|
|
192
|
+
{
|
|
193
|
+
tokenInfo *const token = xMalloc (1, tokenInfo);
|
|
194
|
+
|
|
195
|
+
token->type = TOKEN_UNDEFINED;
|
|
196
|
+
token->keyword = KEYWORD_NONE;
|
|
197
|
+
token->string = vStringNew ();
|
|
198
|
+
token->scope = vStringNew ();
|
|
199
|
+
token->nestLevel = 0;
|
|
200
|
+
token->ignoreTag = FALSE;
|
|
201
|
+
token->lineNumber = getSourceLineNumber ();
|
|
202
|
+
token->filePosition = getInputFilePosition ();
|
|
203
|
+
|
|
204
|
+
return token;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static void deleteToken (tokenInfo *const token)
|
|
208
|
+
{
|
|
209
|
+
vStringDelete (token->string);
|
|
210
|
+
vStringDelete (token->scope);
|
|
211
|
+
eFree (token);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/*
|
|
215
|
+
* Tag generation functions
|
|
216
|
+
*/
|
|
217
|
+
|
|
218
|
+
static void makeConstTag (tokenInfo *const token, const jsKind kind)
|
|
219
|
+
{
|
|
220
|
+
if (JsKinds [kind].enabled && ! token->ignoreTag )
|
|
221
|
+
{
|
|
222
|
+
const char *const name = vStringValue (token->string);
|
|
223
|
+
tagEntryInfo e;
|
|
224
|
+
initTagEntry (&e, name);
|
|
225
|
+
|
|
226
|
+
e.lineNumber = token->lineNumber;
|
|
227
|
+
e.filePosition = token->filePosition;
|
|
228
|
+
e.kindName = JsKinds [kind].name;
|
|
229
|
+
e.kind = JsKinds [kind].letter;
|
|
230
|
+
|
|
231
|
+
makeTagEntry (&e);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static void makeJsTag (tokenInfo *const token, const jsKind kind)
|
|
236
|
+
{
|
|
237
|
+
vString * fulltag;
|
|
238
|
+
|
|
239
|
+
if (JsKinds [kind].enabled && ! token->ignoreTag )
|
|
240
|
+
{
|
|
241
|
+
/*
|
|
242
|
+
* If a scope has been added to the token, change the token
|
|
243
|
+
* string to include the scope when making the tag.
|
|
244
|
+
*/
|
|
245
|
+
if ( vStringLength(token->scope) > 0 )
|
|
246
|
+
{
|
|
247
|
+
fulltag = vStringNew ();
|
|
248
|
+
vStringCopy(fulltag, token->scope);
|
|
249
|
+
vStringCatS (fulltag, ".");
|
|
250
|
+
vStringCatS (fulltag, vStringValue(token->string));
|
|
251
|
+
vStringTerminate(fulltag);
|
|
252
|
+
vStringCopy(token->string, fulltag);
|
|
253
|
+
vStringDelete (fulltag);
|
|
254
|
+
}
|
|
255
|
+
makeConstTag (token, kind);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
static void makeClassTag (tokenInfo *const token)
|
|
260
|
+
{
|
|
261
|
+
vString * fulltag;
|
|
262
|
+
|
|
263
|
+
if ( ! token->ignoreTag )
|
|
264
|
+
{
|
|
265
|
+
fulltag = vStringNew ();
|
|
266
|
+
if (vStringLength (token->scope) > 0)
|
|
267
|
+
{
|
|
268
|
+
vStringCopy(fulltag, token->scope);
|
|
269
|
+
vStringCatS (fulltag, ".");
|
|
270
|
+
vStringCatS (fulltag, vStringValue(token->string));
|
|
271
|
+
}
|
|
272
|
+
else
|
|
273
|
+
{
|
|
274
|
+
vStringCopy(fulltag, token->string);
|
|
275
|
+
}
|
|
276
|
+
vStringTerminate(fulltag);
|
|
277
|
+
if ( ! stringListHas(ClassNames, vStringValue (fulltag)) )
|
|
278
|
+
{
|
|
279
|
+
stringListAdd (ClassNames, vStringNewCopy (fulltag));
|
|
280
|
+
makeJsTag (token, JSTAG_CLASS);
|
|
281
|
+
}
|
|
282
|
+
vStringDelete (fulltag);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
static void makeFunctionTag (tokenInfo *const token)
|
|
287
|
+
{
|
|
288
|
+
vString * fulltag;
|
|
289
|
+
|
|
290
|
+
if ( ! token->ignoreTag )
|
|
291
|
+
{
|
|
292
|
+
fulltag = vStringNew ();
|
|
293
|
+
if (vStringLength (token->scope) > 0)
|
|
294
|
+
{
|
|
295
|
+
vStringCopy(fulltag, token->scope);
|
|
296
|
+
vStringCatS (fulltag, ".");
|
|
297
|
+
vStringCatS (fulltag, vStringValue(token->string));
|
|
298
|
+
}
|
|
299
|
+
else
|
|
300
|
+
{
|
|
301
|
+
vStringCopy(fulltag, token->string);
|
|
302
|
+
}
|
|
303
|
+
vStringTerminate(fulltag);
|
|
304
|
+
if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) )
|
|
305
|
+
{
|
|
306
|
+
stringListAdd (FunctionNames, vStringNewCopy (fulltag));
|
|
307
|
+
makeJsTag (token, JSTAG_FUNCTION);
|
|
308
|
+
}
|
|
309
|
+
vStringDelete (fulltag);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/*
|
|
314
|
+
* Parsing functions
|
|
315
|
+
*/
|
|
316
|
+
|
|
317
|
+
static void parseString (vString *const string, const int delimiter)
|
|
318
|
+
{
|
|
319
|
+
boolean end = FALSE;
|
|
320
|
+
while (! end)
|
|
321
|
+
{
|
|
322
|
+
int c = fileGetc ();
|
|
323
|
+
if (c == EOF)
|
|
324
|
+
end = TRUE;
|
|
325
|
+
else if (c == '\\')
|
|
326
|
+
{
|
|
327
|
+
c = fileGetc(); /* This maybe a ' or ". */
|
|
328
|
+
vStringPut(string, c);
|
|
329
|
+
}
|
|
330
|
+
else if (c == delimiter)
|
|
331
|
+
end = TRUE;
|
|
332
|
+
else
|
|
333
|
+
vStringPut (string, c);
|
|
334
|
+
}
|
|
335
|
+
vStringTerminate (string);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* Read a C identifier beginning with "firstChar" and places it into
|
|
339
|
+
* "name".
|
|
340
|
+
*/
|
|
341
|
+
static void parseIdentifier (vString *const string, const int firstChar)
|
|
342
|
+
{
|
|
343
|
+
int c = firstChar;
|
|
344
|
+
Assert (isIdentChar (c));
|
|
345
|
+
do
|
|
346
|
+
{
|
|
347
|
+
vStringPut (string, c);
|
|
348
|
+
c = fileGetc ();
|
|
349
|
+
} while (isIdentChar (c));
|
|
350
|
+
vStringTerminate (string);
|
|
351
|
+
if (!isspace (c))
|
|
352
|
+
fileUngetc (c); /* unget non-identifier character */
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
static void readToken (tokenInfo *const token)
|
|
356
|
+
{
|
|
357
|
+
int c;
|
|
358
|
+
|
|
359
|
+
token->type = TOKEN_UNDEFINED;
|
|
360
|
+
token->keyword = KEYWORD_NONE;
|
|
361
|
+
vStringClear (token->string);
|
|
362
|
+
|
|
363
|
+
getNextChar:
|
|
364
|
+
do
|
|
365
|
+
{
|
|
366
|
+
c = fileGetc ();
|
|
367
|
+
token->lineNumber = getSourceLineNumber ();
|
|
368
|
+
token->filePosition = getInputFilePosition ();
|
|
369
|
+
}
|
|
370
|
+
while (c == '\t' || c == ' ' || c == '\n');
|
|
371
|
+
|
|
372
|
+
switch (c)
|
|
373
|
+
{
|
|
374
|
+
case EOF: longjmp (Exception, (int)ExceptionEOF); break;
|
|
375
|
+
case '(': token->type = TOKEN_OPEN_PAREN; break;
|
|
376
|
+
case ')': token->type = TOKEN_CLOSE_PAREN; break;
|
|
377
|
+
case ';': token->type = TOKEN_SEMICOLON; break;
|
|
378
|
+
case ',': token->type = TOKEN_COMMA; break;
|
|
379
|
+
case '.': token->type = TOKEN_PERIOD; break;
|
|
380
|
+
case ':': token->type = TOKEN_COLON; break;
|
|
381
|
+
case '{': token->type = TOKEN_OPEN_CURLY; break;
|
|
382
|
+
case '}': token->type = TOKEN_CLOSE_CURLY; break;
|
|
383
|
+
case '=': token->type = TOKEN_EQUAL_SIGN; break;
|
|
384
|
+
case '[': token->type = TOKEN_OPEN_SQUARE; break;
|
|
385
|
+
case ']': token->type = TOKEN_CLOSE_SQUARE; break;
|
|
386
|
+
|
|
387
|
+
case '\'':
|
|
388
|
+
case '"':
|
|
389
|
+
token->type = TOKEN_STRING;
|
|
390
|
+
parseString (token->string, c);
|
|
391
|
+
token->lineNumber = getSourceLineNumber ();
|
|
392
|
+
token->filePosition = getInputFilePosition ();
|
|
393
|
+
break;
|
|
394
|
+
|
|
395
|
+
case '\\':
|
|
396
|
+
c = fileGetc ();
|
|
397
|
+
if (c != '\\' && c != '"' && !isspace (c))
|
|
398
|
+
fileUngetc (c);
|
|
399
|
+
token->type = TOKEN_CHARACTER;
|
|
400
|
+
token->lineNumber = getSourceLineNumber ();
|
|
401
|
+
token->filePosition = getInputFilePosition ();
|
|
402
|
+
break;
|
|
403
|
+
|
|
404
|
+
case '/':
|
|
405
|
+
{
|
|
406
|
+
int d = fileGetc ();
|
|
407
|
+
if ( (d != '*') && /* is this the start of a comment? */
|
|
408
|
+
(d != '/') ) /* is a one line comment? */
|
|
409
|
+
{
|
|
410
|
+
token->type = TOKEN_FORWARD_SLASH;
|
|
411
|
+
fileUngetc (d);
|
|
412
|
+
}
|
|
413
|
+
else
|
|
414
|
+
{
|
|
415
|
+
if (d == '*')
|
|
416
|
+
{
|
|
417
|
+
do
|
|
418
|
+
{
|
|
419
|
+
fileSkipToCharacter ('*');
|
|
420
|
+
c = fileGetc ();
|
|
421
|
+
if (c == '/')
|
|
422
|
+
break;
|
|
423
|
+
else
|
|
424
|
+
fileUngetc (c);
|
|
425
|
+
} while (c != EOF && c != '\0');
|
|
426
|
+
goto getNextChar;
|
|
427
|
+
}
|
|
428
|
+
else if (d == '/') /* is this the start of a comment? */
|
|
429
|
+
{
|
|
430
|
+
fileSkipToCharacter ('\n');
|
|
431
|
+
goto getNextChar;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
break;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
default:
|
|
438
|
+
if (! isIdentChar (c))
|
|
439
|
+
token->type = TOKEN_UNDEFINED;
|
|
440
|
+
else
|
|
441
|
+
{
|
|
442
|
+
parseIdentifier (token->string, c);
|
|
443
|
+
token->lineNumber = getSourceLineNumber ();
|
|
444
|
+
token->filePosition = getInputFilePosition ();
|
|
445
|
+
token->keyword = analyzeToken (token->string, Lang_js);
|
|
446
|
+
if (isKeyword (token, KEYWORD_NONE))
|
|
447
|
+
token->type = TOKEN_IDENTIFIER;
|
|
448
|
+
else
|
|
449
|
+
token->type = TOKEN_KEYWORD;
|
|
450
|
+
}
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
static void copyToken (tokenInfo *const dest, tokenInfo *const src)
|
|
456
|
+
{
|
|
457
|
+
dest->nestLevel = src->nestLevel;
|
|
458
|
+
dest->lineNumber = src->lineNumber;
|
|
459
|
+
dest->filePosition = src->filePosition;
|
|
460
|
+
dest->type = src->type;
|
|
461
|
+
dest->keyword = src->keyword;
|
|
462
|
+
vStringCopy(dest->string, src->string);
|
|
463
|
+
vStringCopy(dest->scope, src->scope);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/*
|
|
467
|
+
* Token parsing functions
|
|
468
|
+
*/
|
|
469
|
+
|
|
470
|
+
static void skipArgumentList (tokenInfo *const token)
|
|
471
|
+
{
|
|
472
|
+
int nest_level = 0;
|
|
473
|
+
|
|
474
|
+
/*
|
|
475
|
+
* Other databases can have arguments with fully declared
|
|
476
|
+
* datatypes:
|
|
477
|
+
* ( name varchar(30), text binary(10) )
|
|
478
|
+
* So we must check for nested open and closing parantheses
|
|
479
|
+
*/
|
|
480
|
+
|
|
481
|
+
if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
|
|
482
|
+
{
|
|
483
|
+
nest_level++;
|
|
484
|
+
while (! (isType (token, TOKEN_CLOSE_PAREN) && (nest_level == 0)))
|
|
485
|
+
{
|
|
486
|
+
readToken (token);
|
|
487
|
+
if (isType (token, TOKEN_OPEN_PAREN))
|
|
488
|
+
{
|
|
489
|
+
nest_level++;
|
|
490
|
+
}
|
|
491
|
+
if (isType (token, TOKEN_CLOSE_PAREN))
|
|
492
|
+
{
|
|
493
|
+
if (nest_level > 0)
|
|
494
|
+
{
|
|
495
|
+
nest_level--;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
readToken (token);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
static void skipArrayList (tokenInfo *const token)
|
|
504
|
+
{
|
|
505
|
+
int nest_level = 0;
|
|
506
|
+
|
|
507
|
+
/*
|
|
508
|
+
* Handle square brackets
|
|
509
|
+
* var name[1]
|
|
510
|
+
* So we must check for nested open and closing square brackets
|
|
511
|
+
*/
|
|
512
|
+
|
|
513
|
+
if (isType (token, TOKEN_OPEN_SQUARE)) /* arguments? */
|
|
514
|
+
{
|
|
515
|
+
nest_level++;
|
|
516
|
+
while (! (isType (token, TOKEN_CLOSE_SQUARE) && (nest_level == 0)))
|
|
517
|
+
{
|
|
518
|
+
readToken (token);
|
|
519
|
+
if (isType (token, TOKEN_OPEN_SQUARE))
|
|
520
|
+
{
|
|
521
|
+
nest_level++;
|
|
522
|
+
}
|
|
523
|
+
if (isType (token, TOKEN_CLOSE_SQUARE))
|
|
524
|
+
{
|
|
525
|
+
if (nest_level > 0)
|
|
526
|
+
{
|
|
527
|
+
nest_level--;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
readToken (token);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
static void addContext (tokenInfo* const parent, const tokenInfo* const child)
|
|
536
|
+
{
|
|
537
|
+
if (vStringLength (parent->string) > 0)
|
|
538
|
+
{
|
|
539
|
+
vStringCatS (parent->string, ".");
|
|
540
|
+
}
|
|
541
|
+
vStringCatS (parent->string, vStringValue(child->string));
|
|
542
|
+
vStringTerminate(parent->string);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
static void addToScope (tokenInfo* const token, vString* const extra)
|
|
546
|
+
{
|
|
547
|
+
if (vStringLength (token->scope) > 0)
|
|
548
|
+
{
|
|
549
|
+
vStringCatS (token->scope, ".");
|
|
550
|
+
}
|
|
551
|
+
vStringCatS (token->scope, vStringValue(extra));
|
|
552
|
+
vStringTerminate(token->scope);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/*
|
|
556
|
+
* Scanning functions
|
|
557
|
+
*/
|
|
558
|
+
|
|
559
|
+
static void findCmdTerm (tokenInfo *const token)
|
|
560
|
+
{
|
|
561
|
+
/*
|
|
562
|
+
* Read until we find either a semicolon or closing brace.
|
|
563
|
+
* Any nested braces will be handled within.
|
|
564
|
+
*/
|
|
565
|
+
while (! ( isType (token, TOKEN_SEMICOLON) ||
|
|
566
|
+
isType (token, TOKEN_CLOSE_CURLY) ) )
|
|
567
|
+
{
|
|
568
|
+
/* Handle nested blocks */
|
|
569
|
+
if ( isType (token, TOKEN_OPEN_CURLY))
|
|
570
|
+
{
|
|
571
|
+
parseBlock (token, token);
|
|
572
|
+
}
|
|
573
|
+
else if ( isType (token, TOKEN_OPEN_PAREN) )
|
|
574
|
+
{
|
|
575
|
+
skipArgumentList(token);
|
|
576
|
+
}
|
|
577
|
+
else
|
|
578
|
+
{
|
|
579
|
+
readToken (token);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
static void parseSwitch (tokenInfo *const token)
|
|
585
|
+
{
|
|
586
|
+
/*
|
|
587
|
+
* switch (expression){
|
|
588
|
+
* case value1:
|
|
589
|
+
* statement;
|
|
590
|
+
* break;
|
|
591
|
+
* case value2:
|
|
592
|
+
* statement;
|
|
593
|
+
* break;
|
|
594
|
+
* default : statement;
|
|
595
|
+
* }
|
|
596
|
+
*/
|
|
597
|
+
|
|
598
|
+
readToken (token);
|
|
599
|
+
|
|
600
|
+
if (isType (token, TOKEN_OPEN_PAREN))
|
|
601
|
+
{
|
|
602
|
+
/*
|
|
603
|
+
* Handle nameless functions, these will only
|
|
604
|
+
* be considered methods.
|
|
605
|
+
*/
|
|
606
|
+
skipArgumentList(token);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
610
|
+
{
|
|
611
|
+
/*
|
|
612
|
+
* This will be either a function or a class.
|
|
613
|
+
* We can only determine this by checking the body
|
|
614
|
+
* of the function. If we find a "this." we know
|
|
615
|
+
* it is a class, otherwise it is a function.
|
|
616
|
+
*/
|
|
617
|
+
parseBlock (token, token);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
static void parseLoop (tokenInfo *const token)
|
|
623
|
+
{
|
|
624
|
+
/*
|
|
625
|
+
* Handles these statements
|
|
626
|
+
* for (x=0; x<3; x++)
|
|
627
|
+
* document.write("This text is repeated three times<br>");
|
|
628
|
+
*
|
|
629
|
+
* for (x=0; x<3; x++)
|
|
630
|
+
* {
|
|
631
|
+
* document.write("This text is repeated three times<br>");
|
|
632
|
+
* }
|
|
633
|
+
*
|
|
634
|
+
* while (number<5){
|
|
635
|
+
* document.write(number+"<br>");
|
|
636
|
+
* number++;
|
|
637
|
+
* }
|
|
638
|
+
*
|
|
639
|
+
* do{
|
|
640
|
+
* document.write(number+"<br>");
|
|
641
|
+
* number++;
|
|
642
|
+
* }
|
|
643
|
+
* while (number<5);
|
|
644
|
+
*/
|
|
645
|
+
|
|
646
|
+
if (isKeyword (token, KEYWORD_for) || isKeyword (token, KEYWORD_while))
|
|
647
|
+
{
|
|
648
|
+
readToken(token);
|
|
649
|
+
|
|
650
|
+
if (isType (token, TOKEN_OPEN_PAREN))
|
|
651
|
+
{
|
|
652
|
+
/*
|
|
653
|
+
* Handle nameless functions, these will only
|
|
654
|
+
* be considered methods.
|
|
655
|
+
*/
|
|
656
|
+
skipArgumentList(token);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
660
|
+
{
|
|
661
|
+
/*
|
|
662
|
+
* This will be either a function or a class.
|
|
663
|
+
* We can only determine this by checking the body
|
|
664
|
+
* of the function. If we find a "this." we know
|
|
665
|
+
* it is a class, otherwise it is a function.
|
|
666
|
+
*/
|
|
667
|
+
parseBlock (token, token);
|
|
668
|
+
}
|
|
669
|
+
else
|
|
670
|
+
{
|
|
671
|
+
parseLine(token, FALSE);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else if (isKeyword (token, KEYWORD_do))
|
|
675
|
+
{
|
|
676
|
+
readToken(token);
|
|
677
|
+
|
|
678
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
679
|
+
{
|
|
680
|
+
/*
|
|
681
|
+
* This will be either a function or a class.
|
|
682
|
+
* We can only determine this by checking the body
|
|
683
|
+
* of the function. If we find a "this." we know
|
|
684
|
+
* it is a class, otherwise it is a function.
|
|
685
|
+
*/
|
|
686
|
+
parseBlock (token, token);
|
|
687
|
+
}
|
|
688
|
+
else
|
|
689
|
+
{
|
|
690
|
+
parseLine(token, FALSE);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
readToken(token);
|
|
694
|
+
|
|
695
|
+
if (isKeyword (token, KEYWORD_while))
|
|
696
|
+
{
|
|
697
|
+
readToken(token);
|
|
698
|
+
|
|
699
|
+
if (isType (token, TOKEN_OPEN_PAREN))
|
|
700
|
+
{
|
|
701
|
+
/*
|
|
702
|
+
* Handle nameless functions, these will only
|
|
703
|
+
* be considered methods.
|
|
704
|
+
*/
|
|
705
|
+
skipArgumentList(token);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
static boolean parseIf (tokenInfo *const token)
|
|
712
|
+
{
|
|
713
|
+
boolean read_next_token = TRUE;
|
|
714
|
+
/*
|
|
715
|
+
* If statements have two forms
|
|
716
|
+
* if ( ... )
|
|
717
|
+
* one line;
|
|
718
|
+
*
|
|
719
|
+
* if ( ... )
|
|
720
|
+
* statement;
|
|
721
|
+
* else
|
|
722
|
+
* statement
|
|
723
|
+
*
|
|
724
|
+
* if ( ... ) {
|
|
725
|
+
* multiple;
|
|
726
|
+
* statements;
|
|
727
|
+
* }
|
|
728
|
+
*
|
|
729
|
+
*
|
|
730
|
+
* if ( ... ) {
|
|
731
|
+
* return elem
|
|
732
|
+
* }
|
|
733
|
+
*
|
|
734
|
+
* This example if correctly written, but the
|
|
735
|
+
* else contains only 1 statement without a terminator
|
|
736
|
+
* since the function finishes with the closing brace.
|
|
737
|
+
*
|
|
738
|
+
* function a(flag){
|
|
739
|
+
* if(flag)
|
|
740
|
+
* test(1);
|
|
741
|
+
* else
|
|
742
|
+
* test(2)
|
|
743
|
+
* }
|
|
744
|
+
*
|
|
745
|
+
* TODO: Deal with statements that can optional end
|
|
746
|
+
* without a semi-colon. Currently this messes up
|
|
747
|
+
* the parsing of blocks.
|
|
748
|
+
* Need to somehow detect this has happened, and either
|
|
749
|
+
* backup a token, or skip reading the next token if
|
|
750
|
+
* that is possible from all code locations.
|
|
751
|
+
*
|
|
752
|
+
*/
|
|
753
|
+
|
|
754
|
+
readToken (token);
|
|
755
|
+
|
|
756
|
+
if (isKeyword (token, KEYWORD_if))
|
|
757
|
+
{
|
|
758
|
+
/*
|
|
759
|
+
* Check for an "else if" and consume the "if"
|
|
760
|
+
*/
|
|
761
|
+
readToken (token);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (isType (token, TOKEN_OPEN_PAREN))
|
|
765
|
+
{
|
|
766
|
+
/*
|
|
767
|
+
* Handle nameless functions, these will only
|
|
768
|
+
* be considered methods.
|
|
769
|
+
*/
|
|
770
|
+
skipArgumentList(token);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
774
|
+
{
|
|
775
|
+
/*
|
|
776
|
+
* This will be either a function or a class.
|
|
777
|
+
* We can only determine this by checking the body
|
|
778
|
+
* of the function. If we find a "this." we know
|
|
779
|
+
* it is a class, otherwise it is a function.
|
|
780
|
+
*/
|
|
781
|
+
parseBlock (token, token);
|
|
782
|
+
}
|
|
783
|
+
else
|
|
784
|
+
{
|
|
785
|
+
findCmdTerm (token);
|
|
786
|
+
|
|
787
|
+
/*
|
|
788
|
+
* The IF could be followed by an ELSE statement.
|
|
789
|
+
* This too could have two formats, a curly braced
|
|
790
|
+
* multiline section, or another single line.
|
|
791
|
+
*/
|
|
792
|
+
|
|
793
|
+
if (isType (token, TOKEN_CLOSE_CURLY))
|
|
794
|
+
{
|
|
795
|
+
/*
|
|
796
|
+
* This statement did not have a line terminator.
|
|
797
|
+
*/
|
|
798
|
+
read_next_token = FALSE;
|
|
799
|
+
}
|
|
800
|
+
else
|
|
801
|
+
{
|
|
802
|
+
readToken (token);
|
|
803
|
+
|
|
804
|
+
if (isType (token, TOKEN_CLOSE_CURLY))
|
|
805
|
+
{
|
|
806
|
+
/*
|
|
807
|
+
* This statement did not have a line terminator.
|
|
808
|
+
*/
|
|
809
|
+
read_next_token = FALSE;
|
|
810
|
+
}
|
|
811
|
+
else
|
|
812
|
+
{
|
|
813
|
+
if (isKeyword (token, KEYWORD_else))
|
|
814
|
+
read_next_token = parseIf (token);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return read_next_token;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
static void parseFunction (tokenInfo *const token)
|
|
822
|
+
{
|
|
823
|
+
tokenInfo *const name = newToken ();
|
|
824
|
+
boolean is_class = FALSE;
|
|
825
|
+
|
|
826
|
+
/*
|
|
827
|
+
* This deals with these formats
|
|
828
|
+
* function validFunctionTwo(a,b) {}
|
|
829
|
+
*/
|
|
830
|
+
|
|
831
|
+
readToken (name);
|
|
832
|
+
/* Add scope in case this is an INNER function */
|
|
833
|
+
addToScope(name, token->scope);
|
|
834
|
+
|
|
835
|
+
readToken (token);
|
|
836
|
+
if (isType (token, TOKEN_PERIOD))
|
|
837
|
+
{
|
|
838
|
+
do
|
|
839
|
+
{
|
|
840
|
+
readToken (token);
|
|
841
|
+
if ( isKeyword(token, KEYWORD_NONE) )
|
|
842
|
+
{
|
|
843
|
+
addContext (name, token);
|
|
844
|
+
readToken (token);
|
|
845
|
+
}
|
|
846
|
+
} while (isType (token, TOKEN_PERIOD));
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if ( isType (token, TOKEN_OPEN_PAREN) )
|
|
850
|
+
skipArgumentList(token);
|
|
851
|
+
|
|
852
|
+
if ( isType (token, TOKEN_OPEN_CURLY) )
|
|
853
|
+
{
|
|
854
|
+
is_class = parseBlock (token, name);
|
|
855
|
+
if ( is_class )
|
|
856
|
+
makeClassTag (name);
|
|
857
|
+
else
|
|
858
|
+
makeFunctionTag (name);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
findCmdTerm (token);
|
|
862
|
+
|
|
863
|
+
deleteToken (name);
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
static boolean parseBlock (tokenInfo *const token, tokenInfo *const parent)
|
|
867
|
+
{
|
|
868
|
+
boolean is_class = FALSE;
|
|
869
|
+
boolean read_next_token = TRUE;
|
|
870
|
+
vString * saveScope = vStringNew ();
|
|
871
|
+
|
|
872
|
+
token->nestLevel++;
|
|
873
|
+
/*
|
|
874
|
+
* Make this routine a bit more forgiving.
|
|
875
|
+
* If called on an open_curly advance it
|
|
876
|
+
*/
|
|
877
|
+
if ( isType (token, TOKEN_OPEN_CURLY) &&
|
|
878
|
+
isKeyword(token, KEYWORD_NONE) )
|
|
879
|
+
readToken(token);
|
|
880
|
+
|
|
881
|
+
if (! isType (token, TOKEN_CLOSE_CURLY))
|
|
882
|
+
{
|
|
883
|
+
/*
|
|
884
|
+
* Read until we find the closing brace,
|
|
885
|
+
* any nested braces will be handled within
|
|
886
|
+
*/
|
|
887
|
+
do
|
|
888
|
+
{
|
|
889
|
+
read_next_token = TRUE;
|
|
890
|
+
if (isKeyword (token, KEYWORD_this))
|
|
891
|
+
{
|
|
892
|
+
/*
|
|
893
|
+
* Means we are inside a class and have found
|
|
894
|
+
* a class, not a function
|
|
895
|
+
*/
|
|
896
|
+
is_class = TRUE;
|
|
897
|
+
vStringCopy(saveScope, token->scope);
|
|
898
|
+
addToScope (token, parent->string);
|
|
899
|
+
|
|
900
|
+
/*
|
|
901
|
+
* Ignore the remainder of the line
|
|
902
|
+
* findCmdTerm(token);
|
|
903
|
+
*/
|
|
904
|
+
parseLine (token, is_class);
|
|
905
|
+
|
|
906
|
+
vStringCopy(token->scope, saveScope);
|
|
907
|
+
}
|
|
908
|
+
else if (isKeyword (token, KEYWORD_var))
|
|
909
|
+
{
|
|
910
|
+
/*
|
|
911
|
+
* Potentially we have found an inner function.
|
|
912
|
+
* Set something to indicate the scope
|
|
913
|
+
*/
|
|
914
|
+
vStringCopy(saveScope, token->scope);
|
|
915
|
+
addToScope (token, parent->string);
|
|
916
|
+
parseLine (token, is_class);
|
|
917
|
+
vStringCopy(token->scope, saveScope);
|
|
918
|
+
}
|
|
919
|
+
else if (isKeyword (token, KEYWORD_function))
|
|
920
|
+
{
|
|
921
|
+
vStringCopy(saveScope, token->scope);
|
|
922
|
+
addToScope (token, parent->string);
|
|
923
|
+
parseFunction (token);
|
|
924
|
+
vStringCopy(token->scope, saveScope);
|
|
925
|
+
}
|
|
926
|
+
else if (isType (token, TOKEN_OPEN_CURLY))
|
|
927
|
+
{
|
|
928
|
+
/* Handle nested blocks */
|
|
929
|
+
parseBlock (token, parent);
|
|
930
|
+
}
|
|
931
|
+
else
|
|
932
|
+
{
|
|
933
|
+
/*
|
|
934
|
+
* It is possible for a line to have no terminator
|
|
935
|
+
* if the following line is a closing brace.
|
|
936
|
+
* parseLine will detect this case and indicate
|
|
937
|
+
* whether we should read an additional token.
|
|
938
|
+
*/
|
|
939
|
+
read_next_token = parseLine (token, is_class);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/*
|
|
943
|
+
* Always read a new token unless we find a statement without
|
|
944
|
+
* a ending terminator
|
|
945
|
+
*/
|
|
946
|
+
if( read_next_token )
|
|
947
|
+
readToken(token);
|
|
948
|
+
|
|
949
|
+
/*
|
|
950
|
+
* If we find a statement without a terminator consider the
|
|
951
|
+
* block finished, otherwise the stack will be off by one.
|
|
952
|
+
*/
|
|
953
|
+
} while (! isType (token, TOKEN_CLOSE_CURLY) && read_next_token );
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
vStringDelete(saveScope);
|
|
957
|
+
token->nestLevel--;
|
|
958
|
+
|
|
959
|
+
return is_class;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
static void parseMethods (tokenInfo *const token, tokenInfo *const class)
|
|
963
|
+
{
|
|
964
|
+
tokenInfo *const name = newToken ();
|
|
965
|
+
|
|
966
|
+
/*
|
|
967
|
+
* This deals with these formats
|
|
968
|
+
* validProperty : 2,
|
|
969
|
+
* validMethod : function(a,b) {}
|
|
970
|
+
* 'validMethod2' : function(a,b) {}
|
|
971
|
+
* container.dirtyTab = {'url': false, 'title':false, 'snapshot':false, '*': false}
|
|
972
|
+
*/
|
|
973
|
+
|
|
974
|
+
do
|
|
975
|
+
{
|
|
976
|
+
readToken (token);
|
|
977
|
+
if (isType (token, TOKEN_STRING) || isKeyword(token, KEYWORD_NONE))
|
|
978
|
+
{
|
|
979
|
+
copyToken(name, token);
|
|
980
|
+
|
|
981
|
+
readToken (token);
|
|
982
|
+
if ( isType (token, TOKEN_COLON) )
|
|
983
|
+
{
|
|
984
|
+
readToken (token);
|
|
985
|
+
if ( isKeyword (token, KEYWORD_function) )
|
|
986
|
+
{
|
|
987
|
+
readToken (token);
|
|
988
|
+
if ( isType (token, TOKEN_OPEN_PAREN) )
|
|
989
|
+
{
|
|
990
|
+
skipArgumentList(token);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
994
|
+
{
|
|
995
|
+
addToScope (name, class->string);
|
|
996
|
+
makeJsTag (name, JSTAG_METHOD);
|
|
997
|
+
parseBlock (token, name);
|
|
998
|
+
|
|
999
|
+
/*
|
|
1000
|
+
* Read to the closing curly, check next
|
|
1001
|
+
* token, if a comma, we must loop again
|
|
1002
|
+
*/
|
|
1003
|
+
readToken (token);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
else
|
|
1007
|
+
{
|
|
1008
|
+
addToScope (name, class->string);
|
|
1009
|
+
makeJsTag (name, JSTAG_PROPERTY);
|
|
1010
|
+
|
|
1011
|
+
/*
|
|
1012
|
+
* Read the next token, if a comma
|
|
1013
|
+
* we must loop again
|
|
1014
|
+
*/
|
|
1015
|
+
readToken (token);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
} while ( isType(token, TOKEN_COMMA) );
|
|
1020
|
+
|
|
1021
|
+
findCmdTerm (token);
|
|
1022
|
+
|
|
1023
|
+
deleteToken (name);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
static boolean parseStatement (tokenInfo *const token, boolean is_inside_class)
|
|
1027
|
+
{
|
|
1028
|
+
tokenInfo *const name = newToken ();
|
|
1029
|
+
tokenInfo *const secondary_name = newToken ();
|
|
1030
|
+
vString * saveScope = vStringNew ();
|
|
1031
|
+
boolean is_class = FALSE;
|
|
1032
|
+
boolean is_terminated = TRUE;
|
|
1033
|
+
boolean is_global = FALSE;
|
|
1034
|
+
boolean is_prototype = FALSE;
|
|
1035
|
+
vString * fulltag;
|
|
1036
|
+
|
|
1037
|
+
vStringClear(saveScope);
|
|
1038
|
+
/*
|
|
1039
|
+
* Functions can be named or unnamed.
|
|
1040
|
+
* This deals with these formats:
|
|
1041
|
+
* Function
|
|
1042
|
+
* validFunctionOne = function(a,b) {}
|
|
1043
|
+
* testlib.validFunctionFive = function(a,b) {}
|
|
1044
|
+
* var innerThree = function(a,b) {}
|
|
1045
|
+
* var innerFour = (a,b) {}
|
|
1046
|
+
* var D2 = secondary_fcn_name(a,b) {}
|
|
1047
|
+
* var D3 = new Function("a", "b", "return a+b;");
|
|
1048
|
+
* Class
|
|
1049
|
+
* testlib.extras.ValidClassOne = function(a,b) {
|
|
1050
|
+
* this.a = a;
|
|
1051
|
+
* }
|
|
1052
|
+
* Class Methods
|
|
1053
|
+
* testlib.extras.ValidClassOne.prototype = {
|
|
1054
|
+
* 'validMethodOne' : function(a,b) {},
|
|
1055
|
+
* 'validMethodTwo' : function(a,b) {}
|
|
1056
|
+
* }
|
|
1057
|
+
* ValidClassTwo = function ()
|
|
1058
|
+
* {
|
|
1059
|
+
* this.validMethodThree = function() {}
|
|
1060
|
+
* // unnamed method
|
|
1061
|
+
* this.validMethodFour = () {}
|
|
1062
|
+
* }
|
|
1063
|
+
* Database.prototype.validMethodThree = Database_getTodaysDate;
|
|
1064
|
+
*/
|
|
1065
|
+
|
|
1066
|
+
if ( is_inside_class )
|
|
1067
|
+
is_class = TRUE;
|
|
1068
|
+
/*
|
|
1069
|
+
* var can preceed an inner function
|
|
1070
|
+
*/
|
|
1071
|
+
if ( isKeyword(token, KEYWORD_var) )
|
|
1072
|
+
{
|
|
1073
|
+
/*
|
|
1074
|
+
* Only create variables for global scope
|
|
1075
|
+
*/
|
|
1076
|
+
if ( token->nestLevel == 0 )
|
|
1077
|
+
{
|
|
1078
|
+
is_global = TRUE;
|
|
1079
|
+
}
|
|
1080
|
+
readToken(token);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if ( isKeyword(token, KEYWORD_this) )
|
|
1084
|
+
{
|
|
1085
|
+
readToken(token);
|
|
1086
|
+
if (isType (token, TOKEN_PERIOD))
|
|
1087
|
+
{
|
|
1088
|
+
readToken(token);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
copyToken(name, token);
|
|
1093
|
+
|
|
1094
|
+
while (! isType (token, TOKEN_CLOSE_CURLY) &&
|
|
1095
|
+
! isType (token, TOKEN_SEMICOLON) &&
|
|
1096
|
+
! isType (token, TOKEN_EQUAL_SIGN) )
|
|
1097
|
+
{
|
|
1098
|
+
/* Potentially the name of the function */
|
|
1099
|
+
readToken (token);
|
|
1100
|
+
if (isType (token, TOKEN_PERIOD))
|
|
1101
|
+
{
|
|
1102
|
+
/*
|
|
1103
|
+
* Cannot be a global variable is it has dot references in the name
|
|
1104
|
+
*/
|
|
1105
|
+
is_global = FALSE;
|
|
1106
|
+
do
|
|
1107
|
+
{
|
|
1108
|
+
readToken (token);
|
|
1109
|
+
if ( isKeyword(token, KEYWORD_NONE) )
|
|
1110
|
+
{
|
|
1111
|
+
if ( is_class )
|
|
1112
|
+
{
|
|
1113
|
+
vStringCopy(saveScope, token->scope);
|
|
1114
|
+
addToScope(token, name->string);
|
|
1115
|
+
}
|
|
1116
|
+
else
|
|
1117
|
+
addContext (name, token);
|
|
1118
|
+
}
|
|
1119
|
+
else if ( isKeyword(token, KEYWORD_prototype) )
|
|
1120
|
+
{
|
|
1121
|
+
/*
|
|
1122
|
+
* When we reach the "prototype" tag, we infer:
|
|
1123
|
+
* "BindAgent" is a class
|
|
1124
|
+
* "build" is a method
|
|
1125
|
+
*
|
|
1126
|
+
* function BindAgent( repeatableIdName, newParentIdName ) {
|
|
1127
|
+
* }
|
|
1128
|
+
*
|
|
1129
|
+
* CASE 1
|
|
1130
|
+
* Specified function name: "build"
|
|
1131
|
+
* BindAgent.prototype.build = function( mode ) {
|
|
1132
|
+
* ignore everything within this function
|
|
1133
|
+
* }
|
|
1134
|
+
*
|
|
1135
|
+
* CASE 2
|
|
1136
|
+
* Prototype listing
|
|
1137
|
+
* ValidClassOne.prototype = {
|
|
1138
|
+
* 'validMethodOne' : function(a,b) {},
|
|
1139
|
+
* 'validMethodTwo' : function(a,b) {}
|
|
1140
|
+
* }
|
|
1141
|
+
*
|
|
1142
|
+
*/
|
|
1143
|
+
makeClassTag (name);
|
|
1144
|
+
is_class = TRUE;
|
|
1145
|
+
is_prototype = TRUE;
|
|
1146
|
+
|
|
1147
|
+
/*
|
|
1148
|
+
* There should a ".function_name" next.
|
|
1149
|
+
*/
|
|
1150
|
+
readToken (token);
|
|
1151
|
+
if (isType (token, TOKEN_PERIOD))
|
|
1152
|
+
{
|
|
1153
|
+
/*
|
|
1154
|
+
* Handle CASE 1
|
|
1155
|
+
*/
|
|
1156
|
+
readToken (token);
|
|
1157
|
+
if ( isKeyword(token, KEYWORD_NONE) )
|
|
1158
|
+
{
|
|
1159
|
+
vStringCopy(saveScope, token->scope);
|
|
1160
|
+
addToScope(token, name->string);
|
|
1161
|
+
|
|
1162
|
+
makeJsTag (token, JSTAG_METHOD);
|
|
1163
|
+
/*
|
|
1164
|
+
* We can read until the end of the block / statement.
|
|
1165
|
+
* We need to correctly parse any nested blocks, but
|
|
1166
|
+
* we do NOT want to create any tags based on what is
|
|
1167
|
+
* within the blocks.
|
|
1168
|
+
*/
|
|
1169
|
+
token->ignoreTag = TRUE;
|
|
1170
|
+
/*
|
|
1171
|
+
* Find to the end of the statement
|
|
1172
|
+
*/
|
|
1173
|
+
findCmdTerm (token);
|
|
1174
|
+
token->ignoreTag = FALSE;
|
|
1175
|
+
is_terminated = TRUE;
|
|
1176
|
+
goto cleanUp;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
else if (isType (token, TOKEN_EQUAL_SIGN))
|
|
1180
|
+
{
|
|
1181
|
+
readToken (token);
|
|
1182
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
1183
|
+
{
|
|
1184
|
+
/*
|
|
1185
|
+
* Handle CASE 2
|
|
1186
|
+
*
|
|
1187
|
+
* Creates tags for each of these class methods
|
|
1188
|
+
* ValidClassOne.prototype = {
|
|
1189
|
+
* 'validMethodOne' : function(a,b) {},
|
|
1190
|
+
* 'validMethodTwo' : function(a,b) {}
|
|
1191
|
+
* }
|
|
1192
|
+
*/
|
|
1193
|
+
parseMethods(token, name);
|
|
1194
|
+
/*
|
|
1195
|
+
* Find to the end of the statement
|
|
1196
|
+
*/
|
|
1197
|
+
findCmdTerm (token);
|
|
1198
|
+
token->ignoreTag = FALSE;
|
|
1199
|
+
is_terminated = TRUE;
|
|
1200
|
+
goto cleanUp;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
readToken (token);
|
|
1205
|
+
} while (isType (token, TOKEN_PERIOD));
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if ( isType (token, TOKEN_OPEN_PAREN) )
|
|
1209
|
+
skipArgumentList(token);
|
|
1210
|
+
|
|
1211
|
+
if ( isType (token, TOKEN_OPEN_SQUARE) )
|
|
1212
|
+
skipArrayList(token);
|
|
1213
|
+
|
|
1214
|
+
/*
|
|
1215
|
+
if ( isType (token, TOKEN_OPEN_CURLY) )
|
|
1216
|
+
{
|
|
1217
|
+
is_class = parseBlock (token, name);
|
|
1218
|
+
}
|
|
1219
|
+
*/
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
if ( isType (token, TOKEN_CLOSE_CURLY) )
|
|
1223
|
+
{
|
|
1224
|
+
/*
|
|
1225
|
+
* Reaching this section without having
|
|
1226
|
+
* processed an open curly brace indicates
|
|
1227
|
+
* the statement is most likely not terminated.
|
|
1228
|
+
*/
|
|
1229
|
+
is_terminated = FALSE;
|
|
1230
|
+
goto cleanUp;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
if ( isType (token, TOKEN_SEMICOLON) )
|
|
1234
|
+
{
|
|
1235
|
+
/*
|
|
1236
|
+
* Only create variables for global scope
|
|
1237
|
+
*/
|
|
1238
|
+
if ( token->nestLevel == 0 && is_global )
|
|
1239
|
+
{
|
|
1240
|
+
/*
|
|
1241
|
+
* Handles this syntax:
|
|
1242
|
+
* var g_var2;
|
|
1243
|
+
*/
|
|
1244
|
+
if (isType (token, TOKEN_SEMICOLON))
|
|
1245
|
+
makeJsTag (name, JSTAG_VARIABLE);
|
|
1246
|
+
}
|
|
1247
|
+
/*
|
|
1248
|
+
* Statement has ended.
|
|
1249
|
+
* This deals with calls to functions, like:
|
|
1250
|
+
* alert(..);
|
|
1251
|
+
*/
|
|
1252
|
+
goto cleanUp;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
if ( isType (token, TOKEN_EQUAL_SIGN) )
|
|
1256
|
+
{
|
|
1257
|
+
readToken (token);
|
|
1258
|
+
|
|
1259
|
+
if ( isKeyword (token, KEYWORD_function) )
|
|
1260
|
+
{
|
|
1261
|
+
readToken (token);
|
|
1262
|
+
|
|
1263
|
+
if ( isKeyword (token, KEYWORD_NONE) &&
|
|
1264
|
+
! isType (token, TOKEN_OPEN_PAREN) )
|
|
1265
|
+
{
|
|
1266
|
+
/*
|
|
1267
|
+
* Functions of this format:
|
|
1268
|
+
* var D2A = function theAdd(a, b)
|
|
1269
|
+
* {
|
|
1270
|
+
* return a+b;
|
|
1271
|
+
* }
|
|
1272
|
+
* Are really two separate defined functions and
|
|
1273
|
+
* can be referenced in two ways:
|
|
1274
|
+
* alert( D2A(1,2) ); // produces 3
|
|
1275
|
+
* alert( theAdd(1,2) ); // also produces 3
|
|
1276
|
+
* So it must have two tags:
|
|
1277
|
+
* D2A
|
|
1278
|
+
* theAdd
|
|
1279
|
+
* Save the reference to the name for later use, once
|
|
1280
|
+
* we have established this is a valid function we will
|
|
1281
|
+
* create the secondary reference to it.
|
|
1282
|
+
*/
|
|
1283
|
+
copyToken(secondary_name, token);
|
|
1284
|
+
readToken (token);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
if ( isType (token, TOKEN_OPEN_PAREN) )
|
|
1288
|
+
skipArgumentList(token);
|
|
1289
|
+
|
|
1290
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
1291
|
+
{
|
|
1292
|
+
/*
|
|
1293
|
+
* This will be either a function or a class.
|
|
1294
|
+
* We can only determine this by checking the body
|
|
1295
|
+
* of the function. If we find a "this." we know
|
|
1296
|
+
* it is a class, otherwise it is a function.
|
|
1297
|
+
*/
|
|
1298
|
+
if ( is_inside_class )
|
|
1299
|
+
{
|
|
1300
|
+
makeJsTag (name, JSTAG_METHOD);
|
|
1301
|
+
if ( vStringLength(secondary_name->string) > 0 )
|
|
1302
|
+
makeFunctionTag (secondary_name);
|
|
1303
|
+
parseBlock (token, name);
|
|
1304
|
+
}
|
|
1305
|
+
else
|
|
1306
|
+
{
|
|
1307
|
+
is_class = parseBlock (token, name);
|
|
1308
|
+
if ( is_class )
|
|
1309
|
+
makeClassTag (name);
|
|
1310
|
+
else
|
|
1311
|
+
makeFunctionTag (name);
|
|
1312
|
+
|
|
1313
|
+
if ( vStringLength(secondary_name->string) > 0 )
|
|
1314
|
+
makeFunctionTag (secondary_name);
|
|
1315
|
+
|
|
1316
|
+
/*
|
|
1317
|
+
* Find to the end of the statement
|
|
1318
|
+
*/
|
|
1319
|
+
goto cleanUp;
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
else if (isType (token, TOKEN_OPEN_PAREN))
|
|
1324
|
+
{
|
|
1325
|
+
/*
|
|
1326
|
+
* Handle nameless functions
|
|
1327
|
+
* this.method_name = () {}
|
|
1328
|
+
*/
|
|
1329
|
+
skipArgumentList(token);
|
|
1330
|
+
|
|
1331
|
+
if (isType (token, TOKEN_OPEN_CURLY))
|
|
1332
|
+
{
|
|
1333
|
+
/*
|
|
1334
|
+
* Nameless functions are only setup as methods.
|
|
1335
|
+
*/
|
|
1336
|
+
makeJsTag (name, JSTAG_METHOD);
|
|
1337
|
+
parseBlock (token, name);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
else if (isType (token, TOKEN_OPEN_CURLY))
|
|
1341
|
+
{
|
|
1342
|
+
/*
|
|
1343
|
+
* Creates tags for each of these class methods
|
|
1344
|
+
* ValidClassOne.prototype = {
|
|
1345
|
+
* 'validMethodOne' : function(a,b) {},
|
|
1346
|
+
* 'validMethodTwo' : function(a,b) {}
|
|
1347
|
+
* }
|
|
1348
|
+
*/
|
|
1349
|
+
parseMethods(token, name);
|
|
1350
|
+
if (isType (token, TOKEN_CLOSE_CURLY))
|
|
1351
|
+
{
|
|
1352
|
+
/*
|
|
1353
|
+
* Assume the closing parantheses terminates
|
|
1354
|
+
* this statements.
|
|
1355
|
+
*/
|
|
1356
|
+
is_terminated = TRUE;
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
else if (isKeyword (token, KEYWORD_new))
|
|
1360
|
+
{
|
|
1361
|
+
readToken (token);
|
|
1362
|
+
if ( isKeyword (token, KEYWORD_function) ||
|
|
1363
|
+
isKeyword (token, KEYWORD_capital_function) ||
|
|
1364
|
+
isKeyword (token, KEYWORD_object) ||
|
|
1365
|
+
isKeyword (token, KEYWORD_capital_object) )
|
|
1366
|
+
{
|
|
1367
|
+
if ( isKeyword (token, KEYWORD_object) ||
|
|
1368
|
+
isKeyword (token, KEYWORD_capital_object) )
|
|
1369
|
+
is_class = TRUE;
|
|
1370
|
+
|
|
1371
|
+
readToken (token);
|
|
1372
|
+
if ( isType (token, TOKEN_OPEN_PAREN) )
|
|
1373
|
+
skipArgumentList(token);
|
|
1374
|
+
|
|
1375
|
+
if (isType (token, TOKEN_SEMICOLON))
|
|
1376
|
+
{
|
|
1377
|
+
if ( token->nestLevel == 0 )
|
|
1378
|
+
{
|
|
1379
|
+
if ( is_class )
|
|
1380
|
+
{
|
|
1381
|
+
makeClassTag (name);
|
|
1382
|
+
} else {
|
|
1383
|
+
makeFunctionTag (name);
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
else if (isKeyword (token, KEYWORD_NONE))
|
|
1390
|
+
{
|
|
1391
|
+
/*
|
|
1392
|
+
* Only create variables for global scope
|
|
1393
|
+
*/
|
|
1394
|
+
if ( token->nestLevel == 0 && is_global )
|
|
1395
|
+
{
|
|
1396
|
+
/*
|
|
1397
|
+
* A pointer can be created to the function.
|
|
1398
|
+
* If we recognize the function/class name ignore the variable.
|
|
1399
|
+
* This format looks identical to a variable definition.
|
|
1400
|
+
* A variable defined outside of a block is considered
|
|
1401
|
+
* a global variable:
|
|
1402
|
+
* var g_var1 = 1;
|
|
1403
|
+
* var g_var2;
|
|
1404
|
+
* This is not a global variable:
|
|
1405
|
+
* var g_var = function;
|
|
1406
|
+
* This is a global variable:
|
|
1407
|
+
* var g_var = different_var_name;
|
|
1408
|
+
*/
|
|
1409
|
+
fulltag = vStringNew ();
|
|
1410
|
+
if (vStringLength (token->scope) > 0)
|
|
1411
|
+
{
|
|
1412
|
+
vStringCopy(fulltag, token->scope);
|
|
1413
|
+
vStringCatS (fulltag, ".");
|
|
1414
|
+
vStringCatS (fulltag, vStringValue(token->string));
|
|
1415
|
+
}
|
|
1416
|
+
else
|
|
1417
|
+
{
|
|
1418
|
+
vStringCopy(fulltag, token->string);
|
|
1419
|
+
}
|
|
1420
|
+
vStringTerminate(fulltag);
|
|
1421
|
+
if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) &&
|
|
1422
|
+
! stringListHas(ClassNames, vStringValue (fulltag)) )
|
|
1423
|
+
{
|
|
1424
|
+
findCmdTerm (token);
|
|
1425
|
+
if (isType (token, TOKEN_SEMICOLON))
|
|
1426
|
+
makeJsTag (name, JSTAG_VARIABLE);
|
|
1427
|
+
}
|
|
1428
|
+
vStringDelete (fulltag);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
findCmdTerm (token);
|
|
1433
|
+
|
|
1434
|
+
/*
|
|
1435
|
+
* Statements can be optionally terminated in the case of
|
|
1436
|
+
* statement prior to a close curly brace as in the
|
|
1437
|
+
* document.write line below:
|
|
1438
|
+
*
|
|
1439
|
+
* function checkForUpdate() {
|
|
1440
|
+
* if( 1==1 ) {
|
|
1441
|
+
* document.write("hello from checkForUpdate<br>")
|
|
1442
|
+
* }
|
|
1443
|
+
* return 1;
|
|
1444
|
+
* }
|
|
1445
|
+
*/
|
|
1446
|
+
if ( ! is_terminated && isType (token, TOKEN_CLOSE_CURLY))
|
|
1447
|
+
is_terminated = FALSE;
|
|
1448
|
+
|
|
1449
|
+
|
|
1450
|
+
cleanUp:
|
|
1451
|
+
vStringCopy(token->scope, saveScope);
|
|
1452
|
+
deleteToken (name);
|
|
1453
|
+
deleteToken (secondary_name);
|
|
1454
|
+
vStringDelete(saveScope);
|
|
1455
|
+
|
|
1456
|
+
return is_terminated;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
static boolean parseLine (tokenInfo *const token, boolean is_inside_class)
|
|
1460
|
+
{
|
|
1461
|
+
boolean is_terminated = TRUE;
|
|
1462
|
+
/*
|
|
1463
|
+
* Detect the common statements, if, while, for, do, ...
|
|
1464
|
+
* This is necessary since the last statement within a block "{}"
|
|
1465
|
+
* can be optionally terminated.
|
|
1466
|
+
*
|
|
1467
|
+
* If the statement is not terminated, we need to tell
|
|
1468
|
+
* the calling routine to prevent reading an additional token
|
|
1469
|
+
* looking for the end of the statement.
|
|
1470
|
+
*/
|
|
1471
|
+
|
|
1472
|
+
if (isType(token, TOKEN_KEYWORD))
|
|
1473
|
+
{
|
|
1474
|
+
switch (token->keyword)
|
|
1475
|
+
{
|
|
1476
|
+
case KEYWORD_for:
|
|
1477
|
+
case KEYWORD_while:
|
|
1478
|
+
case KEYWORD_do:
|
|
1479
|
+
parseLoop (token);
|
|
1480
|
+
break;
|
|
1481
|
+
case KEYWORD_if:
|
|
1482
|
+
case KEYWORD_else:
|
|
1483
|
+
case KEYWORD_try:
|
|
1484
|
+
case KEYWORD_catch:
|
|
1485
|
+
case KEYWORD_finally:
|
|
1486
|
+
/* Common semantics */
|
|
1487
|
+
is_terminated = parseIf (token);
|
|
1488
|
+
break;
|
|
1489
|
+
case KEYWORD_switch:
|
|
1490
|
+
parseSwitch (token);
|
|
1491
|
+
break;
|
|
1492
|
+
default:
|
|
1493
|
+
parseStatement (token, is_inside_class);
|
|
1494
|
+
break;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
else
|
|
1498
|
+
{
|
|
1499
|
+
/*
|
|
1500
|
+
* Special case where single line statements may not be
|
|
1501
|
+
* SEMICOLON terminated. parseBlock needs to know this
|
|
1502
|
+
* so that it does not read the next token.
|
|
1503
|
+
*/
|
|
1504
|
+
is_terminated = parseStatement (token, is_inside_class);
|
|
1505
|
+
}
|
|
1506
|
+
return is_terminated;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
static void parseJsFile (tokenInfo *const token)
|
|
1510
|
+
{
|
|
1511
|
+
do
|
|
1512
|
+
{
|
|
1513
|
+
readToken (token);
|
|
1514
|
+
|
|
1515
|
+
if (isType(token, TOKEN_KEYWORD))
|
|
1516
|
+
{
|
|
1517
|
+
switch (token->keyword)
|
|
1518
|
+
{
|
|
1519
|
+
case KEYWORD_function: parseFunction (token); break;
|
|
1520
|
+
default: parseLine (token, FALSE); break;
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
else
|
|
1524
|
+
{
|
|
1525
|
+
parseLine (token, FALSE);
|
|
1526
|
+
}
|
|
1527
|
+
} while (TRUE);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
static void initialize (const langType language)
|
|
1531
|
+
{
|
|
1532
|
+
Assert (sizeof (JsKinds) / sizeof (JsKinds [0]) == JSTAG_COUNT);
|
|
1533
|
+
Lang_js = language;
|
|
1534
|
+
buildJsKeywordHash ();
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
static void findJsTags (void)
|
|
1538
|
+
{
|
|
1539
|
+
tokenInfo *const token = newToken ();
|
|
1540
|
+
exception_t exception;
|
|
1541
|
+
|
|
1542
|
+
ClassNames = stringListNew ();
|
|
1543
|
+
FunctionNames = stringListNew ();
|
|
1544
|
+
|
|
1545
|
+
exception = (exception_t) (setjmp (Exception));
|
|
1546
|
+
while (exception == ExceptionNone)
|
|
1547
|
+
parseJsFile (token);
|
|
1548
|
+
|
|
1549
|
+
stringListDelete (ClassNames);
|
|
1550
|
+
stringListDelete (FunctionNames);
|
|
1551
|
+
ClassNames = NULL;
|
|
1552
|
+
FunctionNames = NULL;
|
|
1553
|
+
deleteToken (token);
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
/* Create parser definition stucture */
|
|
1557
|
+
extern parserDefinition* JavaScriptParser (void)
|
|
1558
|
+
{
|
|
1559
|
+
static const char *const extensions [] = { "js", NULL };
|
|
1560
|
+
parserDefinition *const def = parserNew ("JavaScript");
|
|
1561
|
+
def->extensions = extensions;
|
|
1562
|
+
/*
|
|
1563
|
+
* New definitions for parsing instead of regex
|
|
1564
|
+
*/
|
|
1565
|
+
def->kinds = JsKinds;
|
|
1566
|
+
def->kindCount = KIND_COUNT (JsKinds);
|
|
1567
|
+
def->parser = findJsTags;
|
|
1568
|
+
def->initialize = initialize;
|
|
1569
|
+
|
|
1570
|
+
return def;
|
|
1571
|
+
}
|
|
1572
|
+
/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
|