1 // Scintilla source code edit control
2 /** @file LexMSSQL.cxx
3  ** Lexer for MSSQL.
4  **/
5 // By Filip Yaghob <fyaghob@gmail.com>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
14 
15 #include "ILexer.h"
16 #include "Scintilla.h"
17 #include "SciLexer.h"
18 
19 #include "WordList.h"
20 #include "LexAccessor.h"
21 #include "Accessor.h"
22 #include "StyleContext.h"
23 #include "CharacterSet.h"
24 #include "LexerModule.h"
25 
26 using namespace Scintilla;
27 
28 #define KW_MSSQL_STATEMENTS         0
29 #define KW_MSSQL_DATA_TYPES         1
30 #define KW_MSSQL_SYSTEM_TABLES      2
31 #define KW_MSSQL_GLOBAL_VARIABLES   3
32 #define KW_MSSQL_FUNCTIONS          4
33 #define KW_MSSQL_STORED_PROCEDURES  5
34 #define KW_MSSQL_OPERATORS          6
35 
classifyWordSQL(Sci_PositionU start,Sci_PositionU end,WordList * keywordlists[],Accessor & styler,unsigned int actualState,unsigned int prevState)36 static char classifyWordSQL(Sci_PositionU start,
37                             Sci_PositionU end,
38                             WordList *keywordlists[],
39                             Accessor &styler,
40                             unsigned int actualState,
41 							unsigned int prevState) {
42 	char s[256];
43 	bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.');
44 
45 	WordList &kwStatements          = *keywordlists[KW_MSSQL_STATEMENTS];
46     WordList &kwDataTypes           = *keywordlists[KW_MSSQL_DATA_TYPES];
47     WordList &kwSystemTables        = *keywordlists[KW_MSSQL_SYSTEM_TABLES];
48     WordList &kwGlobalVariables     = *keywordlists[KW_MSSQL_GLOBAL_VARIABLES];
49     WordList &kwFunctions           = *keywordlists[KW_MSSQL_FUNCTIONS];
50     WordList &kwStoredProcedures    = *keywordlists[KW_MSSQL_STORED_PROCEDURES];
51     WordList &kwOperators           = *keywordlists[KW_MSSQL_OPERATORS];
52 
53 	for (Sci_PositionU i = 0; i < end - start + 1 && i < 128; i++) {
54 		s[i] = static_cast<char>(tolower(styler[start + i]));
55 		s[i + 1] = '\0';
56 	}
57 	char chAttr = SCE_MSSQL_IDENTIFIER;
58 
59 	if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) {
60 
61         if (kwGlobalVariables.InList(&s[2]))
62             chAttr = SCE_MSSQL_GLOBAL_VARIABLE;
63 
64 	} else if (wordIsNumber) {
65 		chAttr = SCE_MSSQL_NUMBER;
66 
67 	} else if (prevState == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
68 		// Look first in datatypes
69         if (kwDataTypes.InList(s))
70             chAttr = SCE_MSSQL_DATATYPE;
71 		else if (kwOperators.InList(s))
72 			chAttr = SCE_MSSQL_OPERATOR;
73 		else if (kwStatements.InList(s))
74 			chAttr = SCE_MSSQL_STATEMENT;
75 		else if (kwSystemTables.InList(s))
76 			chAttr = SCE_MSSQL_SYSTABLE;
77 		else if (kwFunctions.InList(s))
78             chAttr = SCE_MSSQL_FUNCTION;
79 		else if (kwStoredProcedures.InList(s))
80 			chAttr = SCE_MSSQL_STORED_PROCEDURE;
81 
82 	} else {
83 		if (kwOperators.InList(s))
84 			chAttr = SCE_MSSQL_OPERATOR;
85 		else if (kwStatements.InList(s))
86 			chAttr = SCE_MSSQL_STATEMENT;
87 		else if (kwSystemTables.InList(s))
88 			chAttr = SCE_MSSQL_SYSTABLE;
89 		else if (kwFunctions.InList(s))
90 			chAttr = SCE_MSSQL_FUNCTION;
91 		else if (kwStoredProcedures.InList(s))
92 			chAttr = SCE_MSSQL_STORED_PROCEDURE;
93 		else if (kwDataTypes.InList(s))
94 			chAttr = SCE_MSSQL_DATATYPE;
95 	}
96 
97 	styler.ColourTo(end, chAttr);
98 
99 	return chAttr;
100 }
101 
ColouriseMSSQLDoc(Sci_PositionU startPos,Sci_Position length,int initStyle,WordList * keywordlists[],Accessor & styler)102 static void ColouriseMSSQLDoc(Sci_PositionU startPos, Sci_Position length,
103                               int initStyle, WordList *keywordlists[], Accessor &styler) {
104 
105 
106 	styler.StartAt(startPos);
107 
108 	bool fold = styler.GetPropertyInt("fold") != 0;
109 	Sci_Position lineCurrent = styler.GetLine(startPos);
110 	int spaceFlags = 0;
111 
112 	int state = initStyle;
113 	int prevState = initStyle;
114 	char chPrev = ' ';
115 	char chNext = styler[startPos];
116 	styler.StartSegment(startPos);
117 	Sci_PositionU lengthDoc = startPos + length;
118 	for (Sci_PositionU i = startPos; i < lengthDoc; i++) {
119 		char ch = chNext;
120 		chNext = styler.SafeGetCharAt(i + 1);
121 
122 		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
123 			int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags);
124 			int lev = indentCurrent;
125 			if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
126 				// Only non whitespace lines can be headers
127 				int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags);
128 				if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) {
129 					lev |= SC_FOLDLEVELHEADERFLAG;
130 				}
131 			}
132 			if (fold) {
133 				styler.SetLevel(lineCurrent, lev);
134 			}
135 		}
136 
137 		if (styler.IsLeadByte(ch)) {
138 			chNext = styler.SafeGetCharAt(i + 2);
139 			chPrev = ' ';
140 			i += 1;
141 			continue;
142 		}
143 
144 		// When the last char isn't part of the state (have to deal with it too)...
145 		if ( (state == SCE_MSSQL_IDENTIFIER) ||
146                     (state == SCE_MSSQL_STORED_PROCEDURE) ||
147                     (state == SCE_MSSQL_DATATYPE) ||
148                     //~ (state == SCE_MSSQL_COLUMN_NAME) ||
149                     (state == SCE_MSSQL_FUNCTION) ||
150                     //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) ||
151                     (state == SCE_MSSQL_VARIABLE)) {
152 			if (!iswordchar(ch)) {
153 				int stateTmp;
154 
155                 if ((state == SCE_MSSQL_VARIABLE) || (state == SCE_MSSQL_COLUMN_NAME)) {
156                     styler.ColourTo(i - 1, state);
157 					stateTmp = state;
158                 } else
159                     stateTmp = classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
160 
161 				prevState = state;
162 
163 				if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE)
164 					state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
165 				else
166 					state = SCE_MSSQL_DEFAULT;
167 			}
168 		} else if (state == SCE_MSSQL_LINE_COMMENT) {
169 			if (ch == '\r' || ch == '\n') {
170 				styler.ColourTo(i - 1, state);
171 				prevState = state;
172 				state = SCE_MSSQL_DEFAULT;
173 			}
174 		} else if (state == SCE_MSSQL_GLOBAL_VARIABLE) {
175 			if ((ch != '@') && !iswordchar(ch)) {
176 				classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
177 				prevState = state;
178 				state = SCE_MSSQL_DEFAULT;
179 			}
180 		}
181 
182 		// If is the default or one of the above succeeded
183 		if (state == SCE_MSSQL_DEFAULT || state == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
184 			if (iswordstart(ch)) {
185 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
186 				prevState = state;
187 				state = SCE_MSSQL_IDENTIFIER;
188 			} else if (ch == '/' && chNext == '*') {
189 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
190 				prevState = state;
191 				state = SCE_MSSQL_COMMENT;
192 			} else if (ch == '-' && chNext == '-') {
193 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
194 				prevState = state;
195 				state = SCE_MSSQL_LINE_COMMENT;
196 			} else if (ch == '\'') {
197 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
198 				prevState = state;
199 				state = SCE_MSSQL_STRING;
200 			} else if (ch == '"') {
201 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
202 				prevState = state;
203 				state = SCE_MSSQL_COLUMN_NAME;
204 			} else if (ch == '[') {
205 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
206 				prevState = state;
207 				state = SCE_MSSQL_COLUMN_NAME_2;
208 			} else if (isoperator(ch)) {
209 				styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
210 				styler.ColourTo(i, SCE_MSSQL_OPERATOR);
211                 //~ style = SCE_MSSQL_DEFAULT;
212 				prevState = state;
213 				state = SCE_MSSQL_DEFAULT;
214 			} else if (ch == '@') {
215                 styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
216 				prevState = state;
217                 if (chNext == '@') {
218                     state = SCE_MSSQL_GLOBAL_VARIABLE;
219 //                    i += 2;
220                 } else
221                     state = SCE_MSSQL_VARIABLE;
222             }
223 
224 
225 		// When the last char is part of the state...
226 		} else if (state == SCE_MSSQL_COMMENT) {
227 				if (ch == '/' && chPrev == '*') {
228 					if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_MSSQL_COMMENT) &&
229 					    (styler.GetStartSegment() == startPos)))) {
230 						styler.ColourTo(i, state);
231 						//~ state = SCE_MSSQL_COMMENT;
232 					prevState = state;
233                         state = SCE_MSSQL_DEFAULT;
234 					}
235 				}
236 			} else if (state == SCE_MSSQL_STRING) {
237 				if (ch == '\'') {
238 					if ( chNext == '\'' ) {
239 						i++;
240 					ch = chNext;
241 					chNext = styler.SafeGetCharAt(i + 1);
242 					} else {
243 						styler.ColourTo(i, state);
244 					prevState = state;
245 						state = SCE_MSSQL_DEFAULT;
246 					//i++;
247 					}
248 				//ch = chNext;
249 				//chNext = styler.SafeGetCharAt(i + 1);
250 				}
251 			} else if (state == SCE_MSSQL_COLUMN_NAME) {
252 				if (ch == '"') {
253 					if (chNext == '"') {
254 						i++;
255 					ch = chNext;
256 					chNext = styler.SafeGetCharAt(i + 1);
257 				} else {
258                     styler.ColourTo(i, state);
259 					prevState = state;
260 					state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
261 					//i++;
262                 }
263                 }
264 		} else if (state == SCE_MSSQL_COLUMN_NAME_2) {
265 			if (ch == ']') {
266                 styler.ColourTo(i, state);
267 				prevState = state;
268                 state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
269                 //i++;
270 			}
271 		}
272 
273 		chPrev = ch;
274 	}
275 	styler.ColourTo(lengthDoc - 1, state);
276 }
277 
FoldMSSQLDoc(Sci_PositionU startPos,Sci_Position length,int,WordList * [],Accessor & styler)278 static void FoldMSSQLDoc(Sci_PositionU startPos, Sci_Position length, int, WordList *[], Accessor &styler) {
279 	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
280 	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
281 	Sci_PositionU endPos = startPos + length;
282 	int visibleChars = 0;
283 	Sci_Position lineCurrent = styler.GetLine(startPos);
284 	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
285 	int levelCurrent = levelPrev;
286 	char chNext = styler[startPos];
287 	bool inComment = (styler.StyleAt(startPos-1) == SCE_MSSQL_COMMENT);
288     char s[10] = "";
289 	for (Sci_PositionU i = startPos; i < endPos; i++) {
290 		char ch = chNext;
291 		chNext = styler.SafeGetCharAt(i + 1);
292 		int style = styler.StyleAt(i);
293 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
294         // Comment folding
295 		if (foldComment) {
296 			if (!inComment && (style == SCE_MSSQL_COMMENT))
297 				levelCurrent++;
298 			else if (inComment && (style != SCE_MSSQL_COMMENT))
299 				levelCurrent--;
300 			inComment = (style == SCE_MSSQL_COMMENT);
301 		}
302         if (style == SCE_MSSQL_STATEMENT) {
303             // Folding between begin or case and end
304             if (ch == 'b' || ch == 'B' || ch == 'c' || ch == 'C' || ch == 'e' || ch == 'E') {
305                 for (Sci_PositionU j = 0; j < 5; j++) {
306 					if (!iswordchar(styler[i + j])) {
307 						break;
308 					}
309 					s[j] = static_cast<char>(tolower(styler[i + j]));
310 					s[j + 1] = '\0';
311                 }
312 				if ((strcmp(s, "begin") == 0) || (strcmp(s, "case") == 0)) {
313 					levelCurrent++;
314 				}
315 				if (strcmp(s, "end") == 0) {
316 					levelCurrent--;
317 				}
318             }
319         }
320 		if (atEOL) {
321 			int lev = levelPrev;
322 			if (visibleChars == 0 && foldCompact)
323 				lev |= SC_FOLDLEVELWHITEFLAG;
324 			if ((levelCurrent > levelPrev) && (visibleChars > 0))
325 				lev |= SC_FOLDLEVELHEADERFLAG;
326 			if (lev != styler.LevelAt(lineCurrent)) {
327 				styler.SetLevel(lineCurrent, lev);
328 			}
329 			lineCurrent++;
330 			levelPrev = levelCurrent;
331 			visibleChars = 0;
332 		}
333 		if (!isspacechar(ch))
334 			visibleChars++;
335 	}
336 	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
337 	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
338 	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
339 }
340 
341 static const char * const sqlWordListDesc[] = {
342 	"Statements",
343     "Data Types",
344     "System tables",
345     "Global variables",
346     "Functions",
347     "System Stored Procedures",
348     "Operators",
349 	0,
350 };
351 
352 LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql", FoldMSSQLDoc, sqlWordListDesc);
353