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