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