LexLua.cpp 13 KB

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