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