LexPOV.cpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. // Scintilla source code edit control
  2. /** @file LexPOV.cxx
  3. ** Lexer for POV-Ray SDL (Persistance of Vision Raytracer, Scene Description Language).
  4. ** Written by Philippe Lhoste but this is mostly a derivative of LexCPP...
  5. **/
  6. // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
  7. // The License.txt file describes the conditions under which this software may be distributed.
  8. // Some points that distinguish from a simple C lexer:
  9. // Identifiers start only by a character.
  10. // No line continuation character.
  11. // Strings are limited to 256 characters.
  12. // Directives are similar to preprocessor commands,
  13. // but we match directive keywords and colorize incorrect ones.
  14. // Block comments can be nested (code stolen from my code in LexLua).
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include <stdarg.h>
  19. #include <assert.h>
  20. #include <ctype.h>
  21. #include "ILexer.h"
  22. #include "Scintilla.h"
  23. #include "SciLexer.h"
  24. #include "WordList.h"
  25. #include "LexAccessor.h"
  26. #include "Accessor.h"
  27. #include "StyleContext.h"
  28. #include "CharacterSet.h"
  29. #include "LexerModule.h"
  30. #ifdef SCI_NAMESPACE
  31. using namespace Scintilla;
  32. #endif
  33. static inline bool IsAWordChar(int ch) {
  34. return ch < 0x80 && (isalnum(ch) || ch == '_');
  35. }
  36. static inline bool IsAWordStart(int ch) {
  37. return ch < 0x80 && isalpha(ch);
  38. }
  39. static inline bool IsANumberChar(int ch) {
  40. // Not exactly following number definition (several dots are seen as OK, etc.)
  41. // but probably enough in most cases.
  42. return (ch < 0x80) &&
  43. (isdigit(ch) || toupper(ch) == 'E' ||
  44. ch == '.' || ch == '-' || ch == '+');
  45. }
  46. static void ColourisePovDoc(
  47. Sci_PositionU startPos,
  48. Sci_Position length,
  49. int initStyle,
  50. WordList *keywordlists[],
  51. Accessor &styler) {
  52. WordList &keywords1 = *keywordlists[0];
  53. WordList &keywords2 = *keywordlists[1];
  54. WordList &keywords3 = *keywordlists[2];
  55. WordList &keywords4 = *keywordlists[3];
  56. WordList &keywords5 = *keywordlists[4];
  57. WordList &keywords6 = *keywordlists[5];
  58. WordList &keywords7 = *keywordlists[6];
  59. WordList &keywords8 = *keywordlists[7];
  60. Sci_Position currentLine = styler.GetLine(startPos);
  61. // Initialize the block comment /* */ nesting level, if we are inside such a comment.
  62. int blockCommentLevel = 0;
  63. if (initStyle == SCE_POV_COMMENT) {
  64. blockCommentLevel = styler.GetLineState(currentLine - 1);
  65. }
  66. // Do not leak onto next line
  67. if (initStyle == SCE_POV_STRINGEOL || initStyle == SCE_POV_COMMENTLINE) {
  68. initStyle = SCE_POV_DEFAULT;
  69. }
  70. short stringLen = 0;
  71. StyleContext sc(startPos, length, initStyle, styler);
  72. for (; sc.More(); sc.Forward()) {
  73. if (sc.atLineEnd) {
  74. // Update the line state, so it can be seen by next line
  75. currentLine = styler.GetLine(sc.currentPos);
  76. if (sc.state == SCE_POV_COMMENT) {
  77. // Inside a block comment, we set the line state
  78. styler.SetLineState(currentLine, blockCommentLevel);
  79. } else {
  80. // Reset the line state
  81. styler.SetLineState(currentLine, 0);
  82. }
  83. }
  84. if (sc.atLineStart && (sc.state == SCE_POV_STRING)) {
  85. // Prevent SCE_POV_STRINGEOL from leaking back to previous line
  86. sc.SetState(SCE_POV_STRING);
  87. }
  88. // Determine if the current state should terminate.
  89. if (sc.state == SCE_POV_OPERATOR) {
  90. sc.SetState(SCE_POV_DEFAULT);
  91. } else if (sc.state == SCE_POV_NUMBER) {
  92. // We stop the number definition on non-numerical non-dot non-eE non-sign char
  93. if (!IsANumberChar(sc.ch)) {
  94. sc.SetState(SCE_POV_DEFAULT);
  95. }
  96. } else if (sc.state == SCE_POV_IDENTIFIER) {
  97. if (!IsAWordChar(sc.ch)) {
  98. char s[100];
  99. sc.GetCurrent(s, sizeof(s));
  100. if (keywords2.InList(s)) {
  101. sc.ChangeState(SCE_POV_WORD2);
  102. } else if (keywords3.InList(s)) {
  103. sc.ChangeState(SCE_POV_WORD3);
  104. } else if (keywords4.InList(s)) {
  105. sc.ChangeState(SCE_POV_WORD4);
  106. } else if (keywords5.InList(s)) {
  107. sc.ChangeState(SCE_POV_WORD5);
  108. } else if (keywords6.InList(s)) {
  109. sc.ChangeState(SCE_POV_WORD6);
  110. } else if (keywords7.InList(s)) {
  111. sc.ChangeState(SCE_POV_WORD7);
  112. } else if (keywords8.InList(s)) {
  113. sc.ChangeState(SCE_POV_WORD8);
  114. }
  115. sc.SetState(SCE_POV_DEFAULT);
  116. }
  117. } else if (sc.state == SCE_POV_DIRECTIVE) {
  118. if (!IsAWordChar(sc.ch)) {
  119. char s[100];
  120. char *p;
  121. sc.GetCurrent(s, sizeof(s));
  122. p = s;
  123. // Skip # and whitespace between # and directive word
  124. do {
  125. p++;
  126. } while ((*p == ' ' || *p == '\t') && *p != '\0');
  127. if (!keywords1.InList(p)) {
  128. sc.ChangeState(SCE_POV_BADDIRECTIVE);
  129. }
  130. sc.SetState(SCE_POV_DEFAULT);
  131. }
  132. } else if (sc.state == SCE_POV_COMMENT) {
  133. if (sc.Match('/', '*')) {
  134. blockCommentLevel++;
  135. sc.Forward();
  136. } else if (sc.Match('*', '/') && blockCommentLevel > 0) {
  137. blockCommentLevel--;
  138. sc.Forward();
  139. if (blockCommentLevel == 0) {
  140. sc.ForwardSetState(SCE_POV_DEFAULT);
  141. }
  142. }
  143. } else if (sc.state == SCE_POV_COMMENTLINE) {
  144. if (sc.atLineEnd) {
  145. sc.ForwardSetState(SCE_POV_DEFAULT);
  146. }
  147. } else if (sc.state == SCE_POV_STRING) {
  148. if (sc.ch == '\\') {
  149. stringLen++;
  150. if (strchr("abfnrtuv0'\"", sc.chNext)) {
  151. // Compound characters are counted as one.
  152. // Note: for Unicode chars \u, we shouldn't count the next 4 digits...
  153. sc.Forward();
  154. }
  155. } else if (sc.ch == '\"') {
  156. sc.ForwardSetState(SCE_POV_DEFAULT);
  157. } else if (sc.atLineEnd) {
  158. sc.ChangeState(SCE_POV_STRINGEOL);
  159. sc.ForwardSetState(SCE_POV_DEFAULT);
  160. } else {
  161. stringLen++;
  162. }
  163. if (stringLen > 256) {
  164. // Strings are limited to 256 chars
  165. sc.SetState(SCE_POV_STRINGEOL);
  166. }
  167. } else if (sc.state == SCE_POV_STRINGEOL) {
  168. if (sc.ch == '\\') {
  169. if (sc.chNext == '\"' || sc.chNext == '\\') {
  170. sc.Forward();
  171. }
  172. } else if (sc.ch == '\"') {
  173. sc.ForwardSetState(SCE_C_DEFAULT);
  174. } else if (sc.atLineEnd) {
  175. sc.ForwardSetState(SCE_POV_DEFAULT);
  176. }
  177. }
  178. // Determine if a new state should be entered.
  179. if (sc.state == SCE_POV_DEFAULT) {
  180. if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
  181. sc.SetState(SCE_POV_NUMBER);
  182. } else if (IsAWordStart(sc.ch)) {
  183. sc.SetState(SCE_POV_IDENTIFIER);
  184. } else if (sc.Match('/', '*')) {
  185. blockCommentLevel = 1;
  186. sc.SetState(SCE_POV_COMMENT);
  187. sc.Forward(); // Eat the * so it isn't used for the end of the comment
  188. } else if (sc.Match('/', '/')) {
  189. sc.SetState(SCE_POV_COMMENTLINE);
  190. } else if (sc.ch == '\"') {
  191. sc.SetState(SCE_POV_STRING);
  192. stringLen = 0;
  193. } else if (sc.ch == '#') {
  194. sc.SetState(SCE_POV_DIRECTIVE);
  195. // Skip whitespace between # and directive word
  196. do {
  197. sc.Forward();
  198. } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
  199. if (sc.atLineEnd) {
  200. sc.SetState(SCE_POV_DEFAULT);
  201. }
  202. } else if (isoperator(static_cast<char>(sc.ch))) {
  203. sc.SetState(SCE_POV_OPERATOR);
  204. }
  205. }
  206. }
  207. sc.Complete();
  208. }
  209. static void FoldPovDoc(
  210. Sci_PositionU startPos,
  211. Sci_Position length,
  212. int initStyle,
  213. WordList *[],
  214. Accessor &styler) {
  215. bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
  216. bool foldDirective = styler.GetPropertyInt("fold.directive") != 0;
  217. bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
  218. Sci_PositionU endPos = startPos + length;
  219. int visibleChars = 0;
  220. Sci_Position lineCurrent = styler.GetLine(startPos);
  221. int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
  222. int levelCurrent = levelPrev;
  223. char chNext = styler[startPos];
  224. int styleNext = styler.StyleAt(startPos);
  225. int style = initStyle;
  226. for (Sci_PositionU i = startPos; i < endPos; i++) {
  227. char ch = chNext;
  228. chNext = styler.SafeGetCharAt(i + 1);
  229. int stylePrev = style;
  230. style = styleNext;
  231. styleNext = styler.StyleAt(i + 1);
  232. bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
  233. if (foldComment && (style == SCE_POV_COMMENT)) {
  234. if (stylePrev != SCE_POV_COMMENT) {
  235. levelCurrent++;
  236. } else if ((styleNext != SCE_POV_COMMENT) && !atEOL) {
  237. // Comments don't end at end of line and the next character may be unstyled.
  238. levelCurrent--;
  239. }
  240. }
  241. if (foldComment && (style == SCE_POV_COMMENTLINE)) {
  242. if ((ch == '/') && (chNext == '/')) {
  243. char chNext2 = styler.SafeGetCharAt(i + 2);
  244. if (chNext2 == '{') {
  245. levelCurrent++;
  246. } else if (chNext2 == '}') {
  247. levelCurrent--;
  248. }
  249. }
  250. }
  251. if (foldDirective && (style == SCE_POV_DIRECTIVE)) {
  252. if (ch == '#') {
  253. Sci_PositionU j=i+1;
  254. while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
  255. j++;
  256. }
  257. }
  258. }
  259. if (style == SCE_POV_OPERATOR) {
  260. if (ch == '{') {
  261. levelCurrent++;
  262. } else if (ch == '}') {
  263. levelCurrent--;
  264. }
  265. }
  266. if (atEOL) {
  267. int lev = levelPrev;
  268. if (visibleChars == 0 && foldCompact)
  269. lev |= SC_FOLDLEVELWHITEFLAG;
  270. if ((levelCurrent > levelPrev) && (visibleChars > 0))
  271. lev |= SC_FOLDLEVELHEADERFLAG;
  272. if (lev != styler.LevelAt(lineCurrent)) {
  273. styler.SetLevel(lineCurrent, lev);
  274. }
  275. lineCurrent++;
  276. levelPrev = levelCurrent;
  277. visibleChars = 0;
  278. }
  279. if (!isspacechar(ch))
  280. visibleChars++;
  281. }
  282. // Fill in the real level of the next line, keeping the current flags as they will be filled in later
  283. int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
  284. styler.SetLevel(lineCurrent, levelPrev | flagsNext);
  285. }
  286. static const char * const povWordLists[] = {
  287. "Language directives",
  288. "Objects & CSG & Appearance",
  289. "Types & Modifiers & Items",
  290. "Predefined Identifiers",
  291. "Predefined Functions",
  292. "User defined 1",
  293. "User defined 2",
  294. "User defined 3",
  295. 0,
  296. };
  297. LexerModule lmPOV(SCLEX_POV, ColourisePovDoc, "pov", FoldPovDoc, povWordLists);