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