netlist_db/
span.rs

1use core::{fmt, ops::Index};
2use std::{
3    collections::{HashMap, hash_map::Entry},
4    path::{Path, PathBuf},
5};
6
7pub fn span(file_ctx: &str, line_offset: u32) -> LocatedSpan {
8    unsafe { LocatedSpan::new_from_raw_offset(0, 1 + line_offset, file_ctx, ()) }
9}
10pub type LocatedSpan<'a> = nom_locate::LocatedSpan<&'a str>;
11
12#[derive(Debug, Clone, Default, Copy)]
13pub struct Span {
14    pub start: usize,
15    pub end: usize,
16    /// The line number of the fragment relatively to the input of the parser. It starts at line 1.
17    pub line_num: u32,
18}
19
20#[derive(Debug, Clone, Copy)]
21pub struct Pos {
22    pub start: usize,
23    pub line_num: u32,
24}
25
26impl Pos {
27    pub fn new(i: LocatedSpan) -> Option<Self> {
28        if let Ok((_, pos)) = nom_locate::position::<LocatedSpan, ()>(i) {
29            Some(pos.into())
30        } else {
31            None
32        }
33    }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum FileId {
38    Include { path: PathBuf },
39    Section { path: PathBuf, section: String },
40}
41
42impl fmt::Display for FileId {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match self {
45            FileId::Include { path } => write!(f, "File \"{}\"", path.display()),
46            FileId::Section { path, section } => {
47                write!(f, "File \"{}\", section {section}", path.display())
48            }
49        }
50    }
51}
52
53impl FileId {
54    pub fn path(&self) -> &Path {
55        match self {
56            FileId::Include { path } | FileId::Section { path, section: _ } => path,
57        }
58    }
59}
60
61#[derive(Debug, Clone)]
62pub enum EndReason<'a> {
63    Include {
64        file_name: &'a Path,
65        section: Option<String>,
66    },
67    End,
68}
69
70#[derive(Debug, Clone, Copy)]
71pub struct ParsedId(pub usize);
72
73#[derive(Debug)]
74pub struct FileStorage<Parsed> {
75    pub file: Vec<String>,
76    pub parsed: Vec<(FileId, Parsed)>,
77    pub id2idx: HashMap<FileId, ParsedId>,
78}
79impl<Parsed> Default for FileStorage<Parsed> {
80    #[inline]
81    fn default() -> Self {
82        const CAP: usize = 4;
83        Self {
84            id2idx: HashMap::with_capacity(CAP),
85            file: Vec::with_capacity(CAP),
86            parsed: Vec::with_capacity(CAP),
87        }
88    }
89}
90impl<Parsed: Default> FileStorage<Parsed> {
91    pub fn existed(&self, file_id: &FileId) -> Option<ParsedId> {
92        crate::debug!("load {}", file_id);
93        self.id2idx.get(file_id).copied()
94    }
95    pub fn new_file(&mut self, file_id: FileId) -> ParsedId {
96        match self.id2idx.entry(file_id) {
97            Entry::Occupied(occupied) => {
98                crate::warn!("already loaded {:?}", occupied.key());
99                *occupied.get()
100            }
101            Entry::Vacant(vacant_entry) => {
102                let files_num = self.file.len();
103                self.file.push(String::new());
104                self.parsed
105                    .push((vacant_entry.key().clone(), Parsed::default()));
106                *vacant_entry.insert(ParsedId(files_num))
107            }
108        }
109    }
110    pub fn update_ctx(&mut self, parsed_id: &ParsedId, file_ctx: String, parsed: Parsed) {
111        self.file[parsed_id.0] = file_ctx;
112        self.parsed[parsed_id.0].1 = parsed;
113    }
114}
115
116impl Index<&Span> for str {
117    type Output = str;
118    #[inline]
119    fn index(&self, index: &Span) -> &Self::Output {
120        &self[index.start..index.end]
121    }
122}
123
124impl Index<&Span> for String {
125    type Output = str;
126    #[inline]
127    fn index(&self, index: &Span) -> &Self::Output {
128        &self[index.start..index.end]
129    }
130}
131
132impl From<LocatedSpan<'_>> for Span {
133    #[inline]
134    fn from(s: LocatedSpan<'_>) -> Self {
135        let start = s.location_offset();
136        Self {
137            start,
138            end: start + s.fragment().len(),
139            line_num: s.location_line(),
140        }
141    }
142}
143
144impl From<LocatedSpan<'_>> for Pos {
145    #[inline]
146    fn from(s: LocatedSpan<'_>) -> Self {
147        let start = s.location_offset();
148        Self {
149            start,
150            line_num: s.location_line(),
151        }
152    }
153}
154
155#[cfg(test)]
156mod test {
157    use super::*;
158    use nom::{
159        IResult,
160        bytes::complete::{tag, take_until},
161    };
162
163    #[derive(Debug, Default)]
164    struct Token {
165        // pub pos: Pos,
166        pub _foo: Span,
167        pub _bar: Span,
168    }
169
170    fn parse_foobar(s: LocatedSpan) -> IResult<LocatedSpan, Token> {
171        let (s, _) = take_until("foo")(s)?;
172        let (s, foo) = tag("foo")(s)?;
173        let (s, bar) = tag("bar")(s)?;
174        Ok((
175            s,
176            Token {
177                _foo: foo.into(),
178                _bar: bar.into(),
179            },
180        ))
181    }
182    #[test]
183    fn main() {
184        let mut file_storage = FileStorage::default();
185        let file_ctx = String::from("Lorem ipsum \n foobar");
186        let parsed_id = file_storage.new_file(FileId::Include {
187            path: "dummy.sp".into(),
188        });
189        let input = span(&file_ctx, 0);
190        let output = parse_foobar(input).unwrap().1;
191        file_storage.update_ctx(&parsed_id, file_ctx, output);
192        let file = &file_storage.file[parsed_id.0];
193        println!("{}", &file[&file_storage.parsed[parsed_id.0].1._foo]);
194    }
195}