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 "StyleContext.h"
24 using namespace Scintilla
;
31 static void ColouriseDocument(
32 unsigned int startPos
,
35 WordList
*keywordlists
[],
38 static const char * const adaWordListDesc
[] = {
43 LexerModule
lmAda(SCLEX_ADA
, ColouriseDocument
, "ada", NULL
, adaWordListDesc
);
49 // Functions that have apostropheStartsAttribute as a parameter set it according to whether
50 // an apostrophe encountered after processing the current token will start an attribute or
51 // a character literal.
52 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
);
53 static void ColouriseComment(StyleContext
& sc
, bool& apostropheStartsAttribute
);
54 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
);
55 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
);
56 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
);
57 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
);
58 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
);
59 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& apostropheStartsAttribute
);
60 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
);
62 static inline bool IsDelimiterCharacter(int ch
);
63 static inline bool IsNumberStartCharacter(int ch
);
64 static inline bool IsNumberCharacter(int ch
);
65 static inline bool IsSeparatorOrDelimiterCharacter(int ch
);
66 static bool IsValidIdentifier(const std::string
& identifier
);
67 static bool IsValidNumber(const std::string
& number
);
68 static inline bool IsWordStartCharacter(int ch
);
69 static inline bool IsWordCharacter(int ch
);
71 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
72 apostropheStartsAttribute
= true;
74 sc
.SetState(SCE_ADA_CHARACTER
);
76 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
77 // is handled correctly)
81 ColouriseContext(sc
, '\'', SCE_ADA_CHARACTEREOL
);
84 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
) {
85 while (!sc
.atLineEnd
&& !sc
.Match(chEnd
)) {
90 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
92 sc
.ChangeState(stateEOL
);
96 static void ColouriseComment(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
97 // Apostrophe meaning is not changed, but the parameter is present for uniformity
99 sc
.SetState(SCE_ADA_COMMENTLINE
);
101 while (!sc
.atLineEnd
) {
106 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
107 apostropheStartsAttribute
= sc
.Match (')');
108 sc
.SetState(SCE_ADA_DELIMITER
);
109 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
112 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
113 apostropheStartsAttribute
= false;
115 sc
.SetState(SCE_ADA_LABEL
);
121 std::string identifier
;
123 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
124 identifier
+= static_cast<char>(tolower(sc
.ch
));
129 if (sc
.Match('>', '>')) {
133 sc
.ChangeState(SCE_ADA_ILLEGAL
);
136 // If the name is an invalid identifier or a keyword, then make it invalid label
137 if (!IsValidIdentifier(identifier
) || keywords
.InList(identifier
.c_str())) {
138 sc
.ChangeState(SCE_ADA_ILLEGAL
);
141 sc
.SetState(SCE_ADA_DEFAULT
);
145 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
146 apostropheStartsAttribute
= true;
149 sc
.SetState(SCE_ADA_NUMBER
);
151 // Get all characters up to a delimiter or a separator, including points, but excluding
152 // double points (ranges).
153 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
) || (sc
.ch
== '.' && sc
.chNext
!= '.')) {
154 number
+= static_cast<char>(sc
.ch
);
158 // Special case: exponent with sign
159 if ((sc
.chPrev
== 'e' || sc
.chPrev
== 'E') &&
160 (sc
.ch
== '+' || sc
.ch
== '-')) {
161 number
+= static_cast<char>(sc
.ch
);
164 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
165 number
+= static_cast<char>(sc
.ch
);
170 if (!IsValidNumber(number
)) {
171 sc
.ChangeState(SCE_ADA_ILLEGAL
);
174 sc
.SetState(SCE_ADA_DEFAULT
);
177 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
178 apostropheStartsAttribute
= true;
180 sc
.SetState(SCE_ADA_STRING
);
183 ColouriseContext(sc
, '"', SCE_ADA_STRINGEOL
);
186 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
187 // Apostrophe meaning is not changed, but the parameter is present for uniformity
188 sc
.SetState(SCE_ADA_DEFAULT
);
189 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
192 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
193 apostropheStartsAttribute
= true;
194 sc
.SetState(SCE_ADA_IDENTIFIER
);
198 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
199 word
+= static_cast<char>(tolower(sc
.ch
));
203 if (!IsValidIdentifier(word
)) {
204 sc
.ChangeState(SCE_ADA_ILLEGAL
);
206 } else if (keywords
.InList(word
.c_str())) {
207 sc
.ChangeState(SCE_ADA_WORD
);
210 apostropheStartsAttribute
= false;
214 sc
.SetState(SCE_ADA_DEFAULT
);
221 static void ColouriseDocument(
222 unsigned int startPos
,
225 WordList
*keywordlists
[],
227 WordList
&keywords
= *keywordlists
[0];
229 StyleContext
sc(startPos
, length
, initStyle
, styler
);
231 int lineCurrent
= styler
.GetLine(startPos
);
232 bool apostropheStartsAttribute
= (styler
.GetLineState(lineCurrent
) & 1) != 0;
236 // Go to the next line
240 // Remember the line state for future incremental lexing
241 styler
.SetLineState(lineCurrent
, apostropheStartsAttribute
);
243 // Don't continue any styles on the next line
244 sc
.SetState(SCE_ADA_DEFAULT
);
248 if (sc
.Match('-', '-')) {
249 ColouriseComment(sc
, apostropheStartsAttribute
);
252 } else if (sc
.Match('"')) {
253 ColouriseString(sc
, apostropheStartsAttribute
);
256 } else if (sc
.Match('\'') && !apostropheStartsAttribute
) {
257 ColouriseCharacter(sc
, apostropheStartsAttribute
);
260 } else if (sc
.Match('<', '<')) {
261 ColouriseLabel(sc
, keywords
, apostropheStartsAttribute
);
264 } else if (IsASpace(sc
.ch
)) {
265 ColouriseWhiteSpace(sc
, apostropheStartsAttribute
);
268 } else if (IsDelimiterCharacter(sc
.ch
)) {
269 ColouriseDelimiter(sc
, apostropheStartsAttribute
);
272 } else if (IsADigit(sc
.ch
) || sc
.ch
== '#') {
273 ColouriseNumber(sc
, apostropheStartsAttribute
);
275 // Keywords or identifiers
277 ColouriseWord(sc
, keywords
, apostropheStartsAttribute
);
284 static inline bool IsDelimiterCharacter(int ch
) {
308 static inline bool IsNumberCharacter(int ch
) {
309 return IsNumberStartCharacter(ch
) ||
313 (ch
>= 'a' && ch
<= 'f') ||
314 (ch
>= 'A' && ch
<= 'F');
317 static inline bool IsNumberStartCharacter(int ch
) {
321 static inline bool IsSeparatorOrDelimiterCharacter(int ch
) {
322 return IsASpace(ch
) || IsDelimiterCharacter(ch
);
325 static bool IsValidIdentifier(const std::string
& identifier
) {
326 // First character can't be '_', so initialize the flag to true
327 bool lastWasUnderscore
= true;
329 size_t length
= identifier
.length();
331 // Zero-length identifiers are not valid (these can occur inside labels)
336 // Check for valid character at the start
337 if (!IsWordStartCharacter(identifier
[0])) {
341 // Check for only valid characters and no double underscores
342 for (size_t i
= 0; i
< length
; i
++) {
343 if (!IsWordCharacter(identifier
[i
]) ||
344 (identifier
[i
] == '_' && lastWasUnderscore
)) {
347 lastWasUnderscore
= identifier
[i
] == '_';
350 // Check for underscore at the end
351 if (lastWasUnderscore
== true) {
359 static bool IsValidNumber(const std::string
& number
) {
360 size_t hashPos
= number
.find("#");
361 bool seenDot
= false;
364 size_t length
= number
.length();
367 return false; // Just in case
370 if (hashPos
== std::string::npos
) {
371 bool canBeSpecial
= false;
373 for (; i
< length
; i
++) {
374 if (number
[i
] == '_') {
378 canBeSpecial
= false;
379 } else if (number
[i
] == '.') {
380 if (!canBeSpecial
|| seenDot
) {
383 canBeSpecial
= false;
385 } else if (IsADigit(number
[i
])) {
396 bool canBeSpecial
= false;
400 for (; i
< length
; i
++) {
405 canBeSpecial
= false;
406 } else if (IsADigit(ch
)) {
407 base
= base
* 10 + (ch
- '0');
411 } else if (ch
== '#' && canBeSpecial
) {
423 i
++; // Skip over '#'
426 canBeSpecial
= false;
428 for (; i
< length
; i
++) {
429 int ch
= tolower(number
[i
]);
435 canBeSpecial
= false;
437 } else if (ch
== '.') {
438 if (!canBeSpecial
|| seenDot
) {
441 canBeSpecial
= false;
444 } else if (IsADigit(ch
)) {
445 if (ch
- '0' >= base
) {
450 } else if (ch
>= 'a' && ch
<= 'f') {
451 if (ch
- 'a' + 10 >= base
) {
456 } else if (ch
== '#' && canBeSpecial
) {
471 // Exponent (optional)
473 if (number
[i
] != 'e' && number
[i
] != 'E')
476 i
++; // Move past 'E'
482 if (number
[i
] == '+')
484 else if (number
[i
] == '-') {
488 return false; // Integer literals should not have negative exponents
496 bool canBeSpecial
= false;
498 for (; i
< length
; i
++) {
499 if (number
[i
] == '_') {
503 canBeSpecial
= false;
504 } else if (IsADigit(number
[i
])) {
515 // if i == length, number was parsed successfully.
519 static inline bool IsWordCharacter(int ch
) {
520 return IsWordStartCharacter(ch
) || IsADigit(ch
);
523 static inline bool IsWordStartCharacter(int ch
) {
524 return (isascii(ch
) && isalpha(ch
)) || ch
== '_';