?? sql.c
字號:
/* * $Id: sql.c 574 2007-06-26 23:05:27Z dfishburn $ * * 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>#ifdef DEBUG#include <stdio.h>#endif#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 * * On-line SQL Anywhere Documentation * http://www.ianywhere.com/developer/product_manuals/sqlanywhere/index.html *//* * 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_case, KEYWORD_for, KEYWORD_call, KEYWORD_package, KEYWORD_pragma, KEYWORD_procedure, KEYWORD_record, KEYWORD_object, KEYWORD_ref, KEYWORD_rem, KEYWORD_return, KEYWORD_returns, KEYWORD_subtype, KEYWORD_table, KEYWORD_trigger, KEYWORD_type, KEYWORD_index, KEYWORD_event, KEYWORD_publication, KEYWORD_service, KEYWORD_domain, KEYWORD_datatype, KEYWORD_result, KEYWORD_when, KEYWORD_then, KEYWORD_variable, KEYWORD_exception, KEYWORD_at, KEYWORD_on, KEYWORD_primary, KEYWORD_references, KEYWORD_unique, KEYWORD_check, KEYWORD_constraint, KEYWORD_foreign, KEYWORD_ml_table, KEYWORD_ml_table_lang, KEYWORD_ml_table_dnet, KEYWORD_ml_table_java, KEYWORD_ml_table_chk, KEYWORD_ml_conn, KEYWORD_ml_conn_lang, KEYWORD_ml_conn_dnet, KEYWORD_ml_conn_java, KEYWORD_ml_conn_chk, KEYWORD_local, KEYWORD_temporary, KEYWORD_drop, KEYWORD_view, KEYWORD_synonym, KEYWORD_handler, KEYWORD_comment, KEYWORD_go} 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, TOKEN_PERIOD, TOKEN_OPEN_CURLY, TOKEN_CLOSE_CURLY, TOKEN_TILDE, TOKEN_FORWARD_SLASH} tokenType;typedef struct sTokenInfo { tokenType type; keywordId keyword; vString * string; vString * scope; 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_INDEX, SQLTAG_EVENT, SQLTAG_PUBLICATION, SQLTAG_SERVICE, SQLTAG_DOMAIN, SQLTAG_VIEW, SQLTAG_SYNONYM, SQLTAG_MLTABLE, SQLTAG_MLCONN, 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" }, { FALSE, 'r', "record", "records" }, { TRUE, 's', "subtype", "subtypes" }, { TRUE, 't', "table", "tables" }, { TRUE, 'T', "trigger", "triggers" }, { TRUE, 'v', "variable", "variables" }, { TRUE, 'i', "index", "indexes" }, { TRUE, 'e', "event", "events" }, { TRUE, 'U', "publication", "publications" }, { TRUE, 'R', "service", "services" }, { TRUE, 'D', "domain", "domains" }, { TRUE, 'V', "view", "views" }, { TRUE, 'n', "synonym", "synonyms" }, { TRUE, 'x', "mltable", "MobiLink Table Scripts" }, { TRUE, 'y', "mlconn", "MobiLink Conn Scripts" }};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 }, { "case", KEYWORD_case }, { "for", KEYWORD_for }, { "call", KEYWORD_call }, { "package", KEYWORD_package }, { "pragma", KEYWORD_pragma }, { "procedure", KEYWORD_procedure }, { "record", KEYWORD_record }, { "object", KEYWORD_object }, { "ref", KEYWORD_ref }, { "rem", KEYWORD_rem }, { "return", KEYWORD_return }, { "returns", KEYWORD_returns }, { "subtype", KEYWORD_subtype }, { "table", KEYWORD_table }, { "trigger", KEYWORD_trigger }, { "type", KEYWORD_type }, { "index", KEYWORD_index }, { "event", KEYWORD_event }, { "publication", KEYWORD_publication }, { "service", KEYWORD_service }, { "result", KEYWORD_result }, { "when", KEYWORD_when }, { "then", KEYWORD_then }, { "variable", KEYWORD_variable }, { "exception", KEYWORD_exception }, { "at", KEYWORD_at }, { "on", KEYWORD_on }, { "primary", KEYWORD_primary }, { "references", KEYWORD_references }, { "unique", KEYWORD_unique }, { "check", KEYWORD_check }, { "constraint", KEYWORD_constraint }, { "foreign", KEYWORD_foreign }, { "ml_add_table_script", KEYWORD_ml_table }, { "ml_add_lang_table_script", KEYWORD_ml_table_lang }, { "ml_add_dnet_table_script", KEYWORD_ml_table_dnet }, { "ml_add_java_table_script", KEYWORD_ml_table_java }, { "ml_add_lang_table_script_chk", KEYWORD_ml_table_chk }, { "ml_add_connection_script", KEYWORD_ml_conn }, { "ml_add_lang_connection_script", KEYWORD_ml_conn_lang }, { "ml_add_dnet_connection_script", KEYWORD_ml_conn_dnet }, { "ml_add_java_connection_script", KEYWORD_ml_conn_java }, { "ml_add_lang_conn_script_chk", KEYWORD_ml_conn_chk }, { "local", KEYWORD_local }, { "temporary", KEYWORD_temporary }, { "drop", KEYWORD_drop }, { "view", KEYWORD_view }, { "synonym", KEYWORD_synonym }, { "handler", KEYWORD_handler }, { "comment", KEYWORD_comment }, { "go", KEYWORD_go }};/* * FUNCTION DECLARATIONS *//* Recursive calls */static void parseBlock (tokenInfo *const token, const boolean local);/* * FUNCTION DEFINITIONS */static boolean isIdentChar1 (const int c){ /* * Other databases are less restrictive on the first character of * an identifier. * isIdentChar1 is used to identify the first character of an * identifier, so we are removing some restrictions. */ return (boolean) (isalpha (c) || c == '@' || c == '_' );}static boolean isIdentChar (const int c){ return (boolean) (isalpha (c) || isdigit (c) || c == '$' || c == '@' || c == '_' || c == '#');}static boolean isCmdTerm (tokenInfo *const token){#ifdef DEBUGed printf( "\n isCmdTerm: token same tt:%d tk:%d\n" , token->type , token->keyword );#endif /* * Based on the various customer sites I have been at * the most common command delimiters are * ; * ~ * / * go * This routine will check for any of these, more * can easily be added by modifying readToken and * either adding the character to: * enum eTokenType * enum eTokenType */ return ( isType (token, TOKEN_SEMICOLON) || isType (token, TOKEN_TILDE) || isType (token, TOKEN_FORWARD_SLASH) || isKeyword (token, KEYWORD_go) );}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 (); token->scope = vStringNew (); return token;}static void deleteToken (tokenInfo *const token){ vStringDelete (token->string); vStringDelete (token->scope); eFree (token);}/* * Tag generation functions */static void makeConstTag (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); }}static void makeSqlTag (tokenInfo *const token, const sqlKind kind){ vString * fulltag; if (SqlKinds [kind].enabled) { /* * If a scope has been added to the token, change the token * string to include the scope when making the tag. */ if ( vStringLength(token->scope) > 0 ) { fulltag = vStringNew (); vStringCopy(fulltag, token->scope); vStringCatS (fulltag, "."); vStringCatS (fulltag, vStringValue(token->string)); vStringTerminate(fulltag); vStringCopy(token->string, fulltag); vStringDelete (fulltag); } makeConstTag (token, kind); }}/* * 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){ vString *keyword = vStringNew (); keywordId result; vStringCopyToLower (keyword, name); result = (keywordId) lookupKeyword (vStringValue (keyword), Lang_sql); vStringDelete (keyword); return result;}static void readToken (tokenInfo *const token){ int c; token->type = TOKEN_UNDEFINED; token->keyword = KEYWORD_NONE; vStringClear (token->string);getNextChar: do { c = fileGetc (); /* * Added " to the list of ignores, not sure what this * might break but it gets by this issue: * create table "t1" (...) * * Darren, the code passes all my tests for both * Oracle and SQL Anywhere, but maybe you can tell me * what this may effect. */ } 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_PERIOD; break; case ',': token->type = TOKEN_COMMA; break; case '{': token->type = TOKEN_OPEN_CURLY; break; case '}': token->type = TOKEN_CLOSE_CURLY; break; case '~': token->type = TOKEN_TILDE; break; case '\'': case '"': token->type = TOKEN_STRING; parseString (token->string, c); token->lineNumber = getSourceLineNumber (); token->filePosition = getInputFilePosition (); 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; int d = fileGetc (); if (d == initial) { if (initial == '<') token->type = TOKEN_BLOCK_LABEL_BEGIN; else token->type = TOKEN_BLOCK_LABEL_END; } else { fileUngetc (d); token->type = TOKEN_UNDEFINED; } break; } case '/': { int d = fileGetc (); if ( (d != '*') && /* is this the start of a comment? */ (d != '/') ) /* is a one line comment? */ { token->type = TOKEN_FORWARD_SLASH; fileUngetc (d); } else { if (d == '*') { do { skipToCharacter ('*'); c = fileGetc (); if (c == '/') break; else fileUngetc (c); } while (c != EOF && c != '\0'); goto getNextChar; } else if (d == '/') /* is this the start of a comment? */ { skipToCharacter ('\n'); goto getNextChar; } } break; } default: if (! isIdentChar1 (c)) token->type = TOKEN_UNDEFINED; else { parseIdentifier (token->string, c); token->lineNumber = getSourceLineNumber (); token->filePosition = getInputFilePosition (); token->keyword = analyzeToken (token->string); if (isKeyword (token, KEYWORD_rem)) { vStringClear (token->string); skipToCharacter ('\n'); goto getNextChar; } else if (isKeyword (token, KEYWORD_NONE)) token->type = TOKEN_IDENTIFIER; else token->type = TOKEN_KEYWORD; } break; }}/* * Token parsing functions */
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -