Skip to main content

cl_interpret/
place.rs

1//! [Place]-expressions in the interpreter
2use crate::{
3    convalue::ConValue,
4    env::Environment,
5    error::{Error, ErrorKind, IResult},
6    interpret::{Interpret, todo},
7};
8use cl_ast::{
9    At, Expr, Op,
10    types::{Literal, Symbol},
11};
12use std::{fmt::Display, mem::take};
13
14#[derive(Clone, Debug, Default)]
15pub struct Place {
16    place: usize,
17    projections: Vec<Projection>,
18}
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
21pub enum Projection {
22    Deref,
23    Index(usize, bool),
24    DotSym(Symbol),
25    DotIdx(usize),
26}
27
28impl Place {
29    /// Creates a new [Place] [Projection] expression from an [Expr].
30    ///
31    /// If a place-projection can't be constructed, returns [Error::NotPlace].
32    pub fn new(value: &Expr, env: &mut Environment) -> IResult<Self> {
33        // base case
34        if let Expr::Id(names) = value
35            && let &[name] = names.parts.as_slice()
36        {
37            return Ok(Self { place: env.id_of(name)?, projections: vec![] });
38        };
39        let Expr::Op(op, exprs) = value else {
40            return Err(Error::NotPlace());
41        };
42
43        match (op, exprs.as_slice()) {
44            (Op::As, exprs) => Err(Error::NotPlace()),
45            (Op::Block | Op::Group | Op::MetaInner | Op::MetaOuter | Op::Pub, [expr]) => {
46                Self::new(expr.value(), env)
47            }
48            (Op::Index, [place, idx]) => {
49                let place = Self::new(place.value(), env)?;
50                // this may be surprising, as it evaluates an arbitrary expression!!!
51                match idx.interpret(env)? {
52                    ConValue::Int(idx @ ..0) => Ok(place.index(-idx as usize, true)),
53                    ConValue::Int(idx @ 0..) => Ok(place.index(idx as usize, false)),
54                    ConValue::TupleStruct(t, ref arr)
55                        if let ([ConValue::Int(start), ConValue::Int(end)], "RangeExc") =
56                            (&arr[..], t.0.name()) =>
57                    {
58                        todo!("Projection::Slice ({start}, {end})")
59                    }
60                    err => Err(Error::TypeError("int", err.type_of()))?,
61                }
62            }
63            (Op::Dot, [place, At(Expr::Lit(Literal::Int(idx, _)), _)]) => {
64                Ok(Self::new(place.value(), env)?.dot_idx(*idx as _))
65            }
66            (Op::Dot, [place, At(Expr::Id(path), _)]) if path.parts.len() == 1 => {
67                Ok(Self::new(place.value(), env)?.dot_sym(path.parts[0]))
68            }
69            (Op::Deref, [place]) => Ok(Self::new(place.value(), env)?.deref()),
70            _ => Err(Error::NotPlace()),
71        }
72    }
73
74    /// Creates a new [Place] if this [Expr] is a place expression,
75    /// otherwise allocates a new temporary and returns a Place-reference to it.
76    pub fn new_or_temporary(value: &Expr, env: &mut Environment) -> IResult<Self> {
77        let new = Self::new(value, env);
78        if let Err(Error { kind: ErrorKind::NotPlace, .. }) = new {
79            let value = value.interpret(env)?;
80            return Ok(Self::from_index(env.stack_alloc(value)?));
81        }
82        new
83    }
84
85    pub fn from_index(index: usize) -> Self {
86        Self { place: index, projections: vec![] }
87    }
88
89    pub fn with(mut self, projection: Projection) -> Self {
90        self.projections.push(projection);
91        self
92    }
93    pub fn deref(mut self) -> Self {
94        self.with(Projection::Deref)
95    }
96    pub fn index(mut self, idx: usize, from_end: bool) -> Self {
97        self.with(Projection::Index(idx, from_end))
98    }
99    pub fn dot_idx(mut self, idx: usize) -> Self {
100        self.with(Projection::DotIdx(idx))
101    }
102    pub fn dot_sym(mut self, sym: impl Into<Symbol>) -> Self {
103        self.with(Projection::DotSym(sym.into()))
104    }
105
106    pub fn get_mut<'v, 'e: 'v>(&self, env: &'e mut Environment) -> IResult<&'v mut ConValue> {
107        let Self { place, projections } = self;
108        let env = env as *mut Environment;
109
110        let mut place = unsafe { &mut *env }
111            .get_id_mut(*place)
112            .ok_or(Error::StackOob(*place as _))?;
113
114        for projection in projections {
115            place = match (place, projection) {
116                // SAFETY: fuck it, we ball. Polonius would totally have our back here
117                (ConValue::Ref(place), Projection::Deref) => {
118                    place.get_mut(unsafe { &mut *(env) })?
119                }
120                (ConValue::Ref(place), projection) => place
121                    .clone()
122                    .with(*projection)
123                    .get_mut(unsafe { &mut *env })?,
124                (value, Projection::Deref) => value,
125                (ConValue::Array(arr), &Projection::Index(idx, from_end)) => {
126                    let len = arr.len();
127                    let idx = if from_end { len - idx } else { idx };
128                    arr.get_mut(idx).ok_or_else(|| Error::OobIndex(idx, len))?
129                }
130                (ConValue::Slice(place, start, len), &Projection::Index(idx, from_end)) => {
131                    if *start + idx >= *len {
132                        Err(Error::OobIndex(idx, *len))?
133                    };
134                    // SAFETY: see above
135                    place
136                        .clone()
137                        .index(idx, from_end)
138                        .get_mut(unsafe { &mut *env })?
139                }
140                (place, Projection::Index(_, _)) => Err(Error::NotIndexable())?,
141                (ConValue::Struct(_, values), Projection::DotSym(sym)) => {
142                    values.get_mut(sym).ok_or(Error::NotDefined(*sym))?
143                }
144                (place, Projection::DotSym(name)) => Err(Error::TypeError(name, place.type_of()))?,
145                (ConValue::Tuple(values), &Projection::DotIdx(idx)) => {
146                    let len = values.len();
147                    values.get_mut(idx).ok_or(Error::OobIndex(idx, len))?
148                }
149                (ConValue::TupleStruct(_, values), &Projection::DotIdx(idx)) => {
150                    let len = values.len();
151                    values.get_mut(idx).ok_or(Error::OobIndex(idx, len))?
152                }
153                (place, Projection::DotIdx(_)) => todo!()?,
154                (value, place) => Err(Error::Panic(format!(
155                    "Failed to match {value} against {place:?}"
156                )))?,
157            }
158        }
159
160        Ok(place)
161    }
162
163    pub fn get<'e>(&self, env: &'e Environment) -> IResult<&'e ConValue> {
164        let Self { place, projections } = self;
165
166        let mut place = env.get_id(*place).ok_or(Error::StackOob(*place as _))?;
167
168        for projection in projections {
169            place = match (place, projection) {
170                (ConValue::Ref(place), Projection::Deref) => place.get(env)?,
171                (ConValue::Ref(place), projection) => place.clone().with(*projection).get(env)?,
172                (ConValue::Array(arr), &Projection::Index(idx, from_end)) => {
173                    let len = arr.len();
174                    let idx = if from_end { len - idx } else { idx };
175                    arr.get(idx).ok_or(Error::OobIndex(idx, len))?
176                }
177                (place, Projection::Index(_, _)) => todo!("Index {self}")?,
178                (ConValue::Struct(_, values), Projection::DotSym(sym)) => {
179                    values.get(sym).ok_or(Error::NotDefined(*sym))?
180                }
181                (place, Projection::DotSym(interned)) => todo!(".sym projection for {place}")?,
182                (ConValue::Tuple(values), &Projection::DotIdx(idx)) => {
183                    values.get(idx).ok_or(Error::OobIndex(idx, values.len()))?
184                }
185                (ConValue::TupleStruct(_, values), &Projection::DotIdx(idx)) => {
186                    values.get(idx).ok_or(Error::OobIndex(idx, values.len()))?
187                }
188                (place, Projection::DotIdx(_)) => todo!(".idx projection for {place}")?,
189                _ => Err(Error::NotPlace())?,
190            }
191        }
192
193        Ok(place)
194    }
195}
196
197impl Display for Place {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        let Self { place, projections } = self;
200        fn format_inner(
201            place: usize,
202            projections: &[Projection],
203            f: &mut std::fmt::Formatter<'_>,
204        ) -> std::fmt::Result {
205            match projections {
206                [] => place.fmt(f),
207                [first @ .., Projection::Deref] => {
208                    "*".fmt(f)?;
209                    format_inner(place, first, f)
210                }
211                [Projection::Index(idx, from_end), rest @ ..] => {
212                    format_inner(place, rest, f)?;
213                    write!(f, "[{}{idx}]", if *from_end { "-" } else { "" })
214                }
215                [Projection::DotIdx(idx), rest @ ..] => {
216                    format_inner(place, rest, f)?;
217                    write!(f, ".{idx}")
218                }
219                [Projection::DotSym(idx), rest @ ..] => {
220                    format_inner(place, rest, f)?;
221                    write!(f, ".{idx}")
222                }
223                _ => unreachable!("{projections:?}"),
224            }
225        }
226        format_inner(*place, projections, f)
227    }
228}
229
230#[derive(Clone, Debug)]
231pub struct PlaceIndexIter {
232    place: Place,
233    start: usize,
234    end: usize,
235    projection: Projection,
236}
237
238impl PlaceIndexIter {
239    pub fn index(place: Place, start: usize, length: usize) -> Self {
240        Self { place, start, end: start + length, projection: Projection::Index(0, false) }
241    }
242    pub fn slice(place: Place, start: usize, end: usize) -> Self {
243        Self { place, start, end, projection: Projection::Index(0, false) }
244    }
245    pub fn dot_idx(place: Place, start: usize, length: usize) -> Self {
246        Self { place, start, end: start + length, projection: Projection::DotIdx(0) }
247    }
248}
249
250impl Iterator for PlaceIndexIter {
251    type Item = Place;
252
253    fn next(&mut self) -> Option<Self::Item> {
254        let Self { place, start, end, projection } = self;
255        if start >= end {
256            return None;
257        }
258        let out = match projection {
259            Projection::Index(_, from_end) => place.clone().index(*start, *from_end),
260            Projection::DotIdx(_) => place.clone().dot_idx(*start),
261            _ => place.clone().with(*projection),
262        };
263        *start += 1;
264        Some(out)
265    }
266}