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 <ctype.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14 
15 #include "Platform.h"
16 
17 #include "PropSet.h"
18 #include "Accessor.h"
19 #include "StyleContext.h"
20 #include "KeyWords.h"
21 #include "Scintilla.h"
22 #include "SciLexer.h"
23 
IsAWordChar(const int ch)24 static inline bool IsAWordChar(const int ch) {
25 	return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\'');
26 }
27 
IsAWordStart(const int ch)28 static inline bool IsAWordStart(const int ch) {
29 	return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '$');
30 }
31 
ColouriseVerilogDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)32 static void ColouriseVerilogDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
33                             Accessor &styler) {
34 
35 	WordList &keywords = *keywordlists[0];
36 	WordList &keywords2 = *keywordlists[1];
37 	WordList &keywords3 = *keywordlists[2];
38 	WordList &keywords4 = *keywordlists[3];
39 
40 	// Do not leak onto next line
41 	if (initStyle == SCE_V_STRINGEOL)
42 		initStyle = SCE_V_DEFAULT;
43 
44 	StyleContext sc(startPos, length, initStyle, styler);
45 
46 	for (; sc.More(); sc.Forward()) {
47 
48 		if (sc.atLineStart && (sc.state == SCE_V_STRING)) {
49 			// Prevent SCE_V_STRINGEOL from leaking back to previous line
50 			sc.SetState(SCE_V_STRING);
51 		}
52 
53 		// Handle line continuation generically.
54 		if (sc.ch == '\\') {
55 			if (sc.chNext == '\n' || sc.chNext == '\r') {
56 				sc.Forward();
57 				if (sc.ch == '\r' && sc.chNext == '\n') {
58 					sc.Forward();
59 				}
60 				continue;
61 			}
62 		}
63 
64 		// Determine if the current state should terminate.
65 		if (sc.state == SCE_V_OPERATOR) {
66 			sc.SetState(SCE_V_DEFAULT);
67 		} else if (sc.state == SCE_V_NUMBER) {
68 			if (!IsAWordChar(sc.ch)) {
69 				sc.SetState(SCE_V_DEFAULT);
70 			}
71 		} else if (sc.state == SCE_V_IDENTIFIER) {
72 			if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
73 				char s[100];
74                                 sc.GetCurrent(s, sizeof(s));
75 				if (keywords.InList(s)) {
76 					sc.ChangeState(SCE_V_WORD);
77 				} else if (keywords2.InList(s)) {
78 					sc.ChangeState(SCE_V_WORD2);
79 				} else if (keywords3.InList(s)) {
80 					sc.ChangeState(SCE_V_WORD3);
81                                 } else if (keywords4.InList(s)) {
82 					sc.ChangeState(SCE_V_USER);
83 				}
84 				sc.SetState(SCE_V_DEFAULT);
85 			}
86 		} else if (sc.state == SCE_V_PREPROCESSOR) {
87                         if (!IsAWordChar(sc.ch)) {
88                                 sc.SetState(SCE_V_DEFAULT);
89                         }
90 		} else if (sc.state == SCE_V_COMMENT) {
91 			if (sc.Match('*', '/')) {
92 				sc.Forward();
93 				sc.ForwardSetState(SCE_V_DEFAULT);
94 			}
95 		} else if (sc.state == SCE_V_COMMENTLINE || sc.state == SCE_V_COMMENTLINEBANG) {
96 			if (sc.atLineEnd) {
97 				sc.SetState(SCE_V_DEFAULT);
98 			}
99                 } else if (sc.state == SCE_V_STRING) {
100 			if (sc.ch == '\\') {
101 				if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
102 					sc.Forward();
103 				}
104 			} else if (sc.ch == '\"') {
105 				sc.ForwardSetState(SCE_V_DEFAULT);
106 			} else if (sc.atLineEnd) {
107 				sc.ChangeState(SCE_V_STRINGEOL);
108 				sc.ForwardSetState(SCE_V_DEFAULT);
109 			}
110 		}
111 
112 		// Determine if a new state should be entered.
113 		if (sc.state == SCE_V_DEFAULT) {
114 			if (IsADigit(sc.ch) || (sc.ch == '\'') || (sc.ch == '.' && IsADigit(sc.chNext))) {
115 				sc.SetState(SCE_V_NUMBER);
116 			} else if (IsAWordStart(sc.ch)) {
117 				sc.SetState(SCE_V_IDENTIFIER);
118 			} else if (sc.Match('/', '*')) {
119                                 sc.SetState(SCE_V_COMMENT);
120 				sc.Forward();	// Eat the * so it isn't used for the end of the comment
121 			} else if (sc.Match('/', '/')) {
122 				if (sc.Match("//!"))	// Nice to have a different comment style
123 					sc.SetState(SCE_V_COMMENTLINEBANG);
124 				else
125 					sc.SetState(SCE_V_COMMENTLINE);
126 			} else if (sc.ch == '\"') {
127 				sc.SetState(SCE_V_STRING);
128 			} else if (sc.ch == '`') {
129 				sc.SetState(SCE_V_PREPROCESSOR);
130 				// Skip whitespace between ` and preprocessor word
131 				do {
132 					sc.Forward();
133 				} while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
134 				if (sc.atLineEnd) {
135 					sc.SetState(SCE_V_DEFAULT);
136 				}
137 			} else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '#') {
138 				sc.SetState(SCE_V_OPERATOR);
139 			}
140 		}
141 	}
142 	sc.Complete();
143 }
144 
IsStreamCommentStyle(int style)145 static bool IsStreamCommentStyle(int style) {
146 	return style == SCE_V_COMMENT;
147 }
148 
149 // Store both the current line's fold level and the next lines in the
150 // level store to make it easy to pick up with each increment
151 // and to make it possible to fiddle the current level for "} else {".
FoldNoBoxVerilogDoc(unsigned int startPos,int length,int initStyle,Accessor & styler)152 static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle,
153                             Accessor &styler) {
154 	bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
155 	bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
156 	bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
157 	bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
158         // Verilog specific folding options:
159         // fold_at_module -
160         //      Generally used methodology in verilog code is
161         //      one module per file, so folding at module definition is useless.
162         // fold_at_brace/parenthese -
163         //      Folding of long port lists can be convenient.
164 	bool foldAtModule = styler.GetPropertyInt("fold.verilog.flags", 0) != 0;
165 	bool foldAtBrace  = 1;
166 	bool foldAtParenthese  = 1;
167 
168 	unsigned int endPos = startPos + length;
169 	int visibleChars = 0;
170 	int lineCurrent = styler.GetLine(startPos);
171 	int levelCurrent = SC_FOLDLEVELBASE;
172 	if (lineCurrent > 0)
173 		levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
174 	int levelMinCurrent = levelCurrent;
175 	int levelNext = levelCurrent;
176 	char chNext = styler[startPos];
177 	int styleNext = styler.StyleAt(startPos);
178 	int style = initStyle;
179 	for (unsigned int i = startPos; i < endPos; i++) {
180 		char ch = chNext;
181 		chNext = styler.SafeGetCharAt(i + 1);
182 		int stylePrev = style;
183 		style = styleNext;
184 		styleNext = styler.StyleAt(i + 1);
185 		bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
186 		if (foldComment && IsStreamCommentStyle(style)) {
187 			if (!IsStreamCommentStyle(stylePrev)) {
188 				levelNext++;
189 			} else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
190 				// Comments don't end at end of line and the next character may be unstyled.
191 				levelNext--;
192 			}
193 		}
194 		if (foldComment && (style == SCE_V_COMMENTLINE)) {
195 			if ((ch == '/') && (chNext == '/')) {
196 				char chNext2 = styler.SafeGetCharAt(i + 2);
197 				if (chNext2 == '{') {
198 					levelNext++;
199 				} else if (chNext2 == '}') {
200 					levelNext--;
201 				}
202 			}
203 		}
204 		if (foldPreprocessor && (style == SCE_V_PREPROCESSOR)) {
205 			if (ch == '`') {
206 				unsigned int j = i + 1;
207 				while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
208 					j++;
209 				}
210 				if (styler.Match(j, "if")) {
211 					levelNext++;
212 				} else if (styler.Match(j, "end")) {
213 					levelNext--;
214 				}
215 			}
216 		}
217                 if (style == SCE_V_OPERATOR) {
218                     if (foldAtParenthese) {
219 			if (ch == '(') {
220 				levelNext++;
221 			} else if (ch == ')') {
222 				levelNext--;
223 			}
224                     }
225 		}
226                 if (style == SCE_V_OPERATOR) {
227                     if (foldAtBrace) {
228 			if (ch == '{') {
229 				levelNext++;
230 			} else if (ch == '}') {
231 				levelNext--;
232 			}
233                     }
234 		}
235                 if (style == SCE_V_WORD && stylePrev != SCE_V_WORD) {
236                         unsigned int j = i;
237                         if (styler.Match(j, "case") ||
238                             styler.Match(j, "casex") ||
239                             styler.Match(j, "casez") ||
240                             styler.Match(j, "function") ||
241                             styler.Match(j, "fork") ||
242                             styler.Match(j, "table") ||
243                             styler.Match(j, "task") ||
244                             styler.Match(j, "specify") ||
245                             styler.Match(j, "primitive") ||
246                             styler.Match(j, "module") && foldAtModule ||
247                             styler.Match(j, "begin")) {
248                                 levelNext++;
249                         } else if (styler.Match(j, "endcase") ||
250                                    styler.Match(j, "endfunction") ||
251                                    styler.Match(j, "join") ||
252                                    styler.Match(j, "endtask") ||
253                                    styler.Match(j, "endtable") ||
254                                    styler.Match(j, "endspecify") ||
255                                    styler.Match(j, "endprimitive") ||
256                                    styler.Match(j, "endmodule") && foldAtModule ||
257                                    styler.Match(j, "end") && !IsAWordChar(styler.SafeGetCharAt(j+3))) {
258                                 levelNext--;
259                         }
260 		}
261 		if (atEOL) {
262 			int levelUse = levelCurrent;
263 			if (foldAtElse) {
264 				levelUse = levelMinCurrent;
265 			}
266 			int lev = levelUse | levelNext << 16;
267 			if (visibleChars == 0 && foldCompact)
268 				lev |= SC_FOLDLEVELWHITEFLAG;
269 			if (levelUse < levelNext)
270 				lev |= SC_FOLDLEVELHEADERFLAG;
271 			if (lev != styler.LevelAt(lineCurrent)) {
272 				styler.SetLevel(lineCurrent, lev);
273 			}
274 			lineCurrent++;
275 			levelCurrent = levelNext;
276 			levelMinCurrent = levelCurrent;
277 			visibleChars = 0;
278 		}
279 		if (!isspacechar(ch))
280 			visibleChars++;
281 	}
282 }
283 
FoldVerilogDoc(unsigned int startPos,int length,int initStyle,WordList * [],Accessor & styler)284 static void FoldVerilogDoc(unsigned int startPos, int length, int initStyle, WordList *[],
285                        Accessor &styler) {
286 	FoldNoBoxVerilogDoc(startPos, length, initStyle, styler);
287 }
288 
289 static const char * const verilogWordLists[] = {
290             "Primary keywords and identifiers",
291             "Secondary keywords and identifiers",
292             "System Tasks",
293             "User defined tasks and identifiers",
294             "Unused",
295             0,
296         };
297 
298 
299 LexerModule lmVerilog(SCLEX_VERILOG, ColouriseVerilogDoc, "verilog", FoldVerilogDoc, verilogWordLists);
300