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