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 /*
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);
65
ColouriseCharacter(StyleContext & sc,bool & apostropheStartsAttribute)66 static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) {
67 apostropheStartsAttribute = true;
68
69 sc.SetState(SCE_ADA_CHARACTER);
70
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
ColouriseContext(StyleContext & sc,char chEnd,int stateEOL)79 static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) {
80 while (!sc.atLineEnd && !sc.Match(chEnd)) {
81 sc.Forward();
82 }
83
84 if (!sc.atLineEnd) {
85 sc.ForwardSetState(SCE_ADA_DEFAULT);
86 } else {
87 sc.ChangeState(stateEOL);
88 }
89 }
90
ColouriseComment(StyleContext & sc,bool &)91 static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
92 // Apostrophe meaning is not changed, but the parameter is present for uniformity
93
94 sc.SetState(SCE_ADA_COMMENTLINE);
95
96 while (!sc.atLineEnd) {
97 sc.Forward();
98 }
99 }
100
ColouriseDelimiter(StyleContext & sc,bool & apostropheStartsAttribute)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
ColouriseLabel(StyleContext & sc,WordList & keywords,bool & apostropheStartsAttribute)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
ColouriseNumber(StyleContext & sc,bool & apostropheStartsAttribute)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
ColouriseString(StyleContext & sc,bool & apostropheStartsAttribute)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
ColouriseWhiteSpace(StyleContext & sc,bool &)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
ColouriseWord(StyleContext & sc,WordList & keywords,bool & apostropheStartsAttribute)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
ColouriseDocument(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)216 static void ColouriseDocument(
217 unsigned int startPos,
218 int length,
219 int initStyle,
220 WordList *keywordlists[],
221 Accessor &styler) {
222 WordList &keywords = *keywordlists[0];
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);
240 }
241
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
259 } else if (IsASpace(sc.ch)) {
260 ColouriseWhiteSpace(sc, apostropheStartsAttribute);
261
262 // Delimiters
263 } else if (IsDelimiterCharacter(sc.ch)) {
264 ColouriseDelimiter(sc, apostropheStartsAttribute);
265
266 // Numbers
267 } else if (IsADigit(sc.ch) || sc.ch == '#') {
268 ColouriseNumber(sc, apostropheStartsAttribute);
269
270 // Keywords or identifiers
271 } else {
272 ColouriseWord(sc, keywords, apostropheStartsAttribute);
273 }
274 }
275
276 sc.Complete();
277 }
278
IsDelimiterCharacter(int ch)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
IsNumberCharacter(int ch)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
IsNumberStartCharacter(int ch)312 static inline bool IsNumberStartCharacter(int ch) {
313 return IsADigit(ch);
314 }
315
IsSeparatorOrDelimiterCharacter(int ch)316 static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
317 return IsASpace(ch) || IsDelimiterCharacter(ch);
318 }
319
IsValidIdentifier(const SString & identifier)320 static bool IsValidIdentifier(const SString& identifier) {
321 // First character can't be '_', so initialize the flag to true
322 bool lastWasUnderscore = true;
323
324 size_t length = identifier.length();
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
337 for (size_t i = 0; i < length; i++) {
338 if (!IsWordCharacter(identifier[i]) ||
339 (identifier[i] == '_' && lastWasUnderscore)) {
340 return false;
341 }
342 lastWasUnderscore = identifier[i] == '_';
343 }
344
345 // Check for underscore at the end
346 if (lastWasUnderscore == true) {
347 return false;
348 }
349
350 // All checks passed
351 return true;
352 }
353
IsValidNumber(const SString & number)354 static bool IsValidNumber(const SString& number) {
355 int hashPos = number.search("#");
356 bool seenDot = false;
357
358 size_t i = 0;
359 size_t length = number.length();
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;
380 } else if (IsADigit(number[i])) {
381 canBeSpecial = true;
382 } else {
383 break;
384 }
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;
401 } else if (IsADigit(ch)) {
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;
410 }
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
439 } else if (IsADigit(ch)) {
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;
448 }
449 canBeSpecial = true;
450
451 } else if (ch == '#' && canBeSpecial) {
452 break;
453
454 } else {
455 return false;
456 }
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
484 }
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;
499 } else if (IsADigit(number[i])) {
500 canBeSpecial = true;
501 } else {
502 return false;
503 }
504 }
505
506 if (!canBeSpecial)
507 return false;
508 }
509
510 // if i == length, number was parsed successfully.
511 return i == length;
512 }
513
IsWordCharacter(int ch)514 static inline bool IsWordCharacter(int ch) {
515 return IsWordStartCharacter(ch) || IsADigit(ch);
516 }
517
IsWordStartCharacter(int ch)518 static inline bool IsWordStartCharacter(int ch) {
519 return (isascii(ch) && isalpha(ch)) || ch == '_';
520 }
521