// Scintilla source code edit control /** @file LexCIL.cxx ** Lexer for Common Intermediate Language ** Written by Jad Altahan (github.com/xv) ** CIL manual: https://www.ecma-international.org/publications/standards/Ecma-335.htm **/ // Copyright 1998-2001 by Neil Hodgson // The License.txt file describes the conditions under which this software may be distributed. #include #include #include #include #include #include #include #include #include #include #include #include "ILexer.h" #include "Scintilla.h" #include "SciLexer.h" #include "StringCopy.h" #include "WordList.h" #include "LexAccessor.h" #include "Accessor.h" #include "StyleContext.h" #include "CharacterSet.h" #include "LexerModule.h" #include "OptionSet.h" #include "DefaultLexer.h" using namespace Scintilla; using namespace Lexilla; namespace { // Use an unnamed namespace to protect the functions and classes from name conflicts bool IsAWordChar(const int ch) { return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.'); } bool IsOperator(const int ch) { if ((ch < 0x80) && (isalnum(ch))) return false; if (strchr("!%&*+-/<=>@^|~()[]{}", ch)) { return true; } return false; } constexpr bool IsStreamCommentStyle(const int style) noexcept { return style == SCE_CIL_COMMENT; } struct OptionsCIL { bool fold; bool foldComment; bool foldCommentMultiline; bool foldCompact; OptionsCIL() { fold = true; foldComment = false; foldCommentMultiline = true; foldCompact = true; } }; static const char *const cilWordListDesc[] = { "Primary CIL keywords", "Metadata", "Opcode instructions", 0 }; struct OptionSetCIL : public OptionSet { OptionSetCIL() { DefineProperty("fold", &OptionsCIL::fold); DefineProperty("fold.comment", &OptionsCIL::foldComment); DefineProperty("fold.cil.comment.multiline", &OptionsCIL::foldCommentMultiline, "Set this property to 0 to disable folding multi-line comments when fold.comment=1."); DefineProperty("fold.compact", &OptionsCIL::foldCompact); DefineWordListSets(cilWordListDesc); } }; LexicalClass lexicalClasses[] = { // Lexer CIL SCLEX_CIL SCE_CIL_: 0, "SCE_CIL_DEFAULT", "default", "White space", 1, "SCE_CIL_COMMENT", "comment", "Multi-line comment", 2, "SCE_CIL_COMMENTLINE", "comment line", "Line comment", 3, "SCE_CIL_WORD", "keyword", "Keyword 1", 4, "SCE_CIL_WORD2", "keyword", "Keyword 2", 5, "SCE_CIL_WORD3", "keyword", "Keyword 3", 6, "SCE_CIL_STRING", "literal string", "Double quoted string", 7, "SCE_CIL_LABEL", "label", "Code label", 8, "SCE_CIL_OPERATOR", "operator", "Operators", 9, "SCE_CIL_STRINGEOL", "error literal string", "String is not closed", 10, "SCE_CIL_IDENTIFIER", "identifier", "Identifiers", }; } class LexerCIL : public DefaultLexer { WordList keywords, keywords2, keywords3; OptionsCIL options; OptionSetCIL osCIL; public: LexerCIL() : DefaultLexer("cil", SCLEX_CIL, lexicalClasses, ELEMENTS(lexicalClasses)) { } virtual ~LexerCIL() { } void SCI_METHOD Release() override { delete this; } int SCI_METHOD Version() const override { return lvRelease5; } const char * SCI_METHOD PropertyNames() override { return osCIL.PropertyNames(); } int SCI_METHOD PropertyType(const char *name) override { return osCIL.PropertyType(name); } const char * SCI_METHOD DescribeProperty(const char *name) override { return osCIL.DescribeProperty(name); } Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override; const char * SCI_METHOD PropertyGet(const char* key) override { return osCIL.PropertyGet(key); } const char * SCI_METHOD DescribeWordListSets() override { return osCIL.DescribeWordListSets(); } Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override; void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override; void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override; void * SCI_METHOD PrivateCall(int, void *) override { return 0; } int SCI_METHOD LineEndTypesSupported() override { return SC_LINE_END_TYPE_UNICODE; } int SCI_METHOD PrimaryStyleFromStyle(int style) override { return style; } static ILexer5 *LexerFactoryCIL() { return new LexerCIL(); } }; Sci_Position SCI_METHOD LexerCIL::PropertySet(const char *key, const char *val) { if (osCIL.PropertySet(&options, key, val)) { return 0; } return -1; } Sci_Position SCI_METHOD LexerCIL::WordListSet(int n, const char *wl) { WordList *wordListN = 0; switch (n) { case 0: wordListN = &keywords; break; case 1: wordListN = &keywords2; break; case 2: wordListN = &keywords3; break; } Sci_Position firstModification = -1; if (wordListN) { WordList wlNew; wlNew.Set(wl); if (*wordListN != wlNew) { wordListN->Set(wl); firstModification = 0; } } return firstModification; } void SCI_METHOD LexerCIL::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) { if (initStyle == SCE_CIL_STRINGEOL) { initStyle = SCE_CIL_DEFAULT; } Accessor styler(pAccess, NULL); StyleContext sc(startPos, length, initStyle, styler); bool identAtLineStart = false, // Checks if an identifier is at line start (ignoring spaces) canStyleLabels = false; // Checks if conditions are met to style SCE_CIL_LABEL for (; sc.More(); sc.Forward()) { if (sc.atLineStart) { if (sc.state == SCE_CIL_STRING) { sc.SetState(SCE_CIL_STRING); } identAtLineStart = true; } // Handle string line continuation if (sc.ch == '\\' && (sc.chNext == '\n' || sc.chNext == '\r') && (sc.state == SCE_CIL_STRING)) { sc.Forward(); if (sc.ch == '\r' && sc.chNext == '\n') { sc.Forward(); } continue; } switch (sc.state) { case SCE_CIL_OPERATOR: sc.SetState(SCE_CIL_DEFAULT); break; case SCE_CIL_IDENTIFIER: if (!IsAWordChar(sc.ch)) { if (canStyleLabels && (sc.ch == ':' && sc.chNext != ':')) { sc.ChangeState(SCE_CIL_LABEL); sc.ForwardSetState(SCE_CIL_DEFAULT); } else { char kwSize[100]; sc.GetCurrent(kwSize, sizeof(kwSize)); int style = SCE_CIL_IDENTIFIER; if (keywords.InList(kwSize)) { style = SCE_CIL_WORD; } else if (keywords2.InList(kwSize)) { style = SCE_CIL_WORD2; } else if (keywords3.InList(kwSize)) { style = SCE_CIL_WORD3; } sc.ChangeState(style); sc.SetState(SCE_CIL_DEFAULT); } } break; case SCE_CIL_COMMENT: if (sc.Match('*', '/')) { sc.Forward(); sc.ForwardSetState(SCE_CIL_DEFAULT); } break; case SCE_CIL_COMMENTLINE: if (sc.atLineStart) { sc.SetState(SCE_CIL_DEFAULT); } break; case SCE_CIL_STRING: if (sc.ch == '\\') { if (sc.chNext == '"' || sc.chNext == '\\') { sc.Forward(); } } else if (sc.ch == '"') { sc.ForwardSetState(SCE_CIL_DEFAULT); } else if (sc.atLineEnd) { sc.ChangeState(SCE_CIL_STRINGEOL); sc.ForwardSetState(SCE_CIL_DEFAULT); } break; } if (sc.state == SCE_CIL_DEFAULT) { // String if (sc.ch == '"') { sc.SetState(SCE_CIL_STRING); } // Keyword else if (IsAWordChar(sc.ch)) { // Allow setting SCE_CIL_LABEL style only if the label is the // first token in the line and does not start with a dot or a digit canStyleLabels = identAtLineStart && !(sc.ch == '.' || IsADigit(sc.ch)); sc.SetState(SCE_CIL_IDENTIFIER); } // Multi-line comment else if (sc.Match('/', '*')) { sc.SetState(SCE_CIL_COMMENT); sc.Forward(); } // Line comment else if (sc.Match('/', '/')) { sc.SetState(SCE_CIL_COMMENTLINE); } // Operators else if (IsOperator(sc.ch)) { sc.SetState(SCE_CIL_OPERATOR); } } if (!IsASpace(sc.ch)) { identAtLineStart = false; } } sc.Complete(); } void SCI_METHOD LexerCIL::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) { if (!options.fold) { return; } LexAccessor styler(pAccess); const Sci_PositionU endPos = startPos + length; Sci_Position lineCurrent = styler.GetLine(startPos); int levelCurrent = SC_FOLDLEVELBASE; if (lineCurrent > 0) levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16; int style = initStyle; int styleNext = styler.StyleAt(startPos); int levelNext = levelCurrent; int visibleChars = 0; char chNext = styler[startPos]; for (Sci_PositionU i = startPos; i < endPos; i++) { const char ch = chNext; int stylePrev = style; chNext = styler.SafeGetCharAt(i + 1); style = styleNext; styleNext = styler.StyleAt(i + 1); const bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); if (options.foldComment && options.foldCommentMultiline && IsStreamCommentStyle(style)) { if (!IsStreamCommentStyle(stylePrev)) { levelNext++; } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { levelNext--; } } if (style == SCE_CIL_OPERATOR) { if (ch == '{') { levelNext++; } else if (ch == '}') { levelNext--; } } if (!IsASpace(ch)) { visibleChars++; } if (atEOL || (i == endPos - 1)) { int lev = levelCurrent | levelNext << 16; if (visibleChars == 0 && options.foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; if (levelCurrent < levelNext) lev |= SC_FOLDLEVELHEADERFLAG; if (lev != styler.LevelAt(lineCurrent)) { styler.SetLevel(lineCurrent, lev); } lineCurrent++; levelCurrent = levelNext; if (options.foldCompact && i == static_cast(styler.Length() - 1)) { styler.SetLevel(lineCurrent, lev | SC_FOLDLEVELWHITEFLAG); } visibleChars = 0; } } } extern const LexerModule lmCIL(SCLEX_CIL, LexerCIL::LexerFactoryCIL, "cil", cilWordListDesc);