1use crate::{
3 convalue::ConValue,
4 env::Environment,
5 error::{Error, ErrorKind, IResult},
6 interpret::Interpret,
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 err => Err(Error::TypeError("int", err.typename()))?,
55 }
56 }
57 (Op::Dot, [place, At(Expr::Lit(Literal::Int(idx, _)), _)]) => {
58 Ok(Self::new(place.value(), env)?.dot_idx(*idx as _))
59 }
60 (Op::Dot, [place, At(Expr::Id(path), _)]) if path.parts.len() == 1 => {
61 Ok(Self::new(place.value(), env)?.dot_sym(path.parts[0]))
62 }
63 (Op::Deref, [place]) => Ok(Self::new(place.value(), env)?.deref()),
64 _ => Err(Error::NotPlace()),
65 }
66 }
67
68 pub fn new_or_temporary(value: &Expr, env: &mut Environment) -> IResult<Self> {
71 let new = Self::new(value, env);
72 if let Err(Error { kind: ErrorKind::NotPlace, .. }) = new {
73 let value = value.interpret(env)?;
74 return Ok(Self::from_index(env.stack_alloc(value)?));
75 }
76 new
77 }
78
79 pub fn from_index(index: usize) -> Self {
80 Self { place: index, projections: vec![] }
81 }
82
83 pub fn with(mut self, projection: Projection) -> Self {
84 self.projections.push(projection);
85 self
86 }
87 pub fn deref(mut self) -> Self {
88 self.with(Projection::Deref)
89 }
90 pub fn index(mut self, idx: usize, from_end: bool) -> Self {
91 self.with(Projection::Index(idx, from_end))
92 }
93 pub fn dot_idx(mut self, idx: usize) -> Self {
94 self.with(Projection::DotIdx(idx))
95 }
96 pub fn dot_sym(mut self, sym: impl Into<Symbol>) -> Self {
97 self.with(Projection::DotSym(sym.into()))
98 }
99
100 pub fn get_mut<'v, 'e: 'v>(&self, env: &'e mut Environment) -> IResult<&'v mut ConValue> {
101 let Self { place, projections } = self;
102 let env = env as *mut Environment;
103
104 let mut place = unsafe { &mut *env }
105 .get_id_mut(*place)
106 .ok_or(Error::StackOverflow(*place as _))?;
107
108 for projection in projections {
109 place = match (place, projection) {
110 (ConValue::Ref(place), Projection::Deref) => {
112 place.get_mut(unsafe { &mut *(env) })?
113 }
114 (ConValue::Ref(place), projection) => place
115 .clone()
116 .with(*projection)
117 .get_mut(unsafe { &mut *env })?,
118 (value, Projection::Deref) => value,
119 (ConValue::Array(arr), &Projection::Index(idx, from_end)) => {
120 let len = arr.len();
121 let idx = if from_end { len - idx } else { idx };
122 arr.get_mut(idx).ok_or_else(|| Error::OobIndex(idx, len))?
123 }
124 (ConValue::Slice(place, len), &Projection::Index(idx, from_end)) => {
125 if idx >= *len {
126 Err(Error::OobIndex(idx, *len))?
127 };
128 place
130 .clone()
131 .index(idx, from_end)
132 .get_mut(unsafe { &mut *env })?
133 }
134 (place, Projection::Index(_, _)) => Err(Error::NotIndexable())?,
135 (ConValue::Struct(_, values), Projection::DotSym(sym)) => {
136 values.get_mut(sym).ok_or(Error::NotDefined(*sym))?
137 }
138 (place, Projection::DotSym(name)) => {
139 Err(Error::TypeError(name.to_ref(), place.typename()))?
140 }
141 (ConValue::Tuple(values), &Projection::DotIdx(idx)) => {
142 let len = values.len();
143 values.get_mut(idx).ok_or(Error::OobIndex(idx, len))?
144 }
145 (ConValue::TupleStruct(_, values), &Projection::DotIdx(idx)) => {
146 let len = values.len();
147 values.get_mut(idx).ok_or(Error::OobIndex(idx, len))?
148 }
149 (place, Projection::DotIdx(_)) => todo!(),
150 (value, place) => Err(Error::Panic(format!(
151 "Failed to match {value} against {place:?}"
152 )))?,
153 }
154 }
155
156 Ok(place)
157 }
158
159 pub fn get<'e>(&self, env: &'e Environment) -> IResult<&'e ConValue> {
160 let Self { place, projections } = self;
161
162 let mut place = env
163 .get_id(*place)
164 .ok_or(Error::StackOverflow(*place as _))?;
165
166 for projection in projections {
167 place = match (place, projection) {
168 (ConValue::Ref(place), Projection::Deref) => place.get(env)?,
169 (ConValue::Ref(place), projection) => place.clone().with(*projection).get(env)?,
170 (ConValue::Array(arr), &Projection::Index(idx, from_end)) => {
171 let idx = if from_end { arr.len() - idx } else { idx };
172 arr.get(idx).ok_or(Error::OobIndex(idx, arr.len()))?
173 }
174 (place, Projection::Index(_, _)) => todo!("Index {self}"),
175 (ConValue::Struct(_, values), Projection::DotSym(sym)) => {
176 values.get(sym).ok_or(Error::NotDefined(*sym))?
177 }
178 (place, Projection::DotSym(interned)) => todo!(),
179 (ConValue::Tuple(values), &Projection::DotIdx(idx)) => {
180 values.get(idx).ok_or(Error::OobIndex(idx, values.len()))?
181 }
182 (ConValue::TupleStruct(_, values), &Projection::DotIdx(idx)) => {
183 values.get(idx).ok_or(Error::OobIndex(idx, values.len()))?
184 }
185 (place, Projection::DotIdx(_)) => todo!(),
186 _ => Err(Error::MatchNonexhaustive())?,
187 }
188 }
189
190 Ok(place)
191 }
192}
193
194impl Display for Place {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 let Self { place, projections } = self;
197 fn format_inner(
198 place: usize,
199 projections: &[Projection],
200 f: &mut std::fmt::Formatter<'_>,
201 ) -> std::fmt::Result {
202 match projections {
203 [] => place.fmt(f),
204 [first @ .., Projection::Deref] => {
205 "*".fmt(f)?;
206 format_inner(place, first, f)
207 }
208 [Projection::Index(idx, from_end), rest @ ..] => {
209 format_inner(place, rest, f)?;
210 write!(f, "[{}{idx}]", if *from_end { "-" } else { "" })
211 }
212 [Projection::DotIdx(idx), rest @ ..] => {
213 format_inner(place, rest, f)?;
214 write!(f, ".{idx}")
215 }
216 [Projection::DotSym(idx), rest @ ..] => {
217 format_inner(place, rest, f)?;
218 write!(f, ".{idx}")
219 }
220 _ => unreachable!("{projections:?}"),
221 }
222 }
223 format_inner(*place, projections, f)
224 }
225}