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