1 // Scintilla source code edit control
2 /** @file LexVHDL.cxx
3 ** Lexer for VHDL
4 ** Written by Phil Reid,
5 ** Based on:
6 ** - The Verilog Lexer by Avi Yegudin
7 ** - The Fortran Lexer by Chuan-jian Shen
8 ** - The C++ lexer by Neil Hodgson
9 **/
10 // Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
11 // The License.txt file describes the conditions under which this software may be distributed.
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <ctype.h>
16 #include <stdio.h>
17 #include <stdarg.h>
18
19 #include "Platform.h"
20
21 #include "PropSet.h"
22 #include "Accessor.h"
23 #include "StyleContext.h"
24 #include "KeyWords.h"
25 #include "Scintilla.h"
26 #include "SciLexer.h"
27
28 static void ColouriseVHDLDoc(
29 unsigned int startPos,
30 int length,
31 int initStyle,
32 WordList *keywordlists[],
33 Accessor &styler);
34
35
36 /***************************************/
IsAWordChar(const int ch)37 static inline bool IsAWordChar(const int ch) {
38 return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' );
39 }
40
41 /***************************************/
IsAWordStart(const int ch)42 static inline bool IsAWordStart(const int ch) {
43 return (ch < 0x80) && (isalnum(ch) || ch == '_');
44 }
45
46 /***************************************/
IsABlank(unsigned int ch)47 inline bool IsABlank(unsigned int ch) {
48 return (ch == ' ') || (ch == 0x09) || (ch == 0x0b) ;
49 }
50
51 /***************************************/
ColouriseVHDLDoc(unsigned int startPos,int length,int initStyle,WordList * keywordlists[],Accessor & styler)52 static void ColouriseVHDLDoc(
53 unsigned int startPos,
54 int length,
55 int initStyle,
56 WordList *keywordlists[],
57 Accessor &styler)
58 {
59 WordList &Keywords = *keywordlists[0];
60 WordList &Operators = *keywordlists[1];
61 WordList &Attributes = *keywordlists[2];
62 WordList &Functions = *keywordlists[3];
63 WordList &Packages = *keywordlists[4];
64 WordList &Types = *keywordlists[5];
65 WordList &User = *keywordlists[6];
66
67 StyleContext sc(startPos, length, initStyle, styler);
68
69 for (; sc.More(); sc.Forward())
70 {
71
72 // Determine if the current state should terminate.
73 if (sc.state == SCE_VHDL_OPERATOR) {
74 sc.SetState(SCE_VHDL_DEFAULT);
75 } else if (sc.state == SCE_VHDL_NUMBER) {
76 if (!IsAWordChar(sc.ch) && (sc.ch != '#')) {
77 sc.SetState(SCE_VHDL_DEFAULT);
78 }
79 } else if (sc.state == SCE_VHDL_IDENTIFIER) {
80 if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
81 char s[100];
82 sc.GetCurrentLowered(s, sizeof(s));
83 if (Keywords.InList(s)) {
84 sc.ChangeState(SCE_VHDL_KEYWORD);
85 } else if (Operators.InList(s)) {
86 sc.ChangeState(SCE_VHDL_STDOPERATOR);
87 } else if (Attributes.InList(s)) {
88 sc.ChangeState(SCE_VHDL_ATTRIBUTE);
89 } else if (Functions.InList(s)) {
90 sc.ChangeState(SCE_VHDL_STDFUNCTION);
91 } else if (Packages.InList(s)) {
92 sc.ChangeState(SCE_VHDL_STDPACKAGE);
93 } else if (Types.InList(s)) {
94 sc.ChangeState(SCE_VHDL_STDTYPE);
95 } else if (User.InList(s)) {
96 sc.ChangeState(SCE_VHDL_USERWORD);
97 }
98 sc.SetState(SCE_VHDL_DEFAULT);
99 }
100 } else if (sc.state == SCE_VHDL_COMMENT || sc.state == SCE_V_COMMENTLINEBANG) {
101 if (sc.atLineEnd) {
102 sc.SetState(SCE_VHDL_DEFAULT);
103 }
104 } else if (sc.state == SCE_VHDL_STRING) {
105 if (sc.ch == '\\') {
106 if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
107 sc.Forward();
108 }
109 } else if (sc.ch == '\"') {
110 sc.ForwardSetState(SCE_VHDL_DEFAULT);
111 } else if (sc.atLineEnd) {
112 sc.ChangeState(SCE_V_STRINGEOL);
113 sc.ForwardSetState(SCE_VHDL_DEFAULT);
114 }
115 }
116
117 // Determine if a new state should be entered.
118 if (sc.state == SCE_VHDL_DEFAULT) {
119 if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
120 sc.SetState(SCE_VHDL_NUMBER);
121 } else if (IsAWordStart(sc.ch)) {
122 sc.SetState(SCE_VHDL_IDENTIFIER);
123 } else if (sc.Match('-', '-')) {
124 sc.SetState(SCE_VHDL_COMMENT);
125 sc.Forward();
126 } else if (sc.Match('-', '-')) {
127 if (sc.Match("--!")) // Nice to have a different comment style
128 sc.SetState(SCE_VHDL_COMMENTLINEBANG);
129 else
130 sc.SetState(SCE_VHDL_COMMENT);
131 } else if (sc.ch == '\"') {
132 sc.SetState(SCE_VHDL_STRING);
133 } else if (isoperator(static_cast<char>(sc.ch))) {
134 sc.SetState(SCE_VHDL_OPERATOR);
135 }
136 }
137 }
138 sc.Complete();
139 }
140 //=============================================================================
IsCommentLine(int line,Accessor & styler)141 static bool IsCommentLine(int line, Accessor &styler) {
142 int pos = styler.LineStart(line);
143 int eol_pos = styler.LineStart(line + 1) - 1;
144 for (int i = pos; i < eol_pos; i++) {
145 char ch = styler[i];
146 char chNext = styler[i+1];
147 if ((ch == '-') && (chNext == '-'))
148 return true;
149 else if (ch != ' ' && ch != '\t')
150 return false;
151 }
152 return false;
153 }
154
155 //=============================================================================
156 // Folding the code
FoldNoBoxVHDLDoc(unsigned int startPos,int length,int initStyle,Accessor & styler)157 static void FoldNoBoxVHDLDoc(
158 unsigned int startPos,
159 int length,
160 int initStyle,
161 Accessor &styler)
162 {
163 // Decided it would be smarter to have the lexer have all keywords included. Therefore I
164 // don't check if the style for the keywords that I use to adjust the levels.
165 char words[] =
166 "architecture begin case component else elsif end entity generate loop package process record then "
167 "procedure function when";
168 WordList keywords;
169 keywords.Set(words);
170
171 bool foldComment = styler.GetPropertyInt("fold.comment", 1) != 0;
172 bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
173 bool foldAtElse = styler.GetPropertyInt("fold.at.else", 1) != 0;
174 bool foldAtBegin = styler.GetPropertyInt("fold.at.Begin", 1) != 0;
175 bool foldAtParenthese = styler.GetPropertyInt("fold.at.Parenthese", 1) != 0;
176 //bool foldAtWhen = styler.GetPropertyInt("fold.at.When", 1) != 0; //< fold at when in case statements
177
178 int visibleChars = 0;
179 unsigned int endPos = startPos + length;
180
181 int lineCurrent = styler.GetLine(startPos);
182 int levelCurrent = SC_FOLDLEVELBASE;
183 if(lineCurrent > 0)
184 levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
185 //int levelMinCurrent = levelCurrent;
186 int levelMinCurrentElse = levelCurrent; //< Used for folding at 'else'
187 int levelMinCurrentBegin = levelCurrent; //< Used for folding at 'begin'
188 int levelNext = levelCurrent;
189
190 /***************************************/
191 int lastStart = 0;
192 char prevWord[32] = "";
193
194 /***************************************/
195 // Find prev word
196 // The logic for going up or down a level depends on a the previous keyword
197 // This code could be cleaned up.
198 int end = 0;
199 unsigned int j;
200 for(j = startPos; j>0; j--)
201 {
202 char ch = styler.SafeGetCharAt(j);
203 char chPrev = styler.SafeGetCharAt(j-1);
204 int style = styler.StyleAt(j);
205 int stylePrev = styler.StyleAt(j-1);
206 if ((stylePrev != SCE_VHDL_COMMENT) && (stylePrev != SCE_VHDL_STRING))
207 {
208 if(IsAWordChar(chPrev) && !IsAWordChar(ch))
209 {
210 end = j-1;
211 }
212 }
213 if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING))
214 {
215 if(!IsAWordChar(chPrev) && IsAWordStart(ch) && (end != 0))
216 {
217 char s[32];
218 unsigned int k;
219 for(k=0; (k<31 ) && (k<end-j+1 ); k++) {
220 s[k] = static_cast<char>(tolower(styler[j+k]));
221 }
222 s[k] = '\0';
223
224 if(keywords.InList(s)) {
225 strcpy(prevWord, s);
226 break;
227 }
228 }
229 }
230 }
231 for(j=j+strlen(prevWord); j<endPos; j++)
232 {
233 char ch = styler.SafeGetCharAt(j);
234 int style = styler.StyleAt(j);
235 if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING))
236 {
237 if((ch == ';') && (strcmp(prevWord, "end") == 0))
238 {
239 strcpy(prevWord, ";");
240 }
241 }
242 }
243
244 char chNext = styler[startPos];
245 char chPrev = '\0';
246 char chNextNonBlank;
247 int styleNext = styler.StyleAt(startPos);
248 int style = initStyle;
249 //Platform::DebugPrintf("Line[%04d] Prev[%20s] ************************* Level[%x]\n", lineCurrent+1, prevWord, levelCurrent);
250
251 /***************************************/
252 for (unsigned int i = startPos; i < endPos; i++)
253 {
254 char ch = chNext;
255 chNext = styler.SafeGetCharAt(i + 1);
256 chPrev = styler.SafeGetCharAt(i - 1);
257 chNextNonBlank = chNext;
258 unsigned int j = i+1;
259 while(IsABlank(chNextNonBlank) && j<endPos)
260 {
261 j ++ ;
262 chNextNonBlank = styler.SafeGetCharAt(j);
263 }
264 style = styleNext;
265 styleNext = styler.StyleAt(i + 1);
266 bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
267
268 if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
269 {
270 if(!IsCommentLine(lineCurrent-1, styler) && IsCommentLine(lineCurrent+1, styler))
271 {
272 levelNext++;
273 }
274 else if(IsCommentLine(lineCurrent-1, styler) && !IsCommentLine(lineCurrent+1, styler))
275 {
276 levelNext--;
277 }
278 }
279
280 if ((style == SCE_VHDL_OPERATOR) && foldAtParenthese)
281 {
282 if(ch == '(') {
283 levelNext++;
284 } else if (ch == ')') {
285 levelNext--;
286 }
287 }
288
289 if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING))
290 {
291 if((ch == ';') && (strcmp(prevWord, "end") == 0))
292 {
293 strcpy(prevWord, ";");
294 }
295
296 if(!IsAWordChar(chPrev) && IsAWordStart(ch))
297 {
298 lastStart = i;
299 }
300
301 if(iswordchar(ch) && !iswordchar(chNext)) {
302 char s[32];
303 unsigned int k;
304 for(k=0; (k<31 ) && (k<i-lastStart+1 ); k++) {
305 s[k] = static_cast<char>(tolower(styler[lastStart+k]));
306 }
307 s[k] = '\0';
308
309 if(keywords.InList(s))
310 {
311 if (
312 strcmp(s, "architecture") == 0 ||
313 strcmp(s, "case") == 0 ||
314 strcmp(s, "component") == 0 ||
315 strcmp(s, "entity") == 0 ||
316 strcmp(s, "generate") == 0 ||
317 strcmp(s, "loop") == 0 ||
318 strcmp(s, "package") ==0 ||
319 strcmp(s, "process") == 0 ||
320 strcmp(s, "record") == 0 ||
321 strcmp(s, "then") == 0)
322 {
323 if (strcmp(prevWord, "end") != 0)
324 {
325 if (levelMinCurrentElse > levelNext) {
326 levelMinCurrentElse = levelNext;
327 }
328 levelNext++;
329 }
330 } else if (
331 strcmp(s, "procedure") == 0 ||
332 strcmp(s, "function") == 0)
333 {
334 if (strcmp(prevWord, "end") != 0) // check for "end procedure" etc.
335 { // This code checks to see if the procedure / function is a definition within a "package"
336 // rather than the actual code in the body.
337 int BracketLevel = 0;
338 for(int j=i+1; j<styler.Length(); j++)
339 {
340 int LocalStyle = styler.StyleAt(j);
341 char LocalCh = styler.SafeGetCharAt(j);
342 if(LocalCh == '(') BracketLevel++;
343 if(LocalCh == ')') BracketLevel--;
344 if(
345 (BracketLevel == 0) &&
346 (LocalStyle != SCE_VHDL_COMMENT) &&
347 (LocalStyle != SCE_VHDL_STRING) &&
348 !iswordchar(styler.SafeGetCharAt(j-1)) &&
349 styler.Match(j, "is") &&
350 !iswordchar(styler.SafeGetCharAt(j+2)))
351 {
352 if (levelMinCurrentElse > levelNext) {
353 levelMinCurrentElse = levelNext;
354 }
355 levelNext++;
356 break;
357 }
358 if((BracketLevel == 0) && (LocalCh == ';'))
359 {
360 break;
361 }
362 }
363 }
364
365 } else if (strcmp(s, "end") == 0) {
366 levelNext--;
367 } else if(strcmp(s, "elsif") == 0) { // elsif is followed by then so folding occurs correctly
368 levelNext--;
369 } else if (strcmp(s, "else") == 0) {
370 if(strcmp(prevWord, "when") != 0) // ignore a <= x when y else z;
371 {
372 levelMinCurrentElse = levelNext - 1; // VHDL else is all on its own so just dec. the min level
373 }
374 } else if(
375 ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "architecture") == 0)) ||
376 ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "function") == 0)) ||
377 ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "procedure") == 0)))
378 {
379 levelMinCurrentBegin = levelNext - 1;
380 }
381 //Platform::DebugPrintf("Line[%04d] Prev[%20s] Cur[%20s] Level[%x]\n", lineCurrent+1, prevWord, s, levelCurrent);
382 strcpy(prevWord, s);
383 }
384 }
385 }
386 if (atEOL) {
387 int levelUse = levelCurrent;
388
389 if (foldAtElse && (levelMinCurrentElse < levelUse)) {
390 levelUse = levelMinCurrentElse;
391 }
392 if (foldAtBegin && (levelMinCurrentBegin < levelUse)) {
393 levelUse = levelMinCurrentBegin;
394 }
395 int lev = levelUse | levelNext << 16;
396 if (visibleChars == 0 && foldCompact)
397 lev |= SC_FOLDLEVELWHITEFLAG;
398
399 if (levelUse < levelNext)
400 lev |= SC_FOLDLEVELHEADERFLAG;
401 if (lev != styler.LevelAt(lineCurrent)) {
402 styler.SetLevel(lineCurrent, lev);
403 }
404 //Platform::DebugPrintf("Line[%04d] ---------------------------------------------------- Level[%x]\n", lineCurrent+1, levelCurrent);
405 lineCurrent++;
406 levelCurrent = levelNext;
407 //levelMinCurrent = levelCurrent;
408 levelMinCurrentElse = levelCurrent;
409 levelMinCurrentBegin = levelCurrent;
410 visibleChars = 0;
411 }
412 /***************************************/
413 if (!isspacechar(ch)) visibleChars++;
414 }
415
416 /***************************************/
417 // Platform::DebugPrintf("Line[%04d] ---------------------------------------------------- Level[%x]\n", lineCurrent+1, levelCurrent);
418 }
419
420 //=============================================================================
FoldVHDLDoc(unsigned int startPos,int length,int initStyle,WordList * [],Accessor & styler)421 static void FoldVHDLDoc(unsigned int startPos, int length, int initStyle, WordList *[],
422 Accessor &styler) {
423 FoldNoBoxVHDLDoc(startPos, length, initStyle, styler);
424 }
425
426 //=============================================================================
427 static const char * const VHDLWordLists[] = {
428 "Keywords",
429 "Operators",
430 "Attributes",
431 "Standard Functions",
432 "Standard Packages",
433 "Standard Types",
434 "User Words",
435 0,
436 };
437
438
439 LexerModule lmVHDL(SCLEX_VHDL, ColouriseVHDLDoc, "vhdl", FoldVHDLDoc, VHDLWordLists);
440
441
442 // Keyword:
443 // access after alias all architecture array assert attribute begin block body buffer bus case component
444 // configuration constant disconnect downto else elsif end entity exit file for function generate generic
445 // group guarded if impure in inertial inout is label library linkage literal loop map new next null of
446 // on open others out package port postponed procedure process pure range record register reject report
447 // return select severity shared signal subtype then to transport type unaffected units until use variable
448 // wait when while with
449 //
450 // Operators:
451 // abs and mod nand nor not or rem rol ror sla sll sra srl xnor xor
452 //
453 // Attributes:
454 // left right low high ascending image value pos val succ pred leftof rightof base range reverse_range
455 // length delayed stable quiet transaction event active last_event last_active last_value driving
456 // driving_value simple_name path_name instance_name
457 //
458 // Std Functions:
459 // now readline read writeline write endfile resolved to_bit to_bitvector to_stdulogic to_stdlogicvector
460 // to_stdulogicvector to_x01 to_x01z to_UX01 rising_edge falling_edge is_x shift_left shift_right rotate_left
461 // rotate_right resize to_integer to_unsigned to_signed std_match to_01
462 //
463 // Std Packages:
464 // std ieee work standard textio std_logic_1164 std_logic_arith std_logic_misc std_logic_signed
465 // std_logic_textio std_logic_unsigned numeric_bit numeric_std math_complex math_real vital_primitives
466 // vital_timing
467 //
468 // Std Types:
469 // boolean bit character severity_level integer real time delay_length natural positive string bit_vector
470 // file_open_kind file_open_status line text side width std_ulogic std_ulogic_vector std_logic
471 // std_logic_vector X01 X01Z UX01 UX01Z unsigned signed
472 //
473
474