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