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