]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexNimrod.cxx
8c4d04360673c721a0839b56355b14ea9c0b143d
[wxWidgets.git] / src / stc / scintilla / src / LexNimrod.cxx
1 // Scintilla source code edit control
2 // Nimrod lexer
3 // (c) 2009 Andreas Rumpf
4 /** @file LexNimrod.cxx
5 ** Lexer for Nimrod.
6 **/
7 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
8 // The License.txt file describes the conditions under which this software may be distributed.
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdio.h>
14 #include <stdarg.h>
15
16 #include "Platform.h"
17
18 #include "PropSet.h"
19 #include "Accessor.h"
20 #include "StyleContext.h"
21 #include "KeyWords.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24
25 #ifdef SCI_NAMESPACE
26 using namespace Scintilla;
27 #endif
28
29 static inline bool IsAWordChar(int ch) {
30 return (ch >= 0x80) || isalnum(ch) || ch == '_';
31 }
32
33 static int tillEndOfTripleQuote(Accessor &styler, int pos, int max) {
34 /* search for """ */
35 for (;;) {
36 if (styler.SafeGetCharAt(pos, '\0') == '\0') return pos;
37 if (pos >= max) return pos;
38 if (styler.Match(pos, "\"\"\"")) {
39 return pos + 2;
40 }
41 pos++;
42 }
43 }
44
45 #define CR 13 /* use both because Scite allows changing the line ending */
46 #define LF 10
47
48 static bool inline isNewLine(int ch) {
49 return ch == CR || ch == LF;
50 }
51
52 static int scanString(Accessor &styler, int pos, int max, bool rawMode) {
53 for (;;) {
54 if (pos >= max) return pos;
55 char ch = styler.SafeGetCharAt(pos, '\0');
56 if (ch == CR || ch == LF || ch == '\0') return pos;
57 if (ch == '"') return pos;
58 if (ch == '\\' && !rawMode) {
59 pos += 2;
60 } else {
61 pos++;
62 }
63 }
64 }
65
66 static int scanChar(Accessor &styler, int pos, int max) {
67 for (;;) {
68 if (pos >= max) return pos;
69 char ch = styler.SafeGetCharAt(pos, '\0');
70 if (ch == CR || ch == LF || ch == '\0') return pos;
71 if (ch == '\'' && !isalnum(styler.SafeGetCharAt(pos+1, '\0')) )
72 return pos;
73 if (ch == '\\') {
74 pos += 2;
75 } else {
76 pos++;
77 }
78 }
79 }
80
81 static int scanIdent(Accessor &styler, int pos, WordList &keywords) {
82 char buf[100]; /* copy to lowercase and ignore underscores */
83 int i = 0;
84
85 for (;;) {
86 char ch = styler.SafeGetCharAt(pos, '\0');
87 if (!IsAWordChar(ch)) break;
88 if (ch != '_' && i < ((int)sizeof(buf))-1) {
89 buf[i] = static_cast<char>(tolower(ch));
90 i++;
91 }
92 pos++;
93 }
94 buf[i] = '\0';
95 /* look for keyword */
96 if (keywords.InList(buf)) {
97 styler.ColourTo(pos-1, SCE_P_WORD);
98 } else {
99 styler.ColourTo(pos-1, SCE_P_IDENTIFIER);
100 }
101 return pos;
102 }
103
104 static int scanNumber(Accessor &styler, int pos) {
105 char ch, ch2;
106 ch = styler.SafeGetCharAt(pos, '\0');
107 ch2 = styler.SafeGetCharAt(pos+1, '\0');
108 if (ch == '0' && (ch2 == 'b' || ch2 == 'B')) {
109 /* binary number: */
110 pos += 2;
111 for (;;) {
112 ch = styler.SafeGetCharAt(pos, '\0');
113 if (ch == '_' || (ch >= '0' && ch <= '1')) ++pos;
114 else break;
115 }
116 } else if (ch == '0' &&
117 (ch2 == 'o' || ch2 == 'O' || ch2 == 'c' || ch2 == 'C')) {
118 /* octal number: */
119 pos += 2;
120 for (;;) {
121 ch = styler.SafeGetCharAt(pos, '\0');
122 if (ch == '_' || (ch >= '0' && ch <= '7')) ++pos;
123 else break;
124 }
125 } else if (ch == '0' && (ch2 == 'x' || ch2 == 'X')) {
126 /* hexadecimal number: */
127 pos += 2;
128 for (;;) {
129 ch = styler.SafeGetCharAt(pos, '\0');
130 if (ch == '_' || (ch >= '0' && ch <= '9')
131 || (ch >= 'a' && ch <= 'f')
132 || (ch >= 'A' && ch <= 'F')) ++pos;
133 else break;
134 }
135 } else {
136 // skip decimal part:
137 for (;;) {
138 ch = styler.SafeGetCharAt(pos, '\0');
139 if (ch == '_' || (ch >= '0' && ch <= '9')) ++pos;
140 else break;
141 }
142 ch2 = styler.SafeGetCharAt(pos+1, '\0');
143 if (ch == '.' && ch2 >= '0' && ch2 <= '9') {
144 ++pos; // skip '.'
145 for (;;) {
146 ch = styler.SafeGetCharAt(pos, '\0');
147 if (ch == '_' || (ch >= '0' && ch <= '9')) ++pos;
148 else break;
149 }
150 }
151 if (ch == 'e' || ch == 'E') {
152 ++pos;
153 ch = styler.SafeGetCharAt(pos, '\0');
154 if (ch == '-' || ch == '+') ++pos;
155 for (;;) {
156 ch = styler.SafeGetCharAt(pos, '\0');
157 if (ch == '_' || (ch >= '0' && ch <= '9')) ++pos;
158 else break;
159 }
160 }
161 }
162 if (ch == '\'') {
163 /* a type suffix: */
164 pos++;
165 for (;;) {
166 ch = styler.SafeGetCharAt(pos);
167 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z')
168 || (ch >= 'a' && ch <= 'z') || ch == '_') ++pos;
169 else break;
170 }
171 }
172 styler.ColourTo(pos-1, SCE_P_NUMBER);
173 return pos;
174 }
175
176 /* rewritten from scratch, because I couldn't get rid of the bugs...
177 (A character based approach sucks!)
178 */
179 static void ColouriseNimrodDoc(unsigned int startPos, int length, int initStyle,
180 WordList *keywordlists[], Accessor &styler) {
181 int pos = startPos;
182 int max = startPos + length;
183 char ch;
184 WordList &keywords = *keywordlists[0];
185
186 styler.StartAt(startPos);
187 styler.StartSegment(startPos);
188
189 switch (initStyle) {
190 /* check where we are: */
191 case SCE_P_TRIPLEDOUBLE:
192 pos = tillEndOfTripleQuote(styler, pos, max);
193 styler.ColourTo(pos, SCE_P_TRIPLEDOUBLE);
194 pos++;
195 break;
196 default: /* nothing to do: */
197 break;
198 }
199 while (pos < max) {
200 ch = styler.SafeGetCharAt(pos, '\0');
201 switch (ch) {
202 case '\0': return;
203 case '#': {
204 bool doccomment = (styler.SafeGetCharAt(pos+1) == '#');
205 while (pos < max && !isNewLine(styler.SafeGetCharAt(pos, LF))) pos++;
206 if (doccomment)
207 styler.ColourTo(pos, SCE_C_COMMENTLINEDOC);
208 else
209 styler.ColourTo(pos, SCE_P_COMMENTLINE);
210 } break;
211 case 'r': case 'R': {
212 if (styler.SafeGetCharAt(pos+1) == '"') {
213 pos = scanString(styler, pos+2, max, true);
214 styler.ColourTo(pos, SCE_P_STRING);
215 pos++;
216 } else {
217 pos = scanIdent(styler, pos, keywords);
218 }
219 } break;
220 case '"':
221 if (styler.Match(pos+1, "\"\"")) {
222 pos = tillEndOfTripleQuote(styler, pos+3, max);
223 styler.ColourTo(pos, SCE_P_TRIPLEDOUBLE);
224 } else {
225 pos = scanString(styler, pos+1, max, false);
226 styler.ColourTo(pos, SCE_P_STRING);
227 }
228 pos++;
229 break;
230 case '\'':
231 pos = scanChar(styler, pos+1, max);
232 styler.ColourTo(pos, SCE_P_CHARACTER);
233 pos++;
234 break;
235 default: // identifers, numbers, operators, whitespace
236 if (ch >= '0' && ch <= '9') {
237 pos = scanNumber(styler, pos);
238 } else if (IsAWordChar(ch)) {
239 pos = scanIdent(styler, pos, keywords);
240 } else if (ch == '`') {
241 pos++;
242 while (pos < max) {
243 ch = styler.SafeGetCharAt(pos, LF);
244 if (ch == '`') {
245 ++pos;
246 break;
247 }
248 if (ch == CR || ch == LF) break;
249 ++pos;
250 }
251 styler.ColourTo(pos, SCE_P_IDENTIFIER);
252 } else if (strchr("()[]{}:=;-\\/&%$!+<>|^?,.*~@", ch)) {
253 styler.ColourTo(pos, SCE_P_OPERATOR);
254 pos++;
255 } else {
256 styler.ColourTo(pos, SCE_P_DEFAULT);
257 pos++;
258 }
259 break;
260 }
261 }
262 }
263
264 static bool IsCommentLine(int line, Accessor &styler) {
265 int pos = styler.LineStart(line);
266 int eol_pos = styler.LineStart(line + 1) - 1;
267 for (int i = pos; i < eol_pos; i++) {
268 char ch = styler[i];
269 if (ch == '#')
270 return true;
271 else if (ch != ' ' && ch != '\t')
272 return false;
273 }
274 return false;
275 }
276
277 static bool IsQuoteLine(int line, Accessor &styler) {
278 int style = styler.StyleAt(styler.LineStart(line)) & 31;
279 return ((style == SCE_P_TRIPLE) || (style == SCE_P_TRIPLEDOUBLE));
280 }
281
282
283 static void FoldNimrodDoc(unsigned int startPos, int length,
284 int /*initStyle - unused*/,
285 WordList *[], Accessor &styler) {
286 const int maxPos = startPos + length;
287 const int maxLines = styler.GetLine(maxPos - 1); // Requested last line
288 const int docLines = styler.GetLine(styler.Length() - 1); // Available last line
289 const bool foldComment = styler.GetPropertyInt("fold.comment.nimrod") != 0;
290 const bool foldQuotes = styler.GetPropertyInt("fold.quotes.nimrod") != 0;
291
292 // Backtrack to previous non-blank line so we can determine indent level
293 // for any white space lines (needed esp. within triple quoted strings)
294 // and so we can fix any preceding fold level (which is why we go back
295 // at least one line in all cases)
296 int spaceFlags = 0;
297 int lineCurrent = styler.GetLine(startPos);
298 int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
299 while (lineCurrent > 0) {
300 lineCurrent--;
301 indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
302 if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG) &&
303 (!IsCommentLine(lineCurrent, styler)) &&
304 (!IsQuoteLine(lineCurrent, styler)))
305 break;
306 }
307 int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
308
309 // Set up initial loop state
310 startPos = styler.LineStart(lineCurrent);
311 int prev_state = SCE_P_DEFAULT & 31;
312 if (lineCurrent >= 1)
313 prev_state = styler.StyleAt(startPos - 1) & 31;
314 int prevQuote = foldQuotes && ((prev_state == SCE_P_TRIPLE) ||
315 (prev_state == SCE_P_TRIPLEDOUBLE));
316 int prevComment = 0;
317 if (lineCurrent >= 1)
318 prevComment = foldComment && IsCommentLine(lineCurrent - 1, styler);
319
320 // Process all characters to end of requested range or end of any triple quote
321 // or comment that hangs over the end of the range. Cap processing in all cases
322 // to end of document (in case of unclosed quote or comment at end).
323 while ((lineCurrent <= docLines) && ((lineCurrent <= maxLines) ||
324 prevQuote || prevComment)) {
325
326 // Gather info
327 int lev = indentCurrent;
328 int lineNext = lineCurrent + 1;
329 int indentNext = indentCurrent;
330 int quote = false;
331 if (lineNext <= docLines) {
332 // Information about next line is only available if not at end of document
333 indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
334 int style = styler.StyleAt(styler.LineStart(lineNext)) & 31;
335 quote = foldQuotes && ((style == SCE_P_TRIPLE) || (style == SCE_P_TRIPLEDOUBLE));
336 }
337 const int quote_start = (quote && !prevQuote);
338 const int quote_continue = (quote && prevQuote);
339 const int comment = foldComment && IsCommentLine(lineCurrent, styler);
340 const int comment_start = (comment && !prevComment && (lineNext <= docLines) &&
341 IsCommentLine(lineNext, styler) &&
342 (lev > SC_FOLDLEVELBASE));
343 const int comment_continue = (comment && prevComment);
344 if ((!quote || !prevQuote) && !comment)
345 indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
346 if (quote)
347 indentNext = indentCurrentLevel;
348 if (indentNext & SC_FOLDLEVELWHITEFLAG)
349 indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
350
351 if (quote_start) {
352 // Place fold point at start of triple quoted string
353 lev |= SC_FOLDLEVELHEADERFLAG;
354 } else if (quote_continue || prevQuote) {
355 // Add level to rest of lines in the string
356 lev = lev + 1;
357 } else if (comment_start) {
358 // Place fold point at start of a block of comments
359 lev |= SC_FOLDLEVELHEADERFLAG;
360 } else if (comment_continue) {
361 // Add level to rest of lines in the block
362 lev = lev + 1;
363 }
364
365 // Skip past any blank lines for next indent level info; we skip also
366 // comments (all comments, not just those starting in column 0)
367 // which effectively folds them into surrounding code rather
368 // than screwing up folding.
369
370 while (!quote &&
371 (lineNext < docLines) &&
372 ((indentNext & SC_FOLDLEVELWHITEFLAG) ||
373 (lineNext <= docLines && IsCommentLine(lineNext, styler)))) {
374
375 lineNext++;
376 indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
377 }
378
379 const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK;
380 const int levelBeforeComments =
381 Platform::Maximum(indentCurrentLevel,levelAfterComments);
382
383 // Now set all the indent levels on the lines we skipped
384 // Do this from end to start. Once we encounter one line
385 // which is indented more than the line after the end of
386 // the comment-block, use the level of the block before
387
388 int skipLine = lineNext;
389 int skipLevel = levelAfterComments;
390
391 while (--skipLine > lineCurrent) {
392 int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL);
393
394 if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments)
395 skipLevel = levelBeforeComments;
396
397 int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
398
399 styler.SetLevel(skipLine, skipLevel | whiteFlag);
400 }
401
402 // Set fold header on non-quote/non-comment line
403 if (!quote && !comment && !(indentCurrent & SC_FOLDLEVELWHITEFLAG) ) {
404 if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) <
405 (indentNext & SC_FOLDLEVELNUMBERMASK))
406 lev |= SC_FOLDLEVELHEADERFLAG;
407 }
408
409 // Keep track of triple quote and block comment state of previous line
410 prevQuote = quote;
411 prevComment = comment_start || comment_continue;
412
413 // Set fold level for this line and move to next line
414 styler.SetLevel(lineCurrent, lev);
415 indentCurrent = indentNext;
416 lineCurrent = lineNext;
417 }
418
419 // NOTE: Cannot set level of last line here because indentCurrent doesn't have
420 // header flag set; the loop above is crafted to take care of this case!
421 //styler.SetLevel(lineCurrent, indentCurrent);
422 }
423
424 static const char * const nimrodWordListDesc[] = {
425 "Keywords",
426 0
427 };
428
429 LexerModule lmNimrod(SCLEX_NIMROD, ColouriseNimrodDoc, "nimrod", FoldNimrodDoc,
430 nimrodWordListDesc);