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

nickg / nvc / 19747205009

27 Nov 2025 08:25AM UTC coverage: 92.555% (-0.01%) from 92.565%
19747205009

push

github

nickg
Handle unary operations on Verilog reals

22 of 27 new or added lines in 1 file covered. (81.48%)

226 existing lines in 4 files now uncovered.

75233 of 81285 relevant lines covered (92.55%)

454692.52 hits per line

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

88.57
/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,
5,171✔
89
                       hdl_kind_t kind)
90
{
91
   pp_defines_init();
5,171✔
92

93
   reset_scanner();
5,171✔
94

95
   yylloc = LOC_INVALID;
5,171✔
96

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

105
   src_kind = kind;
5,171✔
106
   pperrors = 0;
5,171✔
107

108
   switch (kind) {
5,171✔
109
   case SOURCE_VERILOG:
647✔
110
      reset_verilog_parser();
647✔
111
      break;
647✔
112
   case SOURCE_VHDL:
4,500✔
113
      reset_vhdl_parser();
4,500✔
114
      break;
4,500✔
115
   case SOURCE_SDF:
24✔
116
      reset_sdf_parser();
24✔
117
      break;
24✔
118
   }
119
}
5,171✔
120

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

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

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

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

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

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

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

175
      file_ref_t file_ref = loc_file_ref(file, map);
4,832✔
176

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

182
   close(fd);
4,874✔
183
}
4,874✔
184

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

189
   yylloc = LOC_INVALID;
26✔
190

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

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

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

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

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

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

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

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

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

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

265
int get_next_char(char *b, int max_buffer)
11,227✔
266
{
267
   const ptrdiff_t navail =
11,227✔
268
      (input_buf.file_start - input_buf.read_ptr) + input_buf.file_sz;
11,227✔
269
   assert(navail >= 0);
11,227✔
270

271
   if (navail == 0)
11,227✔
272
      return 0;
273

274
   const int nchars = MIN(navail, max_buffer);
6,021✔
275

276
   memcpy(b, input_buf.read_ptr, nchars);
6,021✔
277
   input_buf.read_ptr += nchars;
6,021✔
278

279
   return nchars;
6,021✔
280
}
281

282
void begin_token(char *tok, int length)
3,620,443✔
283
{
284
   // Newline must match as a single token for the logic below to work
285
   assert(strchr(tok, '\n') == NULL || length == 1);
3,620,443✔
286

287
   if (macro_stack.count == 0 || input_buf.file_ref != FILE_INVALID) {
7,240,731✔
288
      const int first_col = input_buf.colno;
3,620,288✔
289
      if (*tok == '\n') {
3,620,288✔
290
         input_buf.colno = 0;
411,290✔
291
         input_buf.lineno += 1;
411,290✔
292
      }
293
      else
294
         input_buf.colno += length;
3,208,998✔
295

296
      const int last_col = first_col + length - 1;
3,620,288✔
297

298
      extern loc_t yylloc;
3,620,288✔
299
      yylloc = get_loc(input_buf.lineno, first_col, input_buf.lineno,
3,620,288✔
300
                       last_col, input_buf.file_ref);
3,620,288✔
301
   }
302
   else {
303
      yylloc = LOC_INVALID;
155✔
304
      for (int i = 0; i < macro_stack.count && loc_invalid_p(&yylloc); i++)
310✔
305
         yylloc = macro_stack.items[i].expandloc;
155✔
306
   }
307
}
3,620,443✔
308

309
const char *token_str(token_t tok)
287✔
310
{
311
   if (tok == tEOF)
287✔
312
      return "end of file";
313
   else if (tok < 128) {
285✔
314
      static char buf[2];
57✔
315
      buf[0] = tok;
57✔
316
      return buf;
57✔
317
   }
318
   else {
319
      static const char *token_strs[] = {
228✔
320
         "identifier", "entity", "is", "end", "generic", "port", "constant",
321
         "component", "configuration", "architecture", "of", "begin", "for",
322
         "type", "to", "all", "in", "out", "buffer", "bus", "unaffected",
323
         "signal", "downto", "process", "postponed", "wait", "report", ":=",
324
         "integer", "string", "error", "inout", "linkage", "variable", "if",
325
         "range", "subtype", "units", "package", "library", "use", "null",
326
         "function", "impure", "return", "pure", "array", "<>", "=>", "others",
327
         "assert", "severity", "on", "map", "then", "else", "elsif", "body",
328
         "while", "loop", "after", "alias", "attribute", "procedure", "exit",
329
         "next", "when", "case", "label", "group", "literal", "inertial",
330
         "transport", "reject", "bit string", "block", "with", "select",
331
         "generate", "access", "file", "open", "real", "until", "record",
332
         "new", "shared", "and", "or", "nand", "nor", "xor", "xnor", "/=",
333
         "<=", ">=", "**", "sll", "srl", "sla", "sra", "rol", "ror", "mod",
334
         "rem", "abs", "not", "guarded", "reverse_range", "protected",
335
         "context", "`if", "`else", "`elsif", "`end", "`error", "`warning",
336
         "translate_off", "translate_on", "?=", "?/=", "?<", "?<=", "?>",
337
         "?>=", "register", "disconnect", "??", "<<", ">>", "force", "release",
338
         "parameter", "coverage on", "coverage off", "PSL directive", "always",
339
         "->", "<->", "default", "clock", "next!", "never", "eventually!",
340
         "next_a", "next_a!", "next_e", "next_e!", "next_event", "next_event!",
341
         "module", "endmodule", "input", "output", "reg", "posedge", "negedge",
342
         "initial", "wire", "unsigned number", "assume", "assume_guarantee",
343
         "restrict", "restrict_guarantee", "strong", "fairness", "cover",
344
         "property", "sequence", "const", "mutable", "hdltype", "boolean",
345
         "bit", "bitvector", "numeric", "string", "[*", "[+]", "[->", "[=",
346
         "&&", "within", "system task", "view", "private", "prev", "stable",
347
         "rose", "fell", "ended", "nondet", "nondetv", "union", "translate on",
348
         "translate off", "until!", "until_", "until!_", "`timescale",
349
         "supply0", "supply1", "pulldown", "pullup", "===", "!==", "==", "!=",
350
         "(*", "*)", "number", "forever", "[[", "]]", "specify", "endspecify",
351
         "primitive", "endprimitive", "table", "endtable", "assign",
352
         "level symbol", "edge symbol", "edge indicator", "buf", "||",
353
         "scalar constant (0)", "scalar constant (1)", "delay file",
354
         "sdf version", "design", "date", "vendor", "program", "version",
355
         "divider", "voltage", "temperature", "cell", "celltype", "instance",
356
         "delay", "timing check", "timing env", "path pulse",
357
         "path pulse percent", "IO path", "retain", "cond", "condelse",
358
         "interconnect", "net delay", "device", "setup", "hold", "setuphold",
359
         "recovery", "removal", "recrem", "skew", "bidirectional skew", "width",
360
         "period", "nochange", "cond", "scond", "ccond", "path constraint",
361
         "period constraint", "sum", "diff", "skew constraint", "exception",
362
         "name", "arrival", "departure", "slack", "waveform", "increment",
363
         "absolute", "~&", "~|", "~^", "struct", "packed", "void", "byte",
364
         "shortint", "longint", "int", "integer", "time", "typedef", "logic",
365
         "enum", "tagged", "abort", "sync_abort", "async_abort", "before",
366
         "before!", "before_", "before!_", "|->", "|=>", "next", "inf",
367
         "repeat", "do", "endpoint", "<<", ">>", "<<<", ">>>", "task",
368
         "endtask", "endfunction", "`begin_keywords", "`end_keywords", "real",
369
         "shortreal", "realtime", "`__nvc_push", "`__nvc_pop", "++", "--",
370
         "var", "`default_nettype", "tri", "tri0", "tri1", "wand", "triand",
371
         "wor", "trior", "trireg", "uwire", "none", "localparam", "always_comb",
372
         "always_ff", "always_latch", "(*)", "endcase", "casex", "casez",
373
         "ifnone", "edge", "*>", "$setup", "$hold", "$recovery", "$removal",
374
         "$setuphold", "$recrem", "$width", "+:", "-:", "endgenerate",
375
         "`resetall", "event", "&&&", "specparam", "fork", "join", "automatic",
376
         "genvar", "highz0", "highz1", "strong0", "strong1", "pull0", "pull1",
377
         "weak0", "weak1", "small", "medium", "large", "vectored", "scalared",
378
         "`unconnected_drive", "`nounconnected_drive", "deassign", "signed",
379
         "unsigned", "disable", "class", "chandle", "export", "import",
380
         "endpackage", "extends", "this", "nettype", "ref", "super", "static",
381
         "+=", "-=", "*=", "/=", "%=", "&=", "|=", "^=", "<<=", ">>=", "<<<=",
382
         ">>>=", "bufif0", "bufif1", "notif0", "notif1", "`protect",
383
         "begin_protected", "end_protected", "endprogram", "endclass",
384
         "virtual", "::", "defparam", "tran", "tranif0", "tranif1", "rtran",
385
         "rtranif0", "rtranif1",
386
      };
387

388
      if (tok >= 200 && tok - 200 < ARRAY_LEN(token_strs))
228✔
389
         return token_strs[tok - 200];
228✔
390
   }
391

392
   return "???";
393
}
394

395
void free_token(token_t tok, yylval_t *lval)
1,601✔
396
{
397
   if (tok == tSTRING || tok == tBITSTRING || tok == tUNSNUM)
1,601✔
398
      free(lval->str);
8✔
399

400
   DEBUG_ONLY(lval->str = NULL);
1,601✔
401
}
1,601✔
402

403
static void pp_defines_init(void)
34,401✔
404
{
405
   if (pp_defines != NULL)
34,401✔
406
      return;
407

408
   pp_defines = shash_new(16);
4,815✔
409

410
   pp_defines_add("VHDL_VERSION", standard_text(standard()));
4,815✔
411
   pp_defines_add("TOOL_TYPE",    "SIMULATION");
4,815✔
412
   pp_defines_add("TOOL_VENDOR",  PACKAGE_URL);
4,815✔
413
   pp_defines_add("TOOL_NAME",    PACKAGE_NAME);
4,815✔
414
   pp_defines_add("TOOL_EDITION", "GPL");
4,815✔
415
   pp_defines_add("TOOL_VERSION", PACKAGE_VERSION);
4,815✔
416
}
417

418
void pp_defines_add(const char *name, const char *value)
28,905✔
419
{
420
   pp_defines_init();
28,905✔
421

422
   bool valid = isalpha_iso88591(name[0]) || name[0] == '_';
28,905✔
423
   for (const char *p = name + 1; valid && *p; p++)
313,059✔
424
      valid &= isalnum_iso88591(*p) || *p == '_';
284,154✔
425

426
   if (!valid)
28,905✔
UNCOV
427
      errorf("\"%s\" is not a valid conditional analysis identifier", name);
×
428

429
   char *existing_val = shash_get(pp_defines, name);
28,905✔
430
   if (existing_val) {
28,905✔
UNCOV
431
      warnf("conditional analysis identifier '%s' already defined (%s)",
×
432
            name, existing_val);
UNCOV
433
      free(existing_val);
×
434
   }
435

436
   shash_put(pp_defines, name, xstrdup(value));
28,905✔
437
}
28,905✔
438

439
const char *pp_defines_get(const char *name)
24✔
440
{
441
   pp_defines_init();
24✔
442
   return shash_get(pp_defines, name);
24✔
443
}
444

445
void pp_defines_iter(pp_iter_t fn, void *ctx)
301✔
446
{
447
   pp_defines_init();
301✔
448

449
   const char *key;
301✔
450
   void *value;
301✔
451
   for (hash_iter_t it = HASH_BEGIN;
301✔
452
        shash_iter(pp_defines, &it, &key, &value); )
2,119✔
453
      (*fn)(key, value, ctx);
1,818✔
454
}
301✔
455

456
static int pp_yylex(void)
1,778,965✔
457
{
458
   const int tok = input_buf.lookahead != -1 ? input_buf.lookahead : yylex();
1,778,965✔
459
   input_buf.lookahead = -1;
1,778,965✔
460
   return tok;
1,778,965✔
461
}
462

463
static void pp_error(const char *fmt, ...)
3✔
464
{
465
   va_list ap;
3✔
466
   va_start(ap, fmt);
3✔
467

468
   if (pperrors++ == 0 || opt_get_int(OPT_UNIT_TEST)) {
3✔
469
      diag_t *d = diag_new(DIAG_ERROR, &yylloc);
3✔
470
      diag_vprintf(d, fmt, ap);
3✔
471
      diag_emit(d);
3✔
472
   }
473

474
   va_end(ap);
3✔
475
}
3✔
476

477
static bool pp_expect(int expect)
130✔
478
{
479
   const int got = pp_yylex();
130✔
480
   if (got != expect) {
130✔
UNCOV
481
      pp_error("expected $yellow$%s$$ while parsing conditional "
×
482
               "directive but found $yellow$%s$$", token_str(expect),
483
               token_str(got));
UNCOV
484
      return false;
×
485
   }
486

487
   return true;
488
}
489

490
static bool pp_cond_analysis_relation(void)
60✔
491
{
492
   // ( conditional_analysis_expression )
493
   //   | not ( conditional_analysis_expression )
494
   //   | conditional_analysis_identifier = string_literal
495
   //   | conditional_analysis_identifier /= string_literal
496
   //   | conditional_analysis_identifier < string_literal
497
   //   | conditional_analysis_identifier <= string_literal
498
   //   | conditional_analysis_identifier > string_literal
499
   //   | conditional_analysis_identifier >= string_literal
500

501
   bool result = false;
60✔
502
   token_t tok = pp_yylex();
60✔
503

504
   switch (tok) {
60✔
505
   case tLPAREN:
×
506
      result = pp_cond_analysis_expr();
×
507
      pp_expect(tRPAREN);
×
UNCOV
508
      break;
×
509

510
   case tNOT:
1✔
511
      pp_expect(tLPAREN);
1✔
512
      result = !pp_cond_analysis_expr();
1✔
513
      pp_expect(tRPAREN);
1✔
514
      break;
1✔
515

516
   case tID:
59✔
517
      {
518
         const char *name = istr(yylval.ident);
59✔
519
         token_t rel = pp_yylex();
59✔
520

521
         if (pp_expect(tSTRING)) {
59✔
522
            const char *value = shash_get(pp_defines, name);
59✔
523
            if (value == NULL)
59✔
524
               pp_error("undefined conditional analysis identifier %s", name);
1✔
525
            else {
526
               char *cmp = yylval.str + 1;
58✔
527
               cmp[strlen(cmp) - 1] = '\0';
58✔
528

529
               switch (rel) {
58✔
530
               case tEQ:
17✔
531
                  result = strcmp(value, cmp) == 0;
17✔
532
                  break;
17✔
533
               case tNEQ:
1✔
534
                  result = strcmp(value, cmp) != 0;
1✔
535
                  break;
1✔
536
               case tLT:
39✔
537
                  result = strcmp(value, cmp) < 0;
39✔
538
                  break;
39✔
539
               case tLE:
×
540
                  result = strcmp(value, cmp) <= 0;
×
541
                  break;
×
542
               case tGT:
×
543
                  result = strcmp(value, cmp) > 0;
×
UNCOV
544
                  break;
×
545
               case tGE:
1✔
546
                  result = strcmp(value, cmp) >= 0;
1✔
547
                  break;
1✔
548
               default:
×
UNCOV
549
                  pp_error("expected conditional analysis relation "
×
550
                           "but found $yellow$%s$$", token_str(rel));
UNCOV
551
                  break;
×
552
               }
553
            }
554

555
            free(yylval.str);
59✔
556
         }
557
      }
558
      break;
559

560
   default:
×
UNCOV
561
      pp_error("unexpected $yellow$%s$$ while parsing conditional "
×
562
               "analysis relation", token_str(tok));
563
   }
564

565
   return result;
60✔
566
}
567

568
static bool pp_cond_analysis_expr(void)
59✔
569
{
570
   // conditional_analysis_relation
571
   //   | conditional_analysis_relation { and conditional_analysis_relation }
572
   //   | conditional_analysis_relation { or conditional_analysis_relation }
573
   //   | conditional_analysis_relation { xor conditional_analysis_relation }
574
   //   | conditional_analysis_relation { xnor conditional_analysis_relation }
575

576
   const bool lhs = pp_cond_analysis_relation();
59✔
577
   switch ((input_buf.lookahead = pp_yylex())) {
59✔
578
   case tAND:
1✔
579
      input_buf.lookahead = -1;
1✔
580
      return pp_cond_analysis_relation() && lhs;
1✔
581
   case tOR:
×
582
      input_buf.lookahead = -1;
×
583
      return pp_cond_analysis_relation() || lhs;
×
584
   case tXOR:
×
585
      input_buf.lookahead = -1;
×
586
      return pp_cond_analysis_relation() ^ lhs;
×
587
   case tXNOR:
×
588
      input_buf.lookahead = -1;
×
UNCOV
589
      return !(pp_cond_analysis_relation() ^ lhs);
×
590
   default:
591
      return lhs;
592
   }
593
   return lhs;
594
}
595

596
static void macro_hint_cb(diag_t *d, void *ctx)
1✔
597
{
598
   assert(macro_stack.count > 0);
1✔
599

600
   for (int i = 0; i < macro_stack.count; i++) {
3✔
601
      const macro_expansion_t *me = &(macro_stack.items[i]);
2✔
602
      if (me->include != NULL)
2✔
UNCOV
603
         diag_hint(d, &me->expandloc, "while processing `include \"%s\" "
×
604
                   "directive", me->include);
605
      else
606
         diag_hint(d, NULL, "while expanding macro %s", istr(me->macro));
2✔
607
   }
608
}
1✔
609

610
static void pp_nvc_push(void)
16✔
611
{
612
   token_t tok;
16✔
613
   macro_expansion_t exp = {};
16✔
614
   unsigned first_line, first_column, column_delta;
16✔
615

616
   switch ((tok = pp_yylex())) {
16✔
617
   case tID:
13✔
618
      exp.macro = yylval.ident;
13✔
619
      break;
13✔
620
   case tSTRING:
3✔
621
      exp.include = tb_claim(yylval.text);
3✔
622
      break;
3✔
623
   default:
×
UNCOV
624
      goto error;
×
625
   }
626

627
   if ((tok = pp_yylex()) != tCOMMA)
16✔
UNCOV
628
      goto error;
×
629

630
   if ((tok = pp_yylex()) != tUNSNUM)
16✔
UNCOV
631
      goto error;
×
632

633
   first_line = atoi(yylval.str);
16✔
634
   free(yylval.str);
16✔
635

636
   if ((tok = pp_yylex()) != tCOLON)
16✔
UNCOV
637
      goto error;
×
638

639
   if ((tok = pp_yylex()) != tUNSNUM)
16✔
UNCOV
640
      goto error;
×
641

642
   first_column = atoi(yylval.str);
16✔
643
   free(yylval.str);
16✔
644

645
   if ((tok = pp_yylex()) != tCOMMA)
16✔
UNCOV
646
      goto error;
×
647

648
   if ((tok = pp_yylex()) != tUNSNUM)
16✔
UNCOV
649
      goto error;
×
650

651
   column_delta = atoi(yylval.str);
16✔
652
   free(yylval.str);
16✔
653

654
   exp.expandloc = get_loc(first_line, first_column, first_line,
16✔
655
                           first_column + column_delta, yylloc.file_ref);
16✔
656

657
   input_buf.lineno = 0;
16✔
658
   input_buf.colno = 0;
16✔
659

660
   if (exp.include != NULL)
16✔
661
      input_buf.file_ref = loc_file_ref(exp.include, NULL);
3✔
662
   else
663
      input_buf.file_ref = FILE_INVALID;
13✔
664

665
   yylloc = LOC_INVALID;
16✔
666

667
   if (macro_stack.count == 0)
16✔
668
      diag_add_hint_fn(macro_hint_cb, NULL);
12✔
669

670
   APUSH(macro_stack, exp);
16✔
671
   return;
16✔
672

673
 error:
×
UNCOV
674
   pp_error("unexpected %s while parsing `__nvc_push directive",
×
675
            token_str(tok));
676
}
677

678
static void pp_nvc_pop(void)
16✔
679
{
680
   if (macro_stack.count == 0)
16✔
681
      return;
682

683
   const macro_expansion_t top = APOP(macro_stack);
16✔
684

685
   input_buf.lineno = top.expandloc.first_line - 1;
16✔
686
   input_buf.file_ref = top.expandloc.file_ref;
16✔
687

688
   // Eat the following newline and adjust the next token location
689
   input_buf.lookahead = yylex();
16✔
690

691
   yylloc.first_column +=
16✔
692
      top.expandloc.first_column + top.expandloc.column_delta + 1;
16✔
693

694
   input_buf.colno = yylloc.first_column + yylloc.column_delta;
16✔
695

696
   free(top.include);
16✔
697

698
   if (macro_stack.count == 0)
16✔
699
      diag_remove_hint_fn(macro_hint_cb);
12✔
700
}
701

702
static void pp_protect_block(void)
1✔
703
{
704
   if (!pp_expect(tBEGINPROT))
1✔
705
      return;
706

707
   diag_t *d = diag_new(DIAG_ERROR, &yylloc);
1✔
708
   diag_printf(d, "`protect directives are not supported");
1✔
709
   diag_hint(d, NULL, "encrypted IP can only be practicably implemented by "
1✔
710
             "proprietary tools as the decryption key is shared between "
711
             "the simulator and IP vendors");
712
   diag_lrm(d, STD_08, "24.1");
1✔
713
   diag_emit(d);
1✔
714

715
   for (;;) {
45✔
716
      token_t token = pp_yylex();
45✔
717
      switch (token) {
45✔
718
      case tEOF:
×
719
         pp_error("unexpected end-of-file before $yellow$end_protected$$");
×
UNCOV
720
         return;
×
721
      case tPROTECT:
10✔
722
         if (pp_yylex() == tENDPROT)
10✔
723
            return;
724
         break;
725
      }
726
   }
727
}
728

729
token_t processed_yylex(void)
1,776,810✔
730
{
731
   assert(input_buf.lookahead == -1);
1,776,810✔
732

733
   for (;;) {
1,778,490✔
734
      token_t token = pp_yylex();
1,778,490✔
735
      switch (token) {
1,778,490✔
736
      case tCONDIF:
54✔
737
         {
738
            cond_state_t new = { .loc = yylloc };
54✔
739
            new.result = new.taken = pp_cond_analysis_expr();
54✔
740

741
            if (cond_stack.count > 0 && !ATOP(cond_stack).result) {
54✔
742
               // Suppress nested conditionals in not-taken branches
743
               new.taken = true;
1✔
744
               new.result = false;
1✔
745
            }
746

747
            pp_expect(tTHEN);
54✔
748

749
            new.loc.column_delta =
54✔
750
               yylloc.first_column + yylloc.column_delta - new.loc.first_column;
54✔
751
            new.loc.line_delta =
54✔
752
               yylloc.first_line + yylloc.line_delta - new.loc.first_line;
54✔
753

754
            APUSH(cond_stack, new);
54✔
755
         }
756
         break;
54✔
757

758
      case tCONDELSIF:
4✔
759
         {
760
            if (cond_stack.count == 0)
4✔
761
               pp_error("unexpected $yellow$%s$$ outside conditional "
1✔
762
                        "analysis block", token_str(token));
763

764
            const bool result = pp_cond_analysis_expr();
4✔
765

766
            if (cond_stack.count > 0) {
4✔
767
               if (!ATOP(cond_stack).taken) {
3✔
768
                  ATOP(cond_stack).result = result;
2✔
769
                  ATOP(cond_stack).taken = result;
2✔
770
               }
771
               else
772
                  ATOP(cond_stack).result = false;
1✔
773
            }
774

775
            pp_expect(tTHEN);
4✔
776
         }
777
         break;
4✔
778

779
      case tCONDELSE:
12✔
780
         {
781
            if (cond_stack.count == 0)
12✔
UNCOV
782
               pp_error("unexpected $yellow$%s$$ outside conditional "
×
783
                        "analysis block", token_str(token));
784
            else {
785
               cond_state_t *cs = &(cond_stack.items[cond_stack.count - 1]);
12✔
786
               cs->result = !(cs->taken);
12✔
787
            }
788
         }
789
         break;
790

791
      case tCONDEND:
54✔
792
         {
793
            if (cond_stack.count == 0)
54✔
794
               pp_error("unexpected $yellow$%s$$ outside conditional "
1✔
795
                        "analysis block", token_str(token));
796
            else
797
               APOP(cond_stack);
53✔
798

799
            if ((input_buf.lookahead = yylex()) == tIF)
54✔
800
               input_buf.lookahead = -1;
46✔
801
         }
802
         break;
803

804
      case tCONDERROR:
10✔
805
      case tCONDWARN:
806
         {
807
            loc_t loc = yylloc;
10✔
808
            if (pp_expect(tSTRING)) {
10✔
809
               loc.column_delta =
10✔
810
                  yylloc.first_column + yylloc.column_delta - loc.first_column;
10✔
811
               loc.line_delta =
10✔
812
                  yylloc.first_line + yylloc.line_delta - loc.first_line;
10✔
813

814
               if (cond_stack.count == 0 || ATOP(cond_stack).result) {
10✔
815
                  const diag_level_t level =
10✔
816
                     token == tCONDWARN ? DIAG_WARN : DIAG_ERROR;
5✔
817
                  diag_t *d = diag_new(level, &loc);
5✔
818
                  diag_printf(d, "%s", yylval.str);
5✔
819
                  diag_emit(d);
5✔
820
               }
821

822
               free(yylval.str);
10✔
823
            }
824
         }
825
         break;
10✔
826

827
      case tEOF:
828
         while (cond_stack.count > 0) {
4,874✔
829
            error_at(&(ATOP(cond_stack).loc), "unterminated conditional "
1✔
830
                     "analysis block");
831
            APOP(cond_stack);
1✔
832
         }
833
         return tEOF;
834

835
      case tNVCPUSH:
16✔
836
         pp_nvc_push();
16✔
837
         break;
16✔
838

839
      case tNVCPOP:
16✔
840
         pp_nvc_pop();
16✔
841
         break;
16✔
842

843
      case tPROTECT:
1✔
844
         pp_protect_block();
1✔
845
         break;
1✔
846

847
      default:
1,773,450✔
848
         if (cond_stack.count == 0 || ATOP(cond_stack).result)
1,773,450✔
849
            return token;
1,771,937✔
850
         else {
851
            free_token(token, &yylval);
1,513✔
852
            break;
1,513✔
853
         }
854
      }
855
   }
856
}
857

858
const char *verilog_version_string(vlog_version_t vers)
21✔
859
{
860
   static const char *const map[] = {
21✔
861
      [VLOG_1364_1995] = "1364-1995",
862
      [VLOG_1364_2001_NOCONFIG] = "1364-2001-noconfig",
863
      [VLOG_1364_2001] = "1364-2001",
864
      [VLOG_1364_2005] = "1364-2005",
865
      [VLOG_1800_2005] = "1800-2005",
866
      [VLOG_1800_2009] = "1800-2009",
867
      [VLOG_1800_2012] = "1800-2012",
868
      [VLOG_1800_2017] = "1800-2017",
869
      [VLOG_1800_2023] = "1800-2023",
870
   };
871
   return map[vers];
21✔
872
}
873

874
bool parse_verilog_version(const char *str, vlog_version_t *vers)
3✔
875
{
876
   for (vlog_version_t i = 0; i < VLOG_1800_2023 + 1; i++) {
20✔
877
      if (strcmp(str, verilog_version_string(i)) == 0) {
19✔
878
         *vers = i;
2✔
879
         return true;
2✔
880
      }
881
   }
882

883
   return false;
884
}
885

886
void set_default_keywords(vlog_version_t vers)
357✔
887
{
888
   default_keywords = vers;
357✔
889
}
357✔
890

891
void push_keywords(vlog_version_t vers)
2✔
892
{
893
   APUSH(keywords_stack, vers);
2✔
894
}
2✔
895

896
bool pop_keywords(void)
3✔
897
{
898
   if (keywords_stack.count == 0)
3✔
899
      return false;
900
   else {
901
      APOP(keywords_stack);
2✔
902
      return true;
2✔
903
   }
904
}
905

906
bool get_verilog_keywords(vlog_version_t *vers)
308✔
907
{
908
   if (keywords_stack.count == 0) {
308✔
909
      *vers = default_keywords;
306✔
910
      return false;
306✔
911
   }
912
   else {
913
      *vers = keywords_stack.items[keywords_stack.count - 1];
2✔
914
      return true;
2✔
915
   }
916
}
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