spq_core/ty/
walk.rs

1use crate::ty::Type;
2use std::fmt;
3
4#[derive(Clone)]
5pub enum Seg<'a> {
6    NamedIndex(usize, &'a str),
7    Index(usize),
8}
9impl<'a> fmt::Debug for Seg<'a> {
10    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
11        match self {
12            Seg::NamedIndex(_, name) => f.write_str(name),
13            Seg::Index(i) => write!(f, "{}", i),
14        }
15    }
16}
17
18#[derive(Clone, Debug)]
19pub struct MemberVariableRouting<'a> {
20    pub sym: Vec<Seg<'a>>,
21    pub offset: usize,
22    pub ty: &'a Type,
23}
24
25struct WalkFrame<'a> {
26    sym_stem: Option<Vec<Seg<'a>>>,
27    base_offset: usize,
28    ty: &'a Type,
29    i: usize,
30}
31pub struct Walk<'a> {
32    inner: Vec<WalkFrame<'a>>,
33}
34impl<'a> Walk<'a> {
35    pub fn new(ty: &'a Type) -> Walk<'a> {
36        let frame = WalkFrame {
37            sym_stem: None,
38            base_offset: 0,
39            ty: ty,
40            i: 0,
41        };
42        Walk { inner: vec![frame] }
43    }
44}
45impl<'a> Iterator for Walk<'a> {
46    type Item = MemberVariableRouting<'a>;
47    fn next(&mut self) -> Option<MemberVariableRouting<'a>> {
48        fn get_child_ty_offset_seg<'a>(
49            ty: &'a Type,
50            i: usize,
51        ) -> Option<(&'a Type, usize, Seg<'a>)> {
52            match ty {
53                Type::Struct(struct_ty) => {
54                    let member = struct_ty.members.get(i)?;
55                    let seg = if let Some(name) = &member.name {
56                        Seg::NamedIndex(i, &name)
57                    } else {
58                        Seg::Index(i)
59                    };
60                    Some((&member.ty, member.offset.unwrap_or_default(), seg))
61                }
62                Type::Array(arr_ty) => {
63                    // Unsized buffer are treated as 0-sized.
64                    if i < arr_ty.nelement.unwrap_or_default() as usize {
65                        Some((
66                            &arr_ty.element_ty,
67                            arr_ty.stride.unwrap_or_default() * i,
68                            Seg::Index(i),
69                        ))
70                    } else {
71                        None
72                    }
73                }
74                _ => None,
75            }
76        }
77        enum LoopEnd<'a> {
78            Push(WalkFrame<'a>),
79            PopReturn(MemberVariableRouting<'a>),
80        }
81        loop {
82            // If used, this field will be filled with the next frame to be
83            // pushed at the back of the walk stack; or the last frame will be
84            // popped if the field is kept `None`.
85            let loop_end = if let Some(frame) = self.inner.last_mut() {
86                if let Some((child_ty, offset, seg)) = get_child_ty_offset_seg(frame.ty, frame.i) {
87                    frame.i += 1; // Step member.
88                    let offset = frame.base_offset + offset;
89                    let ty = child_ty;
90                    let sym = if let Some(sym_stem) = &frame.sym_stem {
91                        let mut sym = sym_stem.clone();
92                        sym.push(seg);
93                        sym
94                    } else {
95                        vec![seg]
96                    };
97                    if child_ty.is_struct() || child_ty.is_array() {
98                        // Found composite type, step into it.
99                        LoopEnd::Push(WalkFrame {
100                            sym_stem: Some(sym),
101                            base_offset: offset,
102                            ty,
103                            i: 0,
104                        })
105                    } else {
106                        // Return directly if it's not a composite type.
107                        return Some(MemberVariableRouting { sym, offset, ty });
108                    }
109                } else {
110                    // Here can be reached only when the first frame's type is
111                    // neither an array nor a struct; or a later frame's
112                    // composite type's elements has been exhausted.
113                    let ty = frame.ty;
114                    let offset = frame.base_offset;
115                    let sym = frame.sym_stem.clone().unwrap_or_default();
116                    LoopEnd::PopReturn(MemberVariableRouting { sym, offset, ty })
117                }
118            } else {
119                // We have exhausted all types we have, including the root type
120                // of walk.
121                return None;
122            };
123            match loop_end {
124                LoopEnd::Push(frame) => self.inner.push(frame),
125                LoopEnd::PopReturn(route) => {
126                    self.inner.pop();
127                    return Some(route);
128                }
129            }
130        }
131    }
132}