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