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.
17 #include "Scintilla.h"
21 #include "LexAccessor.h"
23 #include "StyleContext.h"
24 #include "CharacterSet.h"
25 #include "LexerModule.h"
28 using namespace Scintilla
;
35 static void ColouriseDocument(
36 unsigned int startPos
,
39 WordList
*keywordlists
[],
42 static const char * const adaWordListDesc
[] = {
47 LexerModule
lmAda(SCLEX_ADA
, ColouriseDocument
, "ada", NULL
, adaWordListDesc
);
53 // Functions that have apostropheStartsAttribute as a parameter set it according to whether
54 // an apostrophe encountered after processing the current token will start an attribute or
55 // a character literal.
56 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
);
57 static void ColouriseComment(StyleContext
& sc
, bool& apostropheStartsAttribute
);
58 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
);
59 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
);
60 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
);
61 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
);
62 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
);
63 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& apostropheStartsAttribute
);
64 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
);
66 static inline bool IsDelimiterCharacter(int ch
);
67 static inline bool IsNumberStartCharacter(int ch
);
68 static inline bool IsNumberCharacter(int ch
);
69 static inline bool IsSeparatorOrDelimiterCharacter(int ch
);
70 static bool IsValidIdentifier(const std::string
& identifier
);
71 static bool IsValidNumber(const std::string
& number
);
72 static inline bool IsWordStartCharacter(int ch
);
73 static inline bool IsWordCharacter(int ch
);
75 static void ColouriseCharacter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
76 apostropheStartsAttribute
= true;
78 sc
.SetState(SCE_ADA_CHARACTER
);
80 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
81 // is handled correctly)
85 ColouriseContext(sc
, '\'', SCE_ADA_CHARACTEREOL
);
88 static void ColouriseContext(StyleContext
& sc
, char chEnd
, int stateEOL
) {
89 while (!sc
.atLineEnd
&& !sc
.Match(chEnd
)) {
94 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
96 sc
.ChangeState(stateEOL
);
100 static void ColouriseComment(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
101 // Apostrophe meaning is not changed, but the parameter is present for uniformity
103 sc
.SetState(SCE_ADA_COMMENTLINE
);
105 while (!sc
.atLineEnd
) {
110 static void ColouriseDelimiter(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
111 apostropheStartsAttribute
= sc
.Match (')');
112 sc
.SetState(SCE_ADA_DELIMITER
);
113 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
116 static void ColouriseLabel(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
117 apostropheStartsAttribute
= false;
119 sc
.SetState(SCE_ADA_LABEL
);
125 std::string identifier
;
127 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
128 identifier
+= static_cast<char>(tolower(sc
.ch
));
133 if (sc
.Match('>', '>')) {
137 sc
.ChangeState(SCE_ADA_ILLEGAL
);
140 // If the name is an invalid identifier or a keyword, then make it invalid label
141 if (!IsValidIdentifier(identifier
) || keywords
.InList(identifier
.c_str())) {
142 sc
.ChangeState(SCE_ADA_ILLEGAL
);
145 sc
.SetState(SCE_ADA_DEFAULT
);
149 static void ColouriseNumber(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
150 apostropheStartsAttribute
= true;
153 sc
.SetState(SCE_ADA_NUMBER
);
155 // Get all characters up to a delimiter or a separator, including points, but excluding
156 // double points (ranges).
157 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
) || (sc
.ch
== '.' && sc
.chNext
!= '.')) {
158 number
+= static_cast<char>(sc
.ch
);
162 // Special case: exponent with sign
163 if ((sc
.chPrev
== 'e' || sc
.chPrev
== 'E') &&
164 (sc
.ch
== '+' || sc
.ch
== '-')) {
165 number
+= static_cast<char>(sc
.ch
);
168 while (!IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
169 number
+= static_cast<char>(sc
.ch
);
174 if (!IsValidNumber(number
)) {
175 sc
.ChangeState(SCE_ADA_ILLEGAL
);
178 sc
.SetState(SCE_ADA_DEFAULT
);
181 static void ColouriseString(StyleContext
& sc
, bool& apostropheStartsAttribute
) {
182 apostropheStartsAttribute
= true;
184 sc
.SetState(SCE_ADA_STRING
);
187 ColouriseContext(sc
, '"', SCE_ADA_STRINGEOL
);
190 static void ColouriseWhiteSpace(StyleContext
& sc
, bool& /*apostropheStartsAttribute*/) {
191 // Apostrophe meaning is not changed, but the parameter is present for uniformity
192 sc
.SetState(SCE_ADA_DEFAULT
);
193 sc
.ForwardSetState(SCE_ADA_DEFAULT
);
196 static void ColouriseWord(StyleContext
& sc
, WordList
& keywords
, bool& apostropheStartsAttribute
) {
197 apostropheStartsAttribute
= true;
198 sc
.SetState(SCE_ADA_IDENTIFIER
);
202 while (!sc
.atLineEnd
&& !IsSeparatorOrDelimiterCharacter(sc
.ch
)) {
203 word
+= static_cast<char>(tolower(sc
.ch
));
207 if (!IsValidIdentifier(word
)) {
208 sc
.ChangeState(SCE_ADA_ILLEGAL
);
210 } else if (keywords
.InList(word
.c_str())) {
211 sc
.ChangeState(SCE_ADA_WORD
);
214 apostropheStartsAttribute
= false;
218 sc
.SetState(SCE_ADA_DEFAULT
);
225 static void ColouriseDocument(
226 unsigned int startPos
,
229 WordList
*keywordlists
[],
231 WordList
&keywords
= *keywordlists
[0];
233 StyleContext
sc(startPos
, length
, initStyle
, styler
);
235 int lineCurrent
= styler
.GetLine(startPos
);
236 bool apostropheStartsAttribute
= (styler
.GetLineState(lineCurrent
) & 1) != 0;
240 // Go to the next line
244 // Remember the line state for future incremental lexing
245 styler
.SetLineState(lineCurrent
, apostropheStartsAttribute
);
247 // Don't continue any styles on the next line
248 sc
.SetState(SCE_ADA_DEFAULT
);
252 if (sc
.Match('-', '-')) {
253 ColouriseComment(sc
, apostropheStartsAttribute
);
256 } else if (sc
.Match('"')) {
257 ColouriseString(sc
, apostropheStartsAttribute
);
260 } else if (sc
.Match('\'') && !apostropheStartsAttribute
) {
261 ColouriseCharacter(sc
, apostropheStartsAttribute
);
264 } else if (sc
.Match('<', '<')) {
265 ColouriseLabel(sc
, keywords
, apostropheStartsAttribute
);
268 } else if (IsASpace(sc
.ch
)) {
269 ColouriseWhiteSpace(sc
, apostropheStartsAttribute
);
272 } else if (IsDelimiterCharacter(sc
.ch
)) {
273 ColouriseDelimiter(sc
, apostropheStartsAttribute
);
276 } else if (IsADigit(sc
.ch
) || sc
.ch
== '#') {
277 ColouriseNumber(sc
, apostropheStartsAttribute
);
279 // Keywords or identifiers
281 ColouriseWord(sc
, keywords
, apostropheStartsAttribute
);
288 static inline bool IsDelimiterCharacter(int ch
) {
312 static inline bool IsNumberCharacter(int ch
) {
313 return IsNumberStartCharacter(ch
) ||
317 (ch
>= 'a' && ch
<= 'f') ||
318 (ch
>= 'A' && ch
<= 'F');
321 static inline bool IsNumberStartCharacter(int ch
) {
325 static inline bool IsSeparatorOrDelimiterCharacter(int ch
) {
326 return IsASpace(ch
) || IsDelimiterCharacter(ch
);
329 static bool IsValidIdentifier(const std::string
& identifier
) {
330 // First character can't be '_', so initialize the flag to true
331 bool lastWasUnderscore
= true;
333 size_t length
= identifier
.length();
335 // Zero-length identifiers are not valid (these can occur inside labels)
340 // Check for valid character at the start
341 if (!IsWordStartCharacter(identifier
[0])) {
345 // Check for only valid characters and no double underscores
346 for (size_t i
= 0; i
< length
; i
++) {
347 if (!IsWordCharacter(identifier
[i
]) ||
348 (identifier
[i
] == '_' && lastWasUnderscore
)) {
351 lastWasUnderscore
= identifier
[i
] == '_';
354 // Check for underscore at the end
355 if (lastWasUnderscore
== true) {
363 static bool IsValidNumber(const std::string
& number
) {
364 size_t hashPos
= number
.find("#");
365 bool seenDot
= false;
368 size_t length
= number
.length();
371 return false; // Just in case
374 if (hashPos
== std::string::npos
) {
375 bool canBeSpecial
= false;
377 for (; i
< length
; i
++) {
378 if (number
[i
] == '_') {
382 canBeSpecial
= false;
383 } else if (number
[i
] == '.') {
384 if (!canBeSpecial
|| seenDot
) {
387 canBeSpecial
= false;
389 } else if (IsADigit(number
[i
])) {
400 bool canBeSpecial
= false;
404 for (; i
< length
; i
++) {
409 canBeSpecial
= false;
410 } else if (IsADigit(ch
)) {
411 base
= base
* 10 + (ch
- '0');
415 } else if (ch
== '#' && canBeSpecial
) {
427 i
++; // Skip over '#'
430 canBeSpecial
= false;
432 for (; i
< length
; i
++) {
433 int ch
= tolower(number
[i
]);
439 canBeSpecial
= false;
441 } else if (ch
== '.') {
442 if (!canBeSpecial
|| seenDot
) {
445 canBeSpecial
= false;
448 } else if (IsADigit(ch
)) {
449 if (ch
- '0' >= base
) {
454 } else if (ch
>= 'a' && ch
<= 'f') {
455 if (ch
- 'a' + 10 >= base
) {
460 } else if (ch
== '#' && canBeSpecial
) {
475 // Exponent (optional)
477 if (number
[i
] != 'e' && number
[i
] != 'E')
480 i
++; // Move past 'E'
486 if (number
[i
] == '+')
488 else if (number
[i
] == '-') {
492 return false; // Integer literals should not have negative exponents
500 bool canBeSpecial
= false;
502 for (; i
< length
; i
++) {
503 if (number
[i
] == '_') {
507 canBeSpecial
= false;
508 } else if (IsADigit(number
[i
])) {
519 // if i == length, number was parsed successfully.
523 static inline bool IsWordCharacter(int ch
) {
524 return IsWordStartCharacter(ch
) || IsADigit(ch
);
527 static inline bool IsWordStartCharacter(int ch
) {
528 return (isascii(ch
) && isalpha(ch
)) || ch
== '_';