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