LexTADS3.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. // Scintilla source code edit control
  2. /** @file LexTADS3.cxx
  3. ** Lexer for TADS3.
  4. **/
  5. // Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org>
  6. // The License.txt file describes the conditions under which this software may be distributed.
  7. /*
  8. * TADS3 is a language designed by Michael J. Roberts for the writing of text
  9. * based games. TADS comes from Text Adventure Development System. It has good
  10. * support for the processing and outputting of formatted text and much of a
  11. * TADS program listing consists of strings.
  12. *
  13. * TADS has two types of strings, those enclosed in single quotes (') and those
  14. * enclosed in double quotes ("). These strings have different symantics and
  15. * can be given different highlighting if desired.
  16. *
  17. * There can be embedded within both types of strings html tags
  18. * ( <tag key=value> ), library directives ( <.directive> ), and message
  19. * parameters ( {The doctor's/his} ).
  20. *
  21. * Double quoted strings can also contain interpolated expressions
  22. * ( << rug.moved ? ' and a hole in the floor. ' : nil >> ). These expressions
  23. * may themselves contain single or double quoted strings, although the double
  24. * quoted strings may not contain interpolated expressions.
  25. *
  26. * These embedded constructs influence the output and formatting and are an
  27. * important part of a program and require highlighting.
  28. *
  29. * LINKS
  30. * http://www.tads.org/
  31. */
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <stdio.h>
  35. #include <stdarg.h>
  36. #include <assert.h>
  37. #include <ctype.h>
  38. #include "ILexer.h"
  39. #include "Scintilla.h"
  40. #include "SciLexer.h"
  41. #include "WordList.h"
  42. #include "LexAccessor.h"
  43. #include "Accessor.h"
  44. #include "StyleContext.h"
  45. #include "CharacterSet.h"
  46. #include "LexerModule.h"
  47. #ifdef SCI_NAMESPACE
  48. using namespace Scintilla;
  49. #endif
  50. static const int T3_SINGLE_QUOTE = 1;
  51. static const int T3_INT_EXPRESSION = 2;
  52. static const int T3_INT_EXPRESSION_IN_TAG = 4;
  53. static const int T3_HTML_SQUOTE = 8;
  54. static inline bool IsEOL(const int ch, const int chNext) {
  55. return (ch == '\r' && chNext != '\n') || (ch == '\n');
  56. }
  57. /*
  58. * Test the current character to see if it's the START of an EOL sequence;
  59. * if so, skip ahead to the last character of the sequence and return true,
  60. * and if not just return false. There are a few places where we want to
  61. * check to see if a newline sequence occurs at a particular point, but
  62. * where a caller expects a subroutine to stop only upon reaching the END
  63. * of a newline sequence (in particular, CR-LF on Windows). That's why
  64. * IsEOL() above only returns true on CR if the CR isn't followed by an LF
  65. * - it doesn't want to admit that there's a newline until reaching the END
  66. * of the sequence. We meet both needs by saying that there's a newline
  67. * when we see the CR in a CR-LF, but skipping the CR before returning so
  68. * that the caller's caller will see that we've stopped at the LF.
  69. */
  70. static inline bool IsEOLSkip(StyleContext &sc)
  71. {
  72. /* test for CR-LF */
  73. if (sc.ch == '\r' && sc.chNext == '\n')
  74. {
  75. /* got CR-LF - skip the CR and indicate that we're at a newline */
  76. sc.Forward();
  77. return true;
  78. }
  79. /*
  80. * in other cases, we have at most a 1-character newline, so do the
  81. * normal IsEOL test
  82. */
  83. return IsEOL(sc.ch, sc.chNext);
  84. }
  85. static inline bool IsATADS3Operator(const int ch) {
  86. return ch == '=' || ch == '{' || ch == '}' || ch == '(' || ch == ')'
  87. || ch == '[' || ch == ']' || ch == ',' || ch == ':' || ch == ';'
  88. || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%'
  89. || ch == '?' || ch == '!' || ch == '<' || ch == '>' || ch == '|'
  90. || ch == '@' || ch == '&' || ch == '~';
  91. }
  92. static inline bool IsAWordChar(const int ch) {
  93. return isalnum(ch) || ch == '_';
  94. }
  95. static inline bool IsAWordStart(const int ch) {
  96. return isalpha(ch) || ch == '_';
  97. }
  98. static inline bool IsAHexDigit(const int ch) {
  99. int lch = tolower(ch);
  100. return isdigit(lch) || lch == 'a' || lch == 'b' || lch == 'c'
  101. || lch == 'd' || lch == 'e' || lch == 'f';
  102. }
  103. static inline bool IsAnHTMLChar(int ch) {
  104. return isalnum(ch) || ch == '-' || ch == '_' || ch == '.';
  105. }
  106. static inline bool IsADirectiveChar(int ch) {
  107. return isalnum(ch) || isspace(ch) || ch == '-' || ch == '/';
  108. }
  109. static inline bool IsANumberStart(StyleContext &sc) {
  110. return isdigit(sc.ch)
  111. || (!isdigit(sc.chPrev) && sc.ch == '.' && isdigit(sc.chNext));
  112. }
  113. inline static void ColouriseTADS3Operator(StyleContext &sc) {
  114. int initState = sc.state;
  115. int c = sc.ch;
  116. sc.SetState(c == '{' || c == '}' ? SCE_T3_BRACE : SCE_T3_OPERATOR);
  117. sc.ForwardSetState(initState);
  118. }
  119. static void ColouriseTADSHTMLString(StyleContext &sc, int &lineState) {
  120. int endState = sc.state;
  121. int chQuote = sc.ch;
  122. int chString = (lineState & T3_SINGLE_QUOTE) ? '\'' : '"';
  123. if (endState == SCE_T3_HTML_STRING) {
  124. if (lineState&T3_SINGLE_QUOTE) {
  125. endState = SCE_T3_S_STRING;
  126. chString = '\'';
  127. } else if (lineState&T3_INT_EXPRESSION) {
  128. endState = SCE_T3_X_STRING;
  129. chString = '"';
  130. } else {
  131. endState = SCE_T3_HTML_DEFAULT;
  132. chString = '"';
  133. }
  134. chQuote = (lineState & T3_HTML_SQUOTE) ? '\'' : '"';
  135. } else {
  136. sc.SetState(SCE_T3_HTML_STRING);
  137. sc.Forward();
  138. }
  139. if (chQuote == '"')
  140. lineState &= ~T3_HTML_SQUOTE;
  141. else
  142. lineState |= T3_HTML_SQUOTE;
  143. while (sc.More()) {
  144. if (IsEOL(sc.ch, sc.chNext)) {
  145. return;
  146. }
  147. if (sc.ch == chQuote) {
  148. sc.ForwardSetState(endState);
  149. return;
  150. }
  151. if (sc.Match('\\', static_cast<char>(chQuote))) {
  152. sc.Forward(2);
  153. sc.SetState(endState);
  154. return;
  155. }
  156. if (sc.ch == chString) {
  157. sc.SetState(SCE_T3_DEFAULT);
  158. return;
  159. }
  160. if (sc.Match('<', '<')) {
  161. lineState |= T3_INT_EXPRESSION | T3_INT_EXPRESSION_IN_TAG;
  162. sc.SetState(SCE_T3_X_DEFAULT);
  163. sc.Forward(2);
  164. return;
  165. }
  166. if (sc.Match('\\', static_cast<char>(chQuote))
  167. || sc.Match('\\', static_cast<char>(chString))
  168. || sc.Match('\\', '\\')) {
  169. sc.Forward(2);
  170. } else {
  171. sc.Forward();
  172. }
  173. }
  174. }
  175. static void ColouriseTADS3HTMLTagStart(StyleContext &sc) {
  176. sc.SetState(SCE_T3_HTML_TAG);
  177. sc.Forward();
  178. if (sc.ch == '/') {
  179. sc.Forward();
  180. }
  181. while (IsAnHTMLChar(sc.ch)) {
  182. sc.Forward();
  183. }
  184. }
  185. static void ColouriseTADS3HTMLTag(StyleContext &sc, int &lineState) {
  186. int endState = sc.state;
  187. int chQuote = '"';
  188. int chString = '\'';
  189. switch (endState) {
  190. case SCE_T3_S_STRING:
  191. ColouriseTADS3HTMLTagStart(sc);
  192. sc.SetState(SCE_T3_HTML_DEFAULT);
  193. chQuote = '\'';
  194. chString = '"';
  195. break;
  196. case SCE_T3_D_STRING:
  197. case SCE_T3_X_STRING:
  198. ColouriseTADS3HTMLTagStart(sc);
  199. sc.SetState(SCE_T3_HTML_DEFAULT);
  200. break;
  201. case SCE_T3_HTML_DEFAULT:
  202. if (lineState&T3_SINGLE_QUOTE) {
  203. endState = SCE_T3_S_STRING;
  204. chQuote = '\'';
  205. chString = '"';
  206. } else if (lineState&T3_INT_EXPRESSION) {
  207. endState = SCE_T3_X_STRING;
  208. } else {
  209. endState = SCE_T3_D_STRING;
  210. }
  211. break;
  212. }
  213. while (sc.More()) {
  214. if (IsEOL(sc.ch, sc.chNext)) {
  215. return;
  216. }
  217. if (sc.Match('/', '>')) {
  218. sc.SetState(SCE_T3_HTML_TAG);
  219. sc.Forward(2);
  220. sc.SetState(endState);
  221. return;
  222. }
  223. if (sc.ch == '>') {
  224. sc.SetState(SCE_T3_HTML_TAG);
  225. sc.ForwardSetState(endState);
  226. return;
  227. }
  228. if (sc.ch == chQuote) {
  229. sc.SetState(endState);
  230. return;
  231. }
  232. if (sc.Match('\\', static_cast<char>(chQuote))) {
  233. sc.Forward();
  234. ColouriseTADSHTMLString(sc, lineState);
  235. if (sc.state == SCE_T3_X_DEFAULT)
  236. break;
  237. } else if (sc.ch == chString) {
  238. ColouriseTADSHTMLString(sc, lineState);
  239. } else if (sc.ch == '=') {
  240. ColouriseTADS3Operator(sc);
  241. } else {
  242. sc.Forward();
  243. }
  244. }
  245. }
  246. static void ColouriseTADS3Keyword(StyleContext &sc,
  247. WordList *keywordlists[], Sci_PositionU endPos) {
  248. char s[250];
  249. WordList &keywords = *keywordlists[0];
  250. WordList &userwords1 = *keywordlists[1];
  251. WordList &userwords2 = *keywordlists[2];
  252. WordList &userwords3 = *keywordlists[3];
  253. int initState = sc.state;
  254. sc.SetState(SCE_T3_IDENTIFIER);
  255. while (sc.More() && (IsAWordChar(sc.ch))) {
  256. sc.Forward();
  257. }
  258. sc.GetCurrent(s, sizeof(s));
  259. if ( strcmp(s, "is") == 0 || strcmp(s, "not") == 0) {
  260. // have to find if "in" is next
  261. Sci_Position n = 1;
  262. while (n + sc.currentPos < endPos && IsASpaceOrTab(sc.GetRelative(n)))
  263. n++;
  264. if (sc.GetRelative(n) == 'i' && sc.GetRelative(n+1) == 'n') {
  265. sc.Forward(n+2);
  266. sc.ChangeState(SCE_T3_KEYWORD);
  267. }
  268. } else if (keywords.InList(s)) {
  269. sc.ChangeState(SCE_T3_KEYWORD);
  270. } else if (userwords3.InList(s)) {
  271. sc.ChangeState(SCE_T3_USER3);
  272. } else if (userwords2.InList(s)) {
  273. sc.ChangeState(SCE_T3_USER2);
  274. } else if (userwords1.InList(s)) {
  275. sc.ChangeState(SCE_T3_USER1);
  276. }
  277. sc.SetState(initState);
  278. }
  279. static void ColouriseTADS3MsgParam(StyleContext &sc, int &lineState) {
  280. int endState = sc.state;
  281. int chQuote = '"';
  282. switch (endState) {
  283. case SCE_T3_S_STRING:
  284. sc.SetState(SCE_T3_MSG_PARAM);
  285. sc.Forward();
  286. chQuote = '\'';
  287. break;
  288. case SCE_T3_D_STRING:
  289. case SCE_T3_X_STRING:
  290. sc.SetState(SCE_T3_MSG_PARAM);
  291. sc.Forward();
  292. break;
  293. case SCE_T3_MSG_PARAM:
  294. if (lineState&T3_SINGLE_QUOTE) {
  295. endState = SCE_T3_S_STRING;
  296. chQuote = '\'';
  297. } else if (lineState&T3_INT_EXPRESSION) {
  298. endState = SCE_T3_X_STRING;
  299. } else {
  300. endState = SCE_T3_D_STRING;
  301. }
  302. break;
  303. }
  304. while (sc.More() && sc.ch != '}' && sc.ch != chQuote) {
  305. if (IsEOL(sc.ch, sc.chNext)) {
  306. return;
  307. }
  308. if (sc.ch == '\\') {
  309. sc.Forward();
  310. }
  311. sc.Forward();
  312. }
  313. if (sc.ch == chQuote) {
  314. sc.SetState(endState);
  315. } else {
  316. sc.ForwardSetState(endState);
  317. }
  318. }
  319. static void ColouriseTADS3LibDirective(StyleContext &sc, int &lineState) {
  320. int initState = sc.state;
  321. int chQuote = '"';
  322. switch (initState) {
  323. case SCE_T3_S_STRING:
  324. sc.SetState(SCE_T3_LIB_DIRECTIVE);
  325. sc.Forward(2);
  326. chQuote = '\'';
  327. break;
  328. case SCE_T3_D_STRING:
  329. sc.SetState(SCE_T3_LIB_DIRECTIVE);
  330. sc.Forward(2);
  331. break;
  332. case SCE_T3_LIB_DIRECTIVE:
  333. if (lineState&T3_SINGLE_QUOTE) {
  334. initState = SCE_T3_S_STRING;
  335. chQuote = '\'';
  336. } else {
  337. initState = SCE_T3_D_STRING;
  338. }
  339. break;
  340. }
  341. while (sc.More() && IsADirectiveChar(sc.ch)) {
  342. if (IsEOL(sc.ch, sc.chNext)) {
  343. return;
  344. }
  345. sc.Forward();
  346. };
  347. if (sc.ch == '>' || !sc.More()) {
  348. sc.ForwardSetState(initState);
  349. } else if (sc.ch == chQuote) {
  350. sc.SetState(initState);
  351. } else {
  352. sc.ChangeState(initState);
  353. sc.Forward();
  354. }
  355. }
  356. static void ColouriseTADS3String(StyleContext &sc, int &lineState) {
  357. int chQuote = sc.ch;
  358. int endState = sc.state;
  359. switch (sc.state) {
  360. case SCE_T3_DEFAULT:
  361. case SCE_T3_X_DEFAULT:
  362. if (chQuote == '"') {
  363. if (sc.state == SCE_T3_DEFAULT) {
  364. sc.SetState(SCE_T3_D_STRING);
  365. } else {
  366. sc.SetState(SCE_T3_X_STRING);
  367. }
  368. lineState &= ~T3_SINGLE_QUOTE;
  369. } else {
  370. sc.SetState(SCE_T3_S_STRING);
  371. lineState |= T3_SINGLE_QUOTE;
  372. }
  373. sc.Forward();
  374. break;
  375. case SCE_T3_S_STRING:
  376. chQuote = '\'';
  377. endState = lineState&T3_INT_EXPRESSION ?
  378. SCE_T3_X_DEFAULT : SCE_T3_DEFAULT;
  379. break;
  380. case SCE_T3_D_STRING:
  381. chQuote = '"';
  382. endState = SCE_T3_DEFAULT;
  383. break;
  384. case SCE_T3_X_STRING:
  385. chQuote = '"';
  386. endState = SCE_T3_X_DEFAULT;
  387. break;
  388. }
  389. while (sc.More()) {
  390. if (IsEOL(sc.ch, sc.chNext)) {
  391. return;
  392. }
  393. if (sc.ch == chQuote) {
  394. sc.ForwardSetState(endState);
  395. return;
  396. }
  397. if (sc.state == SCE_T3_D_STRING && sc.Match('<', '<')) {
  398. lineState |= T3_INT_EXPRESSION;
  399. sc.SetState(SCE_T3_X_DEFAULT);
  400. sc.Forward(2);
  401. return;
  402. }
  403. if (sc.Match('\\', static_cast<char>(chQuote))
  404. || sc.Match('\\', '\\')) {
  405. sc.Forward(2);
  406. } else if (sc.ch == '{') {
  407. ColouriseTADS3MsgParam(sc, lineState);
  408. } else if (sc.Match('<', '.')) {
  409. ColouriseTADS3LibDirective(sc, lineState);
  410. } else if (sc.ch == '<') {
  411. ColouriseTADS3HTMLTag(sc, lineState);
  412. if (sc.state == SCE_T3_X_DEFAULT)
  413. return;
  414. } else {
  415. sc.Forward();
  416. }
  417. }
  418. }
  419. static void ColouriseTADS3Comment(StyleContext &sc, int endState) {
  420. sc.SetState(SCE_T3_BLOCK_COMMENT);
  421. while (sc.More()) {
  422. if (IsEOL(sc.ch, sc.chNext)) {
  423. return;
  424. }
  425. if (sc.Match('*', '/')) {
  426. sc.Forward(2);
  427. sc.SetState(endState);
  428. return;
  429. }
  430. sc.Forward();
  431. }
  432. }
  433. static void ColouriseToEndOfLine(StyleContext &sc, int initState, int endState) {
  434. sc.SetState(initState);
  435. while (sc.More()) {
  436. if (sc.ch == '\\') {
  437. sc.Forward();
  438. if (IsEOLSkip(sc)) {
  439. return;
  440. }
  441. }
  442. if (IsEOL(sc.ch, sc.chNext)) {
  443. sc.SetState(endState);
  444. return;
  445. }
  446. sc.Forward();
  447. }
  448. }
  449. static void ColouriseTADS3Number(StyleContext &sc) {
  450. int endState = sc.state;
  451. bool inHexNumber = false;
  452. bool seenE = false;
  453. bool seenDot = sc.ch == '.';
  454. sc.SetState(SCE_T3_NUMBER);
  455. if (sc.More()) {
  456. sc.Forward();
  457. }
  458. if (sc.chPrev == '0' && tolower(sc.ch) == 'x') {
  459. inHexNumber = true;
  460. sc.Forward();
  461. }
  462. while (sc.More()) {
  463. if (inHexNumber) {
  464. if (!IsAHexDigit(sc.ch)) {
  465. break;
  466. }
  467. } else if (!isdigit(sc.ch)) {
  468. if (!seenE && tolower(sc.ch) == 'e') {
  469. seenE = true;
  470. seenDot = true;
  471. if (sc.chNext == '+' || sc.chNext == '-') {
  472. sc.Forward();
  473. }
  474. } else if (!seenDot && sc.ch == '.') {
  475. seenDot = true;
  476. } else {
  477. break;
  478. }
  479. }
  480. sc.Forward();
  481. }
  482. sc.SetState(endState);
  483. }
  484. static void ColouriseTADS3Doc(Sci_PositionU startPos, Sci_Position length, int initStyle,
  485. WordList *keywordlists[], Accessor &styler) {
  486. int visibleChars = 0;
  487. int bracketLevel = 0;
  488. int lineState = 0;
  489. Sci_PositionU endPos = startPos + length;
  490. Sci_Position lineCurrent = styler.GetLine(startPos);
  491. if (lineCurrent > 0) {
  492. lineState = styler.GetLineState(lineCurrent-1);
  493. }
  494. StyleContext sc(startPos, length, initStyle, styler);
  495. while (sc.More()) {
  496. if (IsEOL(sc.ch, sc.chNext)) {
  497. styler.SetLineState(lineCurrent, lineState);
  498. lineCurrent++;
  499. visibleChars = 0;
  500. sc.Forward();
  501. if (sc.ch == '\n') {
  502. sc.Forward();
  503. }
  504. }
  505. switch(sc.state) {
  506. case SCE_T3_PREPROCESSOR:
  507. case SCE_T3_LINE_COMMENT:
  508. ColouriseToEndOfLine(sc, sc.state, lineState&T3_INT_EXPRESSION ?
  509. SCE_T3_X_DEFAULT : SCE_T3_DEFAULT);
  510. break;
  511. case SCE_T3_S_STRING:
  512. case SCE_T3_D_STRING:
  513. case SCE_T3_X_STRING:
  514. ColouriseTADS3String(sc, lineState);
  515. visibleChars++;
  516. break;
  517. case SCE_T3_MSG_PARAM:
  518. ColouriseTADS3MsgParam(sc, lineState);
  519. break;
  520. case SCE_T3_LIB_DIRECTIVE:
  521. ColouriseTADS3LibDirective(sc, lineState);
  522. break;
  523. case SCE_T3_HTML_DEFAULT:
  524. ColouriseTADS3HTMLTag(sc, lineState);
  525. break;
  526. case SCE_T3_HTML_STRING:
  527. ColouriseTADSHTMLString(sc, lineState);
  528. break;
  529. case SCE_T3_BLOCK_COMMENT:
  530. ColouriseTADS3Comment(sc, lineState&T3_INT_EXPRESSION ?
  531. SCE_T3_X_DEFAULT : SCE_T3_DEFAULT);
  532. break;
  533. case SCE_T3_DEFAULT:
  534. case SCE_T3_X_DEFAULT:
  535. if (IsASpaceOrTab(sc.ch)) {
  536. sc.Forward();
  537. } else if (sc.ch == '#' && visibleChars == 0) {
  538. ColouriseToEndOfLine(sc, SCE_T3_PREPROCESSOR, sc.state);
  539. } else if (sc.Match('/', '*')) {
  540. ColouriseTADS3Comment(sc, sc.state);
  541. visibleChars++;
  542. } else if (sc.Match('/', '/')) {
  543. ColouriseToEndOfLine(sc, SCE_T3_LINE_COMMENT, sc.state);
  544. } else if (sc.ch == '"') {
  545. bracketLevel = 0;
  546. ColouriseTADS3String(sc, lineState);
  547. visibleChars++;
  548. } else if (sc.ch == '\'') {
  549. ColouriseTADS3String(sc, lineState);
  550. visibleChars++;
  551. } else if (sc.state == SCE_T3_X_DEFAULT && bracketLevel == 0
  552. && sc.Match('>', '>')) {
  553. sc.Forward(2);
  554. sc.SetState(SCE_T3_D_STRING);
  555. if (lineState & T3_INT_EXPRESSION_IN_TAG)
  556. sc.SetState(SCE_T3_HTML_STRING);
  557. lineState &= ~(T3_SINGLE_QUOTE|T3_INT_EXPRESSION
  558. |T3_INT_EXPRESSION_IN_TAG);
  559. } else if (IsATADS3Operator(sc.ch)) {
  560. if (sc.state == SCE_T3_X_DEFAULT) {
  561. if (sc.ch == '(') {
  562. bracketLevel++;
  563. } else if (sc.ch == ')' && bracketLevel > 0) {
  564. bracketLevel--;
  565. }
  566. }
  567. ColouriseTADS3Operator(sc);
  568. visibleChars++;
  569. } else if (IsANumberStart(sc)) {
  570. ColouriseTADS3Number(sc);
  571. visibleChars++;
  572. } else if (IsAWordStart(sc.ch)) {
  573. ColouriseTADS3Keyword(sc, keywordlists, endPos);
  574. visibleChars++;
  575. } else if (sc.Match("...")) {
  576. sc.SetState(SCE_T3_IDENTIFIER);
  577. sc.Forward(3);
  578. sc.SetState(SCE_T3_DEFAULT);
  579. } else {
  580. sc.Forward();
  581. visibleChars++;
  582. }
  583. break;
  584. default:
  585. sc.SetState(SCE_T3_DEFAULT);
  586. sc.Forward();
  587. }
  588. }
  589. sc.Complete();
  590. }
  591. /*
  592. TADS3 has two styles of top level block (TLB). Eg
  593. // default style
  594. silverKey : Key 'small silver key' 'small silver key'
  595. "A small key glints in the sunlight. "
  596. ;
  597. and
  598. silverKey : Key {
  599. 'small silver key'
  600. 'small silver key'
  601. "A small key glints in the sunlight. "
  602. }
  603. Some constructs mandate one or the other, but usually the author has may choose
  604. either.
  605. T3_SEENSTART is used to indicate that a braceless TLB has been (potentially)
  606. seen and is also used to match the closing ';' of the default style.
  607. T3_EXPECTINGIDENTIFIER and T3_EXPECTINGPUNCTUATION are used to keep track of
  608. what characters may be seen without incrementing the block level. The general
  609. pattern is identifier <punc> identifier, acceptable punctuation characters
  610. are ':', ',', '(' and ')'. No attempt is made to ensure that punctuation
  611. characters are syntactically correct, eg parentheses match. A ')' always
  612. signifies the start of a block. We just need to check if it is followed by a
  613. '{', in which case we let the brace handling code handle the folding level.
  614. expectingIdentifier == false && expectingIdentifier == false
  615. Before the start of a TLB.
  616. expectingIdentifier == true && expectingIdentifier == true
  617. Currently in an identifier. Will accept identifier or punctuation.
  618. expectingIdentifier == true && expectingIdentifier == false
  619. Just seen a punctuation character & now waiting for an identifier to start.
  620. expectingIdentifier == false && expectingIdentifier == truee
  621. We were in an identifier and have seen space. Now waiting to see a punctuation
  622. character
  623. Space, comments & preprocessor directives are always acceptable and are
  624. equivalent.
  625. */
  626. static const int T3_SEENSTART = 1 << 12;
  627. static const int T3_EXPECTINGIDENTIFIER = 1 << 13;
  628. static const int T3_EXPECTINGPUNCTUATION = 1 << 14;
  629. static inline bool IsStringTransition(int s1, int s2) {
  630. return s1 != s2
  631. && (s1 == SCE_T3_S_STRING || s1 == SCE_T3_X_STRING
  632. || (s1 == SCE_T3_D_STRING && s2 != SCE_T3_X_DEFAULT))
  633. && s2 != SCE_T3_LIB_DIRECTIVE
  634. && s2 != SCE_T3_MSG_PARAM
  635. && s2 != SCE_T3_HTML_TAG
  636. && s2 != SCE_T3_HTML_STRING;
  637. }
  638. static inline bool IsATADS3Punctuation(const int ch) {
  639. return ch == ':' || ch == ',' || ch == '(' || ch == ')';
  640. }
  641. static inline bool IsAnIdentifier(const int style) {
  642. return style == SCE_T3_IDENTIFIER
  643. || style == SCE_T3_USER1
  644. || style == SCE_T3_USER2
  645. || style == SCE_T3_USER3;
  646. }
  647. static inline bool IsAnOperator(const int style) {
  648. return style == SCE_T3_OPERATOR || style == SCE_T3_BRACE;
  649. }
  650. static inline bool IsSpaceEquivalent(const int ch, const int style) {
  651. return isspace(ch)
  652. || style == SCE_T3_BLOCK_COMMENT
  653. || style == SCE_T3_LINE_COMMENT
  654. || style == SCE_T3_PREPROCESSOR;
  655. }
  656. static char peekAhead(Sci_PositionU startPos, Sci_PositionU endPos,
  657. Accessor &styler) {
  658. for (Sci_PositionU i = startPos; i < endPos; i++) {
  659. int style = styler.StyleAt(i);
  660. char ch = styler[i];
  661. if (!IsSpaceEquivalent(ch, style)) {
  662. if (IsAnIdentifier(style)) {
  663. return 'a';
  664. }
  665. if (IsATADS3Punctuation(ch)) {
  666. return ':';
  667. }
  668. if (ch == '{') {
  669. return '{';
  670. }
  671. return '*';
  672. }
  673. }
  674. return ' ';
  675. }
  676. static void FoldTADS3Doc(Sci_PositionU startPos, Sci_Position length, int initStyle,
  677. WordList *[], Accessor &styler) {
  678. Sci_PositionU endPos = startPos + length;
  679. Sci_Position lineCurrent = styler.GetLine(startPos);
  680. int levelCurrent = SC_FOLDLEVELBASE;
  681. if (lineCurrent > 0)
  682. levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
  683. int seenStart = levelCurrent & T3_SEENSTART;
  684. int expectingIdentifier = levelCurrent & T3_EXPECTINGIDENTIFIER;
  685. int expectingPunctuation = levelCurrent & T3_EXPECTINGPUNCTUATION;
  686. levelCurrent &= SC_FOLDLEVELNUMBERMASK;
  687. int levelMinCurrent = levelCurrent;
  688. int levelNext = levelCurrent;
  689. char chNext = styler[startPos];
  690. int styleNext = styler.StyleAt(startPos);
  691. int style = initStyle;
  692. char ch = chNext;
  693. int stylePrev = style;
  694. bool redo = false;
  695. for (Sci_PositionU i = startPos; i < endPos; i++) {
  696. if (redo) {
  697. redo = false;
  698. i--;
  699. } else {
  700. ch = chNext;
  701. chNext = styler.SafeGetCharAt(i + 1);
  702. stylePrev = style;
  703. style = styleNext;
  704. styleNext = styler.StyleAt(i + 1);
  705. }
  706. bool atEOL = IsEOL(ch, chNext);
  707. if (levelNext == SC_FOLDLEVELBASE) {
  708. if (IsSpaceEquivalent(ch, style)) {
  709. if (expectingPunctuation) {
  710. expectingIdentifier = 0;
  711. }
  712. if (style == SCE_T3_BLOCK_COMMENT) {
  713. levelNext++;
  714. }
  715. } else if (ch == '{') {
  716. levelNext++;
  717. seenStart = 0;
  718. } else if (ch == '\'' || ch == '"' || ch == '[') {
  719. levelNext++;
  720. if (seenStart) {
  721. redo = true;
  722. }
  723. } else if (ch == ';') {
  724. seenStart = 0;
  725. expectingIdentifier = 0;
  726. expectingPunctuation = 0;
  727. } else if (expectingIdentifier && expectingPunctuation) {
  728. if (IsATADS3Punctuation(ch)) {
  729. if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') {
  730. levelNext++;
  731. } else {
  732. expectingPunctuation = 0;
  733. }
  734. } else if (!IsAnIdentifier(style)) {
  735. levelNext++;
  736. }
  737. } else if (expectingIdentifier && !expectingPunctuation) {
  738. if (!IsAnIdentifier(style)) {
  739. levelNext++;
  740. } else {
  741. expectingPunctuation = T3_EXPECTINGPUNCTUATION;
  742. }
  743. } else if (!expectingIdentifier && expectingPunctuation) {
  744. if (!IsATADS3Punctuation(ch)) {
  745. levelNext++;
  746. } else {
  747. if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') {
  748. levelNext++;
  749. } else {
  750. expectingIdentifier = T3_EXPECTINGIDENTIFIER;
  751. expectingPunctuation = 0;
  752. }
  753. }
  754. } else if (!expectingIdentifier && !expectingPunctuation) {
  755. if (IsAnIdentifier(style)) {
  756. seenStart = T3_SEENSTART;
  757. expectingIdentifier = T3_EXPECTINGIDENTIFIER;
  758. expectingPunctuation = T3_EXPECTINGPUNCTUATION;
  759. }
  760. }
  761. if (levelNext != SC_FOLDLEVELBASE && style != SCE_T3_BLOCK_COMMENT) {
  762. expectingIdentifier = 0;
  763. expectingPunctuation = 0;
  764. }
  765. } else if (levelNext == SC_FOLDLEVELBASE+1 && seenStart
  766. && ch == ';' && IsAnOperator(style)) {
  767. levelNext--;
  768. seenStart = 0;
  769. } else if (style == SCE_T3_BLOCK_COMMENT) {
  770. if (stylePrev != SCE_T3_BLOCK_COMMENT) {
  771. levelNext++;
  772. } else if (styleNext != SCE_T3_BLOCK_COMMENT && !atEOL) {
  773. // Comments don't end at end of line and the next character may be unstyled.
  774. levelNext--;
  775. }
  776. } else if (ch == '\'' || ch == '"') {
  777. if (IsStringTransition(style, stylePrev)) {
  778. if (levelMinCurrent > levelNext) {
  779. levelMinCurrent = levelNext;
  780. }
  781. levelNext++;
  782. } else if (IsStringTransition(style, styleNext)) {
  783. levelNext--;
  784. }
  785. } else if (IsAnOperator(style)) {
  786. if (ch == '{' || ch == '[') {
  787. // Measure the minimum before a '{' to allow
  788. // folding on "} else {"
  789. if (levelMinCurrent > levelNext) {
  790. levelMinCurrent = levelNext;
  791. }
  792. levelNext++;
  793. } else if (ch == '}' || ch == ']') {
  794. levelNext--;
  795. }
  796. }
  797. if (atEOL) {
  798. if (seenStart && levelNext == SC_FOLDLEVELBASE) {
  799. switch (peekAhead(i+1, endPos, styler)) {
  800. case ' ':
  801. case '{':
  802. break;
  803. case '*':
  804. levelNext++;
  805. break;
  806. case 'a':
  807. if (expectingPunctuation) {
  808. levelNext++;
  809. }
  810. break;
  811. case ':':
  812. if (expectingIdentifier) {
  813. levelNext++;
  814. }
  815. break;
  816. }
  817. if (levelNext != SC_FOLDLEVELBASE) {
  818. expectingIdentifier = 0;
  819. expectingPunctuation = 0;
  820. }
  821. }
  822. int lev = levelMinCurrent | (levelNext | expectingIdentifier
  823. | expectingPunctuation | seenStart) << 16;
  824. if (levelMinCurrent < levelNext)
  825. lev |= SC_FOLDLEVELHEADERFLAG;
  826. if (lev != styler.LevelAt(lineCurrent)) {
  827. styler.SetLevel(lineCurrent, lev);
  828. }
  829. lineCurrent++;
  830. levelCurrent = levelNext;
  831. levelMinCurrent = levelCurrent;
  832. }
  833. }
  834. }
  835. static const char * const tads3WordList[] = {
  836. "TADS3 Keywords",
  837. "User defined 1",
  838. "User defined 2",
  839. "User defined 3",
  840. 0
  841. };
  842. LexerModule lmTADS3(SCLEX_TADS3, ColouriseTADS3Doc, "tads3", FoldTADS3Doc, tads3WordList);