geo_aid_script/
lib.rs

1//! Everything relating in any way to `GeoScript`. This is where the language gets parsed,
2//! compiled and optimized. All errors are defined and reported here as well. The largest
3//! module of Geo-AID
4
5use std::{
6    fmt::Display,
7    ops::{Deref, DerefMut, Div, Mul},
8};
9
10use crate::cli::{AnnotationKind, Change, DiagnosticData, Fix};
11use geo_aid_figure::math_string::SPECIAL_MATH;
12use num_traits::{One, Zero};
13use serde::Serialize;
14
15use self::parser::Type;
16use self::token::{number::CompExponent, NamedIdent, Span, Token};
17
18pub mod cli;
19pub mod figure;
20pub mod geometry;
21pub mod math;
22pub mod parser;
23pub mod token;
24pub mod unroll;
25
26/// A `GeoScript` error
27#[derive(Debug)]
28pub enum Error {
29    /// Invalid token in the figure script.
30    InvalidToken {
31        /// The token that was encountered.
32        token: Token,
33    },
34    /// Properties exncountered where not expected
35    UnexpectedProperties {
36        /// Where they were encountered.
37        error_span: Span,
38    },
39    /// Invalid (unsupported) character in the figure script.
40    InvalidCharacter {
41        /// The character that was encountered.
42        character: char,
43        /// Where it was encountered.
44        error_span: Span,
45    },
46    /// A newline character was found in a string
47    NewLineInString {
48        /// Where the error happened.
49        error_span: Span,
50    },
51    /// The given number is too large to be parsed.
52    NumberTooLarge {
53        /// Where the number is.
54        error_span: Span,
55    },
56    /// Found an explicit iterator with a single variant.
57    SingleVariantExplicitIterator {
58        /// The location of the iterator.
59        error_span: Span,
60    },
61    /// Unexpected end of script.
62    EndOfInput,
63    /// Found an undefined rule oprator.
64    UndefinedRuleOperator {
65        /// The operator
66        operator: NamedIdent,
67    },
68    /// Iterators with non-matching lengths
69    InconsistentIterators {
70        /// The first iterator's span.
71        first_span: Span,
72        /// The first iterator's length
73        first_length: usize,
74        /// The second iterator's span
75        occurred_span: Span,
76        /// he second iterator's length
77        occurred_length: usize,
78        /// The specific error span.
79        error_span: Span,
80    },
81    /// An iterator contains an iterator with the same id.
82    IteratorWithSameIdIterator {
83        /// Where exactly the error occurred
84        error_span: Span,
85        /// The parent iterator's span.
86        parent_span: Span,
87        /// The contained iterator's span.
88        contained_span: Span,
89    },
90    /// Inconsistent types in a rule or a binary operation.
91    InconsistentTypes {
92        /// The expected type
93        // boxes here to reduce size
94        expected: (Type, Box<Span>),
95        /// The recevied type.
96        got: (Type, Box<Span>),
97        /// Exactly where the error occurred.
98        error_span: Box<Span>,
99    },
100    /// A variable with the same name already exists
101    RedefinedVariable {
102        /// The first definition span
103        defined_at: Span,
104        /// The second definition span
105        error_span: Span,
106        /// The variable name
107        variable_name: String,
108    },
109    /// A variable of undefined type
110    UndefinedTypeVariable {
111        /// Where the variable is defined
112        definition: Span,
113    },
114    /// An undefined variable was referenced
115    UndefinedVariable {
116        /// The reference span
117        error_span: Span,
118        /// The variable name
119        variable_name: String,
120        /// The potentially intended name.
121        suggested: Option<String>,
122        /// Whether to suggest adding the complex numbers flag.
123        suggest_complex: bool,
124    },
125    /// An undefined function was referenced.
126    UndefinedFunction {
127        /// The reference span
128        error_span: Span,
129        /// The function name
130        function_name: String,
131        /// The potentially intended name.
132        suggested: Option<&'static str>,
133    },
134    /// An undefined method was referenced.
135    UndefinedMethod {
136        /// The reference sapn
137        error_span: Span,
138        /// Teh method name
139        function_name: String,
140        /// The potentially intended name.
141        suggested: Option<&'static str>,
142        /// The type the method was searched on.
143        on_type: Type,
144    },
145    /// An attempt to access a field of a value has been made.
146    FieldAccess {
147        /// The access span
148        error_span: Span,
149    },
150    /// An attempt to use an unsupported language feature was made.
151    FeatureNotSupported {
152        /// The exact error span
153        error_span: Span,
154        /// The name of the feature.
155        feature_name: &'static str,
156    },
157    /// Invalid argument count for a function
158    InvalidArgumentCount {
159        /// The call span
160        error_span: Span,
161        /// Expected possible numbers of arguments
162        expected: &'static [u8],
163        /// The count of arguments received.
164        got: u8,
165    },
166    /// An overload for a function was not found
167    OverloadNotFound {
168        /// The call span
169        error_span: Span,
170        /// The call arguments
171        params: Vec<Type>,
172        /// The function name
173        function_name: String,
174    },
175    /// Cannot unpack a type onto a point collection.
176    CannotUnpack {
177        /// The span of the unpack attempt
178        error_span: Span,
179        /// The type that cannot be unpacked.
180        ty: Type,
181        /// The desired collection length
182        length: usize,
183    },
184    /// There's no implicit conversion between two types.
185    ImplicitConversionDoesNotExist {
186        /// The conversion span
187        error_span: Span,
188        /// The type that is attempted to be converted
189        from: Type,
190        /// The target type
191        to: Type,
192    },
193    /// Invalid type of an operand of a binary operation
194    InvalidOperandType {
195        /// The operation span
196        error_span: Box<Span>,
197        /// The received type
198        got: (Type, Box<Span>),
199        /// The operand
200        op: String,
201    },
202    /// An unexpected iterator was found in a let statement.
203    LetStatUnexpectedIterator {
204        /// The variable span
205        var_span: Span,
206        /// The statement's span.
207        error_span: Span,
208    },
209    /// More than one iterator was found in a let statement.
210    LetStatMoreThanOneIterator {
211        /// The statement span
212        error_span: Span,
213        /// First iterator's span
214        first_span: Span,
215        /// Second iterator's span
216        second_span: Span,
217    },
218    /// There's a non-point value in a point collection
219    NonPointInPointCollection {
220        /// The point collection span
221        error_span: Span,
222        /// The non-point part of it.
223        received: (Span, Type),
224    },
225    /// An undefined flag was referenced
226    FlagDoesNotExist {
227        /// The flag name
228        flag_name: String,
229        /// The flag's span
230        flag_span: Span,
231        /// The full span.
232        error_span: Span,
233        /// The potential intended name
234        suggested: Option<&'static str>,
235    },
236    /// A flag set expected as a flag value.
237    FlagSetExpected {
238        /// The exact error span
239        error_span: Span,
240    },
241    /// A string was expected
242    StringExpected { error_span: Span },
243    /// A string or an identifier was expected.
244    StringOrIdentExpected { error_span: Span },
245    /// A non-raw string or an identifier was expected
246    NonRawStringOrIdentExpected { error_span: Span },
247    /// A bool value was expected.
248    BooleanExpected { error_span: Span },
249    /// A number value was expected
250    NumberExpected { error_span: Span },
251    /// The provided identifier cannot be converted into a math string.
252    InvalidIdentMathString { error_span: Span },
253    /// A flag's value was set more than once
254    RedefinedFlag {
255        /// The exact error span
256        error_span: Span,
257        /// The first definition's span
258        first_defined: Span,
259        /// Name of the flag
260        flag_name: &'static str,
261    },
262    /// Invalid value for an enumeration
263    EnumInvalidValue {
264        /// The value span
265        error_span: Span,
266        /// The possible enum values.
267        available_values: &'static [&'static str],
268        /// The received, invalid value.
269        received_value: String,
270    },
271    /// A flag that must be set was not set.
272    RequiredFlagNotSet {
273        /// The flag's name
274        flag_name: &'static str,
275        /// The reason why it's required
276        required_because: Span,
277        /// The flag's definition span, if any
278        definition_span: Option<Span>,
279        /// The possible flag values.
280        available_values: &'static [&'static str],
281    },
282    /// A comparison between two values of a type does not exist
283    ComparisonDoesNotExist {
284        error_span: Span,
285        /// The problematic type
286        ty: Type,
287    },
288    /// An empty lable was found
289    EmptyLabel { error_span: Span },
290    /// There's an unclosed special character in a math string
291    UnclosedSpecial {
292        error_span: Span,
293        /// The longest parsed special character
294        parsed_special: String,
295    },
296    /// An undefined special character was referenced
297    SpecialNotRecognised {
298        error_span: Span,
299        /// The read code
300        code: String,
301        /// The potentially intended code.
302        suggested: Option<String>,
303    },
304    /// There's an unclosed string
305    UnclosedString { error_span: Span },
306    /// An index was found inside another index, in a math string
307    LabelIndexInsideIndex { error_span: Span },
308    /// An unexpected property was found
309    UnexpectedDisplayOption {
310        error_span: Span,
311        /// The unexpected property
312        option: String,
313        /// The potentially intended property
314        suggested: Option<&'static str>,
315    },
316    /// A property was repeated
317    RepeatedDisplayOption {
318        /// The repeated property span
319        error_span: Span,
320        /// The first property span
321        first_span: Span,
322        /// The repeated option
323        option: String,
324    },
325    /// An invalid Point collection
326    InvalidPC { error_span: Span },
327    /// A denominator of zero
328    ZeroDenominator { error_span: Span },
329    /// A function name was expected
330    ExpectedFunction { error_span: Span },
331}
332
333impl Error {
334    /// Match against `ImplicitConversonDoesNotExist`
335    #[must_use]
336    pub fn as_implicit_does_not_exist(&self) -> Option<(&Span, &Type, &Type)> {
337        match self {
338            Self::ImplicitConversionDoesNotExist {
339                error_span,
340                from,
341                to,
342            } => Some((error_span, from, to)),
343            _ => None,
344        }
345    }
346
347    /// Convert the error to a diagnostic
348    #[must_use]
349    #[allow(clippy::too_many_lines)]
350    pub fn diagnostic(self) -> DiagnosticData {
351        match self {
352            Self::InvalidToken { token } => {
353                DiagnosticData::new(&format!("invalid token: `{token}`")).add_span(token.get_span())
354            }
355            Self::UnexpectedProperties { error_span } => {
356                DiagnosticData::new(&"unexpected properties (display options)").add_span(error_span)
357            }
358            Self::InvalidCharacter {
359                character,
360                error_span,
361            } => DiagnosticData::new(&format!("invalid character: `{character}`"))
362                .add_span(error_span),
363            Self::NewLineInString {
364                error_span,
365            } => DiagnosticData::new(&"newline in string")
366                .add_span(error_span),
367            Self::EndOfInput => DiagnosticData::new("unexpected end of input"),
368            Self::UndefinedRuleOperator { operator } => {
369                DiagnosticData::new(&format!("undefined rule operator: `{}`", operator.ident))
370                    .add_span(operator.span)
371            }
372            Self::InconsistentIterators {
373                first_span,
374                first_length,
375                occurred_span,
376                occurred_length,
377                error_span,
378            } => DiagnosticData::new(&"inconsistent iterator lengths")
379                .add_span(error_span)
380                .add_annotation(
381                    first_span,
382                    AnnotationKind::Note,
383                    &format!("First iterator with length {first_length} here."),
384                )
385                .add_annotation(
386                    occurred_span,
387                    AnnotationKind::Note,
388                    &format!("Inconsistency (iterator with length {occurred_length}) here."),
389                ),
390            Self::InconsistentTypes {
391                expected,
392                got,
393                error_span,
394            } => DiagnosticData::new("inconsistent types")
395                .add_span(*error_span)
396                .add_annotation(
397                    *expected.1,
398                    AnnotationKind::Note,
399                    &format!("This expression is of type {}", expected.0),
400                )
401                .add_annotation(
402                    *got.1,
403                    AnnotationKind::Note,
404                    &format!("This expression is of type {}", got.0),
405                ),
406            Self::RedefinedVariable {
407                defined_at,
408                error_span,
409                variable_name,
410            } => DiagnosticData::new(&format!("redefined variable: `{variable_name}`"))
411                .add_span(error_span)
412                .add_annotation(defined_at, AnnotationKind::Note, "First defined here."),
413            Self::UndefinedTypeVariable { definition } => {
414                DiagnosticData::new("variable of undefined type")
415                    .add_span(definition)
416                    .add_annotation(definition, AnnotationKind::Note, "Defined here.")
417            }
418            Self::UndefinedVariable {
419                error_span,
420                variable_name,
421                suggested,
422                suggest_complex
423            } => {
424                let message = suggested.map(|v| format!("did you mean: `{v}`?"));
425                let data = DiagnosticData::new(&format!("undefined variable: `{variable_name}`"))
426                    .add_span(error_span)
427                    .add_annotation_opt_msg(error_span, AnnotationKind::Help, message.as_ref());
428
429                if suggest_complex {
430                    data.add_fix(Fix {
431                        changes: vec![Change {
432                            span: span!(1, 1, 1, 1),
433                            new_content: vec![String::from("@language.complex_numbers: true;"), String::new()]
434                        }],
435                        message: String::from("Did you want to use a complex unit? If so, declare it with a flag.")
436                    })
437                } else {
438                    data
439                }
440            }
441            Self::UndefinedFunction {
442                error_span,
443                function_name,
444                suggested
445            } => {
446                let message = suggested.map(|v| format!("did you mean: `{v}`?"));
447                DiagnosticData::new(&format!("function `{function_name}` not found"))
448                    .add_span(error_span)
449                    .add_annotation_opt_msg(error_span, AnnotationKind::Help, message.as_ref())
450            }
451            Self::UndefinedMethod {
452                error_span,
453                function_name,
454                suggested,
455                on_type
456            } => {
457                let message = suggested.map(|v| format!("did you mean: `{v}`?"));
458                DiagnosticData::new(&format!("method `{function_name}` not found on type {on_type}"))
459                    .add_span(error_span)
460                    .add_annotation_opt_msg(error_span, AnnotationKind::Help, message.as_ref())
461            }
462            Self::FieldAccess { error_span } => {
463                DiagnosticData::new("GeoScript has no fields. Did you mean to call a method?")
464                .add_span(error_span)
465            }
466            Self::FeatureNotSupported {
467                error_span,
468                feature_name,
469            } => {
470                DiagnosticData::new(&format!("feature `{feature_name}` not supported"))
471                    .add_span(error_span)
472            }
473            Self::InvalidArgumentCount {
474                error_span,
475                expected,
476                got,
477            } => {
478                DiagnosticData::new(&format!("invalid argument count. Expected one of `{expected:?}`, got {got}"))
479                    .add_span(error_span)
480            }
481            Self::OverloadNotFound {
482                error_span,
483                params,
484                function_name,
485            } => {
486                DiagnosticData::new(&format!("overload for function `{function_name}` with params `({})` not found", params.into_iter().map(|x| format!("{x}")).collect::<Vec<String>>().join(", ")))
487                    .add_span(error_span)
488            },
489            Self::CannotUnpack { error_span, ty, length } => {
490                DiagnosticData::new(&format!("could not unpack `{ty}` onto a point collection of length {length}"))
491                    .add_span(error_span)
492            }
493            Self::ImplicitConversionDoesNotExist {
494                error_span,
495                from,
496                to,
497            } => {
498                DiagnosticData::new(&format!("implicit conversion from `{from}` to `{to}` does not exist."))
499                    .add_span(error_span)
500            }
501            Self::InvalidOperandType {
502                error_span,
503                got,
504                op,
505            } => {
506                DiagnosticData::new(&format!("invalid operand type `{}` for operator `{op}`", got.0))
507                    .add_span(*error_span)
508                    .add_annotation(*got.1, AnnotationKind::Note, "this is of invalid type")
509            }
510            Self::LetStatUnexpectedIterator {
511                var_span,
512                error_span,
513            } => {
514                DiagnosticData::new(&"unexpected iterator in right-hand side of `let` statement")
515                    .add_span(error_span)
516                    .add_annotation(var_span, AnnotationKind::Note, "there was no iterator of left-hand side, so the same is expected for the right")
517            }
518            Self::IteratorWithSameIdIterator { error_span, parent_span, contained_span } => {
519                DiagnosticData::new(&"an iterator with an id of `x` must not contain an iterator with an id of `x`")
520                    .add_span(error_span)
521                    .add_annotation(parent_span, AnnotationKind::Note, "parent iterator here")
522                    .add_annotation(contained_span, AnnotationKind::Note, "child iterator here")
523            }
524            Self::LetStatMoreThanOneIterator { error_span, first_span, second_span } => {
525                DiagnosticData::new(&"right hand side of a let statement must contain at most a single level of iteration")
526                    .add_span(error_span)
527                    .add_annotation(first_span, AnnotationKind::Note, "first iterator here")
528                    .add_annotation(second_span, AnnotationKind::Note, "second iterator here")
529            }
530            Self::NumberTooLarge { error_span } => {
531                DiagnosticData::new(&"number too large")
532                    .add_span(error_span)
533            }
534            Self::SingleVariantExplicitIterator { error_span } => {
535                DiagnosticData::new(&"explicit iterators must have at least two variants")
536                    .add_span(error_span)
537            }
538            Self::NonPointInPointCollection { error_span, received } => {
539                DiagnosticData::new(&"all values in a point collection constructor must be points")
540                    .add_span(error_span)
541                    .add_annotation(received.0, AnnotationKind::Note, &format!("value should be a point, received {}", received.1))
542            }
543            Self::FlagDoesNotExist { flag_name, flag_span, error_span, suggested } => {
544                let message = suggested.map(|v| format!("Did you mean: `{v}`?"));
545                DiagnosticData::new(&format!("compiler flag `{flag_name}` does not exist"))
546                    .add_span(error_span)
547                    .add_annotation(flag_span, AnnotationKind::Note, &"This does not exist.")
548                    .add_annotation_opt_msg(flag_span, AnnotationKind::Help, message.as_ref())
549            }
550            Self::FlagSetExpected { error_span } => {
551                DiagnosticData::new(&"expected a flag set ({...})")
552                    .add_span(error_span)
553            }
554            Self::StringExpected { error_span } => {
555                DiagnosticData::new(&"expected a string")
556                    .add_span(error_span)
557            }
558            Self::StringOrIdentExpected { error_span } => {
559                DiagnosticData::new(&"expected a string or an identifier")
560                    .add_span(error_span)
561            }
562            Self::NonRawStringOrIdentExpected { error_span } => {
563                DiagnosticData::new(&"expected a non-raw string or an identifier")
564                    .add_span(error_span)
565            }
566            Self::BooleanExpected { error_span } => {
567                DiagnosticData::new(&"expected a boolean value (enabled, disabled, on, off, true, false, 1 or 0)")
568                    .add_span(error_span)
569            }
570            Self::NumberExpected { error_span } => {
571                DiagnosticData::new(&"expected a number value")
572                    .add_span(error_span)
573            }
574            Self::InvalidIdentMathString { error_span } => {
575                DiagnosticData::new(&"invalid ident for a math string")
576                    .add_span(error_span)
577            }
578            Self::RedefinedFlag {
579                first_defined,
580                error_span,
581                flag_name,
582            } => DiagnosticData::new(&format!("redefined flag: `{flag_name}`"))
583                .add_span(error_span)
584                .add_annotation(first_defined, AnnotationKind::Note, "first defined here"),
585            Self::EnumInvalidValue { error_span, available_values, received_value } => {
586                DiagnosticData::new(&format!("invalid value for an enum flag or property: `{received_value}`"))
587                    .add_span(error_span)
588                    .add_annotation(error_span, AnnotationKind::Help, &format!("supported values: {}", available_values.iter().map(
589                        |v| format!("`{v}`")
590                    ).collect::<Vec<String>>().join(", ")))
591            }
592            Self::RequiredFlagNotSet { flag_name, required_because, definition_span: flagdef_span, available_values } => {
593                DiagnosticData::new(&format!("you must set a value for flag `{flag_name}`."))
594                    .add_annotation(required_because, AnnotationKind::Note, &"Required because of this line.")
595                    .add_annotation(required_because, AnnotationKind::Help, &format!("Possible values: {}", available_values.iter().map(
596                        |v| format!("`{v}`")
597                    ).collect::<Vec<String>>().join(", ")))
598                    .add_annotation_opt_span(flagdef_span, AnnotationKind::Note, &"Flag defined here")
599                    .add_fix(Fix {
600                        message: String::from("Consider defining this flag at the top of the file."),
601                        changes: vec![
602                            Change {
603                                span: span!(1, 1, 1, 1),
604                                new_content: vec![
605                                    format!("@{flag_name}: {};", available_values[0]),
606                                    String::new()
607                                ]
608                            }
609                        ]
610                    })
611            }
612            Self::ComparisonDoesNotExist { error_span, ty } => {
613                DiagnosticData::new(&format!("comparison between values of type {ty} does not exist."))
614                    .add_span(error_span)
615            }
616            Self::EmptyLabel { error_span } => {
617                DiagnosticData::new(&"labels cannot be empty.")
618                    .add_span(error_span)
619            }
620            Self::UnclosedSpecial { error_span, parsed_special } => {
621                // Try to figure out if any of the chars could show up here.
622                let found = SPECIAL_MATH.iter().find(|x| parsed_special.starts_with(*x));
623
624                let d = DiagnosticData::new(&"there's a missing ']' somewhere here. Braces denote special characters To escape a brace, use \\[.")
625                    .add_span(error_span);
626
627               if let Some(found) = found {
628                    d.add_fix(Fix {
629                        message: String::from("You may have forgotten to put a `]` here."),
630                        changes: vec![
631                            Change {
632                                span: span!(
633                                    error_span.start.line, error_span.start.column + found.len() + 1,
634                                    error_span.start.line, error_span.start.column + found.len() + 1
635                                ),
636                                new_content: vec![
637                                    String::from("]")
638                                ]
639                            }
640                        ]
641                    })
642                } else {
643                    d
644                }
645            }
646            Self::SpecialNotRecognised {
647                error_span,
648                code,
649                suggested
650            } => {
651                let message = suggested.map(|v| format!("Did you mean: `{v}`?"));
652                DiagnosticData::new(&format!("special code not recognised: `{code}`"))
653                    .add_span(error_span)
654                    .add_annotation_opt_msg(error_span, AnnotationKind::Help, message.as_ref())
655            }
656            Self::UnclosedString { error_span } => {
657                DiagnosticData::new(&"unclosed special tag")
658                    .add_span(error_span)
659            }
660            Self::LabelIndexInsideIndex { error_span } => {
661                DiagnosticData::new(&"lower index cannot be used inside another lower index")
662                    .add_span(error_span)
663            }
664            Self::UnexpectedDisplayOption { error_span, option, suggested } => {
665                let message = suggested.map(|v| format!("did you mean: `{v}`?"));
666                DiagnosticData::new(&format!("unexpected display option: `{option}`"))
667                    .add_span(error_span)
668                    .add_annotation_opt_msg(error_span, AnnotationKind::Help, message.as_ref())
669            }
670            Self::RepeatedDisplayOption { error_span, first_span, option } => {
671                DiagnosticData::new(&format!("repeated display option: `{option}`"))
672                    .add_span(error_span)
673                    .add_annotation(first_span, AnnotationKind::Help, &"first defined here")
674            }
675            Self::InvalidPC { error_span } => {
676                DiagnosticData::new(&"point collections in this place are ambiguous and therefore not valid")
677                    .add_span(error_span)
678            }
679            Self::ZeroDenominator { error_span } => {
680                DiagnosticData::new(&"denominator in a fraction cannot be equal to zero")
681                    .add_span(error_span)
682            }
683            Self::ExpectedFunction { error_span } => {
684                DiagnosticData::new(&"expected function, found, value")
685                    .add_span(error_span)
686            }
687        }
688    }
689}
690
691/// Defines a simple unit.
692#[derive(Clone, Copy, PartialEq, Eq, Debug)]
693pub enum SimpleUnit {
694    Distance,
695    Angle,
696    Unitless,
697}
698
699impl Mul for SimpleUnit {
700    type Output = ComplexUnit;
701
702    fn mul(self, rhs: Self) -> Self::Output {
703        let complex = ComplexUnit::new(self);
704
705        complex * rhs
706    }
707}
708
709/// How many different units are there, minus one for scalar
710const fn unit_count() -> usize {
711    SimpleUnit::Unitless as usize
712}
713
714/// Defines a complex unit: a product of simple units.
715#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Default, Hash)]
716pub struct ComplexUnit([CompExponent; unit_count()]);
717
718/// Unit constants
719pub mod unit {
720    use super::{ComplexUnit, SimpleUnit};
721
722    /// A distance
723    pub const DISTANCE: ComplexUnit = ComplexUnit::new(SimpleUnit::Distance);
724    /// An angle
725    pub const ANGLE: ComplexUnit = ComplexUnit::new(SimpleUnit::Angle);
726    /// A simple, unitless scalar.
727    pub const SCALAR: ComplexUnit = ComplexUnit::new(SimpleUnit::Unitless);
728}
729
730/// Type constants
731pub mod ty {
732    use super::{parser::Type, ComplexUnit, SimpleUnit};
733
734    /// The distance type
735    pub const DISTANCE: Type = Type::Number(Some(ComplexUnit::new(SimpleUnit::Distance)));
736    /// The point type
737    pub const POINT: Type = Type::Point;
738    /// The angle type
739    pub const ANGLE: Type = Type::Number(Some(ComplexUnit::new(SimpleUnit::Angle)));
740    /// The line type
741    pub const LINE: Type = Type::Line;
742    /// The circle type
743    pub const CIRCLE: Type = Type::Circle;
744    /// The unitless scalar type
745    pub const SCALAR: Type = Type::Number(Some(ComplexUnit::new(SimpleUnit::Unitless)));
746    /// The unknown-unit scalar type
747    pub const SCALAR_UNKNOWN: Type = Type::Number(None);
748
749    /// A point collection of given length. A length of 0 signifies a generic point collection
750    #[must_use]
751    pub const fn collection(length: usize) -> Type {
752        Type::PointCollection(length)
753    }
754
755    /// A derived type.
756    #[must_use]
757    pub const fn derived(t: &'static str) -> Type {
758        Type::Derived(t)
759    }
760}
761
762impl ComplexUnit {
763    /// Creates a new complex unit representing no unit.
764    #[must_use]
765    pub const fn new(simple: SimpleUnit) -> Self {
766        let mut arr = [CompExponent::new_raw(0, 1); unit_count()];
767
768        match simple {
769            SimpleUnit::Unitless => (),
770            _ => arr[simple as usize] = CompExponent::new_raw(1, 1),
771        }
772
773        Self(arr)
774    }
775
776    /// Raises the unit to a power
777    #[must_use]
778    pub fn pow(mut self, exp: CompExponent) -> Self {
779        for v in &mut self.0 {
780            *v *= exp;
781        }
782
783        self
784    }
785}
786
787impl Display for ComplexUnit {
788    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
789        let mut s = String::new();
790
791        for i in 0..(SimpleUnit::Unitless as usize) {
792            if !self.0[i].is_zero() {
793                let name = match i {
794                    0 => "Distance",
795                    1 => "Point",
796                    2 => "Angle",
797                    3 => "Line",
798                    _ => unreachable!(),
799                };
800
801                if self.0[i].is_one() {
802                    s += name;
803                } else {
804                    s += &format!("{name}^{}", self.0[i]);
805                };
806
807                s += " * ";
808            }
809        }
810
811        if s.is_empty() {
812            write!(f, "no unit")
813        } else {
814            write!(
815                f,
816                "{}",
817                String::from_utf8(s.as_bytes()[0..(s.len() - 3)].to_vec()).unwrap()
818            )
819        }
820    }
821}
822
823impl Mul<SimpleUnit> for ComplexUnit {
824    type Output = ComplexUnit;
825
826    fn mul(mut self, rhs: SimpleUnit) -> Self::Output {
827        match rhs {
828            SimpleUnit::Unitless => (),
829            // Clippy doesn't like exponentiation. Thanks, Clippy
830            #[allow(clippy::suspicious_arithmetic_impl)]
831            _ => self[rhs as usize] += 1,
832        }
833        self
834    }
835}
836
837impl Mul<&ComplexUnit> for ComplexUnit {
838    type Output = ComplexUnit;
839
840    fn mul(mut self, rhs: &Self) -> Self::Output {
841        self.iter_mut()
842            .enumerate()
843            .map(|(i, x)| *x += rhs[i])
844            .for_each(drop);
845        self
846    }
847}
848
849impl Div<SimpleUnit> for ComplexUnit {
850    type Output = ComplexUnit;
851
852    fn div(mut self, rhs: SimpleUnit) -> Self::Output {
853        match rhs {
854            SimpleUnit::Unitless => (),
855            // Oh, c'mon, Clippy
856            #[allow(clippy::suspicious_arithmetic_impl)]
857            _ => self[rhs as usize] -= 1,
858        }
859        self
860    }
861}
862
863impl Div<&ComplexUnit> for ComplexUnit {
864    type Output = ComplexUnit;
865
866    fn div(mut self, rhs: &Self) -> Self::Output {
867        self.iter_mut()
868            .enumerate()
869            .map(|(i, x)| *x -= rhs[i])
870            .for_each(drop);
871        self
872    }
873}
874
875impl Deref for ComplexUnit {
876    type Target = [CompExponent; unit_count()];
877
878    fn deref(&self) -> &Self::Target {
879        &self.0
880    }
881}
882
883impl DerefMut for ComplexUnit {
884    fn deref_mut(&mut self) -> &mut Self::Target {
885        &mut self.0
886    }
887}