sv_parser_pp/
preprocess.rs

1use crate::range::Range;
2use nom::combinator::all_consuming;
3use nom_greedyerror::error_position;
4use std::collections::{BTreeMap, HashMap};
5use std::convert::TryInto;
6use std::fs::File;
7use std::hash::BuildHasher;
8use std::io::{BufReader, Read};
9use std::path::{Path, PathBuf};
10use sv_parser_error::Error;
11use sv_parser_parser::{pp_parser, Span, SpanInfo};
12use sv_parser_syntaxtree::{
13    IncludeCompilerDirective, Locate, NodeEvent, RefNode, SourceDescription, TextMacroUsage,
14    WhiteSpace,
15};
16use std::collections::hash_map::RandomState;
17
18const RECURSIVE_LIMIT: usize = 64;
19
20#[derive(Debug)]
21pub struct PreprocessedText {
22    text: String,
23    origins: BTreeMap<Range, Origin>,
24}
25
26#[derive(Debug)]
27pub struct Origin {
28    range: Range,
29    origin: Option<(PathBuf, Range)>,
30}
31
32impl PreprocessedText {
33    fn new() -> Self {
34        PreprocessedText {
35            text: String::new(),
36            origins: BTreeMap::new(),
37        }
38    }
39
40    fn push<T: AsRef<Path>>(&mut self, s: &str, origin: Option<(T, Range)>) {
41        let base = self.text.len();
42        self.text.push_str(s);
43
44        let origin = if let Some((origin_path, origin_range)) = origin {
45            let origin_path = PathBuf::from(origin_path.as_ref());
46            Some((origin_path, origin_range))
47        } else {
48            None
49        };
50
51        let range = Range::new(base, base + s.len());
52        let origin = Origin { range, origin };
53        self.origins.insert(range, origin);
54    }
55
56    fn merge(&mut self, other: PreprocessedText) {
57        let base = self.text.len();
58        self.text.push_str(&other.text);
59        for (mut range, mut origin) in other.origins {
60            range.offset(base);
61            origin.range.offset(base);
62            self.origins.insert(range, origin);
63        }
64    }
65
66    pub fn text(&self) -> &str {
67        &self.text
68    }
69
70    pub fn origin(&self, pos: usize) -> Option<(&PathBuf, usize)> {
71        let origin = self.origins.get(&Range::new(pos, pos + 1));
72        if let Some(origin) = origin {
73            if let Some((ref origin_path, ref origin_range)) = origin.origin {
74                let ret_pos = pos - origin.range.begin + origin_range.begin;
75                Some((&origin_path, ret_pos))
76            } else {
77                None
78            }
79        } else {
80            None
81        }
82    }
83}
84
85#[derive(Clone, Debug, Eq, PartialEq)]
86pub struct Define {
87    pub identifier: String,
88    pub arguments: Vec<(String, Option<String>)>,
89    pub text: Option<DefineText>,
90}
91
92#[derive(Clone, Debug, Eq, PartialEq)]
93pub struct DefineText {
94    pub text: String,
95    pub origin: Option<(PathBuf, Range)>,
96}
97
98impl Define {
99    pub fn new(
100        ident: String,
101        args: Vec<(String, Option<String>)>,
102        text: Option<DefineText>,
103    ) -> Self {
104        Define {
105            identifier: ident,
106            arguments: args,
107            text,
108        }
109    }
110}
111
112impl DefineText {
113    pub fn new(text: String, origin: Option<(PathBuf, Range)>) -> Self {
114        DefineText { text, origin }
115    }
116}
117
118pub type Defines<V=RandomState> = HashMap<String, Option<Define>, V>;
119
120pub fn preprocess<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
121    path: T,
122    pre_defines: &Defines<V>,
123    include_paths: &[U],
124    strip_comments: bool,
125    ignore_include: bool,
126) -> Result<(PreprocessedText, Defines), Error> {
127    preprocess_inner(
128        path,
129        pre_defines,
130        include_paths,
131        strip_comments,
132        ignore_include,
133        0, // include_depth
134    )
135}
136
137fn preprocess_inner<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
138    path: T,
139    pre_defines: &Defines<V>,
140    include_paths: &[U],
141    strip_comments: bool,
142    ignore_include: bool,
143    include_depth: usize,
144) -> Result<(PreprocessedText, Defines), Error> {
145
146    let f = File::open(path.as_ref()).map_err(|x| Error::File {
147        source: x,
148        path: PathBuf::from(path.as_ref()),
149    })?;
150    let mut reader = BufReader::new(f);
151    let mut s = String::new();
152
153    if let Err(_) = reader.read_to_string(&mut s) {
154        Err(Error::ReadUtf8(PathBuf::from(path.as_ref())))
155    } else {
156        preprocess_str(
157            &s,
158            path,
159            pre_defines,
160            include_paths,
161            ignore_include,
162            strip_comments,
163            0, // resolve_depth
164            include_depth,
165        )
166    }
167}
168
169struct SkipNodes<'a> {
170    nodes: Vec<RefNode<'a>>,
171}
172
173impl<'a> SkipNodes<'a> {
174    fn new() -> Self {
175        Self { nodes: vec![] }
176    }
177
178    fn push(&mut self, node: RefNode<'a>) {
179        // if a node doesn't have locate, the node should be ignored
180        // because the node can be identified in tree.
181        let mut have_locate = false;
182        for x in node.clone() {
183            if let RefNode::Locate(_) = x {
184                have_locate = true;
185            }
186        }
187        if have_locate {
188            self.nodes.push(node);
189        }
190    }
191
192    fn contains(&self, node: &RefNode<'a>) -> bool {
193        self.nodes.contains(node)
194    }
195}
196
197pub fn preprocess_str<T: AsRef<Path>, U: AsRef<Path>, V: BuildHasher>(
198    s: &str,
199    path: T,
200    pre_defines: &Defines<V>,
201    include_paths: &[U],
202    ignore_include: bool,
203    strip_comments: bool,
204    resolve_depth: usize,
205    include_depth: usize,
206) -> Result<(PreprocessedText, Defines), Error> {
207
208    // IEEE1800-2017 Clause 22.4, page 675
209    // A file included in the source using the `include compiler directive
210    // may contain other `include compiler directives.
211    // The number of nesting levels for include files shall be finite.
212    // Implementations may limit the maximum number of levels to which
213    // include files can be nested, but the limit shall be at least 15.
214    if include_depth > RECURSIVE_LIMIT {
215        return Err(Error::ExceedRecursiveLimit);
216    }
217
218    let mut skip = false;
219    let mut skip_whitespace = false;
220    let mut skip_nodes = SkipNodes::new();
221    let mut defines = HashMap::new();
222
223    let mut last_item_line = None;
224    let mut last_include_line = None;
225
226    // IEEE1800-2017 Clause 40.3.1, page 1121
227    // The following predefined `define macros represent basic real-time
228    // coverage capabilities accessible directly from SystemVerilog:
229    let sv_cov_pre_defines = [
230        ("SV_COV_START",        "0"),
231        ("SV_COV_STOP",         "1"),
232        ("SV_COV_RESET",        "2"),
233        ("SV_COV_CHECK",        "3"),
234        ("SV_COV_MODULE",       "10"),
235        ("SV_COV_HIER",         "11"),
236        ("SV_COV_ASSERTION",    "20"),
237        ("SV_COV_FSM_STATE",    "21"),
238        ("SV_COV_STATEMENT",    "22"),
239        ("SV_COV_TOGGLE",       "23"),
240        ("SV_COV_OVERFLOW",     "-2"),
241        ("SV_COV_ERROR",        "-1"),
242        ("SV_COV_NOCOV",        "0"),
243        ("SV_COV_OK",           "1"),
244        ("SV_COV_PARTIAL",      "2"),
245    ];
246    for (k, v) in sv_cov_pre_defines {
247        let define = Define {
248            identifier: k.to_string(),
249            arguments: Vec::new(),
250            text: Some(DefineText {text: v.to_string(), origin: None}),
251        };
252        defines.insert(k.to_string(), Some(define));
253    }
254
255    for (k, v) in pre_defines {
256        defines.insert(k.clone(), (*v).clone());
257    }
258
259    let span = Span::new_extra(&s, SpanInfo::default());
260    let (_, pp_text) = all_consuming(pp_parser)(span).map_err(|x| match x {
261        nom::Err::Incomplete(_) => Error::Preprocess(None),
262        nom::Err::Error(e) => {
263            if let Some(pos) = error_position(&e) {
264                Error::Preprocess(Some((PathBuf::from(path.as_ref()), pos)))
265            } else {
266                Error::Preprocess(None)
267            }
268        }
269        nom::Err::Failure(e) => {
270            if let Some(pos) = error_position(&e) {
271                Error::Preprocess(Some((PathBuf::from(path.as_ref()), pos)))
272            } else {
273                Error::Preprocess(None)
274            }
275        }
276    })?;
277
278    let mut ret = PreprocessedText::new();
279
280    for n in pp_text.into_iter().event() {
281        match n.clone() {
282            NodeEvent::Enter(x) => {
283                if skip_nodes.contains(&x) {
284                    skip = true;
285                }
286            }
287            NodeEvent::Leave(x) => {
288                if skip_nodes.contains(&x) {
289                    skip = false;
290                }
291            }
292        }
293        if skip {
294            continue;
295        }
296
297        match n.clone() {
298            NodeEvent::Enter(RefNode::SourceDescriptionNotDirective(x)) => {
299                let locate: Locate = x.try_into().unwrap();
300                if let Some(last_include_line) = last_include_line {
301                    if last_include_line == locate.line {
302                        return Err(Error::IncludeLine);
303                    }
304                }
305            }
306            NodeEvent::Enter(RefNode::CompilerDirective(x)) => {
307                let locate: Locate = x.try_into().unwrap();
308                if let Some(last_include_line) = last_include_line {
309                    if last_include_line == locate.line {
310                        return Err(Error::IncludeLine);
311                    }
312                }
313            }
314            NodeEvent::Leave(RefNode::SourceDescriptionNotDirective(x)) => {
315                let locate: Locate = x.try_into().unwrap();
316                // If the item is whitespace, last_item_line should not be updated
317                if !locate.str(s).trim().is_empty() {
318                    last_item_line = Some(locate.line);
319                }
320            }
321            NodeEvent::Leave(RefNode::CompilerDirective(x)) => {
322                let locate: Locate = x.try_into().unwrap();
323                last_item_line = Some(locate.line);
324            }
325            _ => (),
326        }
327
328        match n {
329            NodeEvent::Enter(RefNode::SourceDescriptionNotDirective(x)) => {
330                let locate: Locate = x.try_into().unwrap();
331                let range = Range::new(locate.offset, locate.offset + locate.len);
332                ret.push(locate.str(&s), Some((path.as_ref(), range)));
333            }
334            NodeEvent::Enter(RefNode::SourceDescription(SourceDescription::StringLiteral(x))) => {
335                let locate: Locate = (&**x).try_into().unwrap();
336                let range = Range::new(locate.offset, locate.offset + locate.len);
337                ret.push(locate.str(&s), Some((path.as_ref(), range)));
338            }
339            NodeEvent::Enter(RefNode::SourceDescription(SourceDescription::EscapedIdentifier(
340                x,
341            ))) => {
342                let locate: Locate = (&**x).try_into().unwrap();
343                let range = Range::new(locate.offset, locate.offset + locate.len);
344                ret.push(locate.str(&s), Some((path.as_ref(), range)));
345            }
346            NodeEvent::Enter(RefNode::ResetallCompilerDirective(x)) => {
347                let locate: Locate = x.try_into().unwrap();
348                let range = Range::new(locate.offset, locate.offset + locate.len);
349                ret.push(locate.str(&s), Some((path.as_ref(), range)));
350                skip_whitespace = true;
351            }
352            NodeEvent::Leave(RefNode::ResetallCompilerDirective(_)) => {
353                skip_whitespace = false;
354            }
355            NodeEvent::Enter(RefNode::TimescaleCompilerDirective(x)) => {
356                let locate: Locate = x.try_into().unwrap();
357                let range = Range::new(locate.offset, locate.offset + locate.len);
358                ret.push(locate.str(&s), Some((path.as_ref(), range)));
359                skip_whitespace = true;
360            }
361            NodeEvent::Leave(RefNode::TimescaleCompilerDirective(_)) => {
362                skip_whitespace = false;
363            }
364            NodeEvent::Enter(RefNode::DefaultNettypeCompilerDirective(x)) => {
365                let locate: Locate = x.try_into().unwrap();
366                let range = Range::new(locate.offset, locate.offset + locate.len);
367                ret.push(locate.str(&s), Some((path.as_ref(), range)));
368                skip_whitespace = true;
369            }
370            NodeEvent::Leave(RefNode::DefaultNettypeCompilerDirective(_)) => {
371                skip_whitespace = false;
372            }
373            NodeEvent::Enter(RefNode::UnconnectedDriveCompilerDirective(x)) => {
374                let locate: Locate = x.try_into().unwrap();
375                let range = Range::new(locate.offset, locate.offset + locate.len);
376                ret.push(locate.str(&s), Some((path.as_ref(), range)));
377                skip_whitespace = true;
378            }
379            NodeEvent::Leave(RefNode::UnconnectedDriveCompilerDirective(_)) => {
380                skip_whitespace = false;
381            }
382            NodeEvent::Enter(RefNode::NounconnectedDriveCompilerDirective(x)) => {
383                let locate: Locate = x.try_into().unwrap();
384                let range = Range::new(locate.offset, locate.offset + locate.len);
385                ret.push(locate.str(&s), Some((path.as_ref(), range)));
386                skip_whitespace = true;
387            }
388            NodeEvent::Leave(RefNode::NounconnectedDriveCompilerDirective(_)) => {
389                skip_whitespace = false;
390            }
391            NodeEvent::Enter(RefNode::CelldefineDriveCompilerDirective(x)) => {
392                let locate: Locate = x.try_into().unwrap();
393                let range = Range::new(locate.offset, locate.offset + locate.len);
394                ret.push(locate.str(&s), Some((path.as_ref(), range)));
395                skip_whitespace = true;
396            }
397            NodeEvent::Leave(RefNode::CelldefineDriveCompilerDirective(_)) => {
398                skip_whitespace = false;
399            }
400            NodeEvent::Enter(RefNode::EndcelldefineDriveCompilerDirective(x)) => {
401                let locate: Locate = x.try_into().unwrap();
402                let range = Range::new(locate.offset, locate.offset + locate.len);
403                ret.push(locate.str(&s), Some((path.as_ref(), range)));
404                skip_whitespace = true;
405            }
406            NodeEvent::Leave(RefNode::EndcelldefineDriveCompilerDirective(_)) => {
407                skip_whitespace = false;
408            }
409            NodeEvent::Enter(RefNode::Pragma(x)) => {
410                let locate: Locate = x.try_into().unwrap();
411                let range = Range::new(locate.offset, locate.offset + locate.len);
412                ret.push(locate.str(&s), Some((path.as_ref(), range)));
413                skip_whitespace = true;
414            }
415            NodeEvent::Leave(RefNode::Pragma(_)) => {
416                skip_whitespace = false;
417            }
418            NodeEvent::Enter(RefNode::LineCompilerDirective(x)) => {
419                let locate: Locate = x.try_into().unwrap();
420                let range = Range::new(locate.offset, locate.offset + locate.len);
421                ret.push(locate.str(&s), Some((path.as_ref(), range)));
422                skip_whitespace = true;
423            }
424            NodeEvent::Leave(RefNode::LineCompilerDirective(_)) => {
425                skip_whitespace = false;
426            }
427            NodeEvent::Enter(RefNode::KeywordsDirective(x)) => {
428                let locate: Locate = x.try_into().unwrap();
429                let range = Range::new(locate.offset, locate.offset + locate.len);
430                ret.push(locate.str(&s), Some((path.as_ref(), range)));
431                skip_whitespace = true;
432            }
433            NodeEvent::Leave(RefNode::KeywordsDirective(_)) => {
434                skip_whitespace = false;
435            }
436            NodeEvent::Enter(RefNode::EndkeywordsDirective(x)) => {
437                let locate: Locate = x.try_into().unwrap();
438                let range = Range::new(locate.offset, locate.offset + locate.len);
439                ret.push(locate.str(&s), Some((path.as_ref(), range)));
440                skip_whitespace = true;
441            }
442            NodeEvent::Leave(RefNode::EndkeywordsDirective(_)) => {
443                skip_whitespace = false;
444            }
445            NodeEvent::Enter(RefNode::UndefineCompilerDirective(x)) => {
446                let (_, _, ref name) = x.nodes;
447                let id = identifier((&name.nodes.0).into(), &s).unwrap();
448                defines.remove(&id);
449
450                let locate: Locate = x.try_into().unwrap();
451                let range = Range::new(locate.offset, locate.offset + locate.len);
452                ret.push(locate.str(&s), Some((path.as_ref(), range)));
453                skip_whitespace = true;
454            }
455            NodeEvent::Leave(RefNode::UndefineCompilerDirective(_)) => {
456                skip_whitespace = false;
457            }
458            NodeEvent::Enter(RefNode::UndefineallCompilerDirective(x)) => {
459                defines.clear();
460
461                let locate: Locate = x.try_into().unwrap();
462                let range = Range::new(locate.offset, locate.offset + locate.len);
463                ret.push(locate.str(&s), Some((path.as_ref(), range)));
464                skip_whitespace = true;
465            }
466            NodeEvent::Leave(RefNode::UndefineallCompilerDirective(_)) => {
467                skip_whitespace = false;
468            }
469            NodeEvent::Enter(RefNode::IfdefDirective(x)) => {
470                let (_, ref keyword, ref ifid, ref ifbody, ref elsif, ref elsebody, _, _) = x.nodes;
471                skip_nodes.push(keyword.into());
472                skip_nodes.push(ifid.into());
473
474                let ifid = identifier(ifid.into(), &s).unwrap();
475                let mut hit = false;
476                if defines.contains_key(&ifid) || is_predefined_text_macro(&ifid) {
477                    hit = true;
478                } else {
479                    skip_nodes.push(ifbody.into());
480                }
481
482                for x in elsif {
483                    let (_, ref keyword, ref elsifid, ref elsifbody) = x;
484                    skip_nodes.push(keyword.into());
485                    skip_nodes.push(elsifid.into());
486
487                    let elsifid = identifier(elsifid.into(), &s).unwrap();
488                    if hit {
489                        skip_nodes.push(elsifbody.into());
490                    } else if defines.contains_key(&elsifid) || is_predefined_text_macro(&ifid) {
491                        hit = true;
492                    } else {
493                        skip_nodes.push(elsifbody.into());
494                    }
495                }
496
497                if let Some(elsebody) = elsebody {
498                    let (_, ref keyword, ref elsebody) = elsebody;
499                    skip_nodes.push(keyword.into());
500                    if hit {
501                        skip_nodes.push(elsebody.into());
502                    }
503                }
504            }
505            NodeEvent::Enter(RefNode::WhiteSpace(x)) if !skip_whitespace && !strip_comments => {
506                if let WhiteSpace::Space(_) = x {
507                    let locate: Locate = x.try_into().unwrap();
508                    let range = Range::new(locate.offset + locate.len, locate.offset + locate.len);
509                    ret.push(locate.str(&s), Some((path.as_ref(), range)));
510                }
511            }
512            NodeEvent::Enter(RefNode::Comment(x)) if !strip_comments => {
513                let locate: Locate = x.try_into().unwrap();
514                let range = Range::new(locate.offset, locate.offset + locate.len);
515                ret.push(locate.str(&s), Some((path.as_ref(), range)));
516            }
517            NodeEvent::Enter(RefNode::IfndefDirective(x)) => {
518                let (_, ref keyword, ref ifid, ref ifbody, ref elsif, ref elsebody, _, _) = x.nodes;
519                skip_nodes.push(keyword.into());
520                skip_nodes.push(ifid.into());
521
522                let ifid = identifier(ifid.into(), &s).unwrap();
523                let mut hit = false;
524                if !defines.contains_key(&ifid) && !is_predefined_text_macro(&ifid) {
525                    hit = true;
526                } else {
527                    skip_nodes.push(ifbody.into());
528                }
529
530                for x in elsif {
531                    let (_, ref keyword, ref elsifid, ref elsifbody) = x;
532                    skip_nodes.push(keyword.into());
533                    skip_nodes.push(elsifid.into());
534
535                    let elsifid = identifier(elsifid.into(), &s).unwrap();
536                    if hit {
537                        skip_nodes.push(elsifbody.into());
538                    } else if defines.contains_key(&elsifid) || is_predefined_text_macro(&ifid) {
539                        hit = true;
540                    } else {
541                        skip_nodes.push(elsifbody.into());
542                    }
543                }
544
545                if let Some(elsebody) = elsebody {
546                    let (_, ref keyword, ref elsebody) = elsebody;
547                    skip_nodes.push(keyword.into());
548                    if hit {
549                        skip_nodes.push(elsebody.into());
550                    }
551                }
552            }
553            NodeEvent::Enter(RefNode::TextMacroDefinition(x)) => {
554                skip_nodes.push(x.into());
555                skip = true;
556
557                let (_, _, ref proto, ref text) = x.nodes;
558                let (ref name, ref args) = proto.nodes;
559                let id = identifier(name.into(), &s).unwrap();
560
561                if !is_predefined_text_macro(id.as_str()) {
562                    let mut define_args = Vec::new();
563                    if let Some(args) = args {
564                        let (_, ref args, _) = args.nodes;
565                        let (ref args,) = args.nodes;
566                        for arg in args.contents() {
567                            let (ref arg, ref default) = arg.nodes;
568                            let (ref arg, _) = arg.nodes;
569                            let arg = String::from(arg.str(&s));
570
571                            let default = if let Some((_, x)) = default {
572                                let x: Locate = x.try_into().unwrap();
573                                let x = String::from(x.str(&s));
574                                Some(x)
575                            } else {
576                                None
577                            };
578
579                            define_args.push((arg, default));
580                        }
581                    }
582
583                    let define_text = if let Some(text) = text {
584                        let text: Locate = text.try_into().unwrap();
585                        let range = Range::new(text.offset, text.offset + text.len);
586                        let text = String::from(text.str(&s));
587                        Some(DefineText {
588                            text,
589                            origin: Some((PathBuf::from(path.as_ref()), range)),
590                        })
591                    } else {
592                        None
593                    };
594
595                    let define = Define {
596                        identifier: id.clone(),
597                        arguments: define_args,
598                        text: define_text,
599                    };
600
601                    defines.insert(id, Some(define));
602                }
603
604                // Keep TextMacroDefinition after preprocess_inner().
605                let locate: Locate = x.try_into().unwrap();
606                let range = Range::new(locate.offset, locate.offset + locate.len);
607                ret.push(locate.str(&s), Some((path.as_ref(), range)));
608            }
609            NodeEvent::Enter(RefNode::IncludeCompilerDirective(x)) if !ignore_include => {
610                skip_nodes.push(x.into());
611                skip = true;
612
613                let locate: Locate = x.try_into().unwrap();
614                last_include_line = Some(locate.line);
615
616                // IEEE1800-2017 Clause 22.4, page 675
617                // Only white space or a comment may appear on the same line as
618                // the `include compiler directive.
619                if let Some(last_item_line) = last_item_line {
620                    if last_item_line == locate.line {
621                        return Err(Error::IncludeLine);
622                    }
623                }
624
625                let mut path = match x {
626                    IncludeCompilerDirective::DoubleQuote(x) => {
627                        let (_, ref keyword, ref literal) = x.nodes;
628                        skip_nodes.push(keyword.into());
629
630                        let (locate, _) = literal.nodes;
631                        let p = locate.str(&s).trim_matches('"');
632                        PathBuf::from(p)
633                    }
634                    IncludeCompilerDirective::AngleBracket(x) => {
635                        let (_, ref keyword, ref literal) = x.nodes;
636                        skip_nodes.push(keyword.into());
637
638                        let (locate, _) = literal.nodes;
639                        let p = locate.str(&s).trim_start_matches('<').trim_end_matches('>');
640                        PathBuf::from(p)
641                    }
642                    IncludeCompilerDirective::TextMacroUsage(x) => {
643                        let (_, ref keyword, ref x) = x.nodes;
644                        skip_nodes.push(keyword.into());
645                        skip_nodes.push(x.into());
646
647                        if let Some((p, _, _)) = resolve_text_macro_usage(
648                            x,
649                            s,
650                            path.as_ref(),
651                            &defines,
652                            include_paths,
653                            strip_comments,
654                            resolve_depth + 1,
655                        )? {
656                            let p = p.trim().trim_matches('"');
657                            PathBuf::from(p)
658                        } else {
659                            PathBuf::from("")
660                        }
661                    }
662                };
663
664                // IEEE1800-2017 Clause 22.4, page 675
665                // The filename can be enclosed in either quotes or angle brackets,
666                // which affects how a tool searches for the file, as follows:
667                // - When the filename is enclosed in double quotes ("filename"), for
668                //   a relative path the compiler’s current working directory, and
669                //   optionally user-specified locations are searched.
670                // - When the filename is enclosed in angle brackets (<filename>), then
671                //   only an implementationdependent location containing files defined
672                //   by the language standard is searched. Relative path names are
673                //   interpreted relative to that location
674                //
675                // In this implementation, filenames enclosed in angle brackets are
676                // treated equivalently to those enclosed in double quotes.
677                if path.is_relative() && !path.exists() {
678                    for include_path in include_paths {
679                        let new_path = include_path.as_ref().join(&path);
680                        if new_path.exists() {
681                            path = new_path;
682                            break;
683                        }
684                    }
685                }
686
687                let (include, new_defines) =
688                    preprocess_inner(
689                        path,
690                        &defines,
691                        include_paths,
692                        strip_comments,
693                        false, // ignore_include
694                        include_depth + 1).map_err(
695                        |x| Error::Include {
696                            source: Box::new(x),
697                        },
698                    )?;
699                defines = new_defines;
700                ret.merge(include);
701            }
702            NodeEvent::Enter(RefNode::TextMacroUsage(x)) => {
703                skip_nodes.push(x.into());
704                skip = true;
705
706                if let Some((text, origin, new_defines)) = resolve_text_macro_usage(
707                    x,
708                    s,
709                    path.as_ref(),
710                    &defines,
711                    include_paths,
712                    strip_comments,
713                    resolve_depth + 1,
714                )? {
715                    ret.push(&text, origin);
716                    defines = new_defines;
717                }
718
719                // Push the trailing whitespace attached to either
720                // TextMacroIdentifier or Option<Paren<ListOfActualArguments>>.
721                let (ref _symbol, ref id, ref args) = x.nodes;
722                match args {
723                    Some(p) => {
724                        // Arguments given to macro in parentheses.
725                        let (ref _opening, ref _args, ref closing) = p.nodes;
726                        for x in closing {
727                            match x {
728                                RefNode::WhiteSpace(x) => {
729                                    let locate: Locate = x.try_into().unwrap();
730                                    let range = Range::new(locate.offset, locate.offset + locate.len);
731                                    ret.push(locate.str(&s), Some((path.as_ref(), range)));
732                                }
733                                _ => {
734                                }
735                            }
736                        }
737                    }
738                    None => {
739                        // No arguments given to macro.
740                        for x in id {
741                            match x {
742                                RefNode::WhiteSpace(x) => {
743                                    let locate: Locate = x.try_into().unwrap();
744                                    let range = Range::new(locate.offset, locate.offset + locate.len);
745                                    ret.push(locate.str(&s), Some((path.as_ref(), range)));
746                                }
747                                _ => {}
748                            }
749                        }
750                    }
751                }
752            }
753            NodeEvent::Enter(RefNode::PositionCompilerDirective(x)) => {
754                skip_nodes.push(x.into());
755                skip = true;
756
757                let (_, ref x) = x.nodes;
758                let locate: Locate = x.try_into().unwrap();
759                let x = locate.str(s);
760                if x.starts_with("__FILE__") {
761                    ret.push::<PathBuf>(
762                        &x.replace(
763                            "__FILE__",
764                            &format!("\"{}\"", path.as_ref().to_string_lossy()),
765                        ),
766                        None,
767                    );
768                } else if x.starts_with("__LINE__") {
769                    ret.push::<PathBuf>(&x.replace("__LINE__", &format!("{}", locate.line)), None);
770                }
771            }
772            _ => (),
773        }
774    }
775
776    Ok((ret, defines))
777}
778
779fn identifier(node: RefNode, s: &str) -> Option<String> {
780    for x in node {
781        match x {
782            RefNode::SimpleIdentifier(x) => {
783                let x: Locate = x.nodes.0.try_into().unwrap();
784                return Some(String::from(x.str(s)));
785            }
786            RefNode::EscapedIdentifier(x) => {
787                let x: Locate = x.nodes.0.try_into().unwrap();
788                let x = x.str(s);
789                let x = &x[1..]; // remove \
790                return Some(String::from(x));
791            }
792            _ => (),
793        }
794    }
795    None
796}
797
798fn get_str(node: RefNode, s: &str) -> String {
799    let mut ret = String::from("");
800    for x in node {
801        match x {
802            RefNode::Locate(x) => {
803                ret.push_str(x.str(s));
804            }
805            _ => (),
806        }
807    }
808    ret
809}
810
811fn is_predefined_text_macro(s: &str) -> bool {
812    match s {
813        "__LINE__" | "__FILE__" => {
814            true
815        }
816        _ => {
817            false
818        }
819    }
820}
821
822fn split_text(s: &str) -> Vec<String> {
823    let mut is_string = false;
824    let mut is_ident = false;
825    let mut is_ident_prev;
826    let mut x = String::from("");
827    let mut ret = vec![];
828
829    // IEEE1800-2017 Clause 22.5.1, page 676
830    // If a one-line comment (that is, a comment specified with the
831    // characters //) is included in the text, then the comment shall not
832    // become part of the substituted text.
833    let mut is_comment = false;
834
835    // IEEE1800-2017 Clause 22.5.1, page 680
836    // An `" overrides the usual lexical meaning of " and indicates that the
837    // expansion shall include the quotation mark, substitution of actual
838    // arguments, and expansions of embedded macros.
839    // This allows string literals to be constructed from macro arguments.
840    let mut is_backquote_prev = false;
841
842    let mut is_leading_whitespace = true;
843    let mut is_backslash_prev = false;
844
845    let mut iter = s.chars().peekable();
846    while let Some(c) = iter.next() {
847
848        // IEEE1800-2017 Clause 22.5.1, page 676, Syntax 22-2.
849        // Ignore whitespace immediately after text_macro_name.
850        if is_leading_whitespace {
851            if c != '\\' && !c.is_ascii_whitespace() {
852                // Non-whitespace character, move onto main loop.
853                is_leading_whitespace = false;
854            } else if is_backslash_prev && c == '\n' {
855                // Drop the \n from leading continuation, then move onto main loop.
856                is_leading_whitespace = false;
857                continue;
858            } else {
859                // Still in leading whitespace or possible continuation.
860                // Detect possible continuation, then try next character.
861                is_backslash_prev = c == '\\';
862                continue;
863            }
864        }
865
866        is_ident_prev = is_ident;
867        is_ident = c.is_ascii_alphanumeric() | (c == '_');
868
869        if c == '\n' && is_comment {
870            is_comment = false;
871            x.push(c);
872        } else if is_comment {
873            continue;
874        } else if c == '"' && is_backquote_prev {
875            x.push(c);
876            ret.push(x);
877            x = String::from("");
878        } else if c == '"' && !is_string {
879            ret.push(x);
880            x = String::from("");
881            x.push(c);
882            is_string = true;
883        } else if c == '"' && is_string {
884            x.push(c);
885            ret.push(x);
886            x = String::from("");
887            is_string = false;
888        } else if c == '/' && iter.peek() == Some(&'/') && !is_string {
889            is_comment = true;
890        } else if !is_string {
891            if is_ident != is_ident_prev {
892                ret.push(x);
893                x = String::from("");
894            }
895            x.push(c);
896        } else {
897            x.push(c);
898        }
899
900        is_backquote_prev = c == '`';
901    }
902    ret.push(x);
903    ret
904}
905
906fn resolve_text_macro_usage<T: AsRef<Path>, U: AsRef<Path>>(
907    x: &TextMacroUsage,
908    s: &str,
909    path: T,
910    defines: &Defines,
911    include_paths: &[U],
912    strip_comments: bool,
913    resolve_depth: usize,
914) -> Result<Option<(String, Option<(PathBuf, Range)>, Defines)>, Error> {
915    let (_, ref name, ref args) = x.nodes;
916    let id = identifier((&name.nodes.0).into(), &s).unwrap();
917
918    if resolve_depth > RECURSIVE_LIMIT {
919        return Err(Error::ExceedRecursiveLimit);
920    }
921
922    let mut args_str = String::from("");
923    let mut actual_args = Vec::new();
924    let no_args = args.is_none();
925    if let Some(args) = args {
926        args_str.push_str(&get_str((&args.nodes.0).into(), s));
927        args_str.push_str(&get_str((&args.nodes.1).into(), s));
928        args_str.push_str(&get_str((&args.nodes.2).into(), s));
929
930        let (_, ref args, _) = args.nodes;
931        let (ref args,) = args.nodes;
932        for arg in args.contents() {
933            if let Some(arg) = arg {
934                let (ref arg,) = arg.nodes;
935                let arg = arg.str(&s).trim_end();
936                actual_args.push(Some(arg));
937            } else {
938                actual_args.push(None);
939            }
940        }
941    }
942
943    let define = defines.get(&id);
944    if let Some(Some(define)) = define {
945        let mut arg_map = HashMap::new();
946
947        if !define.arguments.is_empty() && no_args {
948            return Err(Error::DefineNoArgs(define.identifier.clone()));
949        }
950
951        for (i, (arg, default)) in define.arguments.iter().enumerate() {
952            let value = match actual_args.get(i) {
953                Some(Some(actual_arg)) => *actual_arg,
954                Some(None) => {
955                    if let Some(default) = default {
956                        default
957                    } else {
958                        ""
959                    }
960                }
961                None => {
962                    if let Some(default) = default {
963                        default
964                    } else {
965                        return Err(Error::DefineArgNotFound(String::from(arg)));
966                    }
967                }
968            };
969            arg_map.insert(String::from(arg), value);
970        }
971
972        // restore () for textmacro without arguments
973        let paren = if define.arguments.is_empty() {
974            Some(args_str)
975        } else {
976            None
977        };
978
979        if let Some(ref text) = define.text {
980            let mut replaced = String::from("");
981            for text in split_text(&text.text) {
982                if let Some(value) = arg_map.get(&text) {
983                    replaced.push_str(*value);
984                } else {
985                    replaced.push_str(
986                        &text
987                            .replace("``", "")          // Argument substitution.
988                            .replace("`\\`\"", "\\\"")  // Escaped backslash.
989                            .replace("`\"", "\"")       // Escaped quote.
990                            .replace("\\\n", "\n")      // Line continuation (Unix).
991                            .replace("\\\r\n", "\r\n")  // Line continuation (Windows).
992                            .replace("\\\r", "\r"),     // Line continuation (old Mac).
993                    );
994                }
995            }
996
997            if let Some(paren) = paren {
998                replaced.push_str(&paren);
999            }
1000
1001            let (replaced, new_defines) = preprocess_str(
1002                &replaced,
1003                path.as_ref(),
1004                &defines,
1005                include_paths,
1006                false,
1007                strip_comments,
1008                resolve_depth,
1009                0, // include_depth
1010            )?;
1011            Ok(Some((
1012                String::from(replaced.text()),
1013                text.origin.clone(),
1014                new_defines,
1015            )))
1016        } else {
1017            Ok(None)
1018        }
1019    } else if define.is_some() {
1020        Ok(None)
1021    } else {
1022        Err(Error::DefineNotFound(id))
1023    }
1024}
1025
1026#[cfg(test)]
1027mod tests {
1028    use super::*;
1029    use std::env;
1030
1031    fn testfile_path(s: &str) -> String {
1032        format!(
1033            "{}/testcases/{}",
1034            env::var("CARGO_MANIFEST_DIR").unwrap(),
1035            s
1036        )
1037    }
1038
1039    fn testfile_contents(s: &str) -> String {
1040        let path: String = testfile_path(s);
1041
1042        let file = File::open(path).unwrap();
1043        let mut buf_reader = BufReader::new(file);
1044        let mut contents = String::new();
1045        buf_reader.read_to_string(&mut contents).unwrap();
1046
1047        contents
1048    }
1049
1050    // Most tests are called with the same arguments, so this is a convenience.
1051    fn preprocess_usualargs(s: &str) -> Result<(PreprocessedText, Defines), Error> {
1052        let include_paths = [testfile_path("")];
1053        preprocess(
1054            testfile_path(s),   // path
1055            &HashMap::new(),    // pre_defines
1056            &include_paths,     // include_paths
1057            false,              // strip_comments
1058            false,              // ignore_include
1059        )
1060    }
1061
1062    #[test]
1063    fn escaped_identifier() { // {{{
1064        let (ret, _) = preprocess_usualargs("escaped_identifier.sv").unwrap();
1065        assert_eq!(
1066            ret.text(),
1067            testfile_contents("expected/escaped_identifier.sv")
1068        );
1069    } // }}}
1070
1071    #[test]
1072    #[allow(non_snake_case)]
1073    fn err_DefineNoArgs() { // {{{
1074        match preprocess_usualargs("err_DefineNoArgs.sv").unwrap_err() {
1075            Error::DefineNoArgs(identifier) => {
1076                assert_eq!(
1077                    identifier,
1078                    String::from("A")
1079                );
1080            }
1081            _ => {
1082                panic!("Error::DefineNoArgs not raised.");
1083            }
1084        };
1085    } // }}}
1086
1087    #[test]
1088    #[allow(non_snake_case)]
1089    fn err_DefineNotFound() { // {{{
1090        match preprocess_usualargs("err_DefineNotFound.sv").unwrap_err() {
1091            Error::DefineNotFound(identifier) => {
1092                assert_eq!(
1093                    identifier,
1094                    String::from("A")
1095                );
1096            }
1097            _ => {
1098                panic!("Error::DefineNotFound not raised.");
1099            }
1100        };
1101    } // }}}
1102
1103    #[test]
1104    #[allow(non_snake_case)]
1105    fn err_DefineArgNotFound() { // {{{
1106        match preprocess_usualargs("err_DefineArgNotFound.sv").unwrap_err() {
1107            Error::DefineArgNotFound(identifier) => {
1108                assert_eq!(
1109                    identifier,
1110                    String::from("c")
1111                );
1112            }
1113            _ => {
1114                panic!("Error::DefineArgNotFound not raised.");
1115            }
1116        };
1117    } // }}}
1118
1119    #[test]
1120    #[allow(non_snake_case)]
1121    fn err_ReadUtf8() { // {{{
1122        match preprocess_usualargs("err_ReadUtf8.sv").unwrap_err() {
1123            Error::ReadUtf8(path) => {
1124                assert_eq!(
1125                    path,
1126                    PathBuf::from(format!(
1127                        "{}/testcases/err_ReadUtf8.sv",
1128                        env::var("CARGO_MANIFEST_DIR").unwrap(),
1129                    ))
1130                );
1131            }
1132            _ => {
1133                panic!("Error::ReadUtf8 not raised.");
1134            }
1135        };
1136    } // }}}
1137
1138    #[test]
1139    #[allow(non_snake_case)]
1140    fn IEEE18002017_keywords_if2_13642005() { // {{{
1141        let (ret, _) = preprocess_usualargs("IEEE18002017_keywords_if2_13642005.sv").unwrap();
1142        assert_eq!(
1143            ret.text(),
1144            testfile_contents("expected/IEEE18002017_keywords_if2_13642005.sv")
1145        );
1146    } // }}}
1147
1148    #[test]
1149    #[allow(non_snake_case)]
1150    fn IEEE18002017_keywords_m2_13642001() { // {{{
1151        let (ret, _) = preprocess_usualargs("IEEE18002017_keywords_m2_13642001.sv").unwrap();
1152        assert_eq!(
1153            ret.text(),
1154            testfile_contents("expected/IEEE18002017_keywords_m2_13642001.sv")
1155        );
1156    } // }}}
1157
1158    #[test]
1159    #[allow(non_snake_case)]
1160    fn IEEE18002017_keywords_m2_18002005() { // {{{
1161        let (ret, _) = preprocess_usualargs("IEEE18002017_keywords_m2_18002005.sv").unwrap();
1162        assert_eq!(
1163            ret.text(),
1164            testfile_contents("expected/IEEE18002017_keywords_m2_18002005.sv")
1165        );
1166    } // }}}
1167
1168    #[test]
1169    #[allow(non_snake_case)]
1170    fn IEEE18002017_macro_argument_expansion() { // {{{
1171        let (ret, _) = preprocess_usualargs("IEEE18002017_macro_argument_expansion.sv").unwrap();
1172        assert_eq!(
1173            ret.text(),
1174            testfile_contents("expected/IEEE18002017_macro_argument_expansion.sv")
1175        );
1176    } // }}}
1177
1178    #[test]
1179    #[allow(non_snake_case)]
1180    fn IEEE18002017_macro_delimit_tokens() { // {{{
1181        let (ret, _) = preprocess_usualargs("IEEE18002017_macro_delimit_tokens.sv").unwrap();
1182        assert_eq!(
1183            ret.text(),
1184            testfile_contents("expected/IEEE18002017_macro_delimit_tokens.sv")
1185        );
1186    } // }}}
1187
1188    #[test]
1189    #[allow(non_snake_case)]
1190    fn IEEE18002017_macro_mix_quotes() { // {{{
1191        let (ret, _) = preprocess_usualargs("IEEE18002017_macro_mix_quotes.sv").unwrap();
1192        assert_eq!(
1193            ret.text(),
1194            testfile_contents("expected/IEEE18002017_macro_mix_quotes.sv")
1195        );
1196    } // }}}
1197
1198    #[test]
1199    #[allow(non_snake_case)]
1200    fn IEEE18002017_macro_noexpand_string() { // {{{
1201        let (ret, _) = preprocess_usualargs("IEEE18002017_macro_noexpand_string.sv").unwrap();
1202        assert_eq!(
1203            ret.text(),
1204            testfile_contents("expected/IEEE18002017_macro_noexpand_string.sv")
1205        );
1206    } // }}}
1207
1208    #[test]
1209    #[allow(non_snake_case)]
1210    fn IEEE18002017_macro_with_defaults() { // {{{
1211        let (ret, _) = preprocess_usualargs("IEEE18002017_macro_with_defaults.sv").unwrap();
1212        assert_eq!(
1213            ret.text(),
1214            testfile_contents("expected/IEEE18002017_macro_with_defaults.sv")
1215        );
1216    } // }}}
1217
1218    #[test]
1219    #[allow(non_snake_case)]
1220    fn IEEE18002017_macro_without_defaults() { // {{{
1221        let (ret, _) = preprocess_usualargs("IEEE18002017_macro_without_defaults.sv").unwrap();
1222        assert_eq!(
1223            ret.text(),
1224            testfile_contents("expected/IEEE18002017_macro_without_defaults.sv")
1225        );
1226    } // }}}
1227
1228    #[test]
1229    fn celldefine() { // {{{
1230        let (ret, _) = preprocess_usualargs("celldefine.sv").unwrap();
1231        assert_eq!(
1232            ret.text(),
1233            testfile_contents("celldefine.sv")
1234        );
1235    } // }}}
1236
1237    #[test]
1238    fn coverage_constants() { // {{{
1239        let (ret, _) = preprocess_usualargs("coverage_constants.sv").unwrap();
1240        assert_eq!(
1241            ret.text(),
1242            testfile_contents("expected/coverage_constants.sv")
1243        );
1244    } // }}}
1245
1246    #[test]
1247    fn default_nettype() { // {{{
1248        let (ret, _) = preprocess_usualargs("default_nettype.sv").unwrap();
1249        assert_eq!(
1250            ret.text(),
1251            testfile_contents("default_nettype.sv")
1252        );
1253    } // }}}
1254
1255    #[test]
1256    fn ifdef_nested() { // {{{
1257        let (ret, _) = preprocess_usualargs("ifdef_nested.sv").unwrap();
1258        assert_eq!(
1259            ret.text(),
1260            testfile_contents("expected/ifdef_nested.sv")
1261        );
1262    } // }}}
1263
1264    #[test]
1265    fn ifdef_predefined() { // {{{
1266        let mut defines = HashMap::new();
1267        defines.insert(String::from("behavioral"), None);
1268        let (ret, _) = preprocess(
1269            testfile_path("ifdef_predefined.sv"),
1270            &defines,
1271            &[] as &[String],
1272            false, // strip_comments
1273            false, // ignore_include
1274        )
1275        .unwrap();
1276        assert_eq!(
1277            ret.text(),
1278            testfile_contents("expected/ifdef_predefined.sv")
1279        )
1280    } // }}}
1281
1282    #[test]
1283    fn ifdef_undefined() { // {{{
1284        let (ret, _) = preprocess_usualargs("ifdef_undefined.sv").unwrap();
1285        assert_eq!(
1286            ret.text(),
1287            testfile_contents("expected/ifdef_undefined.sv")
1288        );
1289        assert_eq!(
1290            ret.origin(10).unwrap().0,
1291            &PathBuf::from(testfile_path("ifdef_undefined.sv"))
1292        );
1293        assert_eq!(ret.origin(10).unwrap().1, 10);
1294        assert_eq!(ret.origin(50).unwrap().1, 98);
1295        assert_eq!(ret.origin(70).unwrap().1, 124);
1296    } // }}}
1297
1298    #[test]
1299    fn ifndef_undefined() { // {{{
1300        let (ret, _) = preprocess_usualargs("ifndef_undefined.sv").unwrap();
1301        assert_eq!(
1302            ret.text(),
1303            testfile_contents("expected/ifndef_undefined.sv")
1304        );
1305    } // }}}
1306
1307    #[test]
1308    fn include_ignore() { // {{{
1309        let include_paths = [testfile_path("")];
1310        let (ret, _) = preprocess(
1311            testfile_path("include_ignore.sv"),
1312            &HashMap::new(),
1313            &include_paths,
1314            false, // strip_comments
1315            true, // ignore_include
1316        )
1317        .unwrap();
1318        assert_eq!(
1319            ret.text(),
1320            testfile_contents("expected/include_ignore.sv")
1321        );
1322    } // }}}
1323
1324    #[test]
1325    fn include_noindent() { // {{{
1326        let (ret, _) = preprocess_usualargs("include_noindent.sv").unwrap();
1327        assert_eq!(
1328            ret.text(),
1329            testfile_contents("expected/include_noindent.sv")
1330        );
1331
1332        // 11th char of returned text is '_' in the module identifier
1333        // "and_op", and originates from the parent file.
1334        // Characters are zero-indexed.
1335        let n = 10;
1336        assert_eq!(
1337            ret.origin(n).unwrap(),
1338            (&PathBuf::from(testfile_path("include_noindent.sv")), n)
1339        );
1340        assert_eq!(ret.text().chars().nth(n).unwrap(), '_');
1341
1342        // 51st char of returned text is 'd' in the primitive identifier
1343        // "and", and originates from the child file at character index 72.
1344        let n = 50;
1345        assert_eq!(
1346            ret.origin(n).unwrap(),
1347            (&PathBuf::from(testfile_path("included.svh")), 73)
1348        );
1349        assert_eq!(ret.text().chars().nth(n).unwrap(), 'd');
1350
1351        // 71st char of returned text is 'o' in the keword "endmodule", and
1352        // originates from the parent file.
1353        let n = 70;
1354        assert_eq!(
1355            ret.origin(n).unwrap(),
1356            (&PathBuf::from(testfile_path("include_noindent.sv")), 53)
1357        );
1358        assert_eq!(ret.text().chars().nth(n).unwrap(), 'o');
1359    } // }}}
1360
1361    #[test]
1362    fn include_quoted_a() { // {{{
1363        let ret = preprocess_usualargs("include_quoted_a.sv");
1364        assert_eq!(format!("{:?}", ret), "Err(Include { source: File { source: Os { code: 2, kind: NotFound, message: \"No such file or directory\" }, path: \"`PATH\" } })");
1365    } // }}}
1366
1367    #[test]
1368    fn include_quoted_b() { // {{{
1369        let ret = preprocess_usualargs("include_quoted_b.sv");
1370        assert_eq!(format!("{:?}", ret), "Err(Include { source: File { source: Os { code: 2, kind: NotFound, message: \"No such file or directory\" }, path: \"`PATH\" } })");
1371    } // }}}
1372
1373    #[test]
1374    fn include_quoted_c() { // {{{
1375        let (ret, _) = preprocess_usualargs("include_quoted_c.sv").unwrap();
1376        assert_eq!(
1377            ret.text(),
1378            testfile_contents("expected/include_quoted_c.sv")
1379        );
1380    } // }}}
1381
1382    #[test]
1383    fn include_quoted_d() { // {{{
1384        let (ret, _) = preprocess_usualargs("include_quoted_d.sv").unwrap();
1385        assert_eq!(
1386            ret.text(),
1387            testfile_contents("expected/include_quoted_d.sv")
1388        );
1389    } // }}}
1390
1391    #[test]
1392    fn include_recursive() { // {{{
1393        let ret = preprocess_usualargs("include_recursive.svh");
1394        let expected = format!(
1395            "Err({}ExceedRecursiveLimit{})",
1396            "Include { source: ".repeat(RECURSIVE_LIMIT+1),
1397            " }".repeat(RECURSIVE_LIMIT+1),
1398        );
1399        assert_eq!(format!("{:?}", ret), expected);
1400    } // }}}
1401
1402    #[test]
1403    fn include_sameline_comment() { // {{{
1404        let (ret, _) = preprocess_usualargs("include_sameline_comment.sv").unwrap();
1405        assert_eq!(
1406            ret.text(),
1407            testfile_contents("expected/include_sameline_comment.sv")
1408        );
1409        assert_eq!(
1410            ret.origin(10).unwrap().0,
1411            &PathBuf::from(testfile_path("include_sameline_comment.sv"))
1412        );
1413        assert_eq!(ret.origin(10).unwrap().1, 10);
1414        assert_eq!(
1415            ret.origin(50).unwrap().0,
1416            &PathBuf::from(testfile_path("included.svh"))
1417        );
1418        assert_eq!(ret.origin(50).unwrap().1, 73);
1419        assert_eq!(
1420            ret.origin(70).unwrap().0,
1421            &PathBuf::from(testfile_path("include_sameline_comment.sv"))
1422        );
1423        assert_eq!(ret.origin(70).unwrap().1, 53);
1424    } // }}}
1425
1426    #[test]
1427    fn include_sameline_include() { // {{{
1428        let ret = preprocess_usualargs("include_sameline_include.sv");
1429        assert_eq!(format!("{:?}", ret), "Err(IncludeLine)");
1430    } // }}}
1431
1432    #[test]
1433    fn include_sameline_keyword() { // {{{
1434        let ret = preprocess_usualargs("include_sameline_keyword.sv");
1435        assert_eq!(format!("{:?}", ret), "Err(IncludeLine)");
1436    } // }}}
1437
1438    #[test]
1439    fn include_withindent() { // {{{
1440        let (ret, _) = preprocess_usualargs("include_withindent.sv").unwrap();
1441        assert_eq!(
1442            ret.text(),
1443            testfile_contents("expected/include_withindent.sv")
1444        );
1445
1446        // 11th char of returned text is '_' in the module identifier
1447        // "and_op", and originates from the parent file.
1448        // Characters are zero-indexed.
1449        let n = 10;
1450        assert_eq!(
1451            ret.origin(n).unwrap(),
1452            (&PathBuf::from(testfile_path("include_withindent.sv")), n)
1453        );
1454        assert_eq!(ret.text().chars().nth(n).unwrap(), '_');
1455
1456        // 59th char of returned text is 'n' in the primitive identifier
1457        // "and", and originates from the child file at character index 72.
1458        let n = 58;
1459        assert_eq!(
1460            ret.origin(n).unwrap(),
1461            (&PathBuf::from(testfile_path("included.svh")), 72)
1462        );
1463        assert_eq!(ret.text().chars().nth(n).unwrap(), 'n');
1464
1465        // 80th char of returned text is 'o' in the keyword "endmodule", and
1466        // originates from the parent file.
1467        let n = 79;
1468        assert_eq!(
1469            ret.origin(n).unwrap(),
1470            (&PathBuf::from(testfile_path("include_withindent.sv")), 62)
1471        );
1472        assert_eq!(ret.text().chars().nth(n).unwrap(), 'o');
1473    } // }}}
1474
1475    #[test]
1476    fn keywords() { // {{{
1477        let (ret, _) = preprocess_usualargs("keywords.sv").unwrap();
1478        assert_eq!(
1479            ret.text(),
1480            testfile_contents("keywords.sv")
1481        );
1482    } // }}}
1483
1484    #[test]
1485    fn line() { // {{{
1486        let (ret, _) = preprocess_usualargs("line.sv").unwrap();
1487        assert_eq!(
1488            ret.text(),
1489            testfile_contents("line.sv")
1490        );
1491    } // }}}
1492
1493    #[test]
1494    fn macro_arguments() { // {{{
1495        let (ret, _) = preprocess_usualargs("macro_arguments.sv").unwrap();
1496        assert_eq!(
1497            ret.text(),
1498            testfile_contents("expected/macro_arguments.sv")
1499        );
1500    } // }}}
1501
1502    #[test]
1503    fn macro_basic() { // {{{
1504        let (ret, _) = preprocess_usualargs("macro_basic.sv").unwrap();
1505        assert_eq!(
1506            ret.text(),
1507            testfile_contents("expected/macro_basic.sv")
1508        );
1509    } // }}}
1510
1511    #[test]
1512    fn macro_comment() { // {{{
1513        let (ret, _) = preprocess_usualargs("macro_comment.sv").unwrap();
1514        assert_eq!(
1515            ret.text(),
1516            testfile_contents("expected/macro_comment.sv")
1517        );
1518    } // }}}
1519
1520    #[test]
1521    #[ignore = "Exposes unfixed PP parser bug."]
1522    fn macro_comment_embedded() { // {{{
1523        let (ret, _) = preprocess_usualargs("macro_comment_embedded.sv").unwrap();
1524        assert_eq!(
1525            ret.text(),
1526            testfile_contents("expected/macro_comment_embedded.sv")
1527        );
1528    } // }}}
1529
1530    #[test]
1531    fn macro_delimiters() { // {{{
1532        let (ret, _) = preprocess_usualargs("macro_delimiters.sv").unwrap();
1533        assert_eq!(
1534            ret.text(),
1535            testfile_contents("expected/macro_delimiters.sv")
1536        );
1537    } // }}}
1538
1539    #[test]
1540    #[allow(non_snake_case)]
1541    fn macro_FILE() { // {{{
1542        let (ret, _) = preprocess_usualargs("macro_FILE.sv").unwrap();
1543        assert_eq!(
1544            ret.text(),
1545            testfile_contents("expected/macro_FILE.sv")
1546        );
1547    } // }}}
1548
1549    #[test]
1550    fn macro_identifier() { // {{{
1551        let (ret, _) = preprocess_usualargs("macro_identifier.sv").unwrap();
1552        assert_eq!(
1553            ret.text(),
1554            testfile_contents("expected/macro_identifier.sv")
1555        );
1556    } // }}}
1557
1558    #[test]
1559    #[allow(non_snake_case)]
1560    fn macro_LINE() { // {{{
1561        let (ret, _) = preprocess_usualargs("macro_LINE.sv").unwrap();
1562        assert_eq!(
1563            ret.text(),
1564            testfile_contents("expected/macro_LINE.sv")
1565        );
1566    } // }}}
1567
1568    #[test]
1569    fn macro_multiline_comment() { // {{{
1570        let (ret, _) = preprocess_usualargs("macro_multiline_comment.sv").unwrap();
1571        assert_eq!(
1572            ret.text(),
1573            testfile_contents("expected/macro_multiline_comment.sv")
1574        );
1575    } // }}}
1576
1577    #[test]
1578    fn macro_recursion_direct() { // {{{
1579        let ret = preprocess_usualargs("macro_recursion_direct.sv");
1580        assert_eq!(format!("{:?}", ret), "Err(ExceedRecursiveLimit)");
1581    } // }}}
1582
1583    #[test]
1584    fn macro_recursion_indirect() { // {{{
1585        let ret = preprocess_usualargs("macro_recursion_indirect.sv");
1586        assert_eq!(format!("{:?}", ret), "Err(ExceedRecursiveLimit)");
1587    } // }}}
1588
1589    #[test]
1590    fn pragma() { // {{{
1591        let (ret, _) = preprocess_usualargs("pragma.sv").unwrap();
1592        assert_eq!(
1593            ret.text(),
1594            testfile_contents("pragma.sv")
1595        );
1596    } // }}}
1597
1598    #[test]
1599    fn resetall() { // {{{
1600        let (ret, _) = preprocess_usualargs("resetall.sv").unwrap();
1601        assert_eq!(
1602            ret.text(),
1603            testfile_contents("resetall.sv")
1604        );
1605    } // }}}
1606
1607    #[test]
1608    fn timescale() { // {{{
1609        let (ret, _) = preprocess_usualargs("timescale.sv").unwrap();
1610        assert_eq!(
1611            ret.text(),
1612            testfile_contents("timescale.sv")
1613        );
1614    } // }}}
1615
1616    #[test]
1617    fn unconnected_drive() { // {{{
1618        let (ret, _) = preprocess_usualargs("unconnected_drive.sv").unwrap();
1619        assert_eq!(
1620            ret.text(),
1621            testfile_contents("unconnected_drive.sv")
1622        );
1623    } // }}}
1624
1625    #[test]
1626    fn undef() { // {{{
1627        let (ret, _) = preprocess_usualargs("undef.sv").unwrap();
1628        assert_eq!(
1629            ret.text(),
1630            testfile_contents("expected/undef.sv")
1631        );
1632    } // }}}
1633
1634    #[test]
1635    fn undefineall() { // {{{
1636        let (ret, _) = preprocess_usualargs("undefineall.sv").unwrap();
1637        assert_eq!(
1638            ret.text(),
1639            testfile_contents("expected/undefineall.sv")
1640        );
1641    } // }}}
1642}