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