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
16 #include "general.h" /* must always come first */
18 #include <ctype.h> /* to define isalpha () */
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
40 #define isType(token,t) (boolean) ((token)->type == (t))
41 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
47 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
49 /* Used to specify type of keyword.
51 typedef enum eKeywordId
{
75 /* Used to determine whether keyword is valid for the token language and
78 typedef struct sKeywordDesc
{
83 typedef enum eTokenType
{
85 TOKEN_BLOCK_LABEL_BEGIN
,
86 TOKEN_BLOCK_LABEL_END
,
99 typedef struct sTokenInfo
{
103 unsigned long lineNumber
;
111 static langType Lang_sql
;
113 static jmp_buf Exception
;
120 SQLTAG_LOCAL_VARIABLE
,
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
)
191 (isalpha (c
) || isdigit (c
) || c
== '$' || c
== '_' || c
== '#');
194 static void buildSqlKeywordHash (void)
196 const size_t count
= sizeof (SqlKeywordTable
) /
197 sizeof (SqlKeywordTable
[0]);
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 ();
217 static void deleteToken (tokenInfo
*const token
)
219 vStringDelete (token
->string
);
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
);
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
;
248 static int skipToCharacter (const int c
)
254 } while (d
!= EOF
&& d
!= c
);
258 static void parseString (vString
*const string
, const int delimiter
)
267 else if (c
== delimiter
)
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
)
280 Assert (isIdentChar1 (c
));
283 vStringPut (string
, c
);
285 } while (isIdentChar (c
));
286 vStringTerminate (string
);
288 fileUngetc (c
); /* unget non-identifier character */
291 static keywordId
analyzeToken (vString
*const name
)
293 static vString
*keyword
= NULL
;
295 keyword
= vStringNew ();
296 vStringCopyToLower (keyword
, name
);
297 return (keywordId
) lookupKeyword (vStringValue (keyword
), Lang_sql
);
300 static void readToken (tokenInfo
*const token
)
304 token
->type
= TOKEN_UNDEFINED
;
305 token
->keyword
= KEYWORD_NONE
;
306 vStringClear (token
->string
);
311 while (c
== '\t' || c
== ' ' || c
== '\n');
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;
323 token
->type
= TOKEN_STRING
;
324 parseString (token
->string
, c
);
329 if (c
== '-') /* is this the start of a comment? */
331 skipToCharacter ('\n');
338 token
->type
= TOKEN_OPERATOR
;
345 const int initial
= c
;
350 token
->type
= TOKEN_BLOCK_LABEL_BEGIN
;
352 token
->type
= TOKEN_BLOCK_LABEL_END
;
357 token
->type
= TOKEN_UNDEFINED
;
365 if (d
!= '*') /* is this the start of a comment? */
371 skipToCharacter ('*');
384 if (! isIdentChar1 (c
))
385 token
->type
= TOKEN_UNDEFINED
;
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');
398 else if (isKeyword (token
, KEYWORD_NONE
))
399 token
->type
= TOKEN_IDENTIFIER
;
401 token
->type
= TOKEN_KEYWORD
;
411 static void findToken (tokenInfo
*const token
, const tokenType type
)
413 while (! isType (token
, type
))
417 static void skipArgumentList (tokenInfo
*const token
)
419 if (isType (token
, TOKEN_OPEN_PAREN
)) /* arguments? */
421 findToken (token
, TOKEN_CLOSE_PAREN
);
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
));
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
);
448 parseBlock (token
, TRUE
);
450 else if (isType (token
, TOKEN_SEMICOLON
))
451 makeSqlTag (name
, SQLTAG_PROTOTYPE
);
455 static void parseRecord (tokenInfo
*const token
)
457 Assert (isType (token
, TOKEN_OPEN_PAREN
));
461 if (isType (token
, TOKEN_IDENTIFIER
))
462 makeSqlTag (token
, SQLTAG_FIELD
);
463 while (!(isType (token
, TOKEN_COMMA
) ||
464 isType (token
, TOKEN_CLOSE_PAREN
)))
466 } while (! isType (token
, TOKEN_CLOSE_PAREN
));
469 static void parseType (tokenInfo
*const token
)
471 tokenInfo
*const name
= newToken ();
473 if (isType (name
, TOKEN_IDENTIFIER
))
476 if (isKeyword (token
, KEYWORD_is
))
479 switch (token
->keyword
)
482 makeSqlTag (name
, SQLTAG_RECORD
);
487 makeSqlTag (name
, SQLTAG_TABLE
);
492 if (isKeyword (token
, KEYWORD_cursor
))
493 makeSqlTag (name
, SQLTAG_CURSOR
);
503 static void parseSimple (tokenInfo
*const token
, const sqlKind kind
)
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
))
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;
526 if (isType (token
, TOKEN_IDENTIFIER
))
529 makeSqlTag (token
, SQLTAG_LOCAL_VARIABLE
);
531 makeSqlTag (token
, SQLTAG_VARIABLE
);
535 findToken (token
, TOKEN_SEMICOLON
);
540 static void parseLabel (tokenInfo
*const token
)
542 Assert (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
));
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
))
559 switch (token
->keyword
)
564 parseStatements (token
);
567 case KEYWORD_declare
:
569 parseBlock (token
, TRUE
);
576 findToken (token
, TOKEN_SEMICOLON
);
579 } while (! isKeyword (token
, KEYWORD_end
));
582 static void parseBlock (tokenInfo
*const token
, const boolean local
)
584 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
589 if (! isKeyword (token
, KEYWORD_begin
))
590 parseDeclare (token
, local
);
591 if (isKeyword (token
, KEYWORD_begin
))
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 ();
604 if (isKeyword (name
, KEYWORD_body
))
607 if (isKeyword (token
, KEYWORD_is
))
609 if (isType (name
, TOKEN_IDENTIFIER
))
610 makeSqlTag (name
, SQLTAG_PACKAGE
);
612 parseBlock (token
, FALSE
);
614 findToken (token
, TOKEN_SEMICOLON
);
618 static void parseTable (tokenInfo
*const token
)
620 tokenInfo
*const name
= newToken ();
623 if (isType (token
, TOKEN_OPEN_PAREN
))
625 if (isType (name
, TOKEN_IDENTIFIER
))
627 makeSqlTag (name
, SQLTAG_TABLE
);
631 findToken (token
, TOKEN_SEMICOLON
);
635 static void parseSqlFile (tokenInfo
*const token
)
640 if (isType (token
, TOKEN_BLOCK_LABEL_BEGIN
))
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;
656 } while (! isKeyword (token
, KEYWORD_end
));
659 static void initialize (const langType language
)
661 Assert (sizeof (SqlKinds
) / sizeof (SqlKinds
[0]) == SQLTAG_COUNT
);
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
);
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
;
687 /* vi:set tabstop=4 shiftwidth=4: */