You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
16 KiB
473 lines
16 KiB
// 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 <neilh@scintilla.org> |
|
// The License.txt file describes the conditions under which this software may be distributed. |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <ctype.h> |
|
#include <stdio.h> |
|
#include <stdarg.h> |
|
|
|
#include "Platform.h" |
|
|
|
#include "PropSet.h" |
|
#include "Accessor.h" |
|
#include "StyleContext.h" |
|
#include "KeyWords.h" |
|
#include "Scintilla.h" |
|
#include "SciLexer.h" |
|
|
|
static void ColouriseVHDLDoc( |
|
unsigned int startPos, |
|
int 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 == '_'); |
|
} |
|
|
|
/***************************************/ |
|
inline bool IsABlank(unsigned int ch) { |
|
return (ch == ' ') || (ch == 0x09) || (ch == 0x0b) ; |
|
} |
|
|
|
/***************************************/ |
|
static void ColouriseVHDLDoc( |
|
unsigned int startPos, |
|
int 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); |
|
|
|
for (; sc.More(); sc.Forward()) |
|
{ |
|
|
|
// 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 (!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 (sc.state == SCE_VHDL_COMMENT || sc.state == SCE_V_COMMENTLINEBANG) { |
|
if (sc.atLineEnd) { |
|
sc.SetState(SCE_VHDL_DEFAULT); |
|
} |
|
} else if (sc.state == SCE_VHDL_STRING) { |
|
if (sc.ch == '\\') { |
|
if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { |
|
sc.Forward(); |
|
} |
|
} else if (sc.ch == '\"') { |
|
sc.ForwardSetState(SCE_VHDL_DEFAULT); |
|
} else if (sc.atLineEnd) { |
|
sc.ChangeState(SCE_V_STRINGEOL); |
|
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('-', '-')) { |
|
sc.SetState(SCE_VHDL_COMMENT); |
|
sc.Forward(); |
|
} 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.ch == '\"') { |
|
sc.SetState(SCE_VHDL_STRING); |
|
} else if (isoperator(static_cast<char>(sc.ch))) { |
|
sc.SetState(SCE_VHDL_OPERATOR); |
|
} |
|
} |
|
} |
|
sc.Complete(); |
|
} |
|
//============================================================================= |
|
static bool IsCommentLine(int line, Accessor &styler) { |
|
int pos = styler.LineStart(line); |
|
int eol_pos = styler.LineStart(line + 1) - 1; |
|
for (int 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; |
|
} |
|
|
|
//============================================================================= |
|
// Folding the code |
|
static void FoldNoBoxVHDLDoc( |
|
unsigned int startPos, |
|
int length, |
|
int initStyle, |
|
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 case component else elsif end entity generate loop package process record then " |
|
"procedure function when"; |
|
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; |
|
unsigned int endPos = startPos + length; |
|
|
|
int 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; |
|
|
|
/***************************************/ |
|
int 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. |
|
int end = 0; |
|
unsigned int 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 ((stylePrev != SCE_VHDL_COMMENT) && (stylePrev != SCE_VHDL_STRING)) |
|
{ |
|
if(IsAWordChar(chPrev) && !IsAWordChar(ch)) |
|
{ |
|
end = j-1; |
|
} |
|
} |
|
if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING)) |
|
{ |
|
if(!IsAWordChar(chPrev) && IsAWordStart(ch) && (end != 0)) |
|
{ |
|
char s[32]; |
|
unsigned int k; |
|
for(k=0; (k<31 ) && (k<end-j+1 ); k++) { |
|
s[k] = static_cast<char>(tolower(styler[j+k])); |
|
} |
|
s[k] = '\0'; |
|
|
|
if(keywords.InList(s)) { |
|
strcpy(prevWord, s); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
for(j=j+strlen(prevWord); j<endPos; j++) |
|
{ |
|
char ch = styler.SafeGetCharAt(j); |
|
int style = styler.StyleAt(j); |
|
if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING)) |
|
{ |
|
if((ch == ';') && (strcmp(prevWord, "end") == 0)) |
|
{ |
|
strcpy(prevWord, ";"); |
|
} |
|
} |
|
} |
|
|
|
char chNext = styler[startPos]; |
|
char chPrev = '\0'; |
|
char chNextNonBlank; |
|
int styleNext = styler.StyleAt(startPos); |
|
int style = initStyle; |
|
//Platform::DebugPrintf("Line[%04d] Prev[%20s] ************************* Level[%x]\n", lineCurrent+1, prevWord, levelCurrent); |
|
|
|
/***************************************/ |
|
for (unsigned int i = startPos; i < endPos; i++) |
|
{ |
|
char ch = chNext; |
|
chNext = styler.SafeGetCharAt(i + 1); |
|
chPrev = styler.SafeGetCharAt(i - 1); |
|
chNextNonBlank = chNext; |
|
unsigned int j = i+1; |
|
while(IsABlank(chNextNonBlank) && j<endPos) |
|
{ |
|
j ++ ; |
|
chNextNonBlank = styler.SafeGetCharAt(j); |
|
} |
|
style = styleNext; |
|
styleNext = styler.StyleAt(i + 1); |
|
bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); |
|
|
|
if (foldComment && atEOL && IsCommentLine(lineCurrent, styler)) |
|
{ |
|
if(!IsCommentLine(lineCurrent-1, styler) && IsCommentLine(lineCurrent+1, styler)) |
|
{ |
|
levelNext++; |
|
} |
|
else if(IsCommentLine(lineCurrent-1, styler) && !IsCommentLine(lineCurrent+1, styler)) |
|
{ |
|
levelNext--; |
|
} |
|
} |
|
|
|
if ((style == SCE_VHDL_OPERATOR) && foldAtParenthese) |
|
{ |
|
if(ch == '(') { |
|
levelNext++; |
|
} else if (ch == ')') { |
|
levelNext--; |
|
} |
|
} |
|
|
|
if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING)) |
|
{ |
|
if((ch == ';') && (strcmp(prevWord, "end") == 0)) |
|
{ |
|
strcpy(prevWord, ";"); |
|
} |
|
|
|
if(!IsAWordChar(chPrev) && IsAWordStart(ch)) |
|
{ |
|
lastStart = i; |
|
} |
|
|
|
if(iswordchar(ch) && !iswordchar(chNext)) { |
|
char s[32]; |
|
unsigned int k; |
|
for(k=0; (k<31 ) && (k<i-lastStart+1 ); k++) { |
|
s[k] = static_cast<char>(tolower(styler[lastStart+k])); |
|
} |
|
s[k] = '\0'; |
|
|
|
if(keywords.InList(s)) |
|
{ |
|
if ( |
|
strcmp(s, "architecture") == 0 || |
|
strcmp(s, "case") == 0 || |
|
strcmp(s, "component") == 0 || |
|
strcmp(s, "entity") == 0 || |
|
strcmp(s, "generate") == 0 || |
|
strcmp(s, "loop") == 0 || |
|
strcmp(s, "package") ==0 || |
|
strcmp(s, "process") == 0 || |
|
strcmp(s, "record") == 0 || |
|
strcmp(s, "then") == 0) |
|
{ |
|
if (strcmp(prevWord, "end") != 0) |
|
{ |
|
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(int j=i+1; j<styler.Length(); j++) |
|
{ |
|
int LocalStyle = styler.StyleAt(j); |
|
char LocalCh = styler.SafeGetCharAt(j); |
|
if(LocalCh == '(') BracketLevel++; |
|
if(LocalCh == ')') BracketLevel--; |
|
if( |
|
(BracketLevel == 0) && |
|
(LocalStyle != SCE_VHDL_COMMENT) && |
|
(LocalStyle != SCE_VHDL_STRING) && |
|
!iswordchar(styler.SafeGetCharAt(j-1)) && |
|
styler.Match(j, "is") && |
|
!iswordchar(styler.SafeGetCharAt(j+2))) |
|
{ |
|
if (levelMinCurrentElse > levelNext) { |
|
levelMinCurrentElse = levelNext; |
|
} |
|
levelNext++; |
|
break; |
|
} |
|
if((BracketLevel == 0) && (LocalCh == ';')) |
|
{ |
|
break; |
|
} |
|
} |
|
} |
|
|
|
} else if (strcmp(s, "end") == 0) { |
|
levelNext--; |
|
} else if(strcmp(s, "elsif") == 0) { // elsif is followed by then 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))) |
|
{ |
|
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(unsigned int startPos, int 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, |
|
}; |
|
|
|
|
|
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 |
|
// |
|
|
|
|