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