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