home *** CD-ROM | disk | FTP | other *** search
- /* -----------------------------------------------------------------------------
-
- C++ scanner ©1996 Dietmar Eilert, modified by Olaf Barthel
-
- GoldED syntax parser (uses a syntax cache). Dice:
-
- DMAKE
-
- -------------------------------------------------------------------------------
-
- */
-
- #include "defs.h"
-
- /// "Header stuff"
-
- // Buffer handles are allocated for each text buffer to keep track of ressources:
-
- struct BufferHandle {
-
- struct EditConfig *bh_EditConfig; // pointer to text data
- struct GlobalConfig *bh_GlobalConfig; // editor configuration
- struct SyntaxInfo *bh_SyntaxInfo; // parser output
- struct RefreshRequest bh_RefreshRequest; // display refresh request
- };
-
- // known ANSI C keywords are described by syntax structures:
-
- struct Keyword {
-
- UBYTE *Pattern; // known pattern (e.g. "UWORD")
- UBYTE *Next; // valid range for next non-space char (or NULL)
- UBYTE *Required; // this char has to be on the same line (or NULL)
- UWORD Level; // syntax level
- };
-
- #define EMPTY_STACK ((struct SyntaxInfo *)~0) // empty stack flag
-
- #define UPPER(a) ((a) & 95) // simple uppercase conversion
-
- // syntax infos (or EMPTY_STACK) are attached to parsed lines
-
- struct SyntaxInfo {
-
- UWORD Flags; // comment flags
- struct SyntaxChunk SyntaxChunk; // syntax chunks
- };
-
- // these flag bits describe the line's comment properties
-
- #define COMMENT_NONE (0) // no comment in this line
- #define COMMENT_SIMPLE (1L<<0) // there is a comment in this line
- #define COMMENT_START (1L<<1) // line is start of multi-line comment
- #define COMMENT_BODY (1L<<2) // line is body of multi-line comment
- #define COMMENT_END (1L<<3) // line is end of multi-line comment
-
- // macros to access SyntaxInfo members
-
- #define GET_FLAGS(node) (((struct SyntaxInfo *)(node)->UserData)->Flags)
- #define GET_CHUNK(node) (&((struct SyntaxInfo *)(node)->UserData)->SyntaxChunk)
-
- // recognized syntax levels
-
- enum SyntaxLevels {
-
- SYNTAX_STANDARD,
- SYNTAX_COMMENTANSI,
- SYNTAX_COMMENTCPP,
- SYNTAX_RESERVED,
- SYNTAX_PREPROCESSOR,
- SYNTAX_NUMBER,
- SYNTAX_QUOTE,
- SYNTAX_STRING,
- SYNTAX_TYPE,
- SYNTAX_OPERATOR,
- SYNTAX_BRACKET,
- SYNTAX_POINTER,
- SYNTAX_MAXIMUM
- };
-
- ///
- /// "Globals"
-
- struct Keyword *Hash[256];
-
- BOOL IsOperator[256];
- BOOL IsLetter [256];
- BOOL IsSpace [256];
- BOOL IsDigit [256];
- BOOL IsKnown [256];
-
- struct Keyword Reserved[] = {
-
- // known_word next_char on_same_line level
-
- // uppercase
-
- { "APTR", NULL, NULL, SYNTAX_TYPE },
- { "BOOL", NULL, NULL, SYNTAX_TYPE },
- { "BPTR", NULL, NULL, SYNTAX_TYPE },
- { "BPTR", NULL, NULL, SYNTAX_TYPE },
- { "BYTE", NULL, NULL, SYNTAX_TYPE },
- { "FLOAT", NULL, NULL, SYNTAX_TYPE },
- { "LONG", NULL, NULL, SYNTAX_TYPE },
- { "REGISTER", NULL, NULL, SYNTAX_TYPE },
- { "STATIC", NULL, NULL, SYNTAX_TYPE },
- { "STRPTR", NULL, NULL, SYNTAX_TYPE },
- { "TEXT", NULL, NULL, SYNTAX_TYPE },
- { "UBYTE", NULL, NULL, SYNTAX_TYPE },
- { "ULONG", NULL, NULL, SYNTAX_TYPE },
- { "UWORD", NULL, NULL, SYNTAX_TYPE },
- { "VOID", NULL, NULL, SYNTAX_TYPE },
- { "WORD", NULL, NULL, SYNTAX_TYPE },
-
- // lowercase
-
- { "asm", "{", NULL, SYNTAX_RESERVED },
- { "auto", NULL, NULL, SYNTAX_RESERVED },
- { "break", ";", NULL, SYNTAX_RESERVED },
- { "case", NULL, ":", SYNTAX_RESERVED },
- { "catch", "(", NULL, SYNTAX_RESERVED },
- { "char", NULL, NULL, SYNTAX_TYPE },
- { "class", NULL, NULL, SYNTAX_RESERVED },
- { "const", NULL, NULL, SYNTAX_RESERVED },
- { "continue", ";", NULL, SYNTAX_RESERVED },
- { "default", ":", NULL, SYNTAX_RESERVED },
- { "delete", NULL, NULL, SYNTAX_RESERVED },
- { "do", NULL, NULL, SYNTAX_RESERVED },
- { "double", NULL, NULL, SYNTAX_TYPE },
- { "else", NULL, NULL, SYNTAX_RESERVED },
- { "enum", NULL, NULL, SYNTAX_RESERVED },
- { "extern", NULL, ";", SYNTAX_RESERVED },
- { "float", NULL, NULL, SYNTAX_TYPE },
- { "for", "(", "(", SYNTAX_RESERVED },
- { "friend", NULL, NULL, SYNTAX_RESERVED },
- { "goto", NULL, NULL, SYNTAX_RESERVED },
- { "if", "(", "(", SYNTAX_RESERVED },
- { "inline", NULL, NULL, SYNTAX_RESERVED },
- { "int", NULL, NULL, SYNTAX_TYPE },
- { "long", NULL, NULL, SYNTAX_TYPE },
- { "new", NULL, NULL, SYNTAX_RESERVED },
- { "operator", NULL, NULL, SYNTAX_RESERVED },
- { "private", NULL, NULL, SYNTAX_RESERVED },
- { "protected", NULL, NULL, SYNTAX_RESERVED },
- { "public", NULL, NULL, SYNTAX_RESERVED },
- { "register", NULL, NULL, SYNTAX_RESERVED },
- { "return", "(;", NULL, SYNTAX_RESERVED },
- { "short", NULL, NULL, SYNTAX_TYPE },
- { "signed", NULL, NULL, SYNTAX_TYPE },
- { "sizeof", NULL, NULL, SYNTAX_RESERVED },
- { "static", NULL, NULL, SYNTAX_RESERVED },
- { "struct", NULL, NULL, SYNTAX_TYPE },
- { "switch", "(", NULL, SYNTAX_RESERVED },
- { "template", "<", NULL, SYNTAX_RESERVED },
- { "this", NULL, NULL, SYNTAX_RESERVED },
- { "throw", NULL, NULL, SYNTAX_RESERVED },
- { "try", "{", NULL, SYNTAX_RESERVED },
- { "typedef", NULL, NULL, SYNTAX_RESERVED },
- { "union", NULL, NULL, SYNTAX_TYPE },
- { "unsigned", NULL, NULL, SYNTAX_TYPE },
- { "virtual", NULL, NULL, SYNTAX_RESERVED },
- { "void", NULL, NULL, SYNTAX_TYPE },
- { "volatile", NULL, NULL, SYNTAX_RESERVED },
- { "while", "(", NULL, SYNTAX_RESERVED },
-
- { NULL, NULL, NULL, 0}
- };
-
- ///
- /// "Prototype"
-
- // library functions
-
- Prototype LibCall struct ParserData *MountScanner(void);
- Prototype LibCall ULONG StartScanner(__A0 struct GlobalConfig *, __A1 struct EditConfig *, __D0 struct SyntaxChunk *);
- Prototype LibCall ULONG CloseScanner(__D0 ULONG);
- Prototype LibCall void FlushScanner(__D0 ULONG);
- Prototype LibCall void SetupScanner(__A0 struct GlobalConfig *);
- Prototype LibCall struct RefreshRequest *BriefScanner(__D0 ULONG, __A0 struct ScannerNotify *);
- Prototype LibCall struct SyntaxChunk *ParseLine (__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
- Prototype LibCall void UnparseLines(__A0 struct LineNode *, __D0 ULONG);
- Prototype LibCall void ParseSection(__D0 ULONG, __A0 struct LineNode *, __D1 ULONG);
-
- // private functions
-
- Prototype void PrepareHash(void);
- Prototype struct SyntaxChunk *ParseString(UBYTE *, UWORD, ULONG);
- Prototype struct SyntaxInfo *DupInfo(struct SyntaxInfo *);
- Prototype struct Keyword *IsAReservedWord(UBYTE *, UWORD);
- Prototype struct Keyword *IsAReservedOperator(UBYTE *, UWORD);
- Prototype BOOL strnchr(UBYTE *, UWORD, UWORD);
- Prototype BOOL CheckEnvironment(UBYTE *, UWORD, UWORD, struct Keyword *);
- Prototype struct LineNode *FindContextStart(ULONG, struct LineNode *);
- Prototype void ParseContext(struct LineNode *, ULONG, ULONG);
- Prototype struct SyntaxInfo *ParseContextLine(UBYTE *, UWORD, ULONG, ULONG);
- Prototype BOOL BadSuccessor(ULONG, ULONG);
- Prototype struct RefreshRequest *VerifyContext(ULONG, ULONG, ULONG);
- Prototype BOOL ReadKeywords(void);
-
- #define AllocVec AllocVecPooled
- #define FreeVec FreeVecPooled
-
- ///
- /// "Library functions"
-
- /* ------------------------------- MountScanner --------------------------------
-
- Called by the editor before first usage of a scanner. Return a description of
- our abilities.
-
- */
-
- LibCall struct ParserData *
- MountScanner()
- {
- static UBYTE version[] = "$VER: WarpC++ 2.5 (" __COMMODORE_DATE__ ")";
-
- static struct ParserData parserData;
-
- // syntax elements understood by parser
-
- static UBYTE *levelNames[] = {
-
- "Standard text",
- "Comment ANSI",
- "Comment C++",
- "Reserved words",
- "Preprocessor",
- "Numbers",
- "Quotes",
- "Strings",
- "Types",
- "Double operators",
- "{}",
- "->",
- NULL
- };
-
- static UBYTE *example[] = {
-
- "/* Simple ANSI C example */ ",
- " ",
- "#include <defs.h> ",
- " ",
- "int ",
- "main(int argc, char **argv) ",
- "{ ",
- " if (ExecBase->SoftVer >= 39) { ",
- " ",
- " // any arguments available ?",
- " ",
- " if (argc-- > 1) { ",
- " ",
- " puts(\"hello world\"); ",
- " putchar('\\a'); ",
- " } ",
- " } ",
- "} ",
-
- NULL
- };
-
- // color suggestions
-
- static ULONG levelColors[] = {
-
- MAKE_RGB4( 0, 0, 0), // black
- MAKE_RGB4( 5, 7, 9), // blue
- MAKE_RGB4( 8, 8, 8), // grey
- MAKE_RGB4(15, 15, 15), // white
- MAKE_RGB4(15, 15, 9), // yellow
- MAKE_RGB4( 0, 0, 0), // black
- MAKE_RGB4( 0, 0, 0), // black
- MAKE_RGB4( 0, 0, 0), // black
- MAKE_RGB4( 0, 0, 0), // black
- MAKE_RGB4( 0, 0, 0), // black
- MAKE_RGB4(15, 15, 15), // white
- MAKE_RGB4( 0, 0, 0), // black
- };
-
- parserData.pd_Release = SCANLIBVERSION;
- parserData.pd_Version = 1;
- parserData.pd_Serial = 0;
- parserData.pd_Info = "WarpC++ 2.5";
- parserData.pd_Levels = SYNTAX_MAXIMUM;
- parserData.pd_Names = levelNames;
- parserData.pd_Colors = levelColors;
- parserData.pd_Flags = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
- parserData.pd_Example = example;
-
- PrepareHash();
-
- return(&parserData);
- }
-
-
- /* ------------------------------- StartScanner --------------------------------
-
- Called by the editor after a new text buffer has been created. We allocate a
- buffer to hold text-specific data. The buffer address is returned as handle.
-
- */
-
- LibCall ULONG
- StartScanner(__A0 struct GlobalConfig *globalConfigPtr, __A1 struct EditConfig *editConfigPtr, __D0 struct SyntaxChunk *syntaxStack)
- {
- struct BufferHandle *handle;
-
- if (handle = AllocVec(sizeof(struct BufferHandle), MEMF_PUBLIC | MEMF_CLEAR)) {
-
- handle->bh_GlobalConfig = globalConfigPtr;
- handle->bh_EditConfig = editConfigPtr;
- handle->bh_SyntaxInfo = (struct SyntaxInfo *)syntaxStack;
- }
-
- return((ULONG)handle);
- }
-
-
- /* ------------------------------- CloseScanner --------------------------------
-
- Called by the editor if a text buffer is about to be closed. Deallocate buffer
- specific 'global' data.
-
- */
-
- LibCall ULONG
- CloseScanner(__D0 ULONG scanID)
- {
- if (scanID) {
-
- struct BufferHandle *handle = (struct BufferHandle *)scanID;
-
- FreeVec(handle);
- }
-
- return(0);
- }
-
-
- /* ------------------------------- FlushScanner --------------------------------
-
- Called by the editor in low memory situations: we are supposed to free as much
- memory as possible.
-
- */
-
- LibCall void
- FlushScanner(__D0 ULONG scanID)
- {
- struct BufferHandle *handle;
- struct EditConfig *config;
- struct LineNode *lineNode;
-
- handle = (struct BufferHandle *)scanID;
- config = handle->bh_EditConfig;
-
- if (lineNode = config->TextNodes)
- UnparseLines(lineNode, config->Lines);
- }
-
-
- /* ------------------------------- SetupScanner --------------------------------
-
- Called by the editor if the user wants to change the scanner's configuration.
- We do not support user configuration (parserData.pd_Flags: SCPRF_CONFIGWIN flag
- unset).
-
- */
-
- LibCall void
- SetupScanner(__A0 globalConfigPtr)
- {
- return();
- }
-
-
- /* ------------------------------- BriefScanner --------------------------------
-
- Called to notify a context scanner if lines have been added, deleted or
- modified. We are supposed to return an additional display request or NULL
- (the editor will refresh the damaged region only if we return NULL). Damaged
- lines have already been unparsed by the editor. This is what we have to do:
-
- a) lines have been deleted or modified
-
- Check whether the syntax scheme of the remaining lines is still valid (it
- will be invalid if say a comment start has been deleted but the comment
- end has not been deleted). Reparse the lines and return a refresh request
- if not.
-
- b) lines have been inserted
-
- Check whether the syntax sheme of the new lines affects the existing lines.
- Reparse all lines starting at the insertion point and return a refresh
- request if not.
-
- c) lines have been (un)folded
-
- This scanner assumes that folding doesn't affect syntax highlighting for the
- sake of simplicity. This isn't necessarily a valid assumption: we will run
- into touble if the user folds parts of a comment only (e.g. the first line).
-
- */
-
- LibCall struct RefreshRequest *
- BriefScanner(__D0 ULONG scanID, __A0 struct ScannerNotify *notify)
- {
- struct EditConfig *config = ((struct BufferHandle *)scanID)->bh_EditConfig;
-
- switch (notify->sn_Class) {
-
- case SCANNER_NOTIFY_MODIFIED:
-
- ULONG line = notify->sn_Line;
-
- if (notify->sn_Removed > notify->sn_Lines) {
-
- // lines have been deleted
-
- return(VerifyContext(scanID, line, 0));
- }
- else if (notify->sn_Lines)
-
- // lines have been modified or inserted
-
- return(VerifyContext(scanID, line, notify->sn_Lines));
- else
- return(NULL);
-
- break;
-
- default:
-
- return(NULL);
- }
- }
-
-
- /* --------------------------------- ParseLine ---------------------------------
-
- Parse a line, build a syntax description
-
- */
-
- LibCall struct SyntaxChunk *
- ParseLine(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG line)
- {
- if (IS_FOLD(lineNode))
-
- return(NULL);
-
- else if (lineNode->Len) {
-
- // line parsed already ?
-
- if (lineNode->UserData == EMPTY_STACK)
-
- return(NULL);
-
- else if (lineNode->UserData)
-
- return(GET_CHUNK(lineNode));
-
- else {
-
- // not yet parsed ? preparse a couple of lines
-
- ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line;
-
- if (lines > 50)
- lines = 50;
-
- ParseSection(scanID, lineNode, lines);
-
- if (lineNode->UserData == EMPTY_STACK)
- return(NULL);
- else
- return(GET_CHUNK(lineNode));
- }
- }
- else
- return(NULL);
- }
-
-
- /* -------------------------------- UnparseLines -------------------------------
-
- Called by the editor if lines are to be deleted. We are supposed to free
- private data attached to the lines.
-
- */
-
- LibCall void
- UnparseLines(__A0 struct LineNode *lineNode, __D0 ULONG lines)
- {
- while (lines--) {
-
- // free syntax cache
-
- if (lineNode->UserData) {
-
- if (lineNode->UserData != (APTR)EMPTY_STACK)
- FreeVec(lineNode->UserData);
-
- lineNode->UserData = NULL;
- }
-
- // free folded subblock
-
- if (IS_FOLD(lineNode)) {
-
- struct Fold *fold = (struct Fold *)lineNode->SpecialInfo;
-
- UnparseLines(fold->TextNodes, fold->Lines);
- }
-
- ++lineNode;
- }
- }
-
- /* -------------------------------- ParseSection -------------------------------
-
- Called by the editor if lines are to be displayed. The scanner is encouraged to
- preparse the lines.
-
- */
-
- LibCall void
- ParseSection(__D0 ULONG scanID, __A0 struct LineNode *lineNode, __D1 ULONG lines)
- {
- struct LineNode *firstNode;
-
- if (firstNode = FindContextStart(scanID, lineNode))
- ParseContext(firstNode, lines + (lineNode - firstNode), scanID);
- }
-
- ///
- /// "Private"
-
- /* -------------------------------- PrepareHash --------------------------------
-
- Prepare reserved keyword hashtable (used to find keyword description if ascii
- value of first letter is known) and prepare IsOperator array.
-
- */
-
- void
- PrepareHash()
- {
- struct Keyword *keyword;
- UWORD ascii;
-
- memset(Hash, 0, sizeof(Hash));
-
- keyword = Reserved;
-
- while (keyword->Pattern) {
-
- UWORD ascii = *keyword->Pattern;
-
- Hash[ascii] = keyword;
-
- while (keyword->Pattern && (*keyword->Pattern == ascii))
- ++keyword;
- }
-
- memset(IsOperator, FALSE, sizeof(IsOperator));
-
- IsOperator['*'] = TRUE;
- IsOperator['='] = TRUE;
- IsOperator['<'] = TRUE;
- IsOperator['>'] = TRUE;
- IsOperator['!'] = TRUE;
- IsOperator['+'] = TRUE;
- IsOperator['-'] = TRUE;
- IsOperator['&'] = TRUE;
- IsOperator['/'] = TRUE;
- IsOperator['|'] = TRUE;
- IsOperator[':'] = TRUE;
-
- memset(IsLetter, FALSE, sizeof(IsLetter));
- memset(IsSpace, FALSE, sizeof(IsLetter));
- memset(IsKnown, FALSE, sizeof(IsLetter));
-
- for (ascii = 0; ascii <= 255; ++ascii) {
-
- IsLetter[ascii] = ((ascii >= 'A') && (ascii <= 'Z')) || ((ascii >= 'a') && (ascii <= 'z'));
- IsSpace [ascii] = ((ascii >= ' ') && (ascii <= '/')) || ((ascii >= ':') && (ascii <= '?')) || ((ascii >= '[') && (ascii <= '^')) || ((ascii >= '{') && (ascii <= '~'));
- IsDigit [ascii] = ((ascii >= '0') && (ascii <= '9'));
- }
-
- // only expressions starting with a 'known' character are parsed at all
-
- for (ascii = 0; ascii <= 255; ++ascii)
- IsKnown[ascii] = IsOperator[ascii] || IsDigit[ascii] || Hash[ascii] || (ascii == 39) || (ascii == 34) || (ascii == '/') || (ascii == '{') || (ascii == '}') || (ascii == '#');
- }
-
-
- /* ------------------------------ IsAReservedWord ------------------------------
-
- Decide whether word of length <len> is a reserved ANSI C keyword
-
- */
-
- struct Keyword *
- IsAReservedWord(text, len)
-
- UBYTE *text;
- UWORD len;
- {
- struct Keyword *keyword;
-
- if (keyword = Hash[*text]) {
-
- while (keyword->Pattern) {
-
- if (*keyword->Pattern > *text)
-
- break;
-
- else if (keyword->Pattern[len] || (keyword->Pattern[1] != text[1]) || memcmp(text, keyword->Pattern, len))
-
- ++keyword;
- else
- return(keyword);
- }
-
- return(NULL);
- }
- else
- return(NULL);
- }
-
-
- /* ------------------------------ IsAReservedOperator --------------------------
-
- Decide whether word of length <len> is a reserved C 'double' operator
-
- */
-
- struct Keyword *
- IsAReservedOperator(text, len)
-
- UBYTE *text;
- UWORD len;
- {
- static struct Keyword reserved[] = {
-
- // known word next char on same line
-
- { "!=", NULL, NULL, SYNTAX_OPERATOR },
- { "&&", NULL, NULL, SYNTAX_OPERATOR },
- { "&=", NULL, NULL, SYNTAX_OPERATOR },
- { "*=", NULL, NULL, SYNTAX_OPERATOR },
- { "++", NULL, NULL, SYNTAX_OPERATOR },
- { "+=", NULL, NULL, SYNTAX_OPERATOR },
- { "--", NULL, NULL, SYNTAX_OPERATOR },
- { "-=", NULL, NULL, SYNTAX_OPERATOR },
- { "->", NULL, NULL, SYNTAX_POINTER },
- { "/=", NULL, NULL, SYNTAX_OPERATOR },
- { "::", NULL, NULL, SYNTAX_OPERATOR },
- { "<<", NULL, NULL, SYNTAX_OPERATOR },
- { "<=", NULL, NULL, SYNTAX_OPERATOR },
- { "==", NULL, NULL, SYNTAX_OPERATOR },
- { ">=", NULL, NULL, SYNTAX_OPERATOR },
- { ">>", NULL, NULL, SYNTAX_OPERATOR },
- { "|=", NULL, NULL, SYNTAX_OPERATOR },
- { "||", NULL, NULL, SYNTAX_OPERATOR },
- { NULL, NULL, NULL, 0}
- };
-
- struct Keyword *keyword;
-
- for (keyword = reserved; keyword->Pattern; ++keyword) {
-
- if (*keyword->Pattern > *text)
-
- break;
-
- else if ((*keyword->Pattern == *text) && (keyword->Pattern[1] == text[1]))
-
- return(keyword);
- }
-
- return(NULL);
- }
-
-
-
- /* ----------------------------- CheckEnvironment ------------------------------
-
- Check whether keyword environment complies with keyword.
-
- */
-
- BOOL
- CheckEnvironment(text, len, wordLen, keyword)
-
- struct Keyword *keyword;
- UBYTE *text;
- UWORD len, wordLen;
- {
- // move to first non-space character after recognized keyword
-
- for (text += wordLen, len -= wordLen; len && (*text == ' '); ++wordLen, --len)
- ++text;
-
- // check whether first non-space character is valid
-
- if ((keyword->Next == NULL) || strchr(keyword->Next, *text)) {
-
- // check whether required character is used on the same line
-
- if ((keyword->Required == NULL) || strnchr(text, len, *keyword->Required)) {
-
- return(TRUE);
- }
- else
- return(FALSE);
- }
- else
- return(FALSE);
- }
-
-
- /* ---------------------------------- strnchr ----------------------------------
-
- strchr replacement (doesn't require 0-terminated string). Return TRUE if the
- character is found within <text>.
-
- */
-
- BOOL
- strnchr(text, len, character)
-
- UBYTE *text;
- UWORD character, len;
- {
- while (len--)
- if (*text++ == character)
- return(TRUE);
-
- return(FALSE);
- }
-
-
- /* --------------------------------- DupInfo -----------------------------------
-
- Duplicate syntax info (to be FreeVec'ed)
-
- */
-
- struct SyntaxInfo *
- DupInfo(syntaxInfo)
-
- struct SyntaxInfo *syntaxInfo;
- {
- struct SyntaxChunk *chunk;
- struct SyntaxInfo *info;
- ULONG size;
- UWORD elements;
-
- // determine stack size
-
- for (elements = 0, chunk = &syntaxInfo->SyntaxChunk; chunk->sc_Level; ++chunk)
- ++elements;
-
- // create copy of syntax stack (to be attached to a text line by the caller)
-
- size = (++elements) * sizeof(struct SyntaxChunk) + sizeof(UWORD);
-
- if (info = AllocVec(size + sizeof(UWORD), MEMF_PUBLIC)) {
-
- movmem(syntaxInfo, info, size);
-
- return(info);
- }
- else
- return(NULL);
- }
-
-
- /* ----------------------------- FindContextStart ------------------------------
-
- Search backwards until a parsed line or a context free line (fold or start of
- file) is found. Return line node. This node may be used as starting point for
- parsing lines.
-
- */
-
- struct LineNode *
- FindContextStart(scanID, lineNode)
-
- ULONG scanID;
- struct LineNode *lineNode;
- {
- ULONG line = lineNode - ((struct BufferHandle *)scanID)->bh_EditConfig-> TextNodes;
-
- while (line--) {
-
- // found a fold or a parsed line ?
-
- if (lineNode->UserData || IS_FOLD(lineNode))
- break;
-
- --lineNode;
- }
-
- return(lineNode);
- }
-
-
- /* ------------------------------- ParseContext --------------------------------
-
- Preparse lines. The first line is expected to be either a context free line
- (start of file or a fold header) or to be a parsed line.
-
- */
-
- void
- ParseContext(lineNode, lines, scanID)
-
- struct LineNode *lineNode;
- ULONG lines, scanID;
- {
- ULONG mode;
-
- for (mode = COMMENT_NONE; lines--; ++lineNode) {
-
- if (IS_FOLD(lineNode))
-
- mode = COMMENT_NONE;
-
- else {
-
- if (lineNode->UserData == EMPTY_STACK)
-
- mode = COMMENT_NONE;
-
- else if (lineNode->UserData)
-
- mode = GET_FLAGS(lineNode);
-
- else {
-
- struct SyntaxInfo *syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, mode, scanID);
-
- if (syntaxInfo == EMPTY_STACK) {
-
- lineNode->UserData = EMPTY_STACK;
-
- mode = COMMENT_NONE;
- }
- else if (syntaxInfo) {
-
- lineNode->UserData = DupInfo(syntaxInfo);
-
- mode = syntaxInfo->Flags;
- }
- else
- mode = COMMENT_NONE;
- }
- }
- }
- }
-
-
- /* ----------------------------- ParseContextLine ------------------------------
-
- Parse a string, build a syntax description. Return EMPTY_STACK in case there is
- nothing to highlight. Check whether there are comments. The status of the last
- line is passed in by the caller, this function will return the status (flag
- bits) of the new line.
-
- */
-
- struct SyntaxInfo *
- ParseContextLine(text, len, last, scanID)
-
- UBYTE *text;
- UWORD len;
- ULONG last, scanID;
- {
- if (len) {
-
- struct SyntaxInfo *syntaxInfo;
- struct SyntaxChunk *syntaxStack;
- struct Keyword *keyword;
-
- BOOL inString, inComment, anyText, innerComment, inQuote;
- UBYTE *next;
- UWORD indent, startString, startComment, element, lastChar, wordLen, startQuote;
- ULONG status;
-
- syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
-
- // leading spaces have to be ignored
-
- for (indent = 0; len && (*text == ' '); ++indent, --len)
- ++text;
-
- // trailing spaces have to be ignored
-
- while (len && (text[len - 1] == ' '))
- --len;
-
- // preset comment status based on status of previous line
-
- if (last & (COMMENT_START | COMMENT_BODY)) {
-
- status = COMMENT_BODY;
- inComment = TRUE;
- }
- else {
-
- status = COMMENT_NONE;
- inComment = FALSE;
- }
-
- startComment = 0;
- innerComment = FALSE;
-
- inString = FALSE;
- inQuote = FALSE;
- anyText = FALSE;
-
- lastChar = ' ';
-
- syntaxStack = &syntaxInfo->SyntaxChunk;
-
- for (element = 0; len; ++text, ++indent, --len) {
-
- if (IsKnown[*text]) {
-
- if (inComment) {
-
- // end of comment detected ?
-
- if ((*text == '*') && (text[1] == '/') && (len > 1)) {
-
- // end of a muli-line comment or end of inner line comment ?
-
- if (innerComment)
-
- status |= COMMENT_SIMPLE;
-
- else {
-
- status |= COMMENT_END;
- status &= ~COMMENT_BODY;
- }
-
- syntaxStack[element].sc_Level = SYNTAX_COMMENTANSI;
- syntaxStack[element].sc_Start = startComment;
- syntaxStack[element].sc_End = indent + 1;
-
- ++element;
-
- inComment = FALSE;
-
- ++text;
- ++indent;
- --len;
- }
- }
- else if ((*text == '\'') && (inString == FALSE) && (*(text - 1) != '\\')) {
-
- if (inQuote = !inQuote)
-
- startQuote = indent + 1;
-
- else if (indent > startQuote) {
-
- // end of string detected
-
- syntaxStack[element].sc_Level = SYNTAX_QUOTE;
- syntaxStack[element].sc_Start = startQuote;
- syntaxStack[element].sc_End = indent - 1;
-
- ++element;
- }
- }
- else if ((*text == '\"') && (inQuote == FALSE) && (*(text - 1) != '\\')) {
-
- if (inString = !inString)
-
- startString = indent + 1;
-
- else if (indent > startString) {
-
- // end of string detected
-
- syntaxStack[element].sc_Level = SYNTAX_STRING;
- syntaxStack[element].sc_Start = startString;
- syntaxStack[element].sc_End = indent - 1;
-
- ++element;
- }
- }
- else if ((inString == FALSE) && (inQuote == FALSE)) {
-
- if (IsLetter[*text]) {
-
- wordLen = 1;
-
- while (IsLetter[text[wordLen]] && (wordLen < len))
- ++wordLen;
-
- // reserved word detected ?
-
- if (Hash[*text]) {
-
- if (IsSpace[lastChar]) {
-
- if (keyword = IsAReservedWord(text, wordLen)) {
-
- // environment to be checked ?
-
- if (keyword->Next || keyword->Required) {
-
- if (CheckEnvironment(text, len, wordLen, keyword)) {
-
- syntaxStack[element].sc_Level = keyword->Level;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + wordLen - 1;
-
- ++element;
- }
- }
- else {
-
- syntaxStack[element].sc_Level = keyword->Level;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + wordLen - 1;
-
- ++element;
- }
- }
- }
- }
-
- // move to next section (consider end-of-loop action)
-
- --wordLen;
-
- text += wordLen;
- indent += wordLen;
- len -= wordLen;
- }
- else if ((*text >= '0') && (*text <= '9')) {
-
- // number detected
-
- BOOL isHex = (*text == '0') && (text[1] == 'x');
-
- // allows "0x12123UL"
-
- for (next = text + (wordLen = 1); ((UPPER(*next) == 'U' || UPPER(*next) == 'L') || ((*next <= '9') && (*next >= '0')) || (isHex && ((*next == 'x') || ((UPPER(*next) >= 'A') && (UPPER(*next) <= 'F'))))) && (wordLen < len); ++next)
- ++wordLen;
-
- // ignore numbers attached to letters (e.g. __A0)
-
- if (IsLetter[lastChar] == FALSE) {
-
- syntaxStack[element].sc_Level = SYNTAX_NUMBER;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + wordLen - 1;
-
- ++element;
- }
-
- // move to next section (consider end-of-loop action)
-
- --wordLen;
-
- text += wordLen;
- indent += wordLen;
- len -= wordLen;
- }
- else if ((*text == '/') && (text[1] == '/') && (len > 1)) {
-
- // c++ comment detected
-
- syntaxStack[element].sc_Level = SYNTAX_COMMENTCPP;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + len - 1;
-
- ++element;
-
- // terminate parsing (rest of line is a comment)
-
- break;
- }
- else if ((*text == '/') && (text[1] == '*') && (len > 1)) {
-
- // start of comment detected
-
- inComment = TRUE;
- innerComment = TRUE;
- startComment = indent;
-
- ++text;
- ++indent;
- --len;
- }
- else if (IsOperator[*text]) {
-
- wordLen = 1;
-
- while (IsOperator[text[wordLen]] && (wordLen < len))
- ++wordLen;
-
- // trim sequence (handles special case ++=, *++ and *--)
-
- if (wordLen > 2) {
-
- wordLen = 2;
-
- if (*text == '*') {
-
- ++text;
- ++indent;
- --len;
- }
- }
-
- // reserved operator detected ?
-
- if (keyword = IsAReservedOperator(text, wordLen)) {
-
- syntaxStack[element].sc_Level = keyword->Level;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + wordLen - 1;
-
- ++element;
- }
-
- // move to next section (consider end-of-loop action)
-
- --wordLen;
-
- text += wordLen;
- indent += wordLen;
- len -= wordLen;
- }
- else if ((*text == '{') || (*text == '}')) {
-
- // bracket detected
-
- syntaxStack[element].sc_Level = SYNTAX_BRACKET;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent;
-
- ++element;
- }
- else if (*text == '#') {
-
- // only spaces detected so far ?
-
- if (anyText == FALSE) {
-
- // preprocessor line detected
-
- wordLen = 1;
-
- while ((wordLen < len) && (text[wordLen] == ' '))
- ++wordLen;
-
- // get preprocessor command only
-
- while ((wordLen < len) && (text[wordLen] != ' ')) {
-
- if ((text[wordLen] == '/') && (wordLen + 1 < len) && (text[wordLen + 1] == '/'))
- break;
- else
- ++wordLen;
- }
-
- syntaxStack[element].sc_Level = SYNTAX_PREPROCESSOR;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + wordLen - 1;
-
- // move to end of reserved word (consider end-of-loop action)
-
- --wordLen;
-
- text += wordLen;
- indent += wordLen;
- len -= wordLen;
-
- ++element;
- }
- }
- }
-
- anyText = TRUE;
- }
-
- lastChar = *text;
- }
-
- // comment not closed ?
-
- if (inComment) {
-
- // continuation of multi-line comment or new (inner line) comment ?
-
- if (innerComment)
- status |= COMMENT_START;
- else
- status |= COMMENT_BODY;
-
- syntaxStack[element].sc_Level = SYNTAX_COMMENTANSI;
- syntaxStack[element].sc_Start = startComment;
- syntaxStack[element].sc_End = indent - 1;
-
- ++element;
- }
-
- if (element) {
-
- // terminate syntax stack
-
- syntaxStack[element].sc_Start = FALSE;
- syntaxStack[element].sc_End = FALSE;
- syntaxStack[element].sc_Level = FALSE;
-
- syntaxInfo->Flags = status;
-
- return(syntaxInfo);
- }
- else
- return(EMPTY_STACK);
- }
- else if (last) {
-
- if (last & (COMMENT_START | COMMENT_BODY)) {
-
- struct SyntaxInfo *syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
-
- syntaxInfo->SyntaxChunk.sc_Start = FALSE;
- syntaxInfo->SyntaxChunk.sc_End = FALSE;
- syntaxInfo->SyntaxChunk.sc_Level = FALSE;
-
- syntaxInfo->Flags = COMMENT_BODY;
-
- return(syntaxInfo);
- }
- else
- return(EMPTY_STACK);
- }
- else
- return(EMPTY_STACK);
- }
-
-
- /* ------------------------------- VerifyContext -------------------------------
-
- Ensure that syntax information of a specified range of lines ("damage region")
- is valid and consistent with the existing text. Return a refresh request if
- not. The damage area is expected to have been unparsed already. This is what we
- have to do: we preparse existing lines before the damage area if belonging to
- the syntax context of the damage area (ie. all lines affecting highlighting of
- the first line in the damage area). The damage area is parsed, too. Parsed
- lines after the damage area are reparsed if highlighting is found to be
- inconsistent with the line(s) before. Reparsing continues until the end of the
- file respectively until no more inconsistencies are found.
-
- */
-
- struct RefreshRequest *
- VerifyContext(scanID, line, lines)
-
- ULONG scanID, line, lines;
- {
- struct EditConfig *config;
- struct LineNode *lineNode, *lastNode;
- struct SyntaxInfo *syntaxInfo;
-
- ULONG last, new, refreshStart, refresh = FALSE;
-
- config = ((struct BufferHandle *)scanID)->bh_EditConfig;
-
- lineNode = config->TextNodes + line;
- lastNode = config->TextNodes + line + (lines - 1);
-
- // preparse from context start until end of damage area
-
- ParseSection(scanID, lineNode, lines);
-
- // get syntax flags of last line in damage area
-
- if ((lastNode < config->TextNodes) || IS_FOLD(lastNode) || (lastNode->UserData == EMPTY_STACK) || (lastNode->UserData == NULL))
- last = COMMENT_NONE;
- else
- last = GET_FLAGS(lastNode);
-
- // continue parsing until no more inconsistencies are found (until context end)
-
- refreshStart = (line += lines);
-
- for (lineNode = lastNode + 1; line < config->Lines; ++line, ++lineNode, ++refresh) {
-
- if (IS_FOLD(lineNode))
-
- // folds terminate parsing (context end)
-
- break;
-
- else if (lineNode->UserData == NULL) {
-
- // line not yet parsed
-
- syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
-
- if (syntaxInfo == EMPTY_STACK)
-
- lineNode->UserData = EMPTY_STACK;
-
- else if (syntaxInfo)
-
- lineNode->UserData = DupInfo(syntaxInfo);
- }
- else {
-
- // check whether highlighting of this line is consistent with previous line
-
- new = (lineNode->UserData == EMPTY_STACK) ? COMMENT_NONE : GET_FLAGS(lineNode);
-
- if (BadSuccessor(last, new)) {
-
- if (lineNode->UserData != EMPTY_STACK)
- FreeVec(lineNode->UserData);
-
- syntaxInfo = ParseContextLine(lineNode->Text, lineNode->Len, last, scanID);
-
- if (syntaxInfo == EMPTY_STACK)
-
- lineNode->UserData = EMPTY_STACK;
-
- else if (syntaxInfo)
-
- lineNode->UserData = DupInfo(syntaxInfo);
- }
- else
- break;
- }
-
- if ((lineNode->UserData == EMPTY_STACK) || (lineNode->UserData == NULL) || IS_FOLD(lineNode))
- last = COMMENT_NONE;
- else
- last = GET_FLAGS(lineNode);
- }
-
- if (refresh) {
-
- struct RefreshRequest *refreshRequest = &((struct BufferHandle *)scanID)->bh_RefreshRequest;
-
- refreshRequest->rr_Line = refreshStart;
- refreshRequest->rr_Lines = refresh;
-
- return(refreshRequest);
- }
- else
- return(NULL);
- }
-
-
- /* ------------------------------- BadSuccessor --------------------------------
-
- Return TRUE if syntax information of two adjacent lines is inconsistent.
-
- */
-
- BOOL
- BadSuccessor(pred, succ)
-
- ULONG pred, succ;
- {
- if (succ & (COMMENT_BODY | COMMENT_END)) {
-
- // comment body/end without start ?
-
- if ((pred & (COMMENT_START | COMMENT_BODY)) == FALSE)
-
- return(TRUE);
- }
- else if (pred & COMMENT_START) {
-
- // comment start without body/end ?
-
- if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
-
- return(TRUE);
-
- // comment inside comment ?
-
- if (succ & COMMENT_START)
-
- return(TRUE);
- }
- else if (pred & COMMENT_BODY) {
-
- // body line without end ?
-
- if ((succ & (COMMENT_BODY | COMMENT_END)) == FALSE)
-
- return(TRUE);
- }
-
- return(FALSE);
- }
-
- ///
-