Skip to main content

scarf_parser/preprocessor/
state.rs

1// =======================================================================
2// state.rs
3// =======================================================================
4//! The state and setup of the preprocessor
5
6use crate::*;
7use scarf_syntax::SpanRelation;
8use std::collections::HashMap;
9use std::io;
10use std::path::{Path, PathBuf};
11
12const DEFAULT_TIMESCALE: Timescale = Timescale::new_default(
13    (TimescaleValue::One, TimescaleUnit::NS),
14    (TimescaleValue::One, TimescaleUnit::NS),
15);
16
17const DEFAULT_NETTYPE: DefaultNettype = DefaultNettype::Wire;
18
19const DEFAULT_UNCONNECTED_DRIVE: UnconnectedDrive =
20    UnconnectedDrive::NoUnconnected;
21
22/// A preprocessor definition
23#[derive(Clone, Debug)]
24pub struct Define<'a> {
25    pub name: SpannedString<'a>,
26    pub body: DefineBody<'a>,
27}
28
29impl<'a> Define<'a> {
30    /// Whether the define is from the command line
31    ///
32    /// CLI defines have an empty `file` for their [`Span`]
33    pub fn is_from_command_line(&self) -> bool {
34        self.name.1.file == ""
35    }
36}
37
38/// The body of a preprocessor definition
39///
40/// Defines can either be
41/// - Empty (no associated text)
42/// - Some sequence of tokens
43/// - A function (see [`DefineFunction`])
44#[derive(Clone, Debug)]
45pub enum DefineBody<'a> {
46    Empty,
47    Text(Vec<SpannedToken<'a>>),
48    Function(DefineFunction<'a>),
49}
50
51/// A preprocessor text macro function
52#[derive(Clone, Debug)]
53pub struct DefineFunction<'a> {
54    /// A list of arguments (possibly with defaults), with the
55    /// [`Span`] being that of the `=`
56    pub args: Vec<(
57        SpannedString<'a>,
58        Option<(
59            Span<'a>, // =
60            Vec<SpannedToken<'a>>,
61        )>,
62    )>,
63    /// The body of the function
64    pub body: Option<Vec<SpannedToken<'a>>>,
65}
66
67impl<'a> DefineBody<'a> {
68    /// The tokens associated with a definition
69    ///
70    /// This returns `(tokens, args)`, where `args` is
71    /// some function arguments if the definition is a function,
72    /// and [`None`] if not
73    pub fn get_tokens(
74        &self,
75    ) -> (
76        Vec<SpannedToken<'a>>,
77        Option<Vec<(SpannedString<'a>, Option<Vec<SpannedToken<'a>>>)>>,
78    ) {
79        match self {
80            DefineBody::Empty => (vec![], None),
81            DefineBody::Text(token_vec) => (token_vec.clone(), None),
82            DefineBody::Function(def_func) => {
83                let function_args = def_func
84                    .args
85                    .iter()
86                    .map(|(a, b)| match b {
87                        Some((_, tokens)) => (a.clone(), Some(tokens.clone())),
88                        None => (a.clone(), None),
89                    })
90                    .collect();
91                match &def_func.body {
92                    Some(token_vec) => (token_vec.clone(), Some(function_args)),
93                    None => (vec![], Some(function_args)),
94                }
95            }
96        }
97    }
98}
99
100/// A line directive found during preprocessing
101#[derive(Clone)]
102pub struct LineDirective<'a> {
103    /// The file name indicated in the directive
104    pub directive_file_name: &'a str,
105    /// The line number indicated in the directive
106    pub directive_line_number: usize,
107    /// The span the directive was found at
108    pub original_span: Span<'a>,
109    /// The line number the directive was found at
110    pub original_line_num: usize,
111}
112
113/// The current state of the preprocessor
114///
115/// The preprocessor has to keep track of various directives and
116/// how they affect later preprocessing. A [`PreprocessorState`]
117/// encapsulates all of this.
118///
119/// This is primarily meant to be used in the preprocessor, but
120/// can be examined afterwards to glean extra information, such
121/// as which files were included.
122#[derive(Clone)]
123pub struct PreprocessorState<'a> {
124    /// The include paths to search for included files
125    pub includes: Vec<&'a Path>,
126    /// The preprocessor definitions
127    pub defines: Vec<Define<'a>>,
128    /// Any timescales declared with `` `timescale ``
129    pub timescales: Vec<Timescale<'a>>,
130    /// Default nettypes declared with `` `default_nettype ``
131    pub default_nettypes: Vec<(DefaultNettype, Span<'a>)>,
132    /// Unconnected drives declared with `` `unconnected_drive ``
133    /// or `` `nounconnected_drive ``
134    pub unconnected_drives: Vec<(UnconnectedDrive, Span<'a>)>,
135    /// Cell definitions from `` `celldefine `` and `` `endcelldefine ``
136    pub cell_defines: Vec<(bool, Span<'a>)>,
137    /// Line directives declared with `` `line ``
138    pub line_directives: Vec<LineDirective<'a>>,
139    /// The contents of included files (`file_name` -> `content`)
140    pub included_files: HashMap<&'a str, &'a str>,
141    /// The current standard for reserved keywords
142    pub curr_standard: StandardVersion,
143    /// Any errors encountered so far
144    pub errors: Vec<PreprocessorError<'a>>,
145    pub(crate) in_define: bool,
146    pub(crate) in_define_arg: bool,
147    pub(crate) include_depth: usize,
148}
149
150impl<'a> PreprocessorState<'a> {
151    /// Create a new [`PreprocessorState`]
152    pub fn new(includes: Vec<&'a Path>, defines: Vec<Define<'a>>) -> Self {
153        Self {
154            includes,
155            defines,
156            timescales: vec![],
157            default_nettypes: vec![],
158            unconnected_drives: vec![],
159            cell_defines: vec![],
160            line_directives: vec![],
161            included_files: HashMap::new(),
162            curr_standard: StandardVersion::default(),
163            errors: vec![],
164            in_define: false,
165            in_define_arg: false,
166            include_depth: 0,
167        }
168    }
169    /// Make the [`PreprocessorState`] fresh, as though it had just started preprocessing
170    ///
171    /// This retains all files that were read in, avoiding reading in their contents again
172    pub fn make_fresh(&mut self, defines: Vec<Define<'a>>) {
173        self.defines = defines;
174        self.timescales = vec![];
175        self.default_nettypes = vec![];
176        self.unconnected_drives = vec![];
177        self.cell_defines = vec![];
178        self.line_directives = vec![];
179        self.curr_standard = StandardVersion::default();
180        self.errors = vec![];
181        self.in_define = false;
182        self.in_define_arg = false;
183        self.include_depth = 0;
184    }
185    /// Reset all resetable configs
186    ///
187    /// This is called when a `` `resetall `` is encountered
188    pub fn reset_all(&mut self, reset_all_span: Span<'a>) {
189        self.add_timescale(
190            Timescale::new(
191                reset_all_span.clone(),
192                DEFAULT_TIMESCALE.unit,
193                DEFAULT_TIMESCALE.precision,
194            )
195            .unwrap(),
196        );
197        self.add_default_nettype(reset_all_span.clone(), DEFAULT_NETTYPE);
198        self.add_unconnected_drive(
199            reset_all_span.clone(),
200            DEFAULT_UNCONNECTED_DRIVE,
201        );
202        self.add_cell_define(false, reset_all_span);
203    }
204
205    /// Check whether the given macro is defined
206    pub fn is_defined(&self, macro_name: &'a str) -> bool {
207        self.defines.iter().any(|d| d.name.0 == macro_name)
208    }
209
210    /// Get the definition span for a macro, if it's been defined
211    pub fn get_define_decl(&self, macro_name: &'a str) -> Option<Span<'a>> {
212        match self.defines.iter().find(|d| d.name.0 == macro_name) {
213            None => None,
214            Some(define) => Some(define.name.1.clone()),
215        }
216    }
217
218    /// Called when starting to preprocess a definition
219    ///
220    /// Each call to [`PreprocessorState::enter_define`] should be
221    /// paired with a later call to [`PreprocessorState::exit_define`]
222    #[inline]
223    pub(crate) fn enter_define(&mut self) -> bool {
224        let prev_in_define = self.in_define;
225        self.in_define = true;
226        prev_in_define
227    }
228
229    /// Called when stopping preprocessing of a definition
230    ///
231    /// Each call to [`PreprocessorState::enter_define`] should be
232    /// paired with a later call to [`PreprocessorState::exit_define`]
233    #[inline]
234    pub(crate) fn exit_define(&mut self, prev_in_define: bool) {
235        self.in_define = prev_in_define;
236    }
237
238    /// Whether we're currently preprocessing a definition
239    #[inline]
240    pub fn in_define(&self) -> bool {
241        self.in_define
242    }
243
244    /// Called when starting to preprocess a definition argument
245    ///
246    /// Each call to [`PreprocessorState::enter_define_arg`] should be
247    /// paired with a later call to [`PreprocessorState::exit_define_arg`]
248    #[inline]
249    pub(crate) fn enter_define_arg(&mut self) -> bool {
250        let prev_in_define_arg = self.in_define_arg;
251        self.in_define_arg = true;
252        prev_in_define_arg
253    }
254
255    /// Called when stopping preprocessing of a definition argument
256    ///
257    /// Each call to [`PreprocessorState::enter_define_arg`] should be
258    /// paired with a later call to [`PreprocessorState::exit_define_arg`]
259    #[inline]
260    pub(crate) fn exit_define_arg(&mut self, prev_in_define_arg: bool) {
261        self.in_define_arg = prev_in_define_arg;
262    }
263
264    /// Whether we're currently preprocessing a definition argument
265    #[inline]
266    pub fn in_define_arg(&self) -> bool {
267        self.in_define_arg
268    }
269
270    /// Remove a given macro, evaluating to whether a macro was removed
271    ///
272    /// This is called when a `` `undef `` is encountered
273    pub fn undefine(&mut self, macro_name: &'a str) -> bool {
274        let prev_len = self.defines.len();
275        self.defines.retain(|d| d.name.0 != macro_name);
276        prev_len != self.defines.len()
277    }
278
279    /// Define a new macro
280    ///
281    /// This is called when a `` `define `` is encountered, and assumes that
282    /// any previous definitions were removed with [`PreprocessorState::undefine`]
283    pub fn define(
284        &mut self,
285        macro_name: &'a str,
286        macro_span: Span<'a>,
287        macro_body: DefineBody<'a>,
288    ) {
289        self.defines.push(Define {
290            name: SpannedString(macro_name, macro_span),
291            body: macro_body,
292        });
293    }
294
295    /// Define a new macro from the command line
296    ///
297    /// This is similar to [`PreprocessorState::define`], but
298    /// uses a [`Span`] with no file name
299    pub fn command_line_define(
300        &mut self,
301        macro_name: &'a str,
302        macro_text: Option<Vec<SpannedToken<'a>>>,
303    ) {
304        self.define(
305            macro_name,
306            Span::default(),
307            match macro_text {
308                None => DefineBody::Empty,
309                Some(token_vec) => DefineBody::Text(token_vec),
310            },
311        )
312    }
313
314    /// Undefine all macros
315    ///
316    /// This is called when a `` `undefineall `` is encountered
317    pub fn undefineall(&mut self) {
318        self.defines = vec![];
319    }
320
321    /// Get the ([`Span`], [`DefineBody::get_tokens`]) for a text macro,
322    /// if it exists
323    pub fn get_macro_tokens(
324        &self,
325        macro_name: &'a str,
326    ) -> Option<(
327        Span<'a>,
328        (
329            Vec<SpannedToken<'a>>,
330            Option<Vec<(SpannedString<'a>, Option<Vec<SpannedToken<'a>>>)>>,
331        ),
332    )> {
333        for define in &self.defines {
334            if define.name.0 == macro_name {
335                return Some((define.name.1.clone(), define.body.get_tokens()));
336            }
337        }
338        None
339    }
340
341    /// Get the full path from an `` `include `` statement
342    pub fn get_file_path(&self, include_path: &str) -> Option<PathBuf> {
343        for dir_path in &self.includes {
344            let full_path = Path::new(dir_path).join(include_path);
345            if full_path.exists() {
346                return Some(full_path);
347            }
348        }
349        None
350    }
351
352    /// Add a compiler directive timescale
353    ///
354    /// This is called when a `` `timescale `` is encountered
355    pub fn add_timescale(&mut self, timescale: Timescale<'a>) {
356        self.timescales.push(timescale);
357    }
358
359    /// Get the correct compiler timescale, based on the [`Span`]
360    /// where a delay is encountered
361    pub fn get_timescale(&self, span: &Span<'a>) -> &Timescale<'a> {
362        for timescale in self.timescales.iter().rev() {
363            if timescale.is_valid(span) {
364                return timescale;
365            }
366        }
367        // Default timescale
368        &DEFAULT_TIMESCALE
369    }
370
371    /// Add a compiler directive default nettype
372    ///
373    /// This is called when a `` `default_nettype `` is encountered
374    pub fn add_default_nettype(
375        &mut self,
376        def_span: Span<'a>,
377        default_nettype: DefaultNettype,
378    ) {
379        self.default_nettypes.push((default_nettype, def_span));
380    }
381
382    /// Get the correct compiler default nettype, based on the [`Span`]
383    /// where an implicit nettype is needed
384    pub fn get_default_nettype(&self, span: &Span<'a>) -> &DefaultNettype {
385        for default_nettype in self.default_nettypes.iter().rev() {
386            if default_nettype.1.compare(span) == SpanRelation::Earlier {
387                return &default_nettype.0;
388            }
389        }
390        &DEFAULT_NETTYPE
391    }
392
393    /// Retain the contents of a file found from an `` `include `` statement
394    ///
395    /// Produces `(&path, &file_contents)`
396    ///
397    /// This differs from [`PreprocessorState::retain_file`] by only reading
398    /// in the file if necessary, and using the include paths to find the
399    /// correct file to read in.
400    pub(crate) fn retain_include_file(
401        &mut self,
402        include_path: &'a str,
403        include_path_span: Span<'a>,
404        cache: &'a PreprocessorCache<'a>,
405    ) -> Result<(&'a str, &'a str), PreprocessorError<'a>> {
406        let include_path_buf =
407            self.get_file_path(include_path).ok_or_else(|| {
408                PreprocessorError::Include {
409                    include_path,
410                    include_path_span: include_path_span.clone(),
411                    read_err: io::ErrorKind::NotFound,
412                }
413            })?;
414        match self
415            .included_files
416            .get_key_value::<str>(include_path_buf.to_str().unwrap())
417        {
418            Some((path, contents)) => Ok((*path, *contents)),
419            None => {
420                let cached_path = cache.retain_string(
421                    include_path_buf.to_str().unwrap().to_owned(),
422                );
423                let file_contents = std::fs::read_to_string(&cached_path)
424                    .map_err(|err| PreprocessorError::Include {
425                        include_path,
426                        include_path_span,
427                        read_err: err.kind(),
428                    })?;
429                let cached_contents = cache.retain_string(file_contents);
430                self.included_files.insert(cached_path, cached_contents);
431                Ok((cached_path, cached_contents))
432            }
433        }
434    }
435
436    /// Retain the contents of a file
437    ///
438    /// Produces `(&file_path, &file_contents)` as references to
439    /// the cached passed arguments
440    pub fn retain_file(
441        &mut self,
442        file_path: String,
443        file_contents: String,
444        cache: &'a PreprocessorCache<'a>,
445    ) -> (&'a str, &'a str) {
446        match self.included_files.get_key_value::<str>(file_path.as_ref()) {
447            Some((path, contents)) => (*path, *contents),
448            None => {
449                let path = cache.retain_string(file_path);
450                let contents = cache.retain_string(file_contents);
451                self.included_files.insert(path, contents);
452                (path, contents)
453            }
454        }
455    }
456
457    /// Get the included files as a [`Vec`] of (name, content) tuples
458    pub fn included_files(&self) -> Vec<(String, String)> {
459        self.included_files
460            .iter()
461            .map(|(a, b)| (a.to_string(), b.to_string()))
462            .collect()
463    }
464
465    /// Add an unconnected drive
466    ///
467    /// This is called when a `` `unconnected_drive `` or a
468    /// `` `nounconnected_drive `` is encountered
469    pub fn add_unconnected_drive(
470        &mut self,
471        unconnected_drive_span: Span<'a>,
472        unconnected_drive: UnconnectedDrive,
473    ) {
474        self.unconnected_drives
475            .push((unconnected_drive, unconnected_drive_span));
476    }
477
478    /// Get the unconnected drive based on the [`Span`] where an
479    /// unconnected net is encountered
480    pub fn get_unconnected_drive(&self, span: &Span<'a>) -> &UnconnectedDrive {
481        for unconnected_drive in self.unconnected_drives.iter().rev() {
482            if unconnected_drive.1.compare(span) == SpanRelation::Earlier {
483                return &unconnected_drive.0;
484            }
485        }
486        &DEFAULT_UNCONNECTED_DRIVE
487    }
488
489    /// Add a cell define declaration
490    ///
491    /// This is called when a `` `celldefine `` is encountered
492    pub fn add_cell_define(&mut self, is_cell_define: bool, span: Span<'a>) {
493        self.cell_defines.push((is_cell_define, span));
494    }
495
496    /// Determine whether a module is a cell module, based on the [`Span`]
497    /// of the module declaration
498    pub fn is_cell_module(&self, declaration_span: &Span<'a>) -> bool {
499        for cell_define in self.cell_defines.iter().rev() {
500            if cell_define.1.compare(declaration_span) == SpanRelation::Earlier
501            {
502                return cell_define.0;
503            }
504        }
505        false
506    }
507
508    /// Add a line directive
509    ///
510    /// This is called when a `` `line `` is encountered
511    pub fn add_line_directive(
512        &mut self,
513        file_name: &'a str,
514        line_number: &'a str,
515        dir_span: Span<'a>,
516    ) {
517        let offset = dir_span.bytes.end;
518        let file_contents: &str =
519            self.included_files.get(dir_span.file).unwrap();
520        let line_num = file_contents[..offset].lines().count();
521        let new_line_directive = LineDirective {
522            directive_file_name: file_name,
523            directive_line_number: line_number.parse().unwrap(),
524            original_span: dir_span,
525            original_line_num: line_num,
526        };
527        self.line_directives.push(new_line_directive);
528    }
529
530    /// Get the file name from a [`Span`], factoring in `` `line `` directives
531    pub fn get_line_directive_file(&self, span: &Span<'a>) -> &'a str {
532        let Some(line_directive) = self
533            .line_directives
534            .iter()
535            .rev()
536            .filter(|line_directive| {
537                (line_directive.original_span.file == span.file)
538                    && (line_directive.original_span.bytes.start
539                        < span.bytes.start) // Only relevant if file is included twice
540            })
541            .next()
542        else {
543            return span.file;
544        };
545        line_directive.directive_file_name
546    }
547
548    /// Get the line number of a [`Span`], factoring in `` `line `` directives
549    pub fn get_line_directive_line(
550        &mut self,
551        span: &Span<'a>,
552        cache: &'a PreprocessorCache<'a>,
553    ) -> &'a str {
554        let offset = span.bytes.end;
555        let file_contents: &str = self.included_files.get(span.file).unwrap();
556        let line_num = file_contents[..offset].lines().count();
557        let Some(line_directive) = self
558            .line_directives
559            .iter()
560            .rev()
561            .filter(|line_directive| {
562                (line_directive.original_span.file == span.file)
563                    && (line_directive.original_span.bytes.start
564                        < span.bytes.start) // Only relevant if file is included twice
565            })
566            .next()
567        else {
568            return cache.retain_string(line_num.to_string());
569        };
570        let new_line_num = (line_num + line_directive.directive_line_number)
571            - (line_directive.original_line_num + 1);
572        cache.retain_string(new_line_num.to_string())
573    }
574
575    /// Get the text referenced by a [`Span`]
576    pub(crate) fn get_slice(&self, span: &Span<'a>) -> Option<&'a str> {
577        let file_contents: &str = self.included_files.get(span.file)?;
578        Some(&file_contents[span.bytes.start..span.bytes.end])
579    }
580
581    pub fn retain_string(
582        &mut self,
583        string: String,
584        cache: &'a PreprocessorCache<'a>,
585    ) -> &'a str {
586        cache.retain_string(string)
587    }
588
589    /// Add a error encountered during preprocessing
590    pub fn err(&mut self, warning: PreprocessorError<'a>) {
591        self.errors.push(warning);
592    }
593}