void SCI_METHOD LexerVerilog::Lex()

in ext/scintilla/lexers/LexVerilog.cxx [397:764]


void SCI_METHOD LexerVerilog::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess)
{
	LexAccessor styler(pAccess);

	const int kwOther=0, kwDot=0x100, kwInput=0x200, kwOutput=0x300, kwInout=0x400, kwProtected=0x800;
	int lineState = kwOther;
	bool continuationLine = false;

	Sci_Position curLine = styler.GetLine(startPos);
	if (curLine > 0) lineState = styler.GetLineState(curLine - 1);

	// Do not leak onto next line
	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) {
			Sci_Position 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;
	Sci_Position lineEndNext = styler.LineEnd(curLine);
	bool isEscapedId = false;    // true when parsing an escaped Identifier
	bool isProtected = (lineState&kwProtected) != 0;	// true when parsing a protected region

	for (; sc.More(); sc.Forward()) {
		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);
			isEscapedId = false;    // EOL terminates an escaped Identifier
		}

		// Handle line continuation generically.
		if (sc.ch == '\\') {
			if (static_cast<Sci_Position>((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 (MaskActive(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|activitySet);
			} else {
				sc.ChangeState(state|activitySet);
			}
			sc.SetState(state|activitySet);
		}

		const bool atLineEndBeforeSwitch = sc.atLineEnd;

		// Determine if the current state should terminate.
		switch (MaskActive(sc.state)) {
			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 (!isEscapedId &&(!IsAWordChar(sc.ch) || (sc.ch == '.'))) {
					char s[100];
					lineState &= 0xff00;
					sc.GetCurrent(s, sizeof(s));
					if (options.portStyling && (strcmp(s, "input") == 0)) {
						lineState = kwInput;
						sc.ChangeState(SCE_V_INPUT|activitySet);
					} else if (options.portStyling && (strcmp(s, "output") == 0)) {
						lineState = kwOutput;
						sc.ChangeState(SCE_V_OUTPUT|activitySet);
					} else if (options.portStyling && (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;
						if (options.portStyling)
							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 (options.allUppercaseDocKeyword && 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);
				}
				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);
			isEscapedId = false;    // EOL terminates an escaped Identifier
		}

		// Determine if a new state should be entered.
		if (MaskActive(sc.state) == SCE_V_DEFAULT) {
			if (sc.ch == '`') {
				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|activitySet);
					styler.SetLineState(curLine, lineState);
				} else {
					if (sc.Match("protected")) {
						isProtected = true;
						lineState |= kwProtected;
						styler.SetLineState(curLine, lineState);
					} else if (sc.Match("endprotected")) {
						isProtected = false;
						lineState &= ~kwProtected;
						styler.SetLineState(curLine, lineState);
					} else if (!isProtected && 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 (!isProtected) {
				if (IsADigit(sc.ch) || (sc.ch == '\'') || (sc.ch == '.' && IsADigit(sc.chNext))) {
					sc.SetState(SCE_V_NUMBER|activitySet);
				} else if (IsAWordStart(sc.ch)) {
					sc.SetState(SCE_V_IDENTIFIER|activitySet);
				} else if (sc.Match('/', '*')) {
					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|activitySet);
					else
						sc.SetState(SCE_V_COMMENTLINE|activitySet);
				} else if (sc.ch == '\"') {
					sc.SetState(SCE_V_STRING|activitySet);
				} else if (sc.ch == '\\') {
					// escaped identifier, everything is ok up to whitespace
					isEscapedId = true;
					sc.SetState(SCE_V_IDENTIFIER|activitySet);
				} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '#') {
					sc.SetState(SCE_V_OPERATOR|activitySet);
					if (sc.ch == '.') lineState = kwDot;
					if (sc.ch == ';') lineState = kwOther;
				}
			}
		}
		if (isEscapedId && isspacechar(sc.ch)) {
			isEscapedId = false;
		}
	}
	if (definitionsChanged) {
		styler.ChangeLexerState(startPos, startPos + length);
	}
	sc.Complete();
}