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