1use std::ops;
4
5use crate::{
6 ast::TypeAst,
7 error::{Error, ErrorContext, ErrorKind, ErrorLocation},
8 PrimitiveType,
9};
10use arithmetic_parser::{grammars::Grammar, Destructure, Spanned, SpannedExpr, SpannedLvalue};
11
12#[derive(Debug)]
20pub struct OpErrors<'a, Prim: PrimitiveType> {
21 errors: Goat<'a, Vec<ErrorPrecursor<Prim>>>,
22 current_location: Vec<ErrorLocation>,
23}
24
25impl<Prim: PrimitiveType> OpErrors<'_, Prim> {
26 pub fn push(&mut self, kind: ErrorKind<Prim>) {
28 self.errors.push(ErrorPrecursor {
29 kind,
30 location: self.current_location.clone(),
31 });
32 }
33
34 pub fn check(&mut self, check: impl FnOnce(OpErrors<'_, Prim>)) -> bool {
37 let error_count = self.errors.len();
38 check(self.by_ref());
39 self.errors.len() == error_count
40 }
41
42 pub fn by_ref(&mut self) -> OpErrors<'_, Prim> {
44 OpErrors {
45 errors: Goat::Borrowed(&mut *self.errors),
46 current_location: self.current_location.clone(),
47 }
48 }
49
50 pub fn with_location(&mut self, location: impl Into<ErrorLocation>) -> OpErrors<'_, Prim> {
52 let mut current_location = self.current_location.clone();
53 current_location.push(location.into());
54 OpErrors {
55 errors: Goat::Borrowed(&mut *self.errors),
56 current_location,
57 }
58 }
59
60 pub(crate) fn push_location(&mut self, location: impl Into<ErrorLocation>) {
61 self.current_location.push(location.into());
62 }
63
64 pub(crate) fn pop_location(&mut self) {
65 self.current_location.pop().expect("Location is empty");
66 }
67
68 #[cfg(test)]
69 pub(crate) fn into_vec(self) -> Vec<ErrorKind<Prim>> {
70 let errors = match self.errors {
71 Goat::Owned(errors) => errors,
72 Goat::Borrowed(_) => panic!("Attempt to call `into_vec` for borrowed errors"),
73 };
74 errors.into_iter().map(|err| err.kind).collect()
75 }
76}
77
78impl<Prim: PrimitiveType> OpErrors<'static, Prim> {
79 pub(crate) fn new() -> Self {
80 Self {
81 errors: Goat::Owned(vec![]),
82 current_location: vec![],
83 }
84 }
85
86 pub(crate) fn contextualize<'a, T: Grammar<'a>>(
87 self,
88 span: &SpannedExpr<'a, T>,
89 context: impl Into<ErrorContext<Prim>>,
90 ) -> Vec<Error<'a, Prim>> {
91 let context = context.into();
92 self.do_contextualize(|item| item.into_expr_error(context.clone(), span))
93 }
94
95 fn do_contextualize<'a>(
96 self,
97 map_fn: impl Fn(ErrorPrecursor<Prim>) -> Error<'a, Prim>,
98 ) -> Vec<Error<'a, Prim>> {
99 let errors = match self.errors {
100 Goat::Owned(errors) => errors,
101 Goat::Borrowed(_) => unreachable!(),
102 };
103 errors.into_iter().map(map_fn).collect()
104 }
105
106 pub(crate) fn contextualize_assignment<'a>(
107 self,
108 span: &SpannedLvalue<'a, TypeAst<'a>>,
109 context: &ErrorContext<Prim>,
110 ) -> Vec<Error<'a, Prim>> {
111 if self.errors.is_empty() {
112 vec![]
113 } else {
114 self.do_contextualize(|item| item.into_assignment_error(context.clone(), span))
115 }
116 }
117
118 pub(crate) fn contextualize_destructure<'a>(
119 self,
120 span: &Spanned<'a, Destructure<'a, TypeAst<'a>>>,
121 create_context: impl FnOnce() -> ErrorContext<Prim>,
122 ) -> Vec<Error<'a, Prim>> {
123 if self.errors.is_empty() {
124 vec![]
125 } else {
126 let context = create_context();
127 self.do_contextualize(|item| item.into_destructure_error(context.clone(), span))
128 }
129 }
130}
131
132#[derive(Debug)]
134enum Goat<'a, T> {
135 Owned(T),
136 Borrowed(&'a mut T),
137}
138
139impl<T> ops::Deref for Goat<'_, T> {
140 type Target = T;
141
142 fn deref(&self) -> &Self::Target {
143 match self {
144 Self::Owned(value) => value,
145 Self::Borrowed(mut_ref) => mut_ref,
146 }
147 }
148}
149
150impl<T> ops::DerefMut for Goat<'_, T> {
151 fn deref_mut(&mut self) -> &mut Self::Target {
152 match self {
153 Self::Owned(value) => value,
154 Self::Borrowed(mut_ref) => *mut_ref,
155 }
156 }
157}
158
159#[derive(Debug)]
160struct ErrorPrecursor<Prim: PrimitiveType> {
161 kind: ErrorKind<Prim>,
162 location: Vec<ErrorLocation>,
163}
164
165impl<Prim: PrimitiveType> ErrorPrecursor<Prim> {
166 fn into_expr_error<'a, T: Grammar<'a>>(
167 self,
168 context: ErrorContext<Prim>,
169 root_expr: &SpannedExpr<'a, T>,
170 ) -> Error<'a, Prim> {
171 Error {
172 inner: ErrorLocation::walk_expr(&self.location, root_expr).copy_with_extra(self.kind),
173 root_span: root_expr.with_no_extra(),
174 context,
175 location: self.location,
176 }
177 }
178
179 fn into_assignment_error<'a>(
180 self,
181 context: ErrorContext<Prim>,
182 root_lvalue: &SpannedLvalue<'a, TypeAst<'a>>,
183 ) -> Error<'a, Prim> {
184 Error {
185 inner: ErrorLocation::walk_lvalue(&self.location, root_lvalue)
186 .copy_with_extra(self.kind),
187 root_span: root_lvalue.with_no_extra(),
188 context,
189 location: self.location,
190 }
191 }
192
193 fn into_destructure_error<'a>(
194 self,
195 context: ErrorContext<Prim>,
196 root_destructure: &Spanned<'a, Destructure<'a, TypeAst<'a>>>,
197 ) -> Error<'a, Prim> {
198 Error {
199 inner: ErrorLocation::walk_destructure(&self.location, root_destructure)
200 .copy_with_extra(self.kind),
201 root_span: root_destructure.with_no_extra(),
202 context,
203 location: self.location,
204 }
205 }
206}