1 // Scintilla source code edit control
2 /** @file LexVerilog.cxx
4 ** Written by Avi Yegudin, based on C++ lexer by Neil Hodgson
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.
22 #include "Scintilla.h"
26 #include "LexAccessor.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
;
39 // Use an unnamed namespace to protect the functions and classes from name conflicts
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_
) {
56 bool ValidLevel() const {
57 return level
>= 0 && level
< 32;
59 int maskLevel() const {
63 LinePPState() : state(0), ifTaken(0), level(-1) {
65 bool IsInactive() const {
68 bool CurrentIfTaken() const {
69 return (ifTaken
& maskLevel()) != 0;
71 void StartSection(bool on
) {
75 state
&= ~maskLevel();
76 ifTaken
|= maskLevel();
79 ifTaken
&= ~maskLevel();
85 state
&= ~maskLevel();
86 ifTaken
&= ~maskLevel();
90 void InvertCurrentLevel() {
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.
101 std::vector
<LinePPState
> vlls
;
103 LinePPState
ForLine(Sci_Position line
) const {
104 if ((line
> 0) && (vlls
.size() > static_cast<size_t>(line
))) {
107 return LinePPState();
110 void Add(Sci_Position line
, LinePPState lls
) {
116 // Options used for LexerVerilog
117 struct OptionsVerilog
{
119 bool foldPreprocessor
;
120 bool foldPreprocessorElse
;
124 bool trackPreprocessor
;
125 bool updatePreprocessor
;
127 bool allUppercaseDocKeyword
;
130 foldPreprocessor
= false;
131 foldPreprocessorElse
= 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
140 // for backwards compatibility, don't treat all uppercase identifiers as documentation keywords
141 allUppercaseDocKeyword
= false;
145 struct OptionSetVerilog
: public OptionSet
<OptionsVerilog
> {
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
;
181 WordList ppDefinitions
;
183 std::vector
<PPDefinition
> ppDefineHistory
;
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_
) {
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 };
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
;
215 setWord(CharacterSet::setAlphaNum
, "._", 0x80, true),
216 subStyles(styleSubable
, 0x80, 0x40, activeFlag
) {
218 virtual ~LexerVerilog() {}
219 int SCI_METHOD
Version() const override
{
222 void SCI_METHOD
Release() override
{
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
{
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
{
269 void SCI_METHOD
SetIdentifiers(int style
, const char *identifiers
) override
{
270 subStyles
.SetIdentifiers(style
, identifiers
);
272 int SCI_METHOD
DistanceToSecondaryStyles() override
{
275 const char * SCI_METHOD
GetSubStyleBases() override
{
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;
291 wordListN
= &keywords
;
294 wordListN
= &keywords2
;
297 wordListN
= &keywords3
;
300 wordListN
= &keywords4
;
303 wordListN
= &keywords5
;
306 wordListN
= &ppDefinitions
;
309 Sci_Position firstModification
= -1;
313 if (*wordListN
!= wlNew
) {
315 firstModification
= 0;
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
, '=');
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
)) {
329 std::string args
= name
.substr(bracket
+ 1, bracketEnd
- bracket
- 1);
330 name
= name
.substr(0, bracket
);
331 preprocessorDefinitionsStart
[name
] = SymbolValue(val
, args
);
333 preprocessorDefinitionsStart
[name
] = val
;
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
) {
357 if (*a
>= 'a' && *a
<= 'z') return false;
363 // Functor used to truncate history
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
;
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
== '*'))
381 if (allowSpace
|| (ch
!= ' '))
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 '\'
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
) {
439 preprocessorDefinitions
.erase(itDef
->key
);
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
);
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.
475 if (static_cast<Sci_Position
>((sc
.currentPos
+1)) >= lineEndNext
) {
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
);
482 if (sc
.ch
== '\r' && sc
.chNext
== '\n') {
483 // Even in UTF-8, \r and \n are separate
486 continuationLine
= true;
492 // for comment keyword
493 if (MaskActive(sc
.state
) == SCE_V_COMMENT_WORD
&& !IsAWordChar(sc
.ch
)) {
495 int state
= lineState
& 0xff;
496 sc
.GetCurrent(s
, sizeof(s
));
497 if (keywords5
.InList(s
)) {
498 sc
.ChangeState(SCE_V_COMMENT_WORD
|activitySet
);
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
)) {
510 sc
.SetState(SCE_V_DEFAULT
|activitySet
);
513 if (!(IsAWordChar(sc
.ch
) || (sc
.ch
== '?'))) {
514 sc
.SetState(SCE_V_DEFAULT
|activitySet
);
517 case SCE_V_IDENTIFIER
:
518 if (!isEscapedId
&&(!IsAWordChar(sc
.ch
) || (sc
.ch
== '.'))) {
521 sc
.GetCurrent(s
, sizeof(s
));
522 if (options
.portStyling
&& (strcmp(s
, "input") == 0)) {
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)) {
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
) {
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
);
555 case SCE_V_PREPROCESSOR
:
556 if (!IsAWordChar(sc
.ch
) || sc
.atLineEnd
) {
557 sc
.SetState(SCE_V_DEFAULT
|activitySet
);
561 if (sc
.Match('*', '/')) {
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
);
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
);
580 if (sc
.chNext
== '\"' || sc
.chNext
== '\'' || sc
.chNext
== '\\') {
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
);
592 if (sc
.atLineEnd
&& !atLineEndBeforeSwitch
) {
593 // State exit processing consumed characters up to end of line.
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
) {
605 sc
.SetState(SCE_V_PREPROCESSOR
|activitySet
);
606 // Skip whitespace between ` and preprocessor word
609 } while ((sc
.ch
== ' ' || sc
.ch
== '\t') && sc
.More());
611 sc
.SetState(SCE_V_DEFAULT
|activitySet
);
612 styler
.SetLineState(curLine
, lineState
);
614 if (sc
.Match("protected")) {
616 lineState
|= kwProtected
;
617 styler
.SetLineState(curLine
, lineState
);
618 } else if (sc
.Match("endprotected")) {
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;
634 sc
.ChangeState(SCE_V_PREPROCESSOR
|activitySet
);
636 } else if (!preproc
.IsInactive()) {
637 preproc
.InvertCurrentLevel();
638 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
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()) {
647 std::string restOfLine
= GetRestOfLine(styler
, sc
.currentPos
+ 6, true);
648 bool ifGood
= preprocessorDefinitions
.find(restOfLine
) != preprocessorDefinitions
.end();
650 preproc
.InvertCurrentLevel();
651 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
653 sc
.ChangeState(SCE_V_PREPROCESSOR
|activitySet
);
655 } else if (!preproc
.IsInactive()) {
656 preproc
.InvertCurrentLevel();
657 activitySet
= preproc
.IsInactive() ? activeFlag
: 0;
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
]))
671 size_t endName
= startName
;
672 while ((endName
< restOfLine
.length()) && setWord
.Contains(static_cast<unsigned char>(restOfLine
[endName
])))
674 std::string key
= restOfLine
.substr(startName
, endName
-startName
);
675 if ((endName
< restOfLine
.length()) && (restOfLine
.at(endName
) == '(')) {
677 size_t endArgs
= endName
;
678 while ((endArgs
< restOfLine
.length()) && (restOfLine
[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
]))
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;
692 size_t startValue
= endName
;
693 while ((startValue
< restOfLine
.length()) && IsSpaceOrTab(restOfLine
[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
);
716 if (tokens
.size() >= 1) {
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
);
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
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
)) {
756 if (definitionsChanged
) {
757 styler
.ChangeLexerState(startPos
, startPos
+ length
);
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
++) {
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
)) {
776 } else if (!IsASpaceOrTab(ch
)) {
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) {
796 Sci_Position newStartPos
= styler
.LineStart(lineCurrent
);
797 length
+= startPos
- newStartPos
;
798 startPos
= newStartPos
;
801 initStyle
= styler
.StyleAt(startPos
- 1);
804 Sci_PositionU endPos
= startPos
+ length
;
805 int visibleChars
= 0;
806 int levelCurrent
= SC_FOLDLEVELBASE
;
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
++) {
830 chNext
= styler
.SafeGetCharAt(i
+ 1);
831 int stylePrev
= style
;
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
)) {
839 } else if (!IsStreamCommentStyle(styleNext
) && !atEOL
) {
840 // Comments don't end at end of line and the next character may be unstyled.
844 if (options
.foldComment
&& atEOL
&& IsCommentLine(lineCurrent
, styler
))
846 if (!IsCommentLine(lineCurrent
- 1, styler
)
847 && IsCommentLine(lineCurrent
+ 1, styler
))
849 else if (IsCommentLine(lineCurrent
- 1, styler
)
850 && !IsCommentLine(lineCurrent
+1, styler
))
853 if (options
.foldComment
&& (style
== SCE_V_COMMENTLINE
)) {
854 if ((ch
== '/') && (chNext
== '/')) {
855 char chNext2
= styler
.SafeGetCharAt(i
+ 2);
856 if (chNext2
== '{') {
858 } else if (chNext2
== '}') {
865 Sci_PositionU j
= i
+ 1;
866 while ((j
< endPos
) && IsASpaceOrTab(styler
.SafeGetCharAt(j
))) {
869 if (styler
.Match(j
, "protected")) {
870 stateCurrent
|= protectedFlag
;
872 } else if (styler
.Match(j
, "endprotected")) {
873 stateCurrent
&= ~protectedFlag
;
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
;
885 } else if (options
.foldPreprocessorElse
&& styler
.Match(j
, "else")) {
887 if (levelMinCurrent
> levelNext
) {
888 levelMinCurrent
= levelNext
;
891 } else if (options
.foldPreprocessorElse
&& styler
.Match(j
, "elsif")) {
893 // Measure the minimum before a begin to allow
894 // folding on "end else begin"
895 if (levelMinCurrent
> levelNext
) {
896 levelMinCurrent
= levelNext
;
899 } else if (styler
.Match(j
, "endif")) {
904 if (style
== SCE_V_OPERATOR
) {
905 if (foldAtParenthese
) {
908 } else if (ch
== ')') {
912 // semicolons terminate external declarations
914 // extern and pure virtual declarations terminated by semicolon
915 if (stateCurrent
& foldExternFlag
) {
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
930 if (stateCurrent
& foldWaitDisableFlag
) {
931 stateCurrent
&= ~foldWaitDisableFlag
;
935 if (style
== SCE_V_OPERATOR
) {
939 } else if (ch
== '}') {
944 if (style
== SCE_V_WORD
&& stylePrev
!= SCE_V_WORD
) {
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
)) {
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
;
969 } else if (styler
.Match(j
, "class")) {
970 // class does not introduce a block when used in a typedef statement
971 if (!(stateCurrent
& typedefFlag
))
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
;
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)))) {
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
;
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
;
1021 foldState
[lineCurrent
] = stateCurrent
;
1023 if (lev
!= styler
.LevelAt(lineCurrent
)) {
1024 styler
.SetLevel(lineCurrent
, lev
);
1027 levelCurrent
= levelNext
;
1028 levelMinCurrent
= levelCurrent
;
1031 if (!isspacechar(ch
))
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();
1042 if (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1043 // Identifiers and numbers
1044 while (setWord
.Contains(static_cast<unsigned char>(*cp
))) {
1048 } else if (IsSpaceOrTab(*cp
)) {
1049 while (IsSpaceOrTab(*cp
)) {
1054 // Should handle strings, characters, and comments here
1058 tokens
.push_back(word
);
1063 static const char * const verilogWordLists
[] = {
1064 "Primary keywords and identifiers",
1065 "Secondary keywords and identifiers",
1067 "User defined tasks and identifiers",
1068 "Documentation comment keywords",
1069 "Preprocessor definitions",
1073 LexerModule
lmVerilog(SCLEX_VERILOG
, LexerVerilog::LexerFactoryVerilog
, "verilog", verilogWordLists
);