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

Nic30 / hdlConvertor / #201

10 Jun 2025 02:39PM UTC coverage: 59.995% (-2.5%) from 62.506%
#201

push

travis-ci

Nic30
Merge remote-tracking branch 'origin/mesonbuild'

109 of 127 new or added lines in 14 files covered. (85.83%)

11 existing lines in 6 files now uncovered.

41572 of 69293 relevant lines covered (59.99%)

1047819.11 hits per line

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

97.18
/src/verilogPreproc/verilogPreproc.cpp
1
#include <any>
2

3
#include <hdlConvertor/verilogPreproc/verilogPreproc.h>
4

5
// antlr4-runtime/
6
#include <ParserRuleContext.h>
7
#include <misc/Interval.h>
8

9
#include <hdlConvertor/verilogPreproc/macro_def_verilog.h>
10

11

12
namespace hdlConvertor {
13
namespace verilog_pp {
14

15
using namespace std;
16

17
using verilogPreprocParser = verilogPreproc_antlr::verilogPreprocParser;
18
using verilogPreprocLexer = verilogPreproc_antlr::verilogPreprocLexer;
19

20
VerilogPreproc::VerilogPreproc(VerilogPreprocContainer &_container,
8,348✔
21
                VerilogPreprocOutBuffer &_preproc_out, antlr4::TokenStream &tokens,
22
                bool _added_incdir, const std::string _encoding, size_t include_depth_limit) :
8,348✔
23
                container(_container), _tokens(*(antlr4::CommonTokenStream*) &tokens), added_incdir(
8,348✔
24
                                _added_incdir), include_depth_limit(include_depth_limit), preproc_out(
8,348✔
25
                                _preproc_out), encoding(_encoding) {
8,348✔
26
        switch (container.lang) {
8,348✔
27
        case Language::VERILOG1995:
8,348✔
28
        case Language::VERILOG2001:
29
        case Language::VERILOG2001_NOCONFIG:
30
        case Language::VERILOG2005:
31
        case Language::SV2005:
32
        case Language::SV2009:
33
        case Language::SV2012:
34
        case Language::SV2017:
35
                break;
8,348✔
UNCOV
36
        default:
×
UNCOV
37
                throw std::runtime_error("vPreprocessor: unsupported language");
×
38
        }
39
}
8,348✔
40

41
VerilogPreproc::~VerilogPreproc() {
8,348✔
42
}
8,348✔
43

44
antlrcpp::Any VerilogPreproc::visitText(
331,662✔
45
                verilogPreprocParser::TextContext *ctx) {
46
        // text:
47
        //    preprocess_directive
48
        //   | LINE_COMMENT
49
        //   | CODE
50
        //   | NEW_LINE
51
        //   | NUM
52
        //   | ID
53
        //   | STR
54
        //   | NEW_LINE
55
        //   | COMMENT
56
        //   ;
57
        auto c = ctx->children[0];
331,662✔
58
        auto pd =
59
                        dynamic_cast<verilogPreprocParser::Preprocess_directiveContext*>(c);
331,662✔
60
        if (pd) {
331,662✔
61
                return visitPreprocess_directive(pd);
14,344✔
62
        } else {
63
                preproc_out.set_input_line(ctx->getStart()->getLine() - 1);
317,318✔
64
                auto str = ctx->getText();
317,318✔
65
                preproc_out << str;
317,318✔
66
                return nullptr;
317,318✔
67
        }
317,318✔
68

69
}
70

71
antlrcpp::Any VerilogPreproc::visitResetall(
27✔
72
                verilogPreprocParser::ResetallContext*) {
73
        return nullptr;
27✔
74
}
75

76
antlrcpp::Any VerilogPreproc::visitCelldefine(
31✔
77
                verilogPreprocParser::CelldefineContext*) {
78
        return nullptr;
31✔
79
}
80

81
antlrcpp::Any VerilogPreproc::visitEndcelldefine(
32✔
82
                verilogPreprocParser::EndcelldefineContext*) {
83
        return nullptr;
32✔
84
}
85

86
antlrcpp::Any VerilogPreproc::visitTiming_spec(
1,168✔
87
                verilogPreprocParser::Timing_specContext*) {
88
        return nullptr;
1,168✔
89
}
90

91
antlrcpp::Any VerilogPreproc::visitDefault_nettype(
66✔
92
                verilogPreprocParser::Default_nettypeContext*) {
93
        return nullptr;
66✔
94
}
95

96
antlrcpp::Any VerilogPreproc::visitUnconnected_drive(
4✔
97
                verilogPreprocParser::Unconnected_driveContext*) {
98
        return nullptr;
4✔
99
}
100

101
antlrcpp::Any VerilogPreproc::visitNounconnected_drive(
3✔
102
                verilogPreprocParser::Nounconnected_driveContext*) {
103
        return nullptr;
3✔
104
}
105

106
antlrcpp::Any VerilogPreproc::visitKeywords_directive(
13✔
107
                verilogPreprocParser::Keywords_directiveContext*) {
108
        return nullptr;
13✔
109
}
110

111
antlrcpp::Any VerilogPreproc::visitEndkeywords_directive(
10✔
112
                verilogPreprocParser::Endkeywords_directiveContext*) {
113
        return nullptr;
10✔
114
}
115

116
antlrcpp::Any VerilogPreproc::visitPragma(
8✔
117
                verilogPreprocParser::PragmaContext*) {
118
        return nullptr;
8✔
119
}
120

121
antlrcpp::Any VerilogPreproc::visitLine_directive(
7✔
122
                verilogPreprocParser::Line_directiveContext *ctx) {
123
        auto file = ctx->STR()->getText();
7✔
124
        // [todo] process also level parameter (last)
125
        std::istringstream line_no_str(ctx->NUM(0)->getText());
7✔
126
        size_t line;
127
        line_no_str >> line;
7✔
128
        preproc_out.set_input_line(file, line);
7✔
129
        return nullptr;
14✔
130
}
7✔
131

132
antlrcpp::Any VerilogPreproc::visitUndef(
38✔
133
                verilogPreprocParser::UndefContext *ctx) {
134
        // we simply remove the macro from the macroDB object. So it is not anymore
135
        // defined
136
        auto m = container.defineDB.find(ctx->ID()->getText());
38✔
137
        if (m != container.defineDB.end() && !m->second->is_persistent) {
38✔
138
                container.defineDB.erase(m);
30✔
139
                delete m->second;
30✔
140
        }
141

142
        return nullptr;
38✔
143
}
144

145
antlrcpp::Any VerilogPreproc::visitUndefineall(
2✔
146
                verilogPreprocParser::UndefineallContext*) {
147
        container.delete_non_persystent_macro_defs();
2✔
148
        return nullptr;
2✔
149
}
150

151
antlrcpp::Any VerilogPreproc::visitDefine_args_with_def_val(
206✔
152
                verilogPreprocParser::Define_args_with_def_valContext *ctx) {
153
        auto params = ctx->param_with_def_val();
206✔
154
        auto res = new vector<MacroDefVerilog::param_info_t>();
206✔
155
        res->reserve(params.size());
206✔
156
        // define_args_with_def_val:
157
        //         param_with_def_val ( DM_COMMA param_with_def_val )*
158
        // ;
159
        for (auto p : params) {
573✔
160
                // param_with_def_val:
161
                //         var_id (DM_EQUAL default_text?) ?
162
                // ;
163
                MacroDefVerilog::param_info_t i;
367✔
164
                i.name = p->var_id()->getText();
367✔
165
                i.has_def_val = p->EQUAL() != nullptr;
367✔
166
                if (i.has_def_val) {
367✔
167
                        auto dt = p->default_text();
41✔
168
                        if (dt)
41✔
169
                                i.def_val = dt->getText();
40✔
170
                }
171
                res->push_back(i);
367✔
172
        }
367✔
173
        return res;
412✔
174
}
206✔
175

176
antlrcpp::Any VerilogPreproc::visitDefine_args_basic(
25✔
177
                verilogPreprocParser::Define_args_basicContext *ctx) {
178
        // define_args_basic:
179
        //         var_id ( DM_COMMA var_id )*
180
        // ;
181
        auto params = ctx->var_id();
25✔
182
        auto res = new vector<MacroDefVerilog::param_info_t>();
25✔
183
        res->reserve(params.size());
25✔
184
        for (auto p : params) {
63✔
185
                res->push_back( { p->getText(), false, "" });
76✔
186
        }
187
        return res;
50✔
188
}
101✔
189

190
//method call when the definition of a macro is found
191
antlrcpp::Any VerilogPreproc::visitDefine(
1,315✔
192
                verilogPreprocParser::DefineContext *ctx) {
193
        // get the macro name
194
        auto da = ctx->define_args();
1,315✔
195
        vector<MacroDefVerilog::param_info_t> *params;
196
        vector<MacroDefVerilog::param_info_t> params_dummy;
1,315✔
197
        if (da) {
1,315✔
198
#if !defined(ANTLRCPP_VERSION_MAJOR) || (ANTLRCPP_VERSION_MAJOR == 4 && ANTLRCPP_VERSION_MINOR <= 9)
199
                params = visitDefine_args(da).as<vector<MacroDefVerilog::param_info_t> *>();
200
#else
201
                params = std::any_cast<vector<MacroDefVerilog::param_info_t> *>(visitDefine_args(da));
231✔
202
#endif
203
        }
204
        else {
205
                params = &params_dummy;
1,084✔
206
        }
207

208
        string def_name = ctx->macro_id()->getText();
1,315✔
209
        string body;
1,315✔
210
        // get the template
211
        if (ctx->replacement() != nullptr) {
1,315✔
212
                body = _tokens.getText(ctx->replacement()->getSourceInterval());
1,210✔
213
                remove_comment(ctx->replacement()->getStart(),
1,210✔
214
                                ctx->replacement()->getStop(), &body);
1,210✔
215
                body = rtrim(body);
2,420✔
216
        }
217
        bool has_params = da != nullptr;
1,315✔
218
        try {
219
                auto item = new MacroDefVerilog(def_name, has_params, *params, body);
1,315✔
220
                container.defineDB.insert( { def_name, item });
1,315✔
221
        } catch (const ParseException &e) {
×
UNCOV
222
                throw_input_caused_error(ctx, e.what());
×
UNCOV
223
        }
×
224
        if (da)
1,315✔
225
                delete params;
231✔
226

227
        // the macro definition is replace by an empty string in the original
228
        // source code
229
        return nullptr;
2,630✔
230
}
1,315✔
231

232
void VerilogPreproc::parse_macro_args(
1,228✔
233
                verilogPreprocParser::Macro_callContext *ctx, vector<string> &args) {
234
        //create a macroPrototype object
235
        bool expected_value = true;
1,228✔
236
        bool last_was_comma = false;
1,228✔
237
        auto end = ctx->children.end();
1,228✔
238
        for (auto _c = ctx->children.begin() + 1; _c != end; ++_c) {
6,016✔
239
                auto &c = *_c;
4,788✔
240
                string ch_text = c->getText();
4,788✔
241
                if (antlrcpp::is<antlr4::tree::TerminalNode*>(c)) {
4,788✔
242
                        if (expected_value
2,409✔
243
                                        && (ch_text == "," || (last_was_comma && ch_text == ")"))) {
2,409✔
244
                                // the case for macro(,)
245
                                // must prevent case of macro()
246
                                args.push_back("");
52✔
247
                        }
248
                        expected_value = true;
2,409✔
249
                        last_was_comma = ch_text == ",";
2,409✔
250
                } else if (antlrcpp::is<verilogPreprocParser::ValueContext*>(c)) {
2,379✔
251
                        string data = _tokens.getText(c->getSourceInterval());
2,379✔
252
                        data = trim(data);
2,379✔
253
                        args.push_back(data);
2,379✔
254
                        expected_value = false;
2,379✔
255
                        last_was_comma = false;
2,379✔
256
                }
2,379✔
257
        }
4,788✔
258
}
1,228✔
259
//method call when `macro is found in the source code
260
antlrcpp::Any VerilogPreproc::visitMacro_call(
9,859✔
261
                verilogPreprocParser::Macro_callContext *ctx) {
262
        return visitMacro_call(ctx, true);
9,859✔
263
}
264
antlrcpp::Any VerilogPreproc::visitMacro_call(
9,861✔
265
                verilogPreprocParser::Macro_callContext *ctx, bool add_to_output) {
266
        // macro_call:
267
        //         OTHER_MACRO_CALL_NO_ARGS
268
        //         | OTHER_MACRO_CALL_WITH_ARGS value? (COMMA value? )* RP
269
        // ;
270

271
        string macro_name;
9,861✔
272
        vector<string> args;
9,861✔
273
        bool has_args = false;
9,861✔
274
        auto no_args = ctx->OTHER_MACRO_CALL_NO_ARGS();
9,861✔
275
        if (no_args) {
9,861✔
276
                macro_name = no_args->getText();
8,604✔
277
        } else {
278
                auto with_args = ctx->OTHER_MACRO_CALL_WITH_ARGS();
1,257✔
279
                assert(with_args);
1,257✔
280
                auto _macro_name = with_args->getText();
1,257✔
281
                size_t name_len = 1;
1,257✔
282
                for (; name_len < _macro_name.size(); name_len++) {
7,825✔
283
                        auto c = _macro_name[name_len];
7,825✔
284
                        if (!isalnum(c) && c != '_')
7,825✔
285
                                break;
1,257✔
286
                }
287
                macro_name = _macro_name.substr(0, name_len);
1,257✔
288
                has_args = true;
1,257✔
289
        }
1,257✔
290

291
        //test if the macro has already been defined
292
        auto m = container.defineDB.find(macro_name);
9,861✔
293
        if (m == container.defineDB.end()) {
9,861✔
294
                throw_input_caused_error(ctx, macro_name + " is not defined");
84✔
295
        }
296

297
        //build the replacement string by calling the replacement method of the
298
        //macro_replace object and the provided argument of the macro.
299
        bool _has_args = has_args;
9,819✔
300
        if (m->second->requires_args()) {
9,819✔
301
                if (has_args) {
1,231✔
302
                        parse_macro_args(ctx, args);
1,228✔
303
                }
304
        } else {
305
                _has_args = false;
8,588✔
306
        }
307
        string replacement;
9,819✔
308
        try {
309
                replacement = m->second->replace(args, _has_args, this, ctx);
9,827✔
310
        } catch (const ParseException &e) {
8✔
311
                throw_input_caused_error(ctx, e.what());
16✔
312
        }
8✔
313
        if (!m->second->requires_args() && has_args) {
9,811✔
314
                // args belongs to the code and not to macro
315
                auto a = ctx->start->getStartIndex() + macro_name.size();
23✔
316
                auto b = ctx->stop->getStopIndex();
23✔
317
                auto args_str = ctx->start->getInputStream()->getText(
23✔
318
                                antlr4::misc::Interval(a, b));
23✔
319
                replacement += args_str;
23✔
320
        }
23✔
321

322
        if (container.lang >= Language::SV2005) {
9,811✔
323
                replace_substring(replacement, "``", "");
38,572✔
324
                replace_substring(replacement, "`\\`", "\\");
38,572✔
325
        }
326

327
        if (replacement.find("`", 0) != string::npos) {
9,811✔
328
                if (container.macro_call_stack.size()
1,376✔
329
                                >= container.max_macro_call_stack_size) {
1,376✔
330
                        throw_input_caused_error(ctx,
2✔
331
                                        std::string("Macro call stack overflow ")
6✔
332
                                                        + vector_to_string(container.macro_call_stack)
10✔
333
                                                        + ".");
6✔
334
                }
335
                container.macro_call_stack.push_back(macro_name);
1,374✔
336
                auto input_line_no = preproc_out.input_line_begin + ctx->start->getLine() - 1;
1,374✔
337
                VerilogPreprocOutBuffer _replacement(input_line_no);
1,374✔
338
                container.run_preproc_str(replacement, "utf-8", _replacement);
3,004✔
339
                // [todo] it is expected that the macro call won't cause any change in
340
                //        file line map directly
341
                //assert(_replacement.file_line_map.size() <= 1);
342
                replacement = _replacement.str();
1,118✔
343
                container.macro_call_stack.pop_back();
1,118✔
344
        }
1,374✔
345

346
        if (container.lang >= Language::SV2005) {
9,553✔
347
                unescape_string_dblquotes(replacement);
9,385✔
348
        }
349

350
        if (add_to_output) {
9,552✔
351
                // replace the original macro in the source code by the replacement string
352
                // we just setup
353
                preproc_out.set_input_line(ctx->getStart()->getLine() - 1);
9,550✔
354
                preproc_out << replacement;
9,550✔
355
        }
356
        return replacement;
19,104✔
357
}
10,437✔
358

359
/*
360
 * @param is_negated is used to reverse polarity of condition (ifdef/ifndef difference)
361
 * */
362
template<typename CTX_T>
363
void processIfdef(VerilogPreproc &self, CTX_T *ctx, bool is_negated,
1,586✔
364
                VerilogPreprocContainer &container, VerilogPreprocOutBuffer &out) {
365
        bool en_in = !is_negated;
1,586✔
366
        auto cond_ids = ctx->cond_id();
1,586✔
367
        auto group_of_lines = ctx->group_of_lines();
1,586✔
368
        auto group_of_line = group_of_lines.begin();
1,586✔
369
        antlr4::ParserRuleContext *branch_to_keep = nullptr;
1,586✔
370
        for (auto cond_id : cond_ids) {
4,530✔
371
                auto macro_name = cond_id->getText();
1,679✔
372
                auto is_defined = container.defineDB.find(macro_name)
1,679✔
373
                                != container.defineDB.end();
3,358✔
374
                if (is_defined == en_in) {
1,679✔
375
                        auto gl = *group_of_line;
414✔
376
                        assert(gl);
414✔
377
                        branch_to_keep = gl;
414✔
378
                        out.set_input_line(branch_to_keep->getStart()->getLine() - 1);
414✔
379
                        self.visitGroup_of_lines(gl);
414✔
380
                        break;
410✔
381
                }
382
                ++group_of_line;
1,265✔
383
        }
384
        if (!branch_to_keep && ctx->ELSE() != nullptr) {
1,582✔
385
                auto eg = ctx->else_group_of_lines();
488✔
386
                branch_to_keep = eg;
488✔
387
                out.set_input_line(branch_to_keep->getStart()->getLine() - 1);
488✔
388
                self.visitElse_group_of_lines(eg);
488✔
389
        }
390
}
1,599✔
391

392
antlrcpp::Any VerilogPreproc::visitIfdef_directive(
1,259✔
393
                verilogPreprocParser::Ifdef_directiveContext *ctx) {
394
        processIfdef(*this, ctx, false, container, preproc_out);
1,259✔
395
        return nullptr;
1,250✔
396
}
397

398
antlrcpp::Any VerilogPreproc::visitIfndef_directive(
327✔
399
                verilogPreprocParser::Ifndef_directiveContext *ctx) {
400
        processIfdef(*this, ctx, true, container, preproc_out);
327✔
401
        return nullptr;
323✔
402
}
403

404
//method call when `include is found
405
antlrcpp::Any VerilogPreproc::visitInclude(
175✔
406
                verilogPreprocParser::IncludeContext *ctx) {
407
        //iterator on the list of inc. directory
408
        auto incdir_iter = container.incdirs.begin();
175✔
409
        bool found = false;
175✔
410
        string inc_file_path;
175✔
411
        auto mc = ctx->macro_call();
175✔
412
        if (mc) {
175✔
413
#if !defined(ANTLRCPP_VERSION_MAJOR) || (ANTLRCPP_VERSION_MAJOR == 4 && ANTLRCPP_VERSION_MINOR <= 9)
414
                inc_file_path = visitMacro_call(mc, false).as<string>();
415
#else
416
                inc_file_path = std::any_cast<string>(visitMacro_call(mc, false));
2✔
417
#endif
418
        } else {
419
                inc_file_path = ctx->STR()->getText();
173✔
420
        }
421
        filesystem::path filename;
175✔
422
        // iterate on list of directory until we found the file.
423
        while (incdir_iter != container.incdirs.end() && found == false) {
363✔
424
                // build the filename
425
                filename = (*incdir_iter)
188✔
426
                                / inc_file_path.substr(1, inc_file_path.size() - 2);
376✔
427
                //Test if the file exist
428
                if (filesystem::exists(filename)) {
188✔
429
                        // the file exist. we rise the flag to leave the loop over the
430
                        // directory list
431
                        found = true;
164✔
432
                }
433
                //continue on next directory
434
                incdir_iter++;
188✔
435
        }
436

437
        if (found == false) {
175✔
438
                // The file was not found. We throw a exception
439
                string msg = inc_file_path //.substr(1, inc_file_path.size() - 2)
440
                + " was not found in include directories\n";
11✔
441
                throw_input_caused_error(ctx, msg);
11✔
442
        } else if (container.incfile_stack.size() > include_depth_limit) {
175✔
443
                // test the number of nested include.
444
                // If so throw an exception
445
                stringstream msg;
1✔
446
                msg << "Nested include limit reach" << endl;
1✔
447
                for (auto f : container.incfile_stack) {
102✔
448
                        msg << "    " << f.first << endl;
101✔
449
                }
101✔
450
                throw_input_caused_error(ctx, msg.str());
2✔
451
        } else {
1✔
452
                // We are going to replace the content of the `include
453
                // directive by the content of the processed file
454
                filesystem::path my_incdir;
163✔
455
                if (added_incdir) {
163✔
456
                        my_incdir = container.incdirs.back();
32✔
457
                        container.incdirs.pop_back();
32✔
458
                }
459
                // run the pre-processor on it
460
                VerilogPreprocOutBuffer replacement(0);
163✔
461
                container.run_preproc_file(filename, encoding, replacement);
163✔
462
                if (added_incdir) {
63✔
463
                        container.incdirs.push_back(my_incdir);
32✔
464
                }
465
                //update the current source code
466
                preproc_out << replacement;
63✔
467
        }
263✔
468
        return nullptr;
126✔
469
}
287✔
470

471
void VerilogPreproc::throw_input_caused_error(antlr4::ParserRuleContext *ctx,
64✔
472
                const std::string &_msg) {
473
        auto ts = _tokens.getTokenSource();
64✔
474
        auto s = ctx->start;
64✔
475
        // [todo] use line offsets and file to reconstruct position in original code
476
        stringstream msg;
64✔
477
        msg << ts->getSourceName() << ":";
64✔
478
        if (s) {
64✔
479
                // [TODO] use line offset from preproc_out
480
                msg << ctx->start->getLine() << ":"
64✔
481
                                << ctx->start->getCharPositionInLine() << ":";
64✔
482
        }
483
        msg << "Error: ";
64✔
484
        msg << _msg;
64✔
485
        throw ParseException(msg.str());
64✔
486
}
64✔
487

488
/*
489
 * Remove unwanted comment in text macro.
490
 * Line escape and Line comment have to be removed
491
 */
492
void VerilogPreproc::remove_comment(antlr4::Token *start, antlr4::Token *end, string *str) {
1,210✔
493
        //Get the list of token between the start and the end of replacement rule.
494
        vector<antlr4::Token*> cmtChannel = _tokens.getTokens(start->getTokenIndex(),
2,420✔
495
                        end->getTokenIndex());
1,210✔
496
        //For all those token we are going to their channel.
497
        //If the channel is of the kind we search then we search and removed it from the string.
498
        //TODO : replace by " " to preserve character position of the original source code
499
        for (auto cmt : cmtChannel) {
2,831✔
500
                if (cmt->getChannel() == verilogPreprocLexer::CH_LINE_ESCAPE) {
1,621✔
501
                        string comment_txt = cmt->getText();
89✔
502
                        replaceStringInPlace(*str, comment_txt, "\n");
89✔
503
                } else if (cmt->getChannel() == verilogPreprocLexer::CH_LINE_COMMENT) {
1,621✔
504
                        string comment_txt = cmt->getText();
×
UNCOV
505
                        replaceStringInPlace(*str, comment_txt, "");
×
UNCOV
506
                }
×
507
        }
508
}
1,210✔
509

510
}
511
}
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