// Scintilla source code edit control /** @file LexCLW.cxx ** Lexer for Clarion. ** 2004/12/17 Updated Lexer **/ // Copyright 2003-2004 by Ron Schofield // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include #include #include #include "ILexer.h" #include "Scintilla.h" #include "SciLexer.h" #include "WordList.h" #include "LexAccessor.h" #include "Accessor.h" #include "StyleContext.h" #include "CharacterSet.h" #include "LexerModule.h" using namespace Lexilla; // Is an end of line character inline bool IsEOL(const int ch) { return(ch == '\n'); } // Convert character to uppercase static char CharacterUpper(char chChar) { if (chChar < 'a' || chChar > 'z') { return(chChar); } else { return(static_cast(chChar - 'a' + 'A')); } } // Convert string to uppercase static void StringUpper(char *szString) { while (*szString) { *szString = CharacterUpper(*szString); szString++; } } // Is a label start character inline bool IsALabelStart(const int iChar) { return(isalpha(iChar) || iChar == '_'); } // Is a label character inline bool IsALabelCharacter(const int iChar) { return(isalnum(iChar) || iChar == '_' || iChar == ':'); } // Is the character is a ! and the the next character is not a ! inline bool IsACommentStart(const int iChar) { return(iChar == '!'); } // Is the character a Clarion hex character (ABCDEF) inline bool IsAHexCharacter(const int iChar, bool bCaseSensitive) { // Case insensitive. if (!bCaseSensitive) { if (strchr("ABCDEFabcdef", iChar) != NULL) { return(true); } } // Case sensitive else { if (strchr("ABCDEF", iChar) != NULL) { return(true); } } return(false); } // Is the character a Clarion base character (B=Binary, O=Octal, H=Hex) inline bool IsANumericBaseCharacter(const int iChar, bool bCaseSensitive) { // Case insensitive. if (!bCaseSensitive) { // If character is a numeric base character if (strchr("BOHboh", iChar) != NULL) { return(true); } } // Case sensitive else { // If character is a numeric base character if (strchr("BOH", iChar) != NULL) { return(true); } } return(false); } // Set the correct numeric constant state inline bool SetNumericConstantState(StyleContext &scDoc) { int iPoints = 0; // Point counter char cNumericString[512]; // Numeric string buffer // Buffer the current numberic string scDoc.GetCurrent(cNumericString, sizeof(cNumericString)); // Loop through the string until end of string (NULL termination) for (int iIndex = 0; cNumericString[iIndex] != '\0'; iIndex++) { // Depending on the character switch (cNumericString[iIndex]) { // Is a . (point) case '.' : // Increment point counter iPoints++; break; default : break; } } // If points found (can be more than one for improper formatted number if (iPoints > 0) { return(true); } // Else no points found else { return(false); } } // Get the next word in uppercase from the current position (keyword lookahead) inline bool GetNextWordUpper(Accessor &styler, Sci_PositionU uiStartPos, Sci_Position iLength, char *cWord) { Sci_PositionU iIndex = 0; // Buffer Index // Loop through the remaining string from the current position for (Sci_Position iOffset = uiStartPos; iOffset < iLength; iOffset++) { // Get the character from the buffer using the offset char cCharacter = styler[iOffset]; if (IsEOL(cCharacter)) { break; } // If the character is alphabet character if (isalpha(cCharacter)) { // Add UPPERCASE character to the word buffer cWord[iIndex++] = CharacterUpper(cCharacter); } } // Add null termination cWord[iIndex] = '\0'; // If no word was found if (iIndex == 0) { // Return failure return(false); } // Else word was found else { // Return success return(true); } } // Clarion Language Colouring Procedure static void ColouriseClarionDoc(Sci_PositionU uiStartPos, Sci_Position iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler, bool bCaseSensitive) { int iParenthesesLevel = 0; // Parenthese Level int iColumn1Label = false; // Label starts in Column 1 WordList &wlClarionKeywords = *wlKeywords[0]; // Clarion Keywords WordList &wlCompilerDirectives = *wlKeywords[1]; // Compiler Directives WordList &wlRuntimeExpressions = *wlKeywords[2]; // Runtime Expressions WordList &wlBuiltInProcsFuncs = *wlKeywords[3]; // Builtin Procedures and Functions WordList &wlStructsDataTypes = *wlKeywords[4]; // Structures and Data Types WordList &wlAttributes = *wlKeywords[5]; // Procedure Attributes WordList &wlStandardEquates = *wlKeywords[6]; // Standard Equates WordList &wlLabelReservedWords = *wlKeywords[7]; // Clarion Reserved Keywords (Labels) WordList &wlProcLabelReservedWords = *wlKeywords[8]; // Clarion Reserved Keywords (Procedure Labels) const char wlProcReservedKeywordList[] = "PROCEDURE FUNCTION"; WordList wlProcReservedKeywords; wlProcReservedKeywords.Set(wlProcReservedKeywordList); const char wlCompilerKeywordList[] = "COMPILE OMIT"; WordList wlCompilerKeywords; wlCompilerKeywords.Set(wlCompilerKeywordList); const char wlLegacyStatementsList[] = "BOF EOF FUNCTION POINTER SHARE"; WordList wlLegacyStatements; wlLegacyStatements.Set(wlLegacyStatementsList); StyleContext scDoc(uiStartPos, iLength, iInitStyle, accStyler); // lex source code for (; scDoc.More(); scDoc.Forward()) { // // Determine if the current state should terminate. // // Label State Handling if (scDoc.state == SCE_CLW_LABEL) { // If the character is not a valid label if (!IsALabelCharacter(scDoc.ch)) { // If the character is a . (dot syntax) if (scDoc.ch == '.') { // Turn off column 1 label flag as label now cannot be reserved work iColumn1Label = false; // Uncolour the . (dot) to default state, move forward one character, // and change back to the label state. scDoc.SetState(SCE_CLW_DEFAULT); scDoc.Forward(); scDoc.SetState(SCE_CLW_LABEL); } // Else check label else { char cLabel[512]; // Label buffer // Buffer the current label string scDoc.GetCurrent(cLabel,sizeof(cLabel)); // If case insensitive, convert string to UPPERCASE to match passed keywords. if (!bCaseSensitive) { StringUpper(cLabel); } // Else if UPPERCASE label string is in the Clarion compiler keyword list if (wlCompilerKeywords.InList(cLabel) && iColumn1Label){ // change the label to error state scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); } // Else if UPPERCASE label string is in the Clarion reserved keyword list else if (wlLabelReservedWords.InList(cLabel) && iColumn1Label){ // change the label to error state scDoc.ChangeState(SCE_CLW_ERROR); } // Else if UPPERCASE label string is else if (wlProcLabelReservedWords.InList(cLabel) && iColumn1Label) { char cWord[512]; // Word buffer // Get the next word from the current position if (GetNextWordUpper(accStyler,scDoc.currentPos,uiStartPos+iLength,cWord)) { // If the next word is a procedure reserved word if (wlProcReservedKeywords.InList(cWord)) { // Change the label to error state scDoc.ChangeState(SCE_CLW_ERROR); } } } // Else if label string is in the compiler directive keyword list else if (wlCompilerDirectives.InList(cLabel)) { // change the state to compiler directive state scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); } // Terminate the label state and set to default state scDoc.SetState(SCE_CLW_DEFAULT); } } } // Keyword State Handling else if (scDoc.state == SCE_CLW_KEYWORD) { // If character is : (colon) if (scDoc.ch == ':') { char cEquate[512]; // Equate buffer // Move forward to include : (colon) in buffer scDoc.Forward(); // Buffer the equate string scDoc.GetCurrent(cEquate,sizeof(cEquate)); // If case insensitive, convert string to UPPERCASE to match passed keywords. if (!bCaseSensitive) { StringUpper(cEquate); } // If statement string is in the equate list if (wlStandardEquates.InList(cEquate)) { // Change to equate state scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE); } } // If the character is not a valid label character else if (!IsALabelCharacter(scDoc.ch)) { char cStatement[512]; // Statement buffer // Buffer the statement string scDoc.GetCurrent(cStatement,sizeof(cStatement)); // If case insensitive, convert string to UPPERCASE to match passed keywords. if (!bCaseSensitive) { StringUpper(cStatement); } // If statement string is in the Clarion keyword list if (wlClarionKeywords.InList(cStatement)) { // Change the statement string to the Clarion keyword state scDoc.ChangeState(SCE_CLW_KEYWORD); } // Else if statement string is in the compiler directive keyword list else if (wlCompilerDirectives.InList(cStatement)) { // Change the statement string to the compiler directive state scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); } // Else if statement string is in the runtime expressions keyword list else if (wlRuntimeExpressions.InList(cStatement)) { // Change the statement string to the runtime expressions state scDoc.ChangeState(SCE_CLW_RUNTIME_EXPRESSIONS); } // Else if statement string is in the builtin procedures and functions keyword list else if (wlBuiltInProcsFuncs.InList(cStatement)) { // Change the statement string to the builtin procedures and functions state scDoc.ChangeState(SCE_CLW_BUILTIN_PROCEDURES_FUNCTION); } // Else if statement string is in the tructures and data types keyword list else if (wlStructsDataTypes.InList(cStatement)) { // Change the statement string to the structures and data types state scDoc.ChangeState(SCE_CLW_STRUCTURE_DATA_TYPE); } // Else if statement string is in the procedure attribute keyword list else if (wlAttributes.InList(cStatement)) { // Change the statement string to the procedure attribute state scDoc.ChangeState(SCE_CLW_ATTRIBUTE); } // Else if statement string is in the standard equate keyword list else if (wlStandardEquates.InList(cStatement)) { // Change the statement string to the standard equate state scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE); } // Else if statement string is in the deprecated or legacy keyword list else if (wlLegacyStatements.InList(cStatement)) { // Change the statement string to the standard equate state scDoc.ChangeState(SCE_CLW_DEPRECATED); } // Else the statement string doesn't match any work list else { // Change the statement string to the default state scDoc.ChangeState(SCE_CLW_DEFAULT); } // Terminate the keyword state and set to default state scDoc.SetState(SCE_CLW_DEFAULT); } } // String State Handling else if (scDoc.state == SCE_CLW_STRING) { // If the character is an ' (single quote) if (scDoc.ch == '\'') { // Set the state to default and move forward colouring // the ' (single quote) as default state // terminating the string state scDoc.SetState(SCE_CLW_DEFAULT); scDoc.Forward(); } // If the next character is an ' (single quote) if (scDoc.chNext == '\'') { // Move forward one character and set to default state // colouring the next ' (single quote) as default state // terminating the string state scDoc.ForwardSetState(SCE_CLW_DEFAULT); scDoc.Forward(); } } // Picture String State Handling else if (scDoc.state == SCE_CLW_PICTURE_STRING) { // If the character is an ( (open parenthese) if (scDoc.ch == '(') { // Increment the parenthese level iParenthesesLevel++; } // Else if the character is a ) (close parenthese) else if (scDoc.ch == ')') { // If the parenthese level is set to zero // parentheses matched if (!iParenthesesLevel) { scDoc.SetState(SCE_CLW_DEFAULT); } // Else parenthese level is greater than zero // still looking for matching parentheses else { // Decrement the parenthese level iParenthesesLevel--; } } } // Standard Equate State Handling else if (scDoc.state == SCE_CLW_STANDARD_EQUATE) { if (!isalnum(scDoc.ch)) { scDoc.SetState(SCE_CLW_DEFAULT); } } // Integer Constant State Handling else if (scDoc.state == SCE_CLW_INTEGER_CONSTANT) { // If the character is not a digit (0-9) // or character is not a hexidecimal character (A-F) // or character is not a . (point) // or character is not a numberic base character (B,O,H) if (!(isdigit(scDoc.ch) || IsAHexCharacter(scDoc.ch, bCaseSensitive) || scDoc.ch == '.' || IsANumericBaseCharacter(scDoc.ch, bCaseSensitive))) { // If the number was a real if (SetNumericConstantState(scDoc)) { // Colour the matched string to the real constant state scDoc.ChangeState(SCE_CLW_REAL_CONSTANT); } // Else the number was an integer else { // Colour the matched string to an integer constant state scDoc.ChangeState(SCE_CLW_INTEGER_CONSTANT); } // Terminate the integer constant state and set to default state scDoc.SetState(SCE_CLW_DEFAULT); } } // // Determine if a new state should be entered. // // Beginning of Line Handling if (scDoc.atLineStart) { // Reset the column 1 label flag iColumn1Label = false; // If column 1 character is a label start character if (IsALabelStart(scDoc.ch)) { // Label character is found in column 1 // so set column 1 label flag and clear last column 1 label iColumn1Label = true; // Set the state to label scDoc.SetState(SCE_CLW_LABEL); } // else if character is a space or tab else if (IsASpace(scDoc.ch)){ // Set to default state scDoc.SetState(SCE_CLW_DEFAULT); } // else if comment start (!) or is an * (asterisk) else if (IsACommentStart(scDoc.ch) || scDoc.ch == '*' ) { // then set the state to comment. scDoc.SetState(SCE_CLW_COMMENT); } // else the character is a ? (question mark) else if (scDoc.ch == '?') { // Change to the compiler directive state, move forward, // colouring the ? (question mark), change back to default state. scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE); scDoc.Forward(); scDoc.SetState(SCE_CLW_DEFAULT); } // else an invalid character in column 1 else { // Set to error state scDoc.SetState(SCE_CLW_ERROR); } } // End of Line Handling else if (scDoc.atLineEnd) { // Reset to the default state at the end of each line. scDoc.SetState(SCE_CLW_DEFAULT); } // Default Handling else { // If in default state if (scDoc.state == SCE_CLW_DEFAULT) { // If is a letter could be a possible statement if (isalpha(scDoc.ch)) { // Set the state to Clarion Keyword and verify later scDoc.SetState(SCE_CLW_KEYWORD); } // else is a number else if (isdigit(scDoc.ch)) { // Set the state to Integer Constant and verify later scDoc.SetState(SCE_CLW_INTEGER_CONSTANT); } // else if the start of a comment or a | (line continuation) else if (IsACommentStart(scDoc.ch) || scDoc.ch == '|') { // then set the state to comment. scDoc.SetState(SCE_CLW_COMMENT); } // else if the character is a ' (single quote) else if (scDoc.ch == '\'') { // If the character is also a ' (single quote) // Embedded Apostrophe if (scDoc.chNext == '\'') { // Move forward colouring it as default state scDoc.ForwardSetState(SCE_CLW_DEFAULT); } else { // move to the next character and then set the state to comment. scDoc.ForwardSetState(SCE_CLW_STRING); } } // else the character is an @ (ampersand) else if (scDoc.ch == '@') { // Case insensitive. if (!bCaseSensitive) { // If character is a valid picture token character if (strchr("DEKNPSTdeknpst", scDoc.chNext) != NULL) { // Set to the picture string state scDoc.SetState(SCE_CLW_PICTURE_STRING); } } // Case sensitive else { // If character is a valid picture token character if (strchr("DEKNPST", scDoc.chNext) != NULL) { // Set the picture string state scDoc.SetState(SCE_CLW_PICTURE_STRING); } } } } } } // lexing complete scDoc.Complete(); } // Clarion Language Case Sensitive Colouring Procedure static void ColouriseClarionDocSensitive(Sci_PositionU uiStartPos, Sci_Position iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) { ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, true); } // Clarion Language Case Insensitive Colouring Procedure static void ColouriseClarionDocInsensitive(Sci_PositionU uiStartPos, Sci_Position iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) { ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, false); } // Fill Buffer static void FillBuffer(Sci_PositionU uiStart, Sci_PositionU uiEnd, Accessor &accStyler, char *szBuffer, Sci_PositionU uiLength) { Sci_PositionU uiPos = 0; while ((uiPos < uiEnd - uiStart + 1) && (uiPos < uiLength-1)) { szBuffer[uiPos] = static_cast(toupper(accStyler[uiStart + uiPos])); uiPos++; } szBuffer[uiPos] = '\0'; } // Classify Clarion Fold Point static int ClassifyClarionFoldPoint(int iLevel, const char* szString) { if (!(isdigit(szString[0]) || (szString[0] == '.'))) { if (strcmp(szString, "PROCEDURE") == 0) { // iLevel = SC_FOLDLEVELBASE + 1; } else if (strcmp(szString, "MAP") == 0 || strcmp(szString,"ACCEPT") == 0 || strcmp(szString,"BEGIN") == 0 || strcmp(szString,"CASE") == 0 || strcmp(szString,"EXECUTE") == 0 || strcmp(szString,"IF") == 0 || strcmp(szString,"ITEMIZE") == 0 || strcmp(szString,"INTERFACE") == 0 || strcmp(szString,"JOIN") == 0 || strcmp(szString,"LOOP") == 0 || strcmp(szString,"MODULE") == 0 || strcmp(szString,"RECORD") == 0) { iLevel++; } else if (strcmp(szString, "APPLICATION") == 0 || strcmp(szString, "CLASS") == 0 || strcmp(szString, "DETAIL") == 0 || strcmp(szString, "FILE") == 0 || strcmp(szString, "FOOTER") == 0 || strcmp(szString, "FORM") == 0 || strcmp(szString, "GROUP") == 0 || strcmp(szString, "HEADER") == 0 || strcmp(szString, "INTERFACE") == 0 || strcmp(szString, "MENU") == 0 || strcmp(szString, "MENUBAR") == 0 || strcmp(szString, "OLE") == 0 || strcmp(szString, "OPTION") == 0 || strcmp(szString, "QUEUE") == 0 || strcmp(szString, "REPORT") == 0 || strcmp(szString, "SHEET") == 0 || strcmp(szString, "TAB") == 0 || strcmp(szString, "TOOLBAR") == 0 || strcmp(szString, "VIEW") == 0 || strcmp(szString, "WINDOW") == 0) { iLevel++; } else if (strcmp(szString, "END") == 0 || strcmp(szString, "UNTIL") == 0 || strcmp(szString, "WHILE") == 0) { iLevel--; } } return(iLevel); } // Clarion Language Folding Procedure static void FoldClarionDoc(Sci_PositionU uiStartPos, Sci_Position iLength, int iInitStyle, WordList *[], Accessor &accStyler) { Sci_PositionU uiEndPos = uiStartPos + iLength; Sci_Position iLineCurrent = accStyler.GetLine(uiStartPos); int iLevelPrev = accStyler.LevelAt(iLineCurrent) & SC_FOLDLEVELNUMBERMASK; int iLevelCurrent = iLevelPrev; char chNext = accStyler[uiStartPos]; int iStyle = iInitStyle; int iStyleNext = accStyler.StyleAt(uiStartPos); int iVisibleChars = 0; Sci_Position iLastStart = 0; for (Sci_PositionU uiPos = uiStartPos; uiPos < uiEndPos; uiPos++) { char chChar = chNext; chNext = accStyler.SafeGetCharAt(uiPos + 1); int iStylePrev = iStyle; iStyle = iStyleNext; iStyleNext = accStyler.StyleAt(uiPos + 1); bool bEOL = (chChar == '\r' && chNext != '\n') || (chChar == '\n'); if (iStylePrev == SCE_CLW_DEFAULT) { if (iStyle == SCE_CLW_KEYWORD || iStyle == SCE_CLW_STRUCTURE_DATA_TYPE) { // Store last word start point. iLastStart = uiPos; } } if (iStylePrev == SCE_CLW_KEYWORD || iStylePrev == SCE_CLW_STRUCTURE_DATA_TYPE) { if(iswordchar(chChar) && !iswordchar(chNext)) { char chBuffer[100]; FillBuffer(iLastStart, uiPos, accStyler, chBuffer, sizeof(chBuffer)); iLevelCurrent = ClassifyClarionFoldPoint(iLevelCurrent,chBuffer); // if ((iLevelCurrent == SC_FOLDLEVELBASE + 1) && iLineCurrent > 1) { // accStyler.SetLevel(iLineCurrent-1,SC_FOLDLEVELBASE); // iLevelPrev = SC_FOLDLEVELBASE; // } } } if (bEOL) { int iLevel = iLevelPrev; if ((iLevelCurrent > iLevelPrev) && (iVisibleChars > 0)) iLevel |= SC_FOLDLEVELHEADERFLAG; if (iLevel != accStyler.LevelAt(iLineCurrent)) { accStyler.SetLevel(iLineCurrent,iLevel); } iLineCurrent++; iLevelPrev = iLevelCurrent; iVisibleChars = 0; } if (!isspacechar(chChar)) iVisibleChars++; } // Fill in the real level of the next line, keeping the current flags // as they will be filled in later. int iFlagsNext = accStyler.LevelAt(iLineCurrent) & ~SC_FOLDLEVELNUMBERMASK; accStyler.SetLevel(iLineCurrent, iLevelPrev | iFlagsNext); } // Word List Descriptions static const char * const rgWordListDescriptions[] = { "Clarion Keywords", "Compiler Directives", "Built-in Procedures and Functions", "Runtime Expressions", "Structure and Data Types", "Attributes", "Standard Equates", "Reserved Words (Labels)", "Reserved Words (Procedure Labels)", 0, }; // Case Sensitive Clarion Language Lexer extern const LexerModule lmClw(SCLEX_CLW, ColouriseClarionDocSensitive, "clarion", FoldClarionDoc, rgWordListDescriptions); // Case Insensitive Clarion Language Lexer extern const LexerModule lmClwNoCase(SCLEX_CLWNOCASE, ColouriseClarionDocInsensitive, "clarionnocase", FoldClarionDoc, rgWordListDescriptions);