1 | // Scintilla source code edit control |
2 | /** @file LexSTTXT.cxx |
3 | ** Lexer for Structured Text language. |
4 | ** Written by Pavel Bulochkin |
5 | **/ |
6 | // The License.txt file describes the conditions under which this software may be distributed. |
7 | |
8 | |
9 | #include <stdlib.h> |
10 | #include <string.h> |
11 | #include <stdio.h> |
12 | #include <stdarg.h> |
13 | #include <assert.h> |
14 | #include <ctype.h> |
15 | |
16 | #include <string> |
17 | #include <string_view> |
18 | |
19 | #include "ILexer.h" |
20 | #include "Scintilla.h" |
21 | #include "SciLexer.h" |
22 | |
23 | #include "WordList.h" |
24 | #include "LexAccessor.h" |
25 | #include "Accessor.h" |
26 | #include "StyleContext.h" |
27 | #include "CharacterSet.h" |
28 | #include "LexerModule.h" |
29 | |
30 | using namespace Lexilla; |
31 | |
32 | static void ClassifySTTXTWord(WordList *keywordlists[], StyleContext &sc) |
33 | { |
34 | char s[256] = { 0 }; |
35 | sc.GetCurrentLowered(s, sizeof(s)); |
36 | |
37 | if ((*keywordlists[0]).InList(s)) { |
38 | sc.ChangeState(SCE_STTXT_KEYWORD); |
39 | } |
40 | |
41 | else if ((*keywordlists[1]).InList(s)) { |
42 | sc.ChangeState(SCE_STTXT_TYPE); |
43 | } |
44 | |
45 | else if ((*keywordlists[2]).InList(s)) { |
46 | sc.ChangeState(SCE_STTXT_FUNCTION); |
47 | } |
48 | |
49 | else if ((*keywordlists[3]).InList(s)) { |
50 | sc.ChangeState(SCE_STTXT_FB); |
51 | } |
52 | |
53 | else if ((*keywordlists[4]).InList(s)) { |
54 | sc.ChangeState(SCE_STTXT_VARS); |
55 | } |
56 | |
57 | else if ((*keywordlists[5]).InList(s)) { |
58 | sc.ChangeState(SCE_STTXT_PRAGMAS); |
59 | } |
60 | |
61 | sc.SetState(SCE_STTXT_DEFAULT); |
62 | } |
63 | |
64 | static void ColouriseSTTXTDoc (Sci_PositionU startPos, Sci_Position length, int initStyle, |
65 | WordList *keywordlists[], Accessor &styler) |
66 | { |
67 | StyleContext sc(startPos, length, initStyle, styler); |
68 | |
69 | CharacterSet setWord(CharacterSet::setAlphaNum, "_" , 0x80, true); |
70 | CharacterSet setWordStart(CharacterSet::setAlpha, "_" , 0x80, true); |
71 | CharacterSet setNumber(CharacterSet::setDigits, "_.eE" ); |
72 | CharacterSet setHexNumber(CharacterSet::setDigits, "_abcdefABCDEF" ); |
73 | CharacterSet setOperator(CharacterSet::setNone,",.+-*/:;<=>[]()%&" ); |
74 | CharacterSet setDataTime(CharacterSet::setDigits,"_.-:dmshDMSH" ); |
75 | |
76 | for ( ; sc.More() ; sc.Forward()) |
77 | { |
78 | if(sc.atLineStart && sc.state != SCE_STTXT_COMMENT) |
79 | sc.SetState(SCE_STTXT_DEFAULT); |
80 | |
81 | switch(sc.state) |
82 | { |
83 | case SCE_STTXT_NUMBER: { |
84 | if(!setNumber.Contains(sc.ch)) |
85 | sc.SetState(SCE_STTXT_DEFAULT); |
86 | break; |
87 | } |
88 | case SCE_STTXT_HEXNUMBER: { |
89 | if (setHexNumber.Contains(sc.ch)) |
90 | continue; |
91 | else if(setDataTime.Contains(sc.ch)) |
92 | sc.ChangeState(SCE_STTXT_DATETIME); |
93 | else if(setWord.Contains(sc.ch)) |
94 | sc.ChangeState(SCE_STTXT_DEFAULT); |
95 | else |
96 | sc.SetState(SCE_STTXT_DEFAULT); |
97 | break; |
98 | } |
99 | case SCE_STTXT_DATETIME: { |
100 | if (setDataTime.Contains(sc.ch)) |
101 | continue; |
102 | else if(setWord.Contains(sc.ch)) |
103 | sc.ChangeState(SCE_STTXT_DEFAULT); |
104 | else |
105 | sc.SetState(SCE_STTXT_DEFAULT); |
106 | break; |
107 | } |
108 | case SCE_STTXT_OPERATOR: { |
109 | sc.SetState(SCE_STTXT_DEFAULT); |
110 | break; |
111 | } |
112 | case SCE_STTXT_PRAGMA: { |
113 | if (sc.ch == '}') |
114 | sc.ForwardSetState(SCE_STTXT_DEFAULT); |
115 | break; |
116 | } |
117 | case SCE_STTXT_COMMENTLINE: { |
118 | if (sc.atLineStart) |
119 | sc.SetState(SCE_STTXT_DEFAULT); |
120 | break; |
121 | } |
122 | case SCE_STTXT_COMMENT: { |
123 | if(sc.Match('*',')')) |
124 | { |
125 | sc.Forward(); |
126 | sc.ForwardSetState(SCE_STTXT_DEFAULT); |
127 | } |
128 | break; |
129 | } |
130 | case SCE_STTXT_STRING1: { |
131 | if(sc.atLineEnd) |
132 | sc.SetState(SCE_STTXT_STRINGEOL); |
133 | else if(sc.ch == '\'' && sc.chPrev != '$') |
134 | sc.ForwardSetState(SCE_STTXT_DEFAULT); |
135 | break; |
136 | } |
137 | case SCE_STTXT_STRING2: { |
138 | if (sc.atLineEnd) |
139 | sc.SetState(SCE_STTXT_STRINGEOL); |
140 | else if(sc.ch == '\"' && sc.chPrev != '$') |
141 | sc.ForwardSetState(SCE_STTXT_DEFAULT); |
142 | break; |
143 | } |
144 | case SCE_STTXT_STRINGEOL: { |
145 | if(sc.atLineStart) |
146 | sc.SetState(SCE_STTXT_DEFAULT); |
147 | break; |
148 | } |
149 | case SCE_STTXT_CHARACTER: { |
150 | if(setHexNumber.Contains(sc.ch)) |
151 | sc.SetState(SCE_STTXT_HEXNUMBER); |
152 | else if(setDataTime.Contains(sc.ch)) |
153 | sc.SetState(SCE_STTXT_DATETIME); |
154 | else sc.SetState(SCE_STTXT_DEFAULT); |
155 | break; |
156 | } |
157 | case SCE_STTXT_IDENTIFIER: { |
158 | if(!setWord.Contains(sc.ch)) |
159 | ClassifySTTXTWord(keywordlists, sc); |
160 | break; |
161 | } |
162 | } |
163 | |
164 | if(sc.state == SCE_STTXT_DEFAULT) |
165 | { |
166 | if(IsADigit(sc.ch)) |
167 | sc.SetState(SCE_STTXT_NUMBER); |
168 | else if (setWordStart.Contains(sc.ch)) |
169 | sc.SetState(SCE_STTXT_IDENTIFIER); |
170 | else if (sc.Match('/', '/')) |
171 | sc.SetState(SCE_STTXT_COMMENTLINE); |
172 | else if(sc.Match('(', '*')) |
173 | sc.SetState(SCE_STTXT_COMMENT); |
174 | else if (sc.ch == '{') |
175 | sc.SetState(SCE_STTXT_PRAGMA); |
176 | else if (sc.ch == '\'') |
177 | sc.SetState(SCE_STTXT_STRING1); |
178 | else if (sc.ch == '\"') |
179 | sc.SetState(SCE_STTXT_STRING2); |
180 | else if(sc.ch == '#') |
181 | sc.SetState(SCE_STTXT_CHARACTER); |
182 | else if (setOperator.Contains(sc.ch)) |
183 | sc.SetState(SCE_STTXT_OPERATOR); |
184 | } |
185 | } |
186 | |
187 | if (sc.state == SCE_STTXT_IDENTIFIER && setWord.Contains(sc.chPrev)) |
188 | ClassifySTTXTWord(keywordlists, sc); |
189 | |
190 | sc.Complete(); |
191 | } |
192 | |
193 | static const char * const STTXTWordListDesc[] = { |
194 | "Keywords" , |
195 | "Types" , |
196 | "Functions" , |
197 | "FB" , |
198 | "Local_Var" , |
199 | "Local_Pragma" , |
200 | 0 |
201 | }; |
202 | |
203 | static bool (Sci_Position line, Accessor &styler, bool type) |
204 | { |
205 | Sci_Position pos = styler.LineStart(line); |
206 | Sci_Position eolPos = styler.LineStart(line + 1) - 1; |
207 | |
208 | for (Sci_Position i = pos; i < eolPos; i++) |
209 | { |
210 | char ch = styler[i]; |
211 | char chNext = styler.SafeGetCharAt(i + 1); |
212 | int style = styler.StyleAt(i); |
213 | |
214 | if(type) { |
215 | if (ch == '/' && chNext == '/' && style == SCE_STTXT_COMMENTLINE) |
216 | return true; |
217 | } |
218 | else if (ch == '(' && chNext == '*' && style == SCE_STTXT_COMMENT) |
219 | break; |
220 | |
221 | if (!IsASpaceOrTab(ch)) |
222 | return false; |
223 | } |
224 | |
225 | for (Sci_Position i = eolPos-2; i>pos; i--) |
226 | { |
227 | char ch = styler[i]; |
228 | char chPrev = styler.SafeGetCharAt(i-1); |
229 | int style = styler.StyleAt(i); |
230 | |
231 | if(ch == ')' && chPrev == '*' && style == SCE_STTXT_COMMENT) |
232 | return true; |
233 | if(!IsASpaceOrTab(ch)) |
234 | return false; |
235 | } |
236 | |
237 | return false; |
238 | } |
239 | |
240 | static bool IsPragmaLine(Sci_Position line, Accessor &styler) |
241 | { |
242 | Sci_Position pos = styler.LineStart(line); |
243 | Sci_Position eolPos = styler.LineStart(line+1) - 1; |
244 | |
245 | for (Sci_Position i = pos ; i < eolPos ; i++) |
246 | { |
247 | char ch = styler[i]; |
248 | int style = styler.StyleAt(i); |
249 | |
250 | if(ch == '{' && style == SCE_STTXT_PRAGMA) |
251 | return true; |
252 | else if (!IsASpaceOrTab(ch)) |
253 | return false; |
254 | } |
255 | return false; |
256 | } |
257 | |
258 | static void GetRangeUpper(Sci_PositionU start,Sci_PositionU end,Accessor &styler,char *s,Sci_PositionU len) |
259 | { |
260 | Sci_PositionU i = 0; |
261 | while ((i < end - start + 1) && (i < len-1)) { |
262 | s[i] = static_cast<char>(toupper(styler[start + i])); |
263 | i++; |
264 | } |
265 | s[i] = '\0'; |
266 | } |
267 | |
268 | static void ClassifySTTXTWordFoldPoint(int &levelCurrent,Sci_PositionU lastStart, |
269 | Sci_PositionU currentPos, Accessor &styler) |
270 | { |
271 | char s[256]; |
272 | GetRangeUpper(lastStart, currentPos, styler, s, sizeof(s)); |
273 | |
274 | // See Table C.2 - Keywords |
275 | if (!strcmp(s, "ACTION" ) || |
276 | !strcmp(s, "CASE" ) || |
277 | !strcmp(s, "CONFIGURATION" ) || |
278 | !strcmp(s, "FOR" ) || |
279 | !strcmp(s, "FUNCTION" ) || |
280 | !strcmp(s, "FUNCTION_BLOCK" ) || |
281 | !strcmp(s, "IF" ) || |
282 | !strcmp(s, "INITIAL_STEP" ) || |
283 | !strcmp(s, "REPEAT" ) || |
284 | !strcmp(s, "RESOURCE" ) || |
285 | !strcmp(s, "STEP" ) || |
286 | !strcmp(s, "STRUCT" ) || |
287 | !strcmp(s, "TRANSITION" ) || |
288 | !strcmp(s, "TYPE" ) || |
289 | !strcmp(s, "VAR" ) || |
290 | !strcmp(s, "VAR_INPUT" ) || |
291 | !strcmp(s, "VAR_OUTPUT" ) || |
292 | !strcmp(s, "VAR_IN_OUT" ) || |
293 | !strcmp(s, "VAR_TEMP" ) || |
294 | !strcmp(s, "VAR_EXTERNAL" ) || |
295 | !strcmp(s, "VAR_ACCESS" ) || |
296 | !strcmp(s, "VAR_CONFIG" ) || |
297 | !strcmp(s, "VAR_GLOBAL" ) || |
298 | !strcmp(s, "WHILE" )) |
299 | { |
300 | levelCurrent++; |
301 | } |
302 | else if (!strcmp(s, "END_ACTION" ) || |
303 | !strcmp(s, "END_CASE" ) || |
304 | !strcmp(s, "END_CONFIGURATION" ) || |
305 | !strcmp(s, "END_FOR" ) || |
306 | !strcmp(s, "END_FUNCTION" ) || |
307 | !strcmp(s, "END_FUNCTION_BLOCK" ) || |
308 | !strcmp(s, "END_IF" ) || |
309 | !strcmp(s, "END_REPEAT" ) || |
310 | !strcmp(s, "END_RESOURCE" ) || |
311 | !strcmp(s, "END_STEP" ) || |
312 | !strcmp(s, "END_STRUCT" ) || |
313 | !strcmp(s, "END_TRANSITION" ) || |
314 | !strcmp(s, "END_TYPE" ) || |
315 | !strcmp(s, "END_VAR" ) || |
316 | !strcmp(s, "END_WHILE" )) |
317 | { |
318 | levelCurrent--; |
319 | if (levelCurrent < SC_FOLDLEVELBASE) { |
320 | levelCurrent = SC_FOLDLEVELBASE; |
321 | } |
322 | } |
323 | } |
324 | |
325 | static void FoldSTTXTDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[],Accessor &styler) |
326 | { |
327 | bool = styler.GetPropertyInt("fold.comment" ) != 0; |
328 | bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor" ) != 0; |
329 | bool foldCompact = styler.GetPropertyInt("fold.compact" , 1) != 0; |
330 | Sci_PositionU endPos = startPos + length; |
331 | int visibleChars = 0; |
332 | Sci_Position lineCurrent = styler.GetLine(startPos); |
333 | int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; |
334 | int levelCurrent = levelPrev; |
335 | char chNext = styler[startPos]; |
336 | int styleNext = styler.StyleAt(startPos); |
337 | int style = initStyle; |
338 | Sci_Position lastStart = 0; |
339 | |
340 | CharacterSet setWord(CharacterSet::setAlphaNum, "_" , 0x80, true); |
341 | |
342 | for (Sci_PositionU i = startPos; i < endPos; i++) |
343 | { |
344 | char ch = chNext; |
345 | chNext = styler.SafeGetCharAt(i + 1); |
346 | int stylePrev = style; |
347 | style = styleNext; |
348 | styleNext = styler.StyleAt(i + 1); |
349 | bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
350 | |
351 | if (foldComment && style == SCE_STTXT_COMMENT) { |
352 | if(stylePrev != SCE_STTXT_COMMENT) |
353 | levelCurrent++; |
354 | else if(styleNext != SCE_STTXT_COMMENT && !atEOL) |
355 | levelCurrent--; |
356 | } |
357 | if ( foldComment && atEOL && ( IsCommentLine(lineCurrent, styler,false) |
358 | || IsCommentLine(lineCurrent,styler,true))) { |
359 | if(!IsCommentLine(lineCurrent-1, styler,true) && IsCommentLine(lineCurrent+1, styler,true)) |
360 | levelCurrent++; |
361 | if (IsCommentLine(lineCurrent-1, styler,true) && !IsCommentLine(lineCurrent+1, styler,true)) |
362 | levelCurrent--; |
363 | if (!IsCommentLine(lineCurrent-1, styler,false) && IsCommentLine(lineCurrent+1, styler,false)) |
364 | levelCurrent++; |
365 | if (IsCommentLine(lineCurrent-1, styler,false) && !IsCommentLine(lineCurrent+1, styler,false)) |
366 | levelCurrent--; |
367 | } |
368 | if(foldPreprocessor && atEOL && IsPragmaLine(lineCurrent, styler)) { |
369 | if(!IsPragmaLine(lineCurrent-1, styler) && IsPragmaLine(lineCurrent+1, styler )) |
370 | levelCurrent++; |
371 | else if(IsPragmaLine(lineCurrent-1, styler) && !IsPragmaLine(lineCurrent+1, styler)) |
372 | levelCurrent--; |
373 | } |
374 | if (stylePrev != SCE_STTXT_KEYWORD && style == SCE_STTXT_KEYWORD) { |
375 | lastStart = i; |
376 | } |
377 | if(stylePrev == SCE_STTXT_KEYWORD) { |
378 | if(setWord.Contains(ch) && !setWord.Contains(chNext)) |
379 | ClassifySTTXTWordFoldPoint(levelCurrent,lastStart, i, styler); |
380 | } |
381 | if (!IsASpace(ch)) { |
382 | visibleChars++; |
383 | } |
384 | if (atEOL) { |
385 | int lev = levelPrev; |
386 | if (visibleChars == 0 && foldCompact) |
387 | lev |= SC_FOLDLEVELWHITEFLAG; |
388 | if ((levelCurrent > levelPrev) && (visibleChars > 0)) |
389 | lev |= SC_FOLDLEVELHEADERFLAG; |
390 | if (lev != styler.LevelAt(lineCurrent)) |
391 | styler.SetLevel(lineCurrent, lev); |
392 | |
393 | lineCurrent++; |
394 | levelPrev = levelCurrent; |
395 | visibleChars = 0; |
396 | } |
397 | |
398 | // If we didn't reach the EOL in previous loop, store line level and whitespace information. |
399 | // The rest will be filled in later... |
400 | int lev = levelPrev; |
401 | if (visibleChars == 0 && foldCompact) |
402 | lev |= SC_FOLDLEVELWHITEFLAG; |
403 | styler.SetLevel(lineCurrent, lev); |
404 | } |
405 | } |
406 | |
407 | LexerModule lmSTTXT(SCLEX_STTXT, ColouriseSTTXTDoc, "fcST" , FoldSTTXTDoc, STTXTWordListDesc); |
408 | |