• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 25986469887

17 May 2026 08:44AM UTC coverage: 92.22% (-0.04%) from 92.258%
25986469887

push

github

nickg
Add PicoRV32 to regression tests

78454 of 85073 relevant lines covered (92.22%)

648774.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.12
/src/scan.c
1
//
2
//  Copyright (C) 2014-2025  Nick Gasson
3
//
4
//  This program is free software: you can redistribute it and/or modify
5
//  it under the terms of the GNU General Public License as published by
6
//  the Free Software Foundation, either version 3 of the License, or
7
//  (at your option) any later version.
8
//
9
//  This program is distributed in the hope that it will be useful,
10
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
//  GNU General Public License for more details.
13
//
14
//  You should have received a copy of the GNU General Public License
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "array.h"
20
#include "diag.h"
21
#include "ident.h"
22
#include "option.h"
23
#include "scan.h"
24
#include "hash.h"
25

26
#include <assert.h>
27
#include <errno.h>
28
#include <fcntl.h>
29
#include <unistd.h>
30
#include <string.h>
31
#include <stdarg.h>
32
#include <stdlib.h>
33

34
typedef struct {
35
   bool  result;
36
   bool  taken;
37
   loc_t loc;
38
} cond_state_t;
39

40
typedef A(cond_state_t) cond_stack_t;
41

42
typedef struct {
43
   ident_t     macro;
44
   char       *include;
45
   loc_t       expandloc;
46
   file_ref_t  file_ref;
47
} macro_expansion_t;
48

49
typedef A(macro_expansion_t) macro_stack_t;
50

51
typedef A(vlog_version_t) keywords_stack_t;
52

53
typedef A(char *) string_list_t;
54

55
typedef struct {
56
   const char *file_start;
57
   size_t      file_sz;
58
   const char *read_ptr;
59
   int         colno;
60
   int         lineno;
61
   int         lookahead;
62
   file_ref_t  file_ref;
63
} input_buf_t;
64

65
typedef A(input_buf_t) buf_stack_t;
66

67
static input_buf_t       input_buf;
68
static buf_stack_t       buf_stack;
69
static hdl_kind_t        src_kind;
70
static int               pperrors;
71
static cond_stack_t      cond_stack;
72
static shash_t          *pp_defines;
73
static macro_stack_t     macro_stack;
74
static vlog_version_t    default_keywords = VLOG_1800_2023;
75
static keywords_stack_t  keywords_stack;
76
static string_list_t     include_dirs;
77

78
extern int yylex(void);
79

80
extern void reset_scanner(void);
81

82
static bool pp_cond_analysis_expr(void);
83
static void pp_defines_init(void);
84

85
yylval_t yylval;
86
loc_t yylloc;
87

88
void input_from_buffer(const char *buf, size_t len, file_ref_t file_ref,
7,543✔
89
                       hdl_kind_t kind)
90
{
91
   pp_defines_init();
7,543✔
92

93
   reset_scanner();
7,543✔
94

95
   yylloc = LOC_INVALID;
7,543✔
96

97
   input_buf.file_start = buf;
7,543✔
98
   input_buf.file_sz    = len;
7,543✔
99
   input_buf.read_ptr   = buf;
7,543✔
100
   input_buf.lineno     = 1;
7,543✔
101
   input_buf.colno      = 0;
7,543✔
102
   input_buf.lookahead  = -1;
7,543✔
103
   input_buf.file_ref   = file_ref;
7,543✔
104

105
   src_kind = kind;
7,543✔
106
   pperrors = 0;
7,543✔
107

108
   switch (kind) {
7,543✔
109
   case SOURCE_VERILOG:
1,586✔
110
      reset_verilog_parser();
1,586✔
111
      break;
1,586✔
112
   case SOURCE_VHDL:
5,933✔
113
      reset_vhdl_parser();
5,933✔
114
      break;
5,933✔
115
   case SOURCE_SDF:
24✔
116
      reset_sdf_parser();
24✔
117
      break;
24✔
118
   }
119
}
7,543✔
120

121
void input_from_file(const char *file)
6,781✔
122
{
123
   // TODO: need a more sophisticated mechanism to determine HDL type
124
   hdl_kind_t kind = SOURCE_VHDL;
6,781✔
125
   size_t len = strlen(file);
6,781✔
126
   if (len > 2 && file[len - 2] == '.' && file[len - 1] == 'v') {
6,781✔
127
      kind = SOURCE_VERILOG;
740✔
128
      set_default_keywords(VLOG_1364_2001);
740✔
129
   }
130
   else if (len > 3 && strcmp(file + len - 3, ".sv") == 0) {
6,041✔
131
      kind = SOURCE_VERILOG;
86✔
132
      set_default_keywords(VLOG_1800_2023);
86✔
133
   }
134
   else if (len > 4 && !strcmp(&(file[len - 4]), ".sdf")) {
5,955✔
135
      kind = SOURCE_SDF;
24✔
136
   }
137

138
   int fd;
6,781✔
139
   if (strcmp(file, "-") == 0)
6,781✔
140
      fd = STDIN_FILENO;
141
   else {
142
      fd = open(file, O_RDONLY);
6,705✔
143
      if (fd < 0)
6,705✔
144
         fatal_errno("opening %s", file);
×
145
   }
146

147
   file_info_t info;
6,781✔
148
   if (!get_handle_info(fd, &info))
6,781✔
149
      fatal_errno("%s: cannot get file info", file);
×
150

151
   if (info.type == FILE_FIFO) {
6,781✔
152
      // Read all the data from the pipe into a buffer
153
      size_t bufsz = 16384, total = 0;
72✔
154
      char *buf = xmalloc(bufsz);
72✔
155
      int nbytes;
144✔
156
      do {
144✔
157
         if (bufsz - 1 - input_buf.file_sz == 0)
144✔
158
            buf = xrealloc(buf, bufsz *= 2);
×
159

160
         nbytes = read(fd, buf + total, bufsz - 1 - total);
144✔
161
         if (nbytes < 0)
144✔
162
            fatal_errno("read");
×
163

164
         total += nbytes;
144✔
165
         buf[total] = '\0';
144✔
166
      } while (nbytes > 0);
144✔
167

168
      input_from_buffer(buf, total, FILE_INVALID, kind);
72✔
169
   }
170
   else if (info.type == FILE_REGULAR) {
6,709✔
171
      void *map = NULL;
6,709✔
172
      if (info.size > 0)
6,709✔
173
         map = map_file(fd, info.size);
6,708✔
174

175
      file_ref_t file_ref = loc_file_ref(file, map);
6,709✔
176

177
      input_from_buffer(map, info.size, file_ref, kind);
6,709✔
178
   }
179
   else
180
      fatal("opening %s: not a regular file", file);
×
181

182
   close(fd);
6,781✔
183
}
6,781✔
184

185
void push_buffer(const char *buf, size_t len, file_ref_t file_ref)
61✔
186
{
187
   APUSH(buf_stack, input_buf);
61✔
188

189
   yylloc = LOC_INVALID;
61✔
190

191
   input_buf.file_start = buf;
61✔
192
   input_buf.file_sz    = len;
61✔
193
   input_buf.read_ptr   = buf;
61✔
194
   input_buf.lineno     = 1;
61✔
195
   input_buf.colno      = 0;
61✔
196
   input_buf.lookahead  = -1;
61✔
197
   input_buf.file_ref   = file_ref;
61✔
198
}
61✔
199

200
void push_file(const char *file, const loc_t *srcloc)
7✔
201
{
202
   int fd = open(file, O_RDONLY);
7✔
203
   if (fd < 0) {
7✔
204
      for (int i = 0; i < include_dirs.count; i++) {
9✔
205
         char path[PATH_MAX];
7✔
206
         checked_sprintf(path, sizeof(path), "%s" DIR_SEP "%s",
7✔
207
                         include_dirs.items[i], file);
7✔
208
         if ((fd = open(path, O_RDONLY)) != -1)
7✔
209
            break;
210
      }
211
   }
212

213
   if (fd < 0) {
7✔
214
      diag_t *d = diag_new(DIAG_ERROR, srcloc);
2✔
215
      if (errno != ENOENT)
2✔
216
         diag_printf(d, "opening %s: %s", file, last_os_error());
×
217
      else {
218
         char *cwd LOCAL = getcwd(NULL, 0);
4✔
219
         diag_printf(d, "cannot find %s in the current working directory %s",
2✔
220
                     file, cwd);
221
         if (include_dirs.count > 0) {
2✔
222
            diag_printf(d, " or any of the supplied include directories");
2✔
223
            for (int i = 0; i < include_dirs.count; i++)
4✔
224
               diag_hint(d, NULL, "searched include directory %s",
2✔
225
                         include_dirs.items[i]);
2✔
226
         }
227
         diag_hint(d, NULL, "add additional include directories with the "
2✔
228
                   "$bold$-I$$ option");
229
      }
230
      diag_emit(d);
2✔
231

232
      push_buffer("", 0, FILE_INVALID);
2✔
233
   }
234
   else {
235
      file_info_t info;
5✔
236
      if (!get_handle_info(fd, &info))
5✔
237
         fatal_errno("%s: cannot get file info", file);
×
238

239
      void *map = NULL;
5✔
240
      if (info.size > 0)
5✔
241
         map = map_file(fd, info.size);
5✔
242

243
      file_ref_t file_ref = loc_file_ref(file, map);
5✔
244

245
      push_buffer(map, info.size, file_ref);
5✔
246
   }
247
}
7✔
248

249
void pop_buffer(void)
61✔
250
{
251
   input_buf = APOP(buf_stack);
61✔
252
   yylloc = LOC_INVALID;
61✔
253
}
61✔
254

255
void add_include_dir(const char *str)
5✔
256
{
257
   APUSH(include_dirs, xstrdup(str));
5✔
258
}
5✔
259

260
hdl_kind_t source_kind(void)
6,111✔
261
{
262
   return src_kind;
6,111✔
263
}
264

265
scan_buf_t get_input_buffer(void)
609,753✔
266
{
267
   const ptrdiff_t navail =
609,753✔
268
      (input_buf.file_start - input_buf.read_ptr) + input_buf.file_sz;
609,753✔
269
   assert(navail >= 0);
609,753✔
270

271
   return (scan_buf_t) {
1,219,506✔
272
      .ptr = navail > 0 ? input_buf.read_ptr : NULL,
609,753✔
273
      .cap = navail,
274
      .len = 0,
275
   };
276
}
277

278
bool scan_next(scan_buf_t *buf, char *ch)
609,743✔
279
{
280
   assert(buf->len <= buf->cap);
609,743✔
281
   if (buf->len == buf->cap)
609,743✔
282
      return false;
283
   else {
284
      *ch = buf->ptr[buf->len++];
608,902✔
285
      return true;
608,902✔
286
   }
287
}
288

289
bool scan_peek(scan_buf_t buf, char *ch)
1,175,827✔
290
{
291
   assert(buf.len <= buf.cap);
1,175,827✔
292
   if (buf.len == buf.cap)
1,175,827✔
293
      return false;
294
   else {
295
      *ch = buf.ptr[buf.len];
1,175,792✔
296
      return true;
1,175,792✔
297
   }
298
}
299

300
void scan_advance(scan_buf_t *buf)
732,019✔
301
{
302
   assert(buf->len <= buf->cap);
732,019✔
303
   buf->len++;
732,019✔
304
}
732,019✔
305

306
void scan_next_line(void)
43,728✔
307
{
308
   input_buf.colno = 0;
43,728✔
309
   input_buf.lineno += 1;
43,728✔
310
}
43,728✔
311

312
token_t make_token(token_t tok, scan_buf_t buf, yylval_t lval)
608,902✔
313
{
314
   assert(buf.len <= buf.cap);
608,902✔
315
   assert(buf.len > 0);
608,902✔
316
   assert(buf.ptr == input_buf.read_ptr);
608,902✔
317

318
   if (macro_stack.count == 0 || input_buf.file_ref != FILE_INVALID) {
1,217,804✔
319
      const int first_col = input_buf.colno;
608,902✔
320
      input_buf.colno += buf.len;
608,902✔
321

322
      const int last_col = first_col + buf.len - 1;
608,902✔
323

324
      extern loc_t yylloc;
608,902✔
325
      yylloc = get_loc(input_buf.lineno, first_col, input_buf.lineno,
608,902✔
326
                       last_col, input_buf.file_ref);
608,902✔
327
   }
328
   else {
329
      yylloc = LOC_INVALID;
×
330
      for (int i = 0; i < macro_stack.count && loc_invalid_p(&yylloc); i++)
×
331
         yylloc = macro_stack.items[i].expandloc;
×
332
   }
333

334
   input_buf.read_ptr += buf.len;
608,902✔
335

336
   yylval = lval;
608,902✔
337
   return tok;
608,902✔
338
}
339

340
int get_next_char(char *b, int max_buffer)
14,624✔
341
{
342
   const ptrdiff_t navail =
14,624✔
343
      (input_buf.file_start - input_buf.read_ptr) + input_buf.file_sz;
14,624✔
344
   assert(navail >= 0);
14,624✔
345

346
   if (navail == 0)
14,624✔
347
      return 0;
348

349
   const int nchars = MIN(navail, max_buffer);
7,846✔
350

351
   memcpy(b, input_buf.read_ptr, nchars);
7,846✔
352
   input_buf.read_ptr += nchars;
7,846✔
353

354
   return nchars;
7,846✔
355
}
356

357
void begin_token(char *tok, int length)
7,764,950✔
358
{
359
   // Newline must match as a single token for the logic below to work
360
   assert(strchr(tok, '\n') == NULL || length == 1);
7,764,950✔
361

362
   if (macro_stack.count == 0 || input_buf.file_ref != FILE_INVALID) {
15,529,803✔
363
      const int first_col = input_buf.colno;
7,764,853✔
364
      if (*tok == '\n') {
7,764,853✔
365
         input_buf.colno = 0;
538,557✔
366
         input_buf.lineno += 1;
538,557✔
367
      }
368
      else
369
         input_buf.colno += length;
7,226,296✔
370

371
      const int last_col = first_col + length - 1;
7,764,853✔
372

373
      extern loc_t yylloc;
7,764,853✔
374
      yylloc = get_loc(input_buf.lineno, first_col, input_buf.lineno,
7,764,853✔
375
                       last_col, input_buf.file_ref);
7,764,853✔
376
   }
377
   else {
378
      yylloc = LOC_INVALID;
97✔
379
      for (int i = 0; i < macro_stack.count && loc_invalid_p(&yylloc); i++)
194✔
380
         yylloc = macro_stack.items[i].expandloc;
97✔
381
   }
382
}
7,764,950✔
383

384
const char *token_str(token_t tok)
355✔
385
{
386
   if (tok == tEOF)
355✔
387
      return "end of file";
388
   else if (tok < 128) {
352✔
389
      static char buf[2];
72✔
390
      buf[0] = tok;
72✔
391
      return buf;
72✔
392
   }
393
   else {
394
      static const char *token_strs[] = {
280✔
395
         "identifier", "entity", "is", "end", "generic", "port", "constant",
396
         "component", "configuration", "architecture", "of", "begin", "for",
397
         "type", "to", "all", "in", "out", "buffer", "bus", "unaffected",
398
         "signal", "downto", "process", "postponed", "wait", "report", ":=",
399
         "integer", "string", "error", "inout", "linkage", "variable", "if",
400
         "range", "subtype", "units", "package", "library", "use", "null",
401
         "function", "impure", "return", "pure", "array", "<>", "=>", "others",
402
         "assert", "severity", "on", "map", "then", "else", "elsif", "body",
403
         "while", "loop", "after", "alias", "attribute", "procedure", "exit",
404
         "next", "when", "case", "label", "group", "literal", "inertial",
405
         "transport", "reject", "bit string", "block", "with", "select",
406
         "generate", "access", "file", "open", "real", "until", "record",
407
         "new", "shared", "and", "or", "nand", "nor", "xor", "xnor", "/=",
408
         "<=", ">=", "**", "sll", "srl", "sla", "sra", "rol", "ror", "mod",
409
         "rem", "abs", "not", "guarded", "reverse_range", "protected",
410
         "context", "`if", "`else", "`elsif", "`end", "`error", "`warning",
411
         "translate_off", "translate_on", "?=", "?/=", "?<", "?<=", "?>",
412
         "?>=", "register", "disconnect", "??", "<<", ">>", "force", "release",
413
         "parameter", "coverage on", "coverage off", "PSL directive", "always",
414
         "->", "<->", "default", "clock", "next!", "never", "eventually!",
415
         "next_a", "next_a!", "next_e", "next_e!", "next_event", "next_event!",
416
         "module", "endmodule", "input", "output", "reg", "posedge", "negedge",
417
         "initial", "wire", "unsigned number", "assume", "assume_guarantee",
418
         "restrict", "restrict_guarantee", "strong", "fairness", "cover",
419
         "property", "sequence", "const", "mutable", "hdltype", "boolean",
420
         "bit", "bitvector", "numeric", "string", "[*", "[+]", "[->", "[=",
421
         "&&", "within", "system task", "view", "private", "prev", "stable",
422
         "rose", "fell", "ended", "nondet", "nondetv", "union", "translate on",
423
         "translate off", "until!", "until_", "until!_", "`timescale",
424
         "supply0", "supply1", "pulldown", "pullup", "===", "!==", "==", "!=",
425
         "(*", "*)", "number", "forever", "[[", "]]", "specify", "endspecify",
426
         "primitive", "endprimitive", "table", "endtable", "assign",
427
         "level symbol", "edge symbol", "edge indicator", "buf", "||",
428
         "scalar constant (0)", "scalar constant (1)", "delay file",
429
         "sdf version", "design", "date", "vendor", "program", "version",
430
         "divider", "voltage", "temperature", "cell", "celltype", "instance",
431
         "delay", "timing check", "timing env", "path pulse",
432
         "path pulse percent", "IO path", "retain", "cond", "condelse",
433
         "interconnect", "net delay", "device", "setup", "hold", "setuphold",
434
         "recovery", "removal", "recrem", "skew", "bidirectional skew", "width",
435
         "period", "nochange", "cond", "scond", "ccond", "path constraint",
436
         "period constraint", "sum", "diff", "skew constraint", "exception",
437
         "name", "arrival", "departure", "slack", "waveform", "increment",
438
         "absolute", "~&", "~|", "~^", "struct", "packed", "void", "byte",
439
         "shortint", "longint", "int", "integer", "time", "typedef", "logic",
440
         "enum", "tagged", "abort", "sync_abort", "async_abort", "before",
441
         "before!", "before_", "before!_", "|->", "|=>", "next", "inf",
442
         "repeat", "do", "endpoint", "<<", ">>", "<<<", ">>>", "task",
443
         "endtask", "endfunction", "`begin_keywords", "`end_keywords", "real",
444
         "shortreal", "realtime", "`__nvc_push", "`__nvc_pop", "++", "--",
445
         "var", "`default_nettype", "tri", "tri0", "tri1", "wand", "triand",
446
         "wor", "trior", "trireg", "uwire", "none", "localparam", "always_comb",
447
         "always_ff", "always_latch", "(*)", "endcase", "casex", "casez",
448
         "ifnone", "edge", "*>", "$setup", "$hold", "$recovery", "$removal",
449
         "$setuphold", "$recrem", "$width", "+:", "-:", "endgenerate",
450
         "`resetall", "event", "&&&", "specparam", "fork", "join", "automatic",
451
         "genvar", "highz0", "highz1", "strong0", "strong1", "pull0", "pull1",
452
         "weak0", "weak1", "small", "medium", "large", "vectored", "scalared",
453
         "`unconnected_drive", "`nounconnected_drive", "deassign", "signed",
454
         "unsigned", "disable", "class", "chandle", "export", "import",
455
         "endpackage", "extends", "this", "nettype", "ref", "super", "static",
456
         "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", "<<<=",
457
         ">>>=", "bufif0", "bufif1", "notif0", "notif1", "`protect",
458
         "begin_protected", "end_protected", "endprogram", "endclass",
459
         "virtual", "::", "defparam", "tran", "tranif0", "tranif1", "rtran",
460
         "rtranif0", "rtranif1", "`define", "macro end", "macro usage",
461
         "`ifdef", "`ifndef", "`endif", "text", "`include", "`undef",
462
         "`undefall", "white space", "newline", "\\", "comment", "$.",
463
         "final",
464
      };
465

466
      if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs))
280✔
467
         return token_strs[tok - 200];
280✔
468
   }
469

470
   return "???";
471
}
472

473
void free_token(token_t tok, yylval_t *lval)
2,333✔
474
{
475
   if (tok == tBITSTRING || tok == tUNSNUM)
2,333✔
476
      free(lval->str);
×
477
   else if (tok == tSTRING)
2,333✔
478
      tb_free(lval->text);
9✔
479

480
   DEBUG_ONLY(lval->str = NULL);
2,333✔
481
}
2,333✔
482

483
ident_t error_marker(void)
848✔
484
{
485
   return well_known(W_ERROR);
848✔
486
}
487

488
static void pp_defines_init(void)
48,437✔
489
{
490
   if (pp_defines != NULL)
48,437✔
491
      return;
492

493
   pp_defines = shash_new(16);
6,679✔
494

495
   pp_defines_add("VHDL_VERSION", standard_text(standard()));
6,679✔
496
   pp_defines_add("TOOL_TYPE",    "SIMULATION");
6,679✔
497
   pp_defines_add("TOOL_VENDOR",  PACKAGE_URL);
6,679✔
498
   pp_defines_add("TOOL_NAME",    PACKAGE_NAME);
6,679✔
499
   pp_defines_add("TOOL_EDITION", "GPL");
6,679✔
500
   pp_defines_add("TOOL_VERSION", PACKAGE_VERSION);
6,679✔
501
}
502

503
void pp_defines_add(const char *name, const char *value)
40,094✔
504
{
505
   pp_defines_init();
40,094✔
506

507
   bool valid = isalpha_iso88591(name[0]) || name[0] == '_';
40,094✔
508
   for (const char *p = name + 1; valid && *p; p++)
434,247✔
509
      valid &= isalnum_iso88591(*p) || *p == '_';
394,153✔
510

511
   if (!valid)
40,094✔
512
      errorf("\"%s\" is not a valid conditional analysis identifier", name);
×
513

514
   char *existing_val = shash_get(pp_defines, name);
40,094✔
515
   if (existing_val) {
40,094✔
516
      warnf("conditional analysis identifier '%s' already defined (%s)",
×
517
            name, existing_val);
518
      free(existing_val);
×
519
   }
520

521
   shash_put(pp_defines, name, xstrdup(value));
40,094✔
522
}
40,094✔
523

524
const char *pp_defines_get(const char *name)
32✔
525
{
526
   pp_defines_init();
32✔
527
   return shash_get(pp_defines, name);
32✔
528
}
529

530
void pp_defines_iter(pp_iter_t fn, void *ctx)
768✔
531
{
532
   pp_defines_init();
768✔
533

534
   const char *key;
768✔
535
   void *value;
768✔
536
   for (hash_iter_t it = HASH_BEGIN;
768✔
537
        shash_iter(pp_defines, &it, &key, &value); )
5,392✔
538
      (*fn)(key, value, ctx);
4,624✔
539
}
768✔
540

541
static int pp_yylex(void)
2,560,395✔
542
{
543
   const int tok = input_buf.lookahead != -1 ? input_buf.lookahead : yylex();
2,560,395✔
544
   input_buf.lookahead = -1;
2,560,395✔
545
   return tok;
2,560,395✔
546
}
547

548
static void pp_error(const char *fmt, ...)
3✔
549
{
550
   va_list ap;
3✔
551
   va_start(ap, fmt);
3✔
552

553
   if (pperrors++ == 0 || opt_get_int(OPT_UNIT_TEST)) {
3✔
554
      diag_t *d = diag_new(DIAG_ERROR, &yylloc);
3✔
555
      diag_vprintf(d, fmt, ap);
3✔
556
      diag_emit(d);
3✔
557
   }
558

559
   va_end(ap);
3✔
560
}
3✔
561

562
static bool pp_expect(int expect)
144✔
563
{
564
   const int got = pp_yylex();
144✔
565
   if (got != expect) {
144✔
566
      pp_error("expected $yellow$%s$$ while parsing conditional "
×
567
               "directive but found $yellow$%s$$", token_str(expect),
568
               token_str(got));
569
      return false;
×
570
   }
571

572
   return true;
573
}
574

575
static bool pp_cond_analysis_relation(void)
67✔
576
{
577
   // ( conditional_analysis_expression )
578
   //   | not ( conditional_analysis_expression )
579
   //   | conditional_analysis_identifier = string_literal
580
   //   | conditional_analysis_identifier /= string_literal
581
   //   | conditional_analysis_identifier < string_literal
582
   //   | conditional_analysis_identifier <= string_literal
583
   //   | conditional_analysis_identifier > string_literal
584
   //   | conditional_analysis_identifier >= string_literal
585

586
   bool result = false;
67✔
587
   token_t tok = pp_yylex();
67✔
588

589
   switch (tok) {
67✔
590
   case tLPAREN:
×
591
      result = pp_cond_analysis_expr();
×
592
      pp_expect(tRPAREN);
×
593
      break;
×
594

595
   case tNOT:
1✔
596
      pp_expect(tLPAREN);
1✔
597
      result = !pp_cond_analysis_expr();
1✔
598
      pp_expect(tRPAREN);
1✔
599
      break;
1✔
600

601
   case tID:
66✔
602
      {
603
         const char *name = istr(yylval.ident);
66✔
604
         token_t rel = pp_yylex();
66✔
605

606
         if (pp_expect(tSTRING)) {
66✔
607
            const char *value = shash_get(pp_defines, name);
66✔
608
            if (value == NULL)
66✔
609
               pp_error("undefined conditional analysis identifier %s", name);
1✔
610
            else {
611
               const char *cmp = tb_get(yylval.text);
65✔
612

613
               switch (rel) {
65✔
614
               case tEQ:
18✔
615
                  result = strcmp(value, cmp) == 0;
18✔
616
                  break;
18✔
617
               case tNEQ:
1✔
618
                  result = strcmp(value, cmp) != 0;
1✔
619
                  break;
1✔
620
               case tLT:
45✔
621
                  result = strcmp(value, cmp) < 0;
45✔
622
                  break;
45✔
623
               case tLE:
×
624
                  result = strcmp(value, cmp) <= 0;
×
625
                  break;
×
626
               case tGT:
×
627
                  result = strcmp(value, cmp) > 0;
×
628
                  break;
×
629
               case tGE:
1✔
630
                  result = strcmp(value, cmp) >= 0;
1✔
631
                  break;
1✔
632
               default:
×
633
                  pp_error("expected conditional analysis relation "
×
634
                           "but found $yellow$%s$$", token_str(rel));
635
                  break;
×
636
               }
637
            }
638

639
            tb_free(yylval.text);
66✔
640
         }
641
      }
642
      break;
643

644
   default:
×
645
      pp_error("unexpected $yellow$%s$$ while parsing conditional "
×
646
               "analysis relation", token_str(tok));
647
   }
648

649
   return result;
67✔
650
}
651

652
static bool pp_cond_analysis_expr(void)
66✔
653
{
654
   // conditional_analysis_relation
655
   //   | conditional_analysis_relation { and conditional_analysis_relation }
656
   //   | conditional_analysis_relation { or conditional_analysis_relation }
657
   //   | conditional_analysis_relation { xor conditional_analysis_relation }
658
   //   | conditional_analysis_relation { xnor conditional_analysis_relation }
659

660
   const bool lhs = pp_cond_analysis_relation();
66✔
661
   switch ((input_buf.lookahead = pp_yylex())) {
66✔
662
   case tAND:
1✔
663
      input_buf.lookahead = -1;
1✔
664
      return pp_cond_analysis_relation() && lhs;
1✔
665
   case tOR:
×
666
      input_buf.lookahead = -1;
×
667
      return pp_cond_analysis_relation() || lhs;
×
668
   case tXOR:
×
669
      input_buf.lookahead = -1;
×
670
      return pp_cond_analysis_relation() ^ lhs;
×
671
   case tXNOR:
×
672
      input_buf.lookahead = -1;
×
673
      return !(pp_cond_analysis_relation() ^ lhs);
×
674
   default:
675
      return lhs;
676
   }
677
   return lhs;
678
}
679

680
static void macro_hint_cb(diag_t *d, void *ctx)
2✔
681
{
682
   assert(macro_stack.count > 0);
2✔
683

684
   for (int i = 0; i < macro_stack.count; i++) {
5✔
685
      const macro_expansion_t *me = &(macro_stack.items[i]);
3✔
686
      if (me->include != NULL)
3✔
687
         diag_hint(d, &me->expandloc, "while processing `include \"%s\" "
1✔
688
                   "directive", me->include);
689
      else
690
         diag_hint(d, NULL, "while expanding macro %s", istr(me->macro));
2✔
691
   }
692
}
2✔
693

694
static void pp_nvc_push(void)
9✔
695
{
696
   token_t tok;
9✔
697
   macro_expansion_t exp = {};
9✔
698
   unsigned first_line, first_column, column_delta;
9✔
699

700
   if ((tok = pp_yylex()) != tSTRING)
9✔
701
      goto error;
×
702

703
   const char *text = tb_get(yylval.text);
9✔
704
   if (text[0] == '`')
9✔
705
      exp.macro = ident_new(text + 1);
5✔
706
   else
707
      exp.include = tb_claim(yylval.text);
4✔
708

709
   if ((tok = pp_yylex()) != tCOMMA)
9✔
710
      goto error;
×
711

712
   if ((tok = pp_yylex()) != tUNSNUM)
9✔
713
      goto error;
×
714

715
   first_line = atoi(yylval.str);
9✔
716
   free(yylval.str);
9✔
717

718
   if ((tok = pp_yylex()) != tCOLON)
9✔
719
      goto error;
×
720

721
   if ((tok = pp_yylex()) != tUNSNUM)
9✔
722
      goto error;
×
723

724
   first_column = atoi(yylval.str);
9✔
725
   free(yylval.str);
9✔
726

727
   if ((tok = pp_yylex()) != tCOMMA)
9✔
728
      goto error;
×
729

730
   if ((tok = pp_yylex()) != tUNSNUM)
9✔
731
      goto error;
×
732

733
   column_delta = atoi(yylval.str);
9✔
734
   free(yylval.str);
9✔
735

736
   exp.expandloc = get_loc(first_line, first_column, first_line,
9✔
737
                           first_column + column_delta, yylloc.file_ref);
9✔
738

739
   input_buf.lineno = 0;
9✔
740
   input_buf.colno = 0;
9✔
741

742
   if (exp.include != NULL)
9✔
743
      input_buf.file_ref = loc_file_ref(exp.include, NULL);
4✔
744
   else
745
      input_buf.file_ref = FILE_INVALID;
5✔
746

747
   yylloc = LOC_INVALID;
9✔
748

749
   if (macro_stack.count == 0)
9✔
750
      diag_add_hint_fn(macro_hint_cb, NULL);
8✔
751

752
   APUSH(macro_stack, exp);
9✔
753
   return;
9✔
754

755
 error:
×
756
   pp_error("unexpected %s while parsing `__nvc_push directive",
×
757
            token_str(tok));
758
}
759

760
static void pp_nvc_pop(void)
5✔
761
{
762
   if (macro_stack.count == 0)
5✔
763
      return;
764

765
   const macro_expansion_t top = APOP(macro_stack);
5✔
766

767
   input_buf.lineno = top.expandloc.first_line - 1;
5✔
768
   input_buf.file_ref = top.expandloc.file_ref;
5✔
769

770
   // Eat the following newline and adjust the next token location
771
   input_buf.lookahead = yylex();
5✔
772

773
   yylloc.first_column +=
5✔
774
      top.expandloc.first_column + top.expandloc.column_delta + 1;
5✔
775

776
   input_buf.colno = yylloc.first_column + yylloc.column_delta;
5✔
777

778
   free(top.include);
5✔
779

780
   if (macro_stack.count == 0)
5✔
781
      diag_remove_hint_fn(macro_hint_cb, NULL);
4✔
782
}
783

784
static void pp_protect_block(void)
1✔
785
{
786
   if (!pp_expect(tBEGINPROT))
1✔
787
      return;
788

789
   diag_t *d = diag_new(DIAG_ERROR, &yylloc);
1✔
790
   diag_printf(d, "`protect directives are not supported");
1✔
791
   diag_hint(d, NULL, "encrypted IP can only be practicably implemented by "
1✔
792
             "proprietary tools as the decryption key is shared between "
793
             "the simulator and IP vendors");
794
   diag_lrm(d, STD_08, "24.1");
1✔
795
   diag_emit(d);
1✔
796

797
   for (;;) {
45✔
798
      token_t token = pp_yylex();
45✔
799
      switch (token) {
45✔
800
      case tEOF:
×
801
         pp_error("unexpected end-of-file before $yellow$end_protected$$");
×
802
         return;
×
803
      case tPROTECT:
10✔
804
         if (pp_yylex() == tENDPROT)
10✔
805
            return;
806
         break;
807
      }
808
   }
809
}
810

811
token_t processed_yylex(void)
2,557,522✔
812
{
813
   assert(input_buf.lookahead == -1);
2,557,522✔
814

815
   for (;;) {
2,559,934✔
816
      token_t token = pp_yylex();
2,559,934✔
817
      switch (token) {
2,559,934✔
818
      case tCONDIF:
61✔
819
         {
820
            cond_state_t new = { .loc = yylloc };
61✔
821
            new.result = new.taken = pp_cond_analysis_expr();
61✔
822

823
            if (cond_stack.count > 0 && !ATOP(cond_stack).result) {
61✔
824
               // Suppress nested conditionals in not-taken branches
825
               new.taken = true;
1✔
826
               new.result = false;
1✔
827
            }
828

829
            pp_expect(tTHEN);
61✔
830

831
            new.loc.column_delta =
61✔
832
               yylloc.first_column + yylloc.column_delta - new.loc.first_column;
61✔
833
            new.loc.line_delta =
61✔
834
               yylloc.first_line + yylloc.line_delta - new.loc.first_line;
61✔
835

836
            APUSH(cond_stack, new);
61✔
837
         }
838
         break;
61✔
839

840
      case tCONDELSIF:
4✔
841
         {
842
            if (cond_stack.count == 0)
4✔
843
               pp_error("unexpected $yellow$%s$$ outside conditional "
1✔
844
                        "analysis block", token_str(token));
845

846
            const bool result = pp_cond_analysis_expr();
4✔
847

848
            if (cond_stack.count > 0) {
4✔
849
               if (!ATOP(cond_stack).taken) {
3✔
850
                  ATOP(cond_stack).result = result;
2✔
851
                  ATOP(cond_stack).taken = result;
2✔
852
               }
853
               else
854
                  ATOP(cond_stack).result = false;
1✔
855
            }
856

857
            pp_expect(tTHEN);
4✔
858
         }
859
         break;
4✔
860

861
      case tCONDELSE:
13✔
862
         {
863
            if (cond_stack.count == 0)
13✔
864
               pp_error("unexpected $yellow$%s$$ outside conditional "
×
865
                        "analysis block", token_str(token));
866
            else {
867
               cond_state_t *cs = &(cond_stack.items[cond_stack.count - 1]);
13✔
868
               cs->result = !(cs->taken);
13✔
869
            }
870
         }
871
         break;
872

873
      case tCONDEND:
61✔
874
         {
875
            if (cond_stack.count == 0)
61✔
876
               pp_error("unexpected $yellow$%s$$ outside conditional "
1✔
877
                        "analysis block", token_str(token));
878
            else
879
               APOP(cond_stack);
60✔
880

881
            if ((input_buf.lookahead = yylex()) == tIF)
61✔
882
               input_buf.lookahead = -1;
52✔
883
         }
884
         break;
885

886
      case tCONDERROR:
10✔
887
      case tCONDWARN:
888
         {
889
            loc_t loc = yylloc;
10✔
890
            if (pp_expect(tSTRING)) {
10✔
891
               loc.column_delta =
10✔
892
                  yylloc.first_column + yylloc.column_delta - loc.first_column;
10✔
893
               loc.line_delta =
10✔
894
                  yylloc.first_line + yylloc.line_delta - loc.first_line;
10✔
895

896
               if (cond_stack.count == 0 || ATOP(cond_stack).result) {
10✔
897
                  const diag_level_t level =
10✔
898
                     token == tCONDWARN ? DIAG_WARN : DIAG_ERROR;
5✔
899
                  diag_t *d = diag_new(level, &loc);
5✔
900
                  diag_printf(d, "%s", tb_get(yylval.text));
5✔
901
                  diag_emit(d);
5✔
902
               }
903

904
               tb_free(yylval.text);
10✔
905
            }
906
         }
907
         break;
10✔
908

909
      case tEOF:
910
         while (cond_stack.count > 0) {
6,777✔
911
            error_at(&(ATOP(cond_stack).loc), "unterminated conditional "
1✔
912
                     "analysis block");
913
            APOP(cond_stack);
1✔
914
         }
915
         return tEOF;
916

917
      case tNVCPUSH:
9✔
918
         pp_nvc_push();
9✔
919
         break;
9✔
920

921
      case tNVCPOP:
5✔
922
         pp_nvc_pop();
5✔
923
         break;
5✔
924

925
      case tPROTECT:
1✔
926
         pp_protect_block();
1✔
927
         break;
1✔
928

929
      default:
2,552,994✔
930
         if (cond_stack.count == 0 || ATOP(cond_stack).result)
2,552,994✔
931
            return token;
2,550,746✔
932
         else {
933
            free_token(token, &yylval);
2,248✔
934
            break;
2,248✔
935
         }
936
      }
937
   }
938
}
939

940
const char *verilog_version_string(vlog_version_t vers)
37✔
941
{
942
   static const char *const map[] = {
37✔
943
      [VLOG_1364_1995] = "1364-1995",
944
      [VLOG_1364_2001_NOCONFIG] = "1364-2001-noconfig",
945
      [VLOG_1364_2001] = "1364-2001",
946
      [VLOG_1364_2005] = "1364-2005",
947
      [VLOG_1800_2005] = "1800-2005",
948
      [VLOG_1800_2009] = "1800-2009",
949
      [VLOG_1800_2012] = "1800-2012",
950
      [VLOG_1800_2017] = "1800-2017",
951
      [VLOG_1800_2023] = "1800-2023",
952
   };
953
   return map[vers];
37✔
954
}
955

956
bool parse_verilog_version(const char *str, vlog_version_t *vers)
7✔
957
{
958
   for (vlog_version_t i = 0; i < VLOG_1800_2023 + 1; i++) {
36✔
959
      if (strcmp(str, verilog_version_string(i)) == 0) {
35✔
960
         *vers = i;
6✔
961
         return true;
6✔
962
      }
963
   }
964

965
   return false;
966
}
967

968
void set_default_keywords(vlog_version_t vers)
836✔
969
{
970
   default_keywords = vers;
836✔
971
}
836✔
972

973
void push_keywords(vlog_version_t vers)
6✔
974
{
975
   APUSH(keywords_stack, vers);
6✔
976
}
6✔
977

978
bool pop_keywords(void)
7✔
979
{
980
   if (keywords_stack.count == 0)
7✔
981
      return false;
982
   else {
983
      APOP(keywords_stack);
6✔
984
      return true;
6✔
985
   }
986
}
987

988
bool get_verilog_keywords(vlog_version_t *vers)
618✔
989
{
990
   if (keywords_stack.count == 0) {
618✔
991
      *vers = default_keywords;
616✔
992
      return false;
616✔
993
   }
994
   else {
995
      *vers = keywords_stack.items[keywords_stack.count - 1];
2✔
996
      return true;
2✔
997
   }
998
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc