moore_svlog_syntax/
preproc.rs

1// Copyright (c) 2016-2021 Fabian Schuiki
2
3//! A preprocessor for SystemVerilog files that takes the raw stream of
4//! tokens generated by a lexer and performs include and macro
5//! resolution.
6
7use crate::cat::*;
8use moore_common::errors::{DiagBuilder2, DiagResult2};
9use moore_common::source::*;
10use std::{collections::HashMap, fmt, path::Path, rc::Rc};
11
12use once_cell::sync::Lazy;
13
14type TokenAndSpan = (CatTokenKind, Span);
15
16pub struct Preprocessor<'a> {
17    /// The stack of input files. Tokens are taken from the topmost stream until
18    /// the end of input, at which point the stream is popped and the process
19    /// continues with the next stream. Used to handle include files.
20    stack: Vec<Stream<'a>>,
21    /// References to the source contents that were touched by the preprocessor.
22    /// Keeping these around ensures that all emitted tokens remain valid (and
23    /// point to valid memory locations) at least until the preprocessor is
24    /// dropped.
25    contents: Vec<Rc<dyn SourceContent>>,
26    /// The current token, or None if either the end of the stream has been
27    /// encountered, or at the beginning when no token has been read yet.
28    token: Option<TokenAndSpan>,
29    /// The defined macros.
30    macro_defs: HashMap<String, Macro>,
31    /// The stack used to inject expanded macros into the token stream.
32    macro_stack: Vec<TokenAndSpan>,
33    /// The paths that are searched for included files, besides the current
34    /// file's directory.
35    include_paths: &'a [&'a Path],
36    /// The define conditional stack. Whenever a `ifdef, `ifndef, `else, `elsif,
37    /// or `endif directive is encountered, the stack is expanded, modified, or
38    /// reduced to reflect the kind of conditional block we're in.
39    defcond_stack: Vec<Defcond>,
40    /// Currently enabled directives.
41    dirs: Directives,
42}
43
44impl<'a> Preprocessor<'a> {
45    /// Create a new preprocessor for the given source file.
46    pub fn new(
47        source: Source,
48        include_paths: &'a [&'a Path],
49        macro_defs: &'a [(&'a str, Option<&'a str>)],
50    ) -> Preprocessor<'a> {
51        let content = source.get_content();
52        let content_unbound = unsafe { &*(content.as_ref() as *const dyn SourceContent) };
53        let iter = content_unbound.iter();
54        let macro_defs = macro_defs
55            .into_iter()
56            .map(|(name, value)| {
57                let body = match value {
58                    Some(value) => {
59                        // Create dummy sources for each user defined macro.
60                        let src = get_source_manager().add_anonymous(*value);
61                        let span = Span::new(src, 0, value.len());
62                        Cat::new(Box::new(value.char_indices()))
63                            .map(|x| (x.0, span))
64                            .collect()
65                    }
66                    None => Vec::new(),
67                };
68                (
69                    name.to_string(),
70                    Macro {
71                        name: name.to_string(),
72                        span: INVALID_SPAN,
73                        args: Vec::new(),
74                        body: body,
75                    },
76                )
77            })
78            .collect();
79        Preprocessor {
80            stack: vec![Stream {
81                source: source,
82                iter: Cat::new(iter),
83            }],
84            contents: vec![content],
85            token: None,
86            macro_defs,
87            macro_stack: Vec::new(),
88            include_paths: include_paths,
89            defcond_stack: Vec::new(),
90            dirs: Default::default(),
91        }
92    }
93
94    /// Advance to the next token in the input stream.
95    fn bump(&mut self) {
96        self.token = self.macro_stack.pop();
97        if self.token.is_some() {
98            return;
99        }
100        loop {
101            self.token = match self.stack.last_mut() {
102                Some(stream) => stream
103                    .iter
104                    .next()
105                    .map(|tkn| (tkn.0, Span::new(stream.source, tkn.1, tkn.2))),
106                None => return,
107            };
108            if self.token.is_none() {
109                self.stack.pop();
110            } else {
111                break;
112            }
113        }
114    }
115
116    /// Called whenever we have encountered a backtick followed by a text token.
117    /// This function handles all compiler directives and performs file
118    /// inclusion and macro expansion.
119    fn handle_directive<S: AsRef<str>>(&mut self, dir_name: S, span: Span) -> DiagResult2<()> {
120        let dir_name = dir_name.as_ref();
121        let dir = DIRECTIVES_TABLE
122            .get(dir_name)
123            .map(|x| *x)
124            .unwrap_or(Directive::Unknown);
125
126        match dir {
127            Directive::Include => {
128                if self.is_inactive() {
129                    return Ok(());
130                }
131
132                // Skip leading whitespace.
133                match self.token {
134                    Some((Whitespace, _)) => self.bump(),
135                    _ => (),
136                }
137
138                // Match the opening double quotes or angular bracket.
139                let name_p;
140                let name_q;
141                let closing =
142                    match self.token {
143                        Some((Symbol('"'), sp)) => {
144                            name_p = sp.end();
145                            self.bump();
146                            '"'
147                        }
148                        Some((Symbol('<'), sp)) => {
149                            name_p = sp.end();
150                            self.bump();
151                            '>'
152                        }
153                        _ => return Err(DiagBuilder2::fatal(
154                            "expected filename inside double quotes (\"...\") or angular brackets \
155                             (<...>) after `include",
156                        )
157                        .span(span)),
158                    };
159
160                // Accumulate the include path until the closing symbol.
161                let mut filename = String::new();
162                loop {
163                    match self.token {
164                        Some((Symbol(c), sp)) if c == closing => {
165                            name_q = sp.begin();
166                            break;
167                        }
168                        Some((Newline, sp)) => {
169                            return Err(DiagBuilder2::fatal(
170                                "expected end of included file's name before line break",
171                            )
172                            .span(sp));
173                        }
174                        Some((_, sp)) => {
175                            filename.push_str(&sp.extract());
176                            self.bump();
177                        }
178                        None => {
179                            return Err(DiagBuilder2::fatal(
180                                "expected filename after `include directive before the end of the \
181                                 input",
182                            )
183                            .span(span));
184                        }
185                    }
186                }
187
188                // Create a new lexer for the included filename and push it onto the
189                // stream stack.
190                // TODO: Search only system location if `include <...> is used
191                let included_source = match self.open_include(&filename, &span.source.get_path()) {
192                    Some(src) => src,
193                    None => {
194                        // TODO: Add notes to the message indicating which files have been tried.
195                        return Err(DiagBuilder2::fatal(format!(
196                            "cannot open included file \"{}\"",
197                            filename
198                        ))
199                        .span(Span::union(name_p, name_q)));
200                    }
201                };
202
203                let content = included_source.get_content();
204                let content_unbound = unsafe { &*(content.as_ref() as *const dyn SourceContent) };
205                let iter = content_unbound.iter();
206                self.contents.push(content);
207                self.stack.push(Stream {
208                    source: included_source,
209                    iter: Cat::new(iter),
210                });
211
212                self.bump();
213                return Ok(());
214            }
215
216            Directive::Define => {
217                if self.is_inactive() {
218                    return Ok(());
219                }
220
221                // Skip leading whitespace.
222                match self.token {
223                    Some((Whitespace, _)) => self.bump(),
224                    _ => (),
225                }
226
227                let makro = self.handle_macro_definition(span)?;
228
229                self.macro_defs.insert(makro.name.clone(), makro);
230                return Ok(());
231            }
232
233            Directive::Undef => {
234                if self.is_inactive() {
235                    return Ok(());
236                }
237
238                // Skip leading whitespace.
239                match self.token {
240                    Some((Whitespace, _)) => self.bump(),
241                    _ => (),
242                }
243
244                // Consume the macro name.
245                let (name, _) = match self.try_eat_name() {
246                    Some(x) => x,
247                    None => {
248                        return Err(
249                            DiagBuilder2::fatal("expected macro name after \"`undef\"").span(span)
250                        );
251                    }
252                };
253
254                // Remove the macro definition.
255                self.macro_defs.remove(&name);
256                return Ok(());
257            }
258
259            Directive::Undefineall => {
260                if self.is_inactive() {
261                    return Ok(());
262                }
263                self.macro_defs.clear();
264            }
265
266            Directive::Ifdef | Directive::Ifndef | Directive::Elsif => {
267                // Skip leading whitespace.
268                match self.token {
269                    Some((Whitespace, _)) => self.bump(),
270                    _ => (),
271                }
272
273                // Consume the macro name.
274                let name = match self.try_eat_name() {
275                    Some((x, _)) => x,
276                    _ => {
277                        return Err(DiagBuilder2::fatal(format!(
278                            "expected macro name after {}",
279                            dir_name
280                        ))
281                        .span(span));
282                    }
283                };
284                let exists = self.macro_defs.contains_key(&name);
285
286                // Depending on the directive, modify the define conditional
287                // stack.
288                match dir {
289                    Directive::Ifdef => self.defcond_stack.push(if self.is_inactive() {
290                        Defcond::Done
291                    } else if exists {
292                        Defcond::Enabled
293                    } else {
294                        Defcond::Disabled
295                    }),
296                    Directive::Ifndef => self.defcond_stack.push(if self.is_inactive() {
297                        Defcond::Done
298                    } else if exists {
299                        Defcond::Disabled
300                    } else {
301                        Defcond::Enabled
302                    }),
303                    Directive::Elsif => {
304                        match self.defcond_stack.pop() {
305                            Some(Defcond::Done) | Some(Defcond::Enabled) => {
306                                self.defcond_stack.push(Defcond::Done)
307                            }
308                            Some(Defcond::Disabled) => {
309                                self.defcond_stack.push(if self.is_inactive() {
310                                    Defcond::Done
311                                } else if exists {
312                                    Defcond::Enabled
313                                } else {
314                                    Defcond::Disabled
315                                })
316                            }
317                            None => {
318                                return Err(DiagBuilder2::fatal(
319                                    "found `elsif without any preceeding `ifdef, `ifndef, or \
320                                     `elsif directive",
321                                )
322                                .span(span))
323                            }
324                        };
325                    }
326                    _ => unreachable!(),
327                }
328
329                return Ok(());
330            }
331
332            Directive::Else => {
333                match self.defcond_stack.pop() {
334                    Some(Defcond::Disabled) => self.defcond_stack.push(Defcond::Enabled),
335                    Some(Defcond::Enabled) | Some(Defcond::Done) => {
336                        self.defcond_stack.push(Defcond::Done)
337                    }
338                    None => {
339                        return Err(DiagBuilder2::fatal(
340                            "found `else without any preceeding `ifdef, `ifndef, or `elsif \
341                             directive",
342                        )
343                        .span(span))
344                    }
345                }
346                return Ok(());
347            }
348
349            Directive::Endif => {
350                if self.defcond_stack.pop().is_none() {
351                    return Err(DiagBuilder2::fatal(
352                        "found `endif without any preceeding `ifdef, `ifndef, `else, or `elsif \
353                         directive",
354                    )
355                    .span(span));
356                }
357                return Ok(());
358            }
359
360            // Perform macro substitution. If we're currently inside the
361            // inactive region of a define conditional (i.e. disabled or done),
362            // don't bother expanding the macro.
363            Directive::Unknown => {
364                if self.is_inactive() {
365                    return Ok(());
366                }
367                // TODO(fschuiki): This unsafe here is ugly. Why is this needed
368                // again?
369                if let Some(ref makro) = unsafe { &*(self as *const Preprocessor) }
370                    .macro_defs
371                    .get(dir_name)
372                {
373                    let args = self.handle_macro_expansion_args(makro, span)?;
374
375                    // Now we have a problem. All the tokens of the macro name
376                    // have been parsed and we would like to continue by
377                    // injecting the tokens of the macro body, such as to
378                    // perform substitution. The token just after the macro use,
379                    // e.g. the whitespace in "`foo ", is already in the buffer.
380                    // However, we don't want this token to be the next, but
381                    // rather have it follow after the macro expansion. To do
382                    // this, we need to push the token onto the macro stack and
383                    // then call `self.bump()` once the expansion has been added
384                    // to the stack.
385                    match self.token {
386                        Some((x, sp)) => self.macro_stack.push((x, sp)),
387                        None => (),
388                    }
389
390                    // Push the tokens of the macro onto the stack, potentially
391                    // substituting any macro parameters as necessary.
392                    if args.is_empty() {
393                        self.macro_stack
394                            .extend(makro.body.iter().rev().map(|&(tkn, sp)| (tkn, sp)));
395                    } else {
396                        let mut replacement = Vec::<TokenAndSpan>::new();
397                        // TODO: Make this work for argument names that contain
398                        // underscores.
399                        for tkn in &makro.body {
400                            match *tkn {
401                                (Text, sp) => match args.get(&sp.extract()) {
402                                    Some(substitute) => {
403                                        replacement.extend(substitute);
404                                    }
405                                    None => replacement.push(*tkn),
406                                },
407                                x => replacement.push(x),
408                            }
409                        }
410                        self.macro_stack
411                            .extend(replacement.iter().rev().map(|&(tkn, sp)| (tkn, sp)));
412                    }
413
414                    self.bump();
415                    return Ok(());
416                }
417            }
418
419            // Ignore the "`timescale" directive for now.
420            Directive::Timescale => {
421                while let Some((tkn, _)) = self.token {
422                    if tkn == Newline {
423                        break;
424                    }
425                    self.bump();
426                }
427                return Ok(());
428            }
429
430            Directive::CurrentFile => {
431                if !self.is_inactive() {
432                    self.macro_stack.push((CatTokenKind::Text, span));
433                }
434                return Ok(());
435            }
436
437            Directive::CurrentLine => {
438                if !self.is_inactive() {
439                    self.macro_stack.push((CatTokenKind::Digits, span));
440                }
441                return Ok(());
442            }
443
444            Directive::Resetall => {
445                if !self.is_inactive() {
446                    self.dirs = Default::default();
447                }
448                return Ok(());
449            }
450
451            Directive::Celldefine => {
452                if !self.is_inactive() {
453                    self.dirs.celldefine = true;
454                }
455                return Ok(());
456            }
457
458            Directive::Endcelldefine => {
459                if !self.is_inactive() {
460                    self.dirs.celldefine = false;
461                }
462                return Ok(());
463            }
464
465            Directive::DefaultNettype => {
466                if !self.is_inactive() {
467                    // Skip leading whitespace.
468                    match self.token {
469                        Some((Whitespace, _)) => self.bump(),
470                        _ => (),
471                    }
472
473                    // Parse the nettype.
474                    let tkn = match self.token {
475                        Some(tkn @ (Text, _)) => {
476                            self.bump();
477                            tkn
478                        }
479                        _ => {
480                            return Err(DiagBuilder2::fatal(
481                                "expected nettype after `default_nettype",
482                            )
483                            .span(span));
484                        }
485                    };
486
487                    // Store the nettype in the directive set.
488                    self.dirs.default_nettype = if tkn.1.extract() == "none" {
489                        None
490                    } else {
491                        Some(tkn)
492                    };
493                    debug!(
494                        "Set default_nettype to `{}`",
495                        self.dirs
496                            .default_nettype
497                            .map(|(_, sp)| sp.extract())
498                            .unwrap_or_else(|| "none".to_string())
499                    );
500                }
501                return Ok(());
502            }
503
504            Directive::BeginKeywords => {
505                if !self.is_inactive() {
506                    // Skip leading whitespace.
507                    match self.token {
508                        Some((Whitespace, _)) => self.bump(),
509                        _ => (),
510                    }
511
512                    // Consume the opening symbol.
513                    match self.token {
514                        Some((Symbol('"'), _)) => self.bump(),
515                        _ => {
516                            return Err(DiagBuilder2::fatal("expected `\"` after `begin_keywords")
517                                .span(span));
518                        }
519                    };
520
521                    // Consume the version specifier.
522                    let mut spec = String::new();
523                    while let Some(tkn) = self.token {
524                        if tkn.0 == Symbol('"') {
525                            break;
526                        }
527                        spec.push_str(&tkn.1.extract());
528                        self.bump();
529                    }
530
531                    // Consume the closing symbol.
532                    match self.token {
533                        Some((Symbol('"'), _)) => self.bump(),
534                        _ => {
535                            return Err(DiagBuilder2::fatal(
536                                "expected `\"` after version specifier",
537                            )
538                            .span(span));
539                        }
540                    };
541
542                    // Parse the version.
543                    let spec = match KeywordsDirective::from_str(&spec) {
544                        Some(spec) => spec,
545                        _ => {
546                            return Err(DiagBuilder2::fatal(format!(
547                                "unknown `begin_keywords version specifier `{}`",
548                                spec
549                            ))
550                            .span(span));
551                        }
552                    };
553                    self.dirs.keywords.push(spec);
554                    debug!("Push keywords; now `{:?}`", self.dirs.keywords.last());
555                }
556                return Ok(());
557            }
558
559            Directive::EndKeywords => {
560                if !self.is_inactive() {
561                    if self.dirs.keywords.pop().is_none() {
562                        return Err(DiagBuilder2::fatal(
563                            "`end_keywords without earlier `begin_keywords",
564                        )
565                        .span(span));
566                    }
567                    debug!("Pop keywords; now `{:?}`", self.dirs.keywords.last());
568                }
569                return Ok(());
570            }
571
572            Directive::Line => {
573                if !self.is_inactive() {
574                    // Skip whitespace.
575                    match self.token {
576                        Some((Whitespace, _)) => self.bump(),
577                        _ => (),
578                    }
579
580                    // Consume line number.
581                    // TODO: Actually make use of this.
582                    let _line = match self.token {
583                        Some((Digits, sp)) => {
584                            self.bump();
585                            sp
586                        }
587                        _ => {
588                            return Err(
589                                DiagBuilder2::fatal("expected line number after `line").span(span)
590                            );
591                        }
592                    };
593
594                    // Skip whitespace.
595                    match self.token {
596                        Some((Whitespace, _)) => self.bump(),
597                        _ => (),
598                    }
599
600                    // Consume the opening symbol.
601                    match self.token {
602                        Some((Symbol('"'), _)) => self.bump(),
603                        _ => {
604                            return Err(DiagBuilder2::fatal(
605                                "expected `\"` after line number in `line",
606                            )
607                            .span(span));
608                        }
609                    };
610
611                    // Consume the file name.
612                    let mut filename = String::new();
613                    while let Some(tkn) = self.token {
614                        if tkn.0 == Symbol('"') {
615                            break;
616                        }
617                        filename.push_str(&tkn.1.extract());
618                        self.bump();
619                    }
620
621                    // Consume the closing symbol.
622                    match self.token {
623                        Some((Symbol('"'), _)) => self.bump(),
624                        _ => {
625                            return Err(DiagBuilder2::fatal(
626                                "expected `\"` after filename in `line",
627                            )
628                            .span(span));
629                        }
630                    };
631
632                    // Skip whitespace.
633                    match self.token {
634                        Some((Whitespace, _)) => self.bump(),
635                        _ => (),
636                    }
637
638                    // Consume level.
639                    // TODO: Actually make use of this.
640                    let _level = match self.token {
641                        Some((Digits, sp)) => {
642                            self.bump();
643                            sp
644                        }
645                        _ => {
646                            return Err(
647                                DiagBuilder2::fatal("expected level after `line").span(span)
648                            );
649                        }
650                    };
651
652                    debug!("Ignoring `line directive");
653                }
654                return Ok(());
655            }
656
657            Directive::UnconnectedDrive => {
658                if !self.is_inactive() {
659                    // Skip leading whitespace.
660                    match self.token {
661                        Some((Whitespace, _)) => self.bump(),
662                        _ => (),
663                    }
664
665                    // Parse the pull type.
666                    let tkn = match self.token {
667                        Some((Text, _)) => self.token,
668                        _ => None,
669                    };
670                    let pull = match tkn.map(|(_, sp)| sp.extract()).as_ref().map(|s| s.as_str()) {
671                        Some("pull0") => UnconnectedDrive::Pull0,
672                        Some("pull1") => UnconnectedDrive::Pull1,
673                        _ => {
674                            return Err(DiagBuilder2::fatal(
675                                "expected `pull0` or `pull1` after `unconnected_drive",
676                            )
677                            .span(span));
678                        }
679                    };
680                    self.bump(); // consume the pull
681
682                    // Store the directive.
683                    self.dirs.unconnected_drive = Some(pull);
684                    debug!("Set unconnected_drive to {:?}", self.dirs.unconnected_drive);
685                }
686                return Ok(());
687            }
688
689            Directive::NoUnconnectedDrive => {
690                if !self.is_inactive() {
691                    self.dirs.unconnected_drive = None;
692                    debug!("Set unconnected_drive to {:?}", self.dirs.unconnected_drive);
693                }
694                return Ok(());
695            }
696        }
697
698        return Err(
699            DiagBuilder2::fatal(format!("unknown compiler directive '`{}'", dir_name)).span(span),
700        );
701    }
702
703    fn open_include(&mut self, filename: &str, current_file: &str) -> Option<Source> {
704        // println!("Resolving include '{}' from '{}'", filename, current_file);
705        let first = [Path::new(current_file)
706            .parent()
707            .expect("current file path must have a valid parent")];
708        let prefices = first.iter().chain(self.include_paths.iter());
709        let sm = get_source_manager();
710        for prefix in prefices {
711            let mut buf = prefix.to_path_buf();
712            buf.push(filename);
713            // println!("  trying {}", buf.to_str().unwrap());
714            let src = sm.open(buf.to_str().unwrap());
715            if src.is_some() {
716                return src;
717            }
718        }
719        return None;
720    }
721
722    /// Check whether we are inside a disabled define conditional. That is,
723    /// whether a preceeding `ifdef, `ifndef, `else, or `elsif directive have
724    /// disabled the subsequent code.
725    fn is_inactive(&self) -> bool {
726        match self.defcond_stack.last() {
727            Some(&Defcond::Enabled) | None => false,
728            _ => true,
729        }
730    }
731
732    fn try_eat_name(&mut self) -> Option<(String, Span)> {
733        // Eat the first token of the name, which may either be a letter or an
734        // underscore.
735        let (mut name, mut span) = match self.token {
736            Some((Text, sp)) | Some((Symbol('_'), sp)) => (sp.extract(), sp),
737            _ => return None,
738        };
739        self.bump();
740
741        // Eat the remaining tokens of the name, which may be letters, digits,
742        // or underscores.
743        loop {
744            match self.token {
745                Some((Text, sp)) | Some((Digits, sp)) | Some((Symbol('_'), sp)) => {
746                    name.push_str(&sp.extract());
747                    span.expand(sp);
748                    self.bump();
749                }
750                _ => break,
751            }
752        }
753
754        Some((name, span))
755    }
756
757    // Skip over any white space characters.
758    fn skip_whitespace(&mut self) -> bool {
759        match self.token {
760            Some((Whitespace, _)) => {
761                self.bump();
762                true
763            }
764            _ => false,
765        }
766    }
767
768    // Parse the macro definition following a '`define' directive.
769    fn handle_macro_definition(&mut self, define_span: Span) -> Result<Macro, DiagBuilder2> {
770        let mut all_span = define_span;
771
772        // Consume the macro name.
773        let (name, name_span) = match self.try_eat_name() {
774            Some(x) => x,
775            None => {
776                return Err(
777                    DiagBuilder2::fatal("expected macro name after \"`define\"").span(define_span)
778                );
779            }
780        };
781        all_span.expand(define_span);
782        let mut makro = Macro::new(name, name_span);
783
784        // NOTE: No whitespace is allowed after the macro name such that
785        // the preprocessor does not mistake the a in "`define FOO (a)"
786        // for a macro argument.
787
788        makro.args = self.handle_macro_definition_args()?;
789
790        // Skip whitespace between the macro parameters and definition.
791        self.skip_whitespace();
792
793        // Consume the macro definition up to the next newline not preceded
794        // by a backslash, ignoring comments, whitespace and newlines.
795        loop {
796            match self.token {
797                Some((Newline, _)) => {
798                    self.bump();
799                    break;
800                }
801                // Some((Whitespace, _)) => self.bump(),
802                // Some((Comment, _)) => self.bump(),
803                Some((Symbol('\\'), _)) => {
804                    self.bump();
805                    match self.token {
806                        Some((Newline, _)) => self.bump(),
807                        _ => (),
808                    };
809                }
810                Some(x) => {
811                    makro.body.push(x);
812                    self.bump();
813                }
814                None => break,
815            }
816        }
817        Ok(makro)
818    }
819
820    fn handle_macro_definition_args(&mut self) -> Result<Vec<MacroArg>, DiagBuilder2> {
821        // Consume the opening parenthesis.
822        let mut all_span = match self.token {
823            Some((Symbol('('), sp)) => {
824                self.bump();
825                sp
826            }
827            _ => return Ok(vec![]),
828        };
829
830        // Consume the arguments.
831        let mut args = vec![];
832        loop {
833            // Check if we ran out of arguments.
834            self.skip_whitespace();
835            match self.token {
836                Some((Symbol(')'), _)) => {
837                    self.bump();
838                    break;
839                }
840                None => {
841                    return Err(
842                        DiagBuilder2::fatal("expected `)` after macro arguments").span(all_span)
843                    )
844                }
845                _ => (),
846            }
847
848            // Consume the argument name.
849            let (name, name_span) = match self.try_eat_name() {
850                Some(x) => x,
851                _ => {
852                    return Err(
853                        DiagBuilder2::fatal("expected macro argument name").span(all_span.end())
854                    );
855                }
856            };
857            self.skip_whitespace();
858
859            // Consume the optional default parameter.
860            let default = match self.token {
861                Some((Symbol('='), sp)) => {
862                    all_span.expand(sp);
863                    self.bump();
864                    self.skip_whitespace();
865                    let mut tokens = vec![];
866                    let mut nesting = 0;
867                    loop {
868                        match self.token {
869                            Some((Symbol(','), _)) | Some((Symbol(')'), _)) if nesting == 0 => {
870                                match tokens.last() {
871                                    Some((Whitespace, _)) => {
872                                        tokens.pop();
873                                    }
874                                    _ => (),
875                                }
876                                break;
877                            }
878                            Some(x @ (Symbol('('), _))
879                            | Some(x @ (Symbol('{'), _))
880                            | Some(x @ (Symbol('['), _)) => {
881                                nesting += 1;
882                                all_span.expand(x.1);
883                                tokens.push(x);
884                                self.bump();
885                            }
886                            Some(x @ (Symbol(')'), _))
887                            | Some(x @ (Symbol('}'), _))
888                            | Some(x @ (Symbol(']'), _)) => {
889                                nesting -= 1;
890                                all_span.expand(x.1);
891                                tokens.push(x);
892                                self.bump();
893                            }
894                            Some(x) => {
895                                all_span.expand(x.1);
896                                tokens.push(x);
897                                self.bump();
898                            }
899                            _ => break,
900                        }
901                    }
902                    Some(tokens)
903                }
904                _ => None,
905            };
906
907            args.push(MacroArg {
908                name,
909                span: name_span,
910                default,
911            });
912
913            // Consume the trailing comma.
914            self.skip_whitespace();
915            match self.token {
916                Some((Symbol(','), _)) => self.bump(),
917                Some((Symbol(')'), _)) => (),
918                Some((_, sp)) => {
919                    return Err(
920                        DiagBuilder2::fatal("expected `,` or `)` after macro argument").span(sp),
921                    )
922                }
923                None => (),
924            }
925        }
926        Ok(args)
927    }
928
929    fn handle_macro_expansion_args(
930        &mut self,
931        makro: &Macro,
932        span: Span,
933    ) -> Result<MacroExpansionParams, DiagBuilder2> {
934        // If the macro does not define any arguments, we're done.
935        if makro.args.is_empty() {
936            return Ok(Default::default());
937        }
938        let mut all_span = span;
939
940        // Skip whitespace between '`foo' and `(`.
941        self.skip_whitespace();
942
943        // Consume the opening paranthesis.
944        match self.token {
945            Some((Symbol('('), sp)) => {
946                self.bump();
947                all_span.expand(sp);
948            }
949            _ => {
950                return Err(DiagBuilder2::fatal(format!(
951                    "expected macro arguments for `{}`",
952                    makro.name
953                ))
954                .span(all_span)
955                .add_note(format!(
956                    "At least parenthesis are needed: `{}()`",
957                    makro.name
958                ))
959                .add_note(format!(
960                    "Macro `{}` defines {} arguments:",
961                    makro.name,
962                    makro.args.len()
963                ))
964                .span(makro.span));
965            }
966        }
967        self.skip_whitespace();
968
969        // Consume the macro arguments. Be careful about the fact that it is
970        // allowed to have parentheses as macro arguments, which requires
971        // bookkeeping of the parentheses nesting level. If a comma is
972        // encountered, we break out of the inner loop such that the next
973        // parameter will be read. If a  closing parenthesis is encountered, we
974        // break out of the outer loop to finish parameter parsing.
975        let mut args = vec![];
976        'outer: loop {
977            let mut arg_tokens = Vec::<TokenAndSpan>::new();
978            let mut nesting = 0;
979            loop {
980                match self.token {
981                    Some((Symbol(','), sp)) if nesting == 0 => {
982                        args.push(arg_tokens);
983                        all_span.expand(sp);
984                        self.bump();
985                        self.skip_whitespace();
986                        break;
987                    }
988                    Some((Symbol(')'), sp)) if nesting == 0 => {
989                        args.push(arg_tokens);
990                        all_span.expand(sp);
991                        self.bump();
992                        break 'outer;
993                    }
994                    Some(x @ (Symbol('('), _))
995                    | Some(x @ (Symbol('{'), _))
996                    | Some(x @ (Symbol('['), _)) => {
997                        arg_tokens.push(x);
998                        nesting += 1;
999                        self.bump();
1000                        all_span.expand(x.1);
1001                    }
1002                    Some(x @ (Symbol(')'), _))
1003                    | Some(x @ (Symbol('}'), _))
1004                    | Some(x @ (Symbol(']'), _)) => {
1005                        arg_tokens.push(x);
1006                        nesting -= 1;
1007                        self.bump();
1008                        all_span.expand(x.1);
1009                    }
1010                    Some(x) => {
1011                        arg_tokens.push(x);
1012                        self.bump();
1013                        all_span.expand(x.1);
1014                    }
1015                    None => {
1016                        return Err(DiagBuilder2::fatal("expected `)` after macro arguments")
1017                            .span(all_span));
1018                    }
1019                }
1020            }
1021        }
1022
1023        // Remove trailing whitespace from macro arguments.
1024        for arg in &mut args {
1025            match arg.last() {
1026                Some((Whitespace, _)) => {
1027                    arg.pop();
1028                }
1029                _ => (),
1030            }
1031        }
1032
1033        // Align the expansion arguments with those in the macro definition.
1034        if makro.args.len() < args.len() {
1035            let d = DiagBuilder2::fatal(format!(
1036                "macro expansion with {} arguments, but `{}` expects {} arguments",
1037                args.len(),
1038                makro.name,
1039                makro.args.len()
1040            ))
1041            .span(all_span)
1042            .add_note(format!("Definition of `{}` was here:", makro.name))
1043            .span(makro.span);
1044            return Err(d);
1045        }
1046        let args = makro
1047            .args
1048            .iter()
1049            .zip(args.into_iter().map(Some).chain(std::iter::repeat(None)))
1050            .map(|(def, exp)| {
1051                let is_empty = !exp.iter().flatten().any(|(t, _)| match t {
1052                    Whitespace => false,
1053                    _ => true,
1054                });
1055                let value = match (exp, def.default.as_ref()) {
1056                    // Expansion arguments include this argument, but may be
1057                    // empty.
1058                    (Some(ref _exp), Some(default)) if is_empty => default.clone(),
1059                    (Some(ref _exp), None) if is_empty => vec![],
1060                    (Some(exp), _) => exp,
1061
1062                    // Expansion arguments ended before this argument.
1063                    (None, Some(default)) => default.clone(),
1064                    (None, None) => {
1065                        let d = DiagBuilder2::fatal(format!(
1066                            "macro expansion missing value for `{}`",
1067                            def.name
1068                        ))
1069                        .span(all_span)
1070                        .add_note(format!(
1071                            "Macro argument `{}` needs a value because it has no default:",
1072                            def.name
1073                        ))
1074                        .span(def.span);
1075                        return Err(d);
1076                    }
1077                };
1078                Ok((def.name.clone(), value))
1079            })
1080            .collect::<Result<HashMap<_, _>, _>>()?;
1081
1082        Ok(args)
1083    }
1084}
1085
1086type MacroExpansionParams = HashMap<String, Vec<TokenAndSpan>>;
1087
1088impl<'a> Iterator for Preprocessor<'a> {
1089    type Item = DiagResult2<TokenAndSpan>;
1090
1091    fn next(&mut self) -> Option<DiagResult2<TokenAndSpan>> {
1092        // In case this is the first call to next(), the token has not been
1093        // populated yet. In this case we need to artificially bump the lexer.
1094        if self.token.is_none() {
1095            self.bump();
1096        }
1097        loop {
1098            // This is the main loop of the lexer. Upon each iteration the next
1099            // token is inspected and the lexer decides whether to emit it or
1100            // not. If no token was emitted (e.g. because it was a preprocessor
1101            // directive or we're inside an inactive `ifdef block), the loop
1102            // continues with the next token.
1103            match self.token {
1104                Some((Symbol('`'), sp_backtick)) => {
1105                    self.bump(); // consume the backtick
1106                    if let Some((name, sp)) = self.try_eat_name() {
1107                        // We arrive here if the sequence a backtick
1108                        // followed by text was encountered. In this case we
1109                        // call upon the handle_directive function to
1110                        // perform the necessary actions.
1111                        let dir_span = Span::union(sp_backtick, sp);
1112                        match self.handle_directive(name, dir_span) {
1113                            Err(x) => return Some(Err(x)),
1114                            _ => (),
1115                        }
1116                        continue;
1117                    } else if let Some(tkn @ (Symbol('"'), _)) = self.token {
1118                        // emit the '"'
1119                        self.bump();
1120                        if !self.is_inactive() {
1121                            return Some(Ok(tkn));
1122                        }
1123                    } else if let Some(tkn @ (Symbol('\\'), _)) = self.token {
1124                        // emit the '\'
1125                        self.bump();
1126                        if !self.is_inactive() {
1127                            return Some(Ok(tkn));
1128                        }
1129                    } else if let Some((Symbol('`'), _)) = self.token {
1130                        self.bump(); // consume the second backtick and ignore
1131                    } else {
1132                        return Some(Err(DiagBuilder2::fatal(
1133                            "expected compiler directive after '`', or '``', '`\"', or '`\\'",
1134                        )
1135                        .span(sp_backtick)));
1136                    }
1137                }
1138                _ => {
1139                    // All tokens other than preprocessor directives are
1140                    // emitted, unless we're currently inside a disabled define
1141                    // conditional.
1142                    if self.is_inactive() {
1143                        self.bump();
1144                    } else {
1145                        let tkn = self.token.map(|x| Ok(x));
1146                        self.bump();
1147                        return tkn;
1148                    }
1149                }
1150            }
1151        }
1152    }
1153}
1154
1155struct Stream<'a> {
1156    source: Source,
1157    iter: Cat<'a>,
1158}
1159
1160/// The different compiler directives recognized by the preprocessor.
1161#[derive(Debug, Clone, Copy)]
1162enum Directive {
1163    Include,
1164    Define,
1165    Undef,
1166    Undefineall,
1167    Ifdef,
1168    Ifndef,
1169    Else,
1170    Elsif,
1171    Endif,
1172    Timescale,
1173    CurrentFile,
1174    CurrentLine,
1175    Resetall,
1176    Celldefine,
1177    Endcelldefine,
1178    DefaultNettype,
1179    BeginKeywords,
1180    EndKeywords,
1181    Line,
1182    UnconnectedDrive,
1183    NoUnconnectedDrive,
1184    Unknown,
1185}
1186
1187impl fmt::Display for Directive {
1188    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1189        match self {
1190            Directive::Include => write!(f, "`include"),
1191            Directive::Define => write!(f, "`define"),
1192            Directive::Undef => write!(f, "`undef"),
1193            Directive::Undefineall => write!(f, "`undefineall"),
1194            Directive::Ifdef => write!(f, "`ifdef"),
1195            Directive::Ifndef => write!(f, "`ifndef"),
1196            Directive::Else => write!(f, "`else"),
1197            Directive::Elsif => write!(f, "`elsif"),
1198            Directive::Endif => write!(f, "`endif"),
1199            Directive::Timescale => write!(f, "`timescale"),
1200            Directive::CurrentFile => write!(f, "`__FILE__"),
1201            Directive::CurrentLine => write!(f, "`__LINE__"),
1202            Directive::Resetall => write!(f, "`resetall"),
1203            Directive::Celldefine => write!(f, "`celldefine"),
1204            Directive::Endcelldefine => write!(f, "`endcelldefine"),
1205            Directive::DefaultNettype => write!(f, "`default_nettype"),
1206            Directive::BeginKeywords => write!(f, "`begin_keywords"),
1207            Directive::EndKeywords => write!(f, "`end_keywords"),
1208            Directive::Line => write!(f, "`line"),
1209            Directive::UnconnectedDrive => write!(f, "`unconnected_drive"),
1210            Directive::NoUnconnectedDrive => write!(f, "`nounconnected_drive"),
1211            Directive::Unknown => write!(f, "unknown"),
1212        }
1213    }
1214}
1215
1216static DIRECTIVES_TABLE: Lazy<HashMap<&'static str, Directive>> = Lazy::new(|| {
1217    let mut table = HashMap::new();
1218    table.insert("include", Directive::Include);
1219    table.insert("define", Directive::Define);
1220    table.insert("undef", Directive::Undef);
1221    table.insert("undefineall", Directive::Undefineall);
1222    table.insert("ifdef", Directive::Ifdef);
1223    table.insert("ifndef", Directive::Ifndef);
1224    table.insert("else", Directive::Else);
1225    table.insert("elsif", Directive::Elsif);
1226    table.insert("endif", Directive::Endif);
1227    table.insert("__FILE__", Directive::CurrentFile);
1228    table.insert("__LINE__", Directive::CurrentLine);
1229    table.insert("resetall", Directive::Resetall);
1230    table.insert("celldefine", Directive::Celldefine);
1231    table.insert("endcelldefine", Directive::Endcelldefine);
1232    table.insert("default_nettype", Directive::DefaultNettype);
1233    table.insert("begin_keywords", Directive::BeginKeywords);
1234    table.insert("end_keywords", Directive::EndKeywords);
1235    table.insert("line", Directive::Line);
1236    table.insert("unconnected_drive", Directive::UnconnectedDrive);
1237    table.insert("nounconnected_drive", Directive::NoUnconnectedDrive);
1238    table.insert("timescale", Directive::Timescale);
1239    table
1240});
1241
1242#[derive(Debug)]
1243struct Macro {
1244    name: String,
1245    span: Span,
1246    args: Vec<MacroArg>,
1247    body: Vec<TokenAndSpan>,
1248}
1249
1250impl Macro {
1251    fn new(name: String, span: Span) -> Macro {
1252        Macro {
1253            name: name,
1254            span: span,
1255            args: Vec::new(),
1256            body: Vec::new(),
1257        }
1258    }
1259}
1260
1261#[derive(Debug)]
1262struct MacroArg {
1263    name: String,
1264    span: Span,
1265    default: Option<Vec<TokenAndSpan>>,
1266}
1267
1268enum Defcond {
1269    Done,
1270    Enabled,
1271    Disabled,
1272}
1273
1274#[derive(Default)]
1275struct Directives {
1276    celldefine: bool,
1277    default_nettype: Option<TokenAndSpan>,
1278    keywords: Vec<KeywordsDirective>,
1279    unconnected_drive: Option<UnconnectedDrive>,
1280}
1281
1282#[allow(non_camel_case_types)]
1283#[derive(Debug)]
1284enum KeywordsDirective {
1285    Ieee1800_2009,
1286    Ieee1800_2005,
1287    Ieee1364_2005,
1288    Ieee1364_2001,
1289    Ieee1364_2001_Noconfig,
1290    Ieee1364_1995,
1291}
1292
1293#[derive(Debug)]
1294enum UnconnectedDrive {
1295    Pull0,
1296    Pull1,
1297}
1298
1299impl KeywordsDirective {
1300    pub fn from_str(s: &str) -> Option<Self> {
1301        match s {
1302            "1800-2009" => Some(Self::Ieee1800_2009),
1303            "1800-2005" => Some(Self::Ieee1800_2005),
1304            "1364-2005" => Some(Self::Ieee1364_2005),
1305            "1364-2001" => Some(Self::Ieee1364_2001),
1306            "1364-2001-noconfig" => Some(Self::Ieee1364_2001_Noconfig),
1307            "1364-1995" => Some(Self::Ieee1364_1995),
1308            _ => None,
1309        }
1310    }
1311}
1312
1313#[cfg(test)]
1314mod tests {
1315    use super::*;
1316
1317    fn preproc(input: &str) -> Preprocessor {
1318        use std::cell::Cell;
1319        thread_local!(static INDEX: Cell<usize> = Cell::new(0));
1320        let sm = get_source_manager();
1321        let idx = INDEX.with(|i| {
1322            let v = i.get();
1323            i.set(v + 1);
1324            v
1325        });
1326        let source = sm.add(&format!("test_{}.sv", idx), input);
1327        Preprocessor::new(source, &[], &[])
1328    }
1329
1330    fn check_str(input: &str, expected: &str) {
1331        let pp = preproc(input);
1332        let actual: String = pp.map(|x| x.unwrap().1.extract()).collect();
1333        assert_eq!(actual, expected);
1334    }
1335
1336    #[test]
1337    fn include() {
1338        let sm = get_source_manager();
1339        sm.add("other.sv", "bar\n");
1340        sm.add("test.sv", "foo\n`include \"other.sv\"\nbaz");
1341        let pp = Preprocessor::new(sm.open("test.sv").unwrap(), &[], &[]);
1342        let actual: Vec<_> = pp.map(|x| x.unwrap().0).collect();
1343        assert_eq!(actual, &[Text, Newline, Text, Newline, Newline, Text,]);
1344    }
1345
1346    #[test]
1347    fn include_and_define() {
1348        let sm = get_source_manager();
1349        sm.add("other.sv", "/* World */\n`define foo 42\nbar");
1350        sm.add(
1351            "test.sv",
1352            "// Hello\n`include \"other.sv\"\n`foo something\n",
1353        );
1354        let pp = Preprocessor::new(sm.open("test.sv").unwrap(), &[], &[]);
1355        let actual: String = pp
1356            .map(|x| {
1357                let x = x.unwrap();
1358                println!("{:?}", x);
1359                x.1.extract()
1360            })
1361            .collect();
1362        assert_eq!(actual, "// Hello\n/* World */\nbar\n42 something\n");
1363    }
1364
1365    #[test]
1366    #[should_panic(expected = "unknown compiler directive")]
1367    fn conditional_define() {
1368        let sm = get_source_manager();
1369        let source = sm.add("test.sv", "`ifdef FOO\n`define BAR\n`endif\n`BAR");
1370        let mut pp = Preprocessor::new(source, &[], &[]);
1371        while let Some(tkn) = pp.next() {
1372            tkn.unwrap();
1373        }
1374    }
1375
1376    #[test]
1377    fn macro_args() {
1378        check_str(
1379            "`define foo(x,y) {x + y _bar}\n`foo(12, foo)\n",
1380            "{12 + foo _bar}\n",
1381        );
1382    }
1383
1384    /// Verify that macros that take no arguments but have parantheses around
1385    /// their body parse properly.
1386    #[test]
1387    fn macro_noargs_parentheses() {
1388        check_str(
1389            "`define FOO 4\n`define BAR (`FOO+$clog2(2))\n`BAR",
1390            "(4+$clog2(2))",
1391        );
1392    }
1393
1394    #[test]
1395    fn macro_name_with_digits_and_underscores() {
1396        check_str("`define AXI_BUS21_SV 42\n`AXI_BUS21_SV", "42");
1397    }
1398}