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