📄 sql.c
字号:
/*
* $Id: sql.c,v 1.10 2003/12/21 19:19:27 darren Exp $
*
* Copyright (c) 2002-2003, Darren Hiebert
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* This module contains functions for generating tags for PL/SQL language
* files.
*/
/*
* INCLUDE FILES
*/
#include "general.h" /* must always come first */
#include <ctype.h> /* to define isalpha () */
#include <setjmp.h>
#include "debug.h"
#include "entry.h"
#include "keyword.h"
#include "parse.h"
#include "read.h"
#include "routines.h"
#include "vstring.h"
/*
* On-line PL/SQL Reference Guide:
* http://info-it.umsystem.edu/oradocs/doc/server/doc/PLS23/toc.htm
*
* Sample PL/SQL code is available from:
* http://www.orafaq.com/faqscrpt.htm#GENPLSQL
*/
/*
* MACROS
*/
#define isType(token,t) (boolean) ((token)->type == (t))
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
/*
* DATA DECLARATIONS
*/
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
/* Used to specify type of keyword.
*/
typedef enum eKeywordId {
KEYWORD_NONE = -1,
KEYWORD_is,
KEYWORD_begin,
KEYWORD_body,
KEYWORD_cursor,
KEYWORD_declare,
KEYWORD_end,
KEYWORD_function,
KEYWORD_if,
KEYWORD_loop,
KEYWORD_package,
KEYWORD_pragma,
KEYWORD_procedure,
KEYWORD_record,
KEYWORD_ref,
KEYWORD_rem,
KEYWORD_return,
KEYWORD_subtype,
KEYWORD_table,
KEYWORD_trigger,
KEYWORD_type
} keywordId;
/* Used to determine whether keyword is valid for the token language and
* what its ID is.
*/
typedef struct sKeywordDesc {
const char *name;
keywordId id;
} keywordDesc;
typedef enum eTokenType {
TOKEN_UNDEFINED,
TOKEN_BLOCK_LABEL_BEGIN,
TOKEN_BLOCK_LABEL_END,
TOKEN_CHARACTER,
TOKEN_CLOSE_PAREN,
TOKEN_SEMICOLON,
TOKEN_COMMA,
TOKEN_IDENTIFIER,
TOKEN_KEYWORD,
TOKEN_OPEN_PAREN,
TOKEN_OPERATOR,
TOKEN_OTHER,
TOKEN_STRING
} tokenType;
typedef struct sTokenInfo {
tokenType type;
keywordId keyword;
vString * string;
unsigned long lineNumber;
fpos_t filePosition;
} tokenInfo;
/*
* DATA DEFINITIONS
*/
static langType Lang_sql;
static jmp_buf Exception;
typedef enum {
SQLTAG_CURSOR,
SQLTAG_PROTOTYPE,
SQLTAG_FUNCTION,
SQLTAG_FIELD,
SQLTAG_LOCAL_VARIABLE,
SQLTAG_BLOCK_LABEL,
SQLTAG_PACKAGE,
SQLTAG_PROCEDURE,
SQLTAG_RECORD,
SQLTAG_SUBTYPE,
SQLTAG_TABLE,
SQLTAG_TRIGGER,
SQLTAG_VARIABLE,
SQLTAG_COUNT
} sqlKind;
static kindOption SqlKinds [] = {
{ TRUE, 'c', "cursor", "cursors" },
{ FALSE, 'd', "prototype", "prototypes" },
{ TRUE, 'f', "function", "functions" },
{ TRUE, 'F', "field", "record fields" },
{ FALSE, 'l', "local", "local variables"},
{ TRUE, 'L', "label", "block label" },
{ TRUE, 'P', "package", "packages" },
{ TRUE, 'p', "procedure", "procedures" },
{ TRUE, 'r', "record", "records" },
{ TRUE, 's', "subtype", "subtypes" },
{ TRUE, 't', "table", "tables" },
{ TRUE, 'T', "trigger", "triggers" },
{ TRUE, 'v', "variable", "variables" },
};
static const keywordDesc SqlKeywordTable [] = {
/* keyword keyword ID */
{ "as", KEYWORD_is },
{ "begin", KEYWORD_begin },
{ "body", KEYWORD_body },
{ "cursor", KEYWORD_cursor },
{ "declare", KEYWORD_declare },
{ "end", KEYWORD_end },
{ "function", KEYWORD_function },
{ "if", KEYWORD_if },
{ "is", KEYWORD_is },
{ "loop", KEYWORD_loop },
{ "package", KEYWORD_package },
{ "pragma", KEYWORD_pragma },
{ "procedure", KEYWORD_procedure },
{ "record", KEYWORD_record },
{ "ref", KEYWORD_ref },
{ "rem", KEYWORD_rem },
{ "return", KEYWORD_return },
{ "subtype", KEYWORD_subtype },
{ "table", KEYWORD_table },
{ "trigger", KEYWORD_trigger },
{ "type", KEYWORD_type }
};
/*
* FUNCTION DECLARATIONS
*/
static void parseBlock (tokenInfo *const token, const boolean local);
/*
* FUNCTION DEFINITIONS
*/
static boolean isIdentChar1 (const int c)
{
return (boolean) isalpha (c);
}
static boolean isIdentChar (const int c)
{
return (boolean)
(isalpha (c) || isdigit (c) || c == '$' || c == '_' || c == '#');
}
static void buildSqlKeywordHash (void)
{
const size_t count = sizeof (SqlKeywordTable) /
sizeof (SqlKeywordTable [0]);
size_t i;
for (i = 0 ; i < count ; ++i)
{
const keywordDesc* const p = &SqlKeywordTable [i];
addKeyword (p->name, Lang_sql, (int) p->id);
}
}
static tokenInfo *newToken (void)
{
tokenInfo *const token = xMalloc (1, tokenInfo);
token->type = TOKEN_UNDEFINED;
token->keyword = KEYWORD_NONE;
token->string = vStringNew ();
return token;
}
static void deleteToken (tokenInfo *const token)
{
vStringDelete (token->string);
eFree (token);
}
/*
* Tag generation functions
*/
static void makeSqlTag (tokenInfo *const token, const sqlKind kind)
{
if (SqlKinds [kind].enabled)
{
const char *const name = vStringValue (token->string);
tagEntryInfo e;
initTagEntry (&e, name);
e.lineNumber = token->lineNumber;
e.filePosition = token->filePosition;
e.kindName = SqlKinds [kind].name;
e.kind = SqlKinds [kind].letter;
makeTagEntry (&e);
}
}
/*
* Parsing functions
*/
static int skipToCharacter (const int c)
{
int d;
do
{
d = fileGetc ();
} while (d != EOF && d != c);
return d;
}
static void parseString (vString *const string, const int delimiter)
{
boolean end = FALSE;
int c;
while (! end)
{
c = fileGetc ();
if (c == EOF)
end = TRUE;
else if (c == delimiter)
end = TRUE;
else
vStringPut (string, c);
}
vStringTerminate (string);
}
/* Read a C identifier beginning with "firstChar" and places it into "name".
*/
static void parseIdentifier (vString *const string, const int firstChar)
{
int c = firstChar;
Assert (isIdentChar1 (c));
do
{
vStringPut (string, c);
c = fileGetc ();
} while (isIdentChar (c));
vStringTerminate (string);
if (!isspace (c))
fileUngetc (c); /* unget non-identifier character */
}
static keywordId analyzeToken (vString *const name)
{
static vString *keyword = NULL;
if (keyword == NULL)
keyword = vStringNew ();
vStringCopyToLower (keyword, name);
return (keywordId) lookupKeyword (vStringValue (keyword), Lang_sql);
}
static void readToken (tokenInfo *const token)
{
int c;
token->type = TOKEN_UNDEFINED;
token->keyword = KEYWORD_NONE;
vStringClear (token->string);
getNextChar:
do
c = fileGetc ();
while (c == '\t' || c == ' ' || c == '\n');
switch (c)
{
case EOF: longjmp (Exception, (int)ExceptionEOF); break;
case '(': token->type = TOKEN_OPEN_PAREN; break;
case ')': token->type = TOKEN_CLOSE_PAREN; break;
case ';': token->type = TOKEN_SEMICOLON; break;
case ',': token->type = TOKEN_COMMA; break;
case '\'':
case '"':
token->type = TOKEN_STRING;
parseString (token->string, c);
break;
case '-':
c = fileGetc ();
if (c == '-') /* is this the start of a comment? */
{
skipToCharacter ('\n');
goto getNextChar;
}
else
{
if (!isspace (c))
fileUngetc (c);
token->type = TOKEN_OPERATOR;
}
break;
case '<':
case '>':
{
const int initial = c;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -