1 // Scintilla source code edit control
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.
18 #include "Scintilla.h"
22 #include "LexAccessor.h"
24 #include "StyleContext.h"
25 #include "CharacterSet.h"
26 #include "LexerModule.h"
29 using namespace Scintilla
;
36 static void ColouriseDocument(
37 unsigned int startPos
,
40 WordList
*keywordlists
[],
43 static const char * const adaWordListDesc
[] = {
48 LexerModule
lmAda(SCLEX_ADA
, ColouriseDocument
, "ada", NULL
, adaWordListDesc
);
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 IsNumberStartCharacter(int ch
);
69 static inline bool IsNumberCharacter(int ch
);
70 static inline bool IsSeparatorOrDelimiterCharacter(int ch
);
71 static bool IsValidIdentifier(const std::string
& identifier
);
72 static bool IsValidNumber(const std::string
& number
);
73 static inline bool IsWordStartCharacter(int ch
);
74 static inline bool IsWordCharacter(int ch
);
76 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
77 apostropheStartsAttribute
= true;
79 sc
.SetState(SCE_ADA_CHARACTER
);
81 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
82 // is handled correctly)
86 ColouriseContext(sc
, '\'', SCE_ADA_CHARACTEREOL
);
89 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
) {
90 while (!sc
.atLineEnd
&& !sc
.Match(chEnd
)) {
95 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
97 sc
.ChangeState(stateEOL
);
101 static void ColouriseComment(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
102 // Apostrophe meaning is not changed, but the parameter is present for uniformity
104 sc
.SetState(SCE_ADA_COMMENTLINE
);
106 while (!sc
.atLineEnd
) {
111 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
112 apostropheStartsAttribute
= sc
.Match (')');
113 sc
.SetState(SCE_ADA_DELIMITER
);
114 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
117 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
118 apostropheStartsAttribute
= false;
120 sc
.SetState(SCE_ADA_LABEL
);
126 std::string identifier
;
128 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
129 identifier
+= static_cast<char>(tolower(sc
.ch
));
134 if (sc
.Match('>', '>')) {
138 sc
.ChangeState(SCE_ADA_ILLEGAL
);
141 // If the name is an invalid identifier or a keyword, then make it invalid label
142 if (!IsValidIdentifier(identifier
) || keywords
.InList(identifier
.c_str())) {
143 sc
.ChangeState(SCE_ADA_ILLEGAL
);
146 sc
.SetState(SCE_ADA_DEFAULT
);
150 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
151 apostropheStartsAttribute
= true;
154 sc
.SetState(SCE_ADA_NUMBER
);
156 // Get all characters up to a delimiter or a separator, including points, but excluding
157 // double points (ranges).
158 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
) || (sc
.ch
== '.' && sc
.chNext
!= '.')) {
159 number
+= static_cast<char>(sc
.ch
);
163 // Special case: exponent with sign
164 if ((sc
.chPrev
== 'e' || sc
.chPrev
== 'E') &&
165 (sc
.ch
== '+' || sc
.ch
== '-')) {
166 number
+= static_cast<char>(sc
.ch
);
169 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
170 number
+= static_cast<char>(sc
.ch
);
175 if (!IsValidNumber(number
)) {
176 sc
.ChangeState(SCE_ADA_ILLEGAL
);
179 sc
.SetState(SCE_ADA_DEFAULT
);
182 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
183 apostropheStartsAttribute
= true;
185 sc
.SetState(SCE_ADA_STRING
);
188 ColouriseContext(sc
, '"', SCE_ADA_STRINGEOL
);
191 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
192 // Apostrophe meaning is not changed, but the parameter is present for uniformity
193 sc
.SetState(SCE_ADA_DEFAULT
);
194 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
197 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
198 apostropheStartsAttribute
= true;
199 sc
.SetState(SCE_ADA_IDENTIFIER
);
203 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
204 word
+= static_cast<char>(tolower(sc
.ch
));
208 if (!IsValidIdentifier(word
)) {
209 sc
.ChangeState(SCE_ADA_ILLEGAL
);
211 } else if (keywords
.InList(word
.c_str())) {
212 sc
.ChangeState(SCE_ADA_WORD
);
215 apostropheStartsAttribute
= false;
219 sc
.SetState(SCE_ADA_DEFAULT
);
226 static void ColouriseDocument(
227 unsigned int startPos
,
230 WordList
*keywordlists
[],
232 WordList
&keywords
= *keywordlists
[0];
234 StyleContext
sc(startPos
, length
, initStyle
, styler
);
236 int lineCurrent
= styler
.GetLine(startPos
);
237 bool apostropheStartsAttribute
= (styler
.GetLineState(lineCurrent
) & 1) != 0;
241 // Go to the next line
245 // Remember the line state for future incremental lexing
246 styler
.SetLineState(lineCurrent
, apostropheStartsAttribute
);
248 // Don't continue any styles on the next line
249 sc
.SetState(SCE_ADA_DEFAULT
);
253 if (sc
.Match('-', '-')) {
254 ColouriseComment(sc
, apostropheStartsAttribute
);
257 } else if (sc
.Match('"')) {
258 ColouriseString(sc
, apostropheStartsAttribute
);
261 } else if (sc
.Match('\'') && !apostropheStartsAttribute
) {
262 ColouriseCharacter(sc
, apostropheStartsAttribute
);
265 } else if (sc
.Match('<', '<')) {
266 ColouriseLabel(sc
, keywords
, apostropheStartsAttribute
);
269 } else if (IsASpace(sc
.ch
)) {
270 ColouriseWhiteSpace(sc
, apostropheStartsAttribute
);
273 } else if (IsDelimiterCharacter(sc
.ch
)) {
274 ColouriseDelimiter(sc
, apostropheStartsAttribute
);
277 } else if (IsADigit(sc
.ch
) || sc
.ch
== '#') {
278 ColouriseNumber(sc
, apostropheStartsAttribute
);
280 // Keywords or identifiers
282 ColouriseWord(sc
, keywords
, apostropheStartsAttribute
);
289 static inline bool IsDelimiterCharacter(int ch
) {
313 static inline bool IsNumberCharacter(int ch
) {
314 return IsNumberStartCharacter(ch
) ||
318 (ch
>= 'a' && ch
<= 'f') ||
319 (ch
>= 'A' && ch
<= 'F');
322 static inline bool IsNumberStartCharacter(int ch
) {
326 static inline bool IsSeparatorOrDelimiterCharacter(int ch
) {
327 return IsASpace(ch
) || IsDelimiterCharacter(ch
);
330 static bool IsValidIdentifier(const std::string
& identifier
) {
331 // First character can't be '_', so initialize the flag to true
332 bool lastWasUnderscore
= true;
334 size_t length
= identifier
.length();
336 // Zero-length identifiers are not valid (these can occur inside labels)
341 // Check for valid character at the start
342 if (!IsWordStartCharacter(identifier
[0])) {
346 // Check for only valid characters and no double underscores
347 for (size_t i
= 0; i
< length
; i
++) {
348 if (!IsWordCharacter(identifier
[i
]) ||
349 (identifier
[i
] == '_' && lastWasUnderscore
)) {
352 lastWasUnderscore
= identifier
[i
] == '_';
355 // Check for underscore at the end
356 if (lastWasUnderscore
== true) {
364 static bool IsValidNumber(const std::string
& number
) {
365 size_t hashPos
= number
.find("#");
366 bool seenDot
= false;
369 size_t length
= number
.length();
372 return false; // Just in case
375 if (hashPos
== std::string::npos
) {
376 bool canBeSpecial
= false;
378 for (; i
< length
; i
++) {
379 if (number
[i
] == '_') {
383 canBeSpecial
= false;
384 } else if (number
[i
] == '.') {
385 if (!canBeSpecial
|| seenDot
) {
388 canBeSpecial
= false;
390 } else if (IsADigit(number
[i
])) {
401 bool canBeSpecial
= false;
405 for (; i
< length
; i
++) {
410 canBeSpecial
= false;
411 } else if (IsADigit(ch
)) {
412 base
= base
* 10 + (ch
- '0');
416 } else if (ch
== '#' && canBeSpecial
) {
428 i
++; // Skip over '#'
431 canBeSpecial
= false;
433 for (; i
< length
; i
++) {
434 int ch
= tolower(number
[i
]);
440 canBeSpecial
= false;
442 } else if (ch
== '.') {
443 if (!canBeSpecial
|| seenDot
) {
446 canBeSpecial
= false;
449 } else if (IsADigit(ch
)) {
450 if (ch
- '0' >= base
) {
455 } else if (ch
>= 'a' && ch
<= 'f') {
456 if (ch
- 'a' + 10 >= base
) {
461 } else if (ch
== '#' && canBeSpecial
) {
476 // Exponent (optional)
478 if (number
[i
] != 'e' && number
[i
] != 'E')
481 i
++; // Move past 'E'
487 if (number
[i
] == '+')
489 else if (number
[i
] == '-') {
493 return false; // Integer literals should not have negative exponents
501 bool canBeSpecial
= false;
503 for (; i
< length
; i
++) {
504 if (number
[i
] == '_') {
508 canBeSpecial
= false;
509 } else if (IsADigit(number
[i
])) {
520 // if i == length, number was parsed successfully.
524 static inline bool IsWordCharacter(int ch
) {
525 return IsWordStartCharacter(ch
) || IsADigit(ch
);
528 static inline bool IsWordStartCharacter(int ch
) {
529 return (isascii(ch
) && isalpha(ch
)) || ch
== '_';