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