ctags.rb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: */