diff options
Diffstat (limited to 'lexers/LexVerilog.cxx')
| -rw-r--r-- | lexers/LexVerilog.cxx | 794 | 
1 files changed, 652 insertions, 142 deletions
| diff --git a/lexers/LexVerilog.cxx b/lexers/LexVerilog.cxx index 8062c09cf..dae650bf8 100644 --- a/lexers/LexVerilog.cxx +++ b/lexers/LexVerilog.cxx @@ -24,10 +24,303 @@  #include "CharacterSet.h"  #include "LexerModule.h" +#include <string> +#include <vector> +#include <map> + +#include "OptionSet.h" +#include "SubStyles.h" +  #ifdef SCI_NAMESPACE  using namespace Scintilla;  #endif +namespace { +	// Use an unnamed namespace to protect the functions and classes from name conflicts + +struct PPDefinition { +	int line; +	std::string key; +	std::string value; +	bool isUndef; +	std::string arguments; +	PPDefinition(int line_, const std::string &key_, const std::string &value_, bool isUndef_ = false, std::string arguments_="") : +		line(line_), key(key_), value(value_), isUndef(isUndef_), arguments(arguments_) { +	} +}; + +class LinePPState { +	int state; +	int ifTaken; +	int level; +	bool ValidLevel() const { +		return level >= 0 && level < 32; +	} +	int maskLevel() const { +		return 1 << level; +	} +public: +	LinePPState() : state(0), ifTaken(0), level(-1) { +	} +	bool IsInactive() const { +		return state != 0; +	} +	bool CurrentIfTaken() const { +		return (ifTaken & maskLevel()) != 0; +	} +	void StartSection(bool on) { +		level++; +		if (ValidLevel()) { +			if (on) { +				state &= ~maskLevel(); +				ifTaken |= maskLevel(); +			} else { +				state |= maskLevel(); +				ifTaken &= ~maskLevel(); +			} +		} +	} +	void EndSection() { +		if (ValidLevel()) { +			state &= ~maskLevel(); +			ifTaken &= ~maskLevel(); +		} +		level--; +	} +	void InvertCurrentLevel() { +		if (ValidLevel()) { +			state ^= maskLevel(); +			ifTaken |= maskLevel(); +		} +	} +}; + +// Hold the preprocessor state for each line seen. +// Currently one entry per line but could become sparse with just one entry per preprocessor line. +class PPStates { +	std::vector<LinePPState> vlls; +public: +	LinePPState ForLine(int line) const { +		if ((line > 0) && (vlls.size() > static_cast<size_t>(line))) { +			return vlls[line]; +		} else { +			return LinePPState(); +		} +	} +	void Add(int line, LinePPState lls) { +		vlls.resize(line+1); +		vlls[line] = lls; +	} +}; + +// Options used for LexerVerilog +struct OptionsVerilog { +	bool foldComment; +	bool foldPreprocessor; +	bool foldCompact; +	bool foldAtElse; +	bool foldAtModule; +	bool trackPreprocessor; +	bool updatePreprocessor; +	OptionsVerilog() { +		foldComment = false; +		foldPreprocessor = false; +		foldCompact = false; +		foldAtElse = false; +		foldAtModule = false; +		// for backwards compatibility, preprocessor functionality is disabled by default +		trackPreprocessor = false; +		updatePreprocessor = false; +	} +}; + +struct OptionSetVerilog : public OptionSet<OptionsVerilog> { +	OptionSetVerilog() { +		DefineProperty("fold.comment", &OptionsVerilog::foldComment, +			"This option enables folding multi-line comments when using the Verilog lexer."); +		DefineProperty("fold.preprocessor", &OptionsVerilog::foldPreprocessor, +			"This option enables folding preprocessor directives when using the Verilog lexer."); +		DefineProperty("fold.compact", &OptionsVerilog::foldCompact); +		DefineProperty("fold.at.else", &OptionsVerilog::foldAtElse, +			"This option enables folding on the else line of an if statement."); +		DefineProperty("fold.verilog.flags", &OptionsVerilog::foldAtModule, +			"This option enables folding module definitions. Typically source files " +			"contain only one module definition so this option is somewhat useless."); +		DefineProperty("lexer.verilog.track.preprocessor", &OptionsVerilog::trackPreprocessor, +			"Set to 1 to interpret `if/`else/`endif to grey out code that is not active."); +		DefineProperty("lexer.verilog.update.preprocessor", &OptionsVerilog::updatePreprocessor, +			"Set to 1 to update preprocessor definitions when `define, `undef, or `undefineall found."); +	} +}; + +const char styleSubable[] = {0}; + +} + +class LexerVerilog : public ILexerWithSubStyles { +	CharacterSet setWord; +	WordList keywords; +	WordList keywords2; +	WordList keywords3; +	WordList keywords4; +	WordList keywords5; +	WordList ppDefinitions; +	PPStates vlls; +	std::vector<PPDefinition> ppDefineHistory; +	struct SymbolValue { +		std::string value; +		std::string arguments; +		SymbolValue(const std::string &value_="", const std::string &arguments_="") : value(value_), arguments(arguments_) { +		} +		SymbolValue &operator = (const std::string &value_) { +			value = value_; +			arguments.clear(); +			return *this; +		} +		bool IsMacro() const { +			return !arguments.empty(); +		} +	}; +	typedef std::map<std::string, SymbolValue> SymbolTable; +	SymbolTable preprocessorDefinitionsStart; +	OptionsVerilog options; +	OptionSetVerilog osVerilog; +	enum { activeFlag = 0x40 }; +	SubStyles subStyles; +public: +	LexerVerilog() : +		setWord(CharacterSet::setAlphaNum, "._", 0x80, true), +		subStyles(styleSubable, 0x80, 0x40, activeFlag) { +		} +	virtual ~LexerVerilog() {} +	int SCI_METHOD Version() const { +		return lvSubStyles; +	} +	void SCI_METHOD Release() { +		delete this; +	} +	const char* SCI_METHOD PropertyNames() { +		return osVerilog.PropertyNames(); +	} +	int SCI_METHOD PropertyType(const char* name) { +		return osVerilog.PropertyType(name); +	} +	const char* SCI_METHOD DescribeProperty(const char* name) { +		return osVerilog.DescribeProperty(name); +	} +	int SCI_METHOD PropertySet(const char* key, const char* val) { +	    return osVerilog.PropertySet(&options, key, val); +	} +	const char* SCI_METHOD DescribeWordListSets() { +		return osVerilog.DescribeWordListSets(); +	} +	int SCI_METHOD WordListSet(int n, const char* wl); +	void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess); +	void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess); +	void* SCI_METHOD PrivateCall(int, void*) { +		return 0; +	} +	int SCI_METHOD LineEndTypesSupported() { +		return SC_LINE_END_TYPE_UNICODE; +	} +	int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) { +		return subStyles.Allocate(styleBase, numberStyles); +	} +	int SCI_METHOD SubStylesStart(int styleBase) { +		return subStyles.Start(styleBase); +	} +	int SCI_METHOD SubStylesLength(int styleBase) { +		return subStyles.Length(styleBase); +	} +	int SCI_METHOD StyleFromSubStyle(int subStyle) { +		int styleBase = subStyles.BaseStyle(MaskActive(subStyle)); +		int active = subStyle & activeFlag; +		return styleBase | active; +	} +	int SCI_METHOD PrimaryStyleFromStyle(int style) { +		return MaskActive(style); + 	} +	void SCI_METHOD FreeSubStyles() { +		subStyles.Free(); +	} +	void SCI_METHOD SetIdentifiers(int style, const char *identifiers) { +		subStyles.SetIdentifiers(style, identifiers); +	} +	int SCI_METHOD DistanceToSecondaryStyles() { +		return activeFlag; +	} +	const char * SCI_METHOD GetSubStyleBases() { +		return styleSubable; +	} +	static ILexer* LexerFactoryVerilog() { +		return new LexerVerilog(); +	} +	static int MaskActive(int style) { +		return style & ~activeFlag; +	} +	std::vector<std::string> Tokenize(const std::string &expr) const; +}; + +int SCI_METHOD LexerVerilog::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; +	case 3: +		wordListN = &keywords4; +		break; +	case 4: +		wordListN = &keywords5; +		break; +	case 5: +		wordListN = &ppDefinitions; +		break; +	} +	int firstModification = -1; +	if (wordListN) { +		WordList wlNew; +		wlNew.Set(wl); +		if (*wordListN != wlNew) { +			wordListN->Set(wl); +			firstModification = 0; +			if (n == 5) { +				// Rebuild preprocessorDefinitions +				preprocessorDefinitionsStart.clear(); +				for (int nDefinition = 0; nDefinition < ppDefinitions.Length(); nDefinition++) { +					const char *cpDefinition = ppDefinitions.WordAt(nDefinition); +					const char *cpEquals = strchr(cpDefinition, '='); +					if (cpEquals) { +						std::string name(cpDefinition, cpEquals - cpDefinition); +						std::string val(cpEquals+1); +						size_t bracket = name.find('('); +						size_t bracketEnd = name.find(')'); +						if ((bracket != std::string::npos) && (bracketEnd != std::string::npos)) { +							// Macro +							std::string args = name.substr(bracket + 1, bracketEnd - bracket - 1); +							name = name.substr(0, bracket); +							preprocessorDefinitionsStart[name] = SymbolValue(val, args); +						} else { +							preprocessorDefinitionsStart[name] = val; +						} +					} else { +						std::string name(cpDefinition); +						std::string val("1"); +						preprocessorDefinitionsStart[name] = val; +					} +				} +			} +		} +	} +	return firstModification; +} +  static inline bool IsAWordChar(const int ch) {  	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\''|| ch == '$');  } @@ -44,17 +337,43 @@ static inline bool AllUpperCase(const char *a) {  	return true;  } -static void ColouriseVerilogDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], -                            Accessor &styler) { +// Functor used to truncate history +struct After { +	int line; +	explicit After(int line_) : line(line_) {} +	bool operator()(PPDefinition &p) const { +		return p.line > line; +	} +}; + +static std::string GetRestOfLine(LexAccessor &styler, int start, bool allowSpace) { +	std::string restOfLine; +	int i =0; +	char ch = styler.SafeGetCharAt(start, '\n'); +	int endLine = styler.LineEnd(styler.GetLine(start)); +	while (((start+i) < endLine) && (ch != '\r')) { +		char chNext = styler.SafeGetCharAt(start + i + 1, '\n'); +		if (ch == '/' && (chNext == '/' || chNext == '*')) +			break; +		if (allowSpace || (ch != ' ')) +			restOfLine += ch; +		i++; +		ch = chNext; +	} +	return restOfLine; +} + +static bool IsSpaceOrTab(int ch) { +	return ch == ' ' || ch == '\t'; +} + +void SCI_METHOD LexerVerilog::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) +{ +	LexAccessor styler(pAccess);  	const int kwOther=0, kwDot=0x100, kwInput=0x200, kwOutput=0x300, kwInout=0x400;  	int lineState = kwOther; - -	WordList &keywords = *keywordlists[0]; -	WordList &keywords2 = *keywordlists[1]; -	WordList &keywords3 = *keywordlists[2]; -	WordList &keywords4 = *keywordlists[3]; -	WordList &keywords5 = *keywordlists[4]; +	bool continuationLine = false;  	int curLine = styler.GetLine(startPos);  	if (curLine > 0) lineState = styler.GetLineState(curLine - 1); @@ -63,158 +382,336 @@ static void ColouriseVerilogDoc(unsigned int startPos, int length, int initStyle  	if (initStyle == SCE_V_STRINGEOL)  		initStyle = SCE_V_DEFAULT; +	if ((MaskActive(initStyle) == SCE_V_PREPROCESSOR) || +			(MaskActive(initStyle) == SCE_V_COMMENTLINE) || +			(MaskActive(initStyle) == SCE_V_COMMENTLINEBANG)) { +		// Set continuationLine if last character of previous line is '\' +		if (curLine > 0) { +			int endLinePrevious = styler.LineEnd(curLine - 1); +			if (endLinePrevious > 0) { +				continuationLine = styler.SafeGetCharAt(endLinePrevious-1) == '\\'; +			} +		} +	} +  	StyleContext sc(startPos, length, initStyle, styler); +	LinePPState preproc = vlls.ForLine(curLine); + +	bool definitionsChanged = false; + +	// Truncate ppDefineHistory before current line + +	if (!options.updatePreprocessor) +		ppDefineHistory.clear(); + +	std::vector<PPDefinition>::iterator itInvalid = std::find_if(ppDefineHistory.begin(), ppDefineHistory.end(), After(curLine-1)); +	if (itInvalid != ppDefineHistory.end()) { +		ppDefineHistory.erase(itInvalid, ppDefineHistory.end()); +		definitionsChanged = true; +	} + +	SymbolTable preprocessorDefinitions = preprocessorDefinitionsStart; +	for (std::vector<PPDefinition>::iterator itDef = ppDefineHistory.begin(); itDef != ppDefineHistory.end(); ++itDef) { +		if (itDef->isUndef) +			preprocessorDefinitions.erase(itDef->key); +		else +			preprocessorDefinitions[itDef->key] = SymbolValue(itDef->value, itDef->arguments); +	} + +	int activitySet = preproc.IsInactive() ? activeFlag : 0; +	int lineEndNext = styler.LineEnd(curLine);  	for (; sc.More(); sc.Forward()) { -		curLine = styler.GetLine(sc.currentPos); +		if (sc.atLineStart) { +			if (sc.state == SCE_V_STRING) { +				// Prevent SCE_V_STRINGEOL from leaking back to previous line +				sc.SetState(SCE_V_STRING); +			} +			if ((MaskActive(sc.state) == SCE_V_PREPROCESSOR) && (!continuationLine)) { +				sc.SetState(SCE_V_DEFAULT|activitySet); +			} +			if (preproc.IsInactive()) { +				activitySet = activeFlag; +				sc.SetState(sc.state | activitySet); +			} +		}  		if (sc.atLineEnd) { +			curLine++; +			lineEndNext = styler.LineEnd(curLine); +			vlls.Add(curLine, preproc);  			// Update the line state, so it can be seen by next line  			styler.SetLineState(curLine, lineState);  		} -		if (sc.atLineStart && (sc.state == SCE_V_STRING)) { -			// Prevent SCE_V_STRINGEOL from leaking back to previous line -			sc.SetState(SCE_V_STRING); -		} -  		// Handle line continuation generically.  		if (sc.ch == '\\') { -			if (sc.chNext == '\n' || sc.chNext == '\r') { +			if (static_cast<int>((sc.currentPos+1)) >= lineEndNext) { +				curLine++; +				lineEndNext = styler.LineEnd(curLine); +				vlls.Add(curLine, preproc); +				// Update the line state, so it can be seen by next line +				styler.SetLineState(curLine, lineState);  				sc.Forward();  				if (sc.ch == '\r' && sc.chNext == '\n') { +					// Even in UTF-8, \r and \n are separate  					sc.Forward();  				} +				continuationLine = true; +				sc.Forward();  				continue;  			}  		} -		// for comment keyword -		if (sc.state == SCE_V_COMMENT_WORD && !IsAWordChar(sc.ch)) { -			char s[100]; -			int state = lineState & 0xff; -			sc.GetCurrent(s, sizeof(s)); -			if (keywords5.InList(s)) { -				sc.ChangeState(SCE_V_COMMENT_WORD); -			} else { -				sc.ChangeState(state); -			} -			sc.SetState(state); -		} +		const bool atLineEndBeforeSwitch = sc.atLineEnd; +  		// Determine if the current state should terminate. -		if (sc.state == SCE_V_OPERATOR) { -			sc.SetState(SCE_V_DEFAULT); -		} else if (sc.state == SCE_V_NUMBER) { -			if (!(IsAWordChar(sc.ch) || (sc.ch == '?'))) { -				sc.SetState(SCE_V_DEFAULT); -			} -		} else if (sc.state == SCE_V_IDENTIFIER) { -			if (!IsAWordChar(sc.ch) || (sc.ch == '.')) { -				char s[100]; -				lineState &= 0xff00; - -				sc.GetCurrent(s, sizeof(s)); - -				if (strcmp(s, "input") == 0) { -					lineState = kwInput; -					sc.ChangeState(SCE_V_INPUT); -				} else if (strcmp(s, "output") == 0) { -					lineState = kwOutput; -					sc.ChangeState(SCE_V_OUTPUT); -				} else if (strcmp(s, "inout") == 0) { -					lineState = kwInout; -					sc.ChangeState(SCE_V_INOUT); - -				} else if (lineState == kwInput) { -					sc.ChangeState(SCE_V_INPUT); -				} else if (lineState == kwOutput) { -					sc.ChangeState(SCE_V_OUTPUT); -				} else if (lineState == kwInout) { -					sc.ChangeState(SCE_V_INOUT); -				} else if (lineState == kwDot) { -					lineState = kwOther; -					sc.ChangeState(SCE_V_PORT_CONNECT); - -				} else if (keywords.InList(s)) { -					sc.ChangeState(SCE_V_WORD); -				} else if (keywords2.InList(s)) { -					sc.ChangeState(SCE_V_WORD2); -				} else if (keywords3.InList(s)) { -					sc.ChangeState(SCE_V_WORD3); -				} else if (keywords4.InList(s)) { -					sc.ChangeState(SCE_V_USER); - -				} else if (AllUpperCase(s)) { -					sc.ChangeState(SCE_V_USER); +		switch (MaskActive(sc.state)) { +			case SCE_V_COMMENT_WORD: +				// for comment keyword +				if (!IsAWordChar(sc.ch)) { +					char s[100]; +					int state = lineState & 0xff; +					sc.GetCurrent(s, sizeof(s)); +					if (keywords5.InList(s)) { +						sc.ChangeState(SCE_V_COMMENT_WORD|activitySet); +					} else { +						sc.ChangeState(state|activitySet); +					} +				    sc.SetState(state|activitySet);  				} - -				sc.SetState(SCE_V_DEFAULT); -			} -		} else if (sc.state == SCE_V_PREPROCESSOR) { -			if (!IsAWordChar(sc.ch)) { -				sc.SetState(SCE_V_DEFAULT); -			} -		} else if (sc.state == SCE_V_COMMENT) { -			if (sc.Match('*', '/')) { -				sc.Forward(); -				sc.ForwardSetState(SCE_V_DEFAULT); -			} else if (IsAWordStart(sc.ch)) { -				lineState = sc.state | (lineState & 0xff00); -				sc.SetState(SCE_V_COMMENT_WORD); -			} -		} else if (sc.state == SCE_V_COMMENTLINE || sc.state == SCE_V_COMMENTLINEBANG) { -			if (sc.atLineStart) { -				sc.SetState(SCE_V_DEFAULT); -			} else if (IsAWordStart(sc.ch)) { -				lineState = sc.state | (lineState & 0xff00); -				sc.SetState(SCE_V_COMMENT_WORD); -			} -		} else if (sc.state == SCE_V_STRING) { -			if (sc.ch == '\\') { -				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { +				break; +			case SCE_V_OPERATOR: +				sc.SetState(SCE_V_DEFAULT|activitySet); +				break; +			case SCE_V_NUMBER: +				if (!(IsAWordChar(sc.ch) || (sc.ch == '?'))) { +					sc.SetState(SCE_V_DEFAULT|activitySet); +				} +				break; +			case SCE_V_IDENTIFIER: +				if (!IsAWordChar(sc.ch) || (sc.ch == '.')) { +					char s[100]; +					lineState &= 0xff00; +					sc.GetCurrent(s, sizeof(s)); +					if (strcmp(s, "input") == 0) { +						lineState = kwInput; +						sc.ChangeState(SCE_V_INPUT|activitySet); +					} else if (strcmp(s, "output") == 0) { +						lineState = kwOutput; +						sc.ChangeState(SCE_V_OUTPUT|activitySet); +					} else if (strcmp(s, "inout") == 0) { +						lineState = kwInout; +						sc.ChangeState(SCE_V_INOUT|activitySet); +					} else if (lineState == kwInput) { +						sc.ChangeState(SCE_V_INPUT|activitySet); +					} else if (lineState == kwOutput) { +						sc.ChangeState(SCE_V_OUTPUT|activitySet); +					} else if (lineState == kwInout) { +						sc.ChangeState(SCE_V_INOUT|activitySet); +					} else if (lineState == kwDot) { +						lineState = kwOther; +						sc.ChangeState(SCE_V_PORT_CONNECT|activitySet); +					} else if (keywords.InList(s)) { +						sc.ChangeState(SCE_V_WORD|activitySet); +					} else if (keywords2.InList(s)) { +						sc.ChangeState(SCE_V_WORD2|activitySet); +					} else if (keywords3.InList(s)) { +						sc.ChangeState(SCE_V_WORD3|activitySet); +					} else if (keywords4.InList(s)) { +						sc.ChangeState(SCE_V_USER|activitySet); +					} else if (AllUpperCase(s)) { +						sc.ChangeState(SCE_V_USER|activitySet); +					} +					sc.SetState(SCE_V_DEFAULT|activitySet); +				} +				break; +			case SCE_V_PREPROCESSOR: +				if (!IsAWordChar(sc.ch) && !sc.atLineEnd) { +					sc.SetState(SCE_V_DEFAULT|activitySet); +				} +				break; +			case SCE_V_COMMENT: +				if (sc.Match('*', '/')) {  					sc.Forward(); +					sc.ForwardSetState(SCE_V_DEFAULT|activitySet); +				} else if (IsAWordStart(sc.ch)) { +					lineState = sc.state | (lineState & 0xff00); +					sc.SetState(SCE_V_COMMENT_WORD|activitySet);  				} -			} else if (sc.ch == '\"') { -				sc.ForwardSetState(SCE_V_DEFAULT); -			} else if (sc.atLineEnd) { -				sc.ChangeState(SCE_V_STRINGEOL); -				sc.ForwardSetState(SCE_V_DEFAULT); -			} +				break; +			case SCE_V_COMMENTLINE: +			case SCE_V_COMMENTLINEBANG: +				if (sc.atLineStart) { +					sc.SetState(SCE_V_DEFAULT|activitySet); +				} else if (IsAWordStart(sc.ch)) { +					lineState = sc.state | (lineState & 0xff00); +					sc.SetState(SCE_V_COMMENT_WORD|activitySet); +				} +				break; +			case SCE_V_STRING: +				if (sc.ch == '\\') { +					if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') { +						sc.Forward(); +					} +				} else if (sc.ch == '\"') { +					sc.ForwardSetState(SCE_V_DEFAULT|activitySet); +				} else if (sc.atLineEnd) { +					sc.ChangeState(SCE_V_STRINGEOL|activitySet); +					sc.ForwardSetState(SCE_V_DEFAULT|activitySet); +				} +				break; +		} + +		if (sc.atLineEnd && !atLineEndBeforeSwitch) { +			// State exit processing consumed characters up to end of line. +			curLine++; +			lineEndNext = styler.LineEnd(curLine); +			vlls.Add(curLine, preproc); +			// Update the line state, so it can be seen by next line +			styler.SetLineState(curLine, lineState);  		}  		// Determine if a new state should be entered. -		if (sc.state == SCE_V_DEFAULT) { +		if (MaskActive(sc.state) == SCE_V_DEFAULT) {  			if (IsADigit(sc.ch) || (sc.ch == '\'') || (sc.ch == '.' && IsADigit(sc.chNext))) { -				sc.SetState(SCE_V_NUMBER); +				sc.SetState(SCE_V_NUMBER|activitySet);  			} else if (IsAWordStart(sc.ch)) { -				sc.SetState(SCE_V_IDENTIFIER); +				sc.SetState(SCE_V_IDENTIFIER|activitySet);  			} else if (sc.Match('/', '*')) { -				sc.SetState(SCE_V_COMMENT); +				sc.SetState(SCE_V_COMMENT|activitySet);  				sc.Forward();	// Eat the * so it isn't used for the end of the comment  			} else if (sc.Match('/', '/')) {  				if (sc.Match("//!"))	// Nice to have a different comment style -					sc.SetState(SCE_V_COMMENTLINEBANG); +					sc.SetState(SCE_V_COMMENTLINEBANG|activitySet);  				else -					sc.SetState(SCE_V_COMMENTLINE); +					sc.SetState(SCE_V_COMMENTLINE|activitySet);  			} else if (sc.ch == '\"') { -				sc.SetState(SCE_V_STRING); +				sc.SetState(SCE_V_STRING|activitySet);  			} else if (sc.ch == '`') { -				sc.SetState(SCE_V_PREPROCESSOR); +				sc.SetState(SCE_V_PREPROCESSOR|activitySet);  				// Skip whitespace between ` and preprocessor word  				do {  					sc.Forward();  				} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());  				if (sc.atLineEnd) { -					sc.SetState(SCE_V_DEFAULT); +					sc.SetState(SCE_V_DEFAULT|activitySet);  					styler.SetLineState(curLine, lineState); +				} else { +					if (options.trackPreprocessor) { +						if (sc.Match("ifdef") || sc.Match("ifndef")) { +							bool isIfDef = sc.Match("ifdef"); +							int i = isIfDef ? 5 : 6; +							std::string restOfLine = GetRestOfLine(styler, sc.currentPos + i + 1, false); +							bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end(); +							preproc.StartSection(isIfDef == foundDef); +						} else if (sc.Match("else")) { +							if (!preproc.CurrentIfTaken()) { +								preproc.InvertCurrentLevel(); +								activitySet = preproc.IsInactive() ? activeFlag : 0; +								if (!activitySet) { +									sc.ChangeState(SCE_V_PREPROCESSOR|activitySet); +								} +							} else if (!preproc.IsInactive()) { +								preproc.InvertCurrentLevel(); +								activitySet = preproc.IsInactive() ? activeFlag : 0; +								if (!activitySet) { +									sc.ChangeState(SCE_V_PREPROCESSOR|activitySet); +								} +							} +						} else if (sc.Match("elsif")) { +							// Ensure only one chosen out of `if .. `elsif .. `elsif .. `else .. `endif +							if (!preproc.CurrentIfTaken()) { +								// Similar to `ifdef +								std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true); +								bool ifGood = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end(); +								if (ifGood) { +									preproc.InvertCurrentLevel(); +									activitySet = preproc.IsInactive() ? activeFlag : 0; +									if (!activitySet) +										sc.ChangeState(SCE_V_PREPROCESSOR|activitySet); +								} +							} else if (!preproc.IsInactive()) { +								preproc.InvertCurrentLevel(); +								activitySet = preproc.IsInactive() ? activeFlag : 0; +								if (!activitySet) +									sc.ChangeState(SCE_V_PREPROCESSOR|activitySet); +							} +						} else if (sc.Match("endif")) { +							preproc.EndSection(); +							activitySet = preproc.IsInactive() ? activeFlag : 0; +							sc.ChangeState(SCE_V_PREPROCESSOR|activitySet); +						} else if (sc.Match("define")) { +							if (options.updatePreprocessor && !preproc.IsInactive()) { +								std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true); +								size_t startName = 0; +								while ((startName < restOfLine.length()) && IsSpaceOrTab(restOfLine[startName])) +									startName++; +								size_t endName = startName; +								while ((endName < restOfLine.length()) && setWord.Contains(static_cast<unsigned char>(restOfLine[endName]))) +									endName++; +								std::string key = restOfLine.substr(startName, endName-startName); +								if ((endName < restOfLine.length()) && (restOfLine.at(endName) == '(')) { +									// Macro +									size_t endArgs = endName; +									while ((endArgs < restOfLine.length()) && (restOfLine[endArgs] != ')')) +										endArgs++; +									std::string args = restOfLine.substr(endName + 1, endArgs - endName - 1); +									size_t startValue = endArgs+1; +									while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue])) +										startValue++; +									std::string value; +									if (startValue < restOfLine.length()) +										value = restOfLine.substr(startValue); +									preprocessorDefinitions[key] = SymbolValue(value, args); +									ppDefineHistory.push_back(PPDefinition(curLine, key, value, false, args)); +									definitionsChanged = true; +								} else { +									// Value +									size_t startValue = endName; +									while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue])) +										startValue++; +									std::string value = restOfLine.substr(startValue); +									preprocessorDefinitions[key] = value; +									ppDefineHistory.push_back(PPDefinition(curLine, key, value)); +									definitionsChanged = true; +								} +							} +						} else if (sc.Match("undefineall")) { +							if (options.updatePreprocessor && !preproc.IsInactive()) { +								// remove all preprocessor definitions +								std::map<std::string, SymbolValue>::iterator itDef; +								for(itDef = preprocessorDefinitions.begin(); itDef != preprocessorDefinitions.end(); ++itDef) { +									ppDefineHistory.push_back(PPDefinition(curLine, itDef->first, "", true)); +								} +								preprocessorDefinitions.clear(); +								definitionsChanged = true; +							} +						} else if (sc.Match("undef")) { +							if (options.updatePreprocessor && !preproc.IsInactive()) { +								std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 5, true); +								std::vector<std::string> tokens = Tokenize(restOfLine); +								std::string key; +								if (tokens.size() >= 1) { +									key = tokens[0]; +									preprocessorDefinitions.erase(key); +									ppDefineHistory.push_back(PPDefinition(curLine, key, "", true)); +									definitionsChanged = true; +								} +							} +						} +					}  				}  			} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '#') { -				sc.SetState(SCE_V_OPERATOR); +				sc.SetState(SCE_V_OPERATOR|activitySet);  				if (sc.ch == '.') lineState = kwDot;  				if (sc.ch == ';') lineState = kwOther;  			}  		}  	} +	if (definitionsChanged) { +		styler.ChangeLexerState(startPos, startPos + length); +	}  	sc.Complete();  } @@ -222,7 +719,7 @@ static bool IsStreamCommentStyle(int style) {  	return style == SCE_V_COMMENT;  } -static bool IsCommentLine(int line, Accessor &styler) { +static bool IsCommentLine(int line, LexAccessor &styler) {  	int pos = styler.LineStart(line);  	int eolPos = styler.LineStart(line + 1) - 1;  	for (int i = pos; i < eolPos; i++) { @@ -238,22 +735,13 @@ static bool IsCommentLine(int line, Accessor &styler) {  	}  	return false;  } +  // Store both the current line's fold level and the next lines in the  // level store to make it easy to pick up with each increment  // and to make it possible to fiddle the current level for "} else {". -static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle, -                            Accessor &styler) { -	bool foldComment = styler.GetPropertyInt("fold.comment") != 0; -	bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0; -	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0; -	bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0; -	// Verilog specific folding options: -	// fold_at_module - -	//      Generally used methodology in verilog code is -	//      one module per file, so folding at module definition is useless. -	// fold_at_brace/parenthese - -	//      Folding of long port lists can be convenient. -	bool foldAtModule = styler.GetPropertyInt("fold.verilog.flags", 0) != 0; +void SCI_METHOD LexerVerilog::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) +{ +	LexAccessor styler(pAccess);  	bool foldAtBrace  = 1;  	bool foldAtParenthese  = 1; @@ -266,16 +754,16 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  	int levelMinCurrent = levelCurrent;  	int levelNext = levelCurrent;  	char chNext = styler[startPos]; -	int styleNext = styler.StyleAt(startPos); -	int style = initStyle; +	int styleNext = MaskActive(styler.StyleAt(startPos)); +	int style = MaskActive(initStyle);  	for (unsigned int i = startPos; i < endPos; i++) {  		char ch = chNext;  		chNext = styler.SafeGetCharAt(i + 1);  		int stylePrev = style;  		style = styleNext; -		styleNext = styler.StyleAt(i + 1); +		styleNext = MaskActive(styler.StyleAt(i + 1));  		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); -		if (foldComment && IsStreamCommentStyle(style)) { +		if (options.foldComment && IsStreamCommentStyle(style)) {  			if (!IsStreamCommentStyle(stylePrev)) {  				levelNext++;  			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) { @@ -283,7 +771,7 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  				levelNext--;  			}  		} -		if (foldComment && atEOL && IsCommentLine(lineCurrent, styler)) +		if (options.foldComment && atEOL && IsCommentLine(lineCurrent, styler))  		{  			if (!IsCommentLine(lineCurrent - 1, styler)  			    && IsCommentLine(lineCurrent + 1, styler)) @@ -292,7 +780,7 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  			         && !IsCommentLine(lineCurrent+1, styler))  				levelNext--;  		} -		if (foldComment && (style == SCE_V_COMMENTLINE)) { +		if (options.foldComment && (style == SCE_V_COMMENTLINE)) {  			if ((ch == '/') && (chNext == '/')) {  				char chNext2 = styler.SafeGetCharAt(i + 2);  				if (chNext2 == '{') { @@ -302,7 +790,7 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  				}  			}  		} -		if (foldPreprocessor && (style == SCE_V_PREPROCESSOR)) { +		if (options.foldPreprocessor && (style == SCE_V_PREPROCESSOR)) {  			if (ch == '`') {  				unsigned int j = i + 1;  				while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) { @@ -350,7 +838,7 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  				styler.Match(j, "table") ||  				styler.Match(j, "task") ||  				styler.Match(j, "fork") || -				(styler.Match(j, "module") && foldAtModule) || +				(styler.Match(j, "module") && options.foldAtModule) ||  				styler.Match(j, "begin")) {  				levelNext++;  			} else if (styler.Match(j, "endcase") || @@ -368,18 +856,18 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  				styler.Match(j, "join") ||  				styler.Match(j, "join_any") ||  				styler.Match(j, "join_none") || -				(styler.Match(j, "endmodule") && foldAtModule) || +				(styler.Match(j, "endmodule") && options.foldAtModule) ||  				(styler.Match(j, "end") && !IsAWordChar(styler.SafeGetCharAt(j + 3)))) {  				levelNext--;  			}  		}  		if (atEOL) {  			int levelUse = levelCurrent; -			if (foldAtElse) { +			if (options.foldAtElse) {  				levelUse = levelMinCurrent;  			}  			int lev = levelUse | levelNext << 16; -			if (visibleChars == 0 && foldCompact) +			if (visibleChars == 0 && options.foldCompact)  				lev |= SC_FOLDLEVELWHITEFLAG;  			if (levelUse < levelNext)  				lev |= SC_FOLDLEVELHEADERFLAG; @@ -396,9 +884,31 @@ static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle  	}  } -static void FoldVerilogDoc(unsigned int startPos, int length, int initStyle, WordList *[], -                       Accessor &styler) { -	FoldNoBoxVerilogDoc(startPos, length, initStyle, styler); +std::vector<std::string> LexerVerilog::Tokenize(const std::string &expr) const { +	// Break into tokens +	std::vector<std::string> tokens; +	const char *cp = expr.c_str(); +	while (*cp) { +		std::string word; +		if (setWord.Contains(static_cast<unsigned char>(*cp))) { +			// Identifiers and numbers +			while (setWord.Contains(static_cast<unsigned char>(*cp))) { +				word += *cp; +				cp++; +			} +		} else if (IsSpaceOrTab(*cp)) { +			while (IsSpaceOrTab(*cp)) { +				cp++; +			} +			continue; +		} else { +			// Should handle strings, characters, and comments here +			word += *cp; +			cp++; +		} +		tokens.push_back(word); +	} +	return tokens;  }  static const char * const verilogWordLists[] = { @@ -407,8 +917,8 @@ static const char * const verilogWordLists[] = {              "System Tasks",              "User defined tasks and identifiers",              "Documentation comment keywords", +            "Preprocessor definitions",              0,          }; - -LexerModule lmVerilog(SCLEX_VERILOG, ColouriseVerilogDoc, "verilog", FoldVerilogDoc, verilogWordLists); +LexerModule lmVerilog(SCLEX_VERILOG, LexerVerilog::LexerFactoryVerilog, "verilog", verilogWordLists); | 
