Skip to main content

forj_parser/preprocessor/
state.rs

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