Browse Source

Re-enable datetime/human

David Peter 1 year ago
parent
commit
558f68b4a0
3 changed files with 83 additions and 76 deletions
  1. 38 38
      examples/datetime_human_tests.nbt
  2. 30 30
      numbat/modules/datetime/human.nbt
  3. 15 8
      numbat/src/typechecker/mod.rs

+ 38 - 38
examples/datetime_human_tests.nbt

@@ -1,38 +1,38 @@
-# assert((0 second         -> human) == "0 seconds")
-# assert((1 second         -> human) == "1 second")
-# assert((5 second         -> human) == "5 seconds")
-# assert((1.5 second       -> human) == "1.500 seconds")
-
-# assert((60 seconds       -> human) == "1 minute")
-# assert((73 seconds       -> human) == "1 minute + 13 seconds")
-# assert((120 seconds      -> human) == "2 minutes")
-# assert((60.1 seconds     -> human) == "1 minute + 0.100 seconds")
-# assert((1 minute         -> human) == "1 minute")
-# assert((1.25 minute      -> human) == "1 minute + 15 seconds")
-# assert((2.5 minute       -> human) == "2 minutes + 30 seconds")
-
-# assert((1 hour           -> human) == "1 hour")
-# assert((1.5 hour         -> human) == "1 hour + 30 minutes")
-# assert((2 hour           -> human) == "2 hours")
-# assert((1 hour + 1 sec   -> human) == "1 hour + 1 second")
-
-# assert((1 day            -> human) == "1 day")
-# assert((1.37 day         -> human) == "1 day + 8 hours + 52 minutes + 48 seconds")
-
-# assert((1 week           -> human) == "7 days")
-# assert((1.5 weeks        -> human) == "10 days + 12 hours")
-# assert((2 weeks          -> human) == "14 days")
-
-# assert((1 sidereal_day   -> human) == "23 hours + 56 minutes + 4.090500 seconds")
-
-# assert((10000 days       -> human) == "10000 days")
-# assert((50 million days  -> human) == "50_000_000 days")
-
-# assert((1e12 days        -> human) == "1_000_000_000_000 days")
-# assert((1e15 days        -> human) == "1.0e+15 days")
-
-# assert((1 ms             -> human) == "0.001 seconds")
-# assert((1 µs             -> human) == "0.000001 seconds")
-# assert((1 ns             -> human) == "0.000000001 seconds")
-# assert((1234 ns          -> human) == "0.000001234 seconds")
-# assert((1s + 1234 ns     -> human) == "1.000001234 seconds")
+assert((0 second         -> human) == "0 seconds")
+assert((1 second         -> human) == "1 second")
+assert((5 second         -> human) == "5 seconds")
+assert((1.5 second       -> human) == "1.500 seconds")
+
+assert((60 seconds       -> human) == "1 minute")
+assert((73 seconds       -> human) == "1 minute + 13 seconds")
+assert((120 seconds      -> human) == "2 minutes")
+assert((60.1 seconds     -> human) == "1 minute + 0.100 seconds")
+assert((1 minute         -> human) == "1 minute")
+assert((1.25 minute      -> human) == "1 minute + 15 seconds")
+assert((2.5 minute       -> human) == "2 minutes + 30 seconds")
+
+assert((1 hour           -> human) == "1 hour")
+assert((1.5 hour         -> human) == "1 hour + 30 minutes")
+assert((2 hour           -> human) == "2 hours")
+assert((1 hour + 1 sec   -> human) == "1 hour + 1 second")
+
+assert((1 day            -> human) == "1 day")
+assert((1.37 day         -> human) == "1 day + 8 hours + 52 minutes + 48 seconds")
+
+assert((1 week           -> human) == "7 days")
+assert((1.5 weeks        -> human) == "10 days + 12 hours")
+assert((2 weeks          -> human) == "14 days")
+
+assert((1 sidereal_day   -> human) == "23 hours + 56 minutes + 4.090500 seconds")
+
+assert((10000 days       -> human) == "10000 days")
+assert((50 million days  -> human) == "50_000_000 days")
+
+assert((1e12 days        -> human) == "1_000_000_000_000 days")
+assert((1e15 days        -> human) == "1.0e+15 days")
+
+assert((1 ms             -> human) == "0.001 seconds")
+assert((1 µs             -> human) == "0.000001 seconds")
+assert((1 ns             -> human) == "0.000000001 seconds")
+assert((1234 ns          -> human) == "0.000001234 seconds")
+assert((1s + 1234 ns     -> human) == "1.000001234 seconds")

+ 30 - 30
numbat/modules/datetime/human.nbt

@@ -1,39 +1,39 @@
-# use core::functions
-# use core::strings
-# use units::si
-# use datetime::functions
+use core::functions
+use core::strings
+use units::si
+use datetime::functions
 
-# fn _human_num_days(time: Time) -> Scalar = floor(time / day)
+fn _human_num_days(time: Time) -> Scalar = floor(time / day)
 
-# fn _human_join(a: String, b: String) -> String =
-#   if str_slice(a, 0, 2) == "0 " then b else if str_slice(b, 0, 2) == "0 " then a else "{a} + {b}"
+fn _human_join(a: String, b: String) -> String =
+  if str_slice(a, 0, 2) == "0 " then b else if str_slice(b, 0, 2) == "0 " then a else "{a} + {b}"
 
-# fn _remove_plural_suffix(str: String) -> String =
-#   if str_slice(str, 0, 2) == "1 " then str_slice(str, 0, str_length(str) - 1) else str
+fn _remove_plural_suffix(str: String) -> String =
+  if str_slice(str, 0, 2) == "1 " then str_slice(str, 0, str_length(str) - 1) else str
 
-# fn _human_seconds(dt: DateTime) -> String =
-#   _remove_plural_suffix(format_datetime("%-S%.f seconds", dt))
+fn _human_seconds(dt: DateTime) -> String =
+  _remove_plural_suffix(format_datetime("%-S%.f seconds", dt))
 
-# fn _human_minutes(dt: DateTime) -> String =
-#   _remove_plural_suffix(format_datetime("%-M minutes", dt))
+fn _human_minutes(dt: DateTime) -> String =
+  _remove_plural_suffix(format_datetime("%-M minutes", dt))
 
-# fn _human_hours(dt: DateTime) -> String =
-#   _remove_plural_suffix(format_datetime("%-H hours", dt))
+fn _human_hours(dt: DateTime) -> String =
+  _remove_plural_suffix(format_datetime("%-H hours", dt))
 
-# fn _human_days(num_days: Scalar) -> String =
-#   _remove_plural_suffix("{num_days} days")
+fn _human_days(num_days: Scalar) -> String =
+  _remove_plural_suffix("{num_days} days")
 
-# fn _human_readable_duration(time: Time, dt: DateTime, num_days: Scalar) -> String =
-#     _human_join(_human_join(_human_join(_human_days(_human_num_days(time)), _human_hours(dt)), _human_minutes(dt)), _human_seconds(dt))
+fn _human_readable_duration(time: Time, dt: DateTime, num_days: Scalar) -> String =
+    _human_join(_human_join(_human_join(_human_days(_human_num_days(time)), _human_hours(dt)), _human_minutes(dt)), _human_seconds(dt))
 
-# # Implementation details:
-# # we skip hours/minutes/seconds for durations larger than 1000 days because:
-# #   (a) we run into floating point precision problems at the nanosecond level at this point
-# #   (b) for much larger numbers, we can't convert to DateTimes anymore
-# @name("Human-readable time duration")
-# @url("https://numbat.dev/doc/date-and-time.html")
-# @description("Converts a time duration to a human-readable string in days, hours, minutes and seconds.")
-# fn human(time: Time) =
-#   if _human_num_days(time) > 1000
-#     then "{_human_num_days(time)} days" 
-#     else _human_readable_duration(time, datetime("0001-01-01T00:00:00Z") + time, _human_num_days(time))
+# Implementation details:
+# we skip hours/minutes/seconds for durations larger than 1000 days because:
+#   (a) we run into floating point precision problems at the nanosecond level at this point
+#   (b) for much larger numbers, we can't convert to DateTimes anymore
+@name("Human-readable time duration")
+@url("https://numbat.dev/doc/date-and-time.html")
+@description("Converts a time duration to a human-readable string in days, hours, minutes and seconds.")
+fn human(time: Time) =
+  if _human_num_days(time) > 1000
+    then "{_human_num_days(time)} days" 
+    else _human_readable_duration(time, datetime("0001-01-01T00:00:00Z") + time, _human_num_days(time))

+ 15 - 8
numbat/src/typechecker/mod.rs

@@ -1279,13 +1279,26 @@ impl TypeChecker {
 
                 let mut typed_parameters = vec![];
                 for (parameter_span, parameter, type_annotation) in parameters {
-                    let parameter_type = typechecker_fn.fresh_type_variable();
-
                     let annotated_type = type_annotation
                         .as_ref()
                         .map(|a| typechecker_fn.type_from_annotation(a))
                         .transpose()?;
 
+                    let parameter_type = match &annotated_type {
+                        Some(annotated_type) if annotated_type.is_closed() => {
+                            annotated_type.clone()
+                        }
+                        Some(annotated_type) => {
+                            // TODO: can we do better than this?
+                            let parameter_type = typechecker_fn.fresh_type_variable();
+                            typechecker_fn
+                                .add_equal_constraint(&parameter_type, &annotated_type)
+                                .ok();
+                            parameter_type
+                        }
+                        None => typechecker_fn.fresh_type_variable(),
+                    };
+
                     if is_ffi_function && annotated_type.is_none() {
                         return Err(TypeCheckError::ForeignFunctionNeedsTypeAnnotations(
                             *parameter_span,
@@ -1293,12 +1306,6 @@ impl TypeChecker {
                         ));
                     }
 
-                    if let Some(annotated_type) = annotated_type {
-                        typechecker_fn
-                            .add_equal_constraint(&parameter_type, &annotated_type)
-                            .ok();
-                    }
-
                     typechecker_fn.env.add_scheme(
                         parameter.clone(),
                         TypeScheme::make_quantified(parameter_type.clone()),