home *** CD-ROM | disk | FTP | other *** search
- /* -----------------------------------------------------------------------------
-
- Modula scanner ©1995 Dietmar Eilert
-
- 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 keywords are described by syntax structures:
-
- struct Keyword {
-
- UBYTE *Pattern; // known pattern (e.g. "PROCEDURE")
- 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
-
- // recognized syntax levels
-
- #define SYNTAX_COMMENT 1
- #define SYNTAX_MODULE 2
- #define SYNTAX_RESERVED 3
- #define SYNTAX_DECLARE 4
- #define SYNTAX_TYPES 5
- #define SYNTAX_NUMBER 6
- #define SYNTAX_STRING 7
- #define SYNTAX_ASSIGN 8
- #define SYNTAX_OPERATOR 9
-
- #define UPPER(a) ((a) & 95) // simple uppercase conversion
-
- // syntax infos (or EMPTY_STACK) are attached to parsed lines
-
- struct SyntaxInfo {
-
- UWORD Flags; // comment flags (upper 8 bits = comment depth)
- 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)
-
- // macro to access comment depths (stored within upper eight bits of the flags)
-
- #define COMMENT_DEPTH(flags) ((WORD)(flags>>8))
-
- ///
- /// "Globals"
-
- struct Keyword *Hash [256];
- BOOL IsAlNum[256];
- BOOL IsDigit[256];
-
- struct Keyword Reserved[] = {
-
- // known_word next_char on_same_line level
-
- { "ABS", NULL, NULL, SYNTAX_OPERATOR },
- { "AND", NULL, NULL, SYNTAX_RESERVED },
- { "ARRAY", NULL, NULL, SYNTAX_TYPES },
- { "ASH", NULL, NULL, SYNTAX_OPERATOR },
- { "ASSERT", NULL, NULL, SYNTAX_OPERATOR },
- { "BEGIN", NULL, NULL, SYNTAX_RESERVED },
- { "BITSET", NULL, NULL, SYNTAX_TYPES },
- { "BOOLEAN", NULL, NULL, SYNTAX_TYPES },
- { "BPOINTER", NULL, NULL, SYNTAX_TYPES },
- { "BY", NULL, NULL, SYNTAX_RESERVED },
- { "CAP", NULL, NULL, SYNTAX_OPERATOR },
- { "CARDINAL", NULL, NULL, SYNTAX_TYPES },
- { "CASE", NULL, NULL, SYNTAX_RESERVED },
- { "CHAR", NULL, NULL, SYNTAX_TYPES },
- { "CHR", NULL, NULL, SYNTAX_OPERATOR },
- { "CLOSE", NULL, NULL, SYNTAX_RESERVED },
- { "CONST", NULL, NULL, SYNTAX_DECLARE },
- { "COPY", NULL, NULL, SYNTAX_OPERATOR },
- { "CPOINTER", NULL, NULL, SYNTAX_TYPES },
- { "DEC", NULL, NULL, SYNTAX_OPERATOR },
- { "DIV", NULL, NULL, SYNTAX_OPERATOR },
- { "DO", NULL, NULL, SYNTAX_RESERVED },
- { "ELSE", NULL, NULL, SYNTAX_RESERVED },
- { "ELSIF", NULL, NULL, SYNTAX_RESERVED },
- { "END", NULL, NULL, SYNTAX_RESERVED },
- { "ENTIER", NULL, NULL, SYNTAX_OPERATOR },
- { "EXCL", NULL, NULL, SYNTAX_OPERATOR },
- { "EXIT", NULL, NULL, SYNTAX_RESERVED },
- { "FALSE", NULL, NULL, SYNTAX_RESERVED },
- { "FLOAT", NULL, NULL, SYNTAX_RESERVED },
- { "FOR", NULL, NULL, SYNTAX_RESERVED },
- { "FROM", NULL, NULL, SYNTAX_MODULE },
- { "HALT", NULL, NULL, SYNTAX_OPERATOR },
- { "HIGH", NULL, NULL, SYNTAX_RESERVED },
- { "IF", NULL, NULL, SYNTAX_RESERVED },
- { "IMPORT", NULL, NULL, SYNTAX_MODULE },
- { "IN", NULL, NULL, SYNTAX_RESERVED },
- { "INC", NULL, NULL, SYNTAX_OPERATOR },
- { "INCL", NULL, NULL, SYNTAX_OPERATOR },
- { "INTEGER", NULL, NULL, SYNTAX_TYPES },
- { "IS", NULL, NULL, SYNTAX_RESERVED },
- { "LEN", NULL, NULL, SYNTAX_OPERATOR },
- { "LIBCALL", NULL, NULL, SYNTAX_RESERVED },
- { "LONG", NULL, NULL, SYNTAX_RESERVED },
- { "LONGCARD", NULL, NULL, SYNTAX_TYPES },
- { "LONGINT", NULL, NULL, SYNTAX_TYPES },
- { "LONGREAL", NULL, NULL, SYNTAX_TYPES },
- { "LONGSET", NULL, NULL, SYNTAX_TYPES },
- { "LOOP", NULL, NULL, SYNTAX_RESERVED },
- { "MAX", NULL, NULL, SYNTAX_OPERATOR },
- { "MIN", NULL, NULL, SYNTAX_OPERATOR },
- { "MOD", NULL, NULL, SYNTAX_OPERATOR },
- { "MODULE", NULL, NULL, SYNTAX_MODULE },
- { "NEW", NULL, NULL, SYNTAX_OPERATOR },
- { "NIL", NULL, NULL, SYNTAX_RESERVED },
- { "NOT", NULL, NULL, SYNTAX_RESERVED },
- { "ODD", NULL, NULL, SYNTAX_OPERATOR },
- { "OF", NULL, NULL, SYNTAX_RESERVED },
- { "OR", NULL, NULL, SYNTAX_RESERVED },
- { "ORD", NULL, NULL, SYNTAX_OPERATOR },
- { "POINTER", NULL, NULL, SYNTAX_TYPES },
- { "PROCEDURE", NULL, NULL, SYNTAX_RESERVED },
- { "REAL", NULL, NULL, SYNTAX_TYPES },
- { "RECORD", NULL, NULL, SYNTAX_TYPES },
- { "REPEAT", NULL, NULL, SYNTAX_RESERVED },
- { "RETURN", NULL, NULL, SYNTAX_RESERVED },
- { "SET", NULL, NULL, SYNTAX_RESERVED },
- { "SHORT", NULL, NULL, SYNTAX_RESERVED },
- { "SHORTINT", NULL, NULL, SYNTAX_TYPES },
- { "SIZE", NULL, NULL, SYNTAX_OPERATOR },
- { "STRUCT", NULL, NULL, SYNTAX_RESERVED },
- { "THEN", NULL, NULL, SYNTAX_RESERVED },
- { "TO", NULL, NULL, SYNTAX_TYPES },
- { "TRUE", NULL, NULL, SYNTAX_RESERVED },
- { "TRUNC", NULL, NULL, SYNTAX_RESERVED },
- { "TYPE", NULL, NULL, SYNTAX_DECLARE },
- { "UNTIL", NULL, NULL, SYNTAX_RESERVED },
- { "UNTRACED", NULL, NULL SYNTAX_RESERVED },
- { "VAL", NULL, NULL, SYNTAX_RESERVED },
- { "VAR", NULL, NULL, SYNTAX_DECLARE },
- { "WHILE", NULL, NULL, SYNTAX_RESERVED },
- { "WITH", NULL, 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 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(UWORD, struct LineNode *);
- Prototype struct RefreshRequest *VerifyContext(ULONG, ULONG, ULONG);
- Prototype WORD CommentDepth(UBYTE *, UWORD, ULONG);
- Prototype BOOL IsNumber(UBYTE *, UWORD);
-
- ///
- /// "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: WarpModula 1.3 (" __COMMODORE_DATE__ ")";
-
- static struct ParserData parserData;
-
- // syntax elements understood by parser
-
- static UBYTE *levelNames[] = {
-
- "Standard text",
- "Comment",
- "Module level",
- "Reserved words",
- "Header sections",
- "Types",
- "Numbers",
- "Strings",
- "Assignments",
- "Operators",
-
- 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
- };
-
- parserData.pd_Release = SCANLIBVERSION;
- parserData.pd_Version = 1;
- parserData.pd_Serial = 0;
- parserData.pd_Info = "Warp MODULA 1.3 ©'95 D.Eilert";
- parserData.pd_Levels = 10;
- parserData.pd_Names = levelNames;
- parserData.pd_Colors = levelColors;
- parserData.pd_Flags = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
- parserData.pd_Reserved = 0;
-
- 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 (lineNode->Fold)
-
- return(NULL);
-
- else if (lineNode->Len) {
-
- // not yet preparsed ? preparse a couple of lines
-
- if (lineNode->UserData == EMPTY_STACK)
-
- return(NULL);
-
- else if (lineNode->UserData)
-
- return(GET_CHUNK(lineNode));
-
- else {
-
- ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line + 1;
-
- 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 (lineNode->Fold)
- UnparseLines(lineNode->Fold->TextNodes, lineNode->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).
-
- */
-
- 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(IsAlNum, FALSE, sizeof(IsAlNum));
- memset(IsDigit, FALSE, sizeof(IsDigit));
-
- for (ascii = 0; ascii <= 255; ++ascii)
- IsAlNum[ascii] = ((ascii >= 'A') && (ascii <= 'Z')) || ((ascii >= 'a') && (ascii <= 'z')) || ((ascii >= '0') && (ascii <= '9'));
-
- for (ascii = '0'; ascii <= '9'; ++ascii)
- IsDigit[ascii] = TRUE;
- }
-
-
- /* ------------------------------ IsAReservedWord ------------------------------
-
- Decide whether word of length <len> is a reserved 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);
- }
-
- /* ----------------------------- 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 == 32); ++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->Fold || lineNode->UserData)
- 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 (lineNode->Fold)
-
- 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.
-
- */
-
- 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 inComment, anyText, innerComment;
- UWORD indent, startString, startComment, element, lastChar, wordLen, inString;
- ULONG status;
- WORD depth;
-
- syntaxInfo = ((struct BufferHandle *)scanID)->bh_SyntaxInfo;
-
- // leading spaces have to be ignored
-
- for (indent = 0; len && (*text == 32); ++indent, --len)
- ++text;
-
- // trailing spaces have to be ignored
-
- while (len && (text[len - 1] == 32))
- --len;
-
- // preset comment status based on status of previous line
-
- if (last & (COMMENT_START | COMMENT_BODY)) {
-
- status = COMMENT_BODY;
- depth = COMMENT_DEPTH(last);
- inComment = TRUE;
- }
- else {
-
- status = COMMENT_NONE;
- inComment = FALSE;
- depth = 0;
- }
-
- startComment = 0;
- innerComment = FALSE;
-
- inString = FALSE;
- anyText = FALSE;
- lastChar = 32;
-
- syntaxStack = &syntaxInfo->SyntaxChunk;
-
- for (element = 0; len; ++text, ++indent, --len) {
-
- if (*text != 32) {
-
- if (inComment) {
-
- // end of comment detected ?
-
- if ((*text == '*') && (text[1] == ')') && (len > 1)) {
-
- --depth;
-
- // end of a muli-line comment or end of inner line comment ?
-
- if (depth == 0) {
-
- if (innerComment)
-
- status |= COMMENT_SIMPLE;
-
- else {
-
- status |= COMMENT_END;
- status &= ~COMMENT_BODY;
- }
-
- syntaxStack[element].sc_Level = SYNTAX_COMMENT;
- syntaxStack[element].sc_Start = startComment;
- syntaxStack[element].sc_End = indent + 1;
-
- ++element;
-
- inComment = FALSE;
- }
-
- ++text;
- ++indent;
- --len;
- }
- else if ((*text == '(') && (text[1] == '*') && (len > 1)) {
-
- // nested comment detected
-
- ++depth;
- ++text;
- ++indent;
- --len;
- }
- }
- else if ((*text == 34) || (*text == 39)) {
-
- if (inString) {
-
- if ((*text == inString) && (*(text - 1) != 92)) {
-
- // end of string detected
-
- inString = FALSE;
-
- syntaxStack[element].sc_Level = SYNTAX_STRING;
- syntaxStack[element].sc_Start = startString;
- syntaxStack[element].sc_End = indent - 1;
-
- ++element;
- }
- }
- else {
-
- inString = *text;
- startString = indent + 1;
- }
- }
- else if (inString == FALSE) {
-
- if ((*text == ':') && (text[1] == '=') && (len > 1)) {
-
- // assignment detected
-
- syntaxStack[element].sc_Level = SYNTAX_ASSIGN;
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + 1;
-
- ++element;
-
- // move to next section (consider end-of-loop action)
-
- ++text;
- ++indent;
- --len;
- }
- else if ((*text == '(') && (text[1] == '*') && (len > 1)) {
-
- // start of comment detected
-
- inComment = TRUE;
- innerComment = TRUE;
- startComment = indent;
-
- ++depth;
- ++text;
- ++indent;
- --len;
- }
- else if (IsAlNum[*text]) {
-
- wordLen = 1;
-
- while (IsAlNum[text[wordLen]] && (wordLen < len))
- ++wordLen;
-
- // reserved word detected ?
-
- if (Hash[*text] && ((lastChar == ' ') || (lastChar == '(') || (lastChar == ':')) && (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;
- }
- }
- else if (IsDigit[*text] && IsNumber(text, wordLen)) {
-
- 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;
- }
- }
-
- 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_COMMENT;
- 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 | (depth<<8);
-
- 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 | (last & 0xff00);
-
- return(syntaxInfo);
- }
- else
- return(EMPTY_STACK);
- }
- else
- return(EMPTY_STACK);
- }
-
-
- /* ------------------------------ CommentDepth ---------------------------------
-
- Determine comment depth of a line
-
- */
-
- WORD
- CommentDepth(text, len, last)
-
- UBYTE *text;
- UWORD len;
- ULONG last;
- {
- if (len) {
-
- BOOL inString, inComment;
- WORD depth;
-
- // preset comment status based on status of previous line
-
- if (last & (COMMENT_START | COMMENT_BODY)) {
-
- depth = COMMENT_DEPTH(last);
- inComment = TRUE;
- }
- else {
-
- depth = 0;
- inComment = FALSE;
- }
-
- for (inString = FALSE; len; ++text, --len) {
-
- // (,),",*
-
- if (*text <= '*') {
-
- if (inComment) {
-
- // end of comment detected ?
-
- if (len > 1) {
-
- if ((*text == '*') && (text[1] == ')')) {
-
- --depth;
- ++text;
- --len;
-
- // end of a muli-line comment or end of inner line comment ?
-
- if (depth == 0)
- inComment = FALSE;
- }
- else if ((*text == '(') && (text[1] == '*')) {
-
- // nested comment detected
-
- ++depth;
- ++text;
- --len;
- }
- }
- }
- else if ((*text == 34) && ((inString == FALSE) || (*(text - 1) != 92)))
-
- inString = !inString;
-
- else if (inString == FALSE) {
-
- if ((*text == '(') && (text[1] == '*') && (len > 1)) {
-
- // start of comment detected
-
- inComment = TRUE;
-
- ++depth;
- ++text;
- --len;
- }
- }
- }
- }
-
- return(depth);
- }
- else if (last & (COMMENT_START | COMMENT_BODY))
-
- return(COMMENT_DEPTH(last));
- else
- return(0);
- }
-
-
- /* ------------------------------- 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, 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->Fold || (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 (lineNode->Fold)
-
- // 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
-
- if (BadSuccessor(last, lineNode)) {
-
- 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->Fold || (lineNode->UserData == EMPTY_STACK) || (lineNode->UserData == NULL))
- 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 (<last>
- are the comment flags of the previous line).
-
- */
-
- BOOL
- BadSuccessor(last, node)
-
- UWORD last;
- struct LineNode *node;
- {
- UWORD flags = (node->UserData == EMPTY_STACK) ? COMMENT_NONE : GET_FLAGS(node);
-
- if (flags & (COMMENT_BODY | COMMENT_END)) {
-
- // comment body/end without start ?
-
- if ((last & (COMMENT_START | COMMENT_BODY)) == FALSE)
- return(TRUE);
-
- // valid comment depth value ?
-
- if (CommentDepth(node->Text, node->Len, last) != COMMENT_DEPTH(flags))
- return(TRUE);
- }
- else if (last & COMMENT_START) {
-
- // comment start without body/end ?
-
- if ((flags & (COMMENT_BODY | COMMENT_END)) == FALSE)
-
- return(TRUE);
-
- // comment inside comment ?
-
- if (flags & COMMENT_START)
-
- return(TRUE);
- }
- else if (last & COMMENT_BODY) {
-
- // body line without end ?
-
- if ((flags & (COMMENT_BODY | COMMENT_END)) == FALSE)
-
- return(TRUE);
- }
-
- return(FALSE);
- }
-
-
- /* --------------------------------- IsNumber ----------------------------------
-
- Return TRUE if text is a number
-
- */
-
- BOOL
- IsNumber(text, len)
-
- UBYTE *text;
- UWORD len;
- {
- UWORD last = text[len - 1];
-
- if ((last == 'X') || (last == 'H')) {
-
- // check hex number
-
- while (--len) {
-
- if ((*text > '9') || (*text < '0')) {
-
- UWORD next = UPPER(*text);
-
- if ((next > 'F') || (next < 'A'))
- return(FALSE);
- }
-
- ++text;
- }
-
- return(TRUE);
- }
- else {
-
- while (len--) {
-
- if ((*text > '9') || (*text < '0'))
- return(FALSE);
- else
- ++text;
- }
-
- return(TRUE);
- }
- }
-
- ///
-