home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 3
/
goldfish_volume_3.bin
/
files
/
text
/
edit
/
warpmod
/
source
/
funcs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-15
|
39KB
|
1,346 lines
/* -----------------------------------------------------------------------------
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);
}
}
///