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(
37    Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
38)]
39pub enum FileId {
40    Include { path: PathBuf },
41    Section { path: PathBuf, section: String },
42}
43
44impl From<PathBuf> for FileId {
45    fn from(path: PathBuf) -> Self {
46        Self::Include { path }
47    }
48}
49
50impl From<(PathBuf, String)> for FileId {
51    fn from(value: (PathBuf, String)) -> Self {
52        Self::Section {
53            path: value.0,
54            section: value.1,
55        }
56    }
57}
58
59impl fmt::Display for FileId {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        match self {
62            FileId::Include { path } => write!(f, "File \"{}\"", path.display()),
63            FileId::Section { path, section } => {
64                write!(f, "File \"{}\", section {section}", path.display())
65            }
66        }
67    }
68}
69
70impl FileId {
71    pub fn path(&self) -> &Path {
72        match self {
73            FileId::Include { path } | FileId::Section { path, section: _ } => path,
74        }
75    }
76}
77
78#[derive(Debug, Clone)]
79pub enum EndReason<'a> {
80    Include {
81        file_name: &'a Path,
82        section: Option<String>,
83    },
84    End,
85}
86
87#[derive(Debug, Clone, Copy)]
88pub struct ParsedId(pub usize);
89
90#[derive(Debug)]
91pub struct FileStorage<Parsed> {
92    pub file: Vec<String>,
93    pub parsed: Vec<(FileId, Parsed)>,
94    pub id2idx: HashMap<FileId, ParsedId>,
95}
96impl<Parsed> Default for FileStorage<Parsed> {
97    #[inline]
98    fn default() -> Self {
99        const CAP: usize = 4;
100        Self {
101            id2idx: HashMap::with_capacity(CAP),
102            file: Vec::with_capacity(CAP),
103            parsed: Vec::with_capacity(CAP),
104        }
105    }
106}
107impl<Parsed: Default> FileStorage<Parsed> {
108    pub fn existed(&self, file_id: &FileId) -> Option<ParsedId> {
109        crate::debug!("load {file_id}");
110        self.id2idx.get(file_id).copied()
111    }
112    pub fn new_file(&mut self, file_id: FileId) -> ParsedId {
113        match self.id2idx.entry(file_id) {
114            Entry::Occupied(occupied) => {
115                crate::warn!("already loaded {:?}", occupied.key());
116                *occupied.get()
117            }
118            Entry::Vacant(vacant_entry) => {
119                let files_num = self.file.len();
120                self.file.push(String::new());
121                self.parsed
122                    .push((vacant_entry.key().clone(), Parsed::default()));
123                *vacant_entry.insert(ParsedId(files_num))
124            }
125        }
126    }
127    pub fn update_ctx(&mut self, parsed_id: &ParsedId, file_ctx: String, parsed: Parsed) {
128        self.file[parsed_id.0] = file_ctx;
129        self.parsed[parsed_id.0].1 = parsed;
130    }
131}
132
133impl Index<&Span> for str {
134    type Output = str;
135    #[inline]
136    fn index(&self, index: &Span) -> &Self::Output {
137        &self[index.start..index.end]
138    }
139}
140
141impl Index<&Span> for String {
142    type Output = str;
143    #[inline]
144    fn index(&self, index: &Span) -> &Self::Output {
145        &self[index.start..index.end]
146    }
147}
148
149impl From<LocatedSpan<'_>> for Span {
150    #[inline]
151    fn from(s: LocatedSpan<'_>) -> Self {
152        let start = s.location_offset();
153        Self {
154            start,
155            end: start + s.fragment().len(),
156            line_num: s.location_line(),
157        }
158    }
159}
160
161impl From<LocatedSpan<'_>> for Pos {
162    #[inline]
163    fn from(s: LocatedSpan<'_>) -> Self {
164        let start = s.location_offset();
165        Self {
166            start,
167            line_num: s.location_line(),
168        }
169    }
170}
171
172#[cfg(test)]
173mod test {
174    use super::*;
175    use nom::{
176        IResult,
177        bytes::complete::{tag, take_until},
178    };
179
180    #[derive(Debug, Default)]
181    struct Token {
182        // pub pos: Pos,
183        pub _foo: Span,
184        pub _bar: Span,
185    }
186
187    fn parse_foobar(s: LocatedSpan) -> IResult<LocatedSpan, Token> {
188        let (s, _) = take_until("foo")(s)?;
189        let (s, foo) = tag("foo")(s)?;
190        let (s, bar) = tag("bar")(s)?;
191        Ok((
192            s,
193            Token {
194                _foo: foo.into(),
195                _bar: bar.into(),
196            },
197        ))
198    }
199    #[test]
200    fn main() {
201        let mut file_storage = FileStorage::default();
202        let file_ctx = String::from("Lorem ipsum \n foobar");
203        let parsed_id = file_storage.new_file(FileId::Include {
204            path: "dummy.sp".into(),
205        });
206        let input = span(&file_ctx, 0);
207        let output = parse_foobar(input).unwrap().1;
208        file_storage.update_ctx(&parsed_id, file_ctx, output);
209        let file = &file_storage.file[parsed_id.0];
210        println!("{}", &file[&file_storage.parsed[parsed_id.0].1._foo]);
211    }
212}