You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
530 lines
12 KiB
530 lines
12 KiB
// Scintilla source code edit control
|
|
/** @file LexAda.cxx
|
|
** Lexer for Ada 95
|
|
**/
|
|
// Copyright 2002 by Sergey Koshcheyev <sergey.k@seznam.cz>
|
|
// The License.txt file describes the conditions under which this software may be distributed.
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
|
|
#include <string>
|
|
|
|
#include "ILexer.h"
|
|
#include "Scintilla.h"
|
|
#include "SciLexer.h"
|
|
|
|
#include "WordList.h"
|
|
#include "LexAccessor.h"
|
|
#include "Accessor.h"
|
|
#include "StyleContext.h"
|
|
#include "CharacterSet.h"
|
|
#include "LexerModule.h"
|
|
|
|
#ifdef SCI_NAMESPACE
|
|
using namespace Scintilla;
|
|
#endif
|
|
|
|
/*
|
|
* Interface
|
|
*/
|
|
|
|
static void ColouriseDocument(
|
|
unsigned int startPos,
|
|
int length,
|
|
int initStyle,
|
|
WordList *keywordlists[],
|
|
Accessor &styler);
|
|
|
|
static const char * const adaWordListDesc[] = {
|
|
"Keywords",
|
|
0
|
|
};
|
|
|
|
LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc);
|
|
|
|
/*
|
|
* Implementation
|
|
*/
|
|
|
|
// Functions that have apostropheStartsAttribute as a parameter set it according to whether
|
|
// an apostrophe encountered after processing the current token will start an attribute or
|
|
// a character literal.
|
|
static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute);
|
|
static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
|
|
static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL);
|
|
static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
|
|
static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
|
|
static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
|
|
static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute);
|
|
static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
|
|
static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
|
|
|
|
static inline bool IsDelimiterCharacter(int ch);
|
|
static inline bool IsNumberStartCharacter(int ch);
|
|
static inline bool IsNumberCharacter(int ch);
|
|
static inline bool IsSeparatorOrDelimiterCharacter(int ch);
|
|
static bool IsValidIdentifier(const std::string& identifier);
|
|
static bool IsValidNumber(const std::string& number);
|
|
static inline bool IsWordStartCharacter(int ch);
|
|
static inline bool IsWordCharacter(int ch);
|
|
|
|
static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) {
|
|
apostropheStartsAttribute = true;
|
|
|
|
sc.SetState(SCE_ADA_CHARACTER);
|
|
|
|
// Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
|
|
// is handled correctly)
|
|
sc.Forward();
|
|
sc.Forward();
|
|
|
|
ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL);
|
|
}
|
|
|
|
static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) {
|
|
while (!sc.atLineEnd && !sc.Match(chEnd)) {
|
|
sc.Forward();
|
|
}
|
|
|
|
if (!sc.atLineEnd) {
|
|
sc.ForwardSetState(SCE_ADA_DEFAULT);
|
|
} else {
|
|
sc.ChangeState(stateEOL);
|
|
}
|
|
}
|
|
|
|
static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
|
|
// Apostrophe meaning is not changed, but the parameter is present for uniformity
|
|
|
|
sc.SetState(SCE_ADA_COMMENTLINE);
|
|
|
|
while (!sc.atLineEnd) {
|
|
sc.Forward();
|
|
}
|
|
}
|
|
|
|
static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
|
|
apostropheStartsAttribute = sc.Match (')');
|
|
sc.SetState(SCE_ADA_DELIMITER);
|
|
sc.ForwardSetState(SCE_ADA_DEFAULT);
|
|
}
|
|
|
|
static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
|
|
apostropheStartsAttribute = false;
|
|
|
|
sc.SetState(SCE_ADA_LABEL);
|
|
|
|
// Skip "<<"
|
|
sc.Forward();
|
|
sc.Forward();
|
|
|
|
std::string identifier;
|
|
|
|
while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
|
|
identifier += static_cast<char>(tolower(sc.ch));
|
|
sc.Forward();
|
|
}
|
|
|
|
// Skip ">>"
|
|
if (sc.Match('>', '>')) {
|
|
sc.Forward();
|
|
sc.Forward();
|
|
} else {
|
|
sc.ChangeState(SCE_ADA_ILLEGAL);
|
|
}
|
|
|
|
// If the name is an invalid identifier or a keyword, then make it invalid label
|
|
if (!IsValidIdentifier(identifier) || keywords.InList(identifier.c_str())) {
|
|
sc.ChangeState(SCE_ADA_ILLEGAL);
|
|
}
|
|
|
|
sc.SetState(SCE_ADA_DEFAULT);
|
|
|
|
}
|
|
|
|
static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
|
|
apostropheStartsAttribute = true;
|
|
|
|
std::string number;
|
|
sc.SetState(SCE_ADA_NUMBER);
|
|
|
|
// Get all characters up to a delimiter or a separator, including points, but excluding
|
|
// double points (ranges).
|
|
while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) {
|
|
number += static_cast<char>(sc.ch);
|
|
sc.Forward();
|
|
}
|
|
|
|
// Special case: exponent with sign
|
|
if ((sc.chPrev == 'e' || sc.chPrev == 'E') &&
|
|
(sc.ch == '+' || sc.ch == '-')) {
|
|
number += static_cast<char>(sc.ch);
|
|
sc.Forward ();
|
|
|
|
while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
|
|
number += static_cast<char>(sc.ch);
|
|
sc.Forward();
|
|
}
|
|
}
|
|
|
|
if (!IsValidNumber(number)) {
|
|
sc.ChangeState(SCE_ADA_ILLEGAL);
|
|
}
|
|
|
|
sc.SetState(SCE_ADA_DEFAULT);
|
|
}
|
|
|
|
static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) {
|
|
apostropheStartsAttribute = true;
|
|
|
|
sc.SetState(SCE_ADA_STRING);
|
|
sc.Forward();
|
|
|
|
ColouriseContext(sc, '"', SCE_ADA_STRINGEOL);
|
|
}
|
|
|
|
static void ColouriseWhiteSpace(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
|
|
// Apostrophe meaning is not changed, but the parameter is present for uniformity
|
|
sc.SetState(SCE_ADA_DEFAULT);
|
|
sc.ForwardSetState(SCE_ADA_DEFAULT);
|
|
}
|
|
|
|
static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
|
|
apostropheStartsAttribute = true;
|
|
sc.SetState(SCE_ADA_IDENTIFIER);
|
|
|
|
std::string word;
|
|
|
|
while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
|
|
word += static_cast<char>(tolower(sc.ch));
|
|
sc.Forward();
|
|
}
|
|
|
|
if (!IsValidIdentifier(word)) {
|
|
sc.ChangeState(SCE_ADA_ILLEGAL);
|
|
|
|
} else if (keywords.InList(word.c_str())) {
|
|
sc.ChangeState(SCE_ADA_WORD);
|
|
|
|
if (word != "all") {
|
|
apostropheStartsAttribute = false;
|
|
}
|
|
}
|
|
|
|
sc.SetState(SCE_ADA_DEFAULT);
|
|
}
|
|
|
|
//
|
|
// ColouriseDocument
|
|
//
|
|
|
|
static void ColouriseDocument(
|
|
unsigned int startPos,
|
|
int length,
|
|
int initStyle,
|
|
WordList *keywordlists[],
|
|
Accessor &styler) {
|
|
WordList &keywords = *keywordlists[0];
|
|
|
|
StyleContext sc(startPos, length, initStyle, styler);
|
|
|
|
int lineCurrent = styler.GetLine(startPos);
|
|
bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
|
|
|
|
while (sc.More()) {
|
|
if (sc.atLineEnd) {
|
|
// Go to the next line
|
|
sc.Forward();
|
|
lineCurrent++;
|
|
|
|
// Remember the line state for future incremental lexing
|
|
styler.SetLineState(lineCurrent, apostropheStartsAttribute);
|
|
|
|
// Don't continue any styles on the next line
|
|
sc.SetState(SCE_ADA_DEFAULT);
|
|
}
|
|
|
|
// Comments
|
|
if (sc.Match('-', '-')) {
|
|
ColouriseComment(sc, apostropheStartsAttribute);
|
|
|
|
// Strings
|
|
} else if (sc.Match('"')) {
|
|
ColouriseString(sc, apostropheStartsAttribute);
|
|
|
|
// Characters
|
|
} else if (sc.Match('\'') && !apostropheStartsAttribute) {
|
|
ColouriseCharacter(sc, apostropheStartsAttribute);
|
|
|
|
// Labels
|
|
} else if (sc.Match('<', '<')) {
|
|
ColouriseLabel(sc, keywords, apostropheStartsAttribute);
|
|
|
|
// Whitespace
|
|
} else if (IsASpace(sc.ch)) {
|
|
ColouriseWhiteSpace(sc, apostropheStartsAttribute);
|
|
|
|
// Delimiters
|
|
} else if (IsDelimiterCharacter(sc.ch)) {
|
|
ColouriseDelimiter(sc, apostropheStartsAttribute);
|
|
|
|
// Numbers
|
|
} else if (IsADigit(sc.ch) || sc.ch == '#') {
|
|
ColouriseNumber(sc, apostropheStartsAttribute);
|
|
|
|
// Keywords or identifiers
|
|
} else {
|
|
ColouriseWord(sc, keywords, apostropheStartsAttribute);
|
|
}
|
|
}
|
|
|
|
sc.Complete();
|
|
}
|
|
|
|
static inline bool IsDelimiterCharacter(int ch) {
|
|
switch (ch) {
|
|
case '&':
|
|
case '\'':
|
|
case '(':
|
|
case ')':
|
|
case '*':
|
|
case '+':
|
|
case ',':
|
|
case '-':
|
|
case '.':
|
|
case '/':
|
|
case ':':
|
|
case ';':
|
|
case '<':
|
|
case '=':
|
|
case '>':
|
|
case '|':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool IsNumberCharacter(int ch) {
|
|
return IsNumberStartCharacter(ch) ||
|
|
ch == '_' ||
|
|
ch == '.' ||
|
|
ch == '#' ||
|
|
(ch >= 'a' && ch <= 'f') ||
|
|
(ch >= 'A' && ch <= 'F');
|
|
}
|
|
|
|
static inline bool IsNumberStartCharacter(int ch) {
|
|
return IsADigit(ch);
|
|
}
|
|
|
|
static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
|
|
return IsASpace(ch) || IsDelimiterCharacter(ch);
|
|
}
|
|
|
|
static bool IsValidIdentifier(const std::string& identifier) {
|
|
// First character can't be '_', so initialize the flag to true
|
|
bool lastWasUnderscore = true;
|
|
|
|
size_t length = identifier.length();
|
|
|
|
// Zero-length identifiers are not valid (these can occur inside labels)
|
|
if (length == 0) {
|
|
return false;
|
|
}
|
|
|
|
// Check for valid character at the start
|
|
if (!IsWordStartCharacter(identifier[0])) {
|
|
return false;
|
|
}
|
|
|
|
// Check for only valid characters and no double underscores
|
|
for (size_t i = 0; i < length; i++) {
|
|
if (!IsWordCharacter(identifier[i]) ||
|
|
(identifier[i] == '_' && lastWasUnderscore)) {
|
|
return false;
|
|
}
|
|
lastWasUnderscore = identifier[i] == '_';
|
|
}
|
|
|
|
// Check for underscore at the end
|
|
if (lastWasUnderscore == true) {
|
|
return false;
|
|
}
|
|
|
|
// All checks passed
|
|
return true;
|
|
}
|
|
|
|
static bool IsValidNumber(const std::string& number) {
|
|
size_t hashPos = number.find("#");
|
|
bool seenDot = false;
|
|
|
|
size_t i = 0;
|
|
size_t length = number.length();
|
|
|
|
if (length == 0)
|
|
return false; // Just in case
|
|
|
|
// Decimal number
|
|
if (hashPos == std::string::npos) {
|
|
bool canBeSpecial = false;
|
|
|
|
for (; i < length; i++) {
|
|
if (number[i] == '_') {
|
|
if (!canBeSpecial) {
|
|
return false;
|
|
}
|
|
canBeSpecial = false;
|
|
} else if (number[i] == '.') {
|
|
if (!canBeSpecial || seenDot) {
|
|
return false;
|
|
}
|
|
canBeSpecial = false;
|
|
seenDot = true;
|
|
} else if (IsADigit(number[i])) {
|
|
canBeSpecial = true;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!canBeSpecial)
|
|
return false;
|
|
} else {
|
|
// Based number
|
|
bool canBeSpecial = false;
|
|
int base = 0;
|
|
|
|
// Parse base
|
|
for (; i < length; i++) {
|
|
int ch = number[i];
|
|
if (ch == '_') {
|
|
if (!canBeSpecial)
|
|
return false;
|
|
canBeSpecial = false;
|
|
} else if (IsADigit(ch)) {
|
|
base = base * 10 + (ch - '0');
|
|
if (base > 16)
|
|
return false;
|
|
canBeSpecial = true;
|
|
} else if (ch == '#' && canBeSpecial) {
|
|
break;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (base < 2)
|
|
return false;
|
|
if (i == length)
|
|
return false;
|
|
|
|
i++; // Skip over '#'
|
|
|
|
// Parse number
|
|
canBeSpecial = false;
|
|
|
|
for (; i < length; i++) {
|
|
int ch = tolower(number[i]);
|
|
|
|
if (ch == '_') {
|
|
if (!canBeSpecial) {
|
|
return false;
|
|
}
|
|
canBeSpecial = false;
|
|
|
|
} else if (ch == '.') {
|
|
if (!canBeSpecial || seenDot) {
|
|
return false;
|
|
}
|
|
canBeSpecial = false;
|
|
seenDot = true;
|
|
|
|
} else if (IsADigit(ch)) {
|
|
if (ch - '0' >= base) {
|
|
return false;
|
|
}
|
|
canBeSpecial = true;
|
|
|
|
} else if (ch >= 'a' && ch <= 'f') {
|
|
if (ch - 'a' + 10 >= base) {
|
|
return false;
|
|
}
|
|
canBeSpecial = true;
|
|
|
|
} else if (ch == '#' && canBeSpecial) {
|
|
break;
|
|
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (i == length) {
|
|
return false;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
// Exponent (optional)
|
|
if (i < length) {
|
|
if (number[i] != 'e' && number[i] != 'E')
|
|
return false;
|
|
|
|
i++; // Move past 'E'
|
|
|
|
if (i == length) {
|
|
return false;
|
|
}
|
|
|
|
if (number[i] == '+')
|
|
i++;
|
|
else if (number[i] == '-') {
|
|
if (seenDot) {
|
|
i++;
|
|
} else {
|
|
return false; // Integer literals should not have negative exponents
|
|
}
|
|
}
|
|
|
|
if (i == length) {
|
|
return false;
|
|
}
|
|
|
|
bool canBeSpecial = false;
|
|
|
|
for (; i < length; i++) {
|
|
if (number[i] == '_') {
|
|
if (!canBeSpecial) {
|
|
return false;
|
|
}
|
|
canBeSpecial = false;
|
|
} else if (IsADigit(number[i])) {
|
|
canBeSpecial = true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!canBeSpecial)
|
|
return false;
|
|
}
|
|
|
|
// if i == length, number was parsed successfully.
|
|
return i == length;
|
|
}
|
|
|
|
static inline bool IsWordCharacter(int ch) {
|
|
return IsWordStartCharacter(ch) || IsADigit(ch);
|
|
}
|
|
|
|
static inline bool IsWordStartCharacter(int ch) {
|
|
return (isascii(ch) && isalpha(ch)) || ch == '_';
|
|
}
|