]> git.saurik.com Git - wxWidgets.git/blame - src/stc/scintilla/src/LexAda.cxx
don't modify the output variable if ToXXX() fails to convert (modified patch 1849041)
[wxWidgets.git] / src / stc / scintilla / src / LexAda.cxx
CommitLineData
9e730a78
RD
1// Scintilla source code edit control
2/** @file LexAda.cxx
3 ** Lexer for Ada 95
4 **/
5// Copyright 2002 by Sergey Koshcheyev <sergey.k@seznam.cz>
65ec6247
RD
6// The License.txt file describes the conditions under which this software may be distributed.
7
9e730a78
RD
8#include <stdlib.h>
9#include <ctype.h>
10#include <string.h>
11#include <stdio.h>
65ec6247
RD
12
13#include "Platform.h"
14
65ec6247 15#include "Accessor.h"
9e730a78
RD
16#include "StyleContext.h"
17#include "PropSet.h"
65ec6247 18#include "KeyWords.h"
65ec6247 19#include "SciLexer.h"
9e730a78
RD
20#include "SString.h"
21
22/*
23 * Interface
24 */
25
26static void ColouriseDocument(
27 unsigned int startPos,
28 int length,
29 int initStyle,
30 WordList *keywordlists[],
31 Accessor &styler);
32
33static const char * const adaWordListDesc[] = {
34 "Keywords",
35 0
36};
37
38LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc);
39
40/*
41 * Implementation
42 */
43
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.
47static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute);
48static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
49static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL);
50static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
51static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
52static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
53static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute);
54static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
55static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
56
57static inline bool IsDelimiterCharacter(int ch);
58static inline bool IsNumberStartCharacter(int ch);
59static inline bool IsNumberCharacter(int ch);
60static inline bool IsSeparatorOrDelimiterCharacter(int ch);
61static bool IsValidIdentifier(const SString& identifier);
62static bool IsValidNumber(const SString& number);
63static inline bool IsWordStartCharacter(int ch);
64static inline bool IsWordCharacter(int ch);
65ec6247 65
9e730a78
RD
66static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) {
67 apostropheStartsAttribute = true;
65ec6247 68
9e730a78 69 sc.SetState(SCE_ADA_CHARACTER);
65ec6247 70
9e730a78
RD
71 // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
72 // is handled correctly)
73 sc.Forward();
74 sc.Forward();
75
76 ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL);
77}
78
79static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) {
80 while (!sc.atLineEnd && !sc.Match(chEnd)) {
81 sc.Forward();
65ec6247 82 }
65ec6247 83
9e730a78
RD
84 if (!sc.atLineEnd) {
85 sc.ForwardSetState(SCE_ADA_DEFAULT);
86 } else {
87 sc.ChangeState(stateEOL);
65ec6247 88 }
65ec6247
RD
89}
90
9e730a78
RD
91static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
92 // Apostrophe meaning is not changed, but the parameter is present for uniformity
65ec6247 93
9e730a78
RD
94 sc.SetState(SCE_ADA_COMMENTLINE);
95
96 while (!sc.atLineEnd) {
97 sc.Forward();
98 }
99}
100
101static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
102 apostropheStartsAttribute = sc.Match (')');
103 sc.SetState(SCE_ADA_DELIMITER);
104 sc.ForwardSetState(SCE_ADA_DEFAULT);
105}
106
107static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
108 apostropheStartsAttribute = false;
109
110 sc.SetState(SCE_ADA_LABEL);
111
112 // Skip "<<"
113 sc.Forward();
114 sc.Forward();
115
116 SString identifier;
117
118 while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
119 identifier += static_cast<char>(tolower(sc.ch));
120 sc.Forward();
121 }
122
123 // Skip ">>"
124 if (sc.Match('>', '>')) {
125 sc.Forward();
126 sc.Forward();
127 } else {
128 sc.ChangeState(SCE_ADA_ILLEGAL);
129 }
130
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);
134 }
135
136 sc.SetState(SCE_ADA_DEFAULT);
137
138}
139
140static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
141 apostropheStartsAttribute = true;
142
143 SString number;
144 sc.SetState(SCE_ADA_NUMBER);
145
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);
150 sc.Forward();
151 }
152
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);
157 sc.Forward ();
158
159 while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
160 number += static_cast<char>(sc.ch);
161 sc.Forward();
162 }
163 }
164
165 if (!IsValidNumber(number)) {
166 sc.ChangeState(SCE_ADA_ILLEGAL);
167 }
168
169 sc.SetState(SCE_ADA_DEFAULT);
170}
171
172static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) {
173 apostropheStartsAttribute = true;
174
175 sc.SetState(SCE_ADA_STRING);
176 sc.Forward();
177
178 ColouriseContext(sc, '"', SCE_ADA_STRINGEOL);
179}
180
181static 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);
185}
186
187static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
188 apostropheStartsAttribute = true;
189 sc.SetState(SCE_ADA_IDENTIFIER);
190
191 SString word;
192
193 while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
194 word += static_cast<char>(tolower(sc.ch));
195 sc.Forward();
196 }
197
198 if (!IsValidIdentifier(word)) {
199 sc.ChangeState(SCE_ADA_ILLEGAL);
200
201 } else if (keywords.InList(word.c_str())) {
202 sc.ChangeState(SCE_ADA_WORD);
203
204 if (word != "all") {
205 apostropheStartsAttribute = false;
206 }
207 }
208
209 sc.SetState(SCE_ADA_DEFAULT);
210}
211
212//
213// ColouriseDocument
214//
215
216static void ColouriseDocument(
217 unsigned int startPos,
218 int length,
219 int initStyle,
220 WordList *keywordlists[],
221 Accessor &styler) {
65ec6247 222 WordList &keywords = *keywordlists[0];
9e730a78
RD
223
224 StyleContext sc(startPos, length, initStyle, styler);
225
226 int lineCurrent = styler.GetLine(startPos);
227 bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
228
229 while (sc.More()) {
230 if (sc.atLineEnd) {
231 // Go to the next line
232 sc.Forward();
233 lineCurrent++;
234
235 // Remember the line state for future incremental lexing
236 styler.SetLineState(lineCurrent, apostropheStartsAttribute);
237
238 // Don't continue any styles on the next line
239 sc.SetState(SCE_ADA_DEFAULT);
65ec6247 240 }
65ec6247 241
9e730a78
RD
242 // Comments
243 if (sc.Match('-', '-')) {
244 ColouriseComment(sc, apostropheStartsAttribute);
245
246 // Strings
247 } else if (sc.Match('"')) {
248 ColouriseString(sc, apostropheStartsAttribute);
249
250 // Characters
251 } else if (sc.Match('\'') && !apostropheStartsAttribute) {
252 ColouriseCharacter(sc, apostropheStartsAttribute);
253
254 // Labels
255 } else if (sc.Match('<', '<')) {
256 ColouriseLabel(sc, keywords, apostropheStartsAttribute);
257
258 // Whitespace
a33203cb 259 } else if (IsASpace(sc.ch)) {
9e730a78
RD
260 ColouriseWhiteSpace(sc, apostropheStartsAttribute);
261
262 // Delimiters
263 } else if (IsDelimiterCharacter(sc.ch)) {
264 ColouriseDelimiter(sc, apostropheStartsAttribute);
265
266 // Numbers
a33203cb 267 } else if (IsADigit(sc.ch) || sc.ch == '#') {
9e730a78
RD
268 ColouriseNumber(sc, apostropheStartsAttribute);
269
270 // Keywords or identifiers
271 } else {
272 ColouriseWord(sc, keywords, apostropheStartsAttribute);
273 }
274 }
275
276 sc.Complete();
277}
278
279static inline bool IsDelimiterCharacter(int ch) {
280 switch (ch) {
281 case '&':
282 case '\'':
283 case '(':
284 case ')':
285 case '*':
286 case '+':
287 case ',':
288 case '-':
289 case '.':
290 case '/':
291 case ':':
292 case ';':
293 case '<':
294 case '=':
295 case '>':
296 case '|':
297 return true;
298 default:
299 return false;
300 }
301}
302
303static inline bool IsNumberCharacter(int ch) {
304 return IsNumberStartCharacter(ch) ||
305 ch == '_' ||
306 ch == '.' ||
307 ch == '#' ||
308 (ch >= 'a' && ch <= 'f') ||
309 (ch >= 'A' && ch <= 'F');
310}
311
312static inline bool IsNumberStartCharacter(int ch) {
a33203cb 313 return IsADigit(ch);
9e730a78
RD
314}
315
316static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
a33203cb 317 return IsASpace(ch) || IsDelimiterCharacter(ch);
9e730a78
RD
318}
319
320static bool IsValidIdentifier(const SString& identifier) {
321 // First character can't be '_', so initialize the flag to true
322 bool lastWasUnderscore = true;
323
88a8b04e 324 size_t length = identifier.length();
9e730a78
RD
325
326 // Zero-length identifiers are not valid (these can occur inside labels)
327 if (length == 0) {
328 return false;
329 }
330
331 // Check for valid character at the start
332 if (!IsWordStartCharacter(identifier[0])) {
333 return false;
334 }
335
336 // Check for only valid characters and no double underscores
88a8b04e 337 for (size_t i = 0; i < length; i++) {
9e730a78
RD
338 if (!IsWordCharacter(identifier[i]) ||
339 (identifier[i] == '_' && lastWasUnderscore)) {
340 return false;
65ec6247 341 }
9e730a78
RD
342 lastWasUnderscore = identifier[i] == '_';
343 }
344
345 // Check for underscore at the end
346 if (lastWasUnderscore == true) {
347 return false;
348 }
65ec6247 349
9e730a78
RD
350 // All checks passed
351 return true;
352}
353
354static bool IsValidNumber(const SString& number) {
355 int hashPos = number.search("#");
356 bool seenDot = false;
357
88a8b04e
RD
358 size_t i = 0;
359 size_t length = number.length();
9e730a78
RD
360
361 if (length == 0)
362 return false; // Just in case
363
364 // Decimal number
365 if (hashPos == -1) {
366 bool canBeSpecial = false;
367
368 for (; i < length; i++) {
369 if (number[i] == '_') {
370 if (!canBeSpecial) {
371 return false;
372 }
373 canBeSpecial = false;
374 } else if (number[i] == '.') {
375 if (!canBeSpecial || seenDot) {
376 return false;
377 }
378 canBeSpecial = false;
379 seenDot = true;
a33203cb 380 } else if (IsADigit(number[i])) {
9e730a78
RD
381 canBeSpecial = true;
382 } else {
383 break;
65ec6247 384 }
9e730a78
RD
385 }
386
387 if (!canBeSpecial)
388 return false;
389 } else {
390 // Based number
391 bool canBeSpecial = false;
392 int base = 0;
393
394 // Parse base
395 for (; i < length; i++) {
396 int ch = number[i];
397 if (ch == '_') {
398 if (!canBeSpecial)
399 return false;
400 canBeSpecial = false;
a33203cb 401 } else if (IsADigit(ch)) {
9e730a78
RD
402 base = base * 10 + (ch - '0');
403 if (base > 16)
404 return false;
405 canBeSpecial = true;
406 } else if (ch == '#' && canBeSpecial) {
407 break;
408 } else {
409 return false;
65ec6247 410 }
9e730a78
RD
411 }
412
413 if (base < 2)
414 return false;
415 if (i == length)
416 return false;
417
418 i++; // Skip over '#'
419
420 // Parse number
421 canBeSpecial = false;
422
423 for (; i < length; i++) {
424 int ch = tolower(number[i]);
425
426 if (ch == '_') {
427 if (!canBeSpecial) {
428 return false;
429 }
430 canBeSpecial = false;
431
432 } else if (ch == '.') {
433 if (!canBeSpecial || seenDot) {
434 return false;
435 }
436 canBeSpecial = false;
437 seenDot = true;
438
a33203cb 439 } else if (IsADigit(ch)) {
9e730a78
RD
440 if (ch - '0' >= base) {
441 return false;
442 }
443 canBeSpecial = true;
444
445 } else if (ch >= 'a' && ch <= 'f') {
446 if (ch - 'a' + 10 >= base) {
447 return false;
65ec6247 448 }
9e730a78
RD
449 canBeSpecial = true;
450
451 } else if (ch == '#' && canBeSpecial) {
452 break;
453
454 } else {
455 return false;
65ec6247 456 }
9e730a78
RD
457 }
458
459 if (i == length) {
460 return false;
461 }
462
463 i++;
464 }
465
466 // Exponent (optional)
467 if (i < length) {
468 if (number[i] != 'e' && number[i] != 'E')
469 return false;
470
471 i++; // Move past 'E'
472
473 if (i == length) {
474 return false;
475 }
476
477 if (number[i] == '+')
478 i++;
479 else if (number[i] == '-') {
480 if (seenDot) {
481 i++;
482 } else {
483 return false; // Integer literals should not have negative exponents
65ec6247 484 }
9e730a78
RD
485 }
486
487 if (i == length) {
488 return false;
489 }
490
491 bool canBeSpecial = false;
492
493 for (; i < length; i++) {
494 if (number[i] == '_') {
495 if (!canBeSpecial) {
496 return false;
497 }
498 canBeSpecial = false;
a33203cb 499 } else if (IsADigit(number[i])) {
9e730a78
RD
500 canBeSpecial = true;
501 } else {
502 return false;
65ec6247
RD
503 }
504 }
505
9e730a78
RD
506 if (!canBeSpecial)
507 return false;
65ec6247 508 }
65ec6247 509
9e730a78
RD
510 // if i == length, number was parsed successfully.
511 return i == length;
512}
513
514static inline bool IsWordCharacter(int ch) {
a33203cb 515 return IsWordStartCharacter(ch) || IsADigit(ch);
65ec6247
RD
516}
517
9e730a78 518static inline bool IsWordStartCharacter(int ch) {
a33203cb 519 return (isascii(ch) && isalpha(ch)) || ch == '_';
9e730a78 520}