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 <string>
19#include <string_view>
20
21#include "ILexer.h"
22#include "Scintilla.h"
23#include "SciLexer.h"
24
25#include "WordList.h"
26#include "LexAccessor.h"
27#include "Accessor.h"
28#include "StyleContext.h"
29#include "CharacterSet.h"
30#include "LexerModule.h"
31
32using namespace Lexilla;
33
34inline bool isTALoperator(char ch)
35 {
36 return ch == '\'' || ch == '@' || ch == '#' || isoperator(ch);
37 }
38
39inline bool isTALwordchar(char ch)
40 {
41 return ch == '$' || ch == '^' || iswordchar(ch);
42 }
43
44inline bool isTALwordstart(char ch)
45 {
46 return ch == '$' || ch == '^' || iswordstart(ch);
47 }
48
49static void getRange(Sci_PositionU start,
50 Sci_PositionU end,
51 Accessor &styler,
52 char *s,
53 Sci_PositionU len) {
54 Sci_PositionU i = 0;
55 while ((i < end - start + 1) && (i < len-1)) {
56 s[i] = static_cast<char>(tolower(styler[start + i]));
57 i++;
58 }
59 s[i] = '\0';
60}
61
62static bool IsStreamCommentStyle(int style) {
63 return style == SCE_C_COMMENT ||
64 style == SCE_C_COMMENTDOC ||
65 style == SCE_C_COMMENTDOCKEYWORD ||
66 style == SCE_C_COMMENTDOCKEYWORDERROR;
67}
68
69static void ColourTo(Accessor &styler, Sci_PositionU end, unsigned int attr, bool bInAsm) {
70 if ((bInAsm) && (attr == SCE_C_OPERATOR || attr == SCE_C_NUMBER || attr == SCE_C_DEFAULT || attr == SCE_C_WORD || attr == SCE_C_IDENTIFIER)) {
71 styler.ColourTo(end, SCE_C_REGEX);
72 } else
73 styler.ColourTo(end, attr);
74}
75
76// returns 1 if the item starts a class definition, and -1 if the word is "end", and 2 if the word is "asm"
77static int classifyWordTAL(Sci_PositionU start, Sci_PositionU end, /*WordList &keywords*/WordList *keywordlists[], Accessor &styler, bool bInAsm) {
78 int ret = 0;
79
80 WordList& keywords = *keywordlists[0];
81 WordList& builtins = *keywordlists[1];
82 WordList& nonreserved_keywords = *keywordlists[2];
83
84 char s[100];
85 getRange(start, end, styler, s, sizeof(s));
86
87 char chAttr = SCE_C_IDENTIFIER;
88 if (isdigit(s[0]) || (s[0] == '.')) {
89 chAttr = SCE_C_NUMBER;
90 }
91 else {
92 if (keywords.InList(s)) {
93 chAttr = SCE_C_WORD;
94
95 if (strcmp(s, "asm") == 0) {
96 ret = 2;
97 }
98 else if (strcmp(s, "end") == 0) {
99 ret = -1;
100 }
101 }
102 else if (s[0] == '$' || builtins.InList(s)) {
103 chAttr = SCE_C_WORD2;
104 }
105 else if (nonreserved_keywords.InList(s)) {
106 chAttr = SCE_C_UUID;
107 }
108 }
109 ColourTo(styler, end, chAttr, (bInAsm && ret != -1));
110 return ret;
111}
112
113static int classifyFoldPointTAL(const char* s) {
114 int lev = 0;
115 if (!(isdigit(s[0]) || (s[0] == '.'))) {
116 if (strcmp(s, "begin") == 0 ||
117 strcmp(s, "block") == 0) {
118 lev=1;
119 } else if (strcmp(s, "end") == 0) {
120 lev=-1;
121 }
122 }
123 return lev;
124}
125
126static void ColouriseTALDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[],
127 Accessor &styler) {
128
129 styler.StartAt(startPos);
130
131 int state = initStyle;
132 if (state == SCE_C_CHARACTER) // Does not leak onto next line
133 state = SCE_C_DEFAULT;
134 char chPrev = ' ';
135 char chNext = styler[startPos];
136 Sci_PositionU lengthDoc = startPos + length;
137
138 bool bInClassDefinition;
139
140 Sci_Position currentLine = styler.GetLine(startPos);
141 if (currentLine > 0) {
142 styler.SetLineState(currentLine, styler.GetLineState(currentLine-1));
143 bInClassDefinition = (styler.GetLineState(currentLine) == 1);
144 } else {
145 styler.SetLineState(currentLine, 0);
146 bInClassDefinition = false;
147 }
148
149 bool bInAsm = (state == SCE_C_REGEX);
150 if (bInAsm)
151 state = SCE_C_DEFAULT;
152
153 styler.StartSegment(startPos);
154 int visibleChars = 0;
155 for (Sci_PositionU 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 (isTALwordstart(ch)) {
182 ColourTo(styler, i-1, state, bInAsm);
183 state = SCE_C_IDENTIFIER;
184 } else if (ch == '!' && chNext != '*') {
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 (isTALoperator(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 (!isTALwordchar(ch)) {
205 int lStateChange = classifyWordTAL(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 state = SCE_C_DEFAULT;
219 chNext = styler.SafeGetCharAt(i + 1);
220 if (ch == '!' && chNext != '*') {
221 state = SCE_C_COMMENT;
222 } else if (ch == '!' && chNext == '*') {
223 ColourTo(styler, i-1, state, bInAsm);
224 state = SCE_C_COMMENTDOC;
225 } else if (ch == '-' && chNext == '-') {
226 state = SCE_C_COMMENTLINE;
227 } else if (ch == '"') {
228 state = SCE_C_STRING;
229 } else if (isTALoperator(ch)) {
230 ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
231 }
232 }
233 } else {
234 if (state == SCE_C_PREPROCESSOR) {
235 if ((ch == '\r' || ch == '\n') && !(chPrev == '\\' || chPrev == '\r')) {
236 ColourTo(styler, i-1, state, bInAsm);
237 state = SCE_C_DEFAULT;
238 }
239 } else if (state == SCE_C_COMMENT) {
240 if (ch == '!' || (ch == '\r' || ch == '\n') ) {
241 ColourTo(styler, i, state, bInAsm);
242 state = SCE_C_DEFAULT;
243 }
244 } else if (state == SCE_C_COMMENTDOC) {
245 if (ch == '!' || (ch == '\r' || ch == '\n')) {
246 if (((i > styler.GetStartSegment() + 2) || (
247 (initStyle == SCE_C_COMMENTDOC) &&
248 (styler.GetStartSegment() == static_cast<Sci_PositionU>(startPos))))) {
249 ColourTo(styler, i, state, bInAsm);
250 state = SCE_C_DEFAULT;
251 }
252 }
253 } else if (state == SCE_C_COMMENTLINE) {
254 if (ch == '\r' || ch == '\n') {
255 ColourTo(styler, i-1, state, bInAsm);
256 state = SCE_C_DEFAULT;
257 }
258 } else if (state == SCE_C_STRING) {
259 if (ch == '"') {
260 ColourTo(styler, i, state, bInAsm);
261 state = SCE_C_DEFAULT;
262 }
263 }
264 }
265 if (!isspacechar(ch))
266 visibleChars++;
267 chPrev = ch;
268 }
269 ColourTo(styler, lengthDoc - 1, state, bInAsm);
270}
271
272static void FoldTALDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[],
273 Accessor &styler) {
274 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
275 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
276 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
277 Sci_PositionU endPos = startPos + length;
278 int visibleChars = 0;
279 Sci_Position lineCurrent = styler.GetLine(startPos);
280 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
281 int levelCurrent = levelPrev;
282 char chNext = styler[startPos];
283 int styleNext = styler.StyleAt(startPos);
284 int style = initStyle;
285 bool was_end = false;
286 bool section = false;
287
288 Sci_Position lastStart = 0;
289
290 for (Sci_PositionU i = startPos; i < endPos; i++) {
291 char ch = chNext;
292 chNext = styler.SafeGetCharAt(i + 1);
293 int stylePrev = style;
294 style = styleNext;
295 styleNext = styler.StyleAt(i + 1);
296 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
297
298 if (stylePrev == SCE_C_DEFAULT && (style == SCE_C_WORD || style == SCE_C_UUID || style == SCE_C_PREPROCESSOR))
299 {
300 // Store last word start point.
301 lastStart = i;
302 }
303
304 if (stylePrev == SCE_C_WORD || style == SCE_C_UUID || stylePrev == SCE_C_PREPROCESSOR) {
305 if(isTALwordchar(ch) && !isTALwordchar(chNext)) {
306 char s[100];
307 getRange(lastStart, i, styler, s, sizeof(s));
308 if (stylePrev == SCE_C_PREPROCESSOR && strcmp(s, "?section") == 0)
309 {
310 section = true;
311 levelCurrent = 1;
312 levelPrev = 0;
313 }
314 else if (stylePrev == SCE_C_WORD || stylePrev == SCE_C_UUID)
315 {
316 if (strcmp(s, "block") == 0)
317 {
318 // block keyword is ignored immediately after end keyword
319 if (!was_end)
320 levelCurrent++;
321 }
322 else
323 levelCurrent += classifyFoldPointTAL(s);
324 if (strcmp(s, "end") == 0)
325 {
326 was_end = true;
327 }
328 else
329 {
330 was_end = false;
331 }
332 }
333 }
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 Sci_PositionU 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
370 if (atEOL) {
371 int lev = levelPrev | SC_FOLDLEVELBASE;
372 if (visibleChars == 0 && foldCompact)
373 lev |= SC_FOLDLEVELWHITEFLAG;
374 if ((levelCurrent > levelPrev || section) && (visibleChars > 0))
375 lev |= SC_FOLDLEVELHEADERFLAG;
376 if (lev != styler.LevelAt(lineCurrent)) {
377 styler.SetLevel(lineCurrent, lev);
378 }
379 lineCurrent++;
380 levelPrev = levelCurrent;
381 visibleChars = 0;
382 section = false;
383 }
384
385 if (!isspacechar(ch))
386 visibleChars++;
387 }
388
389 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
390 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
391 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
392}
393
394static const char * const TALWordListDesc[] = {
395 "Keywords",
396 "Builtins",
397 0
398};
399
400LexerModule lmTAL(SCLEX_TAL, ColouriseTALDoc, "TAL", FoldTALDoc, TALWordListDesc);
401