Updated Spanish translation
[anjuta-git-plugin.git] / tagmanager / sql.c
blobc32e39d6bb48746a9476f6f739bd3e14d4e271a4
1 /*
2 * $Id$
4 * Copyright (c) 2002-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for PL/SQL language
10 * files.
14 * INCLUDE FILES
16 #include "general.h" /* must always come first */
18 #include <ctype.h> /* to define isalpha () */
19 #include <setjmp.h>
21 #include "debug.h"
22 #include "entry.h"
23 #include "keyword.h"
24 #include "parse.h"
25 #include "read.h"
26 #include "routines.h"
27 #include "vstring.h"
30 * On-line PL/SQL Reference Guide:
31 * http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
33 * Sample PL/SQL code is available from:
34 * http://www.orafaq.com/faqscrpt.htm#GENPLSQL
38 * MACROS
40 #define isType(token,t) (boolean) ((token)->type == (t))
41 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
44 * DATA DECLARATIONS
47 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
49 /* Used to specify type of keyword.
51 typedef enum eKeywordId {
52 KEYWORD_NONE = -1,
53 KEYWORD_is,
54 KEYWORD_begin,
55 KEYWORD_body,
56 KEYWORD_cursor,
57 KEYWORD_declare,
58 KEYWORD_end,
59 KEYWORD_function,
60 KEYWORD_if,
61 KEYWORD_loop,
62 KEYWORD_package,
63 KEYWORD_pragma,
64 KEYWORD_procedure,
65 KEYWORD_record,
66 KEYWORD_ref,
67 KEYWORD_rem,
68 KEYWORD_return,
69 KEYWORD_subtype,
70 KEYWORD_table,
71 KEYWORD_trigger,
72 KEYWORD_type
73 } keywordId;
75 /* Used to determine whether keyword is valid for the token language and
76 * what its ID is.
78 typedef struct sKeywordDesc {
79 const char *name;
80 keywordId id;
81 } keywordDesc;
83 typedef enum eTokenType {
84 TOKEN_UNDEFINED,
85 TOKEN_BLOCK_LABEL_BEGIN,
86 TOKEN_BLOCK_LABEL_END,
87 TOKEN_CHARACTER,
88 TOKEN_CLOSE_PAREN,
89 TOKEN_SEMICOLON,
90 TOKEN_COMMA,
91 TOKEN_IDENTIFIER,
92 TOKEN_KEYWORD,
93 TOKEN_OPEN_PAREN,
94 TOKEN_OPERATOR,
95 TOKEN_OTHER,
96 TOKEN_STRING
97 } tokenType;
99 typedef struct sTokenInfo {
100 tokenType type;
101 keywordId keyword;
102 vString* string;
103 unsigned long lineNumber;
104 fpos_t filePosition;
105 } tokenInfo;
108 * DATA DEFINITIONS
111 static langType Lang_sql;
113 static jmp_buf Exception;
115 typedef enum {
116 SQLTAG_CURSOR,
117 SQLTAG_PROTOTYPE,
118 SQLTAG_FUNCTION,
119 SQLTAG_FIELD,
120 SQLTAG_LOCAL_VARIABLE,
121 SQLTAG_BLOCK_LABEL,
122 SQLTAG_PACKAGE,
123 SQLTAG_PROCEDURE,
124 SQLTAG_RECORD,
125 SQLTAG_SUBTYPE,
126 SQLTAG_TABLE,
127 SQLTAG_TRIGGER,
128 SQLTAG_VARIABLE,
129 SQLTAG_COUNT
130 } sqlKind;
132 static kindOption SqlKinds [] = {
133 { TRUE, 'c', "cursor", "cursors" },
134 { FALSE, 'd', "prototype", "prototypes" },
135 { TRUE, 'f', "function", "functions" },
136 { TRUE, 'F', "field", "record fields" },
137 { FALSE, 'l', "local", "local variables" },
138 { TRUE, 'L', "label", "block label" },
139 { TRUE, 'P', "package", "packages" },
140 { TRUE, 'p', "procedure", "procedures" },
141 { TRUE, 'r', "record", "records" },
142 { TRUE, 's', "subtype", "subtypes" },
143 { TRUE, 't', "table", "tables" },
144 { TRUE, 'T', "trigger", "triggers" },
145 { TRUE, 'v', "variable", "variables" },
148 static const keywordDesc SqlKeywordTable [] = {
149 /* keyword keyword ID */
150 { "as", KEYWORD_is },
151 { "begin", KEYWORD_begin },
152 { "body", KEYWORD_body },
153 { "cursor", KEYWORD_cursor },
154 { "declare", KEYWORD_declare },
155 { "end", KEYWORD_end },
156 { "function", KEYWORD_function },
157 { "if", KEYWORD_if },
158 { "is", KEYWORD_is },
159 { "loop", KEYWORD_loop },
160 { "package", KEYWORD_package },
161 { "pragma", KEYWORD_pragma },
162 { "procedure", KEYWORD_procedure },
163 { "record", KEYWORD_record },
164 { "ref", KEYWORD_ref },
165 { "rem", KEYWORD_rem },
166 { "return", KEYWORD_return },
167 { "subtype", KEYWORD_subtype },
168 { "table", KEYWORD_table },
169 { "trigger", KEYWORD_trigger },
170 { "type", KEYWORD_type }
174 * FUNCTION DECLARATIONS
177 static void parseBlock (tokenInfo *const token, const boolean local);
180 * FUNCTION DEFINITIONS
183 static boolean isIdentChar1 (const int c)
185 return (boolean) isalpha (c);
188 static boolean isIdentChar (const int c)
190 return (boolean)
191 (isalpha (c) || isdigit (c) || c == '$' || c == '_' || c == '#');
194 static void buildSqlKeywordHash (void)
196 const size_t count = sizeof (SqlKeywordTable) /
197 sizeof (SqlKeywordTable [0]);
198 size_t i;
199 for (i = 0 ; i < count ; ++i)
201 const keywordDesc* const p = &SqlKeywordTable [i];
202 addKeyword (p->name, Lang_sql, (int) p->id);
206 static tokenInfo *newToken (void)
208 tokenInfo *const token = xMalloc (1, tokenInfo);
210 token->type = TOKEN_UNDEFINED;
211 token->keyword = KEYWORD_NONE;
212 token->string = vStringNew ();
214 return token;
217 static void deleteToken (tokenInfo *const token)
219 vStringDelete (token->string);
220 eFree (token);
224 * Tag generation functions
227 static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
229 if (SqlKinds [kind].enabled)
231 const char *const name = vStringValue (token->string);
232 tagEntryInfo e;
233 initTagEntry (&e, name);
235 e.lineNumber = token->lineNumber;
236 e.filePosition = token->filePosition;
237 e.kindName = SqlKinds [kind].name;
238 e.kind = SqlKinds [kind].letter;
240 makeTagEntry (&e);
245 * Parsing functions
248 static int skipToCharacter (const int c)
250 int d;
253 d = fileGetc ();
254 } while (d != EOF && d != c);
255 return d;
258 static void parseString (vString *const string, const int delimiter)
260 boolean end = FALSE;
261 int c;
262 while (! end)
264 c = fileGetc ();
265 if (c == EOF)
266 end = TRUE;
267 else if (c == delimiter)
268 end = TRUE;
269 else
270 vStringPut (string, c);
272 vStringTerminate (string);
275 /* Read a C identifier beginning with "firstChar" and places it into "name".
277 static void parseIdentifier (vString *const string, const int firstChar)
279 int c = firstChar;
280 Assert (isIdentChar1 (c));
283 vStringPut (string, c);
284 c = fileGetc ();
285 } while (isIdentChar (c));
286 vStringTerminate (string);
287 if (!isspace (c))
288 fileUngetc (c); /* unget non-identifier character */
291 static keywordId analyzeToken (vString *const name)
293 static vString *keyword = NULL;
294 if (keyword == NULL)
295 keyword = vStringNew ();
296 vStringCopyToLower (keyword, name);
297 return (keywordId) lookupKeyword (vStringValue (keyword), Lang_sql);
300 static void readToken (tokenInfo *const token)
302 int c;
304 token->type = TOKEN_UNDEFINED;
305 token->keyword = KEYWORD_NONE;
306 vStringClear (token->string);
308 getNextChar:
310 c = fileGetc ();
311 while (c == '\t' || c == ' ' || c == '\n');
313 switch (c)
315 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
316 case '(': token->type = TOKEN_OPEN_PAREN; break;
317 case ')': token->type = TOKEN_CLOSE_PAREN; break;
318 case ';': token->type = TOKEN_SEMICOLON; break;
319 case ',': token->type = TOKEN_COMMA; break;
321 case '\'':
322 case '"':
323 token->type = TOKEN_STRING;
324 parseString (token->string, c);
325 break;
327 case '-':
328 c = fileGetc ();
329 if (c == '-') /* is this the start of a comment? */
331 skipToCharacter ('\n');
332 goto getNextChar;
334 else
336 if (!isspace (c))
337 fileUngetc (c);
338 token->type = TOKEN_OPERATOR;
340 break;
342 case '<':
343 case '>':
345 const int initial = c;
346 int d = fileGetc ();
347 if (d == initial)
349 if (initial == '<')
350 token->type = TOKEN_BLOCK_LABEL_BEGIN;
351 else
352 token->type = TOKEN_BLOCK_LABEL_END;
354 else
356 fileUngetc (d);
357 token->type = TOKEN_UNDEFINED;
359 break;
362 case '/':
364 int d = fileGetc ();
365 if (d != '*') /* is this the start of a comment? */
366 fileUngetc (d);
367 else
371 skipToCharacter ('*');
372 c = fileGetc ();
373 if (c == '/')
374 break;
375 else
376 fileUngetc (c);
377 } while (c != '\0');
378 goto getNextChar;
380 break;
383 default:
384 if (! isIdentChar1 (c))
385 token->type = TOKEN_UNDEFINED;
386 else
388 parseIdentifier (token->string, c);
389 token->lineNumber = getSourceLineNumber ();
390 token->filePosition = getInputFilePosition ();
391 token->keyword = analyzeToken (token->string);
392 if (isKeyword (token, KEYWORD_rem))
394 vStringClear (token->string);
395 skipToCharacter ('\n');
396 goto getNextChar;
398 else if (isKeyword (token, KEYWORD_NONE))
399 token->type = TOKEN_IDENTIFIER;
400 else
401 token->type = TOKEN_KEYWORD;
403 break;
408 * Scanning functions
411 static void findToken (tokenInfo *const token, const tokenType type)
413 while (! isType (token, type))
414 readToken (token);
417 static void skipArgumentList (tokenInfo *const token)
419 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
421 findToken (token, TOKEN_CLOSE_PAREN);
422 readToken (token);
426 static void parseSubProgram (tokenInfo *const token)
428 tokenInfo *const name = newToken ();
429 const sqlKind kind = isKeyword (token, KEYWORD_function) ?
430 SQLTAG_FUNCTION : SQLTAG_PROCEDURE;
431 Assert (isKeyword (token, KEYWORD_function) ||
432 isKeyword (token, KEYWORD_procedure));
433 readToken (name);
434 readToken (token);
435 skipArgumentList (token);
436 if (isKeyword (token, KEYWORD_return))
439 readToken (token); /* read return type */
440 while (!(isKeyword (token, KEYWORD_is) ||
441 isType (token, TOKEN_SEMICOLON)));
443 if (isKeyword (token, KEYWORD_is))
445 if (isType (name, TOKEN_IDENTIFIER))
446 makeSqlTag (name, kind);
447 readToken (token);
448 parseBlock (token, TRUE);
450 else if (isType (token, TOKEN_SEMICOLON))
451 makeSqlTag (name, SQLTAG_PROTOTYPE);
452 deleteToken (name);
455 static void parseRecord (tokenInfo *const token)
457 Assert (isType (token, TOKEN_OPEN_PAREN));
460 readToken (token);
461 if (isType (token, TOKEN_IDENTIFIER))
462 makeSqlTag (token, SQLTAG_FIELD);
463 while (!(isType (token, TOKEN_COMMA) ||
464 isType (token, TOKEN_CLOSE_PAREN)))
465 readToken (token);
466 } while (! isType (token, TOKEN_CLOSE_PAREN));
469 static void parseType (tokenInfo *const token)
471 tokenInfo *const name = newToken ();
472 readToken (name);
473 if (isType (name, TOKEN_IDENTIFIER))
475 readToken (token);
476 if (isKeyword (token, KEYWORD_is))
478 readToken (token);
479 switch (token->keyword)
481 case KEYWORD_record:
482 makeSqlTag (name, SQLTAG_RECORD);
483 parseRecord (token);
484 break;
486 case KEYWORD_table:
487 makeSqlTag (name, SQLTAG_TABLE);
488 break;
490 case KEYWORD_ref:
491 readToken (token);
492 if (isKeyword (token, KEYWORD_cursor))
493 makeSqlTag (name, SQLTAG_CURSOR);
494 break;
496 default: break;
500 deleteToken (name);
503 static void parseSimple (tokenInfo *const token, const sqlKind kind)
505 readToken (token);
506 if (isType (token, TOKEN_IDENTIFIER))
507 makeSqlTag (token, kind);
510 static void parseDeclare (tokenInfo *const token, const boolean local)
512 if (isKeyword (token, KEYWORD_declare))
513 readToken (token);
514 while (! isKeyword (token, KEYWORD_begin) && ! isKeyword (token, KEYWORD_end))
516 switch (token->keyword)
518 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
519 case KEYWORD_function: parseSubProgram (token); break;
520 case KEYWORD_procedure: parseSubProgram (token); break;
521 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
522 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
523 case KEYWORD_type: parseType (token); break;
525 default:
526 if (isType (token, TOKEN_IDENTIFIER))
528 if (local)
529 makeSqlTag (token, SQLTAG_LOCAL_VARIABLE);
530 else
531 makeSqlTag (token, SQLTAG_VARIABLE);
533 break;
535 findToken (token, TOKEN_SEMICOLON);
536 readToken (token);
540 static void parseLabel (tokenInfo *const token)
542 Assert (isType (token, TOKEN_BLOCK_LABEL_BEGIN));
543 readToken (token);
544 if (isType (token, TOKEN_IDENTIFIER))
546 makeSqlTag (token, SQLTAG_BLOCK_LABEL);
547 readToken (token); /* read end of label */
551 static void parseStatements (tokenInfo *const token)
555 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
556 parseLabel (token);
557 else
559 switch (token->keyword)
561 case KEYWORD_if:
562 case KEYWORD_loop:
563 readToken (token);
564 parseStatements (token);
565 break;
567 case KEYWORD_declare:
568 case KEYWORD_begin:
569 parseBlock (token, TRUE);
570 break;
572 default:
573 readToken (token);
574 break;
576 findToken (token, TOKEN_SEMICOLON);
578 readToken (token);
579 } while (! isKeyword (token, KEYWORD_end));
582 static void parseBlock (tokenInfo *const token, const boolean local)
584 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
586 parseLabel (token);
587 readToken (token);
589 if (! isKeyword (token, KEYWORD_begin))
590 parseDeclare (token, local);
591 if (isKeyword (token, KEYWORD_begin))
593 readToken (token);
594 while (! isKeyword (token, KEYWORD_end))
595 parseStatements (token);
596 findToken (token, TOKEN_SEMICOLON);
600 static void parsePackage (tokenInfo *const token)
602 tokenInfo *const name = newToken ();
603 readToken (name);
604 if (isKeyword (name, KEYWORD_body))
605 readToken (name);
606 readToken (token);
607 if (isKeyword (token, KEYWORD_is))
609 if (isType (name, TOKEN_IDENTIFIER))
610 makeSqlTag (name, SQLTAG_PACKAGE);
611 readToken (token);
612 parseBlock (token, FALSE);
614 findToken (token, TOKEN_SEMICOLON);
615 deleteToken (name);
618 static void parseTable (tokenInfo *const token)
620 tokenInfo *const name = newToken ();
621 readToken (name);
622 readToken (token);
623 if (isType (token, TOKEN_OPEN_PAREN))
625 if (isType (name, TOKEN_IDENTIFIER))
627 makeSqlTag (name, SQLTAG_TABLE);
628 parseRecord (token);
631 findToken (token, TOKEN_SEMICOLON);
632 deleteToken (name);
635 static void parseSqlFile (tokenInfo *const token)
639 readToken (token);
640 if (isType (token, TOKEN_BLOCK_LABEL_BEGIN))
641 parseLabel (token);
642 else switch (token->keyword)
644 case KEYWORD_begin: parseBlock (token, FALSE); break;
645 case KEYWORD_cursor: parseSimple (token, SQLTAG_CURSOR); break;
646 case KEYWORD_declare: parseBlock (token, FALSE); break;
647 case KEYWORD_function: parseSubProgram (token); break;
648 case KEYWORD_package: parsePackage (token); break;
649 case KEYWORD_procedure: parseSubProgram (token); break;
650 case KEYWORD_subtype: parseSimple (token, SQLTAG_SUBTYPE); break;
651 case KEYWORD_table: parseTable (token); break;
652 case KEYWORD_trigger: parseSimple (token, SQLTAG_TRIGGER); break;
653 case KEYWORD_type: parseType (token); break;
654 default: break;
656 } while (! isKeyword (token, KEYWORD_end));
659 static void initialize (const langType language)
661 Assert (sizeof (SqlKinds) / sizeof (SqlKinds [0]) == SQLTAG_COUNT);
662 Lang_sql = language;
663 buildSqlKeywordHash ();
666 static void findSqlTags (void)
668 tokenInfo *const token = newToken ();
669 exception_t exception = (exception_t) (setjmp (Exception));
670 while (exception == ExceptionNone)
671 parseSqlFile (token);
672 deleteToken (token);
675 extern parserDefinition* SqlParser (void)
677 static const char *const extensions [] = { "sql", NULL };
678 parserDefinition* def = parserNew ("SQL");
679 def->kinds = SqlKinds;
680 def->kindCount = KIND_COUNT (SqlKinds);
681 def->extensions = extensions;
682 def->parser = findSqlTags;
683 def->initialize = initialize;
684 return def;
687 /* vi:set tabstop=4 shiftwidth=4: */