Browse Source

Fix unit_of-related crashes

This is a hotfix for #521. It doesn't solve the underlying issue, but it
prevents Numbat from crashing, throwing a runtime error instead.
David Peter 1 year ago
parent
commit
21960902f7

+ 10 - 10
book/src/list-functions-other.md

@@ -89,36 +89,36 @@ fn is_finite<T: Dim>(n: T) -> Bool
 
 Defined in: `core::quantities`
 
-### `unit_of`
-Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.
+### `value_of`
+Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.
 
 ```nbt
-fn unit_of<T: Dim>(x: T) -> T
+fn value_of<T: Dim>(x: T) -> Scalar
 ```
 
 <details>
 <summary>Examples</summary>
 
-<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code"  onclick=" window.open('https://numbat.dev/?q=unit%5Fof%2820%20km%2Fh%29')""></button></div><code class="language-nbt hljs numbat">>>> unit_of(20 km/h)
+<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code"  onclick=" window.open('https://numbat.dev/?q=value%5Fof%2820%20km%2Fh%29')""></button></div><code class="language-nbt hljs numbat">>>> value_of(20 km/h)
 
-    = 1 km/h    [Velocity]
+    = 20
 </code></pre>
 
 </details>
 
-### `value_of`
-Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.
+### `unit_of`
+Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. Returns an error if the quantity is zero.
 
 ```nbt
-fn value_of<T: Dim>(x: T) -> Scalar
+fn unit_of<T: Dim>(x: T) -> T
 ```
 
 <details>
 <summary>Examples</summary>
 
-<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code"  onclick=" window.open('https://numbat.dev/?q=value%5Fof%2820%20km%2Fh%29')""></button></div><code class="language-nbt hljs numbat">>>> value_of(20 km/h)
+<pre><div class="buttons"><button class="fa fa-play play-button" title="Run this code" aria-label="Run this code"  onclick=" window.open('https://numbat.dev/?q=unit%5Fof%2820%20km%2Fh%29')""></button></div><code class="language-nbt hljs numbat">>>> unit_of(20 km/h)
 
-    = 20
+    = 1 km/h    [Velocity]
 </code></pre>
 
 </details>

+ 0 - 2
examples/tests/core.nbt

@@ -1,7 +1,5 @@
 # unit_of
 
-assert_eq(unit_of(0),            1)
-
 assert_eq(unit_of(1),            1)
 assert_eq(unit_of(1.2345),       1)
 

+ 3 - 1
examples/tests/numerics.nbt

@@ -20,7 +20,9 @@ assert_eq(fixed_point(f_sqrt3, 1, 1e-10), sqrt(3), 1e-10)
 # Differentiation
 
 assert_eq(diff(log, 2.0), 0.5, 1e-5)
-assert_eq(diff(sin, 0.0), 1.0, 1e-5)
+
+# TODO: Sadly, the following is not possible at the moment. See https://github.com/sharkdp/numbat/issues/521 for details
+# assert_eq(diff(sin, 0.0), 1.0, 1e-5)
 
 assert_eq(diff(sqrt, 1.0), 0.5, 1e-5)
 

+ 7 - 5
numbat/modules/core/quantities.nbt

@@ -1,9 +1,11 @@
 use core::scalar
-
-@description("Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.")
-@example("unit_of(20 km/h)")
-fn unit_of<T: Dim>(x: T) -> T
+use core::error
 
 @description("Extract the plain value of a quantity (the `20` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise.")
 @example("value_of(20 km/h)")
-fn value_of<T: Dim>(x: T) -> Scalar = x / unit_of(x)
+fn value_of<T: Dim>(x: T) -> Scalar
+
+@description("Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in generic code, but should generally be avoided otherwise. Returns an error if the quantity is zero.")
+@example("unit_of(20 km/h)")
+fn unit_of<T: Dim>(x: T) -> T = if x_value == 0 then error("…") else x / value_of(x)
+    where x_value = value_of(x)

+ 5 - 4
numbat/src/ffi/functions.rs

@@ -38,7 +38,7 @@ pub(crate) fn functions() -> &'static HashMap<String, ForeignFunction> {
 
         // Core
         insert_function!(error, 1..=1);
-        insert_function!(unit_of, 1..=1);
+        insert_function!(value_of, 1..=1);
 
         // Math
         insert_function!("mod", mod_, 2..=2);
@@ -118,7 +118,8 @@ fn error(mut args: Args) -> Result<Value> {
     Err(RuntimeError::UserError(arg!(args).unsafe_as_string()))
 }
 
-fn unit_of(mut args: Args) -> Result<Value> {
-    let input_unit = quantity_arg!(args).unit().clone();
-    return_quantity!(1.0, input_unit)
+fn value_of(mut args: Args) -> Result<Value> {
+    let quantity = quantity_arg!(args);
+
+    return_scalar!(quantity.unsafe_value().to_f64())
 }