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,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: */