📄 lextads3.cxx.svn-base
字号:
// Scintilla source code edit control
/** @file LexTADS3.cxx
** Lexer for TADS3.
**/
// Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
/*
* TADS3 is a language designed by Michael J. Roberts for the writing of text
* based games. TADS comes from Text Adventure Development System. It has good
* support for the processing and outputting of formatted text and much of a
* TADS program listing consists of strings.
*
* TADS has two types of strings, those enclosed in single quotes (') and those
* enclosed in double quotes ("). These strings have different symantics and
* can be given different highlighting if desired.
*
* There can be embedded within both types of strings html tags
* ( <tag key=value> ), library directives ( <.directive> ), and message
* parameters ( {The doctor's/his} ).
*
* Double quoted strings can also contain interpolated expressions
* ( << rug.moved ? ' and a hole in the floor. ' : nil >> ). These expressions
* may themselves contain single or double quoted strings, although the double
* quoted strings may not contain interpolated expressions.
*
* These embedded constructs influence the output and formatting and are an
* important part of a program and require highlighting.
*
* LINKS
* http://www.tads.org/
*/
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdarg.h>
#include "Platform.h"
#include "PropSet.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "KeyWords.h"
#include "Scintilla.h"
#include "SciLexer.h"
static const int T3_SINGLE_QUOTE = 1;
static const int T3_INT_EXPRESSION = 2;
static const int T3_INT_EXPRESSION_IN_TAG = 4;
static const int T3_HTML_SQUOTE = 8;
static inline bool IsEOL(const int ch, const int chNext) {
return (ch == '\r' && chNext != '\n') || (ch == '\n');
}
/*
* Test the current character to see if it's the START of an EOL sequence;
* if so, skip ahead to the last character of the sequence and return true,
* and if not just return false. There are a few places where we want to
* check to see if a newline sequence occurs at a particular point, but
* where a caller expects a subroutine to stop only upon reaching the END
* of a newline sequence (in particular, CR-LF on Windows). That's why
* IsEOL() above only returns true on CR if the CR isn't followed by an LF
* - it doesn't want to admit that there's a newline until reaching the END
* of the sequence. We meet both needs by saying that there's a newline
* when we see the CR in a CR-LF, but skipping the CR before returning so
* that the caller's caller will see that we've stopped at the LF.
*/
static inline bool IsEOLSkip(StyleContext &sc)
{
/* test for CR-LF */
if (sc.ch == '\r' && sc.chNext == '\n')
{
/* got CR-LF - skip the CR and indicate that we're at a newline */
sc.Forward();
return true;
}
/*
* in other cases, we have at most a 1-character newline, so do the
* normal IsEOL test
*/
return IsEOL(sc.ch, sc.chNext);
}
static inline bool IsASpaceOrTab(const int ch) {
return ch == ' ' || ch == '\t';
}
static inline bool IsATADS3Operator(const int ch) {
return ch == '=' || ch == '{' || ch == '}' || ch == '(' || ch == ')'
|| ch == '[' || ch == ']' || ch == ',' || ch == ':' || ch == ';'
|| ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%'
|| ch == '?' || ch == '!' || ch == '<' || ch == '>' || ch == '|'
|| ch == '@' || ch == '&' || ch == '~';
}
static inline bool IsAWordChar(const int ch) {
return isalnum(ch) || ch == '_' || ch == '.';
}
static inline bool IsAWordStart(const int ch) {
return isalpha(ch) || ch == '_';
}
static inline bool IsAHexDigit(const int ch) {
int lch = tolower(ch);
return isdigit(lch) || lch == 'a' || lch == 'b' || lch == 'c'
|| lch == 'd' || lch == 'e' || lch == 'f';
}
static inline bool IsAnHTMLChar(int ch) {
return isalnum(ch) || ch == '-' || ch == '_' || ch == '.';
}
static inline bool IsADirectiveChar(int ch) {
return isalnum(ch) || isspace(ch) || ch == '-' || ch == '/';
}
static inline bool IsANumberStart(StyleContext &sc) {
return isdigit(sc.ch)
|| (!isdigit(sc.chPrev) && sc.ch == '.' && isdigit(sc.chNext));
}
inline static void ColouriseTADS3Operator(StyleContext &sc) {
int initState = sc.state;
int c = sc.ch;
sc.SetState(c == '{' || c == '}' ? SCE_T3_BRACE : SCE_T3_OPERATOR);
sc.ForwardSetState(initState);
}
static void ColouriseTADSHTMLString(StyleContext &sc, int &lineState) {
int endState = sc.state;
int chQuote = sc.ch;
int chString = (lineState & T3_SINGLE_QUOTE) ? '\'' : '"';
if (endState == SCE_T3_HTML_STRING) {
if (lineState&T3_SINGLE_QUOTE) {
endState = SCE_T3_S_STRING;
chString = '\'';
} else if (lineState&T3_INT_EXPRESSION) {
endState = SCE_T3_X_STRING;
chString = '"';
} else {
endState = SCE_T3_HTML_DEFAULT;
chString = '"';
}
chQuote = (lineState & T3_HTML_SQUOTE) ? '\'' : '"';
} else {
sc.SetState(SCE_T3_HTML_STRING);
sc.Forward();
}
if (chQuote == '"')
lineState &= ~T3_HTML_SQUOTE;
else
lineState |= T3_HTML_SQUOTE;
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.ch == chQuote) {
sc.ForwardSetState(endState);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))) {
sc.Forward(2);
sc.SetState(endState);
return;
}
if (sc.ch == chString) {
sc.SetState(SCE_T3_DEFAULT);
return;
}
if (sc.Match('<', '<')) {
lineState |= T3_INT_EXPRESSION | T3_INT_EXPRESSION_IN_TAG;
sc.SetState(SCE_T3_X_DEFAULT);
sc.Forward(2);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))
|| sc.Match('\\', static_cast<char>(chString))
|| sc.Match('\\', '\\')) {
sc.Forward(2);
} else {
sc.Forward();
}
}
}
static void ColouriseTADS3HTMLTagStart(StyleContext &sc) {
sc.SetState(SCE_T3_HTML_TAG);
sc.Forward();
if (sc.ch == '/') {
sc.Forward();
}
while (IsAnHTMLChar(sc.ch)) {
sc.Forward();
}
}
static void ColouriseTADS3HTMLTag(StyleContext &sc, int &lineState) {
int endState = sc.state;
int chQuote = '"';
int chString = '\'';
switch (endState) {
case SCE_T3_S_STRING:
ColouriseTADS3HTMLTagStart(sc);
sc.SetState(SCE_T3_HTML_DEFAULT);
chQuote = '\'';
chString = '"';
break;
case SCE_T3_D_STRING:
case SCE_T3_X_STRING:
ColouriseTADS3HTMLTagStart(sc);
sc.SetState(SCE_T3_HTML_DEFAULT);
break;
case SCE_T3_HTML_DEFAULT:
if (lineState&T3_SINGLE_QUOTE) {
endState = SCE_T3_S_STRING;
chQuote = '\'';
chString = '"';
} else if (lineState&T3_INT_EXPRESSION) {
endState = SCE_T3_X_STRING;
} else {
endState = SCE_T3_D_STRING;
}
break;
}
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.Match('/', '>')) {
sc.SetState(SCE_T3_HTML_TAG);
sc.Forward(2);
sc.SetState(endState);
return;
}
if (sc.ch == '>') {
sc.SetState(SCE_T3_HTML_TAG);
sc.ForwardSetState(endState);
return;
}
if (sc.ch == chQuote) {
sc.SetState(endState);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))) {
sc.Forward();
ColouriseTADSHTMLString(sc, lineState);
if (sc.state == SCE_T3_X_DEFAULT)
break;
} else if (sc.ch == chString) {
ColouriseTADSHTMLString(sc, lineState);
} else if (sc.ch == '=') {
ColouriseTADS3Operator(sc);
} else {
sc.Forward();
}
}
}
static void ColouriseTADS3Keyword(StyleContext &sc,
WordList *keywordlists[], unsigned int endPos) {
char s[250];
WordList &keywords = *keywordlists[0];
WordList &userwords1 = *keywordlists[1];
WordList &userwords2 = *keywordlists[2];
WordList &userwords3 = *keywordlists[3];
int initState = sc.state;
sc.SetState(SCE_T3_IDENTIFIER);
while (sc.More() && (IsAWordChar(sc.ch))) {
sc.Forward();
}
sc.GetCurrent(s, sizeof(s));
if ( strcmp(s, "is") == 0 || strcmp(s, "not") == 0) {
// have to find if "in" is next
int n = 1;
while (n + sc.currentPos < endPos && IsASpaceOrTab(sc.GetRelative(n)))
n++;
if (sc.GetRelative(n) == 'i' && sc.GetRelative(n+1) == 'n') {
sc.Forward(n+2);
sc.ChangeState(SCE_T3_KEYWORD);
}
} else if (keywords.InList(s)) {
sc.ChangeState(SCE_T3_KEYWORD);
} else if (userwords3.InList(s)) {
sc.ChangeState(SCE_T3_USER3);
} else if (userwords2.InList(s)) {
sc.ChangeState(SCE_T3_USER2);
} else if (userwords1.InList(s)) {
sc.ChangeState(SCE_T3_USER1);
}
sc.SetState(initState);
}
static void ColouriseTADS3MsgParam(StyleContext &sc, int &lineState) {
int endState = sc.state;
int chQuote = '"';
switch (endState) {
case SCE_T3_S_STRING:
sc.SetState(SCE_T3_MSG_PARAM);
sc.Forward();
chQuote = '\'';
break;
case SCE_T3_D_STRING:
case SCE_T3_X_STRING:
sc.SetState(SCE_T3_MSG_PARAM);
sc.Forward();
break;
case SCE_T3_MSG_PARAM:
if (lineState&T3_SINGLE_QUOTE) {
endState = SCE_T3_S_STRING;
chQuote = '\'';
} else if (lineState&T3_INT_EXPRESSION) {
endState = SCE_T3_X_STRING;
} else {
endState = SCE_T3_D_STRING;
}
break;
}
while (sc.More() && sc.ch != '}' && sc.ch != chQuote) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.ch == '\\') {
sc.Forward();
}
sc.Forward();
}
if (sc.ch == chQuote) {
sc.SetState(endState);
} else {
sc.ForwardSetState(endState);
}
}
static void ColouriseTADS3LibDirective(StyleContext &sc, int &lineState) {
int initState = sc.state;
int chQuote = '"';
switch (initState) {
case SCE_T3_S_STRING:
sc.SetState(SCE_T3_LIB_DIRECTIVE);
sc.Forward(2);
chQuote = '\'';
break;
case SCE_T3_D_STRING:
sc.SetState(SCE_T3_LIB_DIRECTIVE);
sc.Forward(2);
break;
case SCE_T3_LIB_DIRECTIVE:
if (lineState&T3_SINGLE_QUOTE) {
initState = SCE_T3_S_STRING;
chQuote = '\'';
} else {
initState = SCE_T3_D_STRING;
}
break;
}
while (sc.More() && IsADirectiveChar(sc.ch)) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
sc.Forward();
};
if (sc.ch == '>' || !sc.More()) {
sc.ForwardSetState(initState);
} else if (sc.ch == chQuote) {
sc.SetState(initState);
} else {
sc.ChangeState(initState);
sc.Forward();
}
}
static void ColouriseTADS3String(StyleContext &sc, int &lineState) {
int chQuote = sc.ch;
int endState = sc.state;
switch (sc.state) {
case SCE_T3_DEFAULT:
case SCE_T3_X_DEFAULT:
if (chQuote == '"') {
if (sc.state == SCE_T3_DEFAULT) {
sc.SetState(SCE_T3_D_STRING);
} else {
sc.SetState(SCE_T3_X_STRING);
}
lineState &= ~T3_SINGLE_QUOTE;
} else {
sc.SetState(SCE_T3_S_STRING);
lineState |= T3_SINGLE_QUOTE;
}
sc.Forward();
break;
case SCE_T3_S_STRING:
chQuote = '\'';
endState = lineState&T3_INT_EXPRESSION ?
SCE_T3_X_DEFAULT : SCE_T3_DEFAULT;
break;
case SCE_T3_D_STRING:
chQuote = '"';
endState = SCE_T3_DEFAULT;
break;
case SCE_T3_X_STRING:
chQuote = '"';
endState = SCE_T3_X_DEFAULT;
break;
}
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.ch == chQuote) {
sc.ForwardSetState(endState);
return;
}
if (sc.state == SCE_T3_D_STRING && sc.Match('<', '<')) {
lineState |= T3_INT_EXPRESSION;
sc.SetState(SCE_T3_X_DEFAULT);
sc.Forward(2);
return;
}
if (sc.Match('\\', static_cast<char>(chQuote))
|| sc.Match('\\', '\\')) {
sc.Forward(2);
} else if (sc.ch == '{') {
ColouriseTADS3MsgParam(sc, lineState);
} else if (sc.Match('<', '.')) {
ColouriseTADS3LibDirective(sc, lineState);
} else if (sc.ch == '<') {
ColouriseTADS3HTMLTag(sc, lineState);
if (sc.state == SCE_T3_X_DEFAULT)
return;
} else {
sc.Forward();
}
}
}
static void ColouriseTADS3Comment(StyleContext &sc, int endState) {
sc.SetState(SCE_T3_BLOCK_COMMENT);
while (sc.More()) {
if (IsEOL(sc.ch, sc.chNext)) {
return;
}
if (sc.Match('*', '/')) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -