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,1842 @@
1
+ /*
2
+ * Copyright (c) 2009, Vincent Berthoux
3
+ *
4
+ * This source code is released for free distribution under the terms of the
5
+ * GNU General Public License.
6
+ *
7
+ * This module contains functions for generating tags for Objective Caml
8
+ * language files.
9
+ */
10
+ /*
11
+ * INCLUDE FILES
12
+ */
13
+ #include "general.h" /* must always come first */
14
+
15
+ #include <string.h>
16
+
17
+ #include "keyword.h"
18
+ #include "entry.h"
19
+ #include "options.h"
20
+ #include "read.h"
21
+ #include "routines.h"
22
+ #include "vstring.h"
23
+
24
+ /* To get rid of unused parameter warning in
25
+ * -Wextra */
26
+ #ifdef UNUSED
27
+ #elif defined(__GNUC__)
28
+ # define UNUSED(x) UNUSED_ ## x __attribute__((unused))
29
+ #elif defined(__LCLINT__)
30
+ # define UNUSED(x) /*@unused@*/ x
31
+ #else
32
+ # define UNUSED(x) x
33
+ #endif
34
+ #define OCAML_MAX_STACK_SIZE 256
35
+
36
+ typedef enum {
37
+ K_CLASS, /* Ocaml class, relatively rare */
38
+ K_METHOD, /* class method */
39
+ K_MODULE, /* Ocaml module OR functor */
40
+ K_VAR,
41
+ K_TYPE, /* name of an OCaml type */
42
+ K_FUNCTION,
43
+ K_CONSTRUCTOR, /* Constructor of a sum type */
44
+ K_RECORDFIELD,
45
+ K_EXCEPTION
46
+ } ocamlKind;
47
+
48
+ static kindOption OcamlKinds[] = {
49
+ {TRUE, 'c', "class", "classes"},
50
+ {TRUE, 'm', "method", "Object's method"},
51
+ {TRUE, 'M', "module", "Module or functor"},
52
+ {TRUE, 'v', "var", "Global variable"},
53
+ {TRUE, 't', "type", "Type name"},
54
+ {TRUE, 'f', "function", "A function"},
55
+ {TRUE, 'C', "Constructor", "A constructor"},
56
+ {TRUE, 'r', "Record field", "A 'structure' field"},
57
+ {TRUE, 'e', "Exception", "An exception"}
58
+ };
59
+
60
+ typedef enum {
61
+ OcaKEYWORD_and,
62
+ OcaKEYWORD_begin,
63
+ OcaKEYWORD_class,
64
+ OcaKEYWORD_do,
65
+ OcaKEYWORD_done,
66
+ OcaKEYWORD_else,
67
+ OcaKEYWORD_end,
68
+ OcaKEYWORD_exception,
69
+ OcaKEYWORD_for,
70
+ OcaKEYWORD_functor,
71
+ OcaKEYWORD_fun,
72
+ OcaKEYWORD_if,
73
+ OcaKEYWORD_in,
74
+ OcaKEYWORD_let,
75
+ OcaKEYWORD_match,
76
+ OcaKEYWORD_method,
77
+ OcaKEYWORD_module,
78
+ OcaKEYWORD_mutable,
79
+ OcaKEYWORD_object,
80
+ OcaKEYWORD_of,
81
+ OcaKEYWORD_rec,
82
+ OcaKEYWORD_sig,
83
+ OcaKEYWORD_struct,
84
+ OcaKEYWORD_then,
85
+ OcaKEYWORD_try,
86
+ OcaKEYWORD_type,
87
+ OcaKEYWORD_val,
88
+ OcaKEYWORD_virtual,
89
+ OcaKEYWORD_while,
90
+ OcaKEYWORD_with,
91
+
92
+ OcaIDENTIFIER,
93
+ Tok_PARL, /* '(' */
94
+ Tok_PARR, /* ')' */
95
+ Tok_BRL, /* '[' */
96
+ Tok_BRR, /* ']' */
97
+ Tok_CurlL, /* '{' */
98
+ Tok_CurlR, /* '}' */
99
+ Tok_Prime, /* '\'' */
100
+ Tok_Pipe, /* '|' */
101
+ Tok_EQ, /* '=' */
102
+ Tok_Val, /* string/number/poo */
103
+ Tok_Op, /* any operator recognized by the language */
104
+ Tok_semi, /* ';' */
105
+ Tok_comma, /* ',' */
106
+ Tok_To, /* '->' */
107
+ Tok_Sharp, /* '#' */
108
+ Tok_Backslash, /* '\\' */
109
+
110
+ Tok_EOF /* END of file */
111
+ } ocamlKeyword;
112
+
113
+ typedef struct sOcaKeywordDesc {
114
+ const char *name;
115
+ ocamlKeyword id;
116
+ } ocaKeywordDesc;
117
+
118
+ typedef ocamlKeyword ocaToken;
119
+
120
+ static const ocaKeywordDesc OcamlKeywordTable[] = {
121
+ { "and" , OcaKEYWORD_and },
122
+ { "begin" , OcaKEYWORD_begin },
123
+ { "class" , OcaKEYWORD_class },
124
+ { "do" , OcaKEYWORD_do },
125
+ { "done" , OcaKEYWORD_done },
126
+ { "else" , OcaKEYWORD_else },
127
+ { "end" , OcaKEYWORD_end },
128
+ { "exception" , OcaKEYWORD_exception },
129
+ { "for" , OcaKEYWORD_for },
130
+ { "fun" , OcaKEYWORD_fun },
131
+ { "function" , OcaKEYWORD_fun },
132
+ { "functor" , OcaKEYWORD_functor },
133
+ { "in" , OcaKEYWORD_in },
134
+ { "let" , OcaKEYWORD_let },
135
+ { "match" , OcaKEYWORD_match },
136
+ { "method" , OcaKEYWORD_method },
137
+ { "module" , OcaKEYWORD_module },
138
+ { "mutable" , OcaKEYWORD_mutable },
139
+ { "object" , OcaKEYWORD_object },
140
+ { "of" , OcaKEYWORD_of },
141
+ { "rec" , OcaKEYWORD_rec },
142
+ { "sig" , OcaKEYWORD_sig },
143
+ { "struct" , OcaKEYWORD_struct },
144
+ { "then" , OcaKEYWORD_then },
145
+ { "try" , OcaKEYWORD_try },
146
+ { "type" , OcaKEYWORD_type },
147
+ { "val" , OcaKEYWORD_val },
148
+ { "value" , OcaKEYWORD_let }, /* just to handle revised syntax */
149
+ { "virtual" , OcaKEYWORD_virtual },
150
+ { "while" , OcaKEYWORD_while },
151
+ { "with" , OcaKEYWORD_with },
152
+
153
+ { "or" , Tok_Op },
154
+ { "mod " , Tok_Op },
155
+ { "land " , Tok_Op },
156
+ { "lor " , Tok_Op },
157
+ { "lxor " , Tok_Op },
158
+ { "lsl " , Tok_Op },
159
+ { "lsr " , Tok_Op },
160
+ { "asr" , Tok_Op },
161
+ { "->" , Tok_To },
162
+ { "true" , Tok_Val },
163
+ { "false" , Tok_Val }
164
+ };
165
+
166
+ static langType Lang_Ocaml;
167
+
168
+ boolean exportLocalInfo = FALSE;
169
+
170
+ /*//////////////////////////////////////////////////////////////////
171
+ //// lexingInit */
172
+ typedef struct _lexingState {
173
+ vString *name; /* current parsed identifier/operator */
174
+ const unsigned char *cp; /* position in stream */
175
+ } lexingState;
176
+
177
+ /* array of the size of all possible value for a char */
178
+ boolean isOperator[1 << (8 * sizeof (char))] = { FALSE };
179
+
180
+ static void initKeywordHash ( void )
181
+ {
182
+ const size_t count = sizeof (OcamlKeywordTable) / sizeof (ocaKeywordDesc);
183
+ size_t i;
184
+
185
+ for (i = 0; i < count; ++i)
186
+ {
187
+ addKeyword (OcamlKeywordTable[i].name, Lang_Ocaml,
188
+ (int) OcamlKeywordTable[i].id);
189
+ }
190
+ }
191
+
192
+ /* definition of all the operator in OCaml,
193
+ * /!\ certain operator get special treatment
194
+ * in regards of their role in OCaml grammar :
195
+ * '|' ':' '=' '~' and '?' */
196
+ static void initOperatorTable ( void )
197
+ {
198
+ isOperator['!'] = TRUE;
199
+ isOperator['$'] = TRUE;
200
+ isOperator['%'] = TRUE;
201
+ isOperator['&'] = TRUE;
202
+ isOperator['*'] = TRUE;
203
+ isOperator['+'] = TRUE;
204
+ isOperator['-'] = TRUE;
205
+ isOperator['.'] = TRUE;
206
+ isOperator['/'] = TRUE;
207
+ isOperator[':'] = TRUE;
208
+ isOperator['<'] = TRUE;
209
+ isOperator['='] = TRUE;
210
+ isOperator['>'] = TRUE;
211
+ isOperator['?'] = TRUE;
212
+ isOperator['@'] = TRUE;
213
+ isOperator['^'] = TRUE;
214
+ isOperator['~'] = TRUE;
215
+ isOperator['|'] = TRUE;
216
+ }
217
+
218
+ /*//////////////////////////////////////////////////////////////////////
219
+ //// Lexing */
220
+ static boolean isNum (char c)
221
+ {
222
+ return c >= '0' && c <= '9';
223
+ }
224
+ static boolean isLowerAlpha (char c)
225
+ {
226
+ return c >= 'a' && c <= 'z';
227
+ }
228
+
229
+ static boolean isUpperAlpha (char c)
230
+ {
231
+ return c >= 'A' && c <= 'Z';
232
+ }
233
+
234
+ static boolean isAlpha (char c)
235
+ {
236
+ return isLowerAlpha (c) || isUpperAlpha (c);
237
+ }
238
+
239
+ static boolean isIdent (char c)
240
+ {
241
+ return isNum (c) || isAlpha (c) || c == '_' || c == '\'';
242
+ }
243
+
244
+ static boolean isSpace (char c)
245
+ {
246
+ return c == ' ' || c == '\t' || c == '\r' || c == '\n';
247
+ }
248
+
249
+ static void eatWhiteSpace (lexingState * st)
250
+ {
251
+ const unsigned char *cp = st->cp;
252
+ while (isSpace (*cp))
253
+ cp++;
254
+
255
+ st->cp = cp;
256
+ }
257
+
258
+ static void eatString (lexingState * st)
259
+ {
260
+ boolean lastIsBackSlash = FALSE;
261
+ boolean unfinished = TRUE;
262
+ const unsigned char *c = st->cp + 1;
263
+
264
+ while (unfinished)
265
+ {
266
+ /* end of line should never happen.
267
+ * we tolerate it */
268
+ if (c == NULL || c[0] == '\0')
269
+ break;
270
+ else if (*c == '"' && !lastIsBackSlash)
271
+ unfinished = FALSE;
272
+ else
273
+ lastIsBackSlash = *c == '\\';
274
+
275
+ c++;
276
+ }
277
+
278
+ st->cp = c;
279
+ }
280
+
281
+ static void eatComment (lexingState * st)
282
+ {
283
+ boolean unfinished = TRUE;
284
+ boolean lastIsStar = FALSE;
285
+ const unsigned char *c = st->cp + 2;
286
+
287
+ while (unfinished)
288
+ {
289
+ /* we've reached the end of the line..
290
+ * so we have to reload a line... */
291
+ if (c == NULL || *c == '\0')
292
+ {
293
+ st->cp = fileReadLine ();
294
+ /* WOOPS... no more input...
295
+ * we return, next lexing read
296
+ * will be null and ok */
297
+ if (st->cp == NULL)
298
+ return;
299
+ c = st->cp;
300
+ continue;
301
+ }
302
+ /* we've reached the end of the comment */
303
+ else if (*c == ')' && lastIsStar)
304
+ unfinished = FALSE;
305
+ /* here we deal with imbricated comment, which
306
+ * are allowed in OCaml */
307
+ else if (c[0] == '(' && c[1] == '*')
308
+ {
309
+ st->cp = c;
310
+ eatComment (st);
311
+ c = st->cp;
312
+ lastIsStar = FALSE;
313
+ }
314
+ else
315
+ lastIsStar = '*' == *c;
316
+
317
+ c++;
318
+ }
319
+
320
+ st->cp = c;
321
+ }
322
+
323
+ static void readIdentifier (lexingState * st)
324
+ {
325
+ const unsigned char *p;
326
+ vStringClear (st->name);
327
+
328
+ /* first char is a simple letter */
329
+ if (isAlpha (*st->cp) || *st->cp == '_')
330
+ vStringPut (st->name, (int) *st->cp);
331
+
332
+ /* Go till you get identifier chars */
333
+ for (p = st->cp + 1; isIdent (*p); p++)
334
+ vStringPut (st->name, (int) *p);
335
+
336
+ st->cp = p;
337
+
338
+ vStringTerminate (st->name);
339
+ }
340
+
341
+ static ocamlKeyword eatNumber (lexingState * st)
342
+ {
343
+ while (isNum (*st->cp))
344
+ st->cp++;
345
+ return Tok_Val;
346
+ }
347
+
348
+ /* Operator can be defined in OCaml as a function
349
+ * so we must be ample enough to parse them normally */
350
+ static ocamlKeyword eatOperator (lexingState * st)
351
+ {
352
+ int count = 0;
353
+ const unsigned char *root = st->cp;
354
+
355
+ vStringClear (st->name);
356
+
357
+ while (isOperator[st->cp[count]])
358
+ {
359
+ vStringPut (st->name, st->cp[count]);
360
+ count++;
361
+ }
362
+
363
+ vStringTerminate (st->name);
364
+
365
+ st->cp += count;
366
+ if (count <= 1)
367
+ {
368
+ switch (root[0])
369
+ {
370
+ case '|':
371
+ return Tok_Pipe;
372
+ case '=':
373
+ return Tok_EQ;
374
+ default:
375
+ return Tok_Op;
376
+ }
377
+ }
378
+ else if (count == 2 && root[0] == '-' && root[1] == '>')
379
+ return Tok_To;
380
+ else
381
+ return Tok_Op;
382
+ }
383
+
384
+ /* The lexer is in charge of reading the file.
385
+ * Some of sub-lexer (like eatComment) also read file.
386
+ * lexing is finished when the lexer return Tok_EOF */
387
+ static ocamlKeyword lex (lexingState * st)
388
+ {
389
+ int retType;
390
+ /* handling data input here */
391
+ while (st->cp == NULL || st->cp[0] == '\0')
392
+ {
393
+ st->cp = fileReadLine ();
394
+ if (st->cp == NULL)
395
+ return Tok_EOF;
396
+ }
397
+
398
+ if (isAlpha (*st->cp))
399
+ {
400
+ readIdentifier (st);
401
+ retType = lookupKeyword (vStringValue (st->name), Lang_Ocaml);
402
+
403
+ if (retType == -1) /* If it's not a keyword */
404
+ {
405
+ return OcaIDENTIFIER;
406
+ }
407
+ else
408
+ {
409
+ return retType;
410
+ }
411
+ }
412
+ else if (isNum (*st->cp))
413
+ return eatNumber (st);
414
+ else if (isSpace (*st->cp))
415
+ {
416
+ eatWhiteSpace (st);
417
+ return lex (st);
418
+ }
419
+ /* OCaml permit the definition of our own operators
420
+ * so here we check all the consecuting chars which
421
+ * are operators to discard them. */
422
+ else if (isOperator[*st->cp])
423
+ return eatOperator (st);
424
+ else
425
+ switch (*st->cp)
426
+ {
427
+ case '(':
428
+ if (st->cp[1] == '*') /* ergl, a comment */
429
+ {
430
+ eatComment (st);
431
+ return lex (st);
432
+ }
433
+ else
434
+ {
435
+ st->cp++;
436
+ return Tok_PARL;
437
+ }
438
+
439
+ case ')':
440
+ st->cp++;
441
+ return Tok_PARR;
442
+ case '[':
443
+ st->cp++;
444
+ return Tok_BRL;
445
+ case ']':
446
+ st->cp++;
447
+ return Tok_BRR;
448
+ case '{':
449
+ st->cp++;
450
+ return Tok_CurlL;
451
+ case '}':
452
+ st->cp++;
453
+ return Tok_CurlR;
454
+ case '\'':
455
+ st->cp++;
456
+ return Tok_Prime;
457
+ case ',':
458
+ st->cp++;
459
+ return Tok_comma;
460
+ case '=':
461
+ st->cp++;
462
+ return Tok_EQ;
463
+ case ';':
464
+ st->cp++;
465
+ return Tok_semi;
466
+ case '"':
467
+ eatString (st);
468
+ return Tok_Val;
469
+ case '_':
470
+ st->cp++;
471
+ return Tok_Val;
472
+ case '#':
473
+ st->cp++;
474
+ return Tok_Sharp;
475
+ case '\\':
476
+ st->cp++;
477
+ return Tok_Backslash;
478
+
479
+ default:
480
+ st->cp++;
481
+ break;
482
+ }
483
+
484
+ /* default return if nothing is recognized,
485
+ * shouldn't happen, but at least, it will
486
+ * be handled without destroying the parsing. */
487
+ return Tok_Val;
488
+ }
489
+
490
+ /*//////////////////////////////////////////////////////////////////////
491
+ //// Parsing */
492
+ typedef void (*parseNext) (vString * const ident, ocaToken what);
493
+
494
+ /********** Helpers */
495
+ /* This variable hold the 'parser' which is going to
496
+ * handle the next token */
497
+ parseNext toDoNext;
498
+
499
+ /* Special variable used by parser eater to
500
+ * determine which action to put after their
501
+ * job is finished. */
502
+ parseNext comeAfter;
503
+
504
+ /* If a token put an end to current delcaration/
505
+ * statement */
506
+ ocaToken terminatingToken;
507
+
508
+ /* Token to be searched by the different
509
+ * parser eater. */
510
+ ocaToken waitedToken;
511
+
512
+ /* name of the last class, used for
513
+ * context stacking. */
514
+ vString *lastClass;
515
+
516
+ vString *voidName;
517
+
518
+ typedef enum _sContextKind {
519
+ ContextStrong,
520
+ ContextSoft
521
+ } contextKind;
522
+
523
+ typedef enum _sContextType {
524
+ ContextType,
525
+ ContextModule,
526
+ ContextClass,
527
+ ContextValue,
528
+ ContextFunction,
529
+ ContextMethod,
530
+ ContextBlock
531
+ } contextType;
532
+
533
+ typedef struct _sOcamlContext {
534
+ contextKind kind; /* well if the context is strong or not */
535
+ contextType type;
536
+ parseNext callback; /* what to do when a context is pop'd */
537
+ vString *contextName; /* name, if any, of the surrounding context */
538
+ } ocamlContext;
539
+
540
+ /* context stack, can be used to output scope information
541
+ * into the tag file. */
542
+ ocamlContext stack[OCAML_MAX_STACK_SIZE];
543
+ /* current position in the tag */
544
+ int stackIndex;
545
+
546
+ /* special function, often recalled, so putting it here */
547
+ static void globalScope (vString * const ident, ocaToken what);
548
+
549
+ /* Return : index of the last named context if one
550
+ * is found, -1 otherwise */
551
+ static int getLastNamedIndex ( void )
552
+ {
553
+ int i;
554
+
555
+ for (i = stackIndex - 1; i >= 0; --i)
556
+ {
557
+ if (stack[i].contextName->buffer &&
558
+ strlen (stack[i].contextName->buffer) > 0)
559
+ {
560
+ return i;
561
+ }
562
+ }
563
+
564
+ return -1;
565
+ }
566
+
567
+ static const char *contextDescription (contextType t)
568
+ {
569
+ switch (t)
570
+ {
571
+ case ContextFunction:
572
+ return "function";
573
+ case ContextMethod:
574
+ return "method";
575
+ case ContextValue:
576
+ return "value";
577
+ case ContextModule:
578
+ return "Module";
579
+ case ContextType:
580
+ return "type";
581
+ case ContextClass:
582
+ return "class";
583
+ case ContextBlock:
584
+ return "begin/end";
585
+ }
586
+
587
+ return NULL;
588
+ }
589
+
590
+ static char contextTypeSuffix (contextType t)
591
+ {
592
+ switch (t)
593
+ {
594
+ case ContextFunction:
595
+ case ContextMethod:
596
+ case ContextValue:
597
+ case ContextModule:
598
+ return '/';
599
+ case ContextType:
600
+ return '.';
601
+ case ContextClass:
602
+ return '#';
603
+ case ContextBlock:
604
+ return ' ';
605
+ }
606
+
607
+ return '$';
608
+ }
609
+
610
+ /* Push a new context, handle null string */
611
+ static void pushContext (contextKind kind, contextType type, parseNext after,
612
+ vString const *contextName)
613
+ {
614
+ int parentIndex;
615
+
616
+ if (stackIndex >= OCAML_MAX_STACK_SIZE)
617
+ {
618
+ verbose ("OCaml Maximum depth reached");
619
+ return;
620
+ }
621
+
622
+
623
+ stack[stackIndex].kind = kind;
624
+ stack[stackIndex].type = type;
625
+ stack[stackIndex].callback = after;
626
+
627
+ parentIndex = getLastNamedIndex ();
628
+ if (contextName == NULL)
629
+ {
630
+ vStringClear (stack[stackIndex++].contextName);
631
+ return;
632
+ }
633
+
634
+ if (parentIndex >= 0)
635
+ {
636
+ vStringCopy (stack[stackIndex].contextName,
637
+ stack[parentIndex].contextName);
638
+ vStringPut (stack[stackIndex].contextName,
639
+ contextTypeSuffix (stack[parentIndex].type));
640
+
641
+ vStringCat (stack[stackIndex].contextName, contextName);
642
+ }
643
+ else
644
+ vStringCopy (stack[stackIndex].contextName, contextName);
645
+
646
+ stackIndex++;
647
+ }
648
+
649
+ static void pushStrongContext (vString * name, contextType type)
650
+ {
651
+ pushContext (ContextStrong, type, &globalScope, name);
652
+ }
653
+
654
+ static void pushSoftContext (parseNext continuation,
655
+ vString * name, contextType type)
656
+ {
657
+ pushContext (ContextSoft, type, continuation, name);
658
+ }
659
+
660
+ static void pushEmptyContext (parseNext continuation)
661
+ {
662
+ pushContext (ContextSoft, ContextValue, continuation, NULL);
663
+ }
664
+
665
+ /* unroll the stack until the last named context.
666
+ * then discard it. Used to handle the :
667
+ * let f x y = ...
668
+ * in ...
669
+ * where the context is reseted after the in. Context may have
670
+ * been really nested before that. */
671
+ static void popLastNamed ( void )
672
+ {
673
+ int i = getLastNamedIndex ();
674
+
675
+ if (i >= 0)
676
+ {
677
+ stackIndex = i;
678
+ toDoNext = stack[i].callback;
679
+ vStringClear (stack[i].contextName);
680
+ }
681
+ else
682
+ {
683
+ /* ok, no named context found...
684
+ * (should not happen). */
685
+ stackIndex = 0;
686
+ toDoNext = &globalScope;
687
+ }
688
+ }
689
+
690
+ /* pop a context without regarding it's content
691
+ * (beside handling empty stack case) */
692
+ static void popSoftContext ( void )
693
+ {
694
+ if (stackIndex <= 0)
695
+ {
696
+ toDoNext = &globalScope;
697
+ }
698
+ else
699
+ {
700
+ stackIndex--;
701
+ toDoNext = stack[stackIndex].callback;
702
+ vStringClear (stack[stackIndex].contextName);
703
+ }
704
+ }
705
+
706
+ /* Reset everything until the last global space.
707
+ * a strong context can be :
708
+ * - module
709
+ * - class definition
710
+ * - the initial global space
711
+ * - a _global_ delcaration (let at global scope or in a module).
712
+ * Created to exit quickly deeply nested context */
713
+ static contextType popStrongContext ( void )
714
+ {
715
+ int i;
716
+
717
+ for (i = stackIndex - 1; i >= 0; --i)
718
+ {
719
+ if (stack[i].kind == ContextStrong)
720
+ {
721
+ stackIndex = i;
722
+ toDoNext = stack[i].callback;
723
+ vStringClear (stack[i].contextName);
724
+ return stack[i].type;
725
+ }
726
+ }
727
+ /* ok, no strong context found... */
728
+ stackIndex = 0;
729
+ toDoNext = &globalScope;
730
+ return -1;
731
+ }
732
+
733
+ /* Ignore everything till waitedToken and jump to comeAfter.
734
+ * If the "end" keyword is encountered break, doesn't remember
735
+ * why though. */
736
+ static void tillToken (vString * const UNUSED (ident), ocaToken what)
737
+ {
738
+ if (what == waitedToken)
739
+ toDoNext = comeAfter;
740
+ else if (what == OcaKEYWORD_end)
741
+ {
742
+ popStrongContext ();
743
+ toDoNext = &globalScope;
744
+ }
745
+ }
746
+
747
+ /* Ignore everything till a waitedToken is seen, but
748
+ * take care of balanced parentheses/bracket use */
749
+ static void contextualTillToken (vString * const UNUSED (ident), ocaToken what)
750
+ {
751
+ static int parentheses = 0;
752
+ static int bracket = 0;
753
+ static int curly = 0;
754
+
755
+ switch (what)
756
+ {
757
+ case Tok_PARL:
758
+ parentheses--;
759
+ break;
760
+ case Tok_PARR:
761
+ parentheses++;
762
+ break;
763
+ case Tok_CurlL:
764
+ curly--;
765
+ break;
766
+ case Tok_CurlR:
767
+ curly++;
768
+ break;
769
+ case Tok_BRL:
770
+ bracket--;
771
+ break;
772
+ case Tok_BRR:
773
+ bracket++;
774
+ break;
775
+
776
+ default: /* other token are ignored */
777
+ break;
778
+ }
779
+
780
+ if (what == waitedToken && parentheses == 0 && bracket == 0 && curly == 0)
781
+ toDoNext = comeAfter;
782
+
783
+ else if (what == OcaKEYWORD_end)
784
+ {
785
+ popStrongContext ();
786
+ toDoNext = &globalScope;
787
+ }
788
+ }
789
+
790
+ /* Wait for waitedToken and jump to comeAfter or let
791
+ * the globalScope handle declarations */
792
+ static void tillTokenOrFallback (vString * const ident, ocaToken what)
793
+ {
794
+ if (what == waitedToken)
795
+ toDoNext = comeAfter;
796
+ else
797
+ globalScope (ident, what);
798
+ }
799
+
800
+ /* ignore token till waitedToken, or give up if find
801
+ * terminatingToken. Use globalScope to handle new
802
+ * declarations. */
803
+ static void tillTokenOrTerminatingOrFallback (vString * const ident,
804
+ ocaToken what)
805
+ {
806
+ if (what == waitedToken)
807
+ toDoNext = comeAfter;
808
+ else if (what == terminatingToken)
809
+ toDoNext = globalScope;
810
+ else
811
+ globalScope (ident, what);
812
+ }
813
+
814
+ /* ignore the next token in the stream and jump to the
815
+ * given comeAfter state */
816
+ static void ignoreToken (vString * const UNUSED (ident), ocaToken UNUSED (what))
817
+ {
818
+ toDoNext = comeAfter;
819
+ }
820
+
821
+ /********** Grammar */
822
+ /* the purpose of each function is detailled near their
823
+ * implementation */
824
+
825
+ static void killCurrentState ( void )
826
+ {
827
+
828
+ /* Tracking the kind of previous strong
829
+ * context, if it doesn't match with a
830
+ * really strong entity, repop */
831
+ switch (popStrongContext ())
832
+ {
833
+
834
+ case ContextValue:
835
+ popStrongContext ();
836
+ break;
837
+ case ContextFunction:
838
+ popStrongContext ();
839
+ break;
840
+ case ContextMethod:
841
+ popStrongContext ();
842
+ break;
843
+
844
+ case ContextType:
845
+ popStrongContext();
846
+ break;
847
+ case ContextBlock:
848
+ break;
849
+ case ContextModule:
850
+ break;
851
+ case ContextClass:
852
+ break;
853
+ default:
854
+ /* nothing more */
855
+ break;
856
+ }
857
+ }
858
+
859
+ /* used to prepare tag for OCaml, just in case their is a need to
860
+ * add additional information to the tag. */
861
+ static void prepareTag (tagEntryInfo * tag, vString const *name, ocamlKind kind)
862
+ {
863
+ int parentIndex;
864
+
865
+ initTagEntry (tag, vStringValue (name));
866
+ tag->kindName = OcamlKinds[kind].name;
867
+ tag->kind = OcamlKinds[kind].letter;
868
+
869
+ parentIndex = getLastNamedIndex ();
870
+ if (parentIndex >= 0)
871
+ {
872
+ tag->extensionFields.scope[0] =
873
+ contextDescription (stack[parentIndex].type);
874
+ tag->extensionFields.scope[1] =
875
+ vStringValue (stack[parentIndex].contextName);
876
+ }
877
+ }
878
+
879
+ /* Used to centralise tag creation, and be able to add
880
+ * more information to it in the future */
881
+ static void addTag (vString * const ident, int kind)
882
+ {
883
+ tagEntryInfo toCreate;
884
+ prepareTag (&toCreate, ident, kind);
885
+ makeTagEntry (&toCreate);
886
+ }
887
+
888
+ boolean needStrongPoping = FALSE;
889
+ static void requestStrongPoping ( void )
890
+ {
891
+ needStrongPoping = TRUE;
892
+ }
893
+
894
+ static void cleanupPreviousParser ( void )
895
+ {
896
+ if (needStrongPoping)
897
+ {
898
+ needStrongPoping = FALSE;
899
+ popStrongContext ();
900
+ }
901
+ }
902
+
903
+ /* Due to some circular dependencies, the following functions
904
+ * must be forward-declared. */
905
+ static void letParam (vString * const ident, ocaToken what);
906
+ static void localScope (vString * const ident, ocaToken what);
907
+ static void mayRedeclare (vString * const ident, ocaToken what);
908
+ static void typeSpecification (vString * const ident, ocaToken what);
909
+
910
+ /*
911
+ * Parse a record type
912
+ * type ident = // parsed previously
913
+ * {
914
+ * ident1: type1;
915
+ * ident2: type2;
916
+ * }
917
+ */
918
+ static void typeRecord (vString * const ident, ocaToken what)
919
+ {
920
+ switch (what)
921
+ {
922
+ case OcaIDENTIFIER:
923
+ addTag (ident, K_RECORDFIELD);
924
+ terminatingToken = Tok_CurlR;
925
+ waitedToken = Tok_semi;
926
+ comeAfter = &typeRecord;
927
+ toDoNext = &tillTokenOrTerminatingOrFallback;
928
+ break;
929
+
930
+ case OcaKEYWORD_mutable:
931
+ /* ignore it */
932
+ break;
933
+
934
+ case Tok_CurlR:
935
+ popStrongContext ();
936
+ toDoNext = &globalScope;
937
+ break;
938
+
939
+ default: /* don't care */
940
+ break;
941
+ }
942
+ }
943
+
944
+ /* handle :
945
+ * exception ExceptionName ... */
946
+ static void exceptionDecl (vString * const ident, ocaToken what)
947
+ {
948
+ if (what == OcaIDENTIFIER)
949
+ {
950
+ addTag (ident, K_EXCEPTION);
951
+ }
952
+ /* don't know what to do on else... */
953
+
954
+ toDoNext = &globalScope;
955
+ }
956
+
957
+ tagEntryInfo tempTag;
958
+ vString *tempIdent;
959
+
960
+ /* Ensure a constructor is not a type path beginning
961
+ * with a module */
962
+ static void constructorValidation (vString * const ident, ocaToken what)
963
+ {
964
+ switch (what)
965
+ {
966
+ case Tok_Op: /* if we got a '.' which is an operator */
967
+ toDoNext = &globalScope;
968
+ popStrongContext ();
969
+ needStrongPoping = FALSE;
970
+ break;
971
+
972
+ case OcaKEYWORD_of: /* OK, it must be a constructor :) */
973
+ makeTagEntry (&tempTag);
974
+ vStringClear (tempIdent);
975
+ toDoNext = &tillTokenOrFallback;
976
+ comeAfter = &typeSpecification;
977
+ waitedToken = Tok_Pipe;
978
+ break;
979
+
980
+ case Tok_Pipe: /* OK, it was a constructor :) */
981
+ makeTagEntry (&tempTag);
982
+ vStringClear (tempIdent);
983
+ toDoNext = &typeSpecification;
984
+ break;
985
+
986
+ default: /* and mean that we're not facing a module name */
987
+ makeTagEntry (&tempTag);
988
+ vStringClear (tempIdent);
989
+ toDoNext = &tillTokenOrFallback;
990
+ comeAfter = &typeSpecification;
991
+ waitedToken = Tok_Pipe;
992
+
993
+ /* nothing in the context, discard it */
994
+ popStrongContext ();
995
+
996
+ /* to be sure we use this token */
997
+ globalScope (ident, what);
998
+ }
999
+ }
1000
+
1001
+
1002
+ /* Parse beginning of type definition
1003
+ * type 'avar ident =
1004
+ * or
1005
+ * type ('var1, 'var2) ident =
1006
+ */
1007
+ static void typeDecl (vString * const ident, ocaToken what)
1008
+ {
1009
+
1010
+ switch (what)
1011
+ {
1012
+ /* parameterized */
1013
+ case Tok_Prime:
1014
+ comeAfter = &typeDecl;
1015
+ toDoNext = &ignoreToken;
1016
+ break;
1017
+ /* LOTS of parameters */
1018
+ case Tok_PARL:
1019
+ comeAfter = &typeDecl;
1020
+ waitedToken = Tok_PARR;
1021
+ toDoNext = &tillToken;
1022
+ break;
1023
+
1024
+ case OcaIDENTIFIER:
1025
+ addTag (ident, K_TYPE);
1026
+ pushStrongContext (ident, ContextType);
1027
+ requestStrongPoping ();
1028
+ waitedToken = Tok_EQ;
1029
+ comeAfter = &typeSpecification;
1030
+ toDoNext = &tillTokenOrFallback;
1031
+ break;
1032
+
1033
+ default:
1034
+ globalScope (ident, what);
1035
+ }
1036
+ }
1037
+
1038
+ /* Parse type of kind
1039
+ * type bidule = Ctor1 of ...
1040
+ * | Ctor2
1041
+ * | Ctor3 of ...
1042
+ * or
1043
+ * type bidule = | Ctor1 of ... | Ctor2
1044
+ *
1045
+ * when type bidule = { ... } is detected,
1046
+ * let typeRecord handle it. */
1047
+ static void typeSpecification (vString * const ident, ocaToken what)
1048
+ {
1049
+
1050
+ switch (what)
1051
+ {
1052
+ case OcaIDENTIFIER:
1053
+ if (isUpperAlpha (ident->buffer[0]))
1054
+ {
1055
+ /* here we handle type aliases of type
1056
+ * type foo = AnotherModule.bar
1057
+ * AnotherModule can mistakenly be took
1058
+ * for a constructor. */
1059
+ vStringCopy (tempIdent, ident);
1060
+ prepareTag (&tempTag, tempIdent, K_CONSTRUCTOR);
1061
+ toDoNext = &constructorValidation;
1062
+ }
1063
+ else
1064
+ {
1065
+ toDoNext = &tillTokenOrFallback;
1066
+ comeAfter = &typeSpecification;
1067
+ waitedToken = Tok_Pipe;
1068
+ }
1069
+ break;
1070
+
1071
+ case OcaKEYWORD_and:
1072
+ toDoNext = &typeDecl;
1073
+ break;
1074
+
1075
+ case Tok_BRL: /* the '[' & ']' are ignored to accommodate */
1076
+ case Tok_BRR: /* with the revised syntax */
1077
+ case Tok_Pipe:
1078
+ /* just ignore it */
1079
+ break;
1080
+
1081
+ case Tok_CurlL:
1082
+ toDoNext = &typeRecord;
1083
+ break;
1084
+
1085
+ default: /* don't care */
1086
+ break;
1087
+ }
1088
+ }
1089
+
1090
+
1091
+ static boolean dirtySpecialParam = FALSE;
1092
+
1093
+
1094
+ /* parse the ~label and ~label:type parameter */
1095
+ static void parseLabel (vString * const ident, ocaToken what)
1096
+ {
1097
+ static int parCount = 0;
1098
+
1099
+ switch (what)
1100
+ {
1101
+ case OcaIDENTIFIER:
1102
+ if (!dirtySpecialParam)
1103
+ {
1104
+
1105
+ if (exportLocalInfo)
1106
+ addTag (ident, K_VAR);
1107
+
1108
+ dirtySpecialParam = TRUE;
1109
+ }
1110
+ break;
1111
+
1112
+ case Tok_PARL:
1113
+ parCount++;
1114
+ break;
1115
+
1116
+ case Tok_PARR:
1117
+ parCount--;
1118
+ if (parCount == 0)
1119
+ toDoNext = &letParam;
1120
+ break;
1121
+
1122
+ case Tok_Op:
1123
+ if (ident->buffer[0] == ':')
1124
+ {
1125
+ toDoNext = &ignoreToken;
1126
+ comeAfter = &letParam;
1127
+ }
1128
+ else if (parCount == 0 && dirtySpecialParam)
1129
+ {
1130
+ toDoNext = &letParam;
1131
+ letParam (ident, what);
1132
+ }
1133
+ break;
1134
+
1135
+ default:
1136
+ if (parCount == 0 && dirtySpecialParam)
1137
+ {
1138
+ toDoNext = &letParam;
1139
+ letParam (ident, what);
1140
+ }
1141
+ break;
1142
+ }
1143
+ }
1144
+
1145
+
1146
+ /* Optional argument with syntax like this :
1147
+ * ?(foo = value) */
1148
+ static void parseOptionnal (vString * const ident, ocaToken what)
1149
+ {
1150
+ static int parCount = 0;
1151
+
1152
+
1153
+ switch (what)
1154
+ {
1155
+ case OcaIDENTIFIER:
1156
+ if (!dirtySpecialParam)
1157
+ {
1158
+ if (exportLocalInfo)
1159
+ addTag (ident, K_VAR);
1160
+
1161
+ dirtySpecialParam = TRUE;
1162
+
1163
+ if (parCount == 0)
1164
+ toDoNext = &letParam;
1165
+ }
1166
+ break;
1167
+
1168
+ case Tok_PARL:
1169
+ parCount++;
1170
+ break;
1171
+
1172
+ case Tok_PARR:
1173
+ parCount--;
1174
+ if (parCount == 0)
1175
+ toDoNext = &letParam;
1176
+ break;
1177
+
1178
+ default: /* don't care */
1179
+ break;
1180
+ }
1181
+ }
1182
+
1183
+
1184
+ /** handle let inside functions (so like it's name
1185
+ * say : local let */
1186
+ static void localLet (vString * const ident, ocaToken what)
1187
+ {
1188
+ switch (what)
1189
+ {
1190
+ case Tok_PARL:
1191
+ /* We ignore this token to be able to parse such
1192
+ * declarations :
1193
+ * let (ident : type) = ...
1194
+ */
1195
+ break;
1196
+
1197
+ case OcaKEYWORD_rec:
1198
+ /* just ignore to be able to parse such declarations:
1199
+ * let rec ident = ... */
1200
+ break;
1201
+
1202
+ case Tok_Op:
1203
+ /* we are defining a new operator, it's a
1204
+ * function definition */
1205
+ if (exportLocalInfo)
1206
+ addTag (ident, K_FUNCTION);
1207
+
1208
+ pushSoftContext (mayRedeclare, ident, ContextFunction);
1209
+ toDoNext = &letParam;
1210
+ break;
1211
+
1212
+ /* Can be a weiiird binding, or an '_' */
1213
+ case Tok_Val:
1214
+ if (exportLocalInfo)
1215
+ addTag (ident, K_VAR);
1216
+ pushSoftContext (mayRedeclare, ident, ContextValue);
1217
+ toDoNext = &letParam;
1218
+ break;
1219
+
1220
+ case OcaIDENTIFIER:
1221
+ if (exportLocalInfo)
1222
+ addTag (ident, K_VAR);
1223
+ pushSoftContext (mayRedeclare, ident, ContextValue);
1224
+ toDoNext = &letParam;
1225
+ break;
1226
+
1227
+ case OcaKEYWORD_end:
1228
+ popStrongContext ();
1229
+ break;
1230
+
1231
+ default:
1232
+ toDoNext = &localScope;
1233
+ break;
1234
+ }
1235
+ }
1236
+
1237
+ /* parse :
1238
+ * | pattern pattern -> ...
1239
+ * or
1240
+ * pattern apttern apttern -> ...
1241
+ * we ignore all identifiers declared in the pattern,
1242
+ * because their scope is likely to be even more limited
1243
+ * than the let definitions.
1244
+ * Used after a match ... with, or a function ... or fun ...
1245
+ * because their syntax is similar. */
1246
+ static void matchPattern (vString * const UNUSED (ident), ocaToken what)
1247
+ {
1248
+ switch (what)
1249
+ {
1250
+ case Tok_To:
1251
+ pushEmptyContext (&matchPattern);
1252
+ toDoNext = &mayRedeclare;
1253
+ break;
1254
+
1255
+
1256
+ case OcaKEYWORD_in:
1257
+ popLastNamed ();
1258
+ break;
1259
+
1260
+ default:
1261
+ break;
1262
+ }
1263
+ }
1264
+
1265
+ /* Used at the beginning of a new scope (begin of a
1266
+ * definition, parenthesis...) to catch inner let
1267
+ * definition that may be in. */
1268
+ static void mayRedeclare (vString * const ident, ocaToken what)
1269
+ {
1270
+ switch (what)
1271
+ {
1272
+ case OcaKEYWORD_let:
1273
+ case OcaKEYWORD_val:
1274
+ toDoNext = localLet;
1275
+ break;
1276
+
1277
+ case OcaKEYWORD_object:
1278
+ vStringClear (lastClass);
1279
+ pushContext (ContextStrong, ContextClass,
1280
+ &localScope, NULL /*voidName */ );
1281
+ needStrongPoping = FALSE;
1282
+ toDoNext = &globalScope;
1283
+ break;
1284
+
1285
+ case OcaKEYWORD_for:
1286
+ case OcaKEYWORD_while:
1287
+ toDoNext = &tillToken;
1288
+ waitedToken = OcaKEYWORD_do;
1289
+ comeAfter = &mayRedeclare;
1290
+ break;
1291
+
1292
+ case OcaKEYWORD_try:
1293
+ toDoNext = &mayRedeclare;
1294
+ pushSoftContext (matchPattern, ident, ContextFunction);
1295
+ break;
1296
+
1297
+ case OcaKEYWORD_fun:
1298
+ toDoNext = &matchPattern;
1299
+ break;
1300
+
1301
+ /* Handle the special ;; from the OCaml
1302
+ * Top level */
1303
+ case Tok_semi:
1304
+ default:
1305
+ toDoNext = &localScope;
1306
+ localScope (ident, what);
1307
+ }
1308
+ }
1309
+
1310
+ /* parse :
1311
+ * p1 p2 ... pn = ...
1312
+ * or
1313
+ * ?(p1=v) p2 ~p3 ~pn:ja ... = ... */
1314
+ static void letParam (vString * const ident, ocaToken what)
1315
+ {
1316
+ switch (what)
1317
+ {
1318
+ case Tok_EQ:
1319
+ toDoNext = &mayRedeclare;
1320
+ break;
1321
+
1322
+ case OcaIDENTIFIER:
1323
+ if (exportLocalInfo)
1324
+ addTag (ident, K_VAR);
1325
+ break;
1326
+
1327
+ case Tok_Op:
1328
+ switch (ident->buffer[0])
1329
+ {
1330
+ case ':':
1331
+ /*popSoftContext(); */
1332
+ /* we got a type signature */
1333
+ comeAfter = &mayRedeclare;
1334
+ toDoNext = &tillTokenOrFallback;
1335
+ waitedToken = Tok_EQ;
1336
+ break;
1337
+
1338
+ /* parse something like
1339
+ * ~varname:type
1340
+ * or
1341
+ * ~varname
1342
+ * or
1343
+ * ~(varname: long type) */
1344
+ case '~':
1345
+ toDoNext = &parseLabel;
1346
+ dirtySpecialParam = FALSE;
1347
+ break;
1348
+
1349
+ /* Optional argument with syntax like this :
1350
+ * ?(bla = value)
1351
+ * or
1352
+ * ?bla */
1353
+ case '?':
1354
+ toDoNext = &parseOptionnal;
1355
+ dirtySpecialParam = FALSE;
1356
+ break;
1357
+
1358
+ default:
1359
+ break;
1360
+ }
1361
+ break;
1362
+
1363
+ default: /* don't care */
1364
+ break;
1365
+ }
1366
+ }
1367
+
1368
+
1369
+ /* parse object ...
1370
+ * used to be sure the class definition is not a type
1371
+ * alias */
1372
+ static void classSpecif (vString * const UNUSED (ident), ocaToken what)
1373
+ {
1374
+ switch (what)
1375
+ {
1376
+ case OcaKEYWORD_object:
1377
+ pushStrongContext (lastClass, ContextClass);
1378
+ toDoNext = &globalScope;
1379
+ break;
1380
+
1381
+ default:
1382
+ vStringClear (lastClass);
1383
+ toDoNext = &globalScope;
1384
+ }
1385
+ }
1386
+
1387
+ /* Handle a method ... class declaration.
1388
+ * nearly a copy/paste of globalLet. */
1389
+ static void methodDecl (vString * const ident, ocaToken what)
1390
+ {
1391
+ switch (what)
1392
+ {
1393
+ case Tok_PARL:
1394
+ /* We ignore this token to be able to parse such
1395
+ * declarations :
1396
+ * let (ident : type) = ... */
1397
+ break;
1398
+
1399
+ case OcaKEYWORD_mutable:
1400
+ case OcaKEYWORD_virtual:
1401
+ case OcaKEYWORD_rec:
1402
+ /* just ignore to be able to parse such declarations:
1403
+ * let rec ident = ... */
1404
+ break;
1405
+
1406
+ case OcaIDENTIFIER:
1407
+ addTag (ident, K_METHOD);
1408
+ /* Normal pushing to get good subs */
1409
+ pushStrongContext (ident, ContextMethod);
1410
+ /*pushSoftContext( globalScope, ident, ContextMethod ); */
1411
+ toDoNext = &letParam;
1412
+ break;
1413
+
1414
+ case OcaKEYWORD_end:
1415
+ popStrongContext ();
1416
+ break;
1417
+
1418
+ default:
1419
+ toDoNext = &globalScope;
1420
+ break;
1421
+ }
1422
+ }
1423
+
1424
+ /* name of the last module, used for
1425
+ * context stacking. */
1426
+ vString *lastModule;
1427
+
1428
+
1429
+ /* parse
1430
+ * ... struct (* new global scope *) end
1431
+ * or
1432
+ * ... sig (* new global scope *) end
1433
+ * or
1434
+ * functor ... -> moduleSpecif
1435
+ */
1436
+ static void moduleSpecif (vString * const ident, ocaToken what)
1437
+ {
1438
+ switch (what)
1439
+ {
1440
+ case OcaKEYWORD_functor:
1441
+ toDoNext = &contextualTillToken;
1442
+ waitedToken = Tok_To;
1443
+ comeAfter = &moduleSpecif;
1444
+ break;
1445
+
1446
+ case OcaKEYWORD_struct:
1447
+ case OcaKEYWORD_sig:
1448
+ pushStrongContext (lastModule, ContextModule);
1449
+ toDoNext = &globalScope;
1450
+ break;
1451
+
1452
+ case Tok_PARL: /* ( */
1453
+ toDoNext = &contextualTillToken;
1454
+ comeAfter = &globalScope;
1455
+ waitedToken = Tok_PARR;
1456
+ contextualTillToken (ident, what);
1457
+ break;
1458
+
1459
+ default:
1460
+ vStringClear (lastModule);
1461
+ toDoNext = &globalScope;
1462
+ }
1463
+ }
1464
+
1465
+ /* parse :
1466
+ * module name = ...
1467
+ * then pass the token stream to moduleSpecif */
1468
+ static void moduleDecl (vString * const ident, ocaToken what)
1469
+ {
1470
+ switch (what)
1471
+ {
1472
+ case OcaKEYWORD_type:
1473
+ /* just ignore it, name come after */
1474
+ break;
1475
+
1476
+ case OcaIDENTIFIER:
1477
+ addTag (ident, K_MODULE);
1478
+ vStringCopy (lastModule, ident);
1479
+ waitedToken = Tok_EQ;
1480
+ comeAfter = &moduleSpecif;
1481
+ toDoNext = &contextualTillToken;
1482
+ break;
1483
+
1484
+ default: /* don't care */
1485
+ break;
1486
+ }
1487
+ }
1488
+
1489
+ /* parse :
1490
+ * class name = ...
1491
+ * or
1492
+ * class virtual ['a,'b] classname = ... */
1493
+ static void classDecl (vString * const ident, ocaToken what)
1494
+ {
1495
+ switch (what)
1496
+ {
1497
+ case OcaIDENTIFIER:
1498
+ addTag (ident, K_CLASS);
1499
+ vStringCopy (lastClass, ident);
1500
+ toDoNext = &contextualTillToken;
1501
+ waitedToken = Tok_EQ;
1502
+ comeAfter = &classSpecif;
1503
+ break;
1504
+
1505
+ case Tok_BRL:
1506
+ toDoNext = &tillToken;
1507
+ waitedToken = Tok_BRR;
1508
+ comeAfter = &classDecl;
1509
+ break;
1510
+
1511
+ default:
1512
+ break;
1513
+ }
1514
+ }
1515
+
1516
+ /* Handle a global
1517
+ * let ident ...
1518
+ * or
1519
+ * let rec ident ... */
1520
+ static void globalLet (vString * const ident, ocaToken what)
1521
+ {
1522
+ switch (what)
1523
+ {
1524
+ case Tok_PARL:
1525
+ /* We ignore this token to be able to parse such
1526
+ * declarations :
1527
+ * let (ident : type) = ...
1528
+ */
1529
+ break;
1530
+
1531
+ case OcaKEYWORD_mutable:
1532
+ case OcaKEYWORD_virtual:
1533
+ case OcaKEYWORD_rec:
1534
+ /* just ignore to be able to parse such declarations:
1535
+ * let rec ident = ... */
1536
+ break;
1537
+
1538
+ case Tok_Op:
1539
+ /* we are defining a new operator, it's a
1540
+ * function definition */
1541
+ addTag (ident, K_FUNCTION);
1542
+ pushStrongContext (ident, ContextFunction);
1543
+ toDoNext = &letParam;
1544
+ break;
1545
+
1546
+ case OcaIDENTIFIER:
1547
+ addTag (ident, K_VAR);
1548
+ pushStrongContext (ident, ContextValue);
1549
+ requestStrongPoping ();
1550
+ toDoNext = &letParam;
1551
+ break;
1552
+
1553
+ case OcaKEYWORD_end:
1554
+ popStrongContext ();
1555
+ break;
1556
+
1557
+ default:
1558
+ toDoNext = &globalScope;
1559
+ break;
1560
+ }
1561
+ }
1562
+
1563
+ /* Handle the "strong" top levels, all 'big' declarations
1564
+ * happen here */
1565
+ static void globalScope (vString * const UNUSED (ident), ocaToken what)
1566
+ {
1567
+ /* Do not touch, this is used only by the global scope
1568
+ * to handle an 'and' */
1569
+ static parseNext previousParser = NULL;
1570
+
1571
+ switch (what)
1572
+ {
1573
+ case OcaKEYWORD_and:
1574
+ cleanupPreviousParser ();
1575
+ toDoNext = previousParser;
1576
+ break;
1577
+
1578
+ case OcaKEYWORD_type:
1579
+ cleanupPreviousParser ();
1580
+ toDoNext = &typeDecl;
1581
+ previousParser = &typeDecl;
1582
+ break;
1583
+
1584
+ case OcaKEYWORD_class:
1585
+ cleanupPreviousParser ();
1586
+ toDoNext = &classDecl;
1587
+ previousParser = &classDecl;
1588
+ break;
1589
+
1590
+ case OcaKEYWORD_module:
1591
+ cleanupPreviousParser ();
1592
+ toDoNext = &moduleDecl;
1593
+ previousParser = &moduleDecl;
1594
+ break;
1595
+
1596
+ case OcaKEYWORD_end:
1597
+ needStrongPoping = FALSE;
1598
+ killCurrentState ();
1599
+ /*popStrongContext(); */
1600
+ break;
1601
+
1602
+ case OcaKEYWORD_method:
1603
+ cleanupPreviousParser ();
1604
+ toDoNext = &methodDecl;
1605
+ /* and is not allowed in methods */
1606
+ break;
1607
+
1608
+ /* val is mixed with let as global
1609
+ * to be able to handle mli & new syntax */
1610
+ case OcaKEYWORD_val:
1611
+ case OcaKEYWORD_let:
1612
+ cleanupPreviousParser ();
1613
+ toDoNext = &globalLet;
1614
+ previousParser = &globalLet;
1615
+ break;
1616
+
1617
+ case OcaKEYWORD_exception:
1618
+ cleanupPreviousParser ();
1619
+ toDoNext = &exceptionDecl;
1620
+ previousParser = NULL;
1621
+ break;
1622
+
1623
+ /* must be a #line directive, discard the
1624
+ * whole line. */
1625
+ case Tok_Sharp:
1626
+ /* ignore */
1627
+ break;
1628
+
1629
+ default:
1630
+ /* we don't care */
1631
+ break;
1632
+ }
1633
+ }
1634
+
1635
+ /* Parse expression. Well ignore it is more the case,
1636
+ * ignore all tokens except "shocking" keywords */
1637
+ static void localScope (vString * const ident, ocaToken what)
1638
+ {
1639
+ switch (what)
1640
+ {
1641
+ case Tok_Pipe:
1642
+ case Tok_PARR:
1643
+ case Tok_BRR:
1644
+ case Tok_CurlR:
1645
+ popSoftContext ();
1646
+ break;
1647
+
1648
+ /* Everything that `begin` has an `end`
1649
+ * as end is overloaded and signal many end
1650
+ * of things, we add an empty strong context to
1651
+ * avoid problem with the end.
1652
+ */
1653
+ case OcaKEYWORD_begin:
1654
+ pushContext (ContextStrong, ContextBlock, &mayRedeclare, NULL);
1655
+ toDoNext = &mayRedeclare;
1656
+ break;
1657
+
1658
+ case OcaKEYWORD_in:
1659
+ popLastNamed ();
1660
+ break;
1661
+
1662
+ /* Ok, we got a '{', which is much likely to create
1663
+ * a record. We cannot treat it like other [ && (,
1664
+ * because it may contain the 'with' keyword and screw
1665
+ * everything else. */
1666
+ case Tok_CurlL:
1667
+ toDoNext = &contextualTillToken;
1668
+ waitedToken = Tok_CurlR;
1669
+ comeAfter = &localScope;
1670
+ contextualTillToken (ident, what);
1671
+ break;
1672
+
1673
+ /* Yeah imperative feature of OCaml,
1674
+ * a ';' like in C */
1675
+ case Tok_semi:
1676
+ toDoNext = &mayRedeclare;
1677
+ break;
1678
+
1679
+ case Tok_PARL:
1680
+ case Tok_BRL:
1681
+ pushEmptyContext (&localScope);
1682
+ toDoNext = &mayRedeclare;
1683
+ break;
1684
+
1685
+ case OcaKEYWORD_and:
1686
+ popLastNamed ();
1687
+ toDoNext = &localLet;
1688
+ break;
1689
+
1690
+ case OcaKEYWORD_else:
1691
+ case OcaKEYWORD_then:
1692
+ popSoftContext ();
1693
+ pushEmptyContext (&localScope);
1694
+ toDoNext = &mayRedeclare;
1695
+ break;
1696
+
1697
+ case OcaKEYWORD_if:
1698
+ pushEmptyContext (&localScope);
1699
+ toDoNext = &mayRedeclare;
1700
+ break;
1701
+
1702
+ case OcaKEYWORD_match:
1703
+ pushEmptyContext (&localScope);
1704
+ toDoNext = &mayRedeclare;
1705
+ break;
1706
+
1707
+ case OcaKEYWORD_with:
1708
+ popSoftContext ();
1709
+ toDoNext = &matchPattern;
1710
+ pushEmptyContext (&matchPattern);
1711
+ break;
1712
+
1713
+ case OcaKEYWORD_end:
1714
+ killCurrentState ();
1715
+ break;
1716
+
1717
+
1718
+ case OcaKEYWORD_fun:
1719
+ comeAfter = &mayRedeclare;
1720
+ toDoNext = &tillToken;
1721
+ waitedToken = Tok_To;
1722
+ break;
1723
+
1724
+ case OcaKEYWORD_done:
1725
+ case OcaKEYWORD_val:
1726
+ /* doesn't care */
1727
+ break;
1728
+
1729
+ default:
1730
+ requestStrongPoping ();
1731
+ globalScope (ident, what);
1732
+ break;
1733
+ }
1734
+ }
1735
+
1736
+ /*////////////////////////////////////////////////////////////////
1737
+ //// Deal with the system */
1738
+ /* in OCaml the file name is the module name used in the language
1739
+ * with it first letter put in upper case */
1740
+ static void computeModuleName ( void )
1741
+ {
1742
+ /* in Ocaml the file name define a module.
1743
+ * so we define a module =)
1744
+ */
1745
+ const char *filename = getSourceFileName ();
1746
+ int beginIndex = 0;
1747
+ int endIndex = strlen (filename) - 1;
1748
+ vString *moduleName = vStringNew ();
1749
+
1750
+ while (filename[endIndex] != '.' && endIndex > 0)
1751
+ endIndex--;
1752
+
1753
+ /* avoid problem with path in front of filename */
1754
+ beginIndex = endIndex;
1755
+ while (beginIndex > 0)
1756
+ {
1757
+ if (filename[beginIndex] == '\\' || filename[beginIndex] == '/')
1758
+ {
1759
+ beginIndex++;
1760
+ break;
1761
+ }
1762
+
1763
+ beginIndex--;
1764
+ }
1765
+
1766
+ vStringNCopyS (moduleName, &filename[beginIndex], endIndex - beginIndex);
1767
+ vStringTerminate (moduleName);
1768
+
1769
+ if (isLowerAlpha (moduleName->buffer[0]))
1770
+ moduleName->buffer[0] += ('A' - 'a');
1771
+
1772
+ makeSimpleTag (moduleName, OcamlKinds, K_MODULE);
1773
+ vStringDelete (moduleName);
1774
+ }
1775
+
1776
+ /* Allocate all string of the context stack */
1777
+ static void initStack ( void )
1778
+ {
1779
+ int i;
1780
+ for (i = 0; i < OCAML_MAX_STACK_SIZE; ++i)
1781
+ stack[i].contextName = vStringNew ();
1782
+ }
1783
+
1784
+ static void clearStack ( void )
1785
+ {
1786
+ int i;
1787
+ for (i = 0; i < OCAML_MAX_STACK_SIZE; ++i)
1788
+ vStringDelete (stack[i].contextName);
1789
+ }
1790
+
1791
+ static void findOcamlTags (void)
1792
+ {
1793
+ vString *name = vStringNew ();
1794
+ lexingState st;
1795
+ ocaToken tok;
1796
+
1797
+ computeModuleName ();
1798
+ initStack ();
1799
+ tempIdent = vStringNew ();
1800
+ lastModule = vStringNew ();
1801
+ lastClass = vStringNew ();
1802
+ voidName = vStringNew ();
1803
+ vStringCopyS (voidName, "_");
1804
+
1805
+ st.name = vStringNew ();
1806
+ st.cp = fileReadLine ();
1807
+ toDoNext = &globalScope;
1808
+ tok = lex (&st);
1809
+ while (tok != Tok_EOF)
1810
+ {
1811
+ (*toDoNext) (st.name, tok);
1812
+ tok = lex (&st);
1813
+ }
1814
+
1815
+ vStringDelete (name);
1816
+ vStringDelete (voidName);
1817
+ vStringDelete (tempIdent);
1818
+ vStringDelete (lastModule);
1819
+ vStringDelete (lastClass);
1820
+ clearStack ();
1821
+ }
1822
+
1823
+ static void ocamlInitialize (const langType language)
1824
+ {
1825
+ Lang_Ocaml = language;
1826
+
1827
+ initOperatorTable ();
1828
+ initKeywordHash ();
1829
+ }
1830
+
1831
+ extern parserDefinition *OcamlParser (void)
1832
+ {
1833
+ static const char *const extensions[] = { "ml", "mli", NULL };
1834
+ parserDefinition *def = parserNew ("OCaml");
1835
+ def->kinds = OcamlKinds;
1836
+ def->kindCount = KIND_COUNT (OcamlKinds);
1837
+ def->extensions = extensions;
1838
+ def->parser = findOcamlTags;
1839
+ def->initialize = ocamlInitialize;
1840
+
1841
+ return def;
1842
+ }