Merge remote-tracking branch 'origin/main' into jess/cleaned-imports
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
use std::{collections::HashSet, str::FromStr};
|
||||
use std::{collections::HashSet, fmt, str::FromStr};
|
||||
|
||||
use regex::Regex;
|
||||
use tower_lsp::lsp_types::{
|
||||
@ -389,21 +389,23 @@ impl FnData {
|
||||
pub fn fn_signature(&self) -> String {
|
||||
let mut signature = String::new();
|
||||
|
||||
signature.push('(');
|
||||
for (i, arg) in self.args.iter().enumerate() {
|
||||
if i > 0 {
|
||||
signature.push_str(", ");
|
||||
}
|
||||
match &arg.kind {
|
||||
ArgKind::Special => signature.push_str(&format!("@{}", arg.name)),
|
||||
ArgKind::Labelled(false) => signature.push_str(&arg.name),
|
||||
ArgKind::Labelled(true) => signature.push_str(&format!("{}?", arg.name)),
|
||||
}
|
||||
if let Some(ty) = &arg.ty {
|
||||
signature.push_str(&format!(": {ty}"));
|
||||
if self.args.is_empty() {
|
||||
signature.push_str("()");
|
||||
} else if self.args.len() == 1 {
|
||||
signature.push('(');
|
||||
signature.push_str(&self.args[0].to_string());
|
||||
signature.push(')');
|
||||
} else {
|
||||
signature.push('(');
|
||||
for a in &self.args {
|
||||
signature.push_str("\n ");
|
||||
signature.push_str(&a.to_string());
|
||||
signature.push(',');
|
||||
}
|
||||
signature.push('\n');
|
||||
signature.push(')');
|
||||
}
|
||||
signature.push(')');
|
||||
|
||||
if let Some(ty) = &self.return_type {
|
||||
signature.push_str(&format!(": {ty}"));
|
||||
}
|
||||
@ -515,6 +517,20 @@ pub struct ArgData {
|
||||
pub docs: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ArgData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.kind {
|
||||
ArgKind::Special => write!(f, "@{}", self.name)?,
|
||||
ArgKind::Labelled(false) => f.write_str(&self.name)?,
|
||||
ArgKind::Labelled(true) => write!(f, "{}?", self.name)?,
|
||||
}
|
||||
if let Some(ty) = &self.ty {
|
||||
write!(f, ": {ty}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ArgKind {
|
||||
Special,
|
||||
@ -766,8 +782,8 @@ trait ApplyMeta {
|
||||
description = summary;
|
||||
summary = None;
|
||||
let d = description.as_mut().unwrap();
|
||||
d.push_str(l);
|
||||
d.push('\n');
|
||||
d.push_str(l);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -360,15 +360,6 @@ impl KclValue {
|
||||
result
|
||||
}
|
||||
|
||||
/// Put the number into a KCL value.
|
||||
pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
|
||||
Self::Number {
|
||||
value: f,
|
||||
meta,
|
||||
ty: NumericType::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn from_number_with_type(f: f64, ty: NumericType, meta: Vec<Metadata>) -> Self {
|
||||
Self::Number { value: f, meta, ty }
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub(super) static ref CHECK_NUMERIC_TYPES: bool = {
|
||||
pub(crate) static ref CHECK_NUMERIC_TYPES: bool = {
|
||||
let env_var = std::env::var("ZOO_NUM_TYS");
|
||||
let Ok(env_var) = env_var else {
|
||||
return false;
|
||||
@ -416,6 +416,80 @@ impl NumericType {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn combine_eq_array(input: &[TyF64]) -> (Vec<f64>, NumericType) {
|
||||
use NumericType::*;
|
||||
|
||||
let mut result = input.iter().map(|t| t.n).collect();
|
||||
|
||||
let mut ty = Any;
|
||||
// Invariant mismatch is true => ty is Known
|
||||
let mut mismatch = false;
|
||||
for i in input {
|
||||
if i.ty == Any || ty == i.ty {
|
||||
continue;
|
||||
}
|
||||
|
||||
match (&ty, &i.ty) {
|
||||
(Any, t) => {
|
||||
ty = t.clone();
|
||||
}
|
||||
(_, Unknown) | (Default { .. }, Default { .. }) => return (result, Unknown),
|
||||
|
||||
// Known types and compatible, but needs adjustment.
|
||||
(Known(UnitType::Length(_)), Known(UnitType::Length(_)))
|
||||
| (Known(UnitType::Angle(_)), Known(UnitType::Angle(_))) => {
|
||||
mismatch = true;
|
||||
}
|
||||
|
||||
// Known but incompatible.
|
||||
(Known(_), Known(_)) => return (result, Unknown),
|
||||
|
||||
// Known and unknown, no adjustment for counting numbers.
|
||||
(Known(UnitType::Count), Default { .. }) | (Default { .. }, Known(UnitType::Count)) => {
|
||||
ty = Known(UnitType::Count);
|
||||
}
|
||||
|
||||
(Known(UnitType::Length(l1)), Default { len: l2, .. }) => {
|
||||
mismatch |= l1 != l2;
|
||||
}
|
||||
(Known(UnitType::Angle(a1)), Default { angle: a2, .. }) => {
|
||||
mismatch |= a1 != a2;
|
||||
}
|
||||
|
||||
(Default { len: l1, .. }, Known(UnitType::Length(l2))) => {
|
||||
mismatch |= l1 != l2;
|
||||
ty = Known(UnitType::Length(*l2));
|
||||
}
|
||||
(Default { angle: a1, .. }, Known(UnitType::Angle(a2))) => {
|
||||
mismatch |= a1 != a2;
|
||||
ty = Known(UnitType::Angle(*a2));
|
||||
}
|
||||
|
||||
(Unknown, _) | (_, Any) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
if !mismatch {
|
||||
return (result, ty);
|
||||
}
|
||||
|
||||
result = result
|
||||
.into_iter()
|
||||
.zip(input)
|
||||
.map(|(n, i)| match (&ty, &i.ty) {
|
||||
(Known(UnitType::Length(l1)), Known(UnitType::Length(l2)) | Default { len: l2, .. }) => {
|
||||
l2.adjust_to(n, *l1)
|
||||
}
|
||||
(Known(UnitType::Angle(a1)), Known(UnitType::Angle(a2)) | Default { angle: a2, .. }) => {
|
||||
a2.adjust_to(n, *a1)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
(result, ty)
|
||||
}
|
||||
|
||||
/// Combine two types for multiplication-like operations.
|
||||
pub fn combine_mul(a: TyF64, b: TyF64) -> (f64, f64, NumericType) {
|
||||
use NumericType::*;
|
||||
@ -621,7 +695,7 @@ pub enum UnitLen {
|
||||
|
||||
impl UnitLen {
|
||||
fn adjust_to(self, value: f64, to: UnitLen) -> f64 {
|
||||
if self == to {
|
||||
if !*CHECK_NUMERIC_TYPES || self == to {
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -734,6 +808,11 @@ impl UnitAngle {
|
||||
fn adjust_to(self, value: f64, to: UnitAngle) -> f64 {
|
||||
use std::f64::consts::PI;
|
||||
use UnitAngle::*;
|
||||
|
||||
if !*CHECK_NUMERIC_TYPES {
|
||||
return value;
|
||||
}
|
||||
|
||||
match (self, to) {
|
||||
(Degrees, Degrees) => value,
|
||||
(Degrees, Radians) => (value / 180.0) * PI,
|
||||
@ -1847,11 +1926,16 @@ n = 10inch / 2mm
|
||||
o = 3mm / 3
|
||||
p = 3_ / 4
|
||||
q = 4inch / 2_
|
||||
|
||||
r = min(0, 3, 42)
|
||||
s = min(0, 3mm, -42)
|
||||
t = min(100, 3in, 142mm)
|
||||
u = min(3rad, 4in)
|
||||
"#;
|
||||
|
||||
let result = parse_execute(program).await.unwrap();
|
||||
if *CHECK_NUMERIC_TYPES {
|
||||
assert_eq!(result.exec_state.errors().len(), 2);
|
||||
assert_eq!(result.exec_state.errors().len(), 3);
|
||||
} else {
|
||||
assert!(result.exec_state.errors().is_empty());
|
||||
}
|
||||
@ -1861,7 +1945,9 @@ q = 4inch / 2_
|
||||
assert_value_and_type("c", &result, 13.0, NumericType::mm());
|
||||
assert_value_and_type("d", &result, 13.0, NumericType::mm());
|
||||
assert_value_and_type("e", &result, 13.0, NumericType::mm());
|
||||
assert_value_and_type("f", &result, 5.0, NumericType::mm());
|
||||
if *CHECK_NUMERIC_TYPES {
|
||||
assert_value_and_type("f", &result, 5.0, NumericType::mm());
|
||||
}
|
||||
|
||||
assert_value_and_type("g", &result, 20.0, NumericType::default());
|
||||
assert_value_and_type("h", &result, 20.0, NumericType::mm());
|
||||
@ -1871,9 +1957,30 @@ q = 4inch / 2_
|
||||
|
||||
assert_value_and_type("l", &result, 0.0, NumericType::count());
|
||||
assert_value_and_type("m", &result, 2.0, NumericType::count());
|
||||
assert_value_and_type("n", &result, 127.0, NumericType::count());
|
||||
if *CHECK_NUMERIC_TYPES {
|
||||
assert_value_and_type("n", &result, 127.0, NumericType::count());
|
||||
}
|
||||
assert_value_and_type("o", &result, 1.0, NumericType::mm());
|
||||
assert_value_and_type("p", &result, 1.0, NumericType::count());
|
||||
assert_value_and_type("q", &result, 2.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
|
||||
|
||||
assert_value_and_type("r", &result, 0.0, NumericType::default());
|
||||
assert_value_and_type("s", &result, -42.0, NumericType::mm());
|
||||
assert_value_and_type("t", &result, 3.0, NumericType::Known(UnitType::Length(UnitLen::Inches)));
|
||||
assert_value_and_type("u", &result, 3.0, NumericType::Unknown);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn bad_typed_arithmetic() {
|
||||
let program = r#"
|
||||
a = 1rad
|
||||
b = 180 / PI * a + 360
|
||||
"#;
|
||||
|
||||
let result = parse_execute(program).await.unwrap();
|
||||
|
||||
assert_value_and_type("a", &result, 1.0, NumericType::radians());
|
||||
// TODO type is not ideal
|
||||
assert_value_and_type("b", &result, 417.0, NumericType::radians());
|
||||
}
|
||||
}
|
||||
|
@ -523,15 +523,6 @@ impl Args {
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn make_user_val_from_f64(&self, f: f64) -> KclValue {
|
||||
KclValue::from_number(
|
||||
f,
|
||||
vec![Metadata {
|
||||
source_range: self.source_range,
|
||||
}],
|
||||
)
|
||||
}
|
||||
|
||||
pub(super) fn make_user_val_from_f64_with_type(&self, f: TyF64) -> KclValue {
|
||||
KclValue::from_number_with_type(
|
||||
f.n,
|
||||
|
@ -133,7 +133,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// ```no_run
|
||||
/// // Declare a function that sketches a decagon.
|
||||
/// fn decagon(radius) {
|
||||
/// // Each side of the decagon is turned this many degrees from the previous angle.
|
||||
/// // Each side of the decagon is turned this many radians from the previous angle.
|
||||
/// stepAngle = (1/10) * TAU
|
||||
///
|
||||
/// // Start the decagon sketch at this point.
|
||||
|
@ -6,18 +6,31 @@ use kcl_derive_docs::stdlib;
|
||||
use super::args::FromArgs;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::NumericType, ExecState, KclValue},
|
||||
execution::{
|
||||
types::{self, NumericType},
|
||||
ExecState, KclValue,
|
||||
},
|
||||
std::args::{Args, TyF64},
|
||||
CompilationError,
|
||||
};
|
||||
|
||||
/// Compute the remainder after dividing `num` by `div`.
|
||||
/// If `num` is negative, the result will be too.
|
||||
pub async fn rem(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let n = args.get_unlabeled_kw_arg("number to divide")?;
|
||||
let d = args.get_kw_arg("divisor")?;
|
||||
pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let n: TyF64 = args.get_unlabeled_kw_arg("number to divide")?;
|
||||
let d: TyF64 = args.get_kw_arg("divisor")?;
|
||||
|
||||
let (n, d, ty) = NumericType::combine_div(n, d);
|
||||
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
|
||||
// TODO suggest how to fix this
|
||||
exec_state.warn(CompilationError::err(
|
||||
args.source_range,
|
||||
"Remainder of numbers which have unknown or incompatible units.",
|
||||
));
|
||||
}
|
||||
let remainder = inner_rem(n, d);
|
||||
|
||||
Ok(args.make_user_val_from_f64(remainder))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(remainder, ty)))
|
||||
}
|
||||
|
||||
/// Compute the remainder after dividing `num` by `div`.
|
||||
@ -243,11 +256,19 @@ fn inner_ceil(num: f64) -> Result<f64, KclError> {
|
||||
}
|
||||
|
||||
/// Compute the minimum of the given arguments.
|
||||
pub async fn min(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array()?;
|
||||
pub async fn min(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array_with_types()?;
|
||||
let (nums, ty) = NumericType::combine_eq_array(&nums);
|
||||
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
|
||||
// TODO suggest how to fix this
|
||||
exec_state.warn(CompilationError::err(
|
||||
args.source_range,
|
||||
"Calling `min` on numbers which have unknown or incompatible units.",
|
||||
));
|
||||
}
|
||||
let result = inner_min(nums);
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
|
||||
}
|
||||
|
||||
/// Compute the minimum of the given arguments.
|
||||
@ -280,11 +301,19 @@ fn inner_min(args: Vec<f64>) -> f64 {
|
||||
}
|
||||
|
||||
/// Compute the maximum of the given arguments.
|
||||
pub async fn max(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array()?;
|
||||
pub async fn max(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let nums = args.get_number_array_with_types()?;
|
||||
let (nums, ty) = NumericType::combine_eq_array(&nums);
|
||||
if *types::CHECK_NUMERIC_TYPES && ty == NumericType::Unknown {
|
||||
// TODO suggest how to fix this
|
||||
exec_state.warn(CompilationError::err(
|
||||
args.source_range,
|
||||
"Calling `max` on numbers which have unknown or incompatible units.",
|
||||
));
|
||||
}
|
||||
let result = inner_max(nums);
|
||||
|
||||
Ok(args.make_user_val_from_f64(result))
|
||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
|
||||
}
|
||||
|
||||
/// Compute the maximum of the given arguments.
|
||||
|
Reference in New Issue
Block a user