diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/LexTADS3.cxx | 333 |
1 files changed, 264 insertions, 69 deletions
diff --git a/src/LexTADS3.cxx b/src/LexTADS3.cxx index 2ef6f1323..ffec06c7c 100644 --- a/src/LexTADS3.cxx +++ b/src/LexTADS3.cxx @@ -44,24 +44,6 @@ * "You see << isKnown? '{iobj/him} lying' : 'nothing' >> on the ground. " * ---------- * - * TO DO - * - * top level blocks can have one of two forms - * - * cave : DarkRoom 'small cave' 'small cave' - * "There is barely enough room to stand in this small cave. " - * ; - * - * or - * - * cave : DarkRoom { - * 'small cave' - * 'small cave' - * "There is barely enough room to stand in this small cave. " - * } - * - * currently only the latter form is folded. - * * LINKS * http://www.tads.org/ */ @@ -112,12 +94,8 @@ static inline bool IsAHexDigit(const int ch) { } static inline bool IsABracket(const int ch) { - return ch == '(' || ch == '{' || ch == '[' - || ch == ')' || ch == '}' || ch == ']'; -} - -static inline bool IsAMsgParamChar(int ch) { - return isalpha(ch) || isspace(ch) || ch == '/' || ch == '\'' || ch == '\\'; + return ch == '{' || ch == '[' || ch == '(' + || ch == '}' || ch == ']' || ch == ')'; } static inline bool IsAnHTMLChar(int ch) { @@ -125,7 +103,7 @@ static inline bool IsAnHTMLChar(int ch) { } static inline bool IsADirectiveChar(int ch) { - return isalnum(ch) || isspace(ch) || ch == '-'; + return isalnum(ch) || isspace(ch) || ch == '-' || ch == '/'; } static inline bool IsANumberStart(StyleContext &sc) { @@ -147,17 +125,21 @@ inline static void ColouriseTADS3Bracket(StyleContext &sc) { static void ColouriseTADSHTMLString(StyleContext &sc) { int initState = sc.state; - int quoteChar = sc.ch; + int chQuote = sc.ch; + int chString = chQuote == '"'? '\'': '"'; sc.SetState(SCE_T3_HTML_STRING); sc.Forward(); while (sc.More()) { - if (sc.Match('\\', static_cast<char>(quoteChar))) { + if (sc.Match('\\', static_cast<char>(chQuote)) + || sc.Match('\\', static_cast<char>(chString))) { sc.Forward(2); - } - if (sc.ch == quoteChar || IsEOL(sc.ch)) { + } else if (sc.ch == chQuote || IsEOL(sc.ch)) { sc.ForwardSetState(initState); return; + } else if (sc.ch == chString) { + sc.SetState(initState); + return; } sc.Forward(); } @@ -176,6 +158,8 @@ static void ColouriseTADS3HTMLTagStart(StyleContext &sc) { static void ColouriseTADS3HTMLTag(StyleContext &sc) { int initState = sc.state; + int chQuote = '\''; + int chString = '"'; switch (initState) { case SCE_T3_S_STRING: ColouriseTADS3HTMLTagStart(sc); @@ -184,6 +168,8 @@ static void ColouriseTADS3HTMLTag(StyleContext &sc) { case SCE_T3_D_STRING: ColouriseTADS3HTMLTagStart(sc); sc.SetState(SCE_T3_S_H_DEFAULT); + chQuote = '"'; + chString = '\''; break; case SCE_T3_X_S_STRING: ColouriseTADS3HTMLTagStart(sc); @@ -192,18 +178,24 @@ static void ColouriseTADS3HTMLTag(StyleContext &sc) { case SCE_T3_X_D_STRING: ColouriseTADS3HTMLTagStart(sc); sc.SetState(SCE_T3_X_D_H_DEFAULT); + chQuote = '"'; + chString = '\''; break; case SCE_T3_S_H_DEFAULT: initState = SCE_T3_S_STRING; break; case SCE_T3_D_H_DEFAULT: initState = SCE_T3_D_STRING; + chQuote = '"'; + chString = '\''; break; case SCE_T3_X_S_H_DEFAULT: initState = SCE_T3_X_S_STRING; break; case SCE_T3_X_D_H_DEFAULT: initState = SCE_T3_X_D_STRING; + chQuote = '"'; + chString = '\''; break; } @@ -217,8 +209,11 @@ static void ColouriseTADS3HTMLTag(StyleContext &sc) { sc.SetState(SCE_T3_HTML_TAG); sc.ForwardSetState(initState); return; + } else if (sc.ch == chQuote) { + sc.SetState(initState); + return; } - if (sc.ch == '\'' || sc.ch == '"') { + if (sc.ch == chString) { ColouriseTADSHTMLString(sc); } else if (sc.ch == '=') { ColouriseTADS3Operator(sc); @@ -265,10 +260,12 @@ static void ColouriseTADS3Keyword(StyleContext &sc, static void ColouriseTADS3MsgParam(StyleContext &sc) { int initState = sc.state; + int chQuote = '"'; switch (initState) { case SCE_T3_S_STRING: sc.SetState(SCE_T3_S_MSG_PARAM); sc.Forward(); + chQuote = '\''; break; case SCE_T3_D_STRING: sc.SetState(SCE_T3_D_MSG_PARAM); @@ -277,6 +274,7 @@ static void ColouriseTADS3MsgParam(StyleContext &sc) { case SCE_T3_X_S_STRING: sc.SetState(SCE_T3_X_S_MSG_PARAM); sc.Forward(); + chQuote = '\''; break; case SCE_T3_X_D_STRING: sc.SetState(SCE_T3_X_D_MSG_PARAM); @@ -284,41 +282,40 @@ static void ColouriseTADS3MsgParam(StyleContext &sc) { break; case SCE_T3_S_MSG_PARAM: initState = SCE_T3_S_STRING; + chQuote = '\''; break; case SCE_T3_D_MSG_PARAM: initState = SCE_T3_D_STRING; break; case SCE_T3_X_S_MSG_PARAM: initState = SCE_T3_X_S_STRING; + chQuote = '\''; break; case SCE_T3_X_D_MSG_PARAM: initState = SCE_T3_X_D_STRING; break; } - while (sc.More() && IsAMsgParamChar(sc.ch)) { + while (sc.More() && sc.ch != '}' && sc.ch != chQuote) { if (sc.ch == '\\') { - if (initState == SCE_T3_D_STRING) { - break; - } else if (sc.chNext != '\'') { - break; - } + sc.Forward(); } sc.Forward(); } - if (sc.ch == '}' || !sc.More()) { - sc.ForwardSetState(initState); + if (sc.ch == chQuote) { + sc.SetState(initState); } else { - sc.ChangeState(initState); - sc.Forward(); + sc.ForwardSetState(initState); } } static void ColouriseTADS3LibDirective(StyleContext &sc) { int initState = sc.state; + int chQuote = '"'; switch (initState) { case SCE_T3_S_STRING: sc.SetState(SCE_T3_S_LIB_DIRECTIVE); sc.Forward(2); + chQuote = '\''; break; case SCE_T3_D_STRING: sc.SetState(SCE_T3_D_LIB_DIRECTIVE); @@ -327,6 +324,7 @@ static void ColouriseTADS3LibDirective(StyleContext &sc) { case SCE_T3_X_S_STRING: sc.SetState(SCE_T3_X_S_LIB_DIRECTIVE); sc.Forward(2); + chQuote = '\''; break; case SCE_T3_X_D_STRING: sc.SetState(SCE_T3_X_D_LIB_DIRECTIVE); @@ -334,12 +332,14 @@ static void ColouriseTADS3LibDirective(StyleContext &sc) { break; case SCE_T3_S_LIB_DIRECTIVE: initState = SCE_T3_S_STRING; + chQuote = '\''; break; case SCE_T3_D_LIB_DIRECTIVE: initState = SCE_T3_D_STRING; break; case SCE_T3_X_S_LIB_DIRECTIVE: initState = SCE_T3_X_S_STRING; + chQuote = '\''; break; case SCE_T3_X_D_LIB_DIRECTIVE: initState = SCE_T3_X_D_STRING; @@ -350,6 +350,8 @@ static void ColouriseTADS3LibDirective(StyleContext &sc) { }; if (sc.ch == '>' || !sc.More()) { sc.ForwardSetState(initState); + } else if (sc.ch == chQuote) { + sc.SetState(initState); } else { sc.ChangeState(initState); sc.Forward(); @@ -357,11 +359,11 @@ static void ColouriseTADS3LibDirective(StyleContext &sc) { } static void ColouriseTADS3String(StyleContext &sc) { - int quoteChar = sc.ch; + int chQuote = sc.ch; int initState = sc.state; switch (sc.state) { case SCE_T3_DEFAULT: - if (quoteChar == '"') { + if (chQuote == '"') { sc.SetState(SCE_T3_D_STRING); } else { sc.SetState(SCE_T3_S_STRING); @@ -369,7 +371,7 @@ static void ColouriseTADS3String(StyleContext &sc) { sc.Forward(); break; case SCE_T3_X_DEFAULT: - if (quoteChar == '"') { + if (chQuote == '"') { sc.SetState(SCE_T3_X_D_STRING); } else { sc.SetState(SCE_T3_X_S_STRING); @@ -377,27 +379,27 @@ static void ColouriseTADS3String(StyleContext &sc) { sc.Forward(); break; case SCE_T3_S_STRING: - quoteChar = '\''; + chQuote = '\''; initState = SCE_T3_DEFAULT; break; case SCE_T3_D_STRING: - quoteChar = '"'; + chQuote = '"'; initState = SCE_T3_DEFAULT; break; case SCE_T3_X_S_STRING: - quoteChar = '\''; + chQuote = '\''; initState = SCE_T3_X_DEFAULT; break; case SCE_T3_X_D_STRING: - quoteChar = '"'; + chQuote = '"'; initState = SCE_T3_X_DEFAULT; break; } while (sc.More()) { - if (sc.Match('\\', static_cast<char>(quoteChar))) { + if (sc.Match('\\', static_cast<char>(chQuote))) { sc.Forward(2); } - if (sc.ch == quoteChar) { + if (sc.ch == chQuote) { sc.ForwardSetState(initState); return; } @@ -619,6 +621,57 @@ static void ColouriseTADS3Doc(unsigned int startPos, int length, int initStyle, sc.Complete(); } +/* + TADS3 has two styles of top level block (TLB). Eg + + // default style + silverKey : Key 'small silver key' 'small silver key' + "A small key glints in the sunlight. " + ; + + and + + silverKey : Key { + 'small silver key' + 'small silver key' + "A small key glints in the sunlight. " + } + + Some constructs mandate one or the other, but usually the author has may choose + either. + + T3_SEENSTART is used to indicate that a braceless TLB has been (potentially) + seen and is also used to match the closing ';' of the default style. + + T3_EXPECTINGIDENTIFIER and T3_EXPECTINGPUNCTUATION are used to keep track of + what characters may be seen without incrementing the block level. The general + pattern is identifier <punc> identifier, acceptable punctuation characters + are ':', ',', '(' and ')'. No attempt is made to ensure that punctuation + characters are syntactically correct, eg parentheses match. A ')' always + signifies the start of a block. We just need to check if it is followed by a + '{', in which case we let the brace handling code handle the folding level. + + expectingIdentifier == false && expectingIdentifier == false + Before the start of a TLB. + + expectingIdentifier == true && expectingIdentifier == true + Currently in an identifier. Will accept identifier or punctuation. + + expectingIdentifier == true && expectingIdentifier == false + Just seen a punctuation character & now waiting for an identifier to start. + + expectingIdentifier == false && expectingIdentifier == truee + We were in an identifier and have seen space. Now waiting to see a punctuation + character + + Space, comments & preprocessor directives are always acceptable and are + equivalent. +*/ + +static const int T3_SEENSTART = 1 << 12; +static const int T3_EXPECTINGIDENTIFIER = 1 << 13; +static const int T3_EXPECTINGPUNCTUATION = 1 << 14; + static inline bool IsStreamCommentStyle(int style) { return style == SCE_T3_BLOCK_COMMENT || style == SCE_T3_X_BLOCK_COMMENT; @@ -630,28 +683,71 @@ static inline bool IsStringTransition(int s1, int s2) { return s2 != s1 && s2 != SCE_T3_S_LIB_DIRECTIVE && s2 != SCE_T3_S_MSG_PARAM - && s2 != SCE_T3_HTML_TAG; + && s2 != SCE_T3_HTML_TAG + && s2 != SCE_T3_HTML_STRING; case SCE_T3_D_STRING: return s2 != s1 && s2 != SCE_T3_D_LIB_DIRECTIVE && s2 != SCE_T3_D_MSG_PARAM && s2 != SCE_T3_HTML_TAG - && s2 != SCE_T3_X_DEFAULT; + && s2 != SCE_T3_X_DEFAULT + && s2 != SCE_T3_HTML_STRING; case SCE_T3_X_S_STRING: return s2 != s1 && s2 != SCE_T3_X_S_LIB_DIRECTIVE && s2 != SCE_T3_X_S_MSG_PARAM - && s2 != SCE_T3_HTML_TAG; + && s2 != SCE_T3_HTML_TAG + && s2 != SCE_T3_HTML_STRING; case SCE_T3_X_D_STRING: return s2 != s1 && s2 != SCE_T3_X_D_LIB_DIRECTIVE && s2 != SCE_T3_X_D_MSG_PARAM - && s2 != SCE_T3_HTML_TAG; + && s2 != SCE_T3_HTML_TAG + && s2 != SCE_T3_HTML_STRING; default: return false; } } +static inline bool IsATADS3Punctuation(const int ch) { + return ch == ':' || ch == ',' || ch == '(' || ch == ')'; +} + +static inline bool IsAnIdentifier(const int ch, const int style) { + return IsAWordChar(ch) && style == SCE_T3_DEFAULT + || style == SCE_T3_USER1 + || style == SCE_T3_USER2 + || ch == '.' && style == SCE_T3_DEFAULT; +} + +static inline bool IsSpaceEquivalent(const int ch, const int style) { + return isspace(ch) + || style == SCE_T3_BLOCK_COMMENT + || style == SCE_T3_LINE_COMMENT + || style == SCE_T3_PREPROCESSOR; +} + +static char peekAhead(unsigned int startPos, unsigned int endPos, + Accessor &styler) { + for (unsigned int i = startPos; i < endPos; i++) { + int style = styler.StyleAt(i); + char ch = styler[i]; + if (!IsSpaceEquivalent(ch, style)) { + if (IsAnIdentifier(ch, style)) { + return 'a'; + } + if (IsATADS3Punctuation(ch)) { + return ':'; + } + if (ch == '{') { + return '{'; + } + return '*'; + } + } + return ' '; +} + static void FoldTADS3Doc(unsigned int startPos, int length, int initStyle, WordList *[], Accessor &styler) { unsigned int endPos = startPos + length; @@ -659,49 +755,149 @@ static void FoldTADS3Doc(unsigned int startPos, int length, int initStyle, int levelCurrent = SC_FOLDLEVELBASE; if (lineCurrent > 0) levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; + int seenStart = levelCurrent & T3_SEENSTART; + int expectingIdentifier = levelCurrent & T3_EXPECTINGIDENTIFIER; + int expectingPunctuation = levelCurrent & T3_EXPECTINGPUNCTUATION; + levelCurrent &= SC_FOLDLEVELNUMBERMASK; int levelMinCurrent = levelCurrent; int levelNext = levelCurrent; char chNext = styler[startPos]; int styleNext = styler.StyleAt(startPos); int style = initStyle; + char ch = chNext; + int stylePrev = style; + bool redo = false; for (unsigned int i = startPos; i < endPos; i++) { - char ch = chNext; - chNext = styler.SafeGetCharAt(i + 1); - int stylePrev = style; - style = styleNext; - styleNext = styler.StyleAt(i + 1); + if (redo) { + redo = false; + i--; + } else { + ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + } bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); - if (IsStreamCommentStyle(style)) { + + if (levelNext == SC_FOLDLEVELBASE) { + if (IsSpaceEquivalent(ch, style)) { + if (expectingPunctuation) { + expectingIdentifier = 0; + } + if (style == SCE_T3_BLOCK_COMMENT) { + levelNext++; + } + } else if (ch == '{') { + levelNext++; + seenStart = 0; + } else if (ch == '\'' || ch == '"' || ch == '[') { + levelNext++; + redo = true; + } else if (ch == ';') { + seenStart = 0; + expectingIdentifier = 0; + expectingPunctuation = 0; + } else if (expectingIdentifier && expectingPunctuation) { + if (IsATADS3Punctuation(ch)) { + if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') { + levelNext++; + } else { + expectingPunctuation = 0; + } + } else if (!IsAnIdentifier(ch, style)) { + levelNext++; + } + } else if (expectingIdentifier && !expectingPunctuation) { + if (!IsAnIdentifier(ch, style)) { + levelNext++; + } else { + expectingPunctuation = T3_EXPECTINGPUNCTUATION; + } + } else if (!expectingIdentifier && expectingPunctuation) { + if (!IsATADS3Punctuation(ch)) { + levelNext++; + } else { + if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') { + levelNext++; + } else { + expectingIdentifier = T3_EXPECTINGIDENTIFIER; + expectingPunctuation = 0; + } + } + } else if (!expectingIdentifier && !expectingPunctuation) { + if (IsAnIdentifier(ch, style)) { + seenStart = T3_SEENSTART; + expectingIdentifier = T3_EXPECTINGIDENTIFIER; + expectingPunctuation = T3_EXPECTINGPUNCTUATION; + } + } + + if (levelNext != SC_FOLDLEVELBASE && style != SCE_T3_BLOCK_COMMENT) { + expectingIdentifier = 0; + expectingPunctuation = 0; + } + + } else if (levelNext == SC_FOLDLEVELBASE+1 && seenStart + && ch == ';' && style == SCE_T3_OPERATOR ) { + levelNext--; + seenStart = 0; + } else if (IsStreamCommentStyle(style)) { if (!IsStreamCommentStyle(stylePrev)) { levelNext++; } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { // Comments don't end at end of line and the next character may be unstyled. levelNext--; } - } else if (style == SCE_T3_BRACKET) { - if (ch == '{' || ch == '[') { - // Measure the minimum before a '{' to allow - // folding on "} else {" + } else if (ch == '\'' || ch == '"') { + if (IsStringTransition(style, stylePrev)) { if (levelMinCurrent > levelNext) { levelMinCurrent = levelNext; } levelNext++; - } else if (ch == '}' || ch == ']') { + } else if (IsStringTransition(style, styleNext)) { levelNext--; } - } else if ((ch == '\'' || ch == '"')) { - if (IsStringTransition(style, stylePrev)) { + } else if (style == SCE_T3_BRACKET) { + if (ch == '{' || ch == '[') { + // Measure the minimum before a '{' to allow + // folding on "} else {" if (levelMinCurrent > levelNext) { levelMinCurrent = levelNext; } levelNext++; - } else if (IsStringTransition(style, styleNext)) { + } else if (ch == '}' || ch == ']') { levelNext--; } } if (atEOL) { - int lev = levelMinCurrent | levelNext << 16; + if (seenStart && levelNext == SC_FOLDLEVELBASE) { + switch (peekAhead(i+1, endPos, styler)) { + case ' ': + case '{': + break; + case '*': + levelNext++; + break; + case 'a': + if (expectingPunctuation) { + levelNext++; + } + break; + case ':': + if (expectingIdentifier) { + levelNext++; + } + break; + } + if (levelNext != SC_FOLDLEVELBASE) { + expectingIdentifier = 0; + expectingPunctuation = 0; + } + } + int lev = levelMinCurrent | (levelNext | expectingIdentifier + | expectingPunctuation | seenStart) << 16; if (levelMinCurrent < levelNext) lev |= SC_FOLDLEVELHEADERFLAG; if (lev != styler.LevelAt(lineCurrent)) { @@ -717,7 +913,6 @@ static void FoldTADS3Doc(unsigned int startPos, int length, int initStyle, static const char * const tads3WordList[] = { "TADS3 Keywords", "User defined 1", - "User defined 2", 0 }; |