1use arithmetic_parser::{
4 grammars::Grammar, Destructure, DestructureRest, Expr, Lvalue, Spanned, SpannedExpr,
5 SpannedLvalue,
6};
7
8use crate::{
9 alloc::{String, ToOwned},
10 ast::{SpannedTypeAst, TupleAst, TypeAst},
11 TupleIndex,
12};
13
14impl TupleIndex {
15 fn get_from_tuple<'r, 'a>(self, tuple: &'r TupleAst<'a>) -> Option<&'r SpannedTypeAst<'a>> {
16 match self {
17 Self::Start(i) => tuple.start.get(i),
18 Self::Middle => tuple
19 .middle
20 .as_ref()
21 .map(|middle| middle.extra.element.as_ref()),
22 Self::End(i) => tuple.end.get(i),
23 }
24 }
25
26 fn get_from_destructure<'r, 'a>(
27 self,
28 destructure: &'r Destructure<'a, TypeAst<'a>>,
29 ) -> Option<SpannedLvalueTree<'r, 'a>> {
30 match self {
31 Self::Start(i) => destructure.start.get(i).map(LvalueTree::from_lvalue),
32 Self::Middle => destructure
33 .middle
34 .as_ref()
35 .and_then(|middle| match &middle.extra {
36 DestructureRest::Named { ty: Some(ty), .. } => Some(LvalueTree::from_span(ty)),
37 DestructureRest::Named { variable, .. } => {
38 Some(LvalueTree::from_span(variable))
39 }
40 DestructureRest::Unnamed => None,
41 }),
42 Self::End(i) => destructure.end.get(i).map(LvalueTree::from_lvalue),
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
49#[non_exhaustive]
50pub enum ErrorPathFragment {
51 FnArg(Option<TupleIndex>),
54 FnReturnType,
56 TupleElement(Option<TupleIndex>),
59 ObjectField(String),
61 Lhs,
63 Rhs,
65}
66
67impl From<TupleIndex> for ErrorPathFragment {
68 fn from(index: TupleIndex) -> Self {
69 Self::TupleElement(Some(index))
70 }
71}
72
73impl From<&str> for ErrorPathFragment {
74 fn from(field_name: &str) -> Self {
75 Self::ObjectField(field_name.to_owned())
76 }
77}
78
79impl ErrorPathFragment {
80 pub(super) fn walk_expr<'a, T: Grammar>(
82 path: &[Self],
83 expr: &SpannedExpr<'a, T>,
84 ) -> Spanned<'a> {
85 let mut refined = Self::walk(path, expr, Self::step_into_expr);
86 while let Expr::TypeCast { value, .. } = &refined.extra {
87 refined = value.as_ref();
88 }
89 refined.with_no_extra()
90 }
91
92 fn walk<T: Copy>(mut path: &[Self], init: T, refine: impl Fn(&Self, T) -> Option<T>) -> T {
93 let mut refined = init;
94 while !path.is_empty() {
95 if let Some(refinement) = refine(&path[0], refined) {
96 refined = refinement;
97 path = &path[1..];
98 } else {
99 break;
100 }
101 }
102 refined
103 }
104
105 fn step_into_expr<'r, 'a, T: Grammar>(
106 &self,
107 mut expr: &'r SpannedExpr<'a, T>,
108 ) -> Option<&'r SpannedExpr<'a, T>> {
109 while let Expr::TypeCast { value, .. } = &expr.extra {
110 expr = value.as_ref();
111 }
112
113 match self {
114 Self::FnArg(Some(TupleIndex::Start(index))) => match &expr.extra {
116 Expr::Function { args, .. } => Some(&args[*index]),
117 Expr::Method { receiver, args, .. } => Some(if *index == 0 {
118 receiver.as_ref()
119 } else {
120 &args[*index - 1]
121 }),
122 _ => None,
123 },
124
125 Self::Lhs => {
126 if let Expr::Binary { lhs, .. } = &expr.extra {
127 Some(lhs.as_ref())
128 } else {
129 None
130 }
131 }
132 Self::Rhs => {
133 if let Expr::Binary { rhs, .. } = &expr.extra {
134 Some(rhs.as_ref())
135 } else {
136 None
137 }
138 }
139
140 Self::TupleElement(Some(TupleIndex::Start(index))) => {
141 if let Expr::Tuple(elements) = &expr.extra {
142 Some(&elements[*index])
143 } else {
144 None
145 }
146 }
147
148 _ => None,
149 }
150 }
151
152 pub(super) fn walk_lvalue<'a>(
153 path: &[Self],
154 lvalue: &SpannedLvalue<'a, TypeAst<'a>>,
155 ) -> Spanned<'a> {
156 let lvalue = LvalueTree::from_lvalue(lvalue);
157 Self::walk(path, lvalue, Self::step_into_lvalue).with_no_extra()
158 }
159
160 pub(super) fn walk_destructure<'a>(
161 path: &[Self],
162 destructure: &Spanned<'a, Destructure<'a, TypeAst<'a>>>,
163 ) -> Spanned<'a> {
164 let destructure = LvalueTree::from_span(destructure);
165 Self::walk(path, destructure, Self::step_into_lvalue).with_no_extra()
166 }
167
168 fn step_into_lvalue<'r, 'a>(
169 &self,
170 lvalue: SpannedLvalueTree<'r, 'a>,
171 ) -> Option<SpannedLvalueTree<'r, 'a>> {
172 match lvalue.extra {
173 LvalueTree::Type(ty) => self.step_into_type(ty),
174 LvalueTree::Destructure(destructure) => self.step_into_destructure(destructure),
175 LvalueTree::JustSpan => None,
176 }
177 }
178
179 fn step_into_type<'r, 'a>(&self, ty: &'r TypeAst<'a>) -> Option<SpannedLvalueTree<'r, 'a>> {
180 match (self, ty) {
181 (Self::TupleElement(Some(i)), TypeAst::Tuple(tuple)) => {
182 i.get_from_tuple(tuple).map(LvalueTree::from_span)
183 }
184 (Self::TupleElement(Some(TupleIndex::Middle)), TypeAst::Slice(slice)) => {
185 Some(LvalueTree::from_span(&slice.element))
186 }
187 _ => None,
188 }
189 }
190
191 fn step_into_destructure<'r, 'a>(
192 &self,
193 destructure: &'r Destructure<'a, TypeAst<'a>>,
194 ) -> Option<SpannedLvalueTree<'r, 'a>> {
195 match self {
196 Self::TupleElement(Some(i)) => i.get_from_destructure(destructure),
197 _ => None,
198 }
199 }
200}
201
202#[derive(Debug, Clone, Copy)]
204enum LvalueTree<'r, 'a> {
205 Destructure(&'r Destructure<'a, TypeAst<'a>>),
206 Type(&'r TypeAst<'a>),
207 JustSpan,
208}
209
210type SpannedLvalueTree<'r, 'a> = Spanned<'a, LvalueTree<'r, 'a>>;
211
212impl<'r, 'a> From<&'r Destructure<'a, TypeAst<'a>>> for LvalueTree<'r, 'a> {
213 fn from(destructure: &'r Destructure<'a, TypeAst<'a>>) -> Self {
214 Self::Destructure(destructure)
215 }
216}
217
218impl<'r, 'a> From<&'r TypeAst<'a>> for LvalueTree<'r, 'a> {
219 fn from(ty: &'r TypeAst<'a>) -> Self {
220 Self::Type(ty)
221 }
222}
223
224impl<'r> From<&'r ()> for LvalueTree<'r, '_> {
225 fn from((): &'r ()) -> Self {
226 Self::JustSpan
227 }
228}
229
230impl<'r, 'a> LvalueTree<'r, 'a> {
231 fn from_lvalue(lvalue: &'r Spanned<'a, Lvalue<'a, TypeAst<'a>>>) -> SpannedLvalueTree<'r, 'a> {
232 match &lvalue.extra {
233 Lvalue::Tuple(destructure) => lvalue.copy_with_extra(Self::Destructure(destructure)),
234 Lvalue::Variable { ty: Some(ty) } => ty.as_ref().map_extra(Self::Type),
235 _ => lvalue.copy_with_extra(Self::JustSpan),
236 }
237 }
238
239 fn from_span<T>(spanned: &'r Spanned<'a, T>) -> SpannedLvalueTree<'r, 'a>
240 where
241 &'r T: Into<Self>,
242 {
243 spanned.as_ref().map_extra(Into::into)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use arithmetic_parser::{
250 grammars::{NumGrammar, Parse},
251 Statement,
252 };
253
254 use super::*;
255 use crate::Annotated;
256
257 type F32Grammar = Annotated<NumGrammar<f32>>;
258
259 fn parse_expr(code: &str) -> SpannedExpr<'_, F32Grammar> {
260 *F32Grammar::parse_statements(code)
261 .unwrap()
262 .return_value
263 .unwrap()
264 }
265
266 fn parse_lvalue(code: &str) -> SpannedLvalue<'_, TypeAst<'_>> {
267 let statement = F32Grammar::parse_statements(code)
268 .unwrap()
269 .statements
270 .pop()
271 .unwrap()
272 .extra;
273 match statement {
274 Statement::Assignment { lhs, .. } => lhs,
275 _ => panic!("Unexpected statement type: {statement:?}"),
276 }
277 }
278
279 #[test]
280 fn walking_simple_expr() {
281 let expr = parse_expr("1 + (2, x)");
282 let path = &[ErrorPathFragment::Rhs, TupleIndex::Start(1).into()];
283 let located = ErrorPathFragment::walk_expr(path, &expr);
284
285 assert_eq!(*located.fragment(), "x");
286 }
287
288 #[test]
289 fn walking_expr_with_fn_call() {
290 let expr = parse_expr("hash(1, (false, 2), x)");
291 let path = &[
292 ErrorPathFragment::FnArg(Some(TupleIndex::Start(1))),
293 TupleIndex::Start(0).into(),
294 ];
295 let located = ErrorPathFragment::walk_expr(path, &expr);
296
297 assert_eq!(*located.fragment(), "false");
298 }
299
300 #[test]
301 fn walking_expr_with_method_call() {
302 let expr = parse_expr("xs.map(|x| x + 1)");
303 let path = &[ErrorPathFragment::FnArg(Some(TupleIndex::Start(0)))];
304 let located = ErrorPathFragment::walk_expr(path, &expr);
305
306 assert_eq!(*located.fragment(), "xs");
307
308 let other_path = &[ErrorPathFragment::FnArg(Some(TupleIndex::Start(1)))];
309 let other_located = ErrorPathFragment::walk_expr(other_path, &expr);
310
311 assert_eq!(*other_located.fragment(), "|x| x + 1");
312 }
313
314 #[test]
315 fn walking_expr_with_partial_match() {
316 let expr = parse_expr("hash(1, xs)");
317 let path = &[
318 ErrorPathFragment::FnArg(Some(TupleIndex::Start(1))),
319 TupleIndex::Start(0).into(),
320 ];
321 let located = ErrorPathFragment::walk_expr(path, &expr);
322
323 assert_eq!(*located.fragment(), "xs");
324 }
325
326 #[test]
327 fn walking_expr_with_intermediate_type_cast() {
328 let expr = parse_expr("hash(1, (xs, ys) as Pair)");
329 let path = &[
330 ErrorPathFragment::FnArg(Some(TupleIndex::Start(1))),
331 TupleIndex::Start(0).into(),
332 ];
333 let located = ErrorPathFragment::walk_expr(path, &expr);
334
335 assert_eq!(*located.fragment(), "xs");
336 }
337
338 #[test]
339 fn walking_expr_with_final_type_cast() {
340 let expr = parse_expr("hash(1, (xs as [_] as Slice, ys))");
341 let path = &[
342 ErrorPathFragment::FnArg(Some(TupleIndex::Start(1))),
343 TupleIndex::Start(0).into(),
344 ];
345 let located = ErrorPathFragment::walk_expr(path, &expr);
346
347 assert_eq!(*located.fragment(), "xs");
348 }
349
350 #[test]
351 fn walking_lvalue() {
352 let lvalue = parse_lvalue("((u, v), ...ys, _, z) = x;");
353 let start_path = &[ErrorPathFragment::from(TupleIndex::Start(0))];
354 let located_start = ErrorPathFragment::walk_lvalue(start_path, &lvalue);
355 assert_eq!(*located_start.fragment(), "(u, v)");
356
357 let embedded_path = &[
358 ErrorPathFragment::from(TupleIndex::Start(0)),
359 ErrorPathFragment::from(TupleIndex::Start(1)),
360 ];
361 let embedded = ErrorPathFragment::walk_lvalue(embedded_path, &lvalue);
362 assert_eq!(*embedded.fragment(), "v");
363
364 let middle_path = &[ErrorPathFragment::from(TupleIndex::Middle)];
365 let located_middle = ErrorPathFragment::walk_lvalue(middle_path, &lvalue);
366 assert_eq!(*located_middle.fragment(), "ys");
367
368 let end_path = &[ErrorPathFragment::from(TupleIndex::End(1))];
369 let located_end = ErrorPathFragment::walk_lvalue(end_path, &lvalue);
370 assert_eq!(*located_end.fragment(), "z");
371 }
372
373 #[test]
374 fn walking_lvalue_with_annotations() {
375 let lvalue = parse_lvalue("x: (Bool, ...[(Num, Bool); _]) = x;");
376 let start_path = &[ErrorPathFragment::from(TupleIndex::Start(0))];
377 let located_start = ErrorPathFragment::walk_lvalue(start_path, &lvalue);
378 assert_eq!(*located_start.fragment(), "Bool");
379
380 let middle_path = &[ErrorPathFragment::from(TupleIndex::Middle)];
381 let located_middle = ErrorPathFragment::walk_lvalue(middle_path, &lvalue);
382 assert_eq!(*located_middle.fragment(), "(Num, Bool)");
383
384 let narrowed_path = &[
385 ErrorPathFragment::from(TupleIndex::Middle),
386 TupleIndex::Start(0).into(),
387 ];
388 let located_ty = ErrorPathFragment::walk_lvalue(narrowed_path, &lvalue);
389 assert_eq!(*located_ty.fragment(), "Num");
390 }
391
392 #[test]
393 fn walking_lvalue_with_annotation_mix() {
394 let lvalue = parse_lvalue("(flag, y: [Num]) = x;");
395 let start_path = &[ErrorPathFragment::from(TupleIndex::Start(0))];
396 let located_start = ErrorPathFragment::walk_lvalue(start_path, &lvalue);
397 assert_eq!(*located_start.fragment(), "flag");
398
399 let slice_path = &[ErrorPathFragment::from(TupleIndex::Start(1))];
400 let located_slice = ErrorPathFragment::walk_lvalue(slice_path, &lvalue);
401 assert_eq!(*located_slice.fragment(), "[Num]");
402
403 let slice_elem_path = &[
404 ErrorPathFragment::from(TupleIndex::Start(1)),
405 ErrorPathFragment::from(TupleIndex::Middle),
406 ];
407 let located_slice_elem = ErrorPathFragment::walk_lvalue(slice_elem_path, &lvalue);
408 assert_eq!(*located_slice_elem.fragment(), "Num");
409 }
410
411 #[test]
412 fn walking_slice() {
413 let lvalue = parse_lvalue("xs: [(Num, Bool); _] = x;");
414 let slice_path = &[ErrorPathFragment::from(TupleIndex::Middle)];
415 let located_slice = ErrorPathFragment::walk_lvalue(slice_path, &lvalue);
416 assert_eq!(*located_slice.fragment(), "(Num, Bool)");
417
418 let narrow_path = &[
419 ErrorPathFragment::from(TupleIndex::Middle),
420 ErrorPathFragment::from(TupleIndex::Start(1)),
421 ];
422 let located_elem = ErrorPathFragment::walk_lvalue(narrow_path, &lvalue);
423 assert_eq!(*located_elem.fragment(), "Bool");
424 }
425}