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