Add an UI to enable/disable specific overlay handlers.
[TortoiseGit.git] / ext / scintilla / src / LexAda.cxx
blob9d1b45702e13b8ef4d748f2372b81db113e73b89
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 <ctype.h>
10 #include <string.h>
11 #include <stdio.h>
13 #include "Platform.h"
15 #include "Accessor.h"
16 #include "StyleContext.h"
17 #include "PropSet.h"
18 #include "KeyWords.h"
19 #include "SciLexer.h"
20 #include "SString.h"
22 #ifdef SCI_NAMESPACE
23 using namespace Scintilla;
24 #endif
27 * Interface
30 static void ColouriseDocument(
31 unsigned int startPos,
32 int length,
33 int initStyle,
34 WordList *keywordlists[],
35 Accessor &styler);
37 static const char * const adaWordListDesc[] = {
38 "Keywords",
42 LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc);
45 * Implementation
48 // Functions that have apostropheStartsAttribute as a parameter set it according to whether
49 // an apostrophe encountered after processing the current token will start an attribute or
50 // a character literal.
51 static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute);
52 static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
53 static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL);
54 static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
55 static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
56 static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
57 static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute);
58 static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
59 static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
61 static inline bool IsDelimiterCharacter(int ch);
62 static inline bool IsNumberStartCharacter(int ch);
63 static inline bool IsNumberCharacter(int ch);
64 static inline bool IsSeparatorOrDelimiterCharacter(int ch);
65 static bool IsValidIdentifier(const SString& identifier);
66 static bool IsValidNumber(const SString& number);
67 static inline bool IsWordStartCharacter(int ch);
68 static inline bool IsWordCharacter(int ch);
70 static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) {
71 apostropheStartsAttribute = true;
73 sc.SetState(SCE_ADA_CHARACTER);
75 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
76 // is handled correctly)
77 sc.Forward();
78 sc.Forward();
80 ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL);
83 static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) {
84 while (!sc.atLineEnd && !sc.Match(chEnd)) {
85 sc.Forward();
88 if (!sc.atLineEnd) {
89 sc.ForwardSetState(SCE_ADA_DEFAULT);
90 } else {
91 sc.ChangeState(stateEOL);
95 static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
96 // Apostrophe meaning is not changed, but the parameter is present for uniformity
98 sc.SetState(SCE_ADA_COMMENTLINE);
100 while (!sc.atLineEnd) {
101 sc.Forward();
105 static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
106 apostropheStartsAttribute = sc.Match (')');
107 sc.SetState(SCE_ADA_DELIMITER);
108 sc.ForwardSetState(SCE_ADA_DEFAULT);
111 static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
112 apostropheStartsAttribute = false;
114 sc.SetState(SCE_ADA_LABEL);
116 // Skip "<<"
117 sc.Forward();
118 sc.Forward();
120 SString identifier;
122 while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
123 identifier += static_cast<char>(tolower(sc.ch));
124 sc.Forward();
127 // Skip ">>"
128 if (sc.Match('>', '>')) {
129 sc.Forward();
130 sc.Forward();
131 } else {
132 sc.ChangeState(SCE_ADA_ILLEGAL);
135 // If the name is an invalid identifier or a keyword, then make it invalid label
136 if (!IsValidIdentifier(identifier) || keywords.InList(identifier.c_str())) {
137 sc.ChangeState(SCE_ADA_ILLEGAL);
140 sc.SetState(SCE_ADA_DEFAULT);
144 static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
145 apostropheStartsAttribute = true;
147 SString number;
148 sc.SetState(SCE_ADA_NUMBER);
150 // Get all characters up to a delimiter or a separator, including points, but excluding
151 // double points (ranges).
152 while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) {
153 number += static_cast<char>(sc.ch);
154 sc.Forward();
157 // Special case: exponent with sign
158 if ((sc.chPrev == 'e' || sc.chPrev == 'E') &&
159 (sc.ch == '+' || sc.ch == '-')) {
160 number += static_cast<char>(sc.ch);
161 sc.Forward ();
163 while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
164 number += static_cast<char>(sc.ch);
165 sc.Forward();
169 if (!IsValidNumber(number)) {
170 sc.ChangeState(SCE_ADA_ILLEGAL);
173 sc.SetState(SCE_ADA_DEFAULT);
176 static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) {
177 apostropheStartsAttribute = true;
179 sc.SetState(SCE_ADA_STRING);
180 sc.Forward();
182 ColouriseContext(sc, '"', SCE_ADA_STRINGEOL);
185 static void ColouriseWhiteSpace(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
186 // Apostrophe meaning is not changed, but the parameter is present for uniformity
187 sc.SetState(SCE_ADA_DEFAULT);
188 sc.ForwardSetState(SCE_ADA_DEFAULT);
191 static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
192 apostropheStartsAttribute = true;
193 sc.SetState(SCE_ADA_IDENTIFIER);
195 SString word;
197 while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
198 word += static_cast<char>(tolower(sc.ch));
199 sc.Forward();
202 if (!IsValidIdentifier(word)) {
203 sc.ChangeState(SCE_ADA_ILLEGAL);
205 } else if (keywords.InList(word.c_str())) {
206 sc.ChangeState(SCE_ADA_WORD);
208 if (word != "all") {
209 apostropheStartsAttribute = false;
213 sc.SetState(SCE_ADA_DEFAULT);
217 // ColouriseDocument
220 static void ColouriseDocument(
221 unsigned int startPos,
222 int length,
223 int initStyle,
224 WordList *keywordlists[],
225 Accessor &styler) {
226 WordList &keywords = *keywordlists[0];
228 StyleContext sc(startPos, length, initStyle, styler);
230 int lineCurrent = styler.GetLine(startPos);
231 bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
233 while (sc.More()) {
234 if (sc.atLineEnd) {
235 // Go to the next line
236 sc.Forward();
237 lineCurrent++;
239 // Remember the line state for future incremental lexing
240 styler.SetLineState(lineCurrent, apostropheStartsAttribute);
242 // Don't continue any styles on the next line
243 sc.SetState(SCE_ADA_DEFAULT);
246 // Comments
247 if (sc.Match('-', '-')) {
248 ColouriseComment(sc, apostropheStartsAttribute);
250 // Strings
251 } else if (sc.Match('"')) {
252 ColouriseString(sc, apostropheStartsAttribute);
254 // Characters
255 } else if (sc.Match('\'') && !apostropheStartsAttribute) {
256 ColouriseCharacter(sc, apostropheStartsAttribute);
258 // Labels
259 } else if (sc.Match('<', '<')) {
260 ColouriseLabel(sc, keywords, apostropheStartsAttribute);
262 // Whitespace
263 } else if (IsASpace(sc.ch)) {
264 ColouriseWhiteSpace(sc, apostropheStartsAttribute);
266 // Delimiters
267 } else if (IsDelimiterCharacter(sc.ch)) {
268 ColouriseDelimiter(sc, apostropheStartsAttribute);
270 // Numbers
271 } else if (IsADigit(sc.ch) || sc.ch == '#') {
272 ColouriseNumber(sc, apostropheStartsAttribute);
274 // Keywords or identifiers
275 } else {
276 ColouriseWord(sc, keywords, apostropheStartsAttribute);
280 sc.Complete();
283 static inline bool IsDelimiterCharacter(int ch) {
284 switch (ch) {
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 case '<':
298 case '=':
299 case '>':
300 case '|':
301 return true;
302 default:
303 return false;
307 static inline bool IsNumberCharacter(int ch) {
308 return IsNumberStartCharacter(ch) ||
309 ch == '_' ||
310 ch == '.' ||
311 ch == '#' ||
312 (ch >= 'a' && ch <= 'f') ||
313 (ch >= 'A' && ch <= 'F');
316 static inline bool IsNumberStartCharacter(int ch) {
317 return IsADigit(ch);
320 static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
321 return IsASpace(ch) || IsDelimiterCharacter(ch);
324 static bool IsValidIdentifier(const SString& identifier) {
325 // First character can't be '_', so initialize the flag to true
326 bool lastWasUnderscore = true;
328 size_t length = identifier.length();
330 // Zero-length identifiers are not valid (these can occur inside labels)
331 if (length == 0) {
332 return false;
335 // Check for valid character at the start
336 if (!IsWordStartCharacter(identifier[0])) {
337 return false;
340 // Check for only valid characters and no double underscores
341 for (size_t i = 0; i < length; i++) {
342 if (!IsWordCharacter(identifier[i]) ||
343 (identifier[i] == '_' && lastWasUnderscore)) {
344 return false;
346 lastWasUnderscore = identifier[i] == '_';
349 // Check for underscore at the end
350 if (lastWasUnderscore == true) {
351 return false;
354 // All checks passed
355 return true;
358 static bool IsValidNumber(const SString& number) {
359 int hashPos = number.search("#");
360 bool seenDot = false;
362 size_t i = 0;
363 size_t length = number.length();
365 if (length == 0)
366 return false; // Just in case
368 // Decimal number
369 if (hashPos == -1) {
370 bool canBeSpecial = false;
372 for (; i < length; i++) {
373 if (number[i] == '_') {
374 if (!canBeSpecial) {
375 return false;
377 canBeSpecial = false;
378 } else if (number[i] == '.') {
379 if (!canBeSpecial || seenDot) {
380 return false;
382 canBeSpecial = false;
383 seenDot = true;
384 } else if (IsADigit(number[i])) {
385 canBeSpecial = true;
386 } else {
387 break;
391 if (!canBeSpecial)
392 return false;
393 } else {
394 // Based number
395 bool canBeSpecial = false;
396 int base = 0;
398 // Parse base
399 for (; i < length; i++) {
400 int ch = number[i];
401 if (ch == '_') {
402 if (!canBeSpecial)
403 return false;
404 canBeSpecial = false;
405 } else if (IsADigit(ch)) {
406 base = base * 10 + (ch - '0');
407 if (base > 16)
408 return false;
409 canBeSpecial = true;
410 } else if (ch == '#' && canBeSpecial) {
411 break;
412 } else {
413 return false;
417 if (base < 2)
418 return false;
419 if (i == length)
420 return false;
422 i++; // Skip over '#'
424 // Parse number
425 canBeSpecial = false;
427 for (; i < length; i++) {
428 int ch = tolower(number[i]);
430 if (ch == '_') {
431 if (!canBeSpecial) {
432 return false;
434 canBeSpecial = false;
436 } else if (ch == '.') {
437 if (!canBeSpecial || seenDot) {
438 return false;
440 canBeSpecial = false;
441 seenDot = true;
443 } else if (IsADigit(ch)) {
444 if (ch - '0' >= base) {
445 return false;
447 canBeSpecial = true;
449 } else if (ch >= 'a' && ch <= 'f') {
450 if (ch - 'a' + 10 >= base) {
451 return false;
453 canBeSpecial = true;
455 } else if (ch == '#' && canBeSpecial) {
456 break;
458 } else {
459 return false;
463 if (i == length) {
464 return false;
467 i++;
470 // Exponent (optional)
471 if (i < length) {
472 if (number[i] != 'e' && number[i] != 'E')
473 return false;
475 i++; // Move past 'E'
477 if (i == length) {
478 return false;
481 if (number[i] == '+')
482 i++;
483 else if (number[i] == '-') {
484 if (seenDot) {
485 i++;
486 } else {
487 return false; // Integer literals should not have negative exponents
491 if (i == length) {
492 return false;
495 bool canBeSpecial = false;
497 for (; i < length; i++) {
498 if (number[i] == '_') {
499 if (!canBeSpecial) {
500 return false;
502 canBeSpecial = false;
503 } else if (IsADigit(number[i])) {
504 canBeSpecial = true;
505 } else {
506 return false;
510 if (!canBeSpecial)
511 return false;
514 // if i == length, number was parsed successfully.
515 return i == length;
518 static inline bool IsWordCharacter(int ch) {
519 return IsWordStartCharacter(ch) || IsADigit(ch);
522 static inline bool IsWordStartCharacter(int ch) {
523 return (isascii(ch) && isalpha(ch)) || ch == '_';