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