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.
Files changed (138) hide show
  1. data/Gemfile +2 -0
  2. data/Rakefile +23 -0
  3. data/ctags.rb.gemspec +23 -0
  4. data/ext/.gitignore +3 -0
  5. data/ext/extconf.rb +15 -0
  6. data/ext/vendor/exuberant-ctags/.gitignore +6 -0
  7. data/ext/vendor/exuberant-ctags/.indent.pro +31 -0
  8. data/ext/vendor/exuberant-ctags/COPYING +340 -0
  9. data/ext/vendor/exuberant-ctags/EXTENDING.html +386 -0
  10. data/ext/vendor/exuberant-ctags/FAQ +371 -0
  11. data/ext/vendor/exuberant-ctags/INSTALL +215 -0
  12. data/ext/vendor/exuberant-ctags/INSTALL.oth +73 -0
  13. data/ext/vendor/exuberant-ctags/MAINTAINERS +88 -0
  14. data/ext/vendor/exuberant-ctags/Makefile.in +222 -0
  15. data/ext/vendor/exuberant-ctags/NEWS +871 -0
  16. data/ext/vendor/exuberant-ctags/README +73 -0
  17. data/ext/vendor/exuberant-ctags/ant.c +42 -0
  18. data/ext/vendor/exuberant-ctags/argproc.c +505 -0
  19. data/ext/vendor/exuberant-ctags/args.c +274 -0
  20. data/ext/vendor/exuberant-ctags/args.h +63 -0
  21. data/ext/vendor/exuberant-ctags/asm.c +387 -0
  22. data/ext/vendor/exuberant-ctags/asp.c +328 -0
  23. data/ext/vendor/exuberant-ctags/awk.c +81 -0
  24. data/ext/vendor/exuberant-ctags/basic.c +203 -0
  25. data/ext/vendor/exuberant-ctags/beta.c +321 -0
  26. data/ext/vendor/exuberant-ctags/c.c +2932 -0
  27. data/ext/vendor/exuberant-ctags/cobol.c +50 -0
  28. data/ext/vendor/exuberant-ctags/config.h.in +277 -0
  29. data/ext/vendor/exuberant-ctags/configure +7704 -0
  30. data/ext/vendor/exuberant-ctags/configure.ac +532 -0
  31. data/ext/vendor/exuberant-ctags/ctags.1 +1186 -0
  32. data/ext/vendor/exuberant-ctags/ctags.h +28 -0
  33. data/ext/vendor/exuberant-ctags/ctags.html +2087 -0
  34. data/ext/vendor/exuberant-ctags/ctags.spec +40 -0
  35. data/ext/vendor/exuberant-ctags/debug.c +113 -0
  36. data/ext/vendor/exuberant-ctags/debug.h +70 -0
  37. data/ext/vendor/exuberant-ctags/descrip.mms +68 -0
  38. data/ext/vendor/exuberant-ctags/dosbatch.c +42 -0
  39. data/ext/vendor/exuberant-ctags/e_amiga.h +24 -0
  40. data/ext/vendor/exuberant-ctags/e_djgpp.h +47 -0
  41. data/ext/vendor/exuberant-ctags/e_mac.h +143 -0
  42. data/ext/vendor/exuberant-ctags/e_msoft.h +76 -0
  43. data/ext/vendor/exuberant-ctags/e_os2.h +37 -0
  44. data/ext/vendor/exuberant-ctags/e_qdos.h +34 -0
  45. data/ext/vendor/exuberant-ctags/e_riscos.h +58 -0
  46. data/ext/vendor/exuberant-ctags/e_vms.h +31 -0
  47. data/ext/vendor/exuberant-ctags/eiffel.c +1352 -0
  48. data/ext/vendor/exuberant-ctags/entry.c +847 -0
  49. data/ext/vendor/exuberant-ctags/entry.h +103 -0
  50. data/ext/vendor/exuberant-ctags/erlang.c +189 -0
  51. data/ext/vendor/exuberant-ctags/flex.c +2243 -0
  52. data/ext/vendor/exuberant-ctags/fortran.c +2197 -0
  53. data/ext/vendor/exuberant-ctags/general.h +127 -0
  54. data/ext/vendor/exuberant-ctags/get.c +669 -0
  55. data/ext/vendor/exuberant-ctags/get.h +50 -0
  56. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/all-wcprops +47 -0
  57. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/entries +112 -0
  58. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/README.txt.svn-base +5 -0
  59. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regcomp.c.svn-base +3818 -0
  60. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.c.svn-base +74 -0
  61. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.h.svn-base +575 -0
  62. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.c.svn-base +1713 -0
  63. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.h.svn-base +773 -0
  64. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regexec.c.svn-base +4338 -0
  65. data/ext/vendor/exuberant-ctags/gnu_regex/README.txt +5 -0
  66. data/ext/vendor/exuberant-ctags/gnu_regex/regcomp.c +3818 -0
  67. data/ext/vendor/exuberant-ctags/gnu_regex/regex.c +74 -0
  68. data/ext/vendor/exuberant-ctags/gnu_regex/regex.h +575 -0
  69. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.c +1713 -0
  70. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.h +773 -0
  71. data/ext/vendor/exuberant-ctags/gnu_regex/regexec.c +4338 -0
  72. data/ext/vendor/exuberant-ctags/html.c +49 -0
  73. data/ext/vendor/exuberant-ctags/jscript.c +1572 -0
  74. data/ext/vendor/exuberant-ctags/keyword.c +258 -0
  75. data/ext/vendor/exuberant-ctags/keyword.h +34 -0
  76. data/ext/vendor/exuberant-ctags/lisp.c +139 -0
  77. data/ext/vendor/exuberant-ctags/lregex.c +704 -0
  78. data/ext/vendor/exuberant-ctags/lua.c +133 -0
  79. data/ext/vendor/exuberant-ctags/mac.c +273 -0
  80. data/ext/vendor/exuberant-ctags/magic.diff +21 -0
  81. data/ext/vendor/exuberant-ctags/main.c +584 -0
  82. data/ext/vendor/exuberant-ctags/main.h +32 -0
  83. data/ext/vendor/exuberant-ctags/maintainer.mak +476 -0
  84. data/ext/vendor/exuberant-ctags/make.c +217 -0
  85. data/ext/vendor/exuberant-ctags/matlab.c +44 -0
  86. data/ext/vendor/exuberant-ctags/mk_bc3.mak +46 -0
  87. data/ext/vendor/exuberant-ctags/mk_bc5.mak +49 -0
  88. data/ext/vendor/exuberant-ctags/mk_djg.mak +18 -0
  89. data/ext/vendor/exuberant-ctags/mk_manx.mak +65 -0
  90. data/ext/vendor/exuberant-ctags/mk_mingw.mak +31 -0
  91. data/ext/vendor/exuberant-ctags/mk_mpw.mak +130 -0
  92. data/ext/vendor/exuberant-ctags/mk_mvc.mak +40 -0
  93. data/ext/vendor/exuberant-ctags/mk_os2.mak +104 -0
  94. data/ext/vendor/exuberant-ctags/mk_qdos.mak +100 -0
  95. data/ext/vendor/exuberant-ctags/mk_sas.mak +63 -0
  96. data/ext/vendor/exuberant-ctags/mkinstalldirs +40 -0
  97. data/ext/vendor/exuberant-ctags/ocaml.c +1842 -0
  98. data/ext/vendor/exuberant-ctags/options.c +1842 -0
  99. data/ext/vendor/exuberant-ctags/options.h +155 -0
  100. data/ext/vendor/exuberant-ctags/parse.c +677 -0
  101. data/ext/vendor/exuberant-ctags/parse.h +129 -0
  102. data/ext/vendor/exuberant-ctags/parsers.h +63 -0
  103. data/ext/vendor/exuberant-ctags/pascal.c +267 -0
  104. data/ext/vendor/exuberant-ctags/perl.c +382 -0
  105. data/ext/vendor/exuberant-ctags/php.c +237 -0
  106. data/ext/vendor/exuberant-ctags/python.c +771 -0
  107. data/ext/vendor/exuberant-ctags/qdos.c +106 -0
  108. data/ext/vendor/exuberant-ctags/read.c +569 -0
  109. data/ext/vendor/exuberant-ctags/read.h +116 -0
  110. data/ext/vendor/exuberant-ctags/readtags.c +959 -0
  111. data/ext/vendor/exuberant-ctags/readtags.h +252 -0
  112. data/ext/vendor/exuberant-ctags/rexx.c +39 -0
  113. data/ext/vendor/exuberant-ctags/routines.c +891 -0
  114. data/ext/vendor/exuberant-ctags/routines.h +134 -0
  115. data/ext/vendor/exuberant-ctags/ruby.c +408 -0
  116. data/ext/vendor/exuberant-ctags/scheme.c +111 -0
  117. data/ext/vendor/exuberant-ctags/sh.c +115 -0
  118. data/ext/vendor/exuberant-ctags/slang.c +41 -0
  119. data/ext/vendor/exuberant-ctags/sml.c +212 -0
  120. data/ext/vendor/exuberant-ctags/sort.c +230 -0
  121. data/ext/vendor/exuberant-ctags/sort.h +32 -0
  122. data/ext/vendor/exuberant-ctags/source.mak +122 -0
  123. data/ext/vendor/exuberant-ctags/sql.c +2112 -0
  124. data/ext/vendor/exuberant-ctags/strlist.c +281 -0
  125. data/ext/vendor/exuberant-ctags/strlist.h +54 -0
  126. data/ext/vendor/exuberant-ctags/tcl.c +116 -0
  127. data/ext/vendor/exuberant-ctags/tex.c +524 -0
  128. data/ext/vendor/exuberant-ctags/verilog.c +340 -0
  129. data/ext/vendor/exuberant-ctags/vhdl.c +835 -0
  130. data/ext/vendor/exuberant-ctags/vim.c +636 -0
  131. data/ext/vendor/exuberant-ctags/vstring.c +232 -0
  132. data/ext/vendor/exuberant-ctags/vstring.h +85 -0
  133. data/ext/vendor/exuberant-ctags/yacc.c +40 -0
  134. data/lib/ctags/exuberant.rb +45 -0
  135. data/lib/ctags/version.rb +3 -0
  136. data/lib/ctags.rb +6 -0
  137. data/test/test_ctags.rb +24 -0
  138. 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: */