Update Scintilla to version 3.5.7
[TortoiseGit.git] / ext / scintilla / lexers / LexAda.cxx
blob13aba46e7911a84cd8e36d1072d1fdd6e096bf62
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.
8 #include <stdlib.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include <stdarg.h>
12 #include <assert.h>
13 #include <ctype.h>
15 #include <string>
17 #include "ILexer.h"
18 #include "Scintilla.h"
19 #include "SciLexer.h"
21 #include "WordList.h"
22 #include "LexAccessor.h"
23 #include "Accessor.h"
24 #include "StyleContext.h"
25 #include "CharacterSet.h"
26 #include "LexerModule.h"
28 #ifdef SCI_NAMESPACE
29 using namespace Scintilla;
30 #endif
33 * Interface
36 static void ColouriseDocument(
37 unsigned int startPos,
38 int length,
39 int initStyle,
40 WordList *keywordlists[],
41 Accessor &styler);
43 static const char * const adaWordListDesc[] = {
44 "Keywords",
48 LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc);
51 * Implementation
54 // Functions that have apostropheStartsAttribute as a parameter set it according to whether
55 // an apostrophe encountered after processing the current token will start an attribute or
56 // a character literal.
57 static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute);
58 static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
59 static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL);
60 static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
61 static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
62 static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
63 static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute);
64 static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
65 static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
67 static inline bool IsDelimiterCharacter(int ch);
68 static inline bool IsSeparatorOrDelimiterCharacter(int ch);
69 static bool IsValidIdentifier(const std::string& identifier);
70 static bool IsValidNumber(const std::string& number);
71 static inline bool IsWordStartCharacter(int ch);
72 static inline bool IsWordCharacter(int ch);
74 static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) {
75 apostropheStartsAttribute = true;
77 sc.SetState(SCE_ADA_CHARACTER);
79 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
80 // is handled correctly)
81 sc.Forward();
82 sc.Forward();
84 ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL);
87 static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) {
88 while (!sc.atLineEnd && !sc.Match(chEnd)) {
89 sc.Forward();
92 if (!sc.atLineEnd) {
93 sc.ForwardSetState(SCE_ADA_DEFAULT);
94 } else {
95 sc.ChangeState(stateEOL);
99 static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
100 // Apostrophe meaning is not changed, but the parameter is present for uniformity
102 sc.SetState(SCE_ADA_COMMENTLINE);
104 while (!sc.atLineEnd) {
105 sc.Forward();
109 static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
110 apostropheStartsAttribute = sc.Match (')');
111 sc.SetState(SCE_ADA_DELIMITER);
112 sc.ForwardSetState(SCE_ADA_DEFAULT);
115 static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
116 apostropheStartsAttribute = false;
118 sc.SetState(SCE_ADA_LABEL);
120 // Skip "<<"
121 sc.Forward();
122 sc.Forward();
124 std::string identifier;
126 while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
127 identifier += static_cast<char>(tolower(sc.ch));
128 sc.Forward();
131 // Skip ">>"
132 if (sc.Match('>', '>')) {
133 sc.Forward();
134 sc.Forward();
135 } else {
136 sc.ChangeState(SCE_ADA_ILLEGAL);
139 // If the name is an invalid identifier or a keyword, then make it invalid label
140 if (!IsValidIdentifier(identifier) || keywords.InList(identifier.c_str())) {
141 sc.ChangeState(SCE_ADA_ILLEGAL);
144 sc.SetState(SCE_ADA_DEFAULT);
148 static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
149 apostropheStartsAttribute = true;
151 std::string number;
152 sc.SetState(SCE_ADA_NUMBER);
154 // Get all characters up to a delimiter or a separator, including points, but excluding
155 // double points (ranges).
156 while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) {
157 number += static_cast<char>(sc.ch);
158 sc.Forward();
161 // Special case: exponent with sign
162 if ((sc.chPrev == 'e' || sc.chPrev == 'E') &&
163 (sc.ch == '+' || sc.ch == '-')) {
164 number += static_cast<char>(sc.ch);
165 sc.Forward ();
167 while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
168 number += static_cast<char>(sc.ch);
169 sc.Forward();
173 if (!IsValidNumber(number)) {
174 sc.ChangeState(SCE_ADA_ILLEGAL);
177 sc.SetState(SCE_ADA_DEFAULT);
180 static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) {
181 apostropheStartsAttribute = true;
183 sc.SetState(SCE_ADA_STRING);
184 sc.Forward();
186 ColouriseContext(sc, '"', SCE_ADA_STRINGEOL);
189 static void ColouriseWhiteSpace(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
190 // Apostrophe meaning is not changed, but the parameter is present for uniformity
191 sc.SetState(SCE_ADA_DEFAULT);
192 sc.ForwardSetState(SCE_ADA_DEFAULT);
195 static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
196 apostropheStartsAttribute = true;
197 sc.SetState(SCE_ADA_IDENTIFIER);
199 std::string word;
201 while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
202 word += static_cast<char>(tolower(sc.ch));
203 sc.Forward();
206 if (!IsValidIdentifier(word)) {
207 sc.ChangeState(SCE_ADA_ILLEGAL);
209 } else if (keywords.InList(word.c_str())) {
210 sc.ChangeState(SCE_ADA_WORD);
212 if (word != "all") {
213 apostropheStartsAttribute = false;
217 sc.SetState(SCE_ADA_DEFAULT);
221 // ColouriseDocument
224 static void ColouriseDocument(
225 unsigned int startPos,
226 int length,
227 int initStyle,
228 WordList *keywordlists[],
229 Accessor &styler) {
230 WordList &keywords = *keywordlists[0];
232 StyleContext sc(startPos, length, initStyle, styler);
234 int lineCurrent = styler.GetLine(startPos);
235 bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
237 while (sc.More()) {
238 if (sc.atLineEnd) {
239 // Go to the next line
240 sc.Forward();
241 lineCurrent++;
243 // Remember the line state for future incremental lexing
244 styler.SetLineState(lineCurrent, apostropheStartsAttribute);
246 // Don't continue any styles on the next line
247 sc.SetState(SCE_ADA_DEFAULT);
250 // Comments
251 if (sc.Match('-', '-')) {
252 ColouriseComment(sc, apostropheStartsAttribute);
254 // Strings
255 } else if (sc.Match('"')) {
256 ColouriseString(sc, apostropheStartsAttribute);
258 // Characters
259 } else if (sc.Match('\'') && !apostropheStartsAttribute) {
260 ColouriseCharacter(sc, apostropheStartsAttribute);
262 // Labels
263 } else if (sc.Match('<', '<')) {
264 ColouriseLabel(sc, keywords, apostropheStartsAttribute);
266 // Whitespace
267 } else if (IsASpace(sc.ch)) {
268 ColouriseWhiteSpace(sc, apostropheStartsAttribute);
270 // Delimiters
271 } else if (IsDelimiterCharacter(sc.ch)) {
272 ColouriseDelimiter(sc, apostropheStartsAttribute);
274 // Numbers
275 } else if (IsADigit(sc.ch) || sc.ch == '#') {
276 ColouriseNumber(sc, apostropheStartsAttribute);
278 // Keywords or identifiers
279 } else {
280 ColouriseWord(sc, keywords, apostropheStartsAttribute);
284 sc.Complete();
287 static inline bool IsDelimiterCharacter(int ch) {
288 switch (ch) {
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 case '>':
304 case '|':
305 return true;
306 default:
307 return false;
311 static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
312 return IsASpace(ch) || IsDelimiterCharacter(ch);
315 static bool IsValidIdentifier(const std::string& identifier) {
316 // First character can't be '_', so initialize the flag to true
317 bool lastWasUnderscore = true;
319 size_t length = identifier.length();
321 // Zero-length identifiers are not valid (these can occur inside labels)
322 if (length == 0) {
323 return false;
326 // Check for valid character at the start
327 if (!IsWordStartCharacter(identifier[0])) {
328 return false;
331 // Check for only valid characters and no double underscores
332 for (size_t i = 0; i < length; i++) {
333 if (!IsWordCharacter(identifier[i]) ||
334 (identifier[i] == '_' && lastWasUnderscore)) {
335 return false;
337 lastWasUnderscore = identifier[i] == '_';
340 // Check for underscore at the end
341 if (lastWasUnderscore == true) {
342 return false;
345 // All checks passed
346 return true;
349 static bool IsValidNumber(const std::string& number) {
350 size_t hashPos = number.find("#");
351 bool seenDot = false;
353 size_t i = 0;
354 size_t length = number.length();
356 if (length == 0)
357 return false; // Just in case
359 // Decimal number
360 if (hashPos == std::string::npos) {
361 bool canBeSpecial = false;
363 for (; i < length; i++) {
364 if (number[i] == '_') {
365 if (!canBeSpecial) {
366 return false;
368 canBeSpecial = false;
369 } else if (number[i] == '.') {
370 if (!canBeSpecial || seenDot) {
371 return false;
373 canBeSpecial = false;
374 seenDot = true;
375 } else if (IsADigit(number[i])) {
376 canBeSpecial = true;
377 } else {
378 break;
382 if (!canBeSpecial)
383 return false;
384 } else {
385 // Based number
386 bool canBeSpecial = false;
387 int base = 0;
389 // Parse base
390 for (; i < length; i++) {
391 int ch = number[i];
392 if (ch == '_') {
393 if (!canBeSpecial)
394 return false;
395 canBeSpecial = false;
396 } else if (IsADigit(ch)) {
397 base = base * 10 + (ch - '0');
398 if (base > 16)
399 return false;
400 canBeSpecial = true;
401 } else if (ch == '#' && canBeSpecial) {
402 break;
403 } else {
404 return false;
408 if (base < 2)
409 return false;
410 if (i == length)
411 return false;
413 i++; // Skip over '#'
415 // Parse number
416 canBeSpecial = false;
418 for (; i < length; i++) {
419 int ch = tolower(number[i]);
421 if (ch == '_') {
422 if (!canBeSpecial) {
423 return false;
425 canBeSpecial = false;
427 } else if (ch == '.') {
428 if (!canBeSpecial || seenDot) {
429 return false;
431 canBeSpecial = false;
432 seenDot = true;
434 } else if (IsADigit(ch)) {
435 if (ch - '0' >= base) {
436 return false;
438 canBeSpecial = true;
440 } else if (ch >= 'a' && ch <= 'f') {
441 if (ch - 'a' + 10 >= base) {
442 return false;
444 canBeSpecial = true;
446 } else if (ch == '#' && canBeSpecial) {
447 break;
449 } else {
450 return false;
454 if (i == length) {
455 return false;
458 i++;
461 // Exponent (optional)
462 if (i < length) {
463 if (number[i] != 'e' && number[i] != 'E')
464 return false;
466 i++; // Move past 'E'
468 if (i == length) {
469 return false;
472 if (number[i] == '+')
473 i++;
474 else if (number[i] == '-') {
475 if (seenDot) {
476 i++;
477 } else {
478 return false; // Integer literals should not have negative exponents
482 if (i == length) {
483 return false;
486 bool canBeSpecial = false;
488 for (; i < length; i++) {
489 if (number[i] == '_') {
490 if (!canBeSpecial) {
491 return false;
493 canBeSpecial = false;
494 } else if (IsADigit(number[i])) {
495 canBeSpecial = true;
496 } else {
497 return false;
501 if (!canBeSpecial)
502 return false;
505 // if i == length, number was parsed successfully.
506 return i == length;
509 static inline bool IsWordCharacter(int ch) {
510 return IsWordStartCharacter(ch) || IsADigit(ch);
513 static inline bool IsWordStartCharacter(int ch) {
514 return (IsASCII(ch) && isalpha(ch)) || ch == '_';