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