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