// Scintilla source code edit control /** @file LexVHDL.cxx ** Lexer for VHDL ** Written by Phil Reid, ** Based on: ** - The Verilog Lexer by Avi Yegudin ** - The Fortran Lexer by Chuan-jian Shen ** - The C++ lexer by Neil Hodgson **/ // Copyright 1998-2002 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 "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; static void ColouriseVHDLDoc( Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler); /***************************************/ static inline bool IsAWordChar(const int ch) { return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' ); } /***************************************/ static inline bool IsAWordStart(const int ch) { return (ch < 0x80) && (isalnum(ch) || ch == '_'); } /***************************************/ static inline bool IsABlank(unsigned int ch) { return (ch == ' ') || (ch == 0x09) || (ch == 0x0b) ; } /***************************************/ static void ColouriseVHDLDoc( Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler) { WordList &Keywords = *keywordlists[0]; WordList &Operators = *keywordlists[1]; WordList &Attributes = *keywordlists[2]; WordList &Functions = *keywordlists[3]; WordList &Packages = *keywordlists[4]; WordList &Types = *keywordlists[5]; WordList &User = *keywordlists[6]; StyleContext sc(startPos, length, initStyle, styler); bool isExtendedId = false; // true when parsing an extended identifier while (sc.More()) { bool advance = true; // Determine if the current state should terminate. if (sc.state == SCE_VHDL_OPERATOR) { sc.SetState(SCE_VHDL_DEFAULT); } else if (sc.state == SCE_VHDL_NUMBER) { if (!IsAWordChar(sc.ch) && (sc.ch != '#')) { sc.SetState(SCE_VHDL_DEFAULT); } } else if (sc.state == SCE_VHDL_IDENTIFIER) { if (!isExtendedId && (!IsAWordChar(sc.ch) || (sc.ch == '.'))) { char s[100]; sc.GetCurrentLowered(s, sizeof(s)); if (Keywords.InList(s)) { sc.ChangeState(SCE_VHDL_KEYWORD); } else if (Operators.InList(s)) { sc.ChangeState(SCE_VHDL_STDOPERATOR); } else if (Attributes.InList(s)) { sc.ChangeState(SCE_VHDL_ATTRIBUTE); } else if (Functions.InList(s)) { sc.ChangeState(SCE_VHDL_STDFUNCTION); } else if (Packages.InList(s)) { sc.ChangeState(SCE_VHDL_STDPACKAGE); } else if (Types.InList(s)) { sc.ChangeState(SCE_VHDL_STDTYPE); } else if (User.InList(s)) { sc.ChangeState(SCE_VHDL_USERWORD); } sc.SetState(SCE_VHDL_DEFAULT); } else if (isExtendedId && ((sc.ch == '\\') || sc.atLineEnd)) { // extended identifiers are terminated by backslash, check for end of line in case we have invalid syntax isExtendedId = false; sc.ForwardSetState(SCE_VHDL_DEFAULT); advance = false; } } else if (sc.state == SCE_VHDL_COMMENT || sc.state == SCE_VHDL_COMMENTLINEBANG) { if (sc.atLineEnd) { sc.SetState(SCE_VHDL_DEFAULT); } } else if (sc.state == SCE_VHDL_STRING) { if (sc.ch == '"') { advance = false; sc.Forward(); if (sc.ch == '"') sc.Forward(); else sc.SetState(SCE_VHDL_DEFAULT); } else if (sc.atLineEnd) { advance = false; sc.ChangeState(SCE_VHDL_STRINGEOL); sc.ForwardSetState(SCE_VHDL_DEFAULT); } } else if (sc.state == SCE_VHDL_BLOCK_COMMENT){ if(sc.ch == '*' && sc.chNext == '/'){ advance = false; sc.Forward(); sc.ForwardSetState(SCE_VHDL_DEFAULT); } } // Determine if a new state should be entered. if (sc.state == SCE_VHDL_DEFAULT) { if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) { sc.SetState(SCE_VHDL_NUMBER); } else if (IsAWordStart(sc.ch)) { sc.SetState(SCE_VHDL_IDENTIFIER); } else if (sc.Match('-', '-')) { if (sc.Match("--!")) // Nice to have a different comment style sc.SetState(SCE_VHDL_COMMENTLINEBANG); else sc.SetState(SCE_VHDL_COMMENT); } else if (sc.Match('/', '*')){ sc.SetState(SCE_VHDL_BLOCK_COMMENT); } else if (sc.ch == '"') { sc.SetState(SCE_VHDL_STRING); } else if (sc.ch == '\'') { if (sc.GetRelative(2) == '\''){ if (sc.chNext != '(' || sc.GetRelative(4) != '\''){ // Can only be a character literal sc.SetState(SCE_VHDL_STRING); sc.Forward(); sc.Forward(); sc.ForwardSetState(SCE_VHDL_DEFAULT); advance = false; } // else can be a tick or a character literal, need more context, eg.: identifier'('x') } // else can only be a tick } else if (sc.ch == '\\') { isExtendedId = true; sc.SetState(SCE_VHDL_IDENTIFIER); } else if (isoperator(static_cast(sc.ch))) { sc.SetState(SCE_VHDL_OPERATOR); } } if (advance) sc.Forward(); } sc.Complete(); } //============================================================================= static bool IsCommentLine(Sci_Position line, Accessor &styler) { Sci_Position pos = styler.LineStart(line); Sci_Position eol_pos = styler.LineStart(line + 1) - 1; for (Sci_Position i = pos; i < eol_pos; i++) { char ch = styler[i]; char chNext = styler[i+1]; if ((ch == '-') && (chNext == '-')) return true; else if (ch != ' ' && ch != '\t') return false; } return false; } static bool IsCommentBlockStart(Sci_Position line, Accessor &styler) { Sci_Position pos = styler.LineStart(line); Sci_Position eol_pos = styler.LineStart(line + 1) - 1; for (Sci_Position i = pos; i < eol_pos; i++) { char ch = styler[i]; char chNext = styler[i+1]; char style = styler.StyleAt(i); if ((style == SCE_VHDL_BLOCK_COMMENT) && (ch == '/') && (chNext == '*')) return true; } return false; } static bool IsCommentBlockEnd(Sci_Position line, Accessor &styler) { Sci_Position pos = styler.LineStart(line); Sci_Position eol_pos = styler.LineStart(line + 1) - 1; for (Sci_Position i = pos; i < eol_pos; i++) { char ch = styler[i]; char chNext = styler[i+1]; char style = styler.StyleAt(i); if ((style == SCE_VHDL_BLOCK_COMMENT) && (ch == '*') && (chNext == '/')) return true; } return false; } static bool IsCommentStyle(char style) { return style == SCE_VHDL_BLOCK_COMMENT || style == SCE_VHDL_COMMENT || style == SCE_VHDL_COMMENTLINEBANG; } //============================================================================= // Folding the code static void FoldNoBoxVHDLDoc( Sci_PositionU startPos, Sci_Position length, int, Accessor &styler) { // Decided it would be smarter to have the lexer have all keywords included. Therefore I // don't check if the style for the keywords that I use to adjust the levels. char words[] = "architecture begin block case component else elsif end entity for generate loop package process record then " "procedure protected function when units"; WordList keywords; keywords.Set(words); bool foldComment = styler.GetPropertyInt("fold.comment", 1) != 0; bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; bool foldAtElse = styler.GetPropertyInt("fold.at.else", 1) != 0; bool foldAtBegin = styler.GetPropertyInt("fold.at.Begin", 1) != 0; bool foldAtParenthese = styler.GetPropertyInt("fold.at.Parenthese", 1) != 0; //bool foldAtWhen = styler.GetPropertyInt("fold.at.When", 1) != 0; //< fold at when in case statements int visibleChars = 0; 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 levelMinCurrent = levelCurrent; int levelMinCurrentElse = levelCurrent; ///< Used for folding at 'else' int levelMinCurrentBegin = levelCurrent; ///< Used for folding at 'begin' int levelNext = levelCurrent; /***************************************/ Sci_Position lastStart = 0; char prevWord[32] = ""; /***************************************/ // Find prev word // The logic for going up or down a level depends on a the previous keyword // This code could be cleaned up. Sci_Position end = 0; Sci_PositionU j; for(j = startPos; j>0; j--) { char ch = styler.SafeGetCharAt(j); char chPrev = styler.SafeGetCharAt(j-1); int style = styler.StyleAt(j); int stylePrev = styler.StyleAt(j-1); if ((!IsCommentStyle(style)) && (stylePrev != SCE_VHDL_STRING)) { if(IsAWordChar(chPrev) && !IsAWordChar(ch)) { end = j-1; } } if ((!IsCommentStyle(style)) && (style != SCE_VHDL_STRING)) { if(!IsAWordChar(chPrev) && IsAWordStart(ch) && (end != 0)) { char s[32]; Sci_PositionU k; for(k=0; (k<31 ) && (k(tolower(styler[j+k])); } s[k] = '\0'; if(keywords.InList(s)) { strcpy(prevWord, s); break; } } } } for(j=j+static_cast(strlen(prevWord)); j(tolower(styler[lastStart+k])); } s[k] = '\0'; if(keywords.InList(s)) { if ( strcmp(s, "architecture") == 0 || strcmp(s, "case") == 0 || strcmp(s, "block") == 0 || strcmp(s, "loop") == 0 || strcmp(s, "package") ==0 || strcmp(s, "process") == 0 || strcmp(s, "protected") == 0 || strcmp(s, "record") == 0 || strcmp(s, "then") == 0 || strcmp(s, "units") == 0) { if (strcmp(prevWord, "end") != 0) { if (levelMinCurrentElse > levelNext) { levelMinCurrentElse = levelNext; } levelNext++; } } else if (strcmp(s, "generate") == 0){ if (strcmp(prevWord, "end") != 0 && strcmp(prevWord, "else") != 0 && // vhdl08 else generate strcmp(prevWord, "case") != 0) // vhdl08 case generate { if (levelMinCurrentElse > levelNext) { levelMinCurrentElse = levelNext; } levelNext++; } } else if ( strcmp(s, "component") == 0 || strcmp(s, "entity") == 0 || strcmp(s, "configuration") == 0 ) { if (strcmp(prevWord, "end") != 0) { // check for instantiated unit by backward searching for the colon. Sci_PositionU pos = lastStart; char chAtPos=0, styleAtPos; do{// skip white spaces if(!pos) break; pos--; styleAtPos = styler.StyleAt(pos); chAtPos = styler.SafeGetCharAt(pos); }while(pos && (chAtPos == ' ' || chAtPos == '\t' || chAtPos == '\n' || chAtPos == '\r' || IsCommentStyle(styleAtPos))); // check for a colon (':') before the instantiated units "entity", "component" or "configuration". Don't fold thereafter. if (chAtPos != ':') { if (levelMinCurrentElse > levelNext) { levelMinCurrentElse = levelNext; } levelNext++; } } } else if ( strcmp(s, "procedure") == 0 || strcmp(s, "function") == 0) { if (strcmp(prevWord, "end") != 0) // check for "end procedure" etc. { // This code checks to see if the procedure / function is a definition within a "package" // rather than the actual code in the body. int BracketLevel = 0; for(Sci_Position pos=i+1; pos levelNext) { levelMinCurrentElse = levelNext; } levelNext++; break; } if((BracketLevel == 0) && (chAtPos == ';')) { break; } } } } else if (strcmp(s, "end") == 0) { levelNext--; } else if(strcmp(s, "elsif") == 0) { // elsif is followed by then or generate so folding occurs correctly levelNext--; } else if (strcmp(s, "else") == 0) { if(strcmp(prevWord, "when") != 0) // ignore a <= x when y else z; { levelMinCurrentElse = levelNext - 1; // VHDL else is all on its own so just dec. the min level } } else if( ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "architecture") == 0)) || ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "function") == 0)) || ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "procedure") == 0)) || ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "generate") == 0))) { levelMinCurrentBegin = levelNext - 1; } //Platform::DebugPrintf("Line[%04d] Prev[%20s] Cur[%20s] Level[%x]\n", lineCurrent+1, prevWord, s, levelCurrent); strcpy(prevWord, s); } } } if (atEOL) { int levelUse = levelCurrent; if (foldAtElse && (levelMinCurrentElse < levelUse)) { levelUse = levelMinCurrentElse; } if (foldAtBegin && (levelMinCurrentBegin < levelUse)) { levelUse = levelMinCurrentBegin; } int lev = levelUse | levelNext << 16; if (visibleChars == 0 && foldCompact) lev |= SC_FOLDLEVELWHITEFLAG; if (levelUse < levelNext) lev |= SC_FOLDLEVELHEADERFLAG; if (lev != styler.LevelAt(lineCurrent)) { styler.SetLevel(lineCurrent, lev); } //Platform::DebugPrintf("Line[%04d] ---------------------------------------------------- Level[%x]\n", lineCurrent+1, levelCurrent); lineCurrent++; levelCurrent = levelNext; //levelMinCurrent = levelCurrent; levelMinCurrentElse = levelCurrent; levelMinCurrentBegin = levelCurrent; visibleChars = 0; } /***************************************/ if (!isspacechar(ch)) visibleChars++; } /***************************************/ // Platform::DebugPrintf("Line[%04d] ---------------------------------------------------- Level[%x]\n", lineCurrent+1, levelCurrent); } //============================================================================= static void FoldVHDLDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *[], Accessor &styler) { FoldNoBoxVHDLDoc(startPos, length, initStyle, styler); } //============================================================================= static const char * const VHDLWordLists[] = { "Keywords", "Operators", "Attributes", "Standard Functions", "Standard Packages", "Standard Types", "User Words", 0, }; extern const LexerModule lmVHDL(SCLEX_VHDL, ColouriseVHDLDoc, "vhdl", FoldVHDLDoc, VHDLWordLists); // Keyword: // access after alias all architecture array assert attribute begin block body buffer bus case component // configuration constant disconnect downto else elsif end entity exit file for function generate generic // group guarded if impure in inertial inout is label library linkage literal loop map new next null of // on open others out package port postponed procedure process pure range record register reject report // return select severity shared signal subtype then to transport type unaffected units until use variable // wait when while with // // Operators: // abs and mod nand nor not or rem rol ror sla sll sra srl xnor xor // // Attributes: // left right low high ascending image value pos val succ pred leftof rightof base range reverse_range // length delayed stable quiet transaction event active last_event last_active last_value driving // driving_value simple_name path_name instance_name // // Std Functions: // now readline read writeline write endfile resolved to_bit to_bitvector to_stdulogic to_stdlogicvector // to_stdulogicvector to_x01 to_x01z to_UX01 rising_edge falling_edge is_x shift_left shift_right rotate_left // rotate_right resize to_integer to_unsigned to_signed std_match to_01 // // Std Packages: // std ieee work standard textio std_logic_1164 std_logic_arith std_logic_misc std_logic_signed // std_logic_textio std_logic_unsigned numeric_bit numeric_std math_complex math_real vital_primitives // vital_timing // // Std Types: // boolean bit character severity_level integer real time delay_length natural positive string bit_vector // file_open_kind file_open_status line text side width std_ulogic std_ulogic_vector std_logic // std_logic_vector X01 X01Z UX01 UX01Z unsigned signed //