]> git.saurik.com Git - wxWidgets.git/blame - src/stc/scintilla/lexers/LexLua.cxx
simplify code so it always returns the same object
[wxWidgets.git] / src / stc / scintilla / lexers / LexLua.cxx
CommitLineData
65ec6247
RD
1// Scintilla source code edit control
2/** @file LexLua.cxx
3 ** Lexer for Lua language.
4 **
5 ** Written by Paul Winwood.
6 ** Folder by Alexey Yutkin.
a834585d 7 ** Modified by Marcos E. Wurzius & Philippe Lhoste
65ec6247 8 **/
d134f170
RD
9
10#include <stdlib.h>
11#include <string.h>
d134f170 12#include <stdio.h>
1dcf666d
RD
13#include <stdarg.h>
14#include <assert.h>
15#include <ctype.h>
d134f170 16
1dcf666d
RD
17#include "ILexer.h"
18#include "Scintilla.h"
19#include "SciLexer.h"
d134f170 20
1dcf666d
RD
21#include "WordList.h"
22#include "LexAccessor.h"
d134f170 23#include "Accessor.h"
1a2fb4cd 24#include "StyleContext.h"
9e96e16f 25#include "CharacterSet.h"
1dcf666d 26#include "LexerModule.h"
d134f170 27
7e0c58e9
RD
28#ifdef SCI_NAMESPACE
29using namespace Scintilla;
30#endif
31
1e9bafca
RD
32// Test for [=[ ... ]=] delimiters, returns 0 if it's only a [ or ],
33// return 1 for [[ or ]], returns >=2 for [=[ or ]=] and so on.
34// The maximum number of '=' characters allowed is 254.
35static int LongDelimCheck(StyleContext &sc) {
36 int sep = 1;
37 while (sc.GetRelative(sep) == '=' && sep < 0xFF)
38 sep++;
39 if (sc.GetRelative(sep) == sc.ch)
40 return sep;
41 return 0;
42}
43
a834585d
RD
44static void ColouriseLuaDoc(
45 unsigned int startPos,
46 int length,
47 int initStyle,
48 WordList *keywordlists[],
49 Accessor &styler) {
65ec6247
RD
50
51 WordList &keywords = *keywordlists[0];
1a2fb4cd
RD
52 WordList &keywords2 = *keywordlists[1];
53 WordList &keywords3 = *keywordlists[2];
54 WordList &keywords4 = *keywordlists[3];
55 WordList &keywords5 = *keywordlists[4];
56 WordList &keywords6 = *keywordlists[5];
88a8b04e
RD
57 WordList &keywords7 = *keywordlists[6];
58 WordList &keywords8 = *keywordlists[7];
a834585d 59
9e96e16f
RD
60 // Accepts accented characters
61 CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
1dcf666d 62 CharacterSet setWord(CharacterSet::setAlphaNum, "_", 0x80, true);
9e96e16f 63 // Not exactly following number definition (several dots are seen as OK, etc.)
1dcf666d
RD
64 // but probably enough in most cases. [pP] is for hex floats.
65 CharacterSet setNumber(CharacterSet::setDigits, ".-+abcdefpABCDEFP");
66 CharacterSet setExponent(CharacterSet::setNone, "eEpP");
9e96e16f
RD
67 CharacterSet setLuaOperator(CharacterSet::setNone, "*/-+()={}~[];<>,.^%:#");
68 CharacterSet setEscapeSkip(CharacterSet::setNone, "\"'\\");
69
9e730a78 70 int currentLine = styler.GetLine(startPos);
1e9bafca
RD
71 // Initialize long string [[ ... ]] or block comment --[[ ... ]] nesting level,
72 // if we are inside such a string. Block comment was introduced in Lua 5.0,
73 // blocks with separators [=[ ... ]=] in Lua 5.1.
1dcf666d 74 // Continuation of a string (\z whitespace escaping) is controlled by stringWs.
1e9bafca
RD
75 int nestLevel = 0;
76 int sepCount = 0;
1dcf666d
RD
77 int stringWs = 0;
78 if (initStyle == SCE_LUA_LITERALSTRING || initStyle == SCE_LUA_COMMENT ||
79 initStyle == SCE_LUA_STRING || initStyle == SCE_LUA_CHARACTER) {
1e9bafca 80 int lineState = styler.GetLineState(currentLine - 1);
1dcf666d 81 nestLevel = lineState >> 9;
1e9bafca 82 sepCount = lineState & 0xFF;
1dcf666d 83 stringWs = lineState & 0x100;
a834585d 84 }
1a2fb4cd
RD
85
86 // Do not leak onto next line
1e9bafca 87 if (initStyle == SCE_LUA_STRINGEOL || initStyle == SCE_LUA_COMMENTLINE || initStyle == SCE_LUA_PREPROCESSOR) {
1a2fb4cd 88 initStyle = SCE_LUA_DEFAULT;
a834585d 89 }
1a2fb4cd
RD
90
91 StyleContext sc(startPos, length, initStyle, styler);
a834585d 92 if (startPos == 0 && sc.ch == '#') {
9e730a78 93 // shbang line: # is a comment only if first char of the script
a834585d
RD
94 sc.SetState(SCE_LUA_COMMENTLINE);
95 }
1a2fb4cd 96 for (; sc.More(); sc.Forward()) {
9e730a78
RD
97 if (sc.atLineEnd) {
98 // Update the line state, so it can be seen by next line
99 currentLine = styler.GetLine(sc.currentPos);
100 switch (sc.state) {
101 case SCE_LUA_LITERALSTRING:
1e9bafca 102 case SCE_LUA_COMMENT:
1dcf666d
RD
103 case SCE_LUA_STRING:
104 case SCE_LUA_CHARACTER:
105 // Inside a literal string, block comment or string, we set the line state
106 styler.SetLineState(currentLine, (nestLevel << 9) | stringWs | sepCount);
9e730a78
RD
107 break;
108 default:
109 // Reset the line state
110 styler.SetLineState(currentLine, 0);
111 break;
112 }
113 }
a834585d
RD
114 if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) {
115 // Prevent SCE_LUA_STRINGEOL from leaking back to previous line
116 sc.SetState(SCE_LUA_STRING);
117 }
118
119 // Handle string line continuation
120 if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) &&
9e96e16f 121 sc.ch == '\\') {
a834585d 122 if (sc.chNext == '\n' || sc.chNext == '\r') {
1a2fb4cd 123 sc.Forward();
a834585d
RD
124 if (sc.ch == '\r' && sc.chNext == '\n') {
125 sc.Forward();
126 }
1a2fb4cd 127 continue;
65ec6247
RD
128 }
129 }
130
1a2fb4cd
RD
131 // Determine if the current state should terminate.
132 if (sc.state == SCE_LUA_OPERATOR) {
1dcf666d
RD
133 if (sc.ch == ':' && sc.chPrev == ':') { // :: <label> :: forward scan
134 sc.Forward();
135 int ln = 0, maxln = startPos + length - sc.currentPos;
136 int c;
137 while (ln < maxln) { // determine line extent
138 c = sc.GetRelative(ln);
139 if (c == '\r' || c == '\n')
140 break;
141 ln++;
142 }
143 maxln = ln; ln = 0;
144 while (ln < maxln) { // skip over spaces/tabs
145 if (!IsASpaceOrTab(sc.GetRelative(ln)))
146 break;
147 ln++;
148 }
149 int ws1 = ln;
150 if (setWordStart.Contains(sc.GetRelative(ln))) {
151 int i = 0;
152 char s[100];
153 while (ln < maxln) { // get potential label
154 c = sc.GetRelative(ln);
155 if (!setWord.Contains(c))
156 break;
157 if (i < 90)
158 s[i++] = c;
159 ln++;
160 }
161 s[i] = '\0'; int lbl = ln;
162 if (!keywords.InList(s)) {
163 while (ln < maxln) { // skip over spaces/tabs
164 if (!IsASpaceOrTab(sc.GetRelative(ln)))
165 break;
166 ln++;
167 }
168 int ws2 = ln - lbl;
169 if (sc.GetRelative(ln) == ':' && sc.GetRelative(ln + 1) == ':') {
170 // final :: found, complete valid label construct
171 sc.ChangeState(SCE_LUA_LABEL);
172 if (ws1) {
173 sc.SetState(SCE_LUA_DEFAULT);
174 sc.Forward(ws1);
175 }
176 sc.SetState(SCE_LUA_LABEL);
177 sc.Forward(lbl - ws1);
178 if (ws2) {
179 sc.SetState(SCE_LUA_DEFAULT);
180 sc.Forward(ws2);
181 }
182 sc.SetState(SCE_LUA_LABEL);
183 sc.Forward(2);
184 }
185 }
186 }
187 }
1a2fb4cd
RD
188 sc.SetState(SCE_LUA_DEFAULT);
189 } else if (sc.state == SCE_LUA_NUMBER) {
1dcf666d 190 // We stop the number definition on non-numerical non-dot non-eEpP non-sign non-hexdigit char
9e96e16f 191 if (!setNumber.Contains(sc.ch)) {
8e54aaed 192 sc.SetState(SCE_LUA_DEFAULT);
7e0c58e9 193 } else if (sc.ch == '-' || sc.ch == '+') {
1dcf666d 194 if (!setExponent.Contains(sc.chPrev))
9e96e16f
RD
195 sc.SetState(SCE_LUA_DEFAULT);
196 }
1a2fb4cd 197 } else if (sc.state == SCE_LUA_IDENTIFIER) {
1dcf666d 198 if (!(setWord.Contains(sc.ch) || sc.ch == '.') || sc.Match('.', '.')) {
1a2fb4cd
RD
199 char s[100];
200 sc.GetCurrent(s, sizeof(s));
201 if (keywords.InList(s)) {
202 sc.ChangeState(SCE_LUA_WORD);
1dcf666d
RD
203 if (strcmp(s, "goto") == 0) { // goto <label> forward scan
204 sc.SetState(SCE_LUA_DEFAULT);
205 while (IsASpaceOrTab(sc.ch) && !sc.atLineEnd)
206 sc.Forward();
207 if (setWordStart.Contains(sc.ch)) {
208 sc.SetState(SCE_LUA_LABEL);
209 sc.Forward();
210 while (setWord.Contains(sc.ch))
211 sc.Forward();
212 sc.GetCurrent(s, sizeof(s));
213 if (keywords.InList(s))
214 sc.ChangeState(SCE_LUA_WORD);
215 }
216 sc.SetState(SCE_LUA_DEFAULT);
217 }
1a2fb4cd
RD
218 } else if (keywords2.InList(s)) {
219 sc.ChangeState(SCE_LUA_WORD2);
220 } else if (keywords3.InList(s)) {
221 sc.ChangeState(SCE_LUA_WORD3);
222 } else if (keywords4.InList(s)) {
223 sc.ChangeState(SCE_LUA_WORD4);
224 } else if (keywords5.InList(s)) {
225 sc.ChangeState(SCE_LUA_WORD5);
226 } else if (keywords6.InList(s)) {
227 sc.ChangeState(SCE_LUA_WORD6);
88a8b04e
RD
228 } else if (keywords7.InList(s)) {
229 sc.ChangeState(SCE_LUA_WORD7);
230 } else if (keywords8.InList(s)) {
231 sc.ChangeState(SCE_LUA_WORD8);
65ec6247 232 }
1a2fb4cd 233 sc.SetState(SCE_LUA_DEFAULT);
65ec6247 234 }
1e9bafca 235 } else if (sc.state == SCE_LUA_COMMENTLINE || sc.state == SCE_LUA_PREPROCESSOR) {
1a2fb4cd 236 if (sc.atLineEnd) {
1e9bafca 237 sc.ForwardSetState(SCE_LUA_DEFAULT);
a834585d 238 }
1a2fb4cd 239 } else if (sc.state == SCE_LUA_STRING) {
1dcf666d
RD
240 if (stringWs) {
241 if (!IsASpace(sc.ch))
242 stringWs = 0;
243 }
1a2fb4cd 244 if (sc.ch == '\\') {
9e96e16f 245 if (setEscapeSkip.Contains(sc.chNext)) {
1a2fb4cd 246 sc.Forward();
1dcf666d
RD
247 } else if (sc.chNext == 'z') {
248 sc.Forward();
249 stringWs = 0x100;
65ec6247 250 }
1a2fb4cd
RD
251 } else if (sc.ch == '\"') {
252 sc.ForwardSetState(SCE_LUA_DEFAULT);
1dcf666d 253 } else if (stringWs == 0 && sc.atLineEnd) {
1a2fb4cd
RD
254 sc.ChangeState(SCE_LUA_STRINGEOL);
255 sc.ForwardSetState(SCE_LUA_DEFAULT);
65ec6247 256 }
1a2fb4cd 257 } else if (sc.state == SCE_LUA_CHARACTER) {
1dcf666d
RD
258 if (stringWs) {
259 if (!IsASpace(sc.ch))
260 stringWs = 0;
261 }
1a2fb4cd 262 if (sc.ch == '\\') {
9e96e16f 263 if (setEscapeSkip.Contains(sc.chNext)) {
1a2fb4cd 264 sc.Forward();
1dcf666d
RD
265 } else if (sc.chNext == 'z') {
266 sc.Forward();
267 stringWs = 0x100;
65ec6247 268 }
1a2fb4cd
RD
269 } else if (sc.ch == '\'') {
270 sc.ForwardSetState(SCE_LUA_DEFAULT);
1dcf666d 271 } else if (stringWs == 0 && sc.atLineEnd) {
1a2fb4cd
RD
272 sc.ChangeState(SCE_LUA_STRINGEOL);
273 sc.ForwardSetState(SCE_LUA_DEFAULT);
274 }
1e9bafca
RD
275 } else if (sc.state == SCE_LUA_LITERALSTRING || sc.state == SCE_LUA_COMMENT) {
276 if (sc.ch == '[') {
277 int sep = LongDelimCheck(sc);
278 if (sep == 1 && sepCount == 1) { // [[-only allowed to nest
279 nestLevel++;
280 sc.Forward();
9e730a78 281 }
1e9bafca
RD
282 } else if (sc.ch == ']') {
283 int sep = LongDelimCheck(sc);
284 if (sep == 1 && sepCount == 1) { // un-nest with ]]-only
285 nestLevel--;
286 sc.Forward();
287 if (nestLevel == 0) {
288 sc.ForwardSetState(SCE_LUA_DEFAULT);
289 }
290 } else if (sep > 1 && sep == sepCount) { // ]=]-style delim
291 sc.Forward(sep);
9e730a78 292 sc.ForwardSetState(SCE_LUA_DEFAULT);
a834585d 293 }
1a2fb4cd 294 }
a834585d 295 }
9e730a78 296
1a2fb4cd
RD
297 // Determine if a new state should be entered.
298 if (sc.state == SCE_LUA_DEFAULT) {
299 if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
a834585d 300 sc.SetState(SCE_LUA_NUMBER);
b8193d80 301 if (sc.ch == '0' && toupper(sc.chNext) == 'X') {
9e96e16f 302 sc.Forward();
b8193d80 303 }
9e96e16f 304 } else if (setWordStart.Contains(sc.ch)) {
a834585d 305 sc.SetState(SCE_LUA_IDENTIFIER);
1e9bafca 306 } else if (sc.ch == '\"') {
1a2fb4cd 307 sc.SetState(SCE_LUA_STRING);
1dcf666d 308 stringWs = 0;
1e9bafca 309 } else if (sc.ch == '\'') {
1a2fb4cd 310 sc.SetState(SCE_LUA_CHARACTER);
1dcf666d 311 stringWs = 0;
1e9bafca
RD
312 } else if (sc.ch == '[') {
313 sepCount = LongDelimCheck(sc);
314 if (sepCount == 0) {
315 sc.SetState(SCE_LUA_OPERATOR);
316 } else {
317 nestLevel = 1;
318 sc.SetState(SCE_LUA_LITERALSTRING);
319 sc.Forward(sepCount);
320 }
a834585d 321 } else if (sc.Match('-', '-')) {
1a2fb4cd 322 sc.SetState(SCE_LUA_COMMENTLINE);
1e9bafca
RD
323 if (sc.Match("--[")) {
324 sc.Forward(2);
325 sepCount = LongDelimCheck(sc);
326 if (sepCount > 0) {
327 nestLevel = 1;
328 sc.ChangeState(SCE_LUA_COMMENT);
329 sc.Forward(sepCount);
330 }
331 } else {
332 sc.Forward();
333 }
9e730a78 334 } else if (sc.atLineStart && sc.Match('$')) {
a834585d 335 sc.SetState(SCE_LUA_PREPROCESSOR); // Obsolete since Lua 4.0, but still in old code
9e96e16f 336 } else if (setLuaOperator.Contains(sc.ch)) {
1a2fb4cd 337 sc.SetState(SCE_LUA_OPERATOR);
65ec6247
RD
338 }
339 }
65ec6247 340 }
9e96e16f 341
1dcf666d 342 if (setWord.Contains(sc.chPrev) || sc.chPrev == '.') {
9e96e16f
RD
343 char s[100];
344 sc.GetCurrent(s, sizeof(s));
345 if (keywords.InList(s)) {
346 sc.ChangeState(SCE_LUA_WORD);
347 } else if (keywords2.InList(s)) {
348 sc.ChangeState(SCE_LUA_WORD2);
349 } else if (keywords3.InList(s)) {
350 sc.ChangeState(SCE_LUA_WORD3);
351 } else if (keywords4.InList(s)) {
352 sc.ChangeState(SCE_LUA_WORD4);
353 } else if (keywords5.InList(s)) {
354 sc.ChangeState(SCE_LUA_WORD5);
355 } else if (keywords6.InList(s)) {
356 sc.ChangeState(SCE_LUA_WORD6);
357 } else if (keywords7.InList(s)) {
358 sc.ChangeState(SCE_LUA_WORD7);
359 } else if (keywords8.InList(s)) {
360 sc.ChangeState(SCE_LUA_WORD8);
361 }
362 }
363
1a2fb4cd 364 sc.Complete();
65ec6247 365}
d134f170 366
65ec6247
RD
367static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
368 Accessor &styler) {
369 unsigned int lengthDoc = startPos + length;
370 int visibleChars = 0;
371 int lineCurrent = styler.GetLine(startPos);
372 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
373 int levelCurrent = levelPrev;
374 char chNext = styler[startPos];
1a2fb4cd 375 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
65ec6247
RD
376 int styleNext = styler.StyleAt(startPos);
377 char s[10];
a834585d 378
65ec6247
RD
379 for (unsigned int i = startPos; i < lengthDoc; i++) {
380 char ch = chNext;
381 chNext = styler.SafeGetCharAt(i + 1);
382 int style = styleNext;
383 styleNext = styler.StyleAt(i + 1);
384 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
1a2fb4cd 385 if (style == SCE_LUA_WORD) {
1e9bafca 386 if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') {
65ec6247 387 for (unsigned int j = 0; j < 8; j++) {
a834585d 388 if (!iswordchar(styler[i + j])) {
1a2fb4cd 389 break;
a834585d 390 }
65ec6247
RD
391 s[j] = styler[i + j];
392 s[j + 1] = '\0';
393 }
a834585d 394
1e9bafca 395 if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
65ec6247 396 levelCurrent++;
a834585d 397 }
1e9bafca 398 if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
1a2fb4cd 399 levelCurrent--;
a834585d 400 }
65ec6247 401 }
a834585d
RD
402 } else if (style == SCE_LUA_OPERATOR) {
403 if (ch == '{' || ch == '(') {
1a2fb4cd 404 levelCurrent++;
a834585d 405 } else if (ch == '}' || ch == ')') {
1a2fb4cd 406 levelCurrent--;
a834585d 407 }
1e9bafca
RD
408 } else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
409 if (ch == '[') {
410 levelCurrent++;
411 } else if (ch == ']') {
412 levelCurrent--;
413 }
1a2fb4cd 414 }
a834585d 415
65ec6247
RD
416 if (atEOL) {
417 int lev = levelPrev;
a834585d 418 if (visibleChars == 0 && foldCompact) {
65ec6247 419 lev |= SC_FOLDLEVELWHITEFLAG;
a834585d
RD
420 }
421 if ((levelCurrent > levelPrev) && (visibleChars > 0)) {
65ec6247 422 lev |= SC_FOLDLEVELHEADERFLAG;
a834585d 423 }
65ec6247
RD
424 if (lev != styler.LevelAt(lineCurrent)) {
425 styler.SetLevel(lineCurrent, lev);
426 }
427 lineCurrent++;
428 levelPrev = levelCurrent;
429 visibleChars = 0;
430 }
a834585d 431 if (!isspacechar(ch)) {
65ec6247 432 visibleChars++;
a834585d 433 }
65ec6247
RD
434 }
435 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
1a2fb4cd 436
65ec6247
RD
437 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
438 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
d134f170
RD
439}
440
9e730a78
RD
441static const char * const luaWordListDesc[] = {
442 "Keywords",
443 "Basic functions",
88a8b04e
RD
444 "String, (table) & math functions",
445 "(coroutines), I/O & system facilities",
1e9bafca
RD
446 "user1",
447 "user2",
448 "user3",
449 "user4",
9e730a78
RD
450 0
451};
452
453LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);