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
30using namespace Lexilla;
31
32static 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
64static 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
193static const char * const STTXTWordListDesc[] = {
194 "Keywords",
195 "Types",
196 "Functions",
197 "FB",
198 "Local_Var",
199 "Local_Pragma",
200 0
201};
202
203static bool IsCommentLine(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
240static 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
258static 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
268static 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
325static void FoldSTTXTDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[],Accessor &styler)
326{
327 bool foldComment = 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
407LexerModule lmSTTXT(SCLEX_STTXT, ColouriseSTTXTDoc, "fcST", FoldSTTXTDoc, STTXTWordListDesc);
408