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.
16 #include "StyleContext.h"
23 using namespace Scintilla
;
30 static void ColouriseDocument(
31 unsigned int startPos
,
34 WordList
*keywordlists
[],
37 static const char * const adaWordListDesc
[] = {
42 LexerModule
lmAda(SCLEX_ADA
, ColouriseDocument
, "ada", NULL
, adaWordListDesc
);
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)
80 ColouriseContext(sc
, '\'', SCE_ADA_CHARACTEREOL
);
83 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
) {
84 while (!sc
.atLineEnd
&& !sc
.Match(chEnd
)) {
89 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
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
) {
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
);
122 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
123 identifier
+= static_cast<char>(tolower(sc
.ch
));
128 if (sc
.Match('>', '>')) {
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;
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
);
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
);
163 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
164 number
+= static_cast<char>(sc
.ch
);
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
);
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
);
197 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
198 word
+= static_cast<char>(tolower(sc
.ch
));
202 if (!IsValidIdentifier(word
)) {
203 sc
.ChangeState(SCE_ADA_ILLEGAL
);
205 } else if (keywords
.InList(word
.c_str())) {
206 sc
.ChangeState(SCE_ADA_WORD
);
209 apostropheStartsAttribute
= false;
213 sc
.SetState(SCE_ADA_DEFAULT
);
220 static void ColouriseDocument(
221 unsigned int startPos
,
224 WordList
*keywordlists
[],
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;
235 // Go to the next line
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
);
247 if (sc
.Match('-', '-')) {
248 ColouriseComment(sc
, apostropheStartsAttribute
);
251 } else if (sc
.Match('"')) {
252 ColouriseString(sc
, apostropheStartsAttribute
);
255 } else if (sc
.Match('\'') && !apostropheStartsAttribute
) {
256 ColouriseCharacter(sc
, apostropheStartsAttribute
);
259 } else if (sc
.Match('<', '<')) {
260 ColouriseLabel(sc
, keywords
, apostropheStartsAttribute
);
263 } else if (IsASpace(sc
.ch
)) {
264 ColouriseWhiteSpace(sc
, apostropheStartsAttribute
);
267 } else if (IsDelimiterCharacter(sc
.ch
)) {
268 ColouriseDelimiter(sc
, apostropheStartsAttribute
);
271 } else if (IsADigit(sc
.ch
) || sc
.ch
== '#') {
272 ColouriseNumber(sc
, apostropheStartsAttribute
);
274 // Keywords or identifiers
276 ColouriseWord(sc
, keywords
, apostropheStartsAttribute
);
283 static inline bool IsDelimiterCharacter(int ch
) {
307 static inline bool IsNumberCharacter(int ch
) {
308 return IsNumberStartCharacter(ch
) ||
312 (ch
>= 'a' && ch
<= 'f') ||
313 (ch
>= 'A' && ch
<= 'F');
316 static inline bool IsNumberStartCharacter(int 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)
335 // Check for valid character at the start
336 if (!IsWordStartCharacter(identifier
[0])) {
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
)) {
346 lastWasUnderscore
= identifier
[i
] == '_';
349 // Check for underscore at the end
350 if (lastWasUnderscore
== true) {
358 static bool IsValidNumber(const SString
& number
) {
359 int hashPos
= number
.search("#");
360 bool seenDot
= false;
363 size_t length
= number
.length();
366 return false; // Just in case
370 bool canBeSpecial
= false;
372 for (; i
< length
; i
++) {
373 if (number
[i
] == '_') {
377 canBeSpecial
= false;
378 } else if (number
[i
] == '.') {
379 if (!canBeSpecial
|| seenDot
) {
382 canBeSpecial
= false;
384 } else if (IsADigit(number
[i
])) {
395 bool canBeSpecial
= false;
399 for (; i
< length
; i
++) {
404 canBeSpecial
= false;
405 } else if (IsADigit(ch
)) {
406 base
= base
* 10 + (ch
- '0');
410 } else if (ch
== '#' && canBeSpecial
) {
422 i
++; // Skip over '#'
425 canBeSpecial
= false;
427 for (; i
< length
; i
++) {
428 int ch
= tolower(number
[i
]);
434 canBeSpecial
= false;
436 } else if (ch
== '.') {
437 if (!canBeSpecial
|| seenDot
) {
440 canBeSpecial
= false;
443 } else if (IsADigit(ch
)) {
444 if (ch
- '0' >= base
) {
449 } else if (ch
>= 'a' && ch
<= 'f') {
450 if (ch
- 'a' + 10 >= base
) {
455 } else if (ch
== '#' && canBeSpecial
) {
470 // Exponent (optional)
472 if (number
[i
] != 'e' && number
[i
] != 'E')
475 i
++; // Move past 'E'
481 if (number
[i
] == '+')
483 else if (number
[i
] == '-') {
487 return false; // Integer literals should not have negative exponents
495 bool canBeSpecial
= false;
497 for (; i
< length
; i
++) {
498 if (number
[i
] == '_') {
502 canBeSpecial
= false;
503 } else if (IsADigit(number
[i
])) {
514 // if i == length, number was parsed successfully.
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
== '_';