]> git.saurik.com Git - wxWidgets.git/blob - src/stc/scintilla/src/LexLua.cxx
7f6df070acb081cb97d95962d2df1d7f2defb54d
[wxWidgets.git] / src / stc / scintilla / src / LexLua.cxx
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.
7 ** Modified by Marcos E. Wurzius & Philippe Lhoste
8 **/
9
10 #include <stdlib.h>
11 #include <string.h>
12 #include <ctype.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15
16 #include "Platform.h"
17
18 #include "PropSet.h"
19 #include "Accessor.h"
20 #include "StyleContext.h"
21 #include "KeyWords.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
24
25 // Extended to accept accented characters
26 static inline bool IsAWordChar(int ch) {
27 return ch >= 0x80 ||
28 (isalnum(ch) || ch == '.' || ch == '_');
29 }
30
31 static inline bool IsAWordStart(int ch) {
32 return ch >= 0x80 ||
33 (isalpha(ch) || ch == '_');
34 }
35
36 static inline bool IsANumberChar(int ch) {
37 // Not exactly following number definition (several dots are seen as OK, etc.)
38 // but probably enough in most cases.
39 return (ch < 0x80) &&
40 (isdigit(ch) || toupper(ch) == 'E' ||
41 ch == '.' || ch == '-' || ch == '+');
42 }
43
44 static inline bool IsLuaOperator(int ch) {
45 if (ch >= 0x80 || isalnum(ch)) {
46 return false;
47 }
48 // '.' left out as it is used to make up numbers
49 if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
50 ch == '(' || ch == ')' || ch == '=' ||
51 ch == '{' || ch == '}' || ch == '~' ||
52 ch == '[' || ch == ']' || ch == ';' ||
53 ch == '<' || ch == '>' || ch == ',' ||
54 ch == '.' || ch == '^' || ch == '%' || ch == ':') {
55 return true;
56 }
57 return false;
58 }
59
60 // Test for [=[ ... ]=] delimiters, returns 0 if it's only a [ or ],
61 // return 1 for [[ or ]], returns >=2 for [=[ or ]=] and so on.
62 // The maximum number of '=' characters allowed is 254.
63 static int LongDelimCheck(StyleContext &sc) {
64 int sep = 1;
65 while (sc.GetRelative(sep) == '=' && sep < 0xFF)
66 sep++;
67 if (sc.GetRelative(sep) == sc.ch)
68 return sep;
69 return 0;
70 }
71
72 static void ColouriseLuaDoc(
73 unsigned int startPos,
74 int length,
75 int initStyle,
76 WordList *keywordlists[],
77 Accessor &styler) {
78
79 WordList &keywords = *keywordlists[0];
80 WordList &keywords2 = *keywordlists[1];
81 WordList &keywords3 = *keywordlists[2];
82 WordList &keywords4 = *keywordlists[3];
83 WordList &keywords5 = *keywordlists[4];
84 WordList &keywords6 = *keywordlists[5];
85 WordList &keywords7 = *keywordlists[6];
86 WordList &keywords8 = *keywordlists[7];
87
88 int currentLine = styler.GetLine(startPos);
89 // Initialize long string [[ ... ]] or block comment --[[ ... ]] nesting level,
90 // if we are inside such a string. Block comment was introduced in Lua 5.0,
91 // blocks with separators [=[ ... ]=] in Lua 5.1.
92 int nestLevel = 0;
93 int sepCount = 0;
94 if (initStyle == SCE_LUA_LITERALSTRING || initStyle == SCE_LUA_COMMENT) {
95 int lineState = styler.GetLineState(currentLine - 1);
96 nestLevel = lineState >> 8;
97 sepCount = lineState & 0xFF;
98 }
99
100 // Do not leak onto next line
101 if (initStyle == SCE_LUA_STRINGEOL || initStyle == SCE_LUA_COMMENTLINE || initStyle == SCE_LUA_PREPROCESSOR) {
102 initStyle = SCE_LUA_DEFAULT;
103 }
104
105 StyleContext sc(startPos, length, initStyle, styler);
106 if (startPos == 0 && sc.ch == '#') {
107 // shbang line: # is a comment only if first char of the script
108 sc.SetState(SCE_LUA_COMMENTLINE);
109 }
110 for (; sc.More(); sc.Forward()) {
111 if (sc.atLineEnd) {
112 // Update the line state, so it can be seen by next line
113 currentLine = styler.GetLine(sc.currentPos);
114 switch (sc.state) {
115 case SCE_LUA_LITERALSTRING:
116 case SCE_LUA_COMMENT:
117 // Inside a literal string or block comment, we set the line state
118 styler.SetLineState(currentLine, (nestLevel << 8) | sepCount);
119 break;
120 default:
121 // Reset the line state
122 styler.SetLineState(currentLine, 0);
123 break;
124 }
125 }
126 if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) {
127 // Prevent SCE_LUA_STRINGEOL from leaking back to previous line
128 sc.SetState(SCE_LUA_STRING);
129 }
130
131 // Handle string line continuation
132 if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) &&
133 sc.ch == '\\') {
134 if (sc.chNext == '\n' || sc.chNext == '\r') {
135 sc.Forward();
136 if (sc.ch == '\r' && sc.chNext == '\n') {
137 sc.Forward();
138 }
139 continue;
140 }
141 }
142
143 // Determine if the current state should terminate.
144 if (sc.state == SCE_LUA_OPERATOR) {
145 sc.SetState(SCE_LUA_DEFAULT);
146 } else if (sc.state == SCE_LUA_NUMBER) {
147 // We stop the number definition on non-numerical non-dot non-eE non-sign char
148 if (!IsANumberChar(sc.ch)) {
149 sc.SetState(SCE_LUA_DEFAULT);
150 }
151 } else if (sc.state == SCE_LUA_IDENTIFIER) {
152 if (!IsAWordChar(sc.ch) || sc.Match('.', '.')) {
153 char s[100];
154 sc.GetCurrent(s, sizeof(s));
155 if (keywords.InList(s)) {
156 sc.ChangeState(SCE_LUA_WORD);
157 } else if (keywords2.InList(s)) {
158 sc.ChangeState(SCE_LUA_WORD2);
159 } else if (keywords3.InList(s)) {
160 sc.ChangeState(SCE_LUA_WORD3);
161 } else if (keywords4.InList(s)) {
162 sc.ChangeState(SCE_LUA_WORD4);
163 } else if (keywords5.InList(s)) {
164 sc.ChangeState(SCE_LUA_WORD5);
165 } else if (keywords6.InList(s)) {
166 sc.ChangeState(SCE_LUA_WORD6);
167 } else if (keywords6.InList(s)) {
168 sc.ChangeState(SCE_LUA_WORD6);
169 } else if (keywords7.InList(s)) {
170 sc.ChangeState(SCE_LUA_WORD7);
171 } else if (keywords8.InList(s)) {
172 sc.ChangeState(SCE_LUA_WORD8);
173 }
174 sc.SetState(SCE_LUA_DEFAULT);
175 }
176 } else if (sc.state == SCE_LUA_COMMENTLINE || sc.state == SCE_LUA_PREPROCESSOR) {
177 if (sc.atLineEnd) {
178 sc.ForwardSetState(SCE_LUA_DEFAULT);
179 }
180 } else if (sc.state == SCE_LUA_STRING) {
181 if (sc.ch == '\\') {
182 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
183 sc.Forward();
184 }
185 } else if (sc.ch == '\"') {
186 sc.ForwardSetState(SCE_LUA_DEFAULT);
187 } else if (sc.atLineEnd) {
188 sc.ChangeState(SCE_LUA_STRINGEOL);
189 sc.ForwardSetState(SCE_LUA_DEFAULT);
190 }
191 } else if (sc.state == SCE_LUA_CHARACTER) {
192 if (sc.ch == '\\') {
193 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
194 sc.Forward();
195 }
196 } else if (sc.ch == '\'') {
197 sc.ForwardSetState(SCE_LUA_DEFAULT);
198 } else if (sc.atLineEnd) {
199 sc.ChangeState(SCE_LUA_STRINGEOL);
200 sc.ForwardSetState(SCE_LUA_DEFAULT);
201 }
202 } else if (sc.state == SCE_LUA_LITERALSTRING || sc.state == SCE_LUA_COMMENT) {
203 if (sc.ch == '[') {
204 int sep = LongDelimCheck(sc);
205 if (sep == 1 && sepCount == 1) { // [[-only allowed to nest
206 nestLevel++;
207 sc.Forward();
208 }
209 } else if (sc.ch == ']') {
210 int sep = LongDelimCheck(sc);
211 if (sep == 1 && sepCount == 1) { // un-nest with ]]-only
212 nestLevel--;
213 sc.Forward();
214 if (nestLevel == 0) {
215 sc.ForwardSetState(SCE_LUA_DEFAULT);
216 }
217 } else if (sep > 1 && sep == sepCount) { // ]=]-style delim
218 sc.Forward(sep);
219 sc.ForwardSetState(SCE_LUA_DEFAULT);
220 }
221 }
222 }
223
224 // Determine if a new state should be entered.
225 if (sc.state == SCE_LUA_DEFAULT) {
226 if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
227 sc.SetState(SCE_LUA_NUMBER);
228 } else if (IsAWordStart(sc.ch)) {
229 sc.SetState(SCE_LUA_IDENTIFIER);
230 } else if (sc.ch == '\"') {
231 sc.SetState(SCE_LUA_STRING);
232 } else if (sc.ch == '\'') {
233 sc.SetState(SCE_LUA_CHARACTER);
234 } else if (sc.ch == '[') {
235 sepCount = LongDelimCheck(sc);
236 if (sepCount == 0) {
237 sc.SetState(SCE_LUA_OPERATOR);
238 } else {
239 nestLevel = 1;
240 sc.SetState(SCE_LUA_LITERALSTRING);
241 sc.Forward(sepCount);
242 }
243 } else if (sc.Match('-', '-')) {
244 sc.SetState(SCE_LUA_COMMENTLINE);
245 if (sc.Match("--[")) {
246 sc.Forward(2);
247 sepCount = LongDelimCheck(sc);
248 if (sepCount > 0) {
249 nestLevel = 1;
250 sc.ChangeState(SCE_LUA_COMMENT);
251 sc.Forward(sepCount);
252 }
253 } else {
254 sc.Forward();
255 }
256 } else if (sc.atLineStart && sc.Match('$')) {
257 sc.SetState(SCE_LUA_PREPROCESSOR); // Obsolete since Lua 4.0, but still in old code
258 } else if (IsLuaOperator(static_cast<char>(sc.ch))) {
259 sc.SetState(SCE_LUA_OPERATOR);
260 }
261 }
262 }
263 sc.Complete();
264 }
265
266 static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
267 Accessor &styler) {
268 unsigned int lengthDoc = startPos + length;
269 int visibleChars = 0;
270 int lineCurrent = styler.GetLine(startPos);
271 int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
272 int levelCurrent = levelPrev;
273 char chNext = styler[startPos];
274 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
275 int styleNext = styler.StyleAt(startPos);
276 char s[10];
277
278 for (unsigned int i = startPos; i < lengthDoc; i++) {
279 char ch = chNext;
280 chNext = styler.SafeGetCharAt(i + 1);
281 int style = styleNext;
282 styleNext = styler.StyleAt(i + 1);
283 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
284 if (style == SCE_LUA_WORD) {
285 if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') {
286 for (unsigned int j = 0; j < 8; j++) {
287 if (!iswordchar(styler[i + j])) {
288 break;
289 }
290 s[j] = styler[i + j];
291 s[j + 1] = '\0';
292 }
293
294 if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
295 levelCurrent++;
296 }
297 if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
298 levelCurrent--;
299 }
300 }
301 } else if (style == SCE_LUA_OPERATOR) {
302 if (ch == '{' || ch == '(') {
303 levelCurrent++;
304 } else if (ch == '}' || ch == ')') {
305 levelCurrent--;
306 }
307 } else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
308 if (ch == '[') {
309 levelCurrent++;
310 } else if (ch == ']') {
311 levelCurrent--;
312 }
313 }
314
315 if (atEOL) {
316 int lev = levelPrev;
317 if (visibleChars == 0 && foldCompact) {
318 lev |= SC_FOLDLEVELWHITEFLAG;
319 }
320 if ((levelCurrent > levelPrev) && (visibleChars > 0)) {
321 lev |= SC_FOLDLEVELHEADERFLAG;
322 }
323 if (lev != styler.LevelAt(lineCurrent)) {
324 styler.SetLevel(lineCurrent, lev);
325 }
326 lineCurrent++;
327 levelPrev = levelCurrent;
328 visibleChars = 0;
329 }
330 if (!isspacechar(ch)) {
331 visibleChars++;
332 }
333 }
334 // Fill in the real level of the next line, keeping the current flags as they will be filled in later
335
336 int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
337 styler.SetLevel(lineCurrent, levelPrev | flagsNext);
338 }
339
340 static const char * const luaWordListDesc[] = {
341 "Keywords",
342 "Basic functions",
343 "String, (table) & math functions",
344 "(coroutines), I/O & system facilities",
345 "user1",
346 "user2",
347 "user3",
348 "user4",
349 0
350 };
351
352 LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);