Scintilla 4.0.3
[TortoiseGit.git] / ext / scintilla / lexers / LexVerilog.cxx
blobe2686dd662042cf94d8afff1c98e864687d53504
1 // Scintilla source code edit control
2 /** @file LexVerilog.cxx
3 ** Lexer for Verilog.
4 ** Written by Avi Yegudin, based on C++ lexer by Neil Hodgson
5 **/
6 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
7 // The License.txt file describes the conditions under which this software may be distributed.
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <assert.h>
14 #include <ctype.h>
16 #include <string>
17 #include <vector>
18 #include <map>
19 #include <algorithm>
21 #include "ILexer.h"
22 #include "Scintilla.h"
23 #include "SciLexer.h"
25 #include "WordList.h"
26 #include "LexAccessor.h"
27 #include "Accessor.h"
28 #include "StyleContext.h"
29 #include "CharacterSet.h"
30 #include "LexerModule.h"
32 #include "OptionSet.h"
33 #include "SubStyles.h"
34 #include "DefaultLexer.h"
36 using namespace Scintilla;
38 namespace {
39 // Use an unnamed namespace to protect the functions and classes from name conflicts
41 struct PPDefinition {
42 Sci_Position line;
43 std::string key;
44 std::string value;
45 bool isUndef;
46 std::string arguments;
47 PPDefinition(Sci_Position line_, const std::string &key_, const std::string &value_, bool isUndef_ = false, std::string arguments_="") :
48 line(line_), key(key_), value(value_), isUndef(isUndef_), arguments(arguments_) {
52 class LinePPState {
53 int state;
54 int ifTaken;
55 int level;
56 bool ValidLevel() const {
57 return level >= 0 && level < 32;
59 int maskLevel() const {
60 return 1 << level;
62 public:
63 LinePPState() : state(0), ifTaken(0), level(-1) {
65 bool IsInactive() const {
66 return state != 0;
68 bool CurrentIfTaken() const {
69 return (ifTaken & maskLevel()) != 0;
71 void StartSection(bool on) {
72 level++;
73 if (ValidLevel()) {
74 if (on) {
75 state &= ~maskLevel();
76 ifTaken |= maskLevel();
77 } else {
78 state |= maskLevel();
79 ifTaken &= ~maskLevel();
83 void EndSection() {
84 if (ValidLevel()) {
85 state &= ~maskLevel();
86 ifTaken &= ~maskLevel();
88 level--;
90 void InvertCurrentLevel() {
91 if (ValidLevel()) {
92 state ^= maskLevel();
93 ifTaken |= maskLevel();
98 // Hold the preprocessor state for each line seen.
99 // Currently one entry per line but could become sparse with just one entry per preprocessor line.
100 class PPStates {
101 std::vector<LinePPState> vlls;
102 public:
103 LinePPState ForLine(Sci_Position line) const {
104 if ((line > 0) && (vlls.size() > static_cast<size_t>(line))) {
105 return vlls[line];
106 } else {
107 return LinePPState();
110 void Add(Sci_Position line, LinePPState lls) {
111 vlls.resize(line+1);
112 vlls[line] = lls;
116 // Options used for LexerVerilog
117 struct OptionsVerilog {
118 bool foldComment;
119 bool foldPreprocessor;
120 bool foldPreprocessorElse;
121 bool foldCompact;
122 bool foldAtElse;
123 bool foldAtModule;
124 bool trackPreprocessor;
125 bool updatePreprocessor;
126 bool portStyling;
127 bool allUppercaseDocKeyword;
128 OptionsVerilog() {
129 foldComment = false;
130 foldPreprocessor = false;
131 foldPreprocessorElse = false;
132 foldCompact = false;
133 foldAtElse = false;
134 foldAtModule = false;
135 // for backwards compatibility, preprocessor functionality is disabled by default
136 trackPreprocessor = false;
137 updatePreprocessor = false;
138 // for backwards compatibility, treat input/output/inout as regular keywords
139 portStyling = false;
140 // for backwards compatibility, don't treat all uppercase identifiers as documentation keywords
141 allUppercaseDocKeyword = false;
145 struct OptionSetVerilog : public OptionSet<OptionsVerilog> {
146 OptionSetVerilog() {
147 DefineProperty("fold.comment", &OptionsVerilog::foldComment,
148 "This option enables folding multi-line comments when using the Verilog lexer.");
149 DefineProperty("fold.preprocessor", &OptionsVerilog::foldPreprocessor,
150 "This option enables folding preprocessor directives when using the Verilog lexer.");
151 DefineProperty("fold.compact", &OptionsVerilog::foldCompact);
152 DefineProperty("fold.at.else", &OptionsVerilog::foldAtElse,
153 "This option enables folding on the else line of an if statement.");
154 DefineProperty("fold.verilog.flags", &OptionsVerilog::foldAtModule,
155 "This option enables folding module definitions. Typically source files "
156 "contain only one module definition so this option is somewhat useless.");
157 DefineProperty("lexer.verilog.track.preprocessor", &OptionsVerilog::trackPreprocessor,
158 "Set to 1 to interpret `if/`else/`endif to grey out code that is not active.");
159 DefineProperty("lexer.verilog.update.preprocessor", &OptionsVerilog::updatePreprocessor,
160 "Set to 1 to update preprocessor definitions when `define, `undef, or `undefineall found.");
161 DefineProperty("lexer.verilog.portstyling", &OptionsVerilog::portStyling,
162 "Set to 1 to style input, output, and inout ports differently from regular keywords.");
163 DefineProperty("lexer.verilog.allupperkeywords", &OptionsVerilog::allUppercaseDocKeyword,
164 "Set to 1 to style identifiers that are all uppercase as documentation keyword.");
165 DefineProperty("lexer.verilog.fold.preprocessor.else", &OptionsVerilog::foldPreprocessorElse,
166 "This option enables folding on `else and `elsif preprocessor directives.");
170 const char styleSubable[] = {0};
174 class LexerVerilog : public DefaultLexer {
175 CharacterSet setWord;
176 WordList keywords;
177 WordList keywords2;
178 WordList keywords3;
179 WordList keywords4;
180 WordList keywords5;
181 WordList ppDefinitions;
182 PPStates vlls;
183 std::vector<PPDefinition> ppDefineHistory;
184 struct SymbolValue {
185 std::string value;
186 std::string arguments;
187 SymbolValue(const std::string &value_="", const std::string &arguments_="") : value(value_), arguments(arguments_) {
189 SymbolValue &operator = (const std::string &value_) {
190 value = value_;
191 arguments.clear();
192 return *this;
194 bool IsMacro() const {
195 return !arguments.empty();
198 typedef std::map<std::string, SymbolValue> SymbolTable;
199 SymbolTable preprocessorDefinitionsStart;
200 OptionsVerilog options;
201 OptionSetVerilog osVerilog;
202 enum { activeFlag = 0x40 };
203 SubStyles subStyles;
205 // states at end of line (EOL) during fold operations:
206 // foldExternFlag: EOL while parsing an extern function/task declaration terminated by ';'
207 // foldWaitDisableFlag: EOL while parsing wait or disable statement, terminated by "fork" or '('
208 // typdefFlag: EOL while parsing typedef statement, terminated by ';'
209 enum {foldExternFlag = 0x01, foldWaitDisableFlag = 0x02, typedefFlag = 0x04, protectedFlag = 0x08};
210 // map using line number as key to store fold state information
211 std::map<Sci_Position, int> foldState;
213 public:
214 LexerVerilog() :
215 setWord(CharacterSet::setAlphaNum, "._", 0x80, true),
216 subStyles(styleSubable, 0x80, 0x40, activeFlag) {
218 virtual ~LexerVerilog() {}
219 int SCI_METHOD Version() const override {
220 return lvRelease4;
222 void SCI_METHOD Release() override {
223 delete this;
225 const char* SCI_METHOD PropertyNames() override {
226 return osVerilog.PropertyNames();
228 int SCI_METHOD PropertyType(const char* name) override {
229 return osVerilog.PropertyType(name);
231 const char* SCI_METHOD DescribeProperty(const char* name) override {
232 return osVerilog.DescribeProperty(name);
234 Sci_Position SCI_METHOD PropertySet(const char* key, const char* val) override {
235 return osVerilog.PropertySet(&options, key, val);
237 const char* SCI_METHOD DescribeWordListSets() override {
238 return osVerilog.DescribeWordListSets();
240 Sci_Position SCI_METHOD WordListSet(int n, const char* wl) override;
241 void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
242 void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
243 void* SCI_METHOD PrivateCall(int, void*) override {
244 return 0;
246 int SCI_METHOD LineEndTypesSupported() override {
247 return SC_LINE_END_TYPE_UNICODE;
249 int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) override {
250 return subStyles.Allocate(styleBase, numberStyles);
252 int SCI_METHOD SubStylesStart(int styleBase) override {
253 return subStyles.Start(styleBase);
255 int SCI_METHOD SubStylesLength(int styleBase) override {
256 return subStyles.Length(styleBase);
258 int SCI_METHOD StyleFromSubStyle(int subStyle) override {
259 int styleBase = subStyles.BaseStyle(MaskActive(subStyle));
260 int active = subStyle & activeFlag;
261 return styleBase | active;
263 int SCI_METHOD PrimaryStyleFromStyle(int style) override {
264 return MaskActive(style);
266 void SCI_METHOD FreeSubStyles() override {
267 subStyles.Free();
269 void SCI_METHOD SetIdentifiers(int style, const char *identifiers) override {
270 subStyles.SetIdentifiers(style, identifiers);
272 int SCI_METHOD DistanceToSecondaryStyles() override {
273 return activeFlag;
275 const char * SCI_METHOD GetSubStyleBases() override {
276 return styleSubable;
278 static ILexer4* LexerFactoryVerilog() {
279 return new LexerVerilog();
281 static int MaskActive(int style) {
282 return style & ~activeFlag;
284 std::vector<std::string> Tokenize(const std::string &expr) const;
287 Sci_Position SCI_METHOD LexerVerilog::WordListSet(int n, const char *wl) {
288 WordList *wordListN = 0;
289 switch (n) {
290 case 0:
291 wordListN = &keywords;
292 break;
293 case 1:
294 wordListN = &keywords2;
295 break;
296 case 2:
297 wordListN = &keywords3;
298 break;
299 case 3:
300 wordListN = &keywords4;
301 break;
302 case 4:
303 wordListN = &keywords5;
304 break;
305 case 5:
306 wordListN = &ppDefinitions;
307 break;
309 Sci_Position firstModification = -1;
310 if (wordListN) {
311 WordList wlNew;
312 wlNew.Set(wl);
313 if (*wordListN != wlNew) {
314 wordListN->Set(wl);
315 firstModification = 0;
316 if (n == 5) {
317 // Rebuild preprocessorDefinitions
318 preprocessorDefinitionsStart.clear();
319 for (int nDefinition = 0; nDefinition < ppDefinitions.Length(); nDefinition++) {
320 const char *cpDefinition = ppDefinitions.WordAt(nDefinition);
321 const char *cpEquals = strchr(cpDefinition, '=');
322 if (cpEquals) {
323 std::string name(cpDefinition, cpEquals - cpDefinition);
324 std::string val(cpEquals+1);
325 size_t bracket = name.find('(');
326 size_t bracketEnd = name.find(')');
327 if ((bracket != std::string::npos) && (bracketEnd != std::string::npos)) {
328 // Macro
329 std::string args = name.substr(bracket + 1, bracketEnd - bracket - 1);
330 name = name.substr(0, bracket);
331 preprocessorDefinitionsStart[name] = SymbolValue(val, args);
332 } else {
333 preprocessorDefinitionsStart[name] = val;
335 } else {
336 std::string name(cpDefinition);
337 std::string val("1");
338 preprocessorDefinitionsStart[name] = val;
344 return firstModification;
347 static inline bool IsAWordChar(const int ch) {
348 return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '\''|| ch == '$');
351 static inline bool IsAWordStart(const int ch) {
352 return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '$');
355 static inline bool AllUpperCase(const char *a) {
356 while (*a) {
357 if (*a >= 'a' && *a <= 'z') return false;
358 a++;
360 return true;
363 // Functor used to truncate history
364 struct After {
365 Sci_Position line;
366 explicit After(Sci_Position line_) : line(line_) {}
367 bool operator()(PPDefinition &p) const {
368 return p.line > line;
372 static std::string GetRestOfLine(LexAccessor &styler, Sci_Position start, bool allowSpace) {
373 std::string restOfLine;
374 Sci_Position i =0;
375 char ch = styler.SafeGetCharAt(start, '\n');
376 Sci_Position endLine = styler.LineEnd(styler.GetLine(start));
377 while (((start+i) < endLine) && (ch != '\r')) {
378 char chNext = styler.SafeGetCharAt(start + i + 1, '\n');
379 if (ch == '/' && (chNext == '/' || chNext == '*'))
380 break;
381 if (allowSpace || (ch != ' '))
382 restOfLine += ch;
383 i++;
384 ch = chNext;
386 return restOfLine;
389 static bool IsSpaceOrTab(int ch) {
390 return ch == ' ' || ch == '\t';
393 void SCI_METHOD LexerVerilog::Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess)
395 LexAccessor styler(pAccess);
397 const int kwOther=0, kwDot=0x100, kwInput=0x200, kwOutput=0x300, kwInout=0x400, kwProtected=0x800;
398 int lineState = kwOther;
399 bool continuationLine = false;
401 Sci_Position curLine = styler.GetLine(startPos);
402 if (curLine > 0) lineState = styler.GetLineState(curLine - 1);
404 // Do not leak onto next line
405 if (initStyle == SCE_V_STRINGEOL)
406 initStyle = SCE_V_DEFAULT;
408 if ((MaskActive(initStyle) == SCE_V_PREPROCESSOR) ||
409 (MaskActive(initStyle) == SCE_V_COMMENTLINE) ||
410 (MaskActive(initStyle) == SCE_V_COMMENTLINEBANG)) {
411 // Set continuationLine if last character of previous line is '\'
412 if (curLine > 0) {
413 Sci_Position endLinePrevious = styler.LineEnd(curLine - 1);
414 if (endLinePrevious > 0) {
415 continuationLine = styler.SafeGetCharAt(endLinePrevious-1) == '\\';
420 StyleContext sc(startPos, length, initStyle, styler);
421 LinePPState preproc = vlls.ForLine(curLine);
423 bool definitionsChanged = false;
425 // Truncate ppDefineHistory before current line
427 if (!options.updatePreprocessor)
428 ppDefineHistory.clear();
430 std::vector<PPDefinition>::iterator itInvalid = std::find_if(ppDefineHistory.begin(), ppDefineHistory.end(), After(curLine-1));
431 if (itInvalid != ppDefineHistory.end()) {
432 ppDefineHistory.erase(itInvalid, ppDefineHistory.end());
433 definitionsChanged = true;
436 SymbolTable preprocessorDefinitions = preprocessorDefinitionsStart;
437 for (std::vector<PPDefinition>::iterator itDef = ppDefineHistory.begin(); itDef != ppDefineHistory.end(); ++itDef) {
438 if (itDef->isUndef)
439 preprocessorDefinitions.erase(itDef->key);
440 else
441 preprocessorDefinitions[itDef->key] = SymbolValue(itDef->value, itDef->arguments);
444 int activitySet = preproc.IsInactive() ? activeFlag : 0;
445 Sci_Position lineEndNext = styler.LineEnd(curLine);
446 bool isEscapedId = false; // true when parsing an escaped Identifier
447 bool isProtected = (lineState&kwProtected) != 0; // true when parsing a protected region
449 for (; sc.More(); sc.Forward()) {
450 if (sc.atLineStart) {
451 if (sc.state == SCE_V_STRING) {
452 // Prevent SCE_V_STRINGEOL from leaking back to previous line
453 sc.SetState(SCE_V_STRING);
455 if ((MaskActive(sc.state) == SCE_V_PREPROCESSOR) && (!continuationLine)) {
456 sc.SetState(SCE_V_DEFAULT|activitySet);
458 if (preproc.IsInactive()) {
459 activitySet = activeFlag;
460 sc.SetState(sc.state | activitySet);
464 if (sc.atLineEnd) {
465 curLine++;
466 lineEndNext = styler.LineEnd(curLine);
467 vlls.Add(curLine, preproc);
468 // Update the line state, so it can be seen by next line
469 styler.SetLineState(curLine, lineState);
470 isEscapedId = false; // EOL terminates an escaped Identifier
473 // Handle line continuation generically.
474 if (sc.ch == '\\') {
475 if (static_cast<Sci_Position>((sc.currentPos+1)) >= lineEndNext) {
476 curLine++;
477 lineEndNext = styler.LineEnd(curLine);
478 vlls.Add(curLine, preproc);
479 // Update the line state, so it can be seen by next line
480 styler.SetLineState(curLine, lineState);
481 sc.Forward();
482 if (sc.ch == '\r' && sc.chNext == '\n') {
483 // Even in UTF-8, \r and \n are separate
484 sc.Forward();
486 continuationLine = true;
487 sc.Forward();
488 continue;
492 // for comment keyword
493 if (MaskActive(sc.state) == SCE_V_COMMENT_WORD && !IsAWordChar(sc.ch)) {
494 char s[100];
495 int state = lineState & 0xff;
496 sc.GetCurrent(s, sizeof(s));
497 if (keywords5.InList(s)) {
498 sc.ChangeState(SCE_V_COMMENT_WORD|activitySet);
499 } else {
500 sc.ChangeState(state|activitySet);
502 sc.SetState(state|activitySet);
505 const bool atLineEndBeforeSwitch = sc.atLineEnd;
507 // Determine if the current state should terminate.
508 switch (MaskActive(sc.state)) {
509 case SCE_V_OPERATOR:
510 sc.SetState(SCE_V_DEFAULT|activitySet);
511 break;
512 case SCE_V_NUMBER:
513 if (!(IsAWordChar(sc.ch) || (sc.ch == '?'))) {
514 sc.SetState(SCE_V_DEFAULT|activitySet);
516 break;
517 case SCE_V_IDENTIFIER:
518 if (!isEscapedId &&(!IsAWordChar(sc.ch) || (sc.ch == '.'))) {
519 char s[100];
520 lineState &= 0xff00;
521 sc.GetCurrent(s, sizeof(s));
522 if (options.portStyling && (strcmp(s, "input") == 0)) {
523 lineState = kwInput;
524 sc.ChangeState(SCE_V_INPUT|activitySet);
525 } else if (options.portStyling && (strcmp(s, "output") == 0)) {
526 lineState = kwOutput;
527 sc.ChangeState(SCE_V_OUTPUT|activitySet);
528 } else if (options.portStyling && (strcmp(s, "inout") == 0)) {
529 lineState = kwInout;
530 sc.ChangeState(SCE_V_INOUT|activitySet);
531 } else if (lineState == kwInput) {
532 sc.ChangeState(SCE_V_INPUT|activitySet);
533 } else if (lineState == kwOutput) {
534 sc.ChangeState(SCE_V_OUTPUT|activitySet);
535 } else if (lineState == kwInout) {
536 sc.ChangeState(SCE_V_INOUT|activitySet);
537 } else if (lineState == kwDot) {
538 lineState = kwOther;
539 if (options.portStyling)
540 sc.ChangeState(SCE_V_PORT_CONNECT|activitySet);
541 } else if (keywords.InList(s)) {
542 sc.ChangeState(SCE_V_WORD|activitySet);
543 } else if (keywords2.InList(s)) {
544 sc.ChangeState(SCE_V_WORD2|activitySet);
545 } else if (keywords3.InList(s)) {
546 sc.ChangeState(SCE_V_WORD3|activitySet);
547 } else if (keywords4.InList(s)) {
548 sc.ChangeState(SCE_V_USER|activitySet);
549 } else if (options.allUppercaseDocKeyword && AllUpperCase(s)) {
550 sc.ChangeState(SCE_V_USER|activitySet);
552 sc.SetState(SCE_V_DEFAULT|activitySet);
554 break;
555 case SCE_V_PREPROCESSOR:
556 if (!IsAWordChar(sc.ch) || sc.atLineEnd) {
557 sc.SetState(SCE_V_DEFAULT|activitySet);
559 break;
560 case SCE_V_COMMENT:
561 if (sc.Match('*', '/')) {
562 sc.Forward();
563 sc.ForwardSetState(SCE_V_DEFAULT|activitySet);
564 } else if (IsAWordStart(sc.ch)) {
565 lineState = sc.state | (lineState & 0xff00);
566 sc.SetState(SCE_V_COMMENT_WORD|activitySet);
568 break;
569 case SCE_V_COMMENTLINE:
570 case SCE_V_COMMENTLINEBANG:
571 if (sc.atLineStart) {
572 sc.SetState(SCE_V_DEFAULT|activitySet);
573 } else if (IsAWordStart(sc.ch)) {
574 lineState = sc.state | (lineState & 0xff00);
575 sc.SetState(SCE_V_COMMENT_WORD|activitySet);
577 break;
578 case SCE_V_STRING:
579 if (sc.ch == '\\') {
580 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
581 sc.Forward();
583 } else if (sc.ch == '\"') {
584 sc.ForwardSetState(SCE_V_DEFAULT|activitySet);
585 } else if (sc.atLineEnd) {
586 sc.ChangeState(SCE_V_STRINGEOL|activitySet);
587 sc.ForwardSetState(SCE_V_DEFAULT|activitySet);
589 break;
592 if (sc.atLineEnd && !atLineEndBeforeSwitch) {
593 // State exit processing consumed characters up to end of line.
594 curLine++;
595 lineEndNext = styler.LineEnd(curLine);
596 vlls.Add(curLine, preproc);
597 // Update the line state, so it can be seen by next line
598 styler.SetLineState(curLine, lineState);
599 isEscapedId = false; // EOL terminates an escaped Identifier
602 // Determine if a new state should be entered.
603 if (MaskActive(sc.state) == SCE_V_DEFAULT) {
604 if (sc.ch == '`') {
605 sc.SetState(SCE_V_PREPROCESSOR|activitySet);
606 // Skip whitespace between ` and preprocessor word
607 do {
608 sc.Forward();
609 } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
610 if (sc.atLineEnd) {
611 sc.SetState(SCE_V_DEFAULT|activitySet);
612 styler.SetLineState(curLine, lineState);
613 } else {
614 if (sc.Match("protected")) {
615 isProtected = true;
616 lineState |= kwProtected;
617 styler.SetLineState(curLine, lineState);
618 } else if (sc.Match("endprotected")) {
619 isProtected = false;
620 lineState &= ~kwProtected;
621 styler.SetLineState(curLine, lineState);
622 } else if (!isProtected && options.trackPreprocessor) {
623 if (sc.Match("ifdef") || sc.Match("ifndef")) {
624 bool isIfDef = sc.Match("ifdef");
625 int i = isIfDef ? 5 : 6;
626 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + i + 1, false);
627 bool foundDef = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end();
628 preproc.StartSection(isIfDef == foundDef);
629 } else if (sc.Match("else")) {
630 if (!preproc.CurrentIfTaken()) {
631 preproc.InvertCurrentLevel();
632 activitySet = preproc.IsInactive() ? activeFlag : 0;
633 if (!activitySet) {
634 sc.ChangeState(SCE_V_PREPROCESSOR|activitySet);
636 } else if (!preproc.IsInactive()) {
637 preproc.InvertCurrentLevel();
638 activitySet = preproc.IsInactive() ? activeFlag : 0;
639 if (!activitySet) {
640 sc.ChangeState(SCE_V_PREPROCESSOR|activitySet);
643 } else if (sc.Match("elsif")) {
644 // Ensure only one chosen out of `if .. `elsif .. `elsif .. `else .. `endif
645 if (!preproc.CurrentIfTaken()) {
646 // Similar to `ifdef
647 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true);
648 bool ifGood = preprocessorDefinitions.find(restOfLine) != preprocessorDefinitions.end();
649 if (ifGood) {
650 preproc.InvertCurrentLevel();
651 activitySet = preproc.IsInactive() ? activeFlag : 0;
652 if (!activitySet)
653 sc.ChangeState(SCE_V_PREPROCESSOR|activitySet);
655 } else if (!preproc.IsInactive()) {
656 preproc.InvertCurrentLevel();
657 activitySet = preproc.IsInactive() ? activeFlag : 0;
658 if (!activitySet)
659 sc.ChangeState(SCE_V_PREPROCESSOR|activitySet);
661 } else if (sc.Match("endif")) {
662 preproc.EndSection();
663 activitySet = preproc.IsInactive() ? activeFlag : 0;
664 sc.ChangeState(SCE_V_PREPROCESSOR|activitySet);
665 } else if (sc.Match("define")) {
666 if (options.updatePreprocessor && !preproc.IsInactive()) {
667 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 6, true);
668 size_t startName = 0;
669 while ((startName < restOfLine.length()) && IsSpaceOrTab(restOfLine[startName]))
670 startName++;
671 size_t endName = startName;
672 while ((endName < restOfLine.length()) && setWord.Contains(static_cast<unsigned char>(restOfLine[endName])))
673 endName++;
674 std::string key = restOfLine.substr(startName, endName-startName);
675 if ((endName < restOfLine.length()) && (restOfLine.at(endName) == '(')) {
676 // Macro
677 size_t endArgs = endName;
678 while ((endArgs < restOfLine.length()) && (restOfLine[endArgs] != ')'))
679 endArgs++;
680 std::string args = restOfLine.substr(endName + 1, endArgs - endName - 1);
681 size_t startValue = endArgs+1;
682 while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue]))
683 startValue++;
684 std::string value;
685 if (startValue < restOfLine.length())
686 value = restOfLine.substr(startValue);
687 preprocessorDefinitions[key] = SymbolValue(value, args);
688 ppDefineHistory.push_back(PPDefinition(curLine, key, value, false, args));
689 definitionsChanged = true;
690 } else {
691 // Value
692 size_t startValue = endName;
693 while ((startValue < restOfLine.length()) && IsSpaceOrTab(restOfLine[startValue]))
694 startValue++;
695 std::string value = restOfLine.substr(startValue);
696 preprocessorDefinitions[key] = value;
697 ppDefineHistory.push_back(PPDefinition(curLine, key, value));
698 definitionsChanged = true;
701 } else if (sc.Match("undefineall")) {
702 if (options.updatePreprocessor && !preproc.IsInactive()) {
703 // remove all preprocessor definitions
704 std::map<std::string, SymbolValue>::iterator itDef;
705 for(itDef = preprocessorDefinitions.begin(); itDef != preprocessorDefinitions.end(); ++itDef) {
706 ppDefineHistory.push_back(PPDefinition(curLine, itDef->first, "", true));
708 preprocessorDefinitions.clear();
709 definitionsChanged = true;
711 } else if (sc.Match("undef")) {
712 if (options.updatePreprocessor && !preproc.IsInactive()) {
713 std::string restOfLine = GetRestOfLine(styler, sc.currentPos + 5, true);
714 std::vector<std::string> tokens = Tokenize(restOfLine);
715 std::string key;
716 if (tokens.size() >= 1) {
717 key = tokens[0];
718 preprocessorDefinitions.erase(key);
719 ppDefineHistory.push_back(PPDefinition(curLine, key, "", true));
720 definitionsChanged = true;
726 } else if (!isProtected) {
727 if (IsADigit(sc.ch) || (sc.ch == '\'') || (sc.ch == '.' && IsADigit(sc.chNext))) {
728 sc.SetState(SCE_V_NUMBER|activitySet);
729 } else if (IsAWordStart(sc.ch)) {
730 sc.SetState(SCE_V_IDENTIFIER|activitySet);
731 } else if (sc.Match('/', '*')) {
732 sc.SetState(SCE_V_COMMENT|activitySet);
733 sc.Forward(); // Eat the * so it isn't used for the end of the comment
734 } else if (sc.Match('/', '/')) {
735 if (sc.Match("//!")) // Nice to have a different comment style
736 sc.SetState(SCE_V_COMMENTLINEBANG|activitySet);
737 else
738 sc.SetState(SCE_V_COMMENTLINE|activitySet);
739 } else if (sc.ch == '\"') {
740 sc.SetState(SCE_V_STRING|activitySet);
741 } else if (sc.ch == '\\') {
742 // escaped identifier, everything is ok up to whitespace
743 isEscapedId = true;
744 sc.SetState(SCE_V_IDENTIFIER|activitySet);
745 } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '#') {
746 sc.SetState(SCE_V_OPERATOR|activitySet);
747 if (sc.ch == '.') lineState = kwDot;
748 if (sc.ch == ';') lineState = kwOther;
752 if (isEscapedId && isspacechar(sc.ch)) {
753 isEscapedId = false;
756 if (definitionsChanged) {
757 styler.ChangeLexerState(startPos, startPos + length);
759 sc.Complete();
762 static bool IsStreamCommentStyle(int style) {
763 return style == SCE_V_COMMENT;
766 static bool IsCommentLine(Sci_Position line, LexAccessor &styler) {
767 Sci_Position pos = styler.LineStart(line);
768 Sci_Position eolPos = styler.LineStart(line + 1) - 1;
769 for (Sci_Position i = pos; i < eolPos; i++) {
770 char ch = styler[i];
771 char chNext = styler.SafeGetCharAt(i + 1);
772 int style = styler.StyleAt(i);
773 if (ch == '/' && chNext == '/' &&
774 (style == SCE_V_COMMENTLINE || style == SCE_V_COMMENTLINEBANG)) {
775 return true;
776 } else if (!IsASpaceOrTab(ch)) {
777 return false;
780 return false;
783 // Store both the current line's fold level and the next lines in the
784 // level store to make it easy to pick up with each increment
785 // and to make it possible to fiddle the current level for "} else {".
786 void SCI_METHOD LexerVerilog::Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess)
788 LexAccessor styler(pAccess);
789 bool foldAtBrace = 1;
790 bool foldAtParenthese = 1;
792 Sci_Position lineCurrent = styler.GetLine(startPos);
793 // Move back one line to be compatible with LexerModule::Fold behavior, fixes problem with foldComment behavior
794 if (lineCurrent > 0) {
795 lineCurrent--;
796 Sci_Position newStartPos = styler.LineStart(lineCurrent);
797 length += startPos - newStartPos;
798 startPos = newStartPos;
799 initStyle = 0;
800 if (startPos > 0) {
801 initStyle = styler.StyleAt(startPos - 1);
804 Sci_PositionU endPos = startPos + length;
805 int visibleChars = 0;
806 int levelCurrent = SC_FOLDLEVELBASE;
807 if (lineCurrent > 0)
808 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
809 int levelMinCurrent = levelCurrent;
810 int levelNext = levelCurrent;
811 char chNext = styler[startPos];
812 int styleNext = MaskActive(styler.StyleAt(startPos));
813 int style = MaskActive(initStyle);
815 // restore fold state (if it exists) for prior line
816 int stateCurrent = 0;
817 std::map<Sci_Position,int>::iterator foldStateIterator = foldState.find(lineCurrent-1);
818 if (foldStateIterator != foldState.end()) {
819 stateCurrent = foldStateIterator->second;
822 // remove all foldState entries after lineCurrent-1
823 foldStateIterator = foldState.upper_bound(lineCurrent-1);
824 if (foldStateIterator != foldState.end()) {
825 foldState.erase(foldStateIterator, foldState.end());
828 for (Sci_PositionU i = startPos; i < endPos; i++) {
829 char ch = chNext;
830 chNext = styler.SafeGetCharAt(i + 1);
831 int stylePrev = style;
832 style = styleNext;
833 styleNext = MaskActive(styler.StyleAt(i + 1));
834 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
835 if (!(stateCurrent & protectedFlag)) {
836 if (options.foldComment && IsStreamCommentStyle(style)) {
837 if (!IsStreamCommentStyle(stylePrev)) {
838 levelNext++;
839 } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
840 // Comments don't end at end of line and the next character may be unstyled.
841 levelNext--;
844 if (options.foldComment && atEOL && IsCommentLine(lineCurrent, styler))
846 if (!IsCommentLine(lineCurrent - 1, styler)
847 && IsCommentLine(lineCurrent + 1, styler))
848 levelNext++;
849 else if (IsCommentLine(lineCurrent - 1, styler)
850 && !IsCommentLine(lineCurrent+1, styler))
851 levelNext--;
853 if (options.foldComment && (style == SCE_V_COMMENTLINE)) {
854 if ((ch == '/') && (chNext == '/')) {
855 char chNext2 = styler.SafeGetCharAt(i + 2);
856 if (chNext2 == '{') {
857 levelNext++;
858 } else if (chNext2 == '}') {
859 levelNext--;
864 if (ch == '`') {
865 Sci_PositionU j = i + 1;
866 while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
867 j++;
869 if (styler.Match(j, "protected")) {
870 stateCurrent |= protectedFlag;
871 levelNext++;
872 } else if (styler.Match(j, "endprotected")) {
873 stateCurrent &= ~protectedFlag;
874 levelNext--;
875 } else if (!(stateCurrent & protectedFlag) && options.foldPreprocessor && (style == SCE_V_PREPROCESSOR)) {
876 if (styler.Match(j, "if")) {
877 if (options.foldPreprocessorElse) {
878 // Measure the minimum before a begin to allow
879 // folding on "end else begin"
880 if (levelMinCurrent > levelNext) {
881 levelMinCurrent = levelNext;
884 levelNext++;
885 } else if (options.foldPreprocessorElse && styler.Match(j, "else")) {
886 levelNext--;
887 if (levelMinCurrent > levelNext) {
888 levelMinCurrent = levelNext;
890 levelNext++;
891 } else if (options.foldPreprocessorElse && styler.Match(j, "elsif")) {
892 levelNext--;
893 // Measure the minimum before a begin to allow
894 // folding on "end else begin"
895 if (levelMinCurrent > levelNext) {
896 levelMinCurrent = levelNext;
898 levelNext++;
899 } else if (styler.Match(j, "endif")) {
900 levelNext--;
904 if (style == SCE_V_OPERATOR) {
905 if (foldAtParenthese) {
906 if (ch == '(') {
907 levelNext++;
908 } else if (ch == ')') {
909 levelNext--;
912 // semicolons terminate external declarations
913 if (ch == ';') {
914 // extern and pure virtual declarations terminated by semicolon
915 if (stateCurrent & foldExternFlag) {
916 levelNext--;
917 stateCurrent &= ~foldExternFlag;
919 // wait and disable statements terminated by semicolon
920 if (stateCurrent & foldWaitDisableFlag) {
921 stateCurrent &= ~foldWaitDisableFlag;
923 // typedef statements terminated by semicolon
924 if (stateCurrent & typedefFlag) {
925 stateCurrent &= ~typedefFlag;
928 // wait and disable statements containing '(' will not contain "fork" keyword, special processing is not needed
929 if (ch == '(') {
930 if (stateCurrent & foldWaitDisableFlag) {
931 stateCurrent &= ~foldWaitDisableFlag;
935 if (style == SCE_V_OPERATOR) {
936 if (foldAtBrace) {
937 if (ch == '{') {
938 levelNext++;
939 } else if (ch == '}') {
940 levelNext--;
944 if (style == SCE_V_WORD && stylePrev != SCE_V_WORD) {
945 Sci_PositionU j = i;
946 if (styler.Match(j, "case") ||
947 styler.Match(j, "casex") ||
948 styler.Match(j, "casez") ||
949 styler.Match(j, "covergroup") ||
950 styler.Match(j, "function") ||
951 styler.Match(j, "generate") ||
952 styler.Match(j, "interface") ||
953 styler.Match(j, "package") ||
954 styler.Match(j, "primitive") ||
955 styler.Match(j, "program") ||
956 styler.Match(j, "sequence") ||
957 styler.Match(j, "specify") ||
958 styler.Match(j, "table") ||
959 styler.Match(j, "task") ||
960 (styler.Match(j, "module") && options.foldAtModule)) {
961 levelNext++;
962 } else if (styler.Match(j, "begin")) {
963 // Measure the minimum before a begin to allow
964 // folding on "end else begin"
965 if (levelMinCurrent > levelNext) {
966 levelMinCurrent = levelNext;
968 levelNext++;
969 } else if (styler.Match(j, "class")) {
970 // class does not introduce a block when used in a typedef statement
971 if (!(stateCurrent & typedefFlag))
972 levelNext++;
973 } else if (styler.Match(j, "fork")) {
974 // fork does not introduce a block when used in a wait or disable statement
975 if (stateCurrent & foldWaitDisableFlag) {
976 stateCurrent &= ~foldWaitDisableFlag;
977 } else
978 levelNext++;
979 } else if (styler.Match(j, "endcase") ||
980 styler.Match(j, "endclass") ||
981 styler.Match(j, "endfunction") ||
982 styler.Match(j, "endgenerate") ||
983 styler.Match(j, "endgroup") ||
984 styler.Match(j, "endinterface") ||
985 styler.Match(j, "endpackage") ||
986 styler.Match(j, "endprimitive") ||
987 styler.Match(j, "endprogram") ||
988 styler.Match(j, "endsequence") ||
989 styler.Match(j, "endspecify") ||
990 styler.Match(j, "endtable") ||
991 styler.Match(j, "endtask") ||
992 styler.Match(j, "join") ||
993 styler.Match(j, "join_any") ||
994 styler.Match(j, "join_none") ||
995 (styler.Match(j, "endmodule") && options.foldAtModule) ||
996 (styler.Match(j, "end") && !IsAWordChar(styler.SafeGetCharAt(j + 3)))) {
997 levelNext--;
998 } else if (styler.Match(j, "extern") ||
999 styler.Match(j, "pure")) {
1000 // extern and pure virtual functions/tasks are terminated by ';' not endfunction/endtask
1001 stateCurrent |= foldExternFlag;
1002 } else if (styler.Match(j, "disable") ||
1003 styler.Match(j, "wait")) {
1004 // fork does not introduce a block when used in a wait or disable statement
1005 stateCurrent |= foldWaitDisableFlag;
1006 } else if (styler.Match(j, "typedef")) {
1007 stateCurrent |= typedefFlag;
1010 if (atEOL) {
1011 int levelUse = levelCurrent;
1012 if (options.foldAtElse||options.foldPreprocessorElse) {
1013 levelUse = levelMinCurrent;
1015 int lev = levelUse | levelNext << 16;
1016 if (visibleChars == 0 && options.foldCompact)
1017 lev |= SC_FOLDLEVELWHITEFLAG;
1018 if (levelUse < levelNext)
1019 lev |= SC_FOLDLEVELHEADERFLAG;
1020 if (stateCurrent) {
1021 foldState[lineCurrent] = stateCurrent;
1023 if (lev != styler.LevelAt(lineCurrent)) {
1024 styler.SetLevel(lineCurrent, lev);
1026 lineCurrent++;
1027 levelCurrent = levelNext;
1028 levelMinCurrent = levelCurrent;
1029 visibleChars = 0;
1031 if (!isspacechar(ch))
1032 visibleChars++;
1036 std::vector<std::string> LexerVerilog::Tokenize(const std::string &expr) const {
1037 // Break into tokens
1038 std::vector<std::string> tokens;
1039 const char *cp = expr.c_str();
1040 while (*cp) {
1041 std::string word;
1042 if (setWord.Contains(static_cast<unsigned char>(*cp))) {
1043 // Identifiers and numbers
1044 while (setWord.Contains(static_cast<unsigned char>(*cp))) {
1045 word += *cp;
1046 cp++;
1048 } else if (IsSpaceOrTab(*cp)) {
1049 while (IsSpaceOrTab(*cp)) {
1050 cp++;
1052 continue;
1053 } else {
1054 // Should handle strings, characters, and comments here
1055 word += *cp;
1056 cp++;
1058 tokens.push_back(word);
1060 return tokens;
1063 static const char * const verilogWordLists[] = {
1064 "Primary keywords and identifiers",
1065 "Secondary keywords and identifiers",
1066 "System Tasks",
1067 "User defined tasks and identifiers",
1068 "Documentation comment keywords",
1069 "Preprocessor definitions",
1073 LexerModule lmVerilog(SCLEX_VERILOG, LexerVerilog::LexerFactoryVerilog, "verilog", verilogWordLists);