home *** CD-ROM | disk | FTP | other *** search
- /* -----------------------------------------------------------------------------
-
- C comment parser ©1996 Dietmar Eilert
-
- GoldED syntax parser example library. This is a context scanner highlighting
- ANSI C comments and C++ comments. This example doesn't use a memory consuming
- syntax cache, ie. results of a prior parser pass are not stored. However, the
- scanner will remember (cache) whether a line has been parsed and whether parts
- of a comment have been detected within the line using flag bits stored in the
- line's UserData slot.
-
- 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 SyntaxChunk *bh_SyntaxStack; // parser output
- struct RefreshRequest bh_RefreshRequest; // display refresh request
- };
-
- // these flag bits (attached to text lines) describe the line's properties
-
- #define COMMENT_NONE (1L<<0) // 1 no comment in this line
- #define COMMENT_SIMPLE (1L<<1) // 2 there is a comment in this line
- #define COMMENT_START (1L<<2) // 4 line is start of multi-line comment
- #define COMMENT_BODY (1L<<3) // 8 line is body of multi-line comment
- #define COMMENT_END (1L<<4) // 16 line is end of multi-line comment
-
- ///
- /// "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 struct LineNode *FindContextStart(ULONG, struct LineNode *);
- Prototype void ParseContext(struct LineNode *, ULONG);
- Prototype ULONG ParseContextLine(struct LineNode *, ULONG);
- Prototype struct SyntaxChunk *ParseString(UBYTE *, UWORD, ULONG, ULONG);
- Prototype BOOL BadSuccessor(ULONG, ULONG);
- Prototype struct RefreshRequest *VerifyContext(ULONG, ULONG, ULONG);
-
- ///
- /// "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: Comments 2.2 (" __COMMODORE_DATE__ ")";
-
- static struct ParserData parserData;
-
- // syntax elements understood by parser
-
- static UBYTE *levelNames[] = {
-
- "Standard text",
- "ANSI Comment",
- "C++ Comment",
- NULL
- };
-
- static UBYTE *example[] = {
-
- "/* Simple ANSI C example */ ",
- " ",
- "#include <defs.h> ",
- " ",
- "int ",
- "main(int argc, char **argv) ",
- "{ ",
- " // anybody at home ? ",
- " ",
- " puts(\"Hello world !\"); ",
- "} ",
-
- NULL
- };
-
- // color suggestions
-
- static ULONG levelColors[] = {
-
- MAKE_RGB4(0, 0, 0),
- MAKE_RGB4(15, 15, 15),
- MAKE_RGB4(15, 15, 0)
- };
-
- parserData.pd_Release = SCANLIBVERSION;
- parserData.pd_Version = 1;
- parserData.pd_Serial = 0;
- parserData.pd_Info = "Comments 2.2";
- parserData.pd_Levels = 3;
- parserData.pd_Names = levelNames;
- parserData.pd_Colors = levelColors;
- parserData.pd_Flags = SCPRF_SYNTAXCACHE | SCPRF_CONTEXT;
- parserData.pd_Example = example;
-
- 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.
- Additionally, we preparse the unfolded text.
-
- */
-
- 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_SyntaxStack = 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. This scanner doesn't use a memory consuming syntax cache,
- there is nothing to be freed.
-
- */
-
- LibCall void
- FlushScanner(__D0 ULONG scanID)
- {
- ;
- }
-
-
- /* ------------------------------- 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)
- {
- ;
- }
-
-
- /* ------------------------------- 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) {
-
- // not yet preparsed ? preparse a couple of lines
-
- if (lineNode->UserData == NULL) {
-
- ULONG lines = ((struct BufferHandle *)scanID)->bh_EditConfig->Lines - line;
-
- if (lines > 50)
- lines = 50;
-
- ParseSection(scanID, lineNode, lines);
- }
-
- if ((ULONG)lineNode->UserData & (COMMENT_SIMPLE | COMMENT_START | COMMENT_BODY | COMMENT_END))
-
- return(ParseString(lineNode->Text, lineNode->Len, scanID, (ULONG)lineNode->UserData));
- else
- return(NULL);
- }
- 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
-
- 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));
- }
-
-
- ///
- /// "private"
-
- /* ----------------------------- 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)
-
- struct LineNode *lineNode;
- ULONG lines;
- {
- ULONG mode;
-
- for (mode = COMMENT_NONE; lines--; ++lineNode) {
-
- if (IS_FOLD(lineNode))
-
- mode = COMMENT_NONE;
-
- else {
-
- if (lineNode->UserData == NULL)
- lineNode->UserData = (APTR)ParseContextLine(lineNode, mode);
-
- mode = (ULONG)lineNode->UserData;
- }
- }
- }
-
-
- /* ----------------------------- ParseContextLine ------------------------------
-
- Preparse a line, ie. 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.
-
- */
-
- ULONG
- ParseContextLine(lineNode, last)
-
- struct LineNode *lineNode;
- ULONG last;
- {
- UWORD len;
- ULONG status;
-
- if (last & (COMMENT_START | COMMENT_BODY))
- status = COMMENT_BODY;
- else
- status = FALSE;
-
- if (len = lineNode->Len) {
-
- BOOL inString, inComment, commentStart;
- UBYTE *text;
-
- // preset status of this line if part of a multi-line comment
-
- inComment = status & COMMENT_BODY;
- inString = FALSE;
- commentStart = FALSE;
-
- for (text = lineNode->Text; len >= 2; ++text, --len) {
-
- if (inComment) {
-
- // end of comment detected ?
-
- if ((text[0] == '*') && (text[1] == '/')) {
-
- // end of a muli-line comment or end of a simple comment ?
-
- if (commentStart)
-
- status |= COMMENT_SIMPLE;
-
- else {
-
- status |= COMMENT_END;
- status &= ~COMMENT_BODY;
- }
-
- inComment = FALSE;
-
- ++text;
- --len;
- }
- }
- else if (*text == 34)
-
- inString = !inString;
-
- else if (inString == FALSE) {
-
- // C++ comment detected (ignore rest of line) ?
-
- if ((text[0] == '/') && (text[1] == '/')) {
-
- status |= COMMENT_SIMPLE;
-
- break;
- }
- else if ((text[0] == '/') && (text[1] == '*')) {
-
- // start of comment detected
-
- inComment = TRUE;
- commentStart = TRUE;
-
- ++text;
- --len;
- }
- }
- }
-
- // comment not closed ?
-
- if (inComment) {
-
- // continuation of multi-line comment or new comment ?
-
- if (commentStart)
- status |= COMMENT_START;
- else
- status |= COMMENT_BODY;
- }
- }
-
- if (status == FALSE)
- status = COMMENT_NONE;
-
- return(status);
- }
-
-
- /* -------------------------------- ParseString --------------------------------
-
- Parse a string, build a syntax description. Return NULL if there is nothing to
- highlight.
-
- */
-
- struct SyntaxChunk *
- ParseString(UBYTE *text, UWORD len, ULONG scanID, ULONG status)
- {
- if (len) {
-
- struct SyntaxChunk *syntaxStack = ((struct BufferHandle *)scanID)->bh_SyntaxStack;
-
- UWORD element, indent, commentStart;
- BOOL inComment;
-
- // leading and trailing spaces have to be ignored
-
- for (indent = 0; (len && (*text == 32)); --len, ++indent)
- ++text;
-
- while (len && (text[len - 1] == 32))
- --len;
-
- // no syntax elements found so far
-
- element = 0;
-
- // inner block line ?
-
- if (status & COMMENT_BODY) {
-
- inComment = TRUE;
- commentStart = indent;
- }
- else {
-
- BOOL inString;
-
- if (inComment = (status & COMMENT_END))
- commentStart = indent;
-
- for (inString = FALSE; len >= 2; ++text, ++indent, --len) {
-
- if (inComment) {
-
- // end of comment detected ?
-
- if ((text[0] == '*') && (text[1] == '/')) {
-
- syntaxStack[element].sc_Start = commentStart;
- syntaxStack[element].sc_End = indent + 1;
- syntaxStack[element].sc_Level = 1;
-
- inComment = FALSE;
-
- ++element;
- ++text;
- ++indent;
- --len;
- }
- }
- else if (*text == 34)
-
- inString = !inString;
-
- else if (inString == FALSE) {
-
- // C++ comment detected (ignore rest of line) ?
-
- if ((text[0] == '/') && (text[1] == '/')) {
-
- syntaxStack[element].sc_Start = indent;
- syntaxStack[element].sc_End = indent + len - 1;
- syntaxStack[element].sc_Level = 2;
-
- ++element;
-
- break;
- }
- else if ((text[0] == '/') && (text[1] == '*')) {
-
- // start of comment detected
-
- inComment = TRUE;
- commentStart = indent;
-
- ++text;
- ++indent;
- --len;
- }
- }
- }
- }
-
- // unterminated comment ? highlight rest of line
-
- if (inComment) {
-
- syntaxStack[element].sc_Start = commentStart;
- syntaxStack[element].sc_End = indent + len - 1;
- syntaxStack[element].sc_Level = 1;
-
- ++element;
- }
-
- if (element) {
-
- // terminate syntax stack
-
- syntaxStack[element].sc_Start = FALSE;
- syntaxStack[element].sc_End = FALSE;
- syntaxStack[element].sc_Level = FALSE;
-
- return(syntaxStack);
- }
- else
- return(NULL);
-
- }
- else
- return(NULL);
- }
-
-
- /* ------------------------------- 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;
-
- ULONG last, refreshStart, refresh = FALSE;
-
- config = ((struct BufferHandle *)scanID)->bh_EditConfig;
-
- lineNode = config->TextNodes + line;
- lastNode = lineNode + (lines - 1);
-
- // preparse from context start until end of damage area
-
- if (lines)
- ParseSection(scanID, lineNode, lines);
-
- // get syntax flags of last line in damage area
-
- if ((lastNode < config->TextNodes) || IS_FOLD(lastNode))
- last = COMMENT_NONE;
- else
- last = (ULONG)lastNode->UserData;
-
- // 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))
-
- break;
-
- else if (lineNode->UserData == NULL)
-
- lineNode->UserData = (APTR)ParseContextLine(lineNode, last);
-
- else if (BadSuccessor(last, (ULONG)lineNode->UserData))
-
- lineNode->UserData = (APTR)ParseContextLine(lineNode, last);
- else
- break;
-
- last = (ULONG)lineNode->UserData;
- }
-
- 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);
- }
-
- // C++ comment inside ANSI comment ?
-
- if (pred & (COMMENT_START | COMMENT_BODY))
-
- if (pred & COMMENT_SIMPLE)
-
- return(TRUE);
-
- return(FALSE);
- }
-
- ///
-