1use 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 pub fn new(value: &Expr, env: &mut Environment) -> IResult<Self> {
33 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 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 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 (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 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}