1use std::{
8 borrow::Cow,
9 cell::Ref,
10 collections::BTreeSet,
11 fmt::{Debug, Display},
12 hash::Hash,
13};
14use indexmap::IndexSet;
15
16use crate::{Design, ParamValue};
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct SourcePosition {
21 pub line: u32,
23 pub column: u32,
25}
26
27#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
32pub enum MetaItem<'a> {
33 #[default]
36 None,
37 Set(BTreeSet<MetaItemRef<'a>>),
41 Source {
43 file: MetaStringRef<'a>,
45 start: SourcePosition,
47 end: SourcePosition,
49 },
50 NamedScope {
54 name: MetaStringRef<'a>,
56 source: MetaItemRef<'a>,
58 parent: MetaItemRef<'a>,
60 },
61 IndexedScope {
65 index: i32,
66 source: MetaItemRef<'a>,
68 parent: MetaItemRef<'a>,
70 },
71 Ident {
73 name: MetaStringRef<'a>,
75 scope: MetaItemRef<'a>,
78 },
79 Attr {
80 name: MetaStringRef<'a>,
82 value: ParamValue,
84 },
85 }
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
91pub(crate) struct MetaStringIndex(usize);
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
94pub(crate) struct MetaItemIndex(usize);
95
96#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
97enum MetaItemRepr {
98 #[default]
99 None,
100 Set(Vec<MetaItemIndex>),
101 Source {
102 file: MetaStringIndex,
103 start: SourcePosition,
104 end: SourcePosition,
105 },
106 NamedScope {
107 name: MetaStringIndex,
108 source: MetaItemIndex,
109 parent: MetaItemIndex,
110 },
111 IndexedScope {
112 index: i32,
113 source: MetaItemIndex,
114 parent: MetaItemIndex,
115 },
116 Ident {
117 name: MetaStringIndex,
118 scope: MetaItemIndex,
119 },
120 Attr {
121 name: MetaStringIndex,
122 value: ParamValue,
123 },
124}
125
126#[derive(Clone, Debug)]
127pub struct MetadataStore {
128 strings: IndexSet<String>,
129 items: IndexSet<MetaItemRepr>,
130}
131
132#[derive(Clone, Copy)]
133pub struct MetaStringRef<'a> {
134 design: &'a Design,
135 index: MetaStringIndex,
136}
137
138#[derive(Clone, Copy)]
139pub struct MetaItemRef<'a> {
140 design: &'a Design,
141 index: MetaItemIndex,
142}
143
144pub struct MetaItemIterator<'a> {
145 item: MetaItemRef<'a>,
146 offset: usize,
147}
148
149impl MetaItem<'_> {
150 pub fn validate(&self) {
151 match self {
152 MetaItem::None => (),
153 MetaItem::Set(items) => {
154 assert!(items.len() > 1, "MetaItem::Set must contain more than one element");
155 for item in items {
156 assert!(
157 !matches!(&*item.get_repr(), MetaItemRepr::None | MetaItemRepr::Set(..)),
158 "MetaItem::Set item must not be MetaItem::None or another MetaItem::Set"
159 )
160 }
161 }
162 MetaItem::Source { file, start, end } => {
163 assert!(!file.get().is_empty(), "MetaItem::Source must have a file");
164 assert!(
165 end.line > start.line || (end.line == start.line && end.column >= start.column),
166 "MetaItem::Source must specify a range in non-descending order"
167 );
168 }
169 MetaItem::NamedScope { name: _, source, parent } | MetaItem::IndexedScope { index: _, source, parent } => {
170 if let MetaItem::NamedScope { name, .. } = self {
171 assert!(!name.get().is_empty(), "MetaItem::NamedScope must have a name");
172 }
173 assert!(
174 matches!(&*source.get_repr(), MetaItemRepr::None | MetaItemRepr::Source { .. }),
175 concat!(
176 "source of a MetaItem::NamedScope or MetaItem::IndexedScope must be ",
177 "MetaItem::None or MetaItem::Source"
178 )
179 );
180 assert!(
181 matches!(
182 &*parent.get_repr(),
183 MetaItemRepr::None | MetaItemRepr::NamedScope { .. } | MetaItemRepr::IndexedScope { .. }
184 ),
185 concat!(
186 "parent of a MetaItem::NamedScope or MetaItem::IndexedScope must be ",
187 "MetaItem::None, MetaItem::NamedScope, or MetaItem::IndexedScope"
188 )
189 );
190 }
191 MetaItem::Ident { name, scope } => {
192 assert!(!name.get().is_empty(), "MetaItem::Ident must have a name");
193 assert!(
194 matches!(&*scope.get_repr(), MetaItemRepr::NamedScope { .. } | MetaItemRepr::IndexedScope { .. }),
195 "scope of a MetaItem::Ident must be MetaItem::NamedScope or MetaItem::IndexedScope"
196 );
197 }
198 MetaItem::Attr { name, value: _ } => {
199 assert!(!name.get().is_empty(), "MetaItem::Attr must have a name");
200 }
201 }
202 }
203}
204
205impl MetaStringIndex {
206 pub const EMPTY: MetaStringIndex = MetaStringIndex(0);
207}
208
209impl MetaItemIndex {
210 pub const NONE: MetaItemIndex = MetaItemIndex(0);
211}
212
213impl MetadataStore {
214 pub(crate) fn new() -> Self {
215 Self { strings: IndexSet::from(["".to_owned()]), items: IndexSet::from([MetaItemRepr::None]) }
216 }
217
218 pub(crate) fn add_string<'a>(&mut self, string: impl Into<Cow<'a, str>>) -> MetaStringIndex {
219 let string = string.into();
220 match self.strings.get_index_of(string.as_ref()) {
221 Some(index) => MetaStringIndex(index),
222 None => MetaStringIndex(self.strings.insert_full(string.into()).0),
223 }
224 }
225
226 pub(crate) fn ref_string<'a>(&self, design: &'a Design, index: MetaStringIndex) -> MetaStringRef<'a> {
227 MetaStringRef { design, index }
228 }
229
230 pub(crate) fn add_item(&mut self, item: &MetaItem) -> MetaItemIndex {
231 let repr = match item {
234 MetaItem::None => MetaItemRepr::None,
235 MetaItem::Set(items) => MetaItemRepr::Set(items.iter().map(|item| item.index).collect()),
236 MetaItem::Source { file, start, end } => {
237 MetaItemRepr::Source { file: file.index, start: *start, end: *end }
238 }
239 MetaItem::NamedScope { name, source, parent } => {
240 MetaItemRepr::NamedScope { parent: parent.index, source: source.index, name: name.index }
241 }
242 MetaItem::IndexedScope { index, source, parent } => {
243 MetaItemRepr::IndexedScope { parent: parent.index, source: source.index, index: *index }
244 }
245 MetaItem::Ident { name, scope } => MetaItemRepr::Ident { scope: scope.index, name: name.index },
246 MetaItem::Attr { name, value } => MetaItemRepr::Attr { name: name.index, value: value.clone() },
247 };
248 MetaItemIndex(self.items.insert_full(repr).0)
249 }
250
251 pub(crate) fn ref_item<'a>(&self, design: &'a Design, index: MetaItemIndex) -> MetaItemRef<'a> {
252 MetaItemRef { design, index }
253 }
254
255 pub(crate) fn iter_items<'a>(&self, design: &'a Design) -> impl Iterator<Item = MetaItemRef<'a>> + use<'a> {
256 (0..self.items.len()).map(|index| MetaItemRef { design, index: MetaItemIndex(index) })
257 }
258}
259
260impl<'a> MetaStringRef<'a> {
261 pub(crate) fn index(&self) -> MetaStringIndex {
262 self.index
263 }
264
265 pub fn is_empty(&self) -> bool {
266 self.index == MetaStringIndex::EMPTY
267 }
268
269 pub fn get(&self) -> Ref<'a, str> {
270 Ref::map(self.design.metadata(), |store| {
271 store.strings.get_index(self.index.0).expect("invalid metadata string reference").as_str()
272 })
273 }
274}
275
276impl<'a> MetaItemRef<'a> {
277 pub(crate) fn index(&self) -> MetaItemIndex {
278 self.index
279 }
280
281 pub fn is_none(&self) -> bool {
282 self.index == MetaItemIndex::NONE
283 }
284
285 fn get_repr(&self) -> Ref<'a, MetaItemRepr> {
286 Ref::map(self.design.metadata(), |store| {
287 store.items.get_index(self.index.0).expect("invalid metadata item reference")
288 })
289 }
290
291 pub fn get(&self) -> MetaItem<'a> {
292 let design = self.design;
293 match &*self.get_repr() {
294 MetaItemRepr::None => MetaItem::None,
295 MetaItemRepr::Set(items) => {
296 MetaItem::Set(items.iter().map(|&index| MetaItemRef { index, design }).collect())
297 }
298 MetaItemRepr::Source { file: filename, start, end } => {
299 MetaItem::Source { file: MetaStringRef { index: *filename, design }, start: *start, end: *end }
300 }
301 MetaItemRepr::NamedScope { name, source, parent } => MetaItem::NamedScope {
302 name: MetaStringRef { index: *name, design },
303 source: MetaItemRef { index: *source, design },
304 parent: MetaItemRef { index: *parent, design },
305 },
306 MetaItemRepr::IndexedScope { index, source, parent } => MetaItem::IndexedScope {
307 index: *index,
308 source: MetaItemRef { index: *source, design },
309 parent: MetaItemRef { index: *parent, design },
310 },
311 MetaItemRepr::Ident { name, scope } => MetaItem::Ident {
312 name: MetaStringRef { index: *name, design },
313 scope: MetaItemRef { index: *scope, design },
314 },
315 MetaItemRepr::Attr { name, value } => {
316 MetaItem::Attr { name: MetaStringRef { index: *name, design }, value: value.clone() }
317 }
318 }
319 }
320
321 pub fn iter(&self) -> impl Iterator<Item = MetaItemRef<'a>> + use<'a> {
322 MetaItemIterator { item: *self, offset: 0 }
323 }
324
325 pub fn from_iter(design: &'a Design, iter: impl IntoIterator<Item = MetaItemRef<'a>>) -> Self {
326 let items = BTreeSet::from_iter(iter);
327 if items.len() == 0 {
328 MetaItemRef { design, index: MetaItemIndex::NONE }
329 } else if items.len() == 1 {
330 *items.first().unwrap()
331 } else {
332 MetaItemRef { design, index: design.add_metadata_item(&MetaItem::Set(items)).index() }
333 }
334 }
335
336 pub fn from_merge(design: &'a Design, iter: impl IntoIterator<Item = MetaItemRef<'a>>) -> Self {
337 Self::from_iter(
338 design,
339 iter.into_iter().flat_map(|item| item.iter()).filter(|item| match &*item.get_repr() {
340 MetaItemRepr::Source { .. }
341 | MetaItemRepr::NamedScope { .. }
342 | MetaItemRepr::IndexedScope { .. }
343 | MetaItemRepr::Ident { .. } => true,
344 _ => false,
345 }),
346 )
347 }
348
349 pub fn merge(&self, other: MetaItemRef<'a>) -> Self {
350 Self::from_merge(&self.design, [*self, other])
351 }
352}
353
354impl<'a> Iterator for MetaItemIterator<'a> {
355 type Item = MetaItemRef<'a>;
356
357 fn next(&mut self) -> Option<Self::Item> {
358 let repr = self.item.get_repr();
359 let slice = match &*repr {
360 MetaItemRepr::None => &[][..],
361 MetaItemRepr::Set(items) => &items[..],
362 _ => std::slice::from_ref(&self.item.index),
363 };
364 if self.offset < slice.len() {
365 let item = MetaItemRef { design: self.item.design, index: slice[self.offset] };
366 self.offset += 1;
367 Some(item)
368 } else {
369 None
370 }
371 }
372}
373
374impl Display for MetaItemIndex {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 let index = self.0;
377 assert!(index > 0);
378 write!(f, "!{}", index - 1)
379 }
380}
381
382impl Debug for MetaStringRef<'_> {
383 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384 write!(f, "{:?}", self.get())
385 }
386}
387
388impl PartialEq<MetaStringRef<'_>> for MetaStringRef<'_> {
389 fn eq(&self, other: &MetaStringRef<'_>) -> bool {
390 std::ptr::eq(self.design, other.design) && self.index == other.index
391 }
392}
393
394impl Eq for MetaStringRef<'_> {}
395
396impl PartialOrd<MetaStringRef<'_>> for MetaStringRef<'_> {
397 fn partial_cmp(&self, other: &MetaStringRef<'_>) -> Option<std::cmp::Ordering> {
398 Some(self.cmp(other))
399 }
400}
401
402impl Ord for MetaStringRef<'_> {
403 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
404 match (self.design as *const Design).cmp(&(other.design as *const Design)) {
405 core::cmp::Ordering::Equal => self.index.cmp(&other.index),
406 ord => ord,
407 }
408 }
409}
410
411impl Hash for MetaStringRef<'_> {
412 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
413 self.index.hash(state);
414 }
415}
416
417impl Debug for MetaItemRef<'_> {
418 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
419 write!(f, "{:?}", self.get())
420 }
421}
422
423impl PartialEq<MetaItemRef<'_>> for MetaItemRef<'_> {
424 fn eq(&self, other: &MetaItemRef<'_>) -> bool {
425 std::ptr::eq(self.design, other.design) && self.index == other.index
426 }
427}
428
429impl Eq for MetaItemRef<'_> {}
430
431impl PartialOrd<MetaItemRef<'_>> for MetaItemRef<'_> {
432 fn partial_cmp(&self, other: &MetaItemRef<'_>) -> Option<std::cmp::Ordering> {
433 Some(self.cmp(other))
434 }
435}
436
437impl Ord for MetaItemRef<'_> {
438 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
439 match (self.design as *const Design).cmp(&(other.design as *const Design)) {
440 core::cmp::Ordering::Equal => self.index.cmp(&other.index),
441 ord => ord,
442 }
443 }
444}
445
446impl Hash for MetaItemRef<'_> {
447 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
448 self.index.hash(state);
449 }
450}