PageRenderTime 62ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/lexers/LexLua.cxx

https://github.com/LuaDist/scintilla
C++ | 453 lines | 431 code | 8 blank | 14 comment | 5 complexity | 533e2fecf7712c41f1e0e641de461d70 MD5 | raw file
  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. unsigned int startPos,
  40. int 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. int 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. int ln = 0, maxln = startPos + length - sc.currentPos;
  123. int c;
  124. while (ln < maxln) { // determine line extent
  125. c = sc.GetRelative(ln);
  126. if (c == '\r' || c == '\n')
  127. break;
  128. ln++;
  129. }
  130. maxln = ln; ln = 0;
  131. while (ln < maxln) { // skip over spaces/tabs
  132. if (!IsASpaceOrTab(sc.GetRelative(ln)))
  133. break;
  134. ln++;
  135. }
  136. int ws1 = ln;
  137. if (setWordStart.Contains(sc.GetRelative(ln))) {
  138. int i = 0;
  139. char s[100];
  140. while (ln < maxln) { // get potential label
  141. c = sc.GetRelative(ln);
  142. if (!setWord.Contains(c))
  143. break;
  144. if (i < 90)
  145. s[i++] = c;
  146. ln++;
  147. }
  148. s[i] = '\0'; int lbl = ln;
  149. if (!keywords.InList(s)) {
  150. while (ln < maxln) { // skip over spaces/tabs
  151. if (!IsASpaceOrTab(sc.GetRelative(ln)))
  152. break;
  153. ln++;
  154. }
  155. int ws2 = ln - lbl;
  156. if (sc.GetRelative(ln) == ':' && sc.GetRelative(ln + 1) == ':') {
  157. // final :: found, complete valid label construct
  158. sc.ChangeState(SCE_LUA_LABEL);
  159. if (ws1) {
  160. sc.SetState(SCE_LUA_DEFAULT);
  161. sc.Forward(ws1);
  162. }
  163. sc.SetState(SCE_LUA_LABEL);
  164. sc.Forward(lbl - ws1);
  165. if (ws2) {
  166. sc.SetState(SCE_LUA_DEFAULT);
  167. sc.Forward(ws2);
  168. }
  169. sc.SetState(SCE_LUA_LABEL);
  170. sc.Forward(2);
  171. }
  172. }
  173. }
  174. }
  175. sc.SetState(SCE_LUA_DEFAULT);
  176. } else if (sc.state == SCE_LUA_NUMBER) {
  177. // We stop the number definition on non-numerical non-dot non-eEpP non-sign non-hexdigit char
  178. if (!setNumber.Contains(sc.ch)) {
  179. sc.SetState(SCE_LUA_DEFAULT);
  180. } else if (sc.ch == '-' || sc.ch == '+') {
  181. if (!setExponent.Contains(sc.chPrev))
  182. sc.SetState(SCE_LUA_DEFAULT);
  183. }
  184. } else if (sc.state == SCE_LUA_IDENTIFIER) {
  185. if (!(setWord.Contains(sc.ch) || sc.ch == '.') || sc.Match('.', '.')) {
  186. char s[100];
  187. sc.GetCurrent(s, sizeof(s));
  188. if (keywords.InList(s)) {
  189. sc.ChangeState(SCE_LUA_WORD);
  190. if (strcmp(s, "goto") == 0) { // goto <label> forward scan
  191. sc.SetState(SCE_LUA_DEFAULT);
  192. while (IsASpaceOrTab(sc.ch) && !sc.atLineEnd)
  193. sc.Forward();
  194. if (setWordStart.Contains(sc.ch)) {
  195. sc.SetState(SCE_LUA_LABEL);
  196. sc.Forward();
  197. while (setWord.Contains(sc.ch))
  198. sc.Forward();
  199. sc.GetCurrent(s, sizeof(s));
  200. if (keywords.InList(s))
  201. sc.ChangeState(SCE_LUA_WORD);
  202. }
  203. sc.SetState(SCE_LUA_DEFAULT);
  204. }
  205. } else if (keywords2.InList(s)) {
  206. sc.ChangeState(SCE_LUA_WORD2);
  207. } else if (keywords3.InList(s)) {
  208. sc.ChangeState(SCE_LUA_WORD3);
  209. } else if (keywords4.InList(s)) {
  210. sc.ChangeState(SCE_LUA_WORD4);
  211. } else if (keywords5.InList(s)) {
  212. sc.ChangeState(SCE_LUA_WORD5);
  213. } else if (keywords6.InList(s)) {
  214. sc.ChangeState(SCE_LUA_WORD6);
  215. } else if (keywords7.InList(s)) {
  216. sc.ChangeState(SCE_LUA_WORD7);
  217. } else if (keywords8.InList(s)) {
  218. sc.ChangeState(SCE_LUA_WORD8);
  219. }
  220. sc.SetState(SCE_LUA_DEFAULT);
  221. }
  222. } else if (sc.state == SCE_LUA_COMMENTLINE || sc.state == SCE_LUA_PREPROCESSOR) {
  223. if (sc.atLineEnd) {
  224. sc.ForwardSetState(SCE_LUA_DEFAULT);
  225. }
  226. } else if (sc.state == SCE_LUA_STRING) {
  227. if (stringWs) {
  228. if (!IsASpace(sc.ch))
  229. stringWs = 0;
  230. }
  231. if (sc.ch == '\\') {
  232. if (setEscapeSkip.Contains(sc.chNext)) {
  233. sc.Forward();
  234. } else if (sc.chNext == 'z') {
  235. sc.Forward();
  236. stringWs = 0x100;
  237. }
  238. } else if (sc.ch == '\"') {
  239. sc.ForwardSetState(SCE_LUA_DEFAULT);
  240. } else if (stringWs == 0 && sc.atLineEnd) {
  241. sc.ChangeState(SCE_LUA_STRINGEOL);
  242. sc.ForwardSetState(SCE_LUA_DEFAULT);
  243. }
  244. } else if (sc.state == SCE_LUA_CHARACTER) {
  245. if (stringWs) {
  246. if (!IsASpace(sc.ch))
  247. stringWs = 0;
  248. }
  249. if (sc.ch == '\\') {
  250. if (setEscapeSkip.Contains(sc.chNext)) {
  251. sc.Forward();
  252. } else if (sc.chNext == 'z') {
  253. sc.Forward();
  254. stringWs = 0x100;
  255. }
  256. } else if (sc.ch == '\'') {
  257. sc.ForwardSetState(SCE_LUA_DEFAULT);
  258. } else if (stringWs == 0 && sc.atLineEnd) {
  259. sc.ChangeState(SCE_LUA_STRINGEOL);
  260. sc.ForwardSetState(SCE_LUA_DEFAULT);
  261. }
  262. } else if (sc.state == SCE_LUA_LITERALSTRING || sc.state == SCE_LUA_COMMENT) {
  263. if (sc.ch == '[') {
  264. int sep = LongDelimCheck(sc);
  265. if (sep == 1 && sepCount == 1) { // [[-only allowed to nest
  266. nestLevel++;
  267. sc.Forward();
  268. }
  269. } else if (sc.ch == ']') {
  270. int sep = LongDelimCheck(sc);
  271. if (sep == 1 && sepCount == 1) { // un-nest with ]]-only
  272. nestLevel--;
  273. sc.Forward();
  274. if (nestLevel == 0) {
  275. sc.ForwardSetState(SCE_LUA_DEFAULT);
  276. }
  277. } else if (sep > 1 && sep == sepCount) { // ]=]-style delim
  278. sc.Forward(sep);
  279. sc.ForwardSetState(SCE_LUA_DEFAULT);
  280. }
  281. }
  282. }
  283. // Determine if a new state should be entered.
  284. if (sc.state == SCE_LUA_DEFAULT) {
  285. if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
  286. sc.SetState(SCE_LUA_NUMBER);
  287. if (sc.ch == '0' && toupper(sc.chNext) == 'X') {
  288. sc.Forward();
  289. }
  290. } else if (setWordStart.Contains(sc.ch)) {
  291. sc.SetState(SCE_LUA_IDENTIFIER);
  292. } else if (sc.ch == '\"') {
  293. sc.SetState(SCE_LUA_STRING);
  294. stringWs = 0;
  295. } else if (sc.ch == '\'') {
  296. sc.SetState(SCE_LUA_CHARACTER);
  297. stringWs = 0;
  298. } else if (sc.ch == '[') {
  299. sepCount = LongDelimCheck(sc);
  300. if (sepCount == 0) {
  301. sc.SetState(SCE_LUA_OPERATOR);
  302. } else {
  303. nestLevel = 1;
  304. sc.SetState(SCE_LUA_LITERALSTRING);
  305. sc.Forward(sepCount);
  306. }
  307. } else if (sc.Match('-', '-')) {
  308. sc.SetState(SCE_LUA_COMMENTLINE);
  309. if (sc.Match("--[")) {
  310. sc.Forward(2);
  311. sepCount = LongDelimCheck(sc);
  312. if (sepCount > 0) {
  313. nestLevel = 1;
  314. sc.ChangeState(SCE_LUA_COMMENT);
  315. sc.Forward(sepCount);
  316. }
  317. } else {
  318. sc.Forward();
  319. }
  320. } else if (sc.atLineStart && sc.Match('$')) {
  321. sc.SetState(SCE_LUA_PREPROCESSOR); // Obsolete since Lua 4.0, but still in old code
  322. } else if (setLuaOperator.Contains(sc.ch)) {
  323. sc.SetState(SCE_LUA_OPERATOR);
  324. }
  325. }
  326. }
  327. if (setWord.Contains(sc.chPrev) || sc.chPrev == '.') {
  328. char s[100];
  329. sc.GetCurrent(s, sizeof(s));
  330. if (keywords.InList(s)) {
  331. sc.ChangeState(SCE_LUA_WORD);
  332. } else if (keywords2.InList(s)) {
  333. sc.ChangeState(SCE_LUA_WORD2);
  334. } else if (keywords3.InList(s)) {
  335. sc.ChangeState(SCE_LUA_WORD3);
  336. } else if (keywords4.InList(s)) {
  337. sc.ChangeState(SCE_LUA_WORD4);
  338. } else if (keywords5.InList(s)) {
  339. sc.ChangeState(SCE_LUA_WORD5);
  340. } else if (keywords6.InList(s)) {
  341. sc.ChangeState(SCE_LUA_WORD6);
  342. } else if (keywords7.InList(s)) {
  343. sc.ChangeState(SCE_LUA_WORD7);
  344. } else if (keywords8.InList(s)) {
  345. sc.ChangeState(SCE_LUA_WORD8);
  346. }
  347. }
  348. sc.Complete();
  349. }
  350. static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
  351. Accessor &styler) {
  352. unsigned int lengthDoc = startPos + length;
  353. int visibleChars = 0;
  354. int lineCurrent = styler.GetLine(startPos);
  355. int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
  356. int levelCurrent = levelPrev;
  357. char chNext = styler[startPos];
  358. bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
  359. int styleNext = styler.StyleAt(startPos);
  360. char s[10];
  361. for (unsigned int i = startPos; i < lengthDoc; i++) {
  362. char ch = chNext;
  363. chNext = styler.SafeGetCharAt(i + 1);
  364. int style = styleNext;
  365. styleNext = styler.StyleAt(i + 1);
  366. bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
  367. if (style == SCE_LUA_WORD) {
  368. if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') {
  369. for (unsigned int j = 0; j < 8; j++) {
  370. if (!iswordchar(styler[i + j])) {
  371. break;
  372. }
  373. s[j] = styler[i + j];
  374. s[j + 1] = '\0';
  375. }
  376. if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
  377. levelCurrent++;
  378. }
  379. if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
  380. levelCurrent--;
  381. }
  382. }
  383. } else if (style == SCE_LUA_OPERATOR) {
  384. if (ch == '{' || ch == '(') {
  385. levelCurrent++;
  386. } else if (ch == '}' || ch == ')') {
  387. levelCurrent--;
  388. }
  389. } else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
  390. if (ch == '[') {
  391. levelCurrent++;
  392. } else if (ch == ']') {
  393. levelCurrent--;
  394. }
  395. }
  396. if (atEOL) {
  397. int lev = levelPrev;
  398. if (visibleChars == 0 && foldCompact) {
  399. lev |= SC_FOLDLEVELWHITEFLAG;
  400. }
  401. if ((levelCurrent > levelPrev) && (visibleChars > 0)) {
  402. lev |= SC_FOLDLEVELHEADERFLAG;
  403. }
  404. if (lev != styler.LevelAt(lineCurrent)) {
  405. styler.SetLevel(lineCurrent, lev);
  406. }
  407. lineCurrent++;
  408. levelPrev = levelCurrent;
  409. visibleChars = 0;
  410. }
  411. if (!isspacechar(ch)) {
  412. visibleChars++;
  413. }
  414. }
  415. // Fill in the real level of the next line, keeping the current flags as they will be filled in later
  416. int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
  417. styler.SetLevel(lineCurrent, levelPrev | flagsNext);
  418. }
  419. static const char * const luaWordListDesc[] = {
  420. "Keywords",
  421. "Basic functions",
  422. "String, (table) & math functions",
  423. "(coroutines), I/O & system facilities",
  424. "user1",
  425. "user2",
  426. "user3",
  427. "user4",
  428. 0
  429. };
  430. LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);