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