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