1 // Scintilla source code edit control
2 /** @file LexTAL.cxx
3  ** Lexer for TAL
4  ** Based on LexPascal.cxx
5  ** Written by Laurent le Tynevez
6  ** Updated by Simon Steele <s.steele@pnotepad.org> September 2002
7  ** Updated by Mathias Rauen <scite@madshi.net> May 2003 (Delphi adjustments)
8  ** Updated by Rod Falck, Aug 2006 Converted to TACL
9  **/
10 
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15 #include <assert.h>
16 #include <ctype.h>
17 
18 #include "ILexer.h"
19 #include "Scintilla.h"
20 #include "SciLexer.h"
21 
22 #include "WordList.h"
23 #include "LexAccessor.h"
24 #include "Accessor.h"
25 #include "StyleContext.h"
26 #include "CharacterSet.h"
27 #include "LexerModule.h"
28 
29 #ifdef SCI_NAMESPACE
30 using namespace Scintilla;
31 #endif
32 
isTACLoperator(char ch)33 inline bool isTACLoperator(char ch)
34 	{
35 	return ch == '\'' || isoperator(ch);
36 	}
37 
isTACLwordchar(char ch)38 inline bool isTACLwordchar(char ch)
39 	{
40 	return ch == '#' || ch == '^' || ch == '|' || ch == '_' || iswordchar(ch);
41 	}
42 
isTACLwordstart(char ch)43 inline bool isTACLwordstart(char ch)
44 	{
45 	return ch == '#' || ch == '|' || ch == '_' || iswordstart(ch);
46 	}
47 
getRange(unsigned int start,unsigned int end,Accessor & styler,char * s,unsigned int len)48 static void getRange(unsigned int start,
49 		unsigned int end,
50 		Accessor &styler,
51 		char *s,
52 		unsigned int len) {
53 	unsigned int i = 0;
54 	while ((i < end - start + 1) && (i < len-1)) {
55 		s[i] = static_cast<char>(tolower(styler[start + i]));
56 		i++;
57 	}
58 	s[i] = '\0';
59 }
60 
IsStreamCommentStyle(int style)61 static bool IsStreamCommentStyle(int style) {
62 	return style == SCE_C_COMMENT ||
63 		style == SCE_C_COMMENTDOC ||
64 		style == SCE_C_COMMENTDOCKEYWORD ||
65 		style == SCE_C_COMMENTDOCKEYWORDERROR;
66 }
67 
ColourTo(Accessor & styler,unsigned int end,unsigned int attr,bool bInAsm)68 static void ColourTo(Accessor &styler, unsigned int end, unsigned int attr, bool bInAsm) {
69 	if ((bInAsm) && (attr == SCE_C_OPERATOR || attr == SCE_C_NUMBER || attr == SCE_C_DEFAULT || attr == SCE_C_WORD || attr == SCE_C_IDENTIFIER)) {
70 		styler.ColourTo(end, SCE_C_REGEX);
71 	} else
72 		styler.ColourTo(end, attr);
73 }
74 
75 // returns 1 if the item starts a class definition, and -1 if the word is "end", and 2 if the word is "asm"
classifyWordTACL(unsigned int start,unsigned int end,WordList * keywordlists[],Accessor & styler,bool bInAsm)76 static int classifyWordTACL(unsigned int start, unsigned int end, /*WordList &keywords*/WordList *keywordlists[], Accessor &styler, bool bInAsm) {
77 	int ret = 0;
78 
79 	WordList& keywords = *keywordlists[0];
80 	WordList& builtins = *keywordlists[1];
81 	WordList& commands = *keywordlists[2];
82 
83 	char s[100];
84 	getRange(start, end, styler, s, sizeof(s));
85 
86 	char chAttr = SCE_C_IDENTIFIER;
87 	if (isdigit(s[0]) || (s[0] == '.')) {
88 		chAttr = SCE_C_NUMBER;
89 	}
90 	else {
91 		if (s[0] == '#' || keywords.InList(s)) {
92 			chAttr = SCE_C_WORD;
93 
94 			if (strcmp(s, "asm") == 0) {
95 				ret = 2;
96 			}
97 			else if (strcmp(s, "end") == 0) {
98 				ret = -1;
99 			}
100 		}
101 		else if (s[0] == '|' || builtins.InList(s)) {
102 			chAttr = SCE_C_WORD2;
103 		}
104 		else if (commands.InList(s)) {
105 			chAttr = SCE_C_UUID;
106 		}
107 		else if (strcmp(s, "comment") == 0) {
108 			chAttr = SCE_C_COMMENTLINE;
109 			ret = 3;
110 		}
111 	}
112 	ColourTo(styler, end, chAttr, (bInAsm && ret != -1));
113 	return ret;
114 }
115 
classifyFoldPointTACL(const char * s)116 static int classifyFoldPointTACL(const char* s) {
117 	int lev = 0;
118 	if (s[0] == '[')
119 		lev=1;
120 	else if (s[0] == ']')
121 		lev=-1;
122 	return lev;
123 }
124 
ColouriseTACLDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)125 static void ColouriseTACLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
126 	Accessor &styler) {
127 
128 	styler.StartAt(startPos);
129 
130 	int state = initStyle;
131 	if (state == SCE_C_CHARACTER)	// Does not leak onto next line
132 		state = SCE_C_DEFAULT;
133 	char chPrev = ' ';
134 	char chNext = styler[startPos];
135 	unsigned int lengthDoc = startPos + length;
136 
137 	bool bInClassDefinition;
138 
139 	int currentLine = styler.GetLine(startPos);
140 	if (currentLine > 0) {
141 		styler.SetLineState(currentLine, styler.GetLineState(currentLine-1));
142 		bInClassDefinition = (styler.GetLineState(currentLine) == 1);
143 	} else {
144 		styler.SetLineState(currentLine, 0);
145 		bInClassDefinition = false;
146 	}
147 
148 	bool bInAsm = (state == SCE_C_REGEX);
149 	if (bInAsm)
150 		state = SCE_C_DEFAULT;
151 
152 	styler.StartSegment(startPos);
153 	int visibleChars = 0;
154 	unsigned int i;
155 	for (i = startPos; i < lengthDoc; i++) {
156 		char ch = chNext;
157 
158 		chNext = styler.SafeGetCharAt(i + 1);
159 
160 		if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
161 			// Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix)
162 			// Avoid triggering two times on Dos/Win
163 			// End of line
164 			if (state == SCE_C_CHARACTER) {
165 				ColourTo(styler, i, state, bInAsm);
166 				state = SCE_C_DEFAULT;
167 			}
168 			visibleChars = 0;
169 			currentLine++;
170 			styler.SetLineState(currentLine, (bInClassDefinition ? 1 : 0));
171 		}
172 
173 		if (styler.IsLeadByte(ch)) {
174 			chNext = styler.SafeGetCharAt(i + 2);
175 			chPrev = ' ';
176 			i += 1;
177 			continue;
178 		}
179 
180 		if (state == SCE_C_DEFAULT) {
181 			if (isTACLwordstart(ch)) {
182 				ColourTo(styler, i-1, state, bInAsm);
183 				state = SCE_C_IDENTIFIER;
184 			} else if (ch == '{') {
185 				ColourTo(styler, i-1, state, bInAsm);
186 				state = SCE_C_COMMENT;
187 			} else if (ch == '{' && chNext == '*') {
188 				ColourTo(styler, i-1, state, bInAsm);
189 				state = SCE_C_COMMENTDOC;
190 			} else if (ch == '=' && chNext == '=') {
191 				ColourTo(styler, i-1, state, bInAsm);
192 				state = SCE_C_COMMENTLINE;
193 			} else if (ch == '"') {
194 				ColourTo(styler, i-1, state, bInAsm);
195 				state = SCE_C_STRING;
196 			} else if (ch == '?' && visibleChars == 0) {
197 				ColourTo(styler, i-1, state, bInAsm);
198 				state = SCE_C_PREPROCESSOR;
199 			} else if (isTACLoperator(ch)) {
200 				ColourTo(styler, i-1, state, bInAsm);
201 				ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
202 			}
203 		} else if (state == SCE_C_IDENTIFIER) {
204 			if (!isTACLwordchar(ch)) {
205 				int lStateChange = classifyWordTACL(styler.GetStartSegment(), i - 1, keywordlists, styler, bInAsm);
206 
207 				if(lStateChange == 1) {
208 					styler.SetLineState(currentLine, 1);
209 					bInClassDefinition = true;
210 				} else if(lStateChange == 2) {
211 					bInAsm = true;
212 				} else if(lStateChange == -1) {
213 					styler.SetLineState(currentLine, 0);
214 					bInClassDefinition = false;
215 					bInAsm = false;
216 				}
217 
218 				if (lStateChange == 3) {
219 					 state = SCE_C_COMMENTLINE;
220 				}
221 				else {
222 					state = SCE_C_DEFAULT;
223 					chNext = styler.SafeGetCharAt(i + 1);
224 					if (ch == '{') {
225 						state = SCE_C_COMMENT;
226 					} else if (ch == '{' && chNext == '*') {
227 						ColourTo(styler, i-1, state, bInAsm);
228 						state = SCE_C_COMMENTDOC;
229 					} else if (ch == '=' && chNext == '=') {
230 						state = SCE_C_COMMENTLINE;
231 					} else if (ch == '"') {
232 						state = SCE_C_STRING;
233 					} else if (isTACLoperator(ch)) {
234 						ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
235 					}
236 				}
237 			}
238 		} else {
239 			if (state == SCE_C_PREPROCESSOR) {
240 				if ((ch == '\r' || ch == '\n') && !(chPrev == '\\' || chPrev == '\r')) {
241 					ColourTo(styler, i-1, state, bInAsm);
242 					state = SCE_C_DEFAULT;
243 				}
244 			} else if (state == SCE_C_COMMENT) {
245 				if (ch == '}' || (ch == '\r' || ch == '\n') ) {
246 					ColourTo(styler, i, state, bInAsm);
247 					state = SCE_C_DEFAULT;
248 				}
249 			} else if (state == SCE_C_COMMENTDOC) {
250 				if (ch == '}' || (ch == '\r' || ch == '\n')) {
251 					if (((i > styler.GetStartSegment() + 2) || (
252 						(initStyle == SCE_C_COMMENTDOC) &&
253 						(styler.GetStartSegment() == static_cast<unsigned int>(startPos))))) {
254 							ColourTo(styler, i, state, bInAsm);
255 							state = SCE_C_DEFAULT;
256 					}
257 				}
258 			} else if (state == SCE_C_COMMENTLINE) {
259 				if (ch == '\r' || ch == '\n') {
260 					ColourTo(styler, i-1, state, bInAsm);
261 					state = SCE_C_DEFAULT;
262 				}
263 			} else if (state == SCE_C_STRING) {
264 				if (ch == '"' || ch == '\r' || ch == '\n') {
265 					ColourTo(styler, i, state, bInAsm);
266 					state = SCE_C_DEFAULT;
267 				}
268 			}
269 		}
270         if (!isspacechar(ch))
271             visibleChars++;
272 		chPrev = ch;
273 	}
274 
275 	// Process to end of document
276 	if (state == SCE_C_IDENTIFIER) {
277 		classifyWordTACL(styler.GetStartSegment(), i - 1, keywordlists, styler, bInAsm);
278 		}
279 	else
280 		ColourTo(styler, lengthDoc - 1, state, bInAsm);
281 }
282 
FoldTACLDoc(unsigned int startPos,int length,int initStyle,WordList * [],Accessor & styler)283 static void FoldTACLDoc(unsigned int startPos, int length, int initStyle, WordList *[],
284                             Accessor &styler) {
285 	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
286 	bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
287 	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
288 	unsigned int endPos = startPos + length;
289 	int visibleChars = 0;
290 	int lineCurrent = styler.GetLine(startPos);
291 	int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
292 	int levelCurrent = levelPrev;
293 	char chNext = styler[startPos];
294 	int styleNext = styler.StyleAt(startPos);
295 	int style = initStyle;
296 	bool section = false;
297 
298 	int lastStart = 0;
299 
300 	for (unsigned int i = startPos; i < endPos; i++) {
301 		char ch = chNext;
302 		chNext = styler.SafeGetCharAt(i + 1);
303 		int stylePrev = style;
304 		style = styleNext;
305 		styleNext = styler.StyleAt(i + 1);
306 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
307 
308 		if (stylePrev == SCE_C_DEFAULT && (style == SCE_C_WORD || style == SCE_C_PREPROCESSOR))
309 		{
310 			// Store last word start point.
311 			lastStart = i;
312 		}
313 
314 		if (stylePrev == SCE_C_WORD || stylePrev == SCE_C_PREPROCESSOR) {
315 			if(isTACLwordchar(ch) && !isTACLwordchar(chNext)) {
316 				char s[100];
317 				getRange(lastStart, i, styler, s, sizeof(s));
318 				if (stylePrev == SCE_C_PREPROCESSOR && strcmp(s, "?section") == 0)
319 					{
320 					section = true;
321 					levelCurrent = 1;
322 					levelPrev = 0;
323 					}
324 				else if (stylePrev == SCE_C_WORD)
325 					levelCurrent += classifyFoldPointTACL(s);
326 			}
327 		}
328 
329 		if (style == SCE_C_OPERATOR) {
330 			if (ch == '[') {
331 				levelCurrent++;
332 			} else if (ch == ']') {
333 				levelCurrent--;
334 			}
335 		}
336 		if (foldComment && (style == SCE_C_COMMENTLINE)) {
337 			if ((ch == '/') && (chNext == '/')) {
338 				char chNext2 = styler.SafeGetCharAt(i + 2);
339 				if (chNext2 == '{') {
340 					levelCurrent++;
341 				} else if (chNext2 == '}') {
342 					levelCurrent--;
343 				}
344 			}
345 		}
346 
347 		if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
348 			if (ch == '{' && chNext == '$') {
349 				unsigned int j=i+2; // skip {$
350 				while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
351 					j++;
352 				}
353 				if (styler.Match(j, "region") || styler.Match(j, "if")) {
354 					levelCurrent++;
355 				} else if (styler.Match(j, "end")) {
356 					levelCurrent--;
357 				}
358 			}
359 		}
360 
361 		if (foldComment && IsStreamCommentStyle(style)) {
362 			if (!IsStreamCommentStyle(stylePrev)) {
363 				levelCurrent++;
364 			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
365 				// Comments don't end at end of line and the next character may be unstyled.
366 				levelCurrent--;
367 			}
368 		}
369 		if (atEOL) {
370 			int lev = levelPrev | SC_FOLDLEVELBASE;
371 			if (visibleChars == 0 && foldCompact)
372 				lev |= SC_FOLDLEVELWHITEFLAG;
373 			if ((levelCurrent > levelPrev || section) && (visibleChars > 0))
374 				lev |= SC_FOLDLEVELHEADERFLAG;
375 			if (lev != styler.LevelAt(lineCurrent)) {
376 				styler.SetLevel(lineCurrent, lev);
377 			}
378 			lineCurrent++;
379 			levelPrev = levelCurrent;
380 			visibleChars = 0;
381 			section = false;
382 		}
383 
384 		if (!isspacechar(ch))
385 			visibleChars++;
386 	}
387 
388 	// Fill in the real level of the next line, keeping the current flags as they will be filled in later
389 	int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
390 	styler.SetLevel(lineCurrent, levelPrev | flagsNext);
391 }
392 
393 static const char * const TACLWordListDesc[] = {
394 	"Builtins",
395 	"Labels",
396 	"Commands",
397 	0
398 };
399 
400 LexerModule lmTACL(SCLEX_TACL, ColouriseTACLDoc, "TACL", FoldTACLDoc, TACLWordListDesc);
401