Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve "incompatible dimension" errors #136

Merged
merged 1 commit into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions numbat/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pretty_dtoa = "0.3"
numbat-exchange-rates = { version = "0.1.0", path = "../numbat-exchange-rates" }
heck = { version = "0.4.1", features = ["unicode"] }
unicode-ident = "1.0.11"
unicode-width = "0.1.10"

[dev-dependencies]
approx = "0.5"
Expand Down
7 changes: 7 additions & 0 deletions numbat/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ pub type Exponent = Rational;

pub trait Power {
fn power(self, e: Exponent) -> Self;

fn invert(self) -> Self
where
Self: Sized,
{
self.power(Exponent::from_integer(-1))
}
}

pub fn pretty_exponent(e: &Exponent) -> String {
Expand Down
11 changes: 7 additions & 4 deletions numbat/src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use codespan_reporting::diagnostic::LabelStyle;

use crate::{
interpreter::RuntimeError, parser::ParseError, resolver::ResolverError,
typechecker::TypeCheckError, NameResolutionError,
interpreter::RuntimeError,
parser::ParseError,
resolver::ResolverError,
typechecker::{IncompatibleDimensionsError, TypeCheckError},
NameResolutionError,
};

pub type Diagnostic = codespan_reporting::diagnostic::Diagnostic<usize>;
Expand Down Expand Up @@ -81,15 +84,15 @@ impl ErrorDiagnostic for TypeCheckError {
TypeCheckError::UnknownCallable(span, _) => d.with_labels(vec![span
.diagnostic_label(LabelStyle::Primary)
.with_message("unknown callable")]),
TypeCheckError::IncompatibleDimensions {
TypeCheckError::IncompatibleDimensions(IncompatibleDimensionsError {
operation,
span_operation,
span_actual,
actual_type,
span_expected,
expected_type,
..
} => {
}) => {
let labels = vec![
span_operation
.diagnostic_label(LabelStyle::Secondary)
Expand Down
8 changes: 8 additions & 0 deletions numbat/src/dimension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ impl DimensionRegistry {
self.registry.get_base_representation_for_name(name)
}

pub fn get_derived_entry_names_for(
&self,
base_representation: &BaseRepresentation,
) -> Vec<String> {
self.registry
.get_derived_entry_names_for(base_representation)
}

pub fn add_base_dimension(&mut self, name: &str) -> Result<BaseRepresentation> {
self.registry.add_base_entry(name, ())?;
Ok(self
Expand Down
56 changes: 55 additions & 1 deletion numbat/src/product.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::ops::{Div, Mul};
use std::{
fmt::Display,
ops::{Div, Mul},
};

use crate::arithmetic::{Exponent, Power};
use itertools::Itertools;
use num_rational::Ratio;
use num_traits::Signed;

pub trait Canonicalize {
type MergeKey: PartialEq;
Expand All @@ -17,6 +21,56 @@ pub struct Product<Factor, const CANONICALIZE: bool = false> {
factors: Vec<Factor>,
}

impl<Factor: Power + Clone + Canonicalize + Ord + Display, const CANONICALIZE: bool>
Product<Factor, CANONICALIZE>
{
pub fn as_string<GetExponent>(
&self,
get_exponent: GetExponent,
times_separator: &'static str,
over_separator: &'static str,
) -> String
where
GetExponent: Fn(&Factor) -> Exponent,
{
let to_string = |fs: &[Factor]| -> String {
let mut result = String::new();
for factor in fs.iter() {
result.push_str(&factor.to_string());
result.push_str(times_separator);
}
result.trim_end_matches(times_separator).into()
};

let factors_positive: Vec<_> = self
.iter()
.filter(|f| get_exponent(*f).is_positive())
.cloned()
.collect();
let factors_negative: Vec<_> = self
.iter()
.filter(|f| !get_exponent(*f).is_positive())
.cloned()
.collect();

match (&factors_positive[..], &factors_negative[..]) {
(&[], &[]) => "".into(),
(&[], negative) => to_string(negative),
(positive, &[]) => to_string(positive),
(positive, [single_negative]) => format!(
"{}{over_separator}{}",
to_string(positive),
to_string(&[single_negative.clone().invert()])
),
(positive, negative) => format!(
"{}{over_separator}({})",
to_string(positive),
to_string(&negative.iter().map(|f| f.clone().invert()).collect_vec())
),
}
}
}

impl<Factor: Clone + Ord + Canonicalize, const CANONICALIZE: bool> Product<Factor, CANONICALIZE> {
pub fn unity() -> Self {
Self::from_factors([])
Expand Down
34 changes: 22 additions & 12 deletions numbat/src/registry.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{collections::HashMap, fmt::Display};

use itertools::Itertools;
use num_traits::Zero;
use thiserror::Error;

Expand Down Expand Up @@ -28,6 +29,12 @@ pub struct BaseIndex(isize);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct BaseRepresentationFactor(pub BaseEntry, pub Exponent);

impl Display for BaseRepresentationFactor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}{}", self.0, pretty_exponent(&self.1))
}
}

impl Canonicalize for BaseRepresentationFactor {
type MergeKey = BaseEntry;

Expand Down Expand Up @@ -56,20 +63,11 @@ pub type BaseRepresentation = Product<BaseRepresentationFactor, true>;

impl Display for BaseRepresentation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let num_factors = self.iter().count();

if num_factors == 0 {
write!(f, "Scalar")?;
if self.iter().count() == 0 {
f.write_str("Scalar")
} else {
for (n, BaseRepresentationFactor(name, exp)) in self.iter().enumerate() {
write!(f, "{}{}", name, pretty_exponent(exp))?;

if n != self.iter().count() - 1 {
write!(f, " × ")?;
}
}
f.write_str(&self.as_string(|f| f.1, " × ", " / "))
}
Ok(())
}
}

Expand Down Expand Up @@ -98,6 +96,18 @@ impl<Metadata> Registry<Metadata> {
Ok(())
}

pub fn get_derived_entry_names_for(
&self,
base_representation: &BaseRepresentation,
) -> Vec<String> {
sharkdp marked this conversation as resolved.
Show resolved Hide resolved
self.derived_entries
.iter()
.filter(|(_, br)| *br == base_representation)
.map(|(name, _)| name.clone())
.sorted_unstable()
.collect()
}

pub fn is_base_entry(&self, name: &str) -> bool {
self.base_entries.iter().any(|(n, _)| n == name)
}
Expand Down
Loading