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 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 _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}