| options{ | |
| STATIC=false; | |
| } | |
| PARSER_BEGIN(QuickSQLParser) | |
| package org.eclipse.datatools.enablement.sybase.parser; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import java.util.regex.Pattern; | |
| import java.util.regex.Matcher; | |
| import java.util.HashMap; | |
| import java.io.StringReader; | |
| import org.eclipse.datatools.enablement.sybase.Activator; | |
| public class QuickSQLParser extends AbstractQuickSQLParser | |
| { | |
| private static QuickSQLParser _instance = new QuickSQLParser(new StringReader("")); | |
| private boolean _debug = false; | |
| private String _input = ""; | |
| private boolean canUseDelimiter = false; | |
| private static final int[] STMT_START = new int[]{ALTER, BEGIN, BREAK, CHECKPOINT, CLOSE, COMMIT, CONNECT, | |
| CONTINUE, CREATE, DEALLOCATE, DECLARE, DELETE, DISK, DROP, DUMP, EXECUTE, EXEC, | |
| FETCH, GOTO, GRANT, IF, INSERT, KILL, LOAD, LOCK, ONLINE, OPEN, PREPARE, //MOUNT, | |
| PRINT, QUIESCE, RAISERROR, READTEXT, RECONFIGURE, REMOVE, REORG, RETURN, REVOKE, | |
| ROLLBACK, SAVE, SELECT, SET, SETUSER, SHUTDOWN, TRUNCATE, UPDATE, USE,//UNMOUNT, | |
| WAITFOR, WHILE, WRITETEXT | |
| }; | |
| public static final String[] STMT_START_STRING = new String[]{"ALTER","BEGIN","BREAK","CHECKPOINT","CLOSE","COMMIT","CONNECT", | |
| "CONTINUE","CREATE","DEALLOCATE","DECLARE","DELETE","DISK","DROP","DUMP","EXECUTE","EXEC", | |
| "FETCH","GOTO","GRANT","IF","INSERT","KILL","LOAD","LOCK","ONLINE","OPEN","PREPARE", | |
| "PRINT","QUIESCE","RAISERROR","READTEXT","RECONFIGURE","REMOVE","REORG","RETURN","REVOKE", | |
| "ROLLBACK","SAVE","SELECT","SET","SETUSER","SHUTDOWN","TRUNCATE","UPDATE","USE", | |
| "WAITFOR","WHILE","WRITETEXT" | |
| }; | |
| private static final int[] DEFINED_STMT_START = new int[]{ALTER, BEGIN, CREATE, DECLARE, DELETE, EXECUTE, EXEC, FETCH, IF, INSERT, PRINT, RETURN, SELECT, UPDATE, USE }; | |
| private static final int[] TERMINATORS = new int[]{GO, SEMICOLON}; | |
| private static final String[] TERMINATORS_STRING = new String[]{"GO", ";"}; | |
| private static final String[] WATCOM_POST_PARAM= new String[] | |
| {"BEGIN","RETURNS", "RESULT","AT","WITH", "AS","EXTERNAL","ON","DYNAMIC"}; | |
| /** | |
| * Returns the statement terminator array. Different vendors will have their | |
| * own terminators defined, so we just leave this method as abstract here. | |
| * | |
| * @return statement terminator array | |
| */ | |
| public String[] getStatementTerminators() | |
| { | |
| return TERMINATORS_STRING; | |
| } | |
| public static QuickSQLParser getInstance(){ | |
| return _instance; | |
| } | |
| public QuickSQLParser() | |
| { | |
| this(new StringReader("")); | |
| } | |
| final private void logDebug(String message) | |
| { | |
| if (_debug) | |
| { | |
| Activator.getDefault().debug(message); | |
| } | |
| } | |
| final private void logError(String message) | |
| { | |
| Activator.getDefault().log(message); | |
| } | |
| protected synchronized void initParser(String text) | |
| { | |
| _input = text; | |
| java.io.StringReader sr = new java.io.StringReader( text ); | |
| java.io.Reader r = new java.io.BufferedReader( sr ); | |
| //ReInit will be generated by JavaCC | |
| ReInit(r); | |
| } | |
| public String getInput() | |
| { | |
| return _input; | |
| } | |
| public boolean match(String input, int pattern) | |
| { | |
| try{ | |
| initParser(input); | |
| switch (pattern) | |
| { | |
| case CREATE_PROC_HEADER_PATTERN: | |
| create_proc_header(); | |
| return true; | |
| case CREATE_FUNC_HEADER_PATTERN: | |
| create_func_header(); | |
| return true; | |
| case CREATE_TRIGGER_HEADER_PATTERN: | |
| create_trigger_header(); | |
| return true; | |
| case CREATE_EVENT_HEADER_PATTERN: | |
| create_event_header(); | |
| return true; | |
| default: | |
| } | |
| }catch(Throwable e) | |
| { | |
| logDebug(e.getMessage()); | |
| return false; | |
| } | |
| return false; | |
| } | |
| public int[] find(String input, String[][] tokens) | |
| { | |
| Token[] ts = getTokens(input, tokens); | |
| if (ts == null) | |
| { | |
| return new int[]{-1, -1}; | |
| } | |
| //calculate index | |
| int[] index = new int[2]; | |
| index[0] = getStartIndex(ts[0]); | |
| index[1] = getEndIndex(ts[1]); | |
| return index; | |
| } | |
| public Token[] getTokens(String input, String[][] tokens) | |
| { | |
| initParser(input); | |
| boolean found = true; | |
| try{ | |
| getNextToken(); | |
| do | |
| { | |
| error_skiptobefore1(tokens[0], new String[]{}, false); | |
| int i = 0; | |
| for (; i< tokens.length; i++) | |
| { | |
| boolean tokenMatch = false; | |
| for (int j=0; j< tokens[i].length; j++) | |
| { | |
| if (tokens[i][j].equalsIgnoreCase(getToken(i).image)) | |
| { | |
| tokenMatch = true; | |
| break; | |
| } | |
| } | |
| if (! tokenMatch) | |
| { | |
| found = false; | |
| getNextToken(); | |
| break; | |
| } | |
| else | |
| { | |
| found = true; | |
| continue; | |
| } | |
| } | |
| if (token.kind == EOF) | |
| { | |
| found = false; | |
| break; | |
| } | |
| }while(!found); | |
| }catch(Throwable e) | |
| { | |
| logDebug(e.getMessage()); | |
| return null; | |
| } | |
| if (! found) | |
| { | |
| return null; | |
| } | |
| //calculate index | |
| Token[] ts = new Token[2]; | |
| ts[0] = getToken(0); | |
| ts[1] = getToken(tokens.length - 1); | |
| return ts; | |
| } | |
| public String[][] getParameters(String input) | |
| { | |
| initParser(input); | |
| try | |
| { | |
| return routine_parameters(); | |
| }catch(Throwable e) | |
| { | |
| logDebug(e.getMessage()); | |
| return null; | |
| } | |
| } | |
| public String[] getDatatypeInfo(String input) | |
| { | |
| initParser(input); | |
| try | |
| { | |
| return datatype_info(); | |
| }catch(Throwable e) | |
| { | |
| logDebug(e.getMessage()); | |
| return null; | |
| } | |
| } | |
| } | |
| PARSER_END(QuickSQLParser) | |
| /*********************************************************************** | |
| * Token definitions | |
| ***********************************************************************/ | |
| SKIP: | |
| { | |
| " " | |
| | "\n" | |
| | "\r" | |
| | "\t" | |
| } | |
| /* COMMENTS */ | |
| MORE : | |
| { | |
| "--" : IN_SINGLE_LINE_COMMENT | |
| } | |
| <IN_SINGLE_LINE_COMMENT> | |
| SPECIAL_TOKEN : | |
| { | |
| <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : DEFAULT | |
| } | |
| <IN_SINGLE_LINE_COMMENT> | |
| MORE : | |
| { | |
| < ~[] > | |
| } | |
| /* | |
| SPECIAL_TOKEN : | |
| { | |
| < SINGLE_LINE_COMMENT: "--"(~["\n","\r"])* ("\n"|"\r"|"\r\n")? > | |
| } | |
| */ | |
| TOKEN_MGR_DECLS : { | |
| int commentNestingDepth = 0 ; | |
| } | |
| MORE : { "/*" { commentNestingDepth = 1 ; } : IN_MULTI_LINE_COMMENT } | |
| < IN_MULTI_LINE_COMMENT > MORE : { "/*" { commentNestingDepth += 1 ; } } | |
| < IN_MULTI_LINE_COMMENT > SPECIAL_TOKEN : { "*/" { | |
| commentNestingDepth -= 1; | |
| SwitchTo( commentNestingDepth==0 ? DEFAULT : IN_MULTI_LINE_COMMENT ); } } | |
| < IN_MULTI_LINE_COMMENT > MORE : { <COMMENT_CONTENT: ~[]> {} } | |
| <IGNORE_STATE> | |
| SKIP : | |
| { | |
| < ~[] > | |
| } | |
| TOKEN: | |
| { | |
| < INVALID_TOKEN: "!%^&" > //Using token will always causes exception | |
| | < ADD: "add" > | |
| | < ALTER: "alter" > | |
| | < AND: "and" > | |
| | < ANY: "any" > | |
| | < AS: "as" > | |
| | < ASC: "asc" > | |
| | < AT: "at" > | |
| | < AUTHORIZATION: "authorization" > | |
| | < AVG: "avg" > | |
| | < BEGIN: "begin" > | |
| | < BETWEEN: "between" > | |
| | < BREAK: "break" > | |
| | < BROWSE: "browse" > | |
| | < BULK: "bulk" > | |
| | < BY: "by" > | |
| | < CASCADE: "cascade" > | |
| | < CASE: "case" > | |
| | < CHECK: "check" > | |
| | < CHECKPOINT: "checkpoint" > | |
| | < CLOSE: "close" > | |
| | < CLUSTERED: "clustered" > | |
| | < COALESCE: "coalesce" > | |
| | < COMMIT: "commit" > | |
| | < COMPUTE: "compute" > | |
| | < CONFIRM: "confirm" > | |
| | < CONNECT: "connect" > | |
| | < CONSTRAINT: "constraint" > | |
| | < CONTINUE: "continue" > | |
| | < CONTROLROW: "controlrow" > | |
| | < CONVERT: "convert" > | |
| | < COUNT: "count" > | |
| | < CREATE: "create" > | |
| | < CURRENT: "current" > | |
| | < CURSOR: "cursor" > | |
| | < DATABASE: "database" > | |
| | < DBCC: "dbcc" > | |
| | < DEALLOCATE: "deallocate" > | |
| | < DECLARE: "declare" > | |
| | < DEFAULT_VAL: "default" > | |
| | < DELETE: "delete" > | |
| | < DESC: "desc" > | |
| | < DETERMINISTIC: "deterministic" > | |
| | < DISK: "disk" > | |
| | < DISTINCT: "distinct" > | |
| | < DROP: "drop" > | |
| | < DUMMY: "dummy" > | |
| | < DUMP: "dump" > | |
| | < ELSE: "else" > | |
| | < END: "end" > | |
| | < ENDTRAN: "endtran" > | |
| | < ESCAPE: "escape" > | |
| | < EXCEPT: "except" > | |
| | < EXCLUSIVE: "exclusive" > | |
| | < EXEC: "exec" > | |
| | < EXECUTE: "execute" > | |
| | < EXISTS: "exists" > | |
| | < EXIT: "exit" > | |
| | < EXTERNAL: "external" > | |
| | < EVENT: "event" > | |
| | < FETCH: "fetch" > | |
| | < FILLFACTOR: "fillfactor" > | |
| | < FOR: "for" > | |
| | < FOREIGN: "foreign" > | |
| | < FROM: "from" > | |
| | < FUNC: "func" > | |
| | < FUNCTION: "function" > | |
| | < GO: "go" > //terminator | |
| | < GOTO: "goto" > | |
| | < GRANT: "grant" > | |
| | < GROUP: "group" > | |
| | < HAVING: "having" > | |
| | < HOLDLOCK: "holdlock" > | |
| | < IDENTITY: "identity" > | |
| | < IF: "if" > | |
| | < IN: "in" > | |
| | < INDEX: "index" > | |
| | < INOUT: "inout" > | |
| | < INSERT: "insert" > | |
| | < INSTALL: "install" > | |
| | < INTERSECT: "intersect" > | |
| | < INTO: "into" > | |
| | < IS: "is" > | |
| | < ISOLATION: "isolation" > | |
| | < JAR: "jar" > | |
| | < JOIN: "join" > | |
| | < KEY: "key" > | |
| | < KILL: "kill" > | |
| | < LEVEL: "level" > | |
| | < LIKE: "like" > | |
| | < LOAD: "load" > | |
| | < LOCK: "lock" > | |
| | < MAX: "max" > | |
| | < MIN: "min" > | |
| | < MODIFY: "modify" > | |
| | < NOHOLDLOCK: "noholdlock" > | |
| | < NONCLUSTERED: "nonclustered" > | |
| | < NOT: "not" > | |
| | < NULL: "null" > | |
| | < NULLIF: "nullif" > | |
| | < OF: "of" > | |
| | < OFF: "off" > | |
| | < OFFSETS: "offsets" > | |
| | < ON: "on" > | |
| | < ONCE: "once" > | |
| | < ONLINE: "online" > | |
| | < ONLY: "only" > | |
| | < OPEN: "open" > | |
| | < OPTION: "option" > | |
| | < OR: "or" > | |
| | < ORDER: "order" > | |
| | < OUT: "out" > | |
| | < OUTPUT: "output" > | |
| | < OVER: "over" > | |
| | < PARTITION: "partition" > | |
| | < PERM: "perm" > | |
| | < PERMANENT: "permanent" > | |
| | < PLAN: "plan" > | |
| | < PREPARE: "prepare" > | |
| | < PRIMARY: "primary" > | |
| | < PRINT: "print" > | |
| | < PRIVILEGES: "privileges" > | |
| | < PROC: "proc" > | |
| | < PROCEDURE: "procedure" > | |
| | < PROCESSEXIT: "processexit" > | |
| | < PROXY_TABLE: "proxy_table" > | |
| | < PUBLIC: "public" > | |
| | < QUIESCE: "quiesce" > | |
| | < RAISERROR: "raiserror" > | |
| | < READ: "read" > | |
| | < READPAST: "readpast" > | |
| | < READTEXT: "readtext" > | |
| | < RECONFIGURE: "reconfigure" > | |
| | < REFERENCES: "references" > | |
| | < REMOVE: "remove" > | |
| | < REORG: "reorg" > | |
| | < REPLACE: "replace" > | |
| | < REPLICATION: "replication" > | |
| | < RETURN: "return" > | |
| | < RETURNS: "returns" > | |
| | < REVOKE: "revoke" > | |
| | < ROLE: "role" > | |
| | < ROLLBACK: "rollback" > | |
| | < ROWCOUNT: "rowcount" > | |
| | < ROWS: "rows" > | |
| | < RULE: "rule" > | |
| | < SAVE: "save" > | |
| | < SCHEMA: "schema" > | |
| | < SELECT: "select" > | |
| | < SET: "set" > | |
| | < SETUSER: "setuser" > | |
| | < SHARED: "shared" > | |
| | < SHUTDOWN: "shutdown" > | |
| | < SOME: "some" > | |
| | < STATISTICS: "statistics" > | |
| | < STRINGSIZE: "stringsize" > | |
| | < STRIPE: "stripe" > | |
| | < SUM: "sum" > | |
| | < TABLE: "table" > | |
| | < TEMP: "temp" > | |
| | < TEMPORARY: "temporary" > | |
| | < TEXTSIZE: "textsize" > | |
| | < TO: "to" > | |
| | < TRAN: "tran" > | |
| | < TRANSACTION: "transaction" > | |
| | < TRIGGER: "trigger" > | |
| | < TRUNCATE: "truncate" > | |
| | < UNION: "union" > | |
| | < UNIQUE: "unique" > | |
| | < UNPARTITION: "unpartition" > | |
| | < UPDATE: "update" > | |
| | < USE: "use" > | |
| | < USER: "user" > | |
| | < USING: "using" > | |
| | < VALUES: "values" > | |
| | < VIEW: "view" > | |
| | < WAITFOR: "waitfor" > | |
| | < WHEN: "when" > | |
| | < WHERE: "where" > | |
| | < WHILE: "while" > | |
| | < WITH: "with" > | |
| | < WORK: "work" > | |
| | < WRITETEXT: "writetext" > | |
| //ASA | |
| | < SQLCODE: "SQLCODE"> | |
| | < SQLSTATE: "SQLSTATE"> | |
| } | |
| <DESCRIPTION_START_STATE> TOKEN: | |
| { | |
| < OPENDESCRIPTION: "\r\n" > : DESCRIPTION_STATE | |
| } | |
| <DESCRIPTION_STATE> TOKEN: /* Line */ | |
| { | |
| < CLOSEDESCRIPTION: "~" > : DEFAULT | |
| | < DESCRIPTION: (~["~"])* "~" > : DEFAULT | |
| } | |
| TOKEN: /* Literals */ | |
| { | |
| < INTEGER_LITERAL: (["0"-"9"])+ > | |
| | < FLOATING_POINT_LITERAL: | |
| (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? | |
| | "." (["0"-"9"])+ (<EXPONENT>)? | |
| | (["0"-"9"])+ (<EXPONENT>)? | |
| > | |
| | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > | |
| | < SINGLE_STRING_LITERAL: "'" (~["'"])* ( "''" (~["'"])* )* "'" > | |
| | < DOUBLE_STRING_LITERAL: "\"" (~["\""])* ( "\"\"" (~["\""])* )* "\"" > | |
| | < BINARY_LITERAL: "0" ("x" | "X" ) ( <HEXDIGIT> )+ > | |
| | < #HEXDIGIT: ["A"-"F", "a"-"f", "0"-"9"] > | |
| | < MONEY_LITERAL: ("$" | "\u00a5" | "\u00a3" )? <FLOATING_POINT_LITERAL> > | |
| } | |
| Token string_literal() : {Token t;} | |
| { | |
| ( t = <SINGLE_STRING_LITERAL> | t = <DOUBLE_STRING_LITERAL> ) | |
| {return t;} | |
| } | |
| TOKEN: /* Identifiers */ | |
| { | |
| < ID: ( <LETTER> | "_" )+ ( <SYMBOL> | <DIGIT> | <LETTER> | "#" | "@" )* > | |
| | < SQUARE_BRACKET_ID: ("[" <ID> "]")> | |
| | < #VAR_NAME_BODY: ( <SYMBOL> | <DIGIT> | <LETTER> | "#" )+ ( <SYMBOL> | <DIGIT> | <LETTER> | "#" | "@" )* > | |
| | < VAR_NAME: "@"<VAR_NAME_BODY> > | |
| | < LABEL: <ID>":" > | |
| | < GLOBAL_VAR_NAME: "@@"<VAR_NAME_BODY> > | |
| | < TEMP_TABLE_NAME: "#" ( <SYMBOL> | <DIGIT> | <LETTER> | "#" | "@" )+ > | |
| /*FIXME: Unicode code point ranges from 0000 to 10ffff, but JavaCC seems only recognize four digits following "u". Not sure what the consequence will be for now.*/ | |
| | < #LETTER: ["A"-"Z", "a"-"z", "\u0080"-"\uffff"] > | |
| | < #DIGIT: ["0"-"9"] > | |
| | < #SYMBOL: ["$" , "\u00a5" , "\u00a3" , "_" ] > | |
| } | |
| TOKEN: /* Separators and operators */ | |
| { | |
| < CONCAT: "||" > | |
| | < COMMA: "," > | |
| | < SEMICOLON: ";" > | |
| | < DOT: "." > | |
| | < ROWTYPE: "%rowtype" > | |
| | < TILDE: "~" > | |
| | < LESS: "<" > | |
| | < LESSEQUAL: "<=" > | |
| | < GREATER: ">" > | |
| | < GREATEREQUAL: ">=" > | |
| | < EQUAL: "=" > | |
| | < NOTEQUAL: "!=" > | |
| | < JOINPLUS: "(+)" > | |
| | < OPENPAREN: "(" > | |
| | < CLOSEPAREN: ")" > | |
| | < ASTERISK: "*" > | |
| | < SLASH: "/" > | |
| | < PLUS: "+" > | |
| | < MINUS: "-" > | |
| | < QUESTIONMARK: "?" > | |
| | < LEQJOIN: "*="> //FIXME: | |
| | < REQJOIN: "=*"> | |
| | < JAVA_REF: ">>"> | |
| } | |
| /******************************************************************* | |
| * The SQL grammar starts here | |
| *******************************************************************/ | |
| /***** ROOT SYNTAXS *****/ | |
| void any_stmt_token():{} | |
| { | |
| { | |
| error_skiptobefore(new int[]{END, SEMICOLON, GO}, STMT_START); | |
| } | |
| } | |
| Token id() : {Token t = null;} | |
| { | |
| (t = idplus() | |
| | t = variable() | |
| ) | |
| {return t;} | |
| } | |
| void string() : {} | |
| { | |
| string_literal() | |
| | variable() | |
| } | |
| Token id_string() : {Token t = null;} | |
| { | |
| ( t = idplus() | |
| | t = <DOUBLE_STRING_LITERAL> | |
| | t = variable() | |
| ) | |
| {return t;} | |
| } | |
| Token id_or_string() : {Token t;} | |
| { | |
| (LOOKAHEAD(1) t = idplus() | |
| | t = string_literal() | |
| ) | |
| {return t;} | |
| } | |
| String prefix() : {Token t = null;} | |
| { | |
| ( "." | |
| | (t = id_or_string() ) "." | |
| ) | |
| {return t == null?".":t.image + ".";} | |
| } | |
| String prefix_list() : {String retval = "", pre = ""; } | |
| { | |
| ( LOOKAHEAD([id_or_string() ] ".") pre = prefix() {retval += pre;} )+ | |
| {return retval;} | |
| } | |
| //database object such as: table, procedure, view, cursor... | |
| String object() : {String retval = ""; Token t;} | |
| { | |
| ([ LOOKAHEAD([id_or_string()] ".") retval = prefix_list() ] t = id_or_string()) | |
| {return retval + t.image;} | |
| } | |
| //Special case: "NEW" can be used as id | |
| Token idplus() : {Token t;} | |
| { | |
| ( t = <ID> | |
| | t = <TEMP_TABLE_NAME> //TEMP_TABLE_NAME can also be used as column name | |
| | t = <SQUARE_BRACKET_ID> | |
| ) | |
| {return t;} | |
| } | |
| //variable reference | |
| Token variable() : {Token t;} | |
| { | |
| ( t = <VAR_NAME> | |
| | t = dyn_question_mark() | |
| | t = <GLOBAL_VAR_NAME> | |
| ) | |
| {return t;} | |
| } | |
| Token dyn_question_mark() : {Token t;} | |
| { | |
| t = <QUESTIONMARK> | |
| {return t;} | |
| } | |
| int number() : {int retval = 0;} | |
| { | |
| ( "-" <INTEGER_LITERAL> {try {retval = Integer.parseInt("-" + getToken(0).image);}catch(Exception e){}} | |
| | [ "+" ] <INTEGER_LITERAL> {try {retval = Integer.parseInt(getToken(0).image);}catch(Exception e){}} | |
| ) | |
| {return retval;} | |
| } | |
| void constant() : {} | |
| { | |
| signed_const() | |
| | unsigned_const() | |
| } | |
| Token signed_const() : {Token t;} | |
| { | |
| ( t = <INTEGER_LITERAL> | |
| | t = <FLOATING_POINT_LITERAL> | |
| | t = <MONEY_LITERAL> | |
| ) | |
| {return t;} | |
| } | |
| Token unsigned_const() : {Token t;} | |
| { | |
| ( t = <BINARY_LITERAL> | |
| | t = string_literal() | |
| | t = null_stmt() | |
| | t = variable() | |
| ) | |
| {return t;} | |
| } | |
| String literal() : {Token t; boolean negative = false;} | |
| { | |
| ( [ "+" | "-" { negative = true;} ] t = signed_const() | |
| | t = unsigned_const() | |
| | t = idplus() | |
| | t = <PRIMARY> | |
| | t = <FOREIGN> | |
| ) | |
| {return negative?"-"+t.image:t.image;} | |
| } | |
| Token null_stmt() : {Token t;} | |
| { | |
| t = <NULL> | |
| {return t;} | |
| } | |
| String length_spec() : {String retval = ""; int n = 0;} | |
| { | |
| [ LOOKAHEAD(2) "(" n = number() {retval = "(" + n;} [ "," n = number() {retval += "," + n;} ] ")" {retval += ")";} ] | |
| {return retval;} | |
| } | |
| String datatype():{String retval = ""; Token t; String l="";} | |
| { | |
| ((t = <ID> {if (retval.equals("")) retval += t.image; else retval += " " + t.image;})+ | t = <SQUARE_BRACKET_ID> {retval += t.image;} | t = <DOUBLE_STRING_LITERAL> {retval += t.image;}) l = length_spec() {retval += l;} | |
| {return retval;} | |
| } | |
| String[] datatype_info():{String name = null; Token t; String precision= null; String scale = null; int n = 0;} | |
| { | |
| ((t = <ID> {if (name == null) name = t.image; else name += " " + t.image;})+ | t = <SQUARE_BRACKET_ID> {name = t.image;} | t = <DOUBLE_STRING_LITERAL> { name = t.image;}) | |
| [ LOOKAHEAD(2) "(" n = number() {precision = "" + n;} [ "," n = number() {scale = "" + n;} ] ")" ] | |
| { | |
| if (scale != null) | |
| { | |
| return new String[]{name, precision, scale}; | |
| } | |
| else if (precision != null) | |
| { | |
| return new String[]{name, precision}; | |
| } | |
| else | |
| { | |
| return new String[]{name}; | |
| } | |
| } | |
| } | |
| /** | |
| * @return name: defaultValue | |
| */ | |
| String[] parameter(): {Token name=null; String defaultValue=null; String type=null; int direction = 0;} | |
| { | |
| try{ | |
| //As long as the param name starts with @, we regard it as a tsql parameter | |
| LOOKAHEAD(1) ( name = <VAR_NAME> | |
| type = datatype() | |
| [ "=" defaultValue = procparmdefault()] | |
| direction = param_options() | |
| )|( | |
| ({direction = 0;} [<IN> | {direction = 1;} <OUT> | {direction = 2;} <INOUT> ] ) | |
| name = id_string() | |
| type = datatype() | |
| [<DEFAULT_VAL> defaultValue = procparmdefault()] | |
| ) | |
| | (name = <SQLSTATE> ) | |
| |( name = <SQLCODE> ) | |
| }catch(ParseException e){ | |
| error_skiptobefore(new int[]{}, new int[]{COMMA, CLOSEPAREN, WITH, AS, BEGIN}); | |
| } | |
| { | |
| String[] param = new String[2]; | |
| param[0] = name.toString(); | |
| param[1] = defaultValue; | |
| return param; | |
| } | |
| } | |
| String optional_param_default() : {String t = null;} | |
| { | |
| [ "=" t = literal()] | |
| {return t;} | |
| } | |
| int param_options() : {int direction = 0;} | |
| { | |
| [ <IN> | |
| | out_option() {direction = 1;}] | |
| {return direction;} | |
| } | |
| void out_option() : {} | |
| { | |
| <OUT> | |
| | <OUTPUT> | |
| } | |
| void any_chars():{} | |
| { | |
| {token_source.SwitchTo(IGNORE_STATE);} | |
| } | |
| void create_proc_header():{} | |
| { | |
| <CREATE> (<PROC> | <PROCEDURE> ) any_chars() | |
| } | |
| void create_func_header():{} | |
| { | |
| <CREATE> (<FUNC> | <FUNCTION> ) any_chars() | |
| } | |
| void create_trigger_header():{} | |
| { | |
| <CREATE> <TRIGGER> any_chars() | |
| } | |
| void create_event_header():{} | |
| { | |
| <CREATE> <EVENT> any_chars() | |
| } | |
| String[][] parameters():{ArrayList params = new ArrayList(); String[] param = new String[2];} | |
| { | |
| [ ["("] param = parameter() {params.add(param);} ( <COMMA> param = parameter() {params.add(param);} )* [")"]] | |
| {return (String[][])params.toArray(new String[params.size()][2]);} | |
| } | |
| String[][] routine_parameters():{String[][] params = null;} | |
| { | |
| ( <CREATE> | <ALTER> ) (<PROC> | <PROCEDURE> | <FUNC> | <FUNCTION> ) object() params = parameters() any_chars() | |
| {return params;} | |
| } | |
| //might contain expressions | |
| String procparmdefault() : {} | |
| { | |
| { | |
| Token start = getToken(1);//next token | |
| int startIndex = getStartIndex(start); | |
| boolean match = false; | |
| do{ | |
| int balance = 0;//open parenthesis minus close parenthesis | |
| error_skiptobefore(new int[]{OPENPAREN}, new int[]{COMMA, CLOSEPAREN, OUTPUT, OUT, IN, WITH, AS}); | |
| if (getToken(0).kind == OPENPAREN ) | |
| { | |
| balance ++; | |
| } | |
| if (getToken(1).kind == CLOSEPAREN ) | |
| { | |
| balance --; | |
| } | |
| Token end = getToken(1); | |
| Token next = getToken(2); | |
| if (end == null || next == null) | |
| { | |
| match = true; | |
| break; | |
| } | |
| else if (end.kind == EOF || end.kind == COMMA) | |
| { | |
| match = true; | |
| break; | |
| } | |
| else if (end.kind == COMMA && balance == 0) | |
| { | |
| match = true; | |
| break; | |
| } | |
| else if (end.kind == WITH && balance == 0) | |
| { | |
| match = true; | |
| break; | |
| } | |
| else if (end.kind == AS && balance == 0)//TSQL | |
| { | |
| match = true; | |
| break; | |
| } | |
| else if (end.kind == OUTPUT || end.kind == OUT || end.kind == IN )//TSQL: IN/OUT follows the default value | |
| { | |
| match = true; | |
| break; | |
| } | |
| else if (end.kind == CLOSEPAREN && balance < 0) | |
| { | |
| for (int i=0;i<WATCOM_POST_PARAM.length; i++) | |
| { | |
| if (WATCOM_POST_PARAM[i].equalsIgnoreCase(next.image)) | |
| { | |
| match = true; | |
| break; | |
| } | |
| } | |
| } | |
| }while (!match); | |
| int endIndex = getEndIndex(getToken(0)); | |
| if (endIndex > startIndex) | |
| { | |
| return _input.substring(startIndex, endIndex); | |
| } | |
| else | |
| { | |
| return null; | |
| } | |
| } | |
| } | |
| JAVACODE | |
| void error_skiptobefore(int[] tokinds, int[] beforekinds) { | |
| boolean match = false; | |
| Token t1 = getToken(0); | |
| // The following loop consumes tokens all the way up to a token of | |
| // "kind". We use a do-while loop rather than a while because the | |
| // current token is the one immediately before the erroneous token | |
| // (in our case the token immediately before what should have been | |
| // "if"/"while". | |
| do { | |
| match = token.kind == 0 || getToken(1).kind == 0 ; // 0 means the <EOF> | |
| if (match) {break;} | |
| for (int i=0; i< tokinds.length; i++){ | |
| match = match || token.kind == tokinds[i]; | |
| if (match) {break;} | |
| } | |
| if (match) {break;} | |
| for (int i=0; i< beforekinds.length; i++){ | |
| match = match || getToken(1).kind == beforekinds[i]; | |
| if (match) {break;} | |
| } | |
| if (!match){ | |
| logDebug("current token:" + token.image); | |
| getNextToken(); | |
| } | |
| } while (!match); | |
| Token t2 = getToken(0); | |
| if (t1 == t2 ){ | |
| //force get next token | |
| logDebug("current token:" + token.image); | |
| //System.out.println("current token:" + token.image); | |
| getNextToken(); | |
| } | |
| } | |
| //overloaded with String[] parameters | |
| JAVACODE | |
| void error_skiptobefore1(String[] tokinds, String[] beforekinds, boolean force) { | |
| boolean match = false; | |
| Token t1 = getToken(0); | |
| // The following loop consumes tokens all the way up to a token of | |
| // "kind". We use a do-while loop rather than a while because the | |
| // current token is the one immediately before the erroneous token | |
| // (in our case the token immediately before what should have been | |
| // "if"/"while". | |
| do { | |
| match = token.kind == 0 || getToken(1).kind == 0 ; // 0 means the <EOF> | |
| if (match) {break;} | |
| for (int i=0; i< tokinds.length; i++){ | |
| match = match || tokinds[i].equalsIgnoreCase(token.image); | |
| if (match) {break;} | |
| } | |
| if (match) {break;} | |
| for (int i=0; i< beforekinds.length; i++){ | |
| match = match || beforekinds[i].equalsIgnoreCase(getToken(1).image); | |
| if (match) {break;} | |
| } | |
| if (!match){ | |
| logDebug("current token:" + token.image); | |
| getNextToken(); | |
| } | |
| } while (!match); | |
| Token t2 = getToken(0); | |
| if (t1 == t2 && force ){ | |
| //force get next token | |
| logDebug("current token:" + token.image); | |
| //System.out.println("current token:" + token.image); | |
| getNextToken(); | |
| } | |
| } | |
| /*TODO: | |
| <ID> need to be refined | |
| Multi-word tokens should be defined seperatedly | |
| Highlight udt? Need to change types definition in ISQLSyntax referenced by SourceViewerConfiguration | |
| setScope must be placed early enough because Lookahead > 1 will prevent it from executing, | |
| We can only set scope to the last element. e.g. in case of db.owner.table, we'll set scope to table | |
| error_skiptobefore has potential possibility of deadloop | |
| Higher lookahead should proceed lower lookahead | |
| When scope is current_table, we need to store current table name in some place. | |
| can't show popup when user has input part of the <ID>, need to | |
| */ | |