LexVisualProlog.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. // Scintilla source code edit control
  2. /** @file LexVisualProlog.cxx
  3. ** Lexer for Visual Prolog.
  4. **/
  5. // Author Thomas Linder Puls, Prolog Development Denter A/S, http://www.visual-prolog.com
  6. // Based on Lexer for C++, C, Java, and JavaScript.
  7. // Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
  8. // The License.txt file describes the conditions under which this software may be distributed.
  9. // The line state contains:
  10. // In SCE_VISUALPROLOG_STRING_VERBATIM_EOL (i.e. multiline string literal): The closingQuote.
  11. // else (for SCE_VISUALPROLOG_COMMENT_BLOCK): The comment nesting level
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <stdio.h>
  15. #include <stdarg.h>
  16. #include <assert.h>
  17. #include <ctype.h>
  18. #ifdef _MSC_VER
  19. #pragma warning(disable: 4786)
  20. #endif
  21. #include <string>
  22. #include <vector>
  23. #include <map>
  24. #include <algorithm>
  25. #include "ILexer.h"
  26. #include "Scintilla.h"
  27. #include "SciLexer.h"
  28. #include "WordList.h"
  29. #include "LexAccessor.h"
  30. #include "Accessor.h"
  31. #include "StyleContext.h"
  32. #include "CharacterSet.h"
  33. #include "CharacterCategory.h"
  34. #include "LexerModule.h"
  35. #include "OptionSet.h"
  36. #ifdef SCI_NAMESPACE
  37. using namespace Scintilla;
  38. #endif
  39. // Options used for LexerVisualProlog
  40. struct OptionsVisualProlog {
  41. OptionsVisualProlog() {
  42. }
  43. };
  44. static const char *const visualPrologWordLists[] = {
  45. "Major keywords (class, predicates, ...)",
  46. "Minor keywords (if, then, try, ...)",
  47. "Directive keywords without the '#' (include, requires, ...)",
  48. "Documentation keywords without the '@' (short, detail, ...)",
  49. 0,
  50. };
  51. struct OptionSetVisualProlog : public OptionSet<OptionsVisualProlog> {
  52. OptionSetVisualProlog() {
  53. DefineWordListSets(visualPrologWordLists);
  54. }
  55. };
  56. class LexerVisualProlog : public ILexer {
  57. WordList majorKeywords;
  58. WordList minorKeywords;
  59. WordList directiveKeywords;
  60. WordList docKeywords;
  61. OptionsVisualProlog options;
  62. OptionSetVisualProlog osVisualProlog;
  63. public:
  64. LexerVisualProlog() {
  65. }
  66. virtual ~LexerVisualProlog() {
  67. }
  68. void SCI_METHOD Release() {
  69. delete this;
  70. }
  71. int SCI_METHOD Version() const {
  72. return lvOriginal;
  73. }
  74. const char * SCI_METHOD PropertyNames() {
  75. return osVisualProlog.PropertyNames();
  76. }
  77. int SCI_METHOD PropertyType(const char *name) {
  78. return osVisualProlog.PropertyType(name);
  79. }
  80. const char * SCI_METHOD DescribeProperty(const char *name) {
  81. return osVisualProlog.DescribeProperty(name);
  82. }
  83. Sci_Position SCI_METHOD PropertySet(const char *key, const char *val);
  84. const char * SCI_METHOD DescribeWordListSets() {
  85. return osVisualProlog.DescribeWordListSets();
  86. }
  87. Sci_Position SCI_METHOD WordListSet(int n, const char *wl);
  88. void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess);
  89. void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess);
  90. void * SCI_METHOD PrivateCall(int, void *) {
  91. return 0;
  92. }
  93. static ILexer *LexerFactoryVisualProlog() {
  94. return new LexerVisualProlog();
  95. }
  96. };
  97. Sci_Position SCI_METHOD LexerVisualProlog::PropertySet(const char *key, const char *val) {
  98. if (osVisualProlog.PropertySet(&options, key, val)) {
  99. return 0;
  100. }
  101. return -1;
  102. }
  103. Sci_Position SCI_METHOD LexerVisualProlog::WordListSet(int n, const char *wl) {
  104. WordList *wordListN = 0;
  105. switch (n) {
  106. case 0:
  107. wordListN = &majorKeywords;
  108. break;
  109. case 1:
  110. wordListN = &minorKeywords;
  111. break;
  112. case 2:
  113. wordListN = &directiveKeywords;
  114. break;
  115. case 3:
  116. wordListN = &docKeywords;
  117. break;
  118. }
  119. Sci_Position firstModification = -1;
  120. if (wordListN) {
  121. WordList wlNew;
  122. wlNew.Set(wl);
  123. if (*wordListN != wlNew) {
  124. wordListN->Set(wl);
  125. firstModification = 0;
  126. }
  127. }
  128. return firstModification;
  129. }
  130. // Functor used to truncate history
  131. struct After {
  132. Sci_Position line;
  133. After(Sci_Position line_) : line(line_) {}
  134. };
  135. static bool isLowerLetter(int ch){
  136. return ccLl == CategoriseCharacter(ch);
  137. }
  138. static bool isUpperLetter(int ch){
  139. return ccLu == CategoriseCharacter(ch);
  140. }
  141. static bool isAlphaNum(int ch){
  142. CharacterCategory cc = CategoriseCharacter(ch);
  143. return (ccLu == cc || ccLl == cc || ccLt == cc || ccLm == cc || ccLo == cc || ccNd == cc || ccNl == cc || ccNo == cc);
  144. }
  145. static bool isStringVerbatimOpenClose(int ch){
  146. CharacterCategory cc = CategoriseCharacter(ch);
  147. return (ccPc <= cc && cc <= ccSo);
  148. }
  149. static bool isIdChar(int ch){
  150. return ('_') == ch || isAlphaNum(ch);
  151. }
  152. static bool isOpenStringVerbatim(int next, int &closingQuote){
  153. switch (next) {
  154. case L'<':
  155. closingQuote = L'>';
  156. return true;
  157. case L'>':
  158. closingQuote = L'<';
  159. return true;
  160. case L'(':
  161. closingQuote = L')';
  162. return true;
  163. case L')':
  164. closingQuote = L'(';
  165. return true;
  166. case L'[':
  167. closingQuote = L']';
  168. return true;
  169. case L']':
  170. closingQuote = L'[';
  171. return true;
  172. case L'{':
  173. closingQuote = L'}';
  174. return true;
  175. case L'}':
  176. closingQuote = L'{';
  177. return true;
  178. case L'_':
  179. case L'.':
  180. case L',':
  181. case L';':
  182. return false;
  183. default:
  184. if (isStringVerbatimOpenClose(next)) {
  185. closingQuote = next;
  186. return true;
  187. } else {
  188. return false;
  189. }
  190. }
  191. }
  192. // Look ahead to see which colour "end" should have (takes colour after the following keyword)
  193. static void endLookAhead(char s[], LexAccessor &styler, Sci_Position start) {
  194. char ch = styler.SafeGetCharAt(start, '\n');
  195. while (' ' == ch) {
  196. start++;
  197. ch = styler.SafeGetCharAt(start, '\n');
  198. }
  199. Sci_Position i = 0;
  200. while (i < 100 && isLowerLetter(ch)){
  201. s[i] = ch;
  202. i++;
  203. ch = styler.SafeGetCharAt(start + i, '\n');
  204. }
  205. s[i] = '\0';
  206. }
  207. static void forwardEscapeLiteral(StyleContext &sc, int EscapeState) {
  208. sc.Forward();
  209. if (sc.Match('"') || sc.Match('\'') || sc.Match('\\') || sc.Match('n') || sc.Match('l') || sc.Match('r') || sc.Match('t')) {
  210. sc.ChangeState(EscapeState);
  211. } else if (sc.Match('u')) {
  212. if (IsADigit(sc.chNext, 16)) {
  213. sc.Forward();
  214. if (IsADigit(sc.chNext, 16)) {
  215. sc.Forward();
  216. if (IsADigit(sc.chNext, 16)) {
  217. sc.Forward();
  218. if (IsADigit(sc.chNext, 16)) {
  219. sc.Forward();
  220. sc.ChangeState(EscapeState);
  221. }
  222. }
  223. }
  224. }
  225. }
  226. }
  227. void SCI_METHOD LexerVisualProlog::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
  228. LexAccessor styler(pAccess);
  229. CharacterSet setDoxygen(CharacterSet::setAlpha, "");
  230. CharacterSet setNumber(CharacterSet::setNone, "0123456789abcdefABCDEFxoXO");
  231. StyleContext sc(startPos, length, initStyle, styler, 0x7f);
  232. int styleBeforeDocKeyword = SCE_VISUALPROLOG_DEFAULT;
  233. Sci_Position currentLine = styler.GetLine(startPos);
  234. int closingQuote = '"';
  235. int nestLevel = 0;
  236. if (currentLine >= 1)
  237. {
  238. nestLevel = styler.GetLineState(currentLine - 1);
  239. closingQuote = nestLevel;
  240. }
  241. // Truncate ppDefineHistory before current line
  242. for (; sc.More(); sc.Forward()) {
  243. // Determine if the current state should terminate.
  244. switch (sc.state) {
  245. case SCE_VISUALPROLOG_OPERATOR:
  246. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  247. break;
  248. case SCE_VISUALPROLOG_NUMBER:
  249. // We accept almost anything because of hex. and number suffixes
  250. if (!(setNumber.Contains(sc.ch)) || (sc.Match('.') && IsADigit(sc.chNext))) {
  251. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  252. }
  253. break;
  254. case SCE_VISUALPROLOG_IDENTIFIER:
  255. if (!isIdChar(sc.ch)) {
  256. char s[1000];
  257. sc.GetCurrent(s, sizeof(s));
  258. if (0 == strcmp(s, "end")) {
  259. endLookAhead(s, styler, sc.currentPos);
  260. }
  261. if (majorKeywords.InList(s)) {
  262. sc.ChangeState(SCE_VISUALPROLOG_KEY_MAJOR);
  263. } else if (minorKeywords.InList(s)) {
  264. sc.ChangeState(SCE_VISUALPROLOG_KEY_MINOR);
  265. }
  266. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  267. }
  268. break;
  269. case SCE_VISUALPROLOG_VARIABLE:
  270. case SCE_VISUALPROLOG_ANONYMOUS:
  271. if (!isIdChar(sc.ch)) {
  272. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  273. }
  274. break;
  275. case SCE_VISUALPROLOG_KEY_DIRECTIVE:
  276. if (!isLowerLetter(sc.ch)) {
  277. char s[1000];
  278. sc.GetCurrent(s, sizeof(s));
  279. if (!directiveKeywords.InList(s+1)) {
  280. sc.ChangeState(SCE_VISUALPROLOG_IDENTIFIER);
  281. }
  282. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  283. }
  284. break;
  285. case SCE_VISUALPROLOG_COMMENT_BLOCK:
  286. if (sc.Match('*', '/')) {
  287. sc.Forward();
  288. nestLevel--;
  289. int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
  290. sc.ForwardSetState(nextState);
  291. } else if (sc.Match('/', '*')) {
  292. sc.Forward();
  293. nestLevel++;
  294. } else if (sc.Match('@')) {
  295. styleBeforeDocKeyword = sc.state;
  296. sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
  297. }
  298. break;
  299. case SCE_VISUALPROLOG_COMMENT_LINE:
  300. if (sc.atLineEnd) {
  301. int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
  302. sc.SetState(nextState);
  303. } else if (sc.Match('@')) {
  304. styleBeforeDocKeyword = sc.state;
  305. sc.SetState(SCE_VISUALPROLOG_COMMENT_KEY_ERROR);
  306. }
  307. break;
  308. case SCE_VISUALPROLOG_COMMENT_KEY_ERROR:
  309. if (!setDoxygen.Contains(sc.ch) || sc.atLineEnd) {
  310. char s[1000];
  311. sc.GetCurrent(s, sizeof(s));
  312. if (docKeywords.InList(s+1)) {
  313. sc.ChangeState(SCE_VISUALPROLOG_COMMENT_KEY);
  314. }
  315. if (SCE_VISUALPROLOG_COMMENT_LINE == styleBeforeDocKeyword && sc.atLineEnd) {
  316. // end line comment
  317. int nextState = (nestLevel == 0) ? SCE_VISUALPROLOG_DEFAULT : SCE_VISUALPROLOG_COMMENT_BLOCK;
  318. sc.SetState(nextState);
  319. } else {
  320. sc.SetState(styleBeforeDocKeyword);
  321. if (SCE_VISUALPROLOG_COMMENT_BLOCK == styleBeforeDocKeyword && sc.Match('*', '/')) {
  322. // we have consumed the '*' if it comes immediately after the docKeyword
  323. sc.Forward();
  324. sc.Forward();
  325. nestLevel--;
  326. if (0 == nestLevel) {
  327. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  328. }
  329. }
  330. }
  331. }
  332. break;
  333. case SCE_VISUALPROLOG_STRING_ESCAPE:
  334. case SCE_VISUALPROLOG_STRING_ESCAPE_ERROR:
  335. // return to SCE_VISUALPROLOG_STRING and treat as such (fall-through)
  336. sc.SetState(SCE_VISUALPROLOG_STRING);
  337. case SCE_VISUALPROLOG_STRING:
  338. if (sc.atLineEnd) {
  339. sc.SetState(SCE_VISUALPROLOG_STRING_EOL_OPEN);
  340. } else if (sc.Match(closingQuote)) {
  341. sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
  342. } else if (sc.Match('\\')) {
  343. sc.SetState(SCE_VISUALPROLOG_STRING_ESCAPE_ERROR);
  344. forwardEscapeLiteral(sc, SCE_VISUALPROLOG_STRING_ESCAPE);
  345. }
  346. break;
  347. case SCE_VISUALPROLOG_STRING_EOL_OPEN:
  348. if (sc.atLineStart) {
  349. sc.SetState(SCE_VISUALPROLOG_DEFAULT);
  350. }
  351. break;
  352. case SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL:
  353. case SCE_VISUALPROLOG_STRING_VERBATIM_EOL:
  354. // return to SCE_VISUALPROLOG_STRING_VERBATIM and treat as such (fall-through)
  355. sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
  356. case SCE_VISUALPROLOG_STRING_VERBATIM:
  357. if (sc.atLineEnd) {
  358. sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_EOL);
  359. } else if (sc.Match(closingQuote)) {
  360. if (closingQuote == sc.chNext) {
  361. sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM_SPECIAL);
  362. sc.Forward();
  363. } else {
  364. sc.ForwardSetState(SCE_VISUALPROLOG_DEFAULT);
  365. }
  366. }
  367. break;
  368. }
  369. if (sc.atLineEnd) {
  370. // Update the line state, so it can be seen by next line
  371. int lineState = 0;
  372. if (SCE_VISUALPROLOG_STRING_VERBATIM_EOL == sc.state) {
  373. lineState = closingQuote;
  374. } else if (SCE_VISUALPROLOG_COMMENT_BLOCK == sc.state) {
  375. lineState = nestLevel;
  376. }
  377. styler.SetLineState(currentLine, lineState);
  378. currentLine++;
  379. }
  380. // Determine if a new state should be entered.
  381. if (sc.state == SCE_VISUALPROLOG_DEFAULT) {
  382. if (sc.Match('@') && isOpenStringVerbatim(sc.chNext, closingQuote)) {
  383. sc.SetState(SCE_VISUALPROLOG_STRING_VERBATIM);
  384. sc.Forward();
  385. } else if (IsADigit(sc.ch) || (sc.Match('.') && IsADigit(sc.chNext))) {
  386. sc.SetState(SCE_VISUALPROLOG_NUMBER);
  387. } else if (isLowerLetter(sc.ch)) {
  388. sc.SetState(SCE_VISUALPROLOG_IDENTIFIER);
  389. } else if (isUpperLetter(sc.ch)) {
  390. sc.SetState(SCE_VISUALPROLOG_VARIABLE);
  391. } else if (sc.Match('_')) {
  392. sc.SetState(SCE_VISUALPROLOG_ANONYMOUS);
  393. } else if (sc.Match('/', '*')) {
  394. sc.SetState(SCE_VISUALPROLOG_COMMENT_BLOCK);
  395. nestLevel = 1;
  396. sc.Forward(); // Eat the * so it isn't used for the end of the comment
  397. } else if (sc.Match('%')) {
  398. sc.SetState(SCE_VISUALPROLOG_COMMENT_LINE);
  399. } else if (sc.Match('\'')) {
  400. closingQuote = '\'';
  401. sc.SetState(SCE_VISUALPROLOG_STRING);
  402. } else if (sc.Match('"')) {
  403. closingQuote = '"';
  404. sc.SetState(SCE_VISUALPROLOG_STRING);
  405. } else if (sc.Match('#')) {
  406. sc.SetState(SCE_VISUALPROLOG_KEY_DIRECTIVE);
  407. } else if (isoperator(static_cast<char>(sc.ch)) || sc.Match('\\')) {
  408. sc.SetState(SCE_VISUALPROLOG_OPERATOR);
  409. }
  410. }
  411. }
  412. sc.Complete();
  413. styler.Flush();
  414. }
  415. // Store both the current line's fold level and the next lines in the
  416. // level store to make it easy to pick up with each increment
  417. // and to make it possible to fiddle the current level for "} else {".
  418. void SCI_METHOD LexerVisualProlog::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) {
  419. LexAccessor styler(pAccess);
  420. Sci_PositionU endPos = startPos + length;
  421. int visibleChars = 0;
  422. Sci_Position currentLine = styler.GetLine(startPos);
  423. int levelCurrent = SC_FOLDLEVELBASE;
  424. if (currentLine > 0)
  425. levelCurrent = styler.LevelAt(currentLine-1) >> 16;
  426. int levelMinCurrent = levelCurrent;
  427. int levelNext = levelCurrent;
  428. char chNext = styler[startPos];
  429. int styleNext = styler.StyleAt(startPos);
  430. int style = initStyle;
  431. for (Sci_PositionU i = startPos; i < endPos; i++) {
  432. char ch = chNext;
  433. chNext = styler.SafeGetCharAt(i + 1);
  434. style = styleNext;
  435. styleNext = styler.StyleAt(i + 1);
  436. bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
  437. if (style == SCE_VISUALPROLOG_OPERATOR) {
  438. if (ch == '{') {
  439. // Measure the minimum before a '{' to allow
  440. // folding on "} else {"
  441. if (levelMinCurrent > levelNext) {
  442. levelMinCurrent = levelNext;
  443. }
  444. levelNext++;
  445. } else if (ch == '}') {
  446. levelNext--;
  447. }
  448. }
  449. if (!IsASpace(ch))
  450. visibleChars++;
  451. if (atEOL || (i == endPos-1)) {
  452. int levelUse = levelCurrent;
  453. int lev = levelUse | levelNext << 16;
  454. if (levelUse < levelNext)
  455. lev |= SC_FOLDLEVELHEADERFLAG;
  456. if (lev != styler.LevelAt(currentLine)) {
  457. styler.SetLevel(currentLine, lev);
  458. }
  459. currentLine++;
  460. levelCurrent = levelNext;
  461. levelMinCurrent = levelCurrent;
  462. if (atEOL && (i == static_cast<Sci_PositionU>(styler.Length()-1))) {
  463. // There is an empty line at end of file so give it same level and empty
  464. styler.SetLevel(currentLine, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG);
  465. }
  466. visibleChars = 0;
  467. }
  468. }
  469. }
  470. LexerModule lmVisualProlog(SCLEX_VISUALPROLOG, LexerVisualProlog::LexerFactoryVisualProlog, "visualprolog", visualPrologWordLists);