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