/Tools/MaterialEditor/wxscintilla_1.69.2/src/scintilla/src/LexLua.cxx

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