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