1// Scintilla source code edit control
2/** @file LexTACL.cxx
3 ** Lexer for TACL
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 <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 isTACLoperator(char ch)
35 {
36 return ch == '\'' || isoperator(ch);
37 }
38
39inline bool isTACLwordchar(char ch)
40 {
41 return ch == '#' || ch == '^' || ch == '|' || ch == '_' || iswordchar(ch);
42 }
43
44inline bool isTACLwordstart(char ch)
45 {
46 return ch == '#' || 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 classifyWordTACL(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& commands = *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 (s[0] == '#' || 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 (commands.InList(s)) {
106 chAttr = SCE_C_UUID;
107 }
108 else if (strcmp(s, "comment") == 0) {
109 chAttr = SCE_C_COMMENTLINE;
110 ret = 3;
111 }
112 }
113 ColourTo(styler, end, chAttr, (bInAsm && ret != -1));
114 return ret;
115}
116
117static int classifyFoldPointTACL(const char* s) {
118 int lev = 0;
119 if (s[0] == '[')
120 lev=1;
121 else if (s[0] == ']')
122 lev=-1;
123 return lev;
124}
125
126static void ColouriseTACLDoc(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 Sci_PositionU i;
156 for (i = startPos; i < lengthDoc; i++) {
157 char ch = chNext;
158
159 chNext = styler.SafeGetCharAt(i + 1);
160
161 if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
162 // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix)
163 // Avoid triggering two times on Dos/Win
164 // End of line
165 if (state == SCE_C_CHARACTER) {
166 ColourTo(styler, i, state, bInAsm);
167 state = SCE_C_DEFAULT;
168 }
169 visibleChars = 0;
170 currentLine++;
171 styler.SetLineState(currentLine, (bInClassDefinition ? 1 : 0));
172 }
173
174 if (styler.IsLeadByte(ch)) {
175 chNext = styler.SafeGetCharAt(i + 2);
176 chPrev = ' ';
177 i += 1;
178 continue;
179 }
180
181 if (state == SCE_C_DEFAULT) {
182 if (isTACLwordstart(ch)) {
183 ColourTo(styler, i-1, state, bInAsm);
184 state = SCE_C_IDENTIFIER;
185 } else if (ch == '{') {
186 ColourTo(styler, i-1, state, bInAsm);
187 state = SCE_C_COMMENT;
188 } else if (ch == '{' && chNext == '*') {
189 ColourTo(styler, i-1, state, bInAsm);
190 state = SCE_C_COMMENTDOC;
191 } else if (ch == '=' && chNext == '=') {
192 ColourTo(styler, i-1, state, bInAsm);
193 state = SCE_C_COMMENTLINE;
194 } else if (ch == '"') {
195 ColourTo(styler, i-1, state, bInAsm);
196 state = SCE_C_STRING;
197 } else if (ch == '?' && visibleChars == 0) {
198 ColourTo(styler, i-1, state, bInAsm);
199 state = SCE_C_PREPROCESSOR;
200 } else if (isTACLoperator(ch)) {
201 ColourTo(styler, i-1, state, bInAsm);
202 ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
203 }
204 } else if (state == SCE_C_IDENTIFIER) {
205 if (!isTACLwordchar(ch)) {
206 int lStateChange = classifyWordTACL(styler.GetStartSegment(), i - 1, keywordlists, styler, bInAsm);
207
208 if(lStateChange == 1) {
209 styler.SetLineState(currentLine, 1);
210 bInClassDefinition = true;
211 } else if(lStateChange == 2) {
212 bInAsm = true;
213 } else if(lStateChange == -1) {
214 styler.SetLineState(currentLine, 0);
215 bInClassDefinition = false;
216 bInAsm = false;
217 }
218
219 if (lStateChange == 3) {
220 state = SCE_C_COMMENTLINE;
221 }
222 else {
223 state = SCE_C_DEFAULT;
224 chNext = styler.SafeGetCharAt(i + 1);
225 if (ch == '{') {
226 state = SCE_C_COMMENT;
227 } else if (ch == '{' && chNext == '*') {
228 ColourTo(styler, i-1, state, bInAsm);
229 state = SCE_C_COMMENTDOC;
230 } else if (ch == '=' && chNext == '=') {
231 state = SCE_C_COMMENTLINE;
232 } else if (ch == '"') {
233 state = SCE_C_STRING;
234 } else if (isTACLoperator(ch)) {
235 ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
236 }
237 }
238 }
239 } else {
240 if (state == SCE_C_PREPROCESSOR) {
241 if ((ch == '\r' || ch == '\n') && !(chPrev == '\\' || chPrev == '\r')) {
242 ColourTo(styler, i-1, state, bInAsm);
243 state = SCE_C_DEFAULT;
244 }
245 } else if (state == SCE_C_COMMENT) {
246 if (ch == '}' || (ch == '\r' || ch == '\n') ) {
247 ColourTo(styler, i, state, bInAsm);
248 state = SCE_C_DEFAULT;
249 }
250 } else if (state == SCE_C_COMMENTDOC) {
251 if (ch == '}' || (ch == '\r' || ch == '\n')) {
252 if (((i > styler.GetStartSegment() + 2) || (
253 (initStyle == SCE_C_COMMENTDOC) &&
254 (styler.GetStartSegment() == static_cast<Sci_PositionU>(startPos))))) {
255 ColourTo(styler, i, state, bInAsm);
256 state = SCE_C_DEFAULT;
257 }
258 }
259 } else if (state == SCE_C_COMMENTLINE) {
260 if (ch == '\r' || ch == '\n') {
261 ColourTo(styler, i-1, state, bInAsm);
262 state = SCE_C_DEFAULT;
263 }
264 } else if (state == SCE_C_STRING) {
265 if (ch == '"' || ch == '\r' || ch == '\n') {
266 ColourTo(styler, i, state, bInAsm);
267 state = SCE_C_DEFAULT;
268 }
269 }
270 }
271 if (!isspacechar(ch))
272 visibleChars++;
273 chPrev = ch;
274 }
275
276 // Process to end of document
277 if (state == SCE_C_IDENTIFIER) {
278 classifyWordTACL(styler.GetStartSegment(), i - 1, keywordlists, styler, bInAsm);
279 }
280 else
281 ColourTo(styler, lengthDoc - 1, state, bInAsm);
282}
283
284static void FoldTACLDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[],
285 Accessor &styler) {
286 bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
287 bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
288 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
289 Sci_PositionU endPos = startPos + length;
290 int visibleChars = 0;
291 Sci_Position lineCurrent = styler.GetLine(startPos);
292 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
293 int levelCurrent = levelPrev;
294 char chNext = styler[startPos];
295 int styleNext = styler.StyleAt(startPos);
296 int style = initStyle;
297 bool section = false;
298
299 Sci_Position lastStart = 0;
300
301 for (Sci_PositionU i = startPos; i < endPos; i++) {
302 char ch = chNext;
303 chNext = styler.SafeGetCharAt(i + 1);
304 int stylePrev = style;
305 style = styleNext;
306 styleNext = styler.StyleAt(i + 1);
307 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
308
309 if (stylePrev == SCE_C_DEFAULT && (style == SCE_C_WORD || style == SCE_C_PREPROCESSOR))
310 {
311 // Store last word start point.
312 lastStart = i;
313 }
314
315 if (stylePrev == SCE_C_WORD || stylePrev == SCE_C_PREPROCESSOR) {
316 if(isTACLwordchar(ch) && !isTACLwordchar(chNext)) {
317 char s[100];
318 getRange(lastStart, i, styler, s, sizeof(s));
319 if (stylePrev == SCE_C_PREPROCESSOR && strcmp(s, "?section") == 0)
320 {
321 section = true;
322 levelCurrent = 1;
323 levelPrev = 0;
324 }
325 else if (stylePrev == SCE_C_WORD)
326 levelCurrent += classifyFoldPointTACL(s);
327 }
328 }
329
330 if (style == SCE_C_OPERATOR) {
331 if (ch == '[') {
332 levelCurrent++;
333 } else if (ch == ']') {
334 levelCurrent--;
335 }
336 }
337 if (foldComment && (style == SCE_C_COMMENTLINE)) {
338 if ((ch == '/') && (chNext == '/')) {
339 char chNext2 = styler.SafeGetCharAt(i + 2);
340 if (chNext2 == '{') {
341 levelCurrent++;
342 } else if (chNext2 == '}') {
343 levelCurrent--;
344 }
345 }
346 }
347
348 if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
349 if (ch == '{' && chNext == '$') {
350 Sci_PositionU j=i+2; // skip {$
351 while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
352 j++;
353 }
354 if (styler.Match(j, "region") || styler.Match(j, "if")) {
355 levelCurrent++;
356 } else if (styler.Match(j, "end")) {
357 levelCurrent--;
358 }
359 }
360 }
361
362 if (foldComment && IsStreamCommentStyle(style)) {
363 if (!IsStreamCommentStyle(stylePrev)) {
364 levelCurrent++;
365 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
366 // Comments don't end at end of line and the next character may be unstyled.
367 levelCurrent--;
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 TACLWordListDesc[] = {
395 "Builtins",
396 "Labels",
397 "Commands",
398 0
399};
400
401LexerModule lmTACL(SCLEX_TACL, ColouriseTACLDoc, "TACL", FoldTACLDoc, TACLWordListDesc);
402