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.
357 lines
11 KiB
357 lines
11 KiB
// Scintilla source code edit control |
|
/** @file LexLua.cxx |
|
** Lexer for Lua language. |
|
** |
|
** Written by Paul Winwood. |
|
** Folder by Alexey Yutkin. |
|
** Modified by Marcos E. Wurzius & Philippe Lhoste |
|
**/ |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <ctype.h> |
|
#include <stdarg.h> |
|
#include <stdio.h> |
|
|
|
#include "Platform.h" |
|
|
|
#include "PropSet.h" |
|
#include "Accessor.h" |
|
#include "StyleContext.h" |
|
#include "KeyWords.h" |
|
#include "Scintilla.h" |
|
#include "SciLexer.h" |
|
|
|
// Extended to accept accented characters |
|
static inline bool IsAWordChar(int ch) { |
|
return ch >= 0x80 || |
|
(isalnum(ch) || ch == '.' || ch == '_'); |
|
} |
|
|
|
static inline bool IsAWordStart(int ch) { |
|
return ch >= 0x80 || |
|
(isalpha(ch) || ch == '_'); |
|
} |
|
|
|
static inline bool IsANumberChar(int ch) { |
|
// Not exactly following number definition (several dots are seen as OK, etc.) |
|
// but probably enough in most cases. |
|
return (ch < 0x80) && |
|
(isdigit(ch) || toupper(ch) == 'E' || |
|
ch == '.' || ch == '-' || ch == '+' || |
|
(ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')); |
|
} |
|
|
|
static inline bool IsLuaOperator(int ch) { |
|
if (ch >= 0x80 || isalnum(ch)) { |
|
return false; |
|
} |
|
// '.' left out as it is used to make up numbers |
|
if (ch == '*' || ch == '/' || ch == '-' || ch == '+' || |
|
ch == '(' || ch == ')' || ch == '=' || |
|
ch == '{' || ch == '}' || ch == '~' || |
|
ch == '[' || ch == ']' || ch == ';' || |
|
ch == '<' || ch == '>' || ch == ',' || |
|
ch == '.' || ch == '^' || ch == '%' || ch == ':' || |
|
ch == '#') { |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
// Test for [=[ ... ]=] delimiters, returns 0 if it's only a [ or ], |
|
// return 1 for [[ or ]], returns >=2 for [=[ or ]=] and so on. |
|
// The maximum number of '=' characters allowed is 254. |
|
static int LongDelimCheck(StyleContext &sc) { |
|
int sep = 1; |
|
while (sc.GetRelative(sep) == '=' && sep < 0xFF) |
|
sep++; |
|
if (sc.GetRelative(sep) == sc.ch) |
|
return sep; |
|
return 0; |
|
} |
|
|
|
static void ColouriseLuaDoc( |
|
unsigned int startPos, |
|
int length, |
|
int initStyle, |
|
WordList *keywordlists[], |
|
Accessor &styler) { |
|
|
|
WordList &keywords = *keywordlists[0]; |
|
WordList &keywords2 = *keywordlists[1]; |
|
WordList &keywords3 = *keywordlists[2]; |
|
WordList &keywords4 = *keywordlists[3]; |
|
WordList &keywords5 = *keywordlists[4]; |
|
WordList &keywords6 = *keywordlists[5]; |
|
WordList &keywords7 = *keywordlists[6]; |
|
WordList &keywords8 = *keywordlists[7]; |
|
|
|
int currentLine = styler.GetLine(startPos); |
|
// Initialize long string [[ ... ]] or block comment --[[ ... ]] nesting level, |
|
// if we are inside such a string. Block comment was introduced in Lua 5.0, |
|
// blocks with separators [=[ ... ]=] in Lua 5.1. |
|
int nestLevel = 0; |
|
int sepCount = 0; |
|
if (initStyle == SCE_LUA_LITERALSTRING || initStyle == SCE_LUA_COMMENT) { |
|
int lineState = styler.GetLineState(currentLine - 1); |
|
nestLevel = lineState >> 8; |
|
sepCount = lineState & 0xFF; |
|
} |
|
|
|
// Do not leak onto next line |
|
if (initStyle == SCE_LUA_STRINGEOL || initStyle == SCE_LUA_COMMENTLINE || initStyle == SCE_LUA_PREPROCESSOR) { |
|
initStyle = SCE_LUA_DEFAULT; |
|
} |
|
|
|
StyleContext sc(startPos, length, initStyle, styler); |
|
if (startPos == 0 && sc.ch == '#') { |
|
// shbang line: # is a comment only if first char of the script |
|
sc.SetState(SCE_LUA_COMMENTLINE); |
|
} |
|
for (; sc.More(); sc.Forward()) { |
|
if (sc.atLineEnd) { |
|
// Update the line state, so it can be seen by next line |
|
currentLine = styler.GetLine(sc.currentPos); |
|
switch (sc.state) { |
|
case SCE_LUA_LITERALSTRING: |
|
case SCE_LUA_COMMENT: |
|
// Inside a literal string or block comment, we set the line state |
|
styler.SetLineState(currentLine, (nestLevel << 8) | sepCount); |
|
break; |
|
default: |
|
// Reset the line state |
|
styler.SetLineState(currentLine, 0); |
|
break; |
|
} |
|
} |
|
if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) { |
|
// Prevent SCE_LUA_STRINGEOL from leaking back to previous line |
|
sc.SetState(SCE_LUA_STRING); |
|
} |
|
|
|
// Handle string line continuation |
|
if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) && |
|
sc.ch == '\\') { |
|
if (sc.chNext == '\n' || sc.chNext == '\r') { |
|
sc.Forward(); |
|
if (sc.ch == '\r' && sc.chNext == '\n') { |
|
sc.Forward(); |
|
} |
|
continue; |
|
} |
|
} |
|
|
|
// Determine if the current state should terminate. |
|
if (sc.state == SCE_LUA_OPERATOR) { |
|
sc.SetState(SCE_LUA_DEFAULT); |
|
} else if (sc.state == SCE_LUA_NUMBER) { |
|
// We stop the number definition on non-numerical non-dot non-eE non-sign non-hexdigit char |
|
if (!IsANumberChar(sc.ch)) { |
|
sc.SetState(SCE_LUA_DEFAULT); |
|
} |
|
} else if (sc.state == SCE_LUA_IDENTIFIER) { |
|
if (!IsAWordChar(sc.ch) || sc.Match('.', '.')) { |
|
char s[100]; |
|
sc.GetCurrent(s, sizeof(s)); |
|
if (keywords.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD); |
|
} else if (keywords2.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD2); |
|
} else if (keywords3.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD3); |
|
} else if (keywords4.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD4); |
|
} else if (keywords5.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD5); |
|
} else if (keywords6.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD6); |
|
} else if (keywords6.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD6); |
|
} else if (keywords7.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD7); |
|
} else if (keywords8.InList(s)) { |
|
sc.ChangeState(SCE_LUA_WORD8); |
|
} |
|
sc.SetState(SCE_LUA_DEFAULT); |
|
} |
|
} else if (sc.state == SCE_LUA_COMMENTLINE || sc.state == SCE_LUA_PREPROCESSOR) { |
|
if (sc.atLineEnd) { |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} |
|
} else if (sc.state == SCE_LUA_STRING) { |
|
if (sc.ch == '\\') { |
|
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
|
sc.Forward(); |
|
} |
|
} else if (sc.ch == '\"') { |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
sc.ChangeState(SCE_LUA_STRINGEOL); |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} |
|
} else if (sc.state == SCE_LUA_CHARACTER) { |
|
if (sc.ch == '\\') { |
|
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
|
sc.Forward(); |
|
} |
|
} else if (sc.ch == '\'') { |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
sc.ChangeState(SCE_LUA_STRINGEOL); |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} |
|
} else if (sc.state == SCE_LUA_LITERALSTRING || sc.state == SCE_LUA_COMMENT) { |
|
if (sc.ch == '[') { |
|
int sep = LongDelimCheck(sc); |
|
if (sep == 1 && sepCount == 1) { // [[-only allowed to nest |
|
nestLevel++; |
|
sc.Forward(); |
|
} |
|
} else if (sc.ch == ']') { |
|
int sep = LongDelimCheck(sc); |
|
if (sep == 1 && sepCount == 1) { // un-nest with ]]-only |
|
nestLevel--; |
|
sc.Forward(); |
|
if (nestLevel == 0) { |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} |
|
} else if (sep > 1 && sep == sepCount) { // ]=]-style delim |
|
sc.Forward(sep); |
|
sc.ForwardSetState(SCE_LUA_DEFAULT); |
|
} |
|
} |
|
} |
|
|
|
// Determine if a new state should be entered. |
|
if (sc.state == SCE_LUA_DEFAULT) { |
|
if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { |
|
sc.SetState(SCE_LUA_NUMBER); |
|
if (sc.ch == '0' && toupper(sc.chNext) == 'X') { |
|
sc.Forward(1); |
|
} |
|
} else if (IsAWordStart(sc.ch)) { |
|
sc.SetState(SCE_LUA_IDENTIFIER); |
|
} else if (sc.ch == '\"') { |
|
sc.SetState(SCE_LUA_STRING); |
|
} else if (sc.ch == '\'') { |
|
sc.SetState(SCE_LUA_CHARACTER); |
|
} else if (sc.ch == '[') { |
|
sepCount = LongDelimCheck(sc); |
|
if (sepCount == 0) { |
|
sc.SetState(SCE_LUA_OPERATOR); |
|
} else { |
|
nestLevel = 1; |
|
sc.SetState(SCE_LUA_LITERALSTRING); |
|
sc.Forward(sepCount); |
|
} |
|
} else if (sc.Match('-', '-')) { |
|
sc.SetState(SCE_LUA_COMMENTLINE); |
|
if (sc.Match("--[")) { |
|
sc.Forward(2); |
|
sepCount = LongDelimCheck(sc); |
|
if (sepCount > 0) { |
|
nestLevel = 1; |
|
sc.ChangeState(SCE_LUA_COMMENT); |
|
sc.Forward(sepCount); |
|
} |
|
} else { |
|
sc.Forward(); |
|
} |
|
} else if (sc.atLineStart && sc.Match('$')) { |
|
sc.SetState(SCE_LUA_PREPROCESSOR); // Obsolete since Lua 4.0, but still in old code |
|
} else if (IsLuaOperator(static_cast<char>(sc.ch))) { |
|
sc.SetState(SCE_LUA_OPERATOR); |
|
} |
|
} |
|
} |
|
sc.Complete(); |
|
} |
|
|
|
static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[], |
|
Accessor &styler) { |
|
unsigned int lengthDoc = startPos + length; |
|
int visibleChars = 0; |
|
int lineCurrent = styler.GetLine(startPos); |
|
int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK; |
|
int levelCurrent = levelPrev; |
|
char chNext = styler[startPos]; |
|
bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; |
|
int styleNext = styler.StyleAt(startPos); |
|
char s[10]; |
|
|
|
for (unsigned int i = startPos; i < lengthDoc; i++) { |
|
char ch = chNext; |
|
chNext = styler.SafeGetCharAt(i + 1); |
|
int style = styleNext; |
|
styleNext = styler.StyleAt(i + 1); |
|
bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
|
if (style == SCE_LUA_WORD) { |
|
if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') { |
|
for (unsigned int j = 0; j < 8; j++) { |
|
if (!iswordchar(styler[i + j])) { |
|
break; |
|
} |
|
s[j] = styler[i + j]; |
|
s[j + 1] = '\0'; |
|
} |
|
|
|
if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) { |
|
levelCurrent++; |
|
} |
|
if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) { |
|
levelCurrent--; |
|
} |
|
} |
|
} else if (style == SCE_LUA_OPERATOR) { |
|
if (ch == '{' || ch == '(') { |
|
levelCurrent++; |
|
} else if (ch == '}' || ch == ')') { |
|
levelCurrent--; |
|
} |
|
} else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) { |
|
if (ch == '[') { |
|
levelCurrent++; |
|
} else if (ch == ']') { |
|
levelCurrent--; |
|
} |
|
} |
|
|
|
if (atEOL) { |
|
int lev = levelPrev; |
|
if (visibleChars == 0 && foldCompact) { |
|
lev |= SC_FOLDLEVELWHITEFLAG; |
|
} |
|
if ((levelCurrent > levelPrev) && (visibleChars > 0)) { |
|
lev |= SC_FOLDLEVELHEADERFLAG; |
|
} |
|
if (lev != styler.LevelAt(lineCurrent)) { |
|
styler.SetLevel(lineCurrent, lev); |
|
} |
|
lineCurrent++; |
|
levelPrev = levelCurrent; |
|
visibleChars = 0; |
|
} |
|
if (!isspacechar(ch)) { |
|
visibleChars++; |
|
} |
|
} |
|
// Fill in the real level of the next line, keeping the current flags as they will be filled in later |
|
|
|
int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK; |
|
styler.SetLevel(lineCurrent, levelPrev | flagsNext); |
|
} |
|
|
|
static const char * const luaWordListDesc[] = { |
|
"Keywords", |
|
"Basic functions", |
|
"String, (table) & math functions", |
|
"(coroutines), I/O & system facilities", |
|
"user1", |
|
"user2", |
|
"user3", |
|
"user4", |
|
0 |
|
}; |
|
|
|
LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);
|
|
|