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"
26 static void ColouriseDocument(
27 unsigned int startPos
,
30 WordList
*keywordlists
[],
33 static const char * const adaWordListDesc
[] = {
38 LexerModule
lmAda(SCLEX_ADA
, ColouriseDocument
, "ada", NULL
, adaWordListDesc
);
44 // Functions that have apostropheStartsAttribute as a parameter set it according to whether
45 // an apostrophe encountered after processing the current token will start an attribute or
46 // a character literal.
47 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
);
48 static void ColouriseComment(StyleContext
& sc
, bool& apostropheStartsAttribute
);
49 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
);
50 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
);
51 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
);
52 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
);
53 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
);
54 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& apostropheStartsAttribute
);
55 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
);
57 static inline bool IsDelimiterCharacter(int ch
);
58 static inline bool IsNumberStartCharacter(int ch
);
59 static inline bool IsNumberCharacter(int ch
);
60 static inline bool IsSeparatorOrDelimiterCharacter(int ch
);
61 static bool IsValidIdentifier(const SString
& identifier
);
62 static bool IsValidNumber(const SString
& number
);
63 static inline bool IsWordStartCharacter(int ch
);
64 static inline bool IsWordCharacter(int ch
);
66 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
67 apostropheStartsAttribute
= true;
69 sc
.SetState(SCE_ADA_CHARACTER
);
71 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
72 // is handled correctly)
76 ColouriseContext(sc
, '\'', SCE_ADA_CHARACTEREOL
);
79 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
) {
80 while (!sc
.atLineEnd
&& !sc
.Match(chEnd
)) {
85 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
87 sc
.ChangeState(stateEOL
);
91 static void ColouriseComment(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
92 // Apostrophe meaning is not changed, but the parameter is present for uniformity
94 sc
.SetState(SCE_ADA_COMMENTLINE
);
96 while (!sc
.atLineEnd
) {
101 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
102 apostropheStartsAttribute
= sc
.Match (')');
103 sc
.SetState(SCE_ADA_DELIMITER
);
104 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
107 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
108 apostropheStartsAttribute
= false;
110 sc
.SetState(SCE_ADA_LABEL
);
118 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
119 identifier
+= static_cast<char>(tolower(sc
.ch
));
124 if (sc
.Match('>', '>')) {
128 sc
.ChangeState(SCE_ADA_ILLEGAL
);
131 // If the name is an invalid identifier or a keyword, then make it invalid label
132 if (!IsValidIdentifier(identifier
) || keywords
.InList(identifier
.c_str())) {
133 sc
.ChangeState(SCE_ADA_ILLEGAL
);
136 sc
.SetState(SCE_ADA_DEFAULT
);
140 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
141 apostropheStartsAttribute
= true;
144 sc
.SetState(SCE_ADA_NUMBER
);
146 // Get all characters up to a delimiter or a separator, including points, but excluding
147 // double points (ranges).
148 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
) || (sc
.ch
== '.' && sc
.chNext
!= '.')) {
149 number
+= static_cast<char>(sc
.ch
);
153 // Special case: exponent with sign
154 if ((sc
.chPrev
== 'e' || sc
.chPrev
== 'E') &&
155 (sc
.ch
== '+' || sc
.ch
== '-')) {
156 number
+= static_cast<char>(sc
.ch
);
159 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
160 number
+= static_cast<char>(sc
.ch
);
165 if (!IsValidNumber(number
)) {
166 sc
.ChangeState(SCE_ADA_ILLEGAL
);
169 sc
.SetState(SCE_ADA_DEFAULT
);
172 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
173 apostropheStartsAttribute
= true;
175 sc
.SetState(SCE_ADA_STRING
);
178 ColouriseContext(sc
, '"', SCE_ADA_STRINGEOL
);
181 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
182 // Apostrophe meaning is not changed, but the parameter is present for uniformity
183 sc
.SetState(SCE_ADA_DEFAULT
);
184 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
187 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
188 apostropheStartsAttribute
= true;
189 sc
.SetState(SCE_ADA_IDENTIFIER
);
193 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
194 word
+= static_cast<char>(tolower(sc
.ch
));
198 if (!IsValidIdentifier(word
)) {
199 sc
.ChangeState(SCE_ADA_ILLEGAL
);
201 } else if (keywords
.InList(word
.c_str())) {
202 sc
.ChangeState(SCE_ADA_WORD
);
205 apostropheStartsAttribute
= false;
209 sc
.SetState(SCE_ADA_DEFAULT
);
216 static void ColouriseDocument(
217 unsigned int startPos
,
220 WordList
*keywordlists
[],
222 WordList
&keywords
= *keywordlists
[0];
224 StyleContext
sc(startPos
, length
, initStyle
, styler
);
226 int lineCurrent
= styler
.GetLine(startPos
);
227 bool apostropheStartsAttribute
= (styler
.GetLineState(lineCurrent
) & 1) != 0;
231 // Go to the next line
235 // Remember the line state for future incremental lexing
236 styler
.SetLineState(lineCurrent
, apostropheStartsAttribute
);
238 // Don't continue any styles on the next line
239 sc
.SetState(SCE_ADA_DEFAULT
);
243 if (sc
.Match('-', '-')) {
244 ColouriseComment(sc
, apostropheStartsAttribute
);
247 } else if (sc
.Match('"')) {
248 ColouriseString(sc
, apostropheStartsAttribute
);
251 } else if (sc
.Match('\'') && !apostropheStartsAttribute
) {
252 ColouriseCharacter(sc
, apostropheStartsAttribute
);
255 } else if (sc
.Match('<', '<')) {
256 ColouriseLabel(sc
, keywords
, apostropheStartsAttribute
);
259 } else if (isspace(sc
.ch
)) {
260 ColouriseWhiteSpace(sc
, apostropheStartsAttribute
);
263 } else if (IsDelimiterCharacter(sc
.ch
)) {
264 ColouriseDelimiter(sc
, apostropheStartsAttribute
);
267 } else if (isdigit(sc
.ch
) || sc
.ch
== '#') {
268 ColouriseNumber(sc
, apostropheStartsAttribute
);
270 // Keywords or identifiers
272 ColouriseWord(sc
, keywords
, apostropheStartsAttribute
);
279 static inline bool IsDelimiterCharacter(int ch
) {
303 static inline bool IsNumberCharacter(int ch
) {
304 return IsNumberStartCharacter(ch
) ||
308 (ch
>= 'a' && ch
<= 'f') ||
309 (ch
>= 'A' && ch
<= 'F');
312 static inline bool IsNumberStartCharacter(int ch
) {
313 return isdigit(ch
) != 0;
316 static inline bool IsSeparatorOrDelimiterCharacter(int ch
) {
317 return isspace(ch
) || IsDelimiterCharacter(ch
);
320 static bool IsValidIdentifier(const SString
& identifier
) {
321 // First character can't be '_', so initialize the flag to true
322 bool lastWasUnderscore
= true;
324 size_t length
= identifier
.length();
326 // Zero-length identifiers are not valid (these can occur inside labels)
331 // Check for valid character at the start
332 if (!IsWordStartCharacter(identifier
[0])) {
336 // Check for only valid characters and no double underscores
337 for (size_t i
= 0; i
< length
; i
++) {
338 if (!IsWordCharacter(identifier
[i
]) ||
339 (identifier
[i
] == '_' && lastWasUnderscore
)) {
342 lastWasUnderscore
= identifier
[i
] == '_';
345 // Check for underscore at the end
346 if (lastWasUnderscore
== true) {
354 static bool IsValidNumber(const SString
& number
) {
355 int hashPos
= number
.search("#");
356 bool seenDot
= false;
359 size_t length
= number
.length();
362 return false; // Just in case
366 bool canBeSpecial
= false;
368 for (; i
< length
; i
++) {
369 if (number
[i
] == '_') {
373 canBeSpecial
= false;
374 } else if (number
[i
] == '.') {
375 if (!canBeSpecial
|| seenDot
) {
378 canBeSpecial
= false;
380 } else if (isdigit(number
[i
])) {
391 bool canBeSpecial
= false;
395 for (; i
< length
; i
++) {
400 canBeSpecial
= false;
401 } else if (isdigit (ch
)) {
402 base
= base
* 10 + (ch
- '0');
406 } else if (ch
== '#' && canBeSpecial
) {
418 i
++; // Skip over '#'
421 canBeSpecial
= false;
423 for (; i
< length
; i
++) {
424 int ch
= tolower(number
[i
]);
430 canBeSpecial
= false;
432 } else if (ch
== '.') {
433 if (!canBeSpecial
|| seenDot
) {
436 canBeSpecial
= false;
439 } else if (isdigit (ch
)) {
440 if (ch
- '0' >= base
) {
445 } else if (ch
>= 'a' && ch
<= 'f') {
446 if (ch
- 'a' + 10 >= base
) {
451 } else if (ch
== '#' && canBeSpecial
) {
466 // Exponent (optional)
468 if (number
[i
] != 'e' && number
[i
] != 'E')
471 i
++; // Move past 'E'
477 if (number
[i
] == '+')
479 else if (number
[i
] == '-') {
483 return false; // Integer literals should not have negative exponents
491 bool canBeSpecial
= false;
493 for (; i
< length
; i
++) {
494 if (number
[i
] == '_') {
498 canBeSpecial
= false;
499 } else if (isdigit(number
[i
])) {
510 // if i == length, number was parsed successfully.
514 static inline bool IsWordCharacter(int ch
) {
515 return IsWordStartCharacter(ch
) || isdigit(ch
);
518 static inline bool IsWordStartCharacter(int ch
) {
519 return isalpha(ch
) || ch
== '_';