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