]>
Commit | Line | Data |
---|---|---|
9e730a78 RD |
1 | // Scintilla source code edit control |
2 | /** @file LexAda.cxx | |
3 | ** Lexer for Ada 95 | |
4 | **/ | |
5 | // Copyright 2002 by Sergey Koshcheyev <sergey.k@seznam.cz> | |
65ec6247 RD |
6 | // The License.txt file describes the conditions under which this software may be distributed. |
7 | ||
9e730a78 RD |
8 | #include <stdlib.h> |
9 | #include <ctype.h> | |
10 | #include <string.h> | |
11 | #include <stdio.h> | |
65ec6247 RD |
12 | |
13 | #include "Platform.h" | |
14 | ||
65ec6247 | 15 | #include "Accessor.h" |
9e730a78 RD |
16 | #include "StyleContext.h" |
17 | #include "PropSet.h" | |
65ec6247 | 18 | #include "KeyWords.h" |
65ec6247 | 19 | #include "SciLexer.h" |
9e730a78 RD |
20 | #include "SString.h" |
21 | ||
22 | /* | |
23 | * Interface | |
24 | */ | |
25 | ||
26 | static void ColouriseDocument( | |
27 | unsigned int startPos, | |
28 | int length, | |
29 | int initStyle, | |
30 | WordList *keywordlists[], | |
31 | Accessor &styler); | |
32 | ||
33 | static const char * const adaWordListDesc[] = { | |
34 | "Keywords", | |
35 | 0 | |
36 | }; | |
37 | ||
38 | LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc); | |
39 | ||
40 | /* | |
41 | * Implementation | |
42 | */ | |
43 | ||
44 | // Functions that have apostropheStartsAttribute as a parameter set it according to whether | |
45 | // an apostrophe encountered after processing the current token will start an attribute or | |
46 | // a character literal. | |
47 | static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute); | |
48 | static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute); | |
49 | static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL); | |
50 | static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute); | |
51 | static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute); | |
52 | static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute); | |
53 | static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute); | |
54 | static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute); | |
55 | static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute); | |
56 | ||
57 | static inline bool IsDelimiterCharacter(int ch); | |
58 | static inline bool IsNumberStartCharacter(int ch); | |
59 | static inline bool IsNumberCharacter(int ch); | |
60 | static inline bool IsSeparatorOrDelimiterCharacter(int ch); | |
61 | static bool IsValidIdentifier(const SString& identifier); | |
62 | static bool IsValidNumber(const SString& number); | |
63 | static inline bool IsWordStartCharacter(int ch); | |
64 | static inline bool IsWordCharacter(int ch); | |
65ec6247 | 65 | |
9e730a78 RD |
66 | static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) { |
67 | apostropheStartsAttribute = true; | |
65ec6247 | 68 | |
9e730a78 | 69 | sc.SetState(SCE_ADA_CHARACTER); |
65ec6247 | 70 | |
9e730a78 RD |
71 | // Skip the apostrophe and one more character (so that '' is shown as non-terminated and ''' |
72 | // is handled correctly) | |
73 | sc.Forward(); | |
74 | sc.Forward(); | |
75 | ||
76 | ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL); | |
77 | } | |
78 | ||
79 | static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) { | |
80 | while (!sc.atLineEnd && !sc.Match(chEnd)) { | |
81 | sc.Forward(); | |
65ec6247 | 82 | } |
65ec6247 | 83 | |
9e730a78 RD |
84 | if (!sc.atLineEnd) { |
85 | sc.ForwardSetState(SCE_ADA_DEFAULT); | |
86 | } else { | |
87 | sc.ChangeState(stateEOL); | |
65ec6247 | 88 | } |
65ec6247 RD |
89 | } |
90 | ||
9e730a78 RD |
91 | static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) { |
92 | // Apostrophe meaning is not changed, but the parameter is present for uniformity | |
65ec6247 | 93 | |
9e730a78 RD |
94 | sc.SetState(SCE_ADA_COMMENTLINE); |
95 | ||
96 | while (!sc.atLineEnd) { | |
97 | sc.Forward(); | |
98 | } | |
99 | } | |
100 | ||
101 | static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) { | |
102 | apostropheStartsAttribute = sc.Match (')'); | |
103 | sc.SetState(SCE_ADA_DELIMITER); | |
104 | sc.ForwardSetState(SCE_ADA_DEFAULT); | |
105 | } | |
106 | ||
107 | static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) { | |
108 | apostropheStartsAttribute = false; | |
109 | ||
110 | sc.SetState(SCE_ADA_LABEL); | |
111 | ||
112 | // Skip "<<" | |
113 | sc.Forward(); | |
114 | sc.Forward(); | |
115 | ||
116 | SString identifier; | |
117 | ||
118 | while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) { | |
119 | identifier += static_cast<char>(tolower(sc.ch)); | |
120 | sc.Forward(); | |
121 | } | |
122 | ||
123 | // Skip ">>" | |
124 | if (sc.Match('>', '>')) { | |
125 | sc.Forward(); | |
126 | sc.Forward(); | |
127 | } else { | |
128 | sc.ChangeState(SCE_ADA_ILLEGAL); | |
129 | } | |
130 | ||
131 | // If the name is an invalid identifier or a keyword, then make it invalid label | |
132 | if (!IsValidIdentifier(identifier) || keywords.InList(identifier.c_str())) { | |
133 | sc.ChangeState(SCE_ADA_ILLEGAL); | |
134 | } | |
135 | ||
136 | sc.SetState(SCE_ADA_DEFAULT); | |
137 | ||
138 | } | |
139 | ||
140 | static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) { | |
141 | apostropheStartsAttribute = true; | |
142 | ||
143 | SString number; | |
144 | sc.SetState(SCE_ADA_NUMBER); | |
145 | ||
146 | // Get all characters up to a delimiter or a separator, including points, but excluding | |
147 | // double points (ranges). | |
148 | while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) { | |
149 | number += static_cast<char>(sc.ch); | |
150 | sc.Forward(); | |
151 | } | |
152 | ||
153 | // Special case: exponent with sign | |
154 | if ((sc.chPrev == 'e' || sc.chPrev == 'E') && | |
155 | (sc.ch == '+' || sc.ch == '-')) { | |
156 | number += static_cast<char>(sc.ch); | |
157 | sc.Forward (); | |
158 | ||
159 | while (!IsSeparatorOrDelimiterCharacter(sc.ch)) { | |
160 | number += static_cast<char>(sc.ch); | |
161 | sc.Forward(); | |
162 | } | |
163 | } | |
164 | ||
165 | if (!IsValidNumber(number)) { | |
166 | sc.ChangeState(SCE_ADA_ILLEGAL); | |
167 | } | |
168 | ||
169 | sc.SetState(SCE_ADA_DEFAULT); | |
170 | } | |
171 | ||
172 | static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) { | |
173 | apostropheStartsAttribute = true; | |
174 | ||
175 | sc.SetState(SCE_ADA_STRING); | |
176 | sc.Forward(); | |
177 | ||
178 | ColouriseContext(sc, '"', SCE_ADA_STRINGEOL); | |
179 | } | |
180 | ||
181 | static void ColouriseWhiteSpace(StyleContext& sc, bool& /*apostropheStartsAttribute*/) { | |
182 | // Apostrophe meaning is not changed, but the parameter is present for uniformity | |
183 | sc.SetState(SCE_ADA_DEFAULT); | |
184 | sc.ForwardSetState(SCE_ADA_DEFAULT); | |
185 | } | |
186 | ||
187 | static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) { | |
188 | apostropheStartsAttribute = true; | |
189 | sc.SetState(SCE_ADA_IDENTIFIER); | |
190 | ||
191 | SString word; | |
192 | ||
193 | while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) { | |
194 | word += static_cast<char>(tolower(sc.ch)); | |
195 | sc.Forward(); | |
196 | } | |
197 | ||
198 | if (!IsValidIdentifier(word)) { | |
199 | sc.ChangeState(SCE_ADA_ILLEGAL); | |
200 | ||
201 | } else if (keywords.InList(word.c_str())) { | |
202 | sc.ChangeState(SCE_ADA_WORD); | |
203 | ||
204 | if (word != "all") { | |
205 | apostropheStartsAttribute = false; | |
206 | } | |
207 | } | |
208 | ||
209 | sc.SetState(SCE_ADA_DEFAULT); | |
210 | } | |
211 | ||
212 | // | |
213 | // ColouriseDocument | |
214 | // | |
215 | ||
216 | static void ColouriseDocument( | |
217 | unsigned int startPos, | |
218 | int length, | |
219 | int initStyle, | |
220 | WordList *keywordlists[], | |
221 | Accessor &styler) { | |
65ec6247 | 222 | WordList &keywords = *keywordlists[0]; |
9e730a78 RD |
223 | |
224 | StyleContext sc(startPos, length, initStyle, styler); | |
225 | ||
226 | int lineCurrent = styler.GetLine(startPos); | |
227 | bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0; | |
228 | ||
229 | while (sc.More()) { | |
230 | if (sc.atLineEnd) { | |
231 | // Go to the next line | |
232 | sc.Forward(); | |
233 | lineCurrent++; | |
234 | ||
235 | // Remember the line state for future incremental lexing | |
236 | styler.SetLineState(lineCurrent, apostropheStartsAttribute); | |
237 | ||
238 | // Don't continue any styles on the next line | |
239 | sc.SetState(SCE_ADA_DEFAULT); | |
65ec6247 | 240 | } |
65ec6247 | 241 | |
9e730a78 RD |
242 | // Comments |
243 | if (sc.Match('-', '-')) { | |
244 | ColouriseComment(sc, apostropheStartsAttribute); | |
245 | ||
246 | // Strings | |
247 | } else if (sc.Match('"')) { | |
248 | ColouriseString(sc, apostropheStartsAttribute); | |
249 | ||
250 | // Characters | |
251 | } else if (sc.Match('\'') && !apostropheStartsAttribute) { | |
252 | ColouriseCharacter(sc, apostropheStartsAttribute); | |
253 | ||
254 | // Labels | |
255 | } else if (sc.Match('<', '<')) { | |
256 | ColouriseLabel(sc, keywords, apostropheStartsAttribute); | |
257 | ||
258 | // Whitespace | |
a33203cb | 259 | } else if (IsASpace(sc.ch)) { |
9e730a78 RD |
260 | ColouriseWhiteSpace(sc, apostropheStartsAttribute); |
261 | ||
262 | // Delimiters | |
263 | } else if (IsDelimiterCharacter(sc.ch)) { | |
264 | ColouriseDelimiter(sc, apostropheStartsAttribute); | |
265 | ||
266 | // Numbers | |
a33203cb | 267 | } else if (IsADigit(sc.ch) || sc.ch == '#') { |
9e730a78 RD |
268 | ColouriseNumber(sc, apostropheStartsAttribute); |
269 | ||
270 | // Keywords or identifiers | |
271 | } else { | |
272 | ColouriseWord(sc, keywords, apostropheStartsAttribute); | |
273 | } | |
274 | } | |
275 | ||
276 | sc.Complete(); | |
277 | } | |
278 | ||
279 | static inline bool IsDelimiterCharacter(int ch) { | |
280 | switch (ch) { | |
281 | case '&': | |
282 | case '\'': | |
283 | case '(': | |
284 | case ')': | |
285 | case '*': | |
286 | case '+': | |
287 | case ',': | |
288 | case '-': | |
289 | case '.': | |
290 | case '/': | |
291 | case ':': | |
292 | case ';': | |
293 | case '<': | |
294 | case '=': | |
295 | case '>': | |
296 | case '|': | |
297 | return true; | |
298 | default: | |
299 | return false; | |
300 | } | |
301 | } | |
302 | ||
303 | static inline bool IsNumberCharacter(int ch) { | |
304 | return IsNumberStartCharacter(ch) || | |
305 | ch == '_' || | |
306 | ch == '.' || | |
307 | ch == '#' || | |
308 | (ch >= 'a' && ch <= 'f') || | |
309 | (ch >= 'A' && ch <= 'F'); | |
310 | } | |
311 | ||
312 | static inline bool IsNumberStartCharacter(int ch) { | |
a33203cb | 313 | return IsADigit(ch); |
9e730a78 RD |
314 | } |
315 | ||
316 | static inline bool IsSeparatorOrDelimiterCharacter(int ch) { | |
a33203cb | 317 | return IsASpace(ch) || IsDelimiterCharacter(ch); |
9e730a78 RD |
318 | } |
319 | ||
320 | static bool IsValidIdentifier(const SString& identifier) { | |
321 | // First character can't be '_', so initialize the flag to true | |
322 | bool lastWasUnderscore = true; | |
323 | ||
88a8b04e | 324 | size_t length = identifier.length(); |
9e730a78 RD |
325 | |
326 | // Zero-length identifiers are not valid (these can occur inside labels) | |
327 | if (length == 0) { | |
328 | return false; | |
329 | } | |
330 | ||
331 | // Check for valid character at the start | |
332 | if (!IsWordStartCharacter(identifier[0])) { | |
333 | return false; | |
334 | } | |
335 | ||
336 | // Check for only valid characters and no double underscores | |
88a8b04e | 337 | for (size_t i = 0; i < length; i++) { |
9e730a78 RD |
338 | if (!IsWordCharacter(identifier[i]) || |
339 | (identifier[i] == '_' && lastWasUnderscore)) { | |
340 | return false; | |
65ec6247 | 341 | } |
9e730a78 RD |
342 | lastWasUnderscore = identifier[i] == '_'; |
343 | } | |
344 | ||
345 | // Check for underscore at the end | |
346 | if (lastWasUnderscore == true) { | |
347 | return false; | |
348 | } | |
65ec6247 | 349 | |
9e730a78 RD |
350 | // All checks passed |
351 | return true; | |
352 | } | |
353 | ||
354 | static bool IsValidNumber(const SString& number) { | |
355 | int hashPos = number.search("#"); | |
356 | bool seenDot = false; | |
357 | ||
88a8b04e RD |
358 | size_t i = 0; |
359 | size_t length = number.length(); | |
9e730a78 RD |
360 | |
361 | if (length == 0) | |
362 | return false; // Just in case | |
363 | ||
364 | // Decimal number | |
365 | if (hashPos == -1) { | |
366 | bool canBeSpecial = false; | |
367 | ||
368 | for (; i < length; i++) { | |
369 | if (number[i] == '_') { | |
370 | if (!canBeSpecial) { | |
371 | return false; | |
372 | } | |
373 | canBeSpecial = false; | |
374 | } else if (number[i] == '.') { | |
375 | if (!canBeSpecial || seenDot) { | |
376 | return false; | |
377 | } | |
378 | canBeSpecial = false; | |
379 | seenDot = true; | |
a33203cb | 380 | } else if (IsADigit(number[i])) { |
9e730a78 RD |
381 | canBeSpecial = true; |
382 | } else { | |
383 | break; | |
65ec6247 | 384 | } |
9e730a78 RD |
385 | } |
386 | ||
387 | if (!canBeSpecial) | |
388 | return false; | |
389 | } else { | |
390 | // Based number | |
391 | bool canBeSpecial = false; | |
392 | int base = 0; | |
393 | ||
394 | // Parse base | |
395 | for (; i < length; i++) { | |
396 | int ch = number[i]; | |
397 | if (ch == '_') { | |
398 | if (!canBeSpecial) | |
399 | return false; | |
400 | canBeSpecial = false; | |
a33203cb | 401 | } else if (IsADigit(ch)) { |
9e730a78 RD |
402 | base = base * 10 + (ch - '0'); |
403 | if (base > 16) | |
404 | return false; | |
405 | canBeSpecial = true; | |
406 | } else if (ch == '#' && canBeSpecial) { | |
407 | break; | |
408 | } else { | |
409 | return false; | |
65ec6247 | 410 | } |
9e730a78 RD |
411 | } |
412 | ||
413 | if (base < 2) | |
414 | return false; | |
415 | if (i == length) | |
416 | return false; | |
417 | ||
418 | i++; // Skip over '#' | |
419 | ||
420 | // Parse number | |
421 | canBeSpecial = false; | |
422 | ||
423 | for (; i < length; i++) { | |
424 | int ch = tolower(number[i]); | |
425 | ||
426 | if (ch == '_') { | |
427 | if (!canBeSpecial) { | |
428 | return false; | |
429 | } | |
430 | canBeSpecial = false; | |
431 | ||
432 | } else if (ch == '.') { | |
433 | if (!canBeSpecial || seenDot) { | |
434 | return false; | |
435 | } | |
436 | canBeSpecial = false; | |
437 | seenDot = true; | |
438 | ||
a33203cb | 439 | } else if (IsADigit(ch)) { |
9e730a78 RD |
440 | if (ch - '0' >= base) { |
441 | return false; | |
442 | } | |
443 | canBeSpecial = true; | |
444 | ||
445 | } else if (ch >= 'a' && ch <= 'f') { | |
446 | if (ch - 'a' + 10 >= base) { | |
447 | return false; | |
65ec6247 | 448 | } |
9e730a78 RD |
449 | canBeSpecial = true; |
450 | ||
451 | } else if (ch == '#' && canBeSpecial) { | |
452 | break; | |
453 | ||
454 | } else { | |
455 | return false; | |
65ec6247 | 456 | } |
9e730a78 RD |
457 | } |
458 | ||
459 | if (i == length) { | |
460 | return false; | |
461 | } | |
462 | ||
463 | i++; | |
464 | } | |
465 | ||
466 | // Exponent (optional) | |
467 | if (i < length) { | |
468 | if (number[i] != 'e' && number[i] != 'E') | |
469 | return false; | |
470 | ||
471 | i++; // Move past 'E' | |
472 | ||
473 | if (i == length) { | |
474 | return false; | |
475 | } | |
476 | ||
477 | if (number[i] == '+') | |
478 | i++; | |
479 | else if (number[i] == '-') { | |
480 | if (seenDot) { | |
481 | i++; | |
482 | } else { | |
483 | return false; // Integer literals should not have negative exponents | |
65ec6247 | 484 | } |
9e730a78 RD |
485 | } |
486 | ||
487 | if (i == length) { | |
488 | return false; | |
489 | } | |
490 | ||
491 | bool canBeSpecial = false; | |
492 | ||
493 | for (; i < length; i++) { | |
494 | if (number[i] == '_') { | |
495 | if (!canBeSpecial) { | |
496 | return false; | |
497 | } | |
498 | canBeSpecial = false; | |
a33203cb | 499 | } else if (IsADigit(number[i])) { |
9e730a78 RD |
500 | canBeSpecial = true; |
501 | } else { | |
502 | return false; | |
65ec6247 RD |
503 | } |
504 | } | |
505 | ||
9e730a78 RD |
506 | if (!canBeSpecial) |
507 | return false; | |
65ec6247 | 508 | } |
65ec6247 | 509 | |
9e730a78 RD |
510 | // if i == length, number was parsed successfully. |
511 | return i == length; | |
512 | } | |
513 | ||
514 | static inline bool IsWordCharacter(int ch) { | |
a33203cb | 515 | return IsWordStartCharacter(ch) || IsADigit(ch); |
65ec6247 RD |
516 | } |
517 | ||
9e730a78 | 518 | static inline bool IsWordStartCharacter(int ch) { |
a33203cb | 519 | return (isascii(ch) && isalpha(ch)) || ch == '_'; |
9e730a78 | 520 | } |