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,2112 @@
1
+ /*
2
+ * $Id: sql.c 703 2009-03-14 22:06:12Z dfishburn $
3
+ *
4
+ * Copyright (c) 2002-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 PL/SQL language
10
+ * files.
11
+ */
12
+
13
+ /*
14
+ * INCLUDE FILES
15
+ */
16
+ #include "general.h" /* must always come first */
17
+
18
+ #include <ctype.h> /* to define isalpha () */
19
+ #include <setjmp.h>
20
+ #ifdef DEBUG
21
+ #include <stdio.h>
22
+ #endif
23
+
24
+ #include "debug.h"
25
+ #include "entry.h"
26
+ #include "keyword.h"
27
+ #include "parse.h"
28
+ #include "read.h"
29
+ #include "routines.h"
30
+ #include "vstring.h"
31
+
32
+ /*
33
+ * On-line "Oracle Database PL/SQL Language Reference":
34
+ * http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm
35
+ *
36
+ * Sample PL/SQL code is available from:
37
+ * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
38
+ *
39
+ * On-line SQL Anywhere Documentation
40
+ * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html
41
+ */
42
+
43
+ /*
44
+ * MACROS
45
+ */
46
+ #define isType(token,t) (boolean) ((token)->type == (t))
47
+ #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
48
+
49
+ /*
50
+ * DATA DECLARATIONS
51
+ */
52
+
53
+ typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
54
+
55
+ /*
56
+ * Used to specify type of keyword.
57
+ */
58
+ typedef enum eKeywordId {
59
+ KEYWORD_NONE = -1,
60
+ KEYWORD_is,
61
+ KEYWORD_begin,
62
+ KEYWORD_body,
63
+ KEYWORD_cursor,
64
+ KEYWORD_declare,
65
+ KEYWORD_end,
66
+ KEYWORD_function,
67
+ KEYWORD_if,
68
+ KEYWORD_loop,
69
+ KEYWORD_case,
70
+ KEYWORD_for,
71
+ KEYWORD_call,
72
+ KEYWORD_package,
73
+ KEYWORD_pragma,
74
+ KEYWORD_procedure,
75
+ KEYWORD_record,
76
+ KEYWORD_object,
77
+ KEYWORD_ref,
78
+ KEYWORD_rem,
79
+ KEYWORD_return,
80
+ KEYWORD_returns,
81
+ KEYWORD_subtype,
82
+ KEYWORD_table,
83
+ KEYWORD_trigger,
84
+ KEYWORD_type,
85
+ KEYWORD_index,
86
+ KEYWORD_event,
87
+ KEYWORD_publication,
88
+ KEYWORD_service,
89
+ KEYWORD_domain,
90
+ KEYWORD_datatype,
91
+ KEYWORD_result,
92
+ KEYWORD_url,
93
+ KEYWORD_internal,
94
+ KEYWORD_external,
95
+ KEYWORD_when,
96
+ KEYWORD_then,
97
+ KEYWORD_variable,
98
+ KEYWORD_exception,
99
+ KEYWORD_at,
100
+ KEYWORD_on,
101
+ KEYWORD_primary,
102
+ KEYWORD_references,
103
+ KEYWORD_unique,
104
+ KEYWORD_check,
105
+ KEYWORD_constraint,
106
+ KEYWORD_foreign,
107
+ KEYWORD_ml_table,
108
+ KEYWORD_ml_table_lang,
109
+ KEYWORD_ml_table_dnet,
110
+ KEYWORD_ml_table_java,
111
+ KEYWORD_ml_table_chk,
112
+ KEYWORD_ml_conn,
113
+ KEYWORD_ml_conn_lang,
114
+ KEYWORD_ml_conn_dnet,
115
+ KEYWORD_ml_conn_java,
116
+ KEYWORD_ml_conn_chk,
117
+ KEYWORD_local,
118
+ KEYWORD_temporary,
119
+ KEYWORD_drop,
120
+ KEYWORD_view,
121
+ KEYWORD_synonym,
122
+ KEYWORD_handler,
123
+ KEYWORD_comment,
124
+ KEYWORD_create,
125
+ KEYWORD_go
126
+ } keywordId;
127
+
128
+ /*
129
+ * Used to determine whether keyword is valid for the token language and
130
+ * what its ID is.
131
+ */
132
+ typedef struct sKeywordDesc {
133
+ const char *name;
134
+ keywordId id;
135
+ } keywordDesc;
136
+
137
+ typedef enum eTokenType {
138
+ TOKEN_UNDEFINED,
139
+ TOKEN_BLOCK_LABEL_BEGIN,
140
+ TOKEN_BLOCK_LABEL_END,
141
+ TOKEN_CHARACTER,
142
+ TOKEN_CLOSE_PAREN,
143
+ TOKEN_SEMICOLON,
144
+ TOKEN_COMMA,
145
+ TOKEN_IDENTIFIER,
146
+ TOKEN_KEYWORD,
147
+ TOKEN_OPEN_PAREN,
148
+ TOKEN_OPERATOR,
149
+ TOKEN_OTHER,
150
+ TOKEN_STRING,
151
+ TOKEN_PERIOD,
152
+ TOKEN_OPEN_CURLY,
153
+ TOKEN_CLOSE_CURLY,
154
+ TOKEN_OPEN_SQUARE,
155
+ TOKEN_CLOSE_SQUARE,
156
+ TOKEN_TILDE,
157
+ TOKEN_FORWARD_SLASH
158
+ } tokenType;
159
+
160
+ typedef struct sTokenInfoSQL {
161
+ tokenType type;
162
+ keywordId keyword;
163
+ vString * string;
164
+ vString * scope;
165
+ int begin_end_nest_lvl;
166
+ unsigned long lineNumber;
167
+ fpos_t filePosition;
168
+ } tokenInfo;
169
+
170
+ /*
171
+ * DATA DEFINITIONS
172
+ */
173
+
174
+ static langType Lang_sql;
175
+
176
+ static jmp_buf Exception;
177
+
178
+ typedef enum {
179
+ SQLTAG_CURSOR,
180
+ SQLTAG_PROTOTYPE,
181
+ SQLTAG_FUNCTION,
182
+ SQLTAG_FIELD,
183
+ SQLTAG_LOCAL_VARIABLE,
184
+ SQLTAG_BLOCK_LABEL,
185
+ SQLTAG_PACKAGE,
186
+ SQLTAG_PROCEDURE,
187
+ SQLTAG_RECORD,
188
+ SQLTAG_SUBTYPE,
189
+ SQLTAG_TABLE,
190
+ SQLTAG_TRIGGER,
191
+ SQLTAG_VARIABLE,
192
+ SQLTAG_INDEX,
193
+ SQLTAG_EVENT,
194
+ SQLTAG_PUBLICATION,
195
+ SQLTAG_SERVICE,
196
+ SQLTAG_DOMAIN,
197
+ SQLTAG_VIEW,
198
+ SQLTAG_SYNONYM,
199
+ SQLTAG_MLTABLE,
200
+ SQLTAG_MLCONN,
201
+ SQLTAG_COUNT
202
+ } sqlKind;
203
+
204
+ static kindOption SqlKinds [] = {
205
+ { TRUE, 'c', "cursor", "cursors" },
206
+ { FALSE, 'd', "prototype", "prototypes" },
207
+ { TRUE, 'f', "function", "functions" },
208
+ { TRUE, 'F', "field", "record fields" },
209
+ { FALSE, 'l', "local", "local variables" },
210
+ { TRUE, 'L', "label", "block label" },
211
+ { TRUE, 'P', "package", "packages" },
212
+ { TRUE, 'p', "procedure", "procedures" },
213
+ { FALSE, 'r', "record", "records" },
214
+ { TRUE, 's', "subtype", "subtypes" },
215
+ { TRUE, 't', "table", "tables" },
216
+ { TRUE, 'T', "trigger", "triggers" },
217
+ { TRUE, 'v', "variable", "variables" },
218
+ { TRUE, 'i', "index", "indexes" },
219
+ { TRUE, 'e', "event", "events" },
220
+ { TRUE, 'U', "publication", "publications" },
221
+ { TRUE, 'R', "service", "services" },
222
+ { TRUE, 'D', "domain", "domains" },
223
+ { TRUE, 'V', "view", "views" },
224
+ { TRUE, 'n', "synonym", "synonyms" },
225
+ { TRUE, 'x', "mltable", "MobiLink Table Scripts" },
226
+ { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" }
227
+ };
228
+
229
+ static const keywordDesc SqlKeywordTable [] = {
230
+ /* keyword keyword ID */
231
+ { "as", KEYWORD_is },
232
+ { "is", KEYWORD_is },
233
+ { "begin", KEYWORD_begin },
234
+ { "body", KEYWORD_body },
235
+ { "cursor", KEYWORD_cursor },
236
+ { "declare", KEYWORD_declare },
237
+ { "end", KEYWORD_end },
238
+ { "function", KEYWORD_function },
239
+ { "if", KEYWORD_if },
240
+ { "loop", KEYWORD_loop },
241
+ { "case", KEYWORD_case },
242
+ { "for", KEYWORD_for },
243
+ { "call", KEYWORD_call },
244
+ { "package", KEYWORD_package },
245
+ { "pragma", KEYWORD_pragma },
246
+ { "procedure", KEYWORD_procedure },
247
+ { "record", KEYWORD_record },
248
+ { "object", KEYWORD_object },
249
+ { "ref", KEYWORD_ref },
250
+ { "rem", KEYWORD_rem },
251
+ { "return", KEYWORD_return },
252
+ { "returns", KEYWORD_returns },
253
+ { "subtype", KEYWORD_subtype },
254
+ { "table", KEYWORD_table },
255
+ { "trigger", KEYWORD_trigger },
256
+ { "type", KEYWORD_type },
257
+ { "index", KEYWORD_index },
258
+ { "event", KEYWORD_event },
259
+ { "publication", KEYWORD_publication },
260
+ { "service", KEYWORD_service },
261
+ { "domain", KEYWORD_domain },
262
+ { "datatype", KEYWORD_datatype },
263
+ { "result", KEYWORD_result },
264
+ { "url", KEYWORD_url },
265
+ { "internal", KEYWORD_internal },
266
+ { "external", KEYWORD_external },
267
+ { "when", KEYWORD_when },
268
+ { "then", KEYWORD_then },
269
+ { "variable", KEYWORD_variable },
270
+ { "exception", KEYWORD_exception },
271
+ { "at", KEYWORD_at },
272
+ { "on", KEYWORD_on },
273
+ { "primary", KEYWORD_primary },
274
+ { "references", KEYWORD_references },
275
+ { "unique", KEYWORD_unique },
276
+ { "check", KEYWORD_check },
277
+ { "constraint", KEYWORD_constraint },
278
+ { "foreign", KEYWORD_foreign },
279
+ { "ml_add_table_script", KEYWORD_ml_table },
280
+ { "ml_add_lang_table_script", KEYWORD_ml_table_lang },
281
+ { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet },
282
+ { "ml_add_java_table_script", KEYWORD_ml_table_java },
283
+ { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk },
284
+ { "ml_add_connection_script", KEYWORD_ml_conn },
285
+ { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang },
286
+ { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet },
287
+ { "ml_add_java_connection_script", KEYWORD_ml_conn_java },
288
+ { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk },
289
+ { "local", KEYWORD_local },
290
+ { "temporary", KEYWORD_temporary },
291
+ { "drop", KEYWORD_drop },
292
+ { "view", KEYWORD_view },
293
+ { "synonym", KEYWORD_synonym },
294
+ { "handler", KEYWORD_handler },
295
+ { "comment", KEYWORD_comment },
296
+ { "create", KEYWORD_create },
297
+ { "go", KEYWORD_go }
298
+ };
299
+
300
+ /*
301
+ * FUNCTION DECLARATIONS
302
+ */
303
+
304
+ /* Recursive calls */
305
+ static void parseBlock (tokenInfo *const token, const boolean local);
306
+ static void parseKeywords (tokenInfo *const token);
307
+ static void parseSqlFile (tokenInfo *const token);
308
+
309
+ /*
310
+ * FUNCTION DEFINITIONS
311
+ */
312
+
313
+ static boolean isIdentChar1 (const int c)
314
+ {
315
+ /*
316
+ * Other databases are less restrictive on the first character of
317
+ * an identifier.
318
+ * isIdentChar1 is used to identify the first character of an
319
+ * identifier, so we are removing some restrictions.
320
+ */
321
+ return (boolean)
322
+ (isalpha (c) || c == '@' || c == '_' );
323
+ }
324
+
325
+ static boolean isIdentChar (const int c)
326
+ {
327
+ return (boolean)
328
+ (isalpha (c) || isdigit (c) || c == '$' ||
329
+ c == '@' || c == '_' || c == '#');
330
+ }
331
+
332
+ static boolean isCmdTerm (tokenInfo *const token)
333
+ {
334
+ DebugStatement (
335
+ debugPrintf (DEBUG_PARSE
336
+ , "\n isCmdTerm: token same tt:%d tk:%d\n"
337
+ , token->type
338
+ , token->keyword
339
+ );
340
+ );
341
+
342
+ /*
343
+ * Based on the various customer sites I have been at
344
+ * the most common command delimiters are
345
+ * ;
346
+ * ~
347
+ * /
348
+ * go
349
+ * This routine will check for any of these, more
350
+ * can easily be added by modifying readToken and
351
+ * either adding the character to:
352
+ * enum eTokenType
353
+ * enum eTokenType
354
+ */
355
+ return ( isType (token, TOKEN_SEMICOLON) ||
356
+ isType (token, TOKEN_TILDE) ||
357
+ isType (token, TOKEN_FORWARD_SLASH) ||
358
+ isKeyword (token, KEYWORD_go)
359
+ );
360
+ }
361
+
362
+ static boolean isMatchedEnd(tokenInfo *const token, int nest_lvl)
363
+ {
364
+ boolean terminated = FALSE;
365
+ /*
366
+ * Since different forms of SQL allow the use of
367
+ * BEGIN
368
+ * ...
369
+ * END
370
+ * blocks, some statements may not be terminated using
371
+ * the standard delimiters:
372
+ * ;
373
+ * ~
374
+ * /
375
+ * go
376
+ * This routine will check to see if we encounter and END
377
+ * for the matching nest level of BEGIN ... END statements.
378
+ * If we find one, then we can assume, the statement was terminated
379
+ * since we have fallen through to the END statement of the BEGIN
380
+ * block.
381
+ */
382
+ if ( nest_lvl > 0 && isKeyword (token, KEYWORD_end) )
383
+ {
384
+ if ( token->begin_end_nest_lvl == nest_lvl )
385
+ terminated = TRUE;
386
+ }
387
+
388
+ return terminated;
389
+ }
390
+
391
+ static void buildSqlKeywordHash (void)
392
+ {
393
+ const size_t count = sizeof (SqlKeywordTable) /
394
+ sizeof (SqlKeywordTable [0]);
395
+ size_t i;
396
+ for (i = 0 ; i < count ; ++i)
397
+ {
398
+ const keywordDesc* const p = &SqlKeywordTable [i];
399
+ addKeyword (p->name, Lang_sql, (int) p->id);
400
+ }
401
+ }
402
+
403
+ static tokenInfo *newToken (void)
404
+ {
405
+ tokenInfo *const token = xMalloc (1, tokenInfo);
406
+
407
+ token->type = TOKEN_UNDEFINED;
408
+ token->keyword = KEYWORD_NONE;
409
+ token->string = vStringNew ();
410
+ token->scope = vStringNew ();
411
+ token->begin_end_nest_lvl = 0;
412
+ token->lineNumber = getSourceLineNumber ();
413
+ token->filePosition = getInputFilePosition ();
414
+
415
+ return token;
416
+ }
417
+
418
+ static void deleteToken (tokenInfo *const token)
419
+ {
420
+ vStringDelete (token->string);
421
+ vStringDelete (token->scope);
422
+ eFree (token);
423
+ }
424
+
425
+ /*
426
+ * Tag generation functions
427
+ */
428
+
429
+ static void makeConstTag (tokenInfo *const token, const sqlKind kind)
430
+ {
431
+ if (SqlKinds [kind].enabled)
432
+ {
433
+ const char *const name = vStringValue (token->string);
434
+ tagEntryInfo e;
435
+ initTagEntry (&e, name);
436
+
437
+ e.lineNumber = token->lineNumber;
438
+ e.filePosition = token->filePosition;
439
+ e.kindName = SqlKinds [kind].name;
440
+ e.kind = SqlKinds [kind].letter;
441
+
442
+ makeTagEntry (&e);
443
+ }
444
+ }
445
+
446
+ static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
447
+ {
448
+ vString * fulltag;
449
+
450
+ if (SqlKinds [kind].enabled)
451
+ {
452
+ /*
453
+ * If a scope has been added to the token, change the token
454
+ * string to include the scope when making the tag.
455
+ */
456
+ if ( vStringLength(token->scope) > 0 )
457
+ {
458
+ fulltag = vStringNew ();
459
+ vStringCopy(fulltag, token->scope);
460
+ vStringCatS (fulltag, ".");
461
+ vStringCatS (fulltag, vStringValue(token->string));
462
+ vStringTerminate(fulltag);
463
+ vStringCopy(token->string, fulltag);
464
+ vStringDelete (fulltag);
465
+ }
466
+ makeConstTag (token, kind);
467
+ }
468
+ }
469
+
470
+ /*
471
+ * Parsing functions
472
+ */
473
+
474
+ static void parseString (vString *const string, const int delimiter)
475
+ {
476
+ boolean end = FALSE;
477
+ while (! end)
478
+ {
479
+ int c = fileGetc ();
480
+ if (c == EOF)
481
+ end = TRUE;
482
+ /*
483
+ else if (c == '\\')
484
+ {
485
+ c = fileGetc(); // This maybe a ' or ". //
486
+ vStringPut(string, c);
487
+ }
488
+ */
489
+ else if (c == delimiter)
490
+ end = TRUE;
491
+ else
492
+ vStringPut (string, c);
493
+ }
494
+ vStringTerminate (string);
495
+ }
496
+
497
+ /* Read a C identifier beginning with "firstChar" and places it into "name".
498
+ */
499
+ static void parseIdentifier (vString *const string, const int firstChar)
500
+ {
501
+ int c = firstChar;
502
+ Assert (isIdentChar1 (c));
503
+ do
504
+ {
505
+ vStringPut (string, c);
506
+ c = fileGetc ();
507
+ } while (isIdentChar (c));
508
+ vStringTerminate (string);
509
+ if (!isspace (c))
510
+ fileUngetc (c); /* unget non-identifier character */
511
+ }
512
+
513
+ static void readToken (tokenInfo *const token)
514
+ {
515
+ int c;
516
+
517
+ token->type = TOKEN_UNDEFINED;
518
+ token->keyword = KEYWORD_NONE;
519
+ vStringClear (token->string);
520
+
521
+ getNextChar:
522
+ do
523
+ {
524
+ c = fileGetc ();
525
+ token->lineNumber = getSourceLineNumber ();
526
+ token->filePosition = getInputFilePosition ();
527
+ /*
528
+ * Added " to the list of ignores, not sure what this
529
+ * might break but it gets by this issue:
530
+ * create table "t1" (...)
531
+ *
532
+ * Darren, the code passes all my tests for both
533
+ * Oracle and SQL Anywhere, but maybe you can tell me
534
+ * what this may effect.
535
+ */
536
+ }
537
+ while (c == '\t' || c == ' ' || c == '\n');
538
+
539
+ switch (c)
540
+ {
541
+ case EOF: longjmp (Exception, (int)ExceptionEOF); break;
542
+ case '(': token->type = TOKEN_OPEN_PAREN; break;
543
+ case ')': token->type = TOKEN_CLOSE_PAREN; break;
544
+ case ';': token->type = TOKEN_SEMICOLON; break;
545
+ case '.': token->type = TOKEN_PERIOD; break;
546
+ case ',': token->type = TOKEN_COMMA; break;
547
+ case '{': token->type = TOKEN_OPEN_CURLY; break;
548
+ case '}': token->type = TOKEN_CLOSE_CURLY; break;
549
+ case '~': token->type = TOKEN_TILDE; break;
550
+ case '[': token->type = TOKEN_OPEN_SQUARE; break;
551
+ case ']': token->type = TOKEN_CLOSE_SQUARE; break;
552
+
553
+ case '\'':
554
+ case '"':
555
+ token->type = TOKEN_STRING;
556
+ parseString (token->string, c);
557
+ token->lineNumber = getSourceLineNumber ();
558
+ token->filePosition = getInputFilePosition ();
559
+ break;
560
+
561
+ case '-':
562
+ c = fileGetc ();
563
+ if (c == '-') /* -- is this the start of a comment? */
564
+ {
565
+ fileSkipToCharacter ('\n');
566
+ goto getNextChar;
567
+ }
568
+ else
569
+ {
570
+ if (!isspace (c))
571
+ fileUngetc (c);
572
+ token->type = TOKEN_OPERATOR;
573
+ }
574
+ break;
575
+
576
+ case '<':
577
+ case '>':
578
+ {
579
+ const int initial = c;
580
+ int d = fileGetc ();
581
+ if (d == initial)
582
+ {
583
+ if (initial == '<')
584
+ token->type = TOKEN_BLOCK_LABEL_BEGIN;
585
+ else
586
+ token->type = TOKEN_BLOCK_LABEL_END;
587
+ }
588
+ else
589
+ {
590
+ fileUngetc (d);
591
+ token->type = TOKEN_UNDEFINED;
592
+ }
593
+ break;
594
+ }
595
+
596
+ case '\\':
597
+ c = fileGetc ();
598
+ if (c != '\\' && c != '"' && c != '\'' && !isspace (c))
599
+ fileUngetc (c);
600
+ token->type = TOKEN_CHARACTER;
601
+ token->lineNumber = getSourceLineNumber ();
602
+ token->filePosition = getInputFilePosition ();
603
+ break;
604
+
605
+ case '/':
606
+ {
607
+ int d = fileGetc ();
608
+ if ( (d != '*') && /* is this the start of a comment? */
609
+ (d != '/') ) /* is a one line comment? */
610
+ {
611
+ token->type = TOKEN_FORWARD_SLASH;
612
+ fileUngetc (d);
613
+ }
614
+ else
615
+ {
616
+ if (d == '*')
617
+ {
618
+ do
619
+ {
620
+ fileSkipToCharacter ('*');
621
+ c = fileGetc ();
622
+ if (c == '/')
623
+ break;
624
+ else
625
+ fileUngetc (c);
626
+ } while (c != EOF && c != '\0');
627
+ goto getNextChar;
628
+ }
629
+ else if (d == '/') /* is this the start of a comment? */
630
+ {
631
+ fileSkipToCharacter ('\n');
632
+ goto getNextChar;
633
+ }
634
+ }
635
+ break;
636
+ }
637
+
638
+ default:
639
+ if (! isIdentChar1 (c))
640
+ token->type = TOKEN_UNDEFINED;
641
+ else
642
+ {
643
+ parseIdentifier (token->string, c);
644
+ token->lineNumber = getSourceLineNumber ();
645
+ token->filePosition = getInputFilePosition ();
646
+ token->keyword = analyzeToken (token->string, Lang_sql);
647
+ if (isKeyword (token, KEYWORD_rem))
648
+ {
649
+ vStringClear (token->string);
650
+ fileSkipToCharacter ('\n');
651
+ goto getNextChar;
652
+ }
653
+ else if (isKeyword (token, KEYWORD_NONE))
654
+ token->type = TOKEN_IDENTIFIER;
655
+ else
656
+ token->type = TOKEN_KEYWORD;
657
+ }
658
+ break;
659
+ }
660
+ }
661
+
662
+ /*
663
+ * Token parsing functions
664
+ */
665
+
666
+ /*
667
+ * static void addContext (tokenInfo* const parent, const tokenInfo* const child)
668
+ * {
669
+ * if (vStringLength (parent->string) > 0)
670
+ * {
671
+ * vStringCatS (parent->string, ".");
672
+ * }
673
+ * vStringCatS (parent->string, vStringValue(child->string));
674
+ * vStringTerminate(parent->string);
675
+ * }
676
+ */
677
+
678
+ static void addToScope (tokenInfo* const token, vString* const extra)
679
+ {
680
+ if (vStringLength (token->scope) > 0)
681
+ {
682
+ vStringCatS (token->scope, ".");
683
+ }
684
+ vStringCatS (token->scope, vStringValue(extra));
685
+ vStringTerminate(token->scope);
686
+ }
687
+
688
+ /*
689
+ * Scanning functions
690
+ */
691
+
692
+ static void findToken (tokenInfo *const token, const tokenType type)
693
+ {
694
+ while (! isType (token, type))
695
+ {
696
+ readToken (token);
697
+ }
698
+ }
699
+
700
+ static void findCmdTerm (tokenInfo *const token, const boolean check_first)
701
+ {
702
+ int begin_end_nest_lvl = token->begin_end_nest_lvl;
703
+
704
+ if ( check_first )
705
+ {
706
+ if ( isCmdTerm(token) )
707
+ return;
708
+ }
709
+ do
710
+ {
711
+ readToken (token);
712
+ } while ( !isCmdTerm(token) && !isMatchedEnd(token, begin_end_nest_lvl) );
713
+ }
714
+
715
+ static void skipToMatched(tokenInfo *const token)
716
+ {
717
+ int nest_level = 0;
718
+ tokenType open_token;
719
+ tokenType close_token;
720
+
721
+ switch (token->type)
722
+ {
723
+ case TOKEN_OPEN_PAREN:
724
+ open_token = TOKEN_OPEN_PAREN;
725
+ close_token = TOKEN_CLOSE_PAREN;
726
+ break;
727
+ case TOKEN_OPEN_CURLY:
728
+ open_token = TOKEN_OPEN_CURLY;
729
+ close_token = TOKEN_CLOSE_CURLY;
730
+ break;
731
+ case TOKEN_OPEN_SQUARE:
732
+ open_token = TOKEN_OPEN_SQUARE;
733
+ close_token = TOKEN_CLOSE_SQUARE;
734
+ break;
735
+ default:
736
+ return;
737
+ }
738
+
739
+ /*
740
+ * This routine will skip to a matching closing token.
741
+ * It will also handle nested tokens like the (, ) below.
742
+ * ( name varchar(30), text binary(10) )
743
+ */
744
+
745
+ if (isType (token, open_token))
746
+ {
747
+ nest_level++;
748
+ while (! (isType (token, close_token) && (nest_level == 0)))
749
+ {
750
+ readToken (token);
751
+ if (isType (token, open_token))
752
+ {
753
+ nest_level++;
754
+ }
755
+ if (isType (token, close_token))
756
+ {
757
+ if (nest_level > 0)
758
+ {
759
+ nest_level--;
760
+ }
761
+ }
762
+ }
763
+ readToken (token);
764
+ }
765
+ }
766
+
767
+ static void skipArgumentList (tokenInfo *const token)
768
+ {
769
+ /*
770
+ * Other databases can have arguments with fully declared
771
+ * datatypes:
772
+ * ( name varchar(30), text binary(10) )
773
+ * So we must check for nested open and closing parantheses
774
+ */
775
+
776
+ if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
777
+ {
778
+ skipToMatched (token);
779
+ }
780
+ }
781
+
782
+ static void parseSubProgram (tokenInfo *const token)
783
+ {
784
+ tokenInfo *const name = newToken ();
785
+
786
+ /*
787
+ * This must handle both prototypes and the body of
788
+ * the procedures.
789
+ *
790
+ * Prototype:
791
+ * FUNCTION func_name RETURN integer;
792
+ * PROCEDURE proc_name( parameters );
793
+ * Procedure
794
+ * FUNCTION GET_ML_USERNAME RETURN VARCHAR2
795
+ * IS
796
+ * BEGIN
797
+ * RETURN v_sync_user_id;
798
+ * END GET_ML_USERNAME;
799
+ *
800
+ * PROCEDURE proc_name( parameters )
801
+ * IS
802
+ * BEGIN
803
+ * END;
804
+ * CREATE PROCEDURE proc_name( parameters )
805
+ * EXTERNAL NAME ... ;
806
+ * CREATE PROCEDURE proc_name( parameters )
807
+ * BEGIN
808
+ * END;
809
+ *
810
+ * CREATE FUNCTION f_GetClassName(
811
+ * IN @object VARCHAR(128)
812
+ * ,IN @code VARCHAR(128)
813
+ * )
814
+ * RETURNS VARCHAR(200)
815
+ * DETERMINISTIC
816
+ * BEGIN
817
+ *
818
+ * IF( @object = 'user_state' ) THEN
819
+ * SET something = something;
820
+ * END IF;
821
+ *
822
+ * RETURN @name;
823
+ * END;
824
+ */
825
+ const sqlKind kind = isKeyword (token, KEYWORD_function) ?
826
+ SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
827
+ Assert (isKeyword (token, KEYWORD_function) ||
828
+ isKeyword (token, KEYWORD_procedure));
829
+ readToken (name);
830
+ readToken (token);
831
+ if (isType (token, TOKEN_PERIOD))
832
+ {
833
+ readToken (name);
834
+ readToken (token);
835
+ }
836
+ if (isType (token, TOKEN_OPEN_PAREN))
837
+ {
838
+ /* Reads to the next token after the TOKEN_CLOSE_PAREN */
839
+ skipArgumentList(token);
840
+ }
841
+
842
+ if (kind == SQLTAG_FUNCTION)
843
+ {
844
+ if (isKeyword (token, KEYWORD_return) || isKeyword (token, KEYWORD_returns))
845
+ {
846
+ /* Read datatype */
847
+ readToken (token);
848
+ /*
849
+ * Read token after which could be the
850
+ * command terminator if a prototype
851
+ * or an open parantheses
852
+ */
853
+ readToken (token);
854
+ if (isType (token, TOKEN_OPEN_PAREN))
855
+ {
856
+ /* Reads to the next token after the TOKEN_CLOSE_PAREN */
857
+ skipArgumentList(token);
858
+ }
859
+ }
860
+ }
861
+ if( isCmdTerm (token) )
862
+ {
863
+ makeSqlTag (name, SQLTAG_PROTOTYPE);
864
+ }
865
+ else
866
+ {
867
+ while (!(isKeyword (token, KEYWORD_is) ||
868
+ isKeyword (token, KEYWORD_begin) ||
869
+ isKeyword (token, KEYWORD_at) ||
870
+ isKeyword (token, KEYWORD_internal) ||
871
+ isKeyword (token, KEYWORD_external) ||
872
+ isKeyword (token, KEYWORD_url) ||
873
+ isCmdTerm (token)
874
+ )
875
+ )
876
+ {
877
+ if ( isKeyword (token, KEYWORD_result) )
878
+ {
879
+ readToken (token);
880
+ if (isType (token, TOKEN_OPEN_PAREN))
881
+ {
882
+ /* Reads to the next token after the TOKEN_CLOSE_PAREN */
883
+ skipArgumentList(token);
884
+ }
885
+ } else {
886
+ readToken (token);
887
+ }
888
+ }
889
+ if (isKeyword (token, KEYWORD_at) ||
890
+ isKeyword (token, KEYWORD_url) ||
891
+ isKeyword (token, KEYWORD_internal) ||
892
+ isKeyword (token, KEYWORD_external) )
893
+ {
894
+ addToScope(token, name->string);
895
+ if (isType (name, TOKEN_IDENTIFIER) ||
896
+ isType (name, TOKEN_STRING) ||
897
+ !isKeyword (token, KEYWORD_NONE)
898
+ )
899
+ makeSqlTag (name, kind);
900
+
901
+ vStringClear (token->scope);
902
+ }
903
+ if (isKeyword (token, KEYWORD_is) ||
904
+ isKeyword (token, KEYWORD_begin) )
905
+ {
906
+ addToScope(token, name->string);
907
+ if (isType (name, TOKEN_IDENTIFIER) ||
908
+ isType (name, TOKEN_STRING) ||
909
+ !isKeyword (token, KEYWORD_NONE)
910
+ )
911
+ makeSqlTag (name, kind);
912
+
913
+ parseBlock (token, TRUE);
914
+ vStringClear (token->scope);
915
+ }
916
+ }
917
+ deleteToken (name);
918
+ }
919
+
920
+ static void parseRecord (tokenInfo *const token)
921
+ {
922
+ /*
923
+ * Make it a bit forgiving, this is called from
924
+ * multiple functions, parseTable, parseType
925
+ */
926
+ if (!isType (token, TOKEN_OPEN_PAREN))
927
+ readToken (token);
928
+
929
+ Assert (isType (token, TOKEN_OPEN_PAREN));
930
+ do
931
+ {
932
+ if ( isType (token, TOKEN_COMMA) || isType (token, TOKEN_OPEN_PAREN) )
933
+ readToken (token);
934
+
935
+ /*
936
+ * Create table statements can end with various constraints
937
+ * which must be excluded from the SQLTAG_FIELD.
938
+ * create table t1 (
939
+ * c1 integer,
940
+ * c2 char(30),
941
+ * c3 numeric(10,5),
942
+ * c4 integer,
943
+ * constraint whatever,
944
+ * primary key(c1),
945
+ * foreign key (),
946
+ * check ()
947
+ * )
948
+ */
949
+ if (! (isKeyword(token, KEYWORD_primary) ||
950
+ isKeyword(token, KEYWORD_references) ||
951
+ isKeyword(token, KEYWORD_unique) ||
952
+ isKeyword(token, KEYWORD_check) ||
953
+ isKeyword(token, KEYWORD_constraint) ||
954
+ isKeyword(token, KEYWORD_foreign) ) )
955
+ {
956
+ if (isType (token, TOKEN_IDENTIFIER) ||
957
+ isType (token, TOKEN_STRING))
958
+ makeSqlTag (token, SQLTAG_FIELD);
959
+ }
960
+
961
+ while (!(isType (token, TOKEN_COMMA) ||
962
+ isType (token, TOKEN_CLOSE_PAREN) ||
963
+ isType (token, TOKEN_OPEN_PAREN)
964
+ ))
965
+ {
966
+ readToken (token);
967
+ /*
968
+ * A table structure can look like this:
969
+ * create table t1 (
970
+ * c1 integer,
971
+ * c2 char(30),
972
+ * c3 numeric(10,5),
973
+ * c4 integer
974
+ * )
975
+ * We can't just look for a COMMA or CLOSE_PAREN
976
+ * since that will not deal with the numeric(10,5)
977
+ * case. So we need to skip the argument list
978
+ * when we find an open paren.
979
+ */
980
+ if (isType (token, TOKEN_OPEN_PAREN))
981
+ {
982
+ /* Reads to the next token after the TOKEN_CLOSE_PAREN */
983
+ skipArgumentList(token);
984
+ }
985
+ }
986
+ } while (! isType (token, TOKEN_CLOSE_PAREN));
987
+ }
988
+
989
+ static void parseType (tokenInfo *const token)
990
+ {
991
+ tokenInfo *const name = newToken ();
992
+ vString * saveScope = vStringNew ();
993
+
994
+ vStringCopy(saveScope, token->scope);
995
+ /* If a scope has been set, add it to the name */
996
+ addToScope (name, token->scope);
997
+ readToken (name);
998
+ if (isType (name, TOKEN_IDENTIFIER))
999
+ {
1000
+ readToken (token);
1001
+ if (isKeyword (token, KEYWORD_is))
1002
+ {
1003
+ readToken (token);
1004
+ addToScope (token, name->string);
1005
+ switch (token->keyword)
1006
+ {
1007
+ case KEYWORD_record:
1008
+ case KEYWORD_object:
1009
+ makeSqlTag (name, SQLTAG_RECORD);
1010
+ parseRecord (token);
1011
+ break;
1012
+
1013
+ case KEYWORD_table:
1014
+ makeSqlTag (name, SQLTAG_TABLE);
1015
+ break;
1016
+
1017
+ case KEYWORD_ref:
1018
+ readToken (token);
1019
+ if (isKeyword (token, KEYWORD_cursor))
1020
+ makeSqlTag (name, SQLTAG_CURSOR);
1021
+ break;
1022
+
1023
+ default: break;
1024
+ }
1025
+ vStringClear (token->scope);
1026
+ }
1027
+ }
1028
+ vStringCopy(token->scope, saveScope);
1029
+ deleteToken (name);
1030
+ vStringDelete(saveScope);
1031
+ }
1032
+
1033
+ static void parseSimple (tokenInfo *const token, const sqlKind kind)
1034
+ {
1035
+ /* This will simply make the tagname from the first word found */
1036
+ readToken (token);
1037
+ if (isType (token, TOKEN_IDENTIFIER) ||
1038
+ isType (token, TOKEN_STRING))
1039
+ makeSqlTag (token, kind);
1040
+ }
1041
+
1042
+ static void parseDeclare (tokenInfo *const token, const boolean local)
1043
+ {
1044
+ /*
1045
+ * PL/SQL declares are of this format:
1046
+ * IS|AS
1047
+ * [declare]
1048
+ * CURSOR curname ...
1049
+ * varname1 datatype;
1050
+ * varname2 datatype;
1051
+ * varname3 datatype;
1052
+ * begin
1053
+ */
1054
+
1055
+ if (isKeyword (token, KEYWORD_declare))
1056
+ readToken (token);
1057
+ while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
1058
+ {
1059
+ switch (token->keyword)
1060
+ {
1061
+ case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
1062
+ case KEYWORD_function: parseSubProgram (token); break;
1063
+ case KEYWORD_procedure: parseSubProgram (token); break;
1064
+ case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
1065
+ case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
1066
+ case KEYWORD_type: parseType (token); break;
1067
+
1068
+ default:
1069
+ if (isType (token, TOKEN_IDENTIFIER))
1070
+ {
1071
+ if (local)
1072
+ {
1073
+ makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1074
+ }
1075
+ else
1076
+ {
1077
+ makeSqlTag (token, SQLTAG_VARIABLE);
1078
+ }
1079
+ }
1080
+ break;
1081
+ }
1082
+ findToken (token, TOKEN_SEMICOLON);
1083
+ readToken (token);
1084
+ }
1085
+ }
1086
+
1087
+ static void parseDeclareANSI (tokenInfo *const token, const boolean local)
1088
+ {
1089
+ tokenInfo *const type = newToken ();
1090
+ /*
1091
+ * ANSI declares are of this format:
1092
+ * BEGIN
1093
+ * DECLARE varname1 datatype;
1094
+ * DECLARE varname2 datatype;
1095
+ * ...
1096
+ *
1097
+ * This differ from PL/SQL where DECLARE preceeds the BEGIN block
1098
+ * and the DECLARE keyword is not repeated.
1099
+ */
1100
+ while (isKeyword (token, KEYWORD_declare))
1101
+ {
1102
+ readToken (token);
1103
+ readToken (type);
1104
+
1105
+ if (isKeyword (type, KEYWORD_cursor))
1106
+ makeSqlTag (token, SQLTAG_CURSOR);
1107
+ else if (isKeyword (token, KEYWORD_local) &&
1108
+ isKeyword (type, KEYWORD_temporary))
1109
+ {
1110
+ /*
1111
+ * DECLARE LOCAL TEMPORARY TABLE table_name (
1112
+ * c1 int,
1113
+ * c2 int
1114
+ * );
1115
+ */
1116
+ readToken (token);
1117
+ if (isKeyword (token, KEYWORD_table))
1118
+ {
1119
+ readToken (token);
1120
+ if (isType(token, TOKEN_IDENTIFIER) ||
1121
+ isType(token, TOKEN_STRING) )
1122
+ {
1123
+ makeSqlTag (token, SQLTAG_TABLE);
1124
+ }
1125
+ }
1126
+ }
1127
+ else if (isType (token, TOKEN_IDENTIFIER) ||
1128
+ isType (token, TOKEN_STRING))
1129
+ {
1130
+ if (local)
1131
+ makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
1132
+ else
1133
+ makeSqlTag (token, SQLTAG_VARIABLE);
1134
+ }
1135
+ findToken (token, TOKEN_SEMICOLON);
1136
+ readToken (token);
1137
+ }
1138
+ deleteToken (type);
1139
+ }
1140
+
1141
+ static void parseLabel (tokenInfo *const token)
1142
+ {
1143
+ /*
1144
+ * A label has this format:
1145
+ * <<tobacco_dependency>>
1146
+ * DECLARE
1147
+ * v_senator VARCHAR2(100) := 'THURMOND, JESSE';
1148
+ * BEGIN
1149
+ * IF total_contributions (v_senator, 'TOBACCO') > 25000
1150
+ * THEN
1151
+ * <<alochol_dependency>>
1152
+ * DECLARE
1153
+ * v_senator VARCHAR2(100) := 'WHATEVERIT, TAKES';
1154
+ * BEGIN
1155
+ * ...
1156
+ */
1157
+
1158
+ Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
1159
+ readToken (token);
1160
+ if (isType (token, TOKEN_IDENTIFIER))
1161
+ {
1162
+ makeSqlTag (token, SQLTAG_BLOCK_LABEL);
1163
+ readToken (token); /* read end of label */
1164
+ }
1165
+ }
1166
+
1167
+ static void parseStatements (tokenInfo *const token)
1168
+ {
1169
+ boolean isAnsi = TRUE;
1170
+ boolean stmtTerm = FALSE;
1171
+ do
1172
+ {
1173
+ if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1174
+ parseLabel (token);
1175
+ else
1176
+ {
1177
+ switch (token->keyword)
1178
+ {
1179
+ case KEYWORD_exception:
1180
+ /*
1181
+ * EXCEPTION
1182
+ * <exception handler>;
1183
+ *
1184
+ * Where an exception handler could be:
1185
+ * BEGIN
1186
+ * WHEN OTHERS THEN
1187
+ * x := x + 3;
1188
+ * END;
1189
+ * In this case we need to skip this keyword and
1190
+ * move on to the next token without reading until
1191
+ * TOKEN_SEMICOLON;
1192
+ */
1193
+ readToken (token);
1194
+ continue;
1195
+
1196
+ case KEYWORD_when:
1197
+ /*
1198
+ * WHEN statements can be used in exception clauses
1199
+ * and CASE statements. The CASE statement should skip
1200
+ * these given below we skip over to an END statement.
1201
+ * But for an exception clause, we can have:
1202
+ * EXCEPTION
1203
+ * WHEN OTHERS THEN
1204
+ * BEGIN
1205
+ * x := x + 3;
1206
+ * END;
1207
+ * If we skip to the TOKEN_SEMICOLON, we miss the begin
1208
+ * of a nested BEGIN END block. So read the next token
1209
+ * after the THEN and restart the LOOP.
1210
+ */
1211
+ while (! isKeyword (token, KEYWORD_then))
1212
+ readToken (token);
1213
+ readToken (token);
1214
+ continue;
1215
+
1216
+ case KEYWORD_if:
1217
+ /*
1218
+ * We do not want to look for a ; since for an empty
1219
+ * IF block, it would skip over the END.
1220
+ * IF...THEN
1221
+ * END IF;
1222
+ *
1223
+ * or non-ANSI
1224
+ * IF ...
1225
+ * BEGIN
1226
+ * END
1227
+ */
1228
+ while ( ! isKeyword (token, KEYWORD_then) &&
1229
+ ! isKeyword (token, KEYWORD_begin) )
1230
+ {
1231
+ readToken (token);
1232
+ }
1233
+
1234
+ if( isKeyword (token, KEYWORD_begin ) )
1235
+ {
1236
+ isAnsi = FALSE;
1237
+ parseBlock(token, FALSE);
1238
+
1239
+ /*
1240
+ * Handle the non-Ansi IF blocks.
1241
+ * parseBlock consumes the END, so if the next
1242
+ * token in a command terminator (like GO)
1243
+ * we know we are done with this statement.
1244
+ */
1245
+ if ( isCmdTerm (token) )
1246
+ stmtTerm = TRUE;
1247
+ }
1248
+ else
1249
+ {
1250
+ readToken (token);
1251
+ parseStatements (token);
1252
+ /*
1253
+ * parseStatements returns when it finds an END, an IF
1254
+ * should follow the END for ANSI anyway.
1255
+ * IF...THEN
1256
+ * END IF;
1257
+ */
1258
+ if( isKeyword (token, KEYWORD_end ) )
1259
+ readToken (token);
1260
+
1261
+ if( ! isKeyword (token, KEYWORD_if ) )
1262
+ {
1263
+ /*
1264
+ * Well we need to do something here.
1265
+ * There are lots of different END statements
1266
+ * END;
1267
+ * END CASE;
1268
+ * ENDIF;
1269
+ * ENDCASE;
1270
+ */
1271
+ }
1272
+ }
1273
+ break;
1274
+
1275
+ case KEYWORD_loop:
1276
+ case KEYWORD_case:
1277
+ case KEYWORD_for:
1278
+ /*
1279
+ * LOOP...
1280
+ * END LOOP;
1281
+ *
1282
+ * CASE
1283
+ * WHEN '1' THEN
1284
+ * END CASE;
1285
+ *
1286
+ * FOR loop_name AS cursor_name CURSOR FOR ...
1287
+ * END FOR;
1288
+ */
1289
+ readToken (token);
1290
+ parseStatements (token);
1291
+
1292
+ if( isKeyword (token, KEYWORD_end ) )
1293
+ readToken (token);
1294
+
1295
+ break;
1296
+
1297
+ case KEYWORD_create:
1298
+ readToken (token);
1299
+ parseKeywords(token);
1300
+ break;
1301
+
1302
+ case KEYWORD_declare:
1303
+ case KEYWORD_begin:
1304
+ parseBlock (token, TRUE);
1305
+ break;
1306
+
1307
+ case KEYWORD_end:
1308
+ break;
1309
+
1310
+ default:
1311
+ readToken (token);
1312
+ break;
1313
+ }
1314
+ /*
1315
+ * Not all statements must end in a semi-colon
1316
+ * begin
1317
+ * if current publisher <> 'publish' then
1318
+ * signal UE_FailStatement
1319
+ * end if
1320
+ * end;
1321
+ * The last statement prior to an end ("signal" above) does
1322
+ * not need a semi-colon, nor does the end if, since it is
1323
+ * also the last statement prior to the end of the block.
1324
+ *
1325
+ * So we must read to the first semi-colon or an END block
1326
+ */
1327
+ while ( ! stmtTerm &&
1328
+ ! ( isKeyword (token, KEYWORD_end) ||
1329
+ (isCmdTerm(token)) )
1330
+ )
1331
+ {
1332
+ readToken (token);
1333
+
1334
+ if (isType (token, TOKEN_OPEN_PAREN) ||
1335
+ isType (token, TOKEN_OPEN_CURLY) ||
1336
+ isType (token, TOKEN_OPEN_SQUARE) )
1337
+ skipToMatched (token);
1338
+
1339
+ }
1340
+ }
1341
+ /*
1342
+ * We assumed earlier all statements ended with a command terminator.
1343
+ * See comment above, now, only read if the current token
1344
+ * is not a command terminator.
1345
+ */
1346
+ if ( isCmdTerm(token) )
1347
+ {
1348
+ readToken (token);
1349
+ }
1350
+ } while (! isKeyword (token, KEYWORD_end) && ! stmtTerm );
1351
+ }
1352
+
1353
+ static void parseBlock (tokenInfo *const token, const boolean local)
1354
+ {
1355
+ if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
1356
+ {
1357
+ parseLabel (token);
1358
+ readToken (token);
1359
+ }
1360
+ if (! isKeyword (token, KEYWORD_begin))
1361
+ {
1362
+ readToken (token);
1363
+ /*
1364
+ * These are Oracle style declares which generally come
1365
+ * between an IS/AS and BEGIN block.
1366
+ */
1367
+ parseDeclare (token, local);
1368
+ }
1369
+ if (isKeyword (token, KEYWORD_begin))
1370
+ {
1371
+ readToken (token);
1372
+ /*
1373
+ * Check for ANSI declarations which always follow
1374
+ * a BEGIN statement. This routine will not advance
1375
+ * the token if none are found.
1376
+ */
1377
+ parseDeclareANSI (token, local);
1378
+ token->begin_end_nest_lvl++;
1379
+ while (! isKeyword (token, KEYWORD_end))
1380
+ {
1381
+ parseStatements (token);
1382
+ }
1383
+ token->begin_end_nest_lvl--;
1384
+
1385
+ /*
1386
+ * Read the next token (we will assume
1387
+ * it is the command delimiter)
1388
+ */
1389
+ readToken (token);
1390
+
1391
+ /*
1392
+ * Check if the END block is terminated
1393
+ */
1394
+ if ( !isCmdTerm (token) )
1395
+ {
1396
+ /*
1397
+ * Not sure what to do here at the moment.
1398
+ * I think the routine that calls parseBlock
1399
+ * must expect the next token has already
1400
+ * been read since it is possible this
1401
+ * token is not a command delimiter.
1402
+ */
1403
+ /* findCmdTerm (token, FALSE); */
1404
+ }
1405
+ }
1406
+ }
1407
+
1408
+ static void parsePackage (tokenInfo *const token)
1409
+ {
1410
+ /*
1411
+ * Packages can be specified in a number of ways:
1412
+ * CREATE OR REPLACE PACKAGE pkg_name AS
1413
+ * or
1414
+ * CREATE OR REPLACE PACKAGE owner.pkg_name AS
1415
+ * or by specifying a package body
1416
+ * CREATE OR REPLACE PACKAGE BODY pkg_name AS
1417
+ * CREATE OR REPLACE PACKAGE BODY owner.pkg_name AS
1418
+ */
1419
+ tokenInfo *const name = newToken ();
1420
+ readToken (name);
1421
+ if (isKeyword (name, KEYWORD_body))
1422
+ {
1423
+ /*
1424
+ * Ignore the BODY tag since we will process
1425
+ * the body or prototypes in the same manner
1426
+ */
1427
+ readToken (name);
1428
+ }
1429
+ /* Check for owner.pkg_name */
1430
+ while (! isKeyword (token, KEYWORD_is))
1431
+ {
1432
+ readToken (token);
1433
+ if ( isType(token, TOKEN_PERIOD) )
1434
+ {
1435
+ readToken (name);
1436
+ }
1437
+ }
1438
+ if (isKeyword (token, KEYWORD_is))
1439
+ {
1440
+ if (isType (name, TOKEN_IDENTIFIER) ||
1441
+ isType (name, TOKEN_STRING))
1442
+ makeSqlTag (name, SQLTAG_PACKAGE);
1443
+ parseBlock (token, FALSE);
1444
+ }
1445
+ findCmdTerm (token, FALSE);
1446
+ deleteToken (name);
1447
+ }
1448
+
1449
+ static void parseTable (tokenInfo *const token)
1450
+ {
1451
+ tokenInfo *const name = newToken ();
1452
+
1453
+ /*
1454
+ * This deals with these formats:
1455
+ * create table t1 (c1 int);
1456
+ * create global tempoary table t2 (c1 int);
1457
+ * create table "t3" (c1 int);
1458
+ * create table bob.t4 (c1 int);
1459
+ * create table bob."t5" (c1 int);
1460
+ * create table "bob"."t6" (c1 int);
1461
+ * create table bob."t7" (c1 int);
1462
+ * Proxy tables use this format:
1463
+ * create existing table bob."t7" AT '...';
1464
+ * SQL Server and Sybase formats
1465
+ * create table OnlyTable (
1466
+ * create table dbo.HasOwner (
1467
+ * create table [dbo].[HasOwnerSquare] (
1468
+ * create table master.dbo.HasDb (
1469
+ * create table master..HasDbNoOwner (
1470
+ * create table [master].dbo.[HasDbAndOwnerSquare] (
1471
+ * create table [master]..[HasDbNoOwnerSquare] (
1472
+ */
1473
+
1474
+ /* This could be a database, owner or table name */
1475
+ readToken (name);
1476
+ if (isType (name, TOKEN_OPEN_SQUARE))
1477
+ {
1478
+ readToken (name);
1479
+ /* Read close square */
1480
+ readToken (token);
1481
+ }
1482
+ readToken (token);
1483
+ if (isType (token, TOKEN_PERIOD))
1484
+ {
1485
+ /*
1486
+ * This could be a owner or table name.
1487
+ * But this is also a special case since the table can be
1488
+ * referenced with a blank owner:
1489
+ * dbname..tablename
1490
+ */
1491
+ readToken (name);
1492
+ if (isType (name, TOKEN_OPEN_SQUARE))
1493
+ {
1494
+ readToken (name);
1495
+ /* Read close square */
1496
+ readToken (token);
1497
+ }
1498
+ /* Check if a blank name was provided */
1499
+ if (isType (name, TOKEN_PERIOD))
1500
+ {
1501
+ readToken (name);
1502
+ if (isType (name, TOKEN_OPEN_SQUARE))
1503
+ {
1504
+ readToken (name);
1505
+ /* Read close square */
1506
+ readToken (token);
1507
+ }
1508
+ }
1509
+ readToken (token);
1510
+ if (isType (token, TOKEN_PERIOD))
1511
+ {
1512
+ /* This can only be the table name */
1513
+ readToken (name);
1514
+ if (isType (name, TOKEN_OPEN_SQUARE))
1515
+ {
1516
+ readToken (name);
1517
+ /* Read close square */
1518
+ readToken (token);
1519
+ }
1520
+ readToken (token);
1521
+ }
1522
+ }
1523
+ if (isType (token, TOKEN_OPEN_PAREN))
1524
+ {
1525
+ if (isType (name, TOKEN_IDENTIFIER) ||
1526
+ isType (name, TOKEN_STRING))
1527
+ {
1528
+ makeSqlTag (name, SQLTAG_TABLE);
1529
+ vStringCopy(token->scope, name->string);
1530
+ parseRecord (token);
1531
+ vStringClear (token->scope);
1532
+ }
1533
+ }
1534
+ else if (isKeyword (token, KEYWORD_at))
1535
+ {
1536
+ if (isType (name, TOKEN_IDENTIFIER))
1537
+ {
1538
+ makeSqlTag (name, SQLTAG_TABLE);
1539
+ }
1540
+ }
1541
+ findCmdTerm (token, FALSE);
1542
+ deleteToken (name);
1543
+ }
1544
+
1545
+ static void parseIndex (tokenInfo *const token)
1546
+ {
1547
+ tokenInfo *const name = newToken ();
1548
+ tokenInfo *const owner = newToken ();
1549
+
1550
+ /*
1551
+ * This deals with these formats
1552
+ * create index i1 on t1(c1) create index "i2" on t1(c1)
1553
+ * create virtual unique clustered index "i3" on t1(c1)
1554
+ * create unique clustered index "i4" on t1(c1)
1555
+ * create clustered index "i5" on t1(c1)
1556
+ * create bitmap index "i6" on t1(c1)
1557
+ */
1558
+
1559
+ readToken (name);
1560
+ readToken (token);
1561
+ if (isType (token, TOKEN_PERIOD))
1562
+ {
1563
+ readToken (name);
1564
+ readToken (token);
1565
+ }
1566
+ if ( isKeyword (token, KEYWORD_on) &&
1567
+ (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING) ) )
1568
+ {
1569
+ readToken (owner);
1570
+ readToken (token);
1571
+ if (isType (token, TOKEN_PERIOD))
1572
+ {
1573
+ readToken (owner);
1574
+ readToken (token);
1575
+ }
1576
+ addToScope(name, owner->string);
1577
+ makeSqlTag (name, SQLTAG_INDEX);
1578
+ }
1579
+ findCmdTerm (token, FALSE);
1580
+ deleteToken (name);
1581
+ deleteToken (owner);
1582
+ }
1583
+
1584
+ static void parseEvent (tokenInfo *const token)
1585
+ {
1586
+ tokenInfo *const name = newToken ();
1587
+
1588
+ /*
1589
+ * This deals with these formats
1590
+ * create event e1 handler begin end;
1591
+ * create event "e2" handler begin end;
1592
+ * create event dba."e3" handler begin end;
1593
+ * create event "dba"."e4" handler begin end;
1594
+ */
1595
+
1596
+ readToken (name);
1597
+ readToken (token);
1598
+ if (isType (token, TOKEN_PERIOD))
1599
+ {
1600
+ readToken (name);
1601
+ }
1602
+ while (! (isKeyword (token, KEYWORD_handler) ||
1603
+ (isType (token, TOKEN_SEMICOLON))) )
1604
+ {
1605
+ readToken (token);
1606
+ }
1607
+
1608
+ if ( isKeyword (token, KEYWORD_handler) ||
1609
+ isType (token, TOKEN_SEMICOLON) )
1610
+ {
1611
+ makeSqlTag (name, SQLTAG_EVENT);
1612
+ }
1613
+
1614
+ if (isKeyword (token, KEYWORD_handler))
1615
+ {
1616
+ readToken (token);
1617
+ if ( isKeyword (token, KEYWORD_begin) )
1618
+ {
1619
+ parseBlock (token, TRUE);
1620
+ }
1621
+ findCmdTerm (token, TRUE);
1622
+ }
1623
+ deleteToken (name);
1624
+ }
1625
+
1626
+ static void parseTrigger (tokenInfo *const token)
1627
+ {
1628
+ tokenInfo *const name = newToken ();
1629
+ tokenInfo *const table = newToken ();
1630
+
1631
+ /*
1632
+ * This deals with these formats
1633
+ * create or replace trigger tr1 begin end;
1634
+ * create trigger "tr2" begin end;
1635
+ * drop trigger "droptr1";
1636
+ * create trigger "tr3" CALL sp_something();
1637
+ * create trigger "owner"."tr4" begin end;
1638
+ * create trigger "tr5" not valid;
1639
+ * create trigger "tr6" begin end;
1640
+ */
1641
+
1642
+ readToken (name);
1643
+ readToken (token);
1644
+ if (isType (token, TOKEN_PERIOD))
1645
+ {
1646
+ readToken (name);
1647
+ readToken (token);
1648
+ }
1649
+
1650
+ while ( !isKeyword (token, KEYWORD_on) &&
1651
+ !isCmdTerm (token) )
1652
+ {
1653
+ readToken (token);
1654
+ }
1655
+
1656
+ /*if (! isType (token, TOKEN_SEMICOLON) ) */
1657
+ if (! isCmdTerm (token) )
1658
+ {
1659
+ readToken (table);
1660
+ readToken (token);
1661
+ if (isType (token, TOKEN_PERIOD))
1662
+ {
1663
+ readToken (table);
1664
+ readToken (token);
1665
+ }
1666
+
1667
+ while (! (isKeyword (token, KEYWORD_begin) ||
1668
+ (isKeyword (token, KEYWORD_call)) ||
1669
+ ( isCmdTerm (token))) )
1670
+ {
1671
+ if ( isKeyword (token, KEYWORD_declare) )
1672
+ {
1673
+ addToScope(token, name->string);
1674
+ parseDeclare(token, TRUE);
1675
+ vStringClear(token->scope);
1676
+ }
1677
+ else
1678
+ readToken (token);
1679
+ }
1680
+
1681
+ if ( isKeyword (token, KEYWORD_begin) ||
1682
+ isKeyword (token, KEYWORD_call) )
1683
+ {
1684
+ addToScope(name, table->string);
1685
+ makeSqlTag (name, SQLTAG_TRIGGER);
1686
+ addToScope(token, table->string);
1687
+ if ( isKeyword (token, KEYWORD_begin) )
1688
+ {
1689
+ parseBlock (token, TRUE);
1690
+ }
1691
+ vStringClear(token->scope);
1692
+ }
1693
+ }
1694
+
1695
+ findCmdTerm (token, TRUE);
1696
+ deleteToken (name);
1697
+ deleteToken (table);
1698
+ }
1699
+
1700
+ static void parsePublication (tokenInfo *const token)
1701
+ {
1702
+ tokenInfo *const name = newToken ();
1703
+
1704
+ /*
1705
+ * This deals with these formats
1706
+ * create or replace publication pu1 ()
1707
+ * create publication "pu2" ()
1708
+ * create publication dba."pu3" ()
1709
+ * create publication "dba"."pu4" ()
1710
+ */
1711
+
1712
+ readToken (name);
1713
+ readToken (token);
1714
+ if (isType (token, TOKEN_PERIOD))
1715
+ {
1716
+ readToken (name);
1717
+ readToken (token);
1718
+ }
1719
+ if (isType (token, TOKEN_OPEN_PAREN))
1720
+ {
1721
+ if (isType (name, TOKEN_IDENTIFIER) ||
1722
+ isType (name, TOKEN_STRING))
1723
+ {
1724
+ makeSqlTag (name, SQLTAG_PUBLICATION);
1725
+ }
1726
+ }
1727
+ findCmdTerm (token, FALSE);
1728
+ deleteToken (name);
1729
+ }
1730
+
1731
+ static void parseService (tokenInfo *const token)
1732
+ {
1733
+ tokenInfo *const name = newToken ();
1734
+
1735
+ /*
1736
+ * This deals with these formats
1737
+ * CREATE SERVICE s1 TYPE 'HTML'
1738
+ * AUTHORIZATION OFF USER DBA AS
1739
+ * SELECT *
1740
+ * FROM SYS.SYSTABLE;
1741
+ * CREATE SERVICE "s2" TYPE 'HTML'
1742
+ * AUTHORIZATION OFF USER DBA AS
1743
+ * CALL sp_Something();
1744
+ */
1745
+
1746
+ readToken (name);
1747
+ readToken (token);
1748
+ if (isKeyword (token, KEYWORD_type))
1749
+ {
1750
+ if (isType (name, TOKEN_IDENTIFIER) ||
1751
+ isType (name, TOKEN_STRING))
1752
+ {
1753
+ makeSqlTag (name, SQLTAG_SERVICE);
1754
+ }
1755
+ }
1756
+ findCmdTerm (token, FALSE);
1757
+ deleteToken (name);
1758
+ }
1759
+
1760
+ static void parseDomain (tokenInfo *const token)
1761
+ {
1762
+ tokenInfo *const name = newToken ();
1763
+
1764
+ /*
1765
+ * This deals with these formats
1766
+ * CREATE DOMAIN|DATATYPE [AS] your_name ...;
1767
+ */
1768
+
1769
+ readToken (name);
1770
+ if (isKeyword (name, KEYWORD_is))
1771
+ {
1772
+ readToken (name);
1773
+ }
1774
+ readToken (token);
1775
+ if (isType (name, TOKEN_IDENTIFIER) ||
1776
+ isType (name, TOKEN_STRING))
1777
+ {
1778
+ makeSqlTag (name, SQLTAG_DOMAIN);
1779
+ }
1780
+ findCmdTerm (token, FALSE);
1781
+ deleteToken (name);
1782
+ }
1783
+
1784
+ static void parseDrop (tokenInfo *const token)
1785
+ {
1786
+ /*
1787
+ * This deals with these formats
1788
+ * DROP TABLE|PROCEDURE|DOMAIN|DATATYPE name;
1789
+ *
1790
+ * Just simply skip over these statements.
1791
+ * They are often confused with PROCEDURE prototypes
1792
+ * since the syntax is similar, this effectively deals with
1793
+ * the issue for all types.
1794
+ */
1795
+
1796
+ findCmdTerm (token, FALSE);
1797
+ }
1798
+
1799
+ static void parseVariable (tokenInfo *const token)
1800
+ {
1801
+ tokenInfo *const name = newToken ();
1802
+
1803
+ /*
1804
+ * This deals with these formats
1805
+ * create variable varname1 integer;
1806
+ * create variable @varname2 integer;
1807
+ * create variable "varname3" integer;
1808
+ * drop variable @varname3;
1809
+ */
1810
+
1811
+ readToken (name);
1812
+ readToken (token);
1813
+ if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1814
+ && !isType (token, TOKEN_SEMICOLON) )
1815
+ {
1816
+ makeSqlTag (name, SQLTAG_VARIABLE);
1817
+ }
1818
+ findCmdTerm (token, TRUE);
1819
+
1820
+ deleteToken (name);
1821
+ }
1822
+
1823
+ static void parseSynonym (tokenInfo *const token)
1824
+ {
1825
+ tokenInfo *const name = newToken ();
1826
+
1827
+ /*
1828
+ * This deals with these formats
1829
+ * create variable varname1 integer;
1830
+ * create variable @varname2 integer;
1831
+ * create variable "varname3" integer;
1832
+ * drop variable @varname3;
1833
+ */
1834
+
1835
+ readToken (name);
1836
+ readToken (token);
1837
+ if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1838
+ && isKeyword (token, KEYWORD_for) )
1839
+ {
1840
+ makeSqlTag (name, SQLTAG_SYNONYM);
1841
+ }
1842
+ findCmdTerm (token, TRUE);
1843
+
1844
+ deleteToken (name);
1845
+ }
1846
+
1847
+ static void parseView (tokenInfo *const token)
1848
+ {
1849
+ tokenInfo *const name = newToken ();
1850
+
1851
+ /*
1852
+ * This deals with these formats
1853
+ * create variable varname1 integer;
1854
+ * create variable @varname2 integer;
1855
+ * create variable "varname3" integer;
1856
+ * drop variable @varname3;
1857
+ */
1858
+
1859
+ readToken (name);
1860
+ readToken (token);
1861
+ if (isType (token, TOKEN_PERIOD))
1862
+ {
1863
+ readToken (name);
1864
+ readToken (token);
1865
+ }
1866
+ if ( isType (token, TOKEN_OPEN_PAREN) )
1867
+ {
1868
+ skipArgumentList(token);
1869
+
1870
+ }
1871
+
1872
+ while (!(isKeyword (token, KEYWORD_is) ||
1873
+ isType (token, TOKEN_SEMICOLON)
1874
+ ))
1875
+ {
1876
+ readToken (token);
1877
+ }
1878
+
1879
+ if ( (isType (name, TOKEN_IDENTIFIER) || isType (name, TOKEN_STRING))
1880
+ && isKeyword (token, KEYWORD_is) )
1881
+ {
1882
+ makeSqlTag (name, SQLTAG_VIEW);
1883
+ }
1884
+
1885
+ findCmdTerm (token, TRUE);
1886
+
1887
+ deleteToken (name);
1888
+ }
1889
+
1890
+ static void parseMLTable (tokenInfo *const token)
1891
+ {
1892
+ tokenInfo *const version = newToken ();
1893
+ tokenInfo *const table = newToken ();
1894
+ tokenInfo *const event = newToken ();
1895
+
1896
+ /*
1897
+ * This deals with these formats
1898
+ * call dbo.ml_add_table_script( 'version', 'table_name', 'event',
1899
+ * 'some SQL statement'
1900
+ * );
1901
+ */
1902
+
1903
+ readToken (token);
1904
+ if ( isType (token, TOKEN_OPEN_PAREN) )
1905
+ {
1906
+ readToken (version);
1907
+ readToken (token);
1908
+ while (!(isType (token, TOKEN_COMMA) ||
1909
+ isType (token, TOKEN_CLOSE_PAREN)
1910
+ ))
1911
+ {
1912
+ readToken (token);
1913
+ }
1914
+
1915
+ if (isType (token, TOKEN_COMMA))
1916
+ {
1917
+ readToken (table);
1918
+ readToken (token);
1919
+ while (!(isType (token, TOKEN_COMMA) ||
1920
+ isType (token, TOKEN_CLOSE_PAREN)
1921
+ ))
1922
+ {
1923
+ readToken (token);
1924
+ }
1925
+
1926
+ if (isType (token, TOKEN_COMMA))
1927
+ {
1928
+ readToken (event);
1929
+
1930
+ if (isType (version, TOKEN_STRING) &&
1931
+ isType (table, TOKEN_STRING) &&
1932
+ isType (event, TOKEN_STRING) )
1933
+ {
1934
+ addToScope(version, table->string);
1935
+ addToScope(version, event->string);
1936
+ makeSqlTag (version, SQLTAG_MLTABLE);
1937
+ }
1938
+ }
1939
+ if( !isType (token, TOKEN_CLOSE_PAREN) )
1940
+ findToken (token, TOKEN_CLOSE_PAREN);
1941
+ }
1942
+ }
1943
+
1944
+ findCmdTerm (token, TRUE);
1945
+
1946
+ deleteToken (version);
1947
+ deleteToken (table);
1948
+ deleteToken (event);
1949
+ }
1950
+
1951
+ static void parseMLConn (tokenInfo *const token)
1952
+ {
1953
+ tokenInfo *const version = newToken ();
1954
+ tokenInfo *const event = newToken ();
1955
+
1956
+ /*
1957
+ * This deals with these formats
1958
+ * call ml_add_connection_script( 'version', 'event',
1959
+ * 'some SQL statement'
1960
+ * );
1961
+ */
1962
+
1963
+ readToken (token);
1964
+ if ( isType (token, TOKEN_OPEN_PAREN) )
1965
+ {
1966
+ readToken (version);
1967
+ readToken (token);
1968
+ while (!(isType (token, TOKEN_COMMA) ||
1969
+ isType (token, TOKEN_CLOSE_PAREN)
1970
+ ))
1971
+ {
1972
+ readToken (token);
1973
+ }
1974
+
1975
+ if (isType (token, TOKEN_COMMA))
1976
+ {
1977
+ readToken (event);
1978
+
1979
+ if (isType (version, TOKEN_STRING) &&
1980
+ isType (event, TOKEN_STRING) )
1981
+ {
1982
+ addToScope(version, event->string);
1983
+ makeSqlTag (version, SQLTAG_MLCONN);
1984
+ }
1985
+ }
1986
+ if( !isType (token, TOKEN_CLOSE_PAREN) )
1987
+ findToken (token, TOKEN_CLOSE_PAREN);
1988
+
1989
+ }
1990
+
1991
+ findCmdTerm (token, TRUE);
1992
+
1993
+ deleteToken (version);
1994
+ deleteToken (event);
1995
+ }
1996
+
1997
+ static void parseComment (tokenInfo *const token)
1998
+ {
1999
+ /*
2000
+ * This deals with this statement:
2001
+ * COMMENT TO PRESERVE FORMAT ON PROCEDURE "DBA"."test" IS
2002
+ * {create PROCEDURE DBA."test"()
2003
+ * BEGIN
2004
+ * signal dave;
2005
+ * END
2006
+ * }
2007
+ * ;
2008
+ * The comment can contain anything between the CURLY
2009
+ * braces
2010
+ * COMMENT ON USER "admin" IS
2011
+ * 'Administration Group'
2012
+ * ;
2013
+ * Or it could be a simple string with no curly braces
2014
+ */
2015
+ while (! isKeyword (token, KEYWORD_is))
2016
+ {
2017
+ readToken (token);
2018
+ }
2019
+ readToken (token);
2020
+ if ( isType(token, TOKEN_OPEN_CURLY) )
2021
+ {
2022
+ findToken (token, TOKEN_CLOSE_CURLY);
2023
+ }
2024
+
2025
+ findCmdTerm (token, TRUE);
2026
+ }
2027
+
2028
+
2029
+ static void parseKeywords (tokenInfo *const token)
2030
+ {
2031
+ switch (token->keyword)
2032
+ {
2033
+ case KEYWORD_begin: parseBlock (token, FALSE); break;
2034
+ case KEYWORD_comment: parseComment (token); break;
2035
+ case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
2036
+ case KEYWORD_datatype: parseDomain (token); break;
2037
+ case KEYWORD_declare: parseBlock (token, FALSE); break;
2038
+ case KEYWORD_domain: parseDomain (token); break;
2039
+ case KEYWORD_drop: parseDrop (token); break;
2040
+ case KEYWORD_event: parseEvent (token); break;
2041
+ case KEYWORD_function: parseSubProgram (token); break;
2042
+ case KEYWORD_if: parseStatements (token); break;
2043
+ case KEYWORD_index: parseIndex (token); break;
2044
+ case KEYWORD_ml_table: parseMLTable (token); break;
2045
+ case KEYWORD_ml_table_lang: parseMLTable (token); break;
2046
+ case KEYWORD_ml_table_dnet: parseMLTable (token); break;
2047
+ case KEYWORD_ml_table_java: parseMLTable (token); break;
2048
+ case KEYWORD_ml_table_chk: parseMLTable (token); break;
2049
+ case KEYWORD_ml_conn: parseMLConn (token); break;
2050
+ case KEYWORD_ml_conn_lang: parseMLConn (token); break;
2051
+ case KEYWORD_ml_conn_dnet: parseMLConn (token); break;
2052
+ case KEYWORD_ml_conn_java: parseMLConn (token); break;
2053
+ case KEYWORD_ml_conn_chk: parseMLConn (token); break;
2054
+ case KEYWORD_package: parsePackage (token); break;
2055
+ case KEYWORD_procedure: parseSubProgram (token); break;
2056
+ case KEYWORD_publication: parsePublication (token); break;
2057
+ case KEYWORD_service: parseService (token); break;
2058
+ case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
2059
+ case KEYWORD_synonym: parseSynonym (token); break;
2060
+ case KEYWORD_table: parseTable (token); break;
2061
+ case KEYWORD_trigger: parseTrigger (token); break;
2062
+ case KEYWORD_type: parseType (token); break;
2063
+ case KEYWORD_variable: parseVariable (token); break;
2064
+ case KEYWORD_view: parseView (token); break;
2065
+ default: break;
2066
+ }
2067
+ }
2068
+
2069
+ static void parseSqlFile (tokenInfo *const token)
2070
+ {
2071
+ do
2072
+ {
2073
+ readToken (token);
2074
+
2075
+ if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
2076
+ parseLabel (token);
2077
+ else
2078
+ parseKeywords (token);
2079
+ } while (! isKeyword (token, KEYWORD_end));
2080
+ }
2081
+
2082
+ static void initialize (const langType language)
2083
+ {
2084
+ Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
2085
+ Lang_sql = language;
2086
+ buildSqlKeywordHash ();
2087
+ }
2088
+
2089
+ static void findSqlTags (void)
2090
+ {
2091
+ tokenInfo *const token = newToken ();
2092
+ exception_t exception = (exception_t) (setjmp (Exception));
2093
+
2094
+ while (exception == ExceptionNone)
2095
+ parseSqlFile (token);
2096
+
2097
+ deleteToken (token);
2098
+ }
2099
+
2100
+ extern parserDefinition* SqlParser (void)
2101
+ {
2102
+ static const char *const extensions [] = { "sql", NULL };
2103
+ parserDefinition* def = parserNew ("SQL");
2104
+ def->kinds = SqlKinds;
2105
+ def->kindCount = KIND_COUNT (SqlKinds);
2106
+ def->extensions = extensions;
2107
+ def->parser = findSqlTags;
2108
+ def->initialize = initialize;
2109
+ return def;
2110
+ }
2111
+
2112
+ /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */