1use core::ops;
4
5use arithmetic_parser::{grammars::Grammar, Destructure, Spanned, SpannedExpr, SpannedLvalue};
6
7use crate::{
8 alloc::Vec,
9 ast::TypeAst,
10 error::{Error, ErrorContext, ErrorKind, ErrorPathFragment},
11 PrimitiveType,
12};
13
14#[derive(Debug)]
22pub struct OpErrors<'a, Prim: PrimitiveType> {
23 errors: Goat<'a, Vec<ErrorPrecursor<Prim>>>,
24 current_path: Vec<ErrorPathFragment>,
25}
26
27impl<Prim: PrimitiveType> OpErrors<'_, Prim> {
28 pub fn push(&mut self, kind: ErrorKind<Prim>) {
30 self.errors.push(ErrorPrecursor {
31 kind,
32 path: self.current_path.clone(),
33 });
34 }
35
36 pub fn check(&mut self, check: impl FnOnce(OpErrors<'_, Prim>)) -> bool {
39 let error_count = self.errors.len();
40 check(self.by_ref());
41 self.errors.len() == error_count
42 }
43
44 pub fn by_ref(&mut self) -> OpErrors<'_, Prim> {
46 OpErrors {
47 errors: Goat::Borrowed(&mut *self.errors),
48 current_path: self.current_path.clone(),
49 }
50 }
51
52 pub fn join_path(&mut self, path: impl Into<ErrorPathFragment>) -> OpErrors<'_, Prim> {
54 let mut current_path = self.current_path.clone();
55 current_path.push(path.into());
56 OpErrors {
57 errors: Goat::Borrowed(&mut *self.errors),
58 current_path,
59 }
60 }
61
62 pub(crate) fn push_path_fragment(&mut self, path: impl Into<ErrorPathFragment>) {
63 self.current_path.push(path.into());
64 }
65
66 pub(crate) fn pop_path_fragment(&mut self) {
67 self.current_path.pop().expect("Location is empty");
68 }
69
70 #[cfg(test)]
71 pub(crate) fn into_vec(self) -> Vec<ErrorKind<Prim>> {
72 let Goat::Owned(errors) = self.errors else {
73 panic!("Attempt to call `into_vec` for borrowed errors");
74 };
75 errors.into_iter().map(|err| err.kind).collect()
76 }
77}
78
79impl<Prim: PrimitiveType> OpErrors<'static, Prim> {
80 pub(crate) fn new() -> Self {
81 Self {
82 errors: Goat::Owned(Vec::new()),
83 current_path: Vec::new(),
84 }
85 }
86
87 pub(crate) fn contextualize<T: Grammar>(
88 self,
89 span: &SpannedExpr<'_, T>,
90 context: impl Into<ErrorContext<Prim>>,
91 ) -> Vec<Error<Prim>> {
92 let context = context.into();
93 self.do_contextualize(|item| item.into_expr_error(context.clone(), span))
94 }
95
96 fn do_contextualize(
97 self,
98 map_fn: impl Fn(ErrorPrecursor<Prim>) -> Error<Prim>,
99 ) -> Vec<Error<Prim>> {
100 let Goat::Owned(errors) = self.errors else {
101 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<Prim>> {
111 if self.errors.is_empty() {
112 Vec::new()
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<Prim>> {
123 if self.errors.is_empty() {
124 Vec::new()
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 path: Vec<ErrorPathFragment>,
163}
164
165impl<Prim: PrimitiveType> ErrorPrecursor<Prim> {
166 fn into_expr_error<T: Grammar>(
167 self,
168 context: ErrorContext<Prim>,
169 root_expr: &SpannedExpr<'_, T>,
170 ) -> Error<Prim> {
171 Error {
172 inner: ErrorPathFragment::walk_expr(&self.path, root_expr)
173 .copy_with_extra(self.kind)
174 .into(),
175 root_location: root_expr.with_no_extra().into(),
176 context,
177 path: self.path,
178 }
179 }
180
181 fn into_assignment_error<'a>(
182 self,
183 context: ErrorContext<Prim>,
184 root_lvalue: &SpannedLvalue<'a, TypeAst<'a>>,
185 ) -> Error<Prim> {
186 Error {
187 inner: ErrorPathFragment::walk_lvalue(&self.path, root_lvalue)
188 .copy_with_extra(self.kind)
189 .into(),
190 root_location: root_lvalue.with_no_extra().into(),
191 context,
192 path: self.path,
193 }
194 }
195
196 fn into_destructure_error<'a>(
197 self,
198 context: ErrorContext<Prim>,
199 root_destructure: &Spanned<'a, Destructure<'a, TypeAst<'a>>>,
200 ) -> Error<Prim> {
201 Error {
202 inner: ErrorPathFragment::walk_destructure(&self.path, root_destructure)
203 .copy_with_extra(self.kind)
204 .into(),
205 root_location: root_destructure.with_no_extra().into(),
206 context,
207 path: self.path,
208 }
209 }
210}