1use std::{fmt::Display, str::FromStr};
5
6use crate::geometry::ValueEnum;
7use crate::math::{EntityKind, IndexMap, Reconstruct, ReconstructCtx, Reindex};
8use geo_aid_figure::math_string::{
9 MathChar, MathIndex, MathSpecial, MathString, ParseErrorKind, SPECIAL_MATH,
10};
11use geo_aid_figure::{Style, VarIndex};
12
13use crate::span;
14
15use super::math::Entity;
16use super::{
17 math,
18 parser::{FromProperty, Parse, PropertyValue},
19 token::{Ident, PointCollectionItem, Span},
20 unroll::most_similar,
21 Error,
22};
23
24#[derive(Debug, Clone)]
26pub struct PointItem {
27 pub id: VarIndex,
29 pub label: MathString,
31 pub display_dot: bool,
33}
34
35impl Reindex for PointItem {
36 fn reindex(&mut self, map: &IndexMap) {
37 self.id.reindex(map);
38 }
39}
40
41impl Reconstruct for PointItem {
42 fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
43 Self {
44 id: self.id.reconstruct(ctx),
45 ..self
46 }
47 }
48}
49
50impl From<PointItem> for Item {
51 fn from(value: PointItem) -> Self {
52 Self::Point(value)
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct CircleItem {
59 pub id: VarIndex,
61 pub label: MathString,
63 pub style: Style,
65}
66
67impl Reindex for CircleItem {
68 fn reindex(&mut self, map: &IndexMap) {
69 self.id.reindex(map);
70 }
71}
72
73impl Reconstruct for CircleItem {
74 fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
75 Self {
76 id: self.id.reconstruct(ctx),
77 ..self
78 }
79 }
80}
81
82impl From<CircleItem> for Item {
83 fn from(value: CircleItem) -> Self {
84 Self::Circle(value)
85 }
86}
87
88#[derive(Debug, Clone)]
90pub struct LineItem {
91 pub id: VarIndex,
93 pub label: MathString,
95 pub style: Style,
97}
98
99impl Reindex for LineItem {
100 fn reindex(&mut self, map: &IndexMap) {
101 self.id.reindex(map);
102 }
103}
104
105impl Reconstruct for LineItem {
106 fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
107 Self {
108 id: self.id.reconstruct(ctx),
109 ..self
110 }
111 }
112}
113
114impl From<LineItem> for Item {
115 fn from(value: LineItem) -> Self {
116 Self::Line(value)
117 }
118}
119
120#[derive(Debug, Clone)]
122pub struct RayItem {
123 pub p_id: VarIndex,
125 pub q_id: VarIndex,
127 pub label: MathString,
129 pub style: Style,
131}
132
133impl Reindex for RayItem {
134 fn reindex(&mut self, map: &IndexMap) {
135 self.p_id.reindex(map);
136 self.q_id.reindex(map);
137 }
138}
139
140impl Reconstruct for RayItem {
141 fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
142 Self {
143 p_id: self.p_id.reconstruct(ctx),
144 q_id: self.q_id.reconstruct(ctx),
145 ..self
146 }
147 }
148}
149
150impl From<RayItem> for Item {
151 fn from(value: RayItem) -> Self {
152 Self::Ray(value)
153 }
154}
155
156#[derive(Debug, Clone)]
158pub struct SegmentItem {
159 pub p_id: VarIndex,
161 pub q_id: VarIndex,
163 pub label: MathString,
165 pub style: Style,
167}
168
169impl From<SegmentItem> for Item {
170 fn from(value: SegmentItem) -> Self {
171 Self::Segment(value)
172 }
173}
174
175impl Reindex for SegmentItem {
176 fn reindex(&mut self, map: &IndexMap) {
177 self.p_id.reindex(map);
178 self.q_id.reindex(map);
179 }
180}
181
182impl Reconstruct for SegmentItem {
183 fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
184 Self {
185 p_id: self.p_id.reconstruct(ctx),
186 q_id: self.q_id.reconstruct(ctx),
187 ..self
188 }
189 }
190}
191
192#[derive(Debug, Clone)]
194pub enum Item {
195 Point(PointItem),
196 Circle(CircleItem),
197 Line(LineItem),
198 Ray(RayItem),
199 Segment(SegmentItem),
200}
201
202impl Reindex for Item {
203 fn reindex(&mut self, map: &IndexMap) {
204 match self {
205 Self::Point(v) => v.reindex(map),
206 Self::Circle(v) => v.reindex(map),
207 Self::Line(v) => v.reindex(map),
208 Self::Ray(v) => v.reindex(map),
209 Self::Segment(v) => v.reindex(map),
210 }
211 }
212}
213
214impl Reconstruct for Item {
215 fn reconstruct(self, ctx: &mut ReconstructCtx) -> Self {
216 match self {
217 Self::Point(v) => Self::Point(v.reconstruct(ctx)),
218 Self::Circle(v) => Self::Circle(v.reconstruct(ctx)),
219 Self::Line(v) => Self::Line(v.reconstruct(ctx)),
220 Self::Ray(v) => Self::Ray(v.reconstruct(ctx)),
221 Self::Segment(v) => Self::Segment(v.reconstruct(ctx)),
222 }
223 }
224}
225
226#[derive(Debug, Default, Clone)]
228pub struct Figure {
229 pub entities: Vec<EntityKind>,
231 pub variables: Vec<math::Expr<()>>,
233 pub items: Vec<Item>,
235}
236
237#[derive(Debug, Clone, Default)]
239pub struct Generated {
240 pub entities: Vec<Entity<ValueEnum>>,
242 pub variables: Vec<math::Expr<ValueEnum>>,
244 pub items: Vec<Item>,
246}
247
248#[derive(Debug, Clone)]
250pub struct SpannedMathString {
251 pub string: MathString,
252 pub span: Span,
253}
254
255impl SpannedMathString {
256 #[must_use]
258 pub fn new(span: Span) -> Self {
259 Self {
260 string: MathString::new(),
261 span,
262 }
263 }
264
265 #[must_use]
273 pub fn displayed_by_default(&self) -> Option<Self> {
274 let mut result = MathString::new();
275
276 let mut letter = String::new();
278
279 let mut chars = self.string.iter().copied().peekable();
280
281 while let Some(MathChar::Ascii(c)) = chars.peek().copied() {
282 chars.next();
283 letter.push(c);
284 }
285
286 if let Some(special) = MathSpecial::parse(&letter) {
287 if special.is_alphabetic() {
288 result.push(MathChar::Special(special));
289 } else {
290 return None;
291 }
292 } else if letter.len() == 1 {
293 result.push(MathChar::Ascii(letter.chars().next().unwrap()));
294 } else {
295 return None;
296 }
297
298 while Some(MathChar::Prime) == chars.peek().copied() {
299 chars.next();
300 result.push(MathChar::Prime);
301 }
302
303 if chars.next() == Some(MathChar::SetIndex(MathIndex::Lower)) {
304 result.push(MathChar::SetIndex(MathIndex::Lower));
305 for c in chars.by_ref() {
306 if c == MathChar::SetIndex(MathIndex::Normal) {
307 break;
308 }
309
310 result.push(c);
311 }
312 result.push(MathChar::SetIndex(MathIndex::Normal));
313 }
314
315 if chars.next().is_none() {
316 Some(Self {
317 string: result,
318 span: self.span,
319 })
320 } else {
321 None
322 }
323 }
324
325 pub fn parse(content: &str, content_span: Span) -> Result<Self, Error> {
330 let string = MathString::from_str(content).map_err(|err| {
331 let error_span = span!(
332 content_span.start.line,
333 content_span.start.column + err.span.start,
334 content_span.end.line,
335 content_span.end.column + err.span.end
336 );
337
338 match err.kind {
339 ParseErrorKind::SpecialNotRecognised(special) => {
340 let suggested = most_similar(&SPECIAL_MATH, &special).map(ToString::to_string);
341
342 Error::SpecialNotRecognised {
343 error_span,
344 code: special,
345 suggested,
346 }
347 }
348 ParseErrorKind::NestedIndex => Error::LabelIndexInsideIndex { error_span },
349 ParseErrorKind::UnclosedSpecialTag(special) => Error::UnclosedSpecial {
350 error_span,
351 parsed_special: special,
352 },
353 }
354 })?;
355
356 Ok(Self {
357 string,
358 span: content_span,
359 })
360 }
361
362 #[must_use]
363 pub fn is_empty(&self) -> bool {
364 self.string.is_empty()
365 }
366
367 #[must_use]
368 pub fn get_span(&self) -> Span {
369 self.span
370 }
371}
372
373impl FromProperty for SpannedMathString {
374 fn from_property(property: PropertyValue) -> Result<Self, Error> {
375 match property {
376 PropertyValue::Number(n) => Err(Error::StringOrIdentExpected {
377 error_span: n.get_span(),
378 }),
379 PropertyValue::Ident(i) => match i {
380 Ident::Collection(mut c) => {
381 if c.len() == 1 {
382 Ok(Self::from(c.collection.swap_remove(0)))
383 } else {
384 Err(Error::InvalidIdentMathString { error_span: c.span })
385 }
386 }
387 Ident::Named(n) => Self::parse(&n.ident, n.span)?
388 .displayed_by_default()
389 .ok_or(Error::InvalidIdentMathString { error_span: n.span }),
390 },
391 PropertyValue::String(s) => Self::parse(
392 &s.content,
393 span!(
394 s.span.start.line,
395 s.span.start.column + 1,
396 s.span.end.line,
397 s.span.end.column - 1
398 ),
399 ),
400 PropertyValue::RawString(s) => Ok(Self {
401 string: MathString::raw(&s.lit.content),
402 span: s.get_span(),
403 }),
404 }
405 }
406}
407
408impl From<PointCollectionItem> for SpannedMathString {
409 fn from(value: PointCollectionItem) -> Self {
410 let mut string = MathString::new();
411 string.push(MathChar::Ascii(value.letter));
412
413 if let Some(index) = value.index {
414 string.extend(index.chars().map(MathChar::Ascii));
415 }
416
417 string.extend([MathChar::Prime].repeat(value.primes.into()));
418
419 Self {
420 string,
421 span: value.span,
422 }
423 }
424}
425
426impl Display for SpannedMathString {
427 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
428 write!(f, "{}", self.string)
429 }
430}