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