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