From efe59213a7705ac95f25d800c87a0e3fc7de6c22 Mon Sep 17 00:00:00 2001 From: Bzero Date: Sat, 14 Sep 2024 20:59:39 +0200 Subject: [PATCH 01/46] Add @examples decorator --- numbat/examples/inspect.rs | 2 +- numbat/src/decorator.rs | 21 +++++++ numbat/src/lib.rs | 2 + numbat/src/parser.rs | 85 +++++++++++++++++++++++++++ numbat/src/typechecker/environment.rs | 1 + numbat/src/typechecker/mod.rs | 1 + numbat/src/typed_ast.rs | 11 ++++ 7 files changed, 122 insertions(+), 1 deletion(-) diff --git a/numbat/examples/inspect.rs b/numbat/examples/inspect.rs index e61b37ac..989f12ed 100644 --- a/numbat/examples/inspect.rs +++ b/numbat/examples/inspect.rs @@ -41,7 +41,7 @@ and — where sensible — units allow for [binary prefixes](https://en.wikipedi } fn inspect_functions_in_module(ctx: &Context, module: String) { - for (fn_name, name, signature, description, url, code_source) in ctx.functions() { + for (fn_name, name, signature, description, url, examples, code_source) in ctx.functions() { let CodeSource::Module(module_path, _) = code_source else { unreachable!(); }; diff --git a/numbat/src/decorator.rs b/numbat/src/decorator.rs index a000dce7..f93758f2 100644 --- a/numbat/src/decorator.rs +++ b/numbat/src/decorator.rs @@ -8,6 +8,7 @@ pub enum Decorator { Url(String), Name(String), Description(String), + Example(String, Option), } pub fn name_and_aliases<'a>( @@ -89,6 +90,16 @@ pub fn description(decorators: &[Decorator]) -> Option { } } +pub fn examples(decorators: &[Decorator]) -> Vec<(String, Option)> { + let mut examples = Vec::new(); + for decorator in decorators { + if let Decorator::Example(example_code, example_description) = decorator { + examples.push((example_code.clone(), example_description.clone())); + } + } + return examples; +} + pub fn contains_aliases_with_prefixes(decorates: &[Decorator]) -> bool { for decorator in decorates { if let Decorator::Aliases(aliases) = decorator { @@ -110,3 +121,13 @@ pub fn contains_aliases(decorators: &[Decorator]) -> bool { false } + +pub fn contains_examples(decorators: &[Decorator]) -> bool { + for decorator in decorators { + if let Decorator::Example(_, _) = decorator { + return true; + } + } + + false +} diff --git a/numbat/src/lib.rs b/numbat/src/lib.rs index a400aaba..1f1ac4a3 100644 --- a/numbat/src/lib.rs +++ b/numbat/src/lib.rs @@ -168,6 +168,7 @@ impl Context { String, Option, Option, + Vec<(String, Option)>, CodeSource, ), > + '_ { @@ -185,6 +186,7 @@ impl Context { .to_string(), meta.description.clone(), meta.url.clone(), + meta.examples.clone(), self.resolver .get_code_source(signature.definition_span.code_source_id), ) diff --git a/numbat/src/parser.rs b/numbat/src/parser.rs index f0f19837..e7567736 100644 --- a/numbat/src/parser.rs +++ b/numbat/src/parser.rs @@ -198,6 +198,9 @@ pub enum ParseErrorKind { #[error("Aliases cannot be used on functions.")] AliasUsedOnFunction, + #[error("Example decorators can only be used on functions.")] + ExampleUsedOnUnsuitableKind, + #[error("Numerical overflow in dimension exponent")] OverflowInDimensionExponent, @@ -456,6 +459,14 @@ impl Parser { span: self.peek(tokens).span, }); } + + if decorator::contains_examples(&self.decorator_stack) { + return Err(ParseError { + kind: ParseErrorKind::ExampleUsedOnUnsuitableKind, + span: self.peek(tokens).span, + }); + } + std::mem::swap(&mut decorators, &mut self.decorator_stack); } @@ -735,6 +746,55 @@ impl Parser { }); } } + "example" => { + if self.match_exact(tokens, TokenKind::LeftParen).is_some() { + if let Some(token_code) = self.match_exact(tokens, TokenKind::StringFixed) { + if self.match_exact(tokens, TokenKind::Comma).is_some() { + //Code and description + if let Some(token_description) = + self.match_exact(tokens, TokenKind::StringFixed) + { + if self.match_exact(tokens, TokenKind::RightParen).is_none() { + return Err(ParseError::new( + ParseErrorKind::MissingClosingParen, + self.peek(tokens).span, + )); + } + + Decorator::Example( + strip_and_escape(&token_code.lexeme), + Some(strip_and_escape(&token_description.lexeme)), + ) + } else { + return Err(ParseError { + kind: ParseErrorKind::ExpectedString, + span: self.peek(tokens).span, + }); + } + } else { + //Code but no description + if self.match_exact(tokens, TokenKind::RightParen).is_none() { + return Err(ParseError::new( + ParseErrorKind::MissingClosingParen, + self.peek(tokens).span, + )); + } + + Decorator::Example(strip_and_escape(&token_code.lexeme), None) + } + } else { + return Err(ParseError { + kind: ParseErrorKind::ExpectedString, + span: self.peek(tokens).span, + }); + } + } else { + return Err(ParseError { + kind: ParseErrorKind::ExpectedLeftParenAfterDecorator, + span: self.peek(tokens).span, + }); + } + } _ => { return Err(ParseError { kind: ParseErrorKind::UnknownDecorator, @@ -769,6 +829,13 @@ impl Parser { let unit_name = identifier.lexeme.to_owned(); + if decorator::contains_examples(&self.decorator_stack) { + return Err(ParseError { + kind: ParseErrorKind::ExampleUsedOnUnsuitableKind, + span: self.peek(tokens).span, + }); + } + let mut decorators = vec![]; std::mem::swap(&mut decorators, &mut self.decorator_stack); @@ -2795,6 +2862,24 @@ mod tests { }, ); + parse_as( + &["@name(\"Some function\") @example(\"some_function(2)\", \"Use this function:\") @example(\"let some_var = some_function(0)\") fn some_function(x) = 1"], + Statement::DefineFunction { + function_name_span: Span::dummy(), + function_name: "some_function".into(), + type_parameters: vec![], + parameters: vec![(Span::dummy(), "x".into(), None)], + body: Some(scalar!(1.0)), + local_variables: vec![], + return_type_annotation: None, + decorators: vec![ + decorator::Decorator::Name("Some function".into()), + decorator::Decorator::Example("some_function(2)".into(), Some("Use this function:".into())), + decorator::Decorator::Example("let some_var = some_function(0)".into(), None), + ], + }, + ); + parse_as( &["fn double_kef(x) = y where y = x * 2"], Statement::DefineFunction { diff --git a/numbat/src/typechecker/environment.rs b/numbat/src/typechecker/environment.rs index d161dd0c..fe367545 100644 --- a/numbat/src/typechecker/environment.rs +++ b/numbat/src/typechecker/environment.rs @@ -68,6 +68,7 @@ pub struct FunctionMetadata { pub name: Option, pub url: Option, pub description: Option, + pub examples: Vec<(String, Option)>, } #[derive(Clone, Debug)] diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index eb64009b..7a296967 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -1420,6 +1420,7 @@ impl TypeChecker { name: crate::decorator::name(decorators), url: crate::decorator::url(decorators), description: crate::decorator::description(decorators), + examples: crate::decorator::examples(decorators), }, ); diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 331bbb7c..2538dae2 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -857,6 +857,17 @@ fn decorator_markup(decorators: &Vec) -> Markup { + m::string(description) + m::operator(")") } + Decorator::Example(example_code, example_description) => { + m::decorator("@example") + + m::operator("(") + + m::string(example_code) + + if let Some(example_description) = example_description { + m::operator(", ") + m::string(example_description) + } else { + m::empty() + } + + m::operator(")") + } } + m::nl(); } From 0210b9cc5295eeff24ef23b2d9ee20700acd9d74 Mon Sep 17 00:00:00 2001 From: Bzero Date: Sun, 15 Sep 2024 14:31:08 +0200 Subject: [PATCH 02/46] Automatically add examples to the documentation --- Cargo.lock | 1 + numbat/Cargo.toml | 1 + numbat/examples/inspect.rs | 138 ++++++++++++++++++++++++++++++++----- numbat/src/markup.rs | 4 ++ numbat/src/resolver.rs | 2 +- 5 files changed, 128 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e1e170e..7c78a4ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1089,6 +1089,7 @@ dependencies = [ "num-traits", "numbat-exchange-rates", "once_cell", + "percent-encoding", "plotly", "pretty_dtoa", "rand", diff --git a/numbat/Cargo.toml b/numbat/Cargo.toml index 69420a40..b527c5fb 100644 --- a/numbat/Cargo.toml +++ b/numbat/Cargo.toml @@ -49,6 +49,7 @@ glob = "0.3" insta = "1.34.0" once_cell = "1.19.0" criterion = { version = "0.5", features = ["html_reports"] } +percent-encoding = "2.3.1" [[bench]] name = "prelude" diff --git a/numbat/examples/inspect.rs b/numbat/examples/inspect.rs index 989f12ed..74f65c67 100644 --- a/numbat/examples/inspect.rs +++ b/numbat/examples/inspect.rs @@ -1,5 +1,10 @@ use itertools::Itertools; -use numbat::{module_importer::FileSystemImporter, resolver::CodeSource, Context}; +use numbat::markup::plain_text_format; +use numbat::module_importer::FileSystemImporter; +use numbat::pretty_print::PrettyPrint; +use numbat::resolver::CodeSource; +use numbat::Context; +use percent_encoding; use std::path::Path; const AUTO_GENERATED_HINT: &str = ""; @@ -57,19 +62,7 @@ fn inspect_functions_in_module(ctx: &Context, module: String) { } if let Some(ref description_raw) = description { - let description_raw = description_raw.trim().to_string(); - - // Replace $..$ with \\( .. \\) for mdbook. - let mut description = String::new(); - for (i, part) in description_raw.split('$').enumerate() { - if i % 2 == 0 { - description.push_str(part); - } else { - description.push_str("\\\\( "); - description.push_str(part); - description.push_str(" \\\\)"); - } - } + let description = replace_equation_delimiters(description_raw.trim().to_string()); if description.ends_with('.') { println!("{description}"); @@ -86,15 +79,126 @@ fn inspect_functions_in_module(ctx: &Context, module: String) { println!("{signature}"); println!("```"); println!(); + + if !examples.is_empty() { + println!("
"); + println!("Examples"); + println!(); + } + + for (example_code, example_description) in examples { + let mut example_ctx = prepare_context(); + let _result = example_ctx + .interpret("use prelude", CodeSource::Internal) + .unwrap(); + + let extra_import = if !example_ctx + .resolver() + .imported_modules + .contains(&module_path) + { + format!("use {}\n", module) + } else { + "".into() + }; + let _result = example_ctx + .interpret(&extra_import, CodeSource::Internal) + .unwrap(); + + if let Ok((statements, results)) = + example_ctx.interpret(&example_code, CodeSource::Internal) + { + //Format the example input + let example_input = format!(">>> {}", example_code); + + //Encode the example url + let url_code = extra_import + &example_code; + let example_url = format!( + "https://numbat.dev/?q={}", + percent_encoding::utf8_percent_encode( + &url_code, + percent_encoding::NON_ALPHANUMERIC + ) + ); + + //Assemble the example output + let mut example_output = String::new(); + example_output += "\n"; + + for statement in &statements { + example_output += &plain_text_format(&statement.pretty_print(), true); + example_output += "\n\n"; + } + + let result_markup = results.to_markup( + statements.last(), + &example_ctx.dimension_registry(), + true, + true, + ); + example_output += &plain_text_format(&result_markup, false); + + if results.is_value() { + example_output += "\n"; + } + + //Print the example + if let Some(example_description) = example_description { + println!( + "* {}\n\n Run this example", + replace_equation_delimiters(example_description), + example_url + ); + } else { + println!( + "* Run this example\n", + example_url + ); + } + + println!(" ```nbt"); + for l in example_input.lines() { + println!(" {}", l); + } + for l in example_output.lines() { + println!(" {}", l); + } + println!(" ```"); + } else { + eprintln!( + "Warning: Example \"{example_code}\" of function {fn_name} did not run successfully." + ); + } + } + println!("
"); + println!(); } } -fn main() { - let module_path = Path::new(&std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("modules"); +// Replace $..$ with \\( .. \\) for mdbook. +fn replace_equation_delimiters(text_in: String) -> String { + let mut text_out = String::new(); + for (i, part) in text_in.split('$').enumerate() { + if i % 2 == 0 { + text_out.push_str(part); + } else { + text_out.push_str("\\\\( "); + text_out.push_str(part); + text_out.push_str(" \\\\)"); + } + } + return text_out; +} +fn prepare_context() -> Context { + let module_path = Path::new(&std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("modules"); let mut importer = FileSystemImporter::default(); importer.add_path(module_path); - let mut ctx = Context::new(importer); + return Context::new(importer); +} + +fn main() { + let mut ctx = prepare_context(); let _result = ctx.interpret("use all", CodeSource::Internal).unwrap(); let mut args = std::env::args(); diff --git a/numbat/src/markup.rs b/numbat/src/markup.rs index 850e3908..f06b5bdc 100644 --- a/numbat/src/markup.rs +++ b/numbat/src/markup.rs @@ -209,3 +209,7 @@ impl Formatter for PlainTextFormatter { text.clone() } } + +pub fn plain_text_format(m: &Markup, indent: bool) -> String { + PlainTextFormatter {}.format(m, indent) +} diff --git a/numbat/src/resolver.rs b/numbat/src/resolver.rs index 5f653fd6..6a626f08 100644 --- a/numbat/src/resolver.rs +++ b/numbat/src/resolver.rs @@ -49,7 +49,7 @@ pub struct Resolver { pub files: SimpleFiles, text_code_source_count: usize, internal_code_source_count: usize, - imported_modules: Vec, + pub imported_modules: Vec, codesources: HashMap, } From 04e1772221a027a9622495818616cd9c5fb41d44 Mon Sep 17 00:00:00 2001 From: Bzero Date: Sun, 15 Sep 2024 14:46:38 +0000 Subject: [PATCH 03/46] Expand documentation by adding examples --- book/build.py | 4 + book/src/list-functions-datetime.md | 230 ++++++ book/src/list-functions-lists.md | 350 +++++++++ book/src/list-functions-math.md | 683 +++++++++++++++++- book/src/list-functions-other.md | 343 ++++++++- book/src/list-functions-strings.md | 252 ++++++- numbat/modules/chemistry/elements.nbt | 4 +- numbat/modules/core/functions.nbt | 29 +- numbat/modules/core/lists.nbt | 23 + numbat/modules/core/numbers.nbt | 6 + numbat/modules/core/quantities.nbt | 2 + numbat/modules/core/strings.nbt | 28 +- numbat/modules/datetime/functions.nbt | 15 + numbat/modules/datetime/human.nbt | 1 + numbat/modules/extra/algebra.nbt | 1 + numbat/modules/extra/color.nbt | 13 +- numbat/modules/math/geometry.nbt | 2 + numbat/modules/math/number_theory.nbt | 2 + numbat/modules/math/statistics.nbt | 12 +- numbat/modules/math/transcendental.nbt | 5 + numbat/modules/numerics/diff.nbt | 1 + numbat/modules/numerics/fixed_point.nbt | 1 + numbat/modules/numerics/solve.nbt | 2 + .../physics/temperature_conversion.nbt | 4 + numbat/modules/units/mixed.nbt | 4 + 25 files changed, 1981 insertions(+), 36 deletions(-) diff --git a/book/build.py b/book/build.py index 61ab370f..7a7c1e1f 100644 --- a/book/build.py +++ b/book/build.py @@ -1,6 +1,7 @@ import subprocess from pathlib import Path import urllib.parse +import os SCRIPT_DIR = Path(__file__).parent.resolve() @@ -119,6 +120,8 @@ def list_of_functions(file_name, document): print( f"Generating list of functions for module '{module}'...", flush=True ) + env = os.environ.copy() + env["TZ"] = "UTC" subprocess.run( [ "cargo", @@ -132,6 +135,7 @@ def list_of_functions(file_name, document): ], stdout=f, text=True, + env=env, ) diff --git a/book/src/list-functions-datetime.md b/book/src/list-functions-datetime.md index ede5544f..d43ad85b 100644 --- a/book/src/list-functions-datetime.md +++ b/book/src/list-functions-datetime.md @@ -11,6 +11,8 @@ Returns the current date and time. fn now() -> DateTime ``` + + ### `datetime` Parses a string (date and time) into a `DateTime` object. See [here](./date-and-time.md#date-time-formats) for an overview of the supported formats. @@ -18,6 +20,41 @@ Parses a string (date and time) into a `DateTime` object. See [here](./date-and- fn datetime(input: String) -> DateTime ``` +
+Examples + +* Run this example + + ```nbt + >>> datetime("2022-07-20T21:52+0200") + + datetime("2022-07-20T21:52+0200") + + = 2022-07-20 19:52:00 UTC [DateTime] + + ``` +* Run this example + + ```nbt + >>> datetime("2022-07-20 21:52 Europe/Berlin") + + datetime("2022-07-20 21:52 Europe/Berlin") + + = 2022-07-20 21:52:00 CEST (UTC +02), Europe/Berlin [DateTime] + + ``` +* Run this example + + ```nbt + >>> datetime("2022/07/20 09:52 PM +0200") + + datetime("2022/07/20 09:52 PM +0200") + + = 2022-07-20 21:52:00 (UTC +02) [DateTime] + + ``` +
+ ### `format_datetime` Formats a `DateTime` object as a string. @@ -25,6 +62,21 @@ Formats a `DateTime` object as a string. fn format_datetime(format: String, input: DateTime) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> format_datetime("This is a date in %B in the year %Y.", datetime("2022-07-20 21:52 +0200")) + + format_datetime("This is a date in %B in the year %Y.", datetime("2022-07-20 21:52 +0200")) + + = "This is a date in July in the year 2022." [String] + + ``` +
+ ### `get_local_timezone` Returns the users local timezone. @@ -32,6 +84,21 @@ Returns the users local timezone. fn get_local_timezone() -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> get_local_timezone() + + get_local_timezone() + + = "UTC" [String] + + ``` +
+ ### `tz` Returns a timezone conversion function, typically used with the conversion operator. @@ -39,6 +106,31 @@ Returns a timezone conversion function, typically used with the conversion opera fn tz(tz: String) -> Fn[(DateTime) -> DateTime] ``` +
+Examples + +* Run this example + + ```nbt + >>> datetime("2022-07-20 21:52 +0200") -> tz("Europe/Amsterdam") + + tz("Europe/Amsterdam")(datetime("2022-07-20 21:52 +0200")) + + = 2022-07-20 21:52:00 CEST (UTC +02), Europe/Amsterdam [DateTime] + + ``` +* Run this example + + ```nbt + >>> datetime("2022-07-20 21:52 +0200") -> tz("Asia/Taipei") + + tz("Asia/Taipei")(datetime("2022-07-20 21:52 +0200")) + + = 2022-07-21 03:52:00 CST (UTC +08), Asia/Taipei [DateTime] + + ``` +
+ ### `unixtime` Converts a `DateTime` to a UNIX timestamp. Can be used on the right hand side of a conversion operator: `now() -> unixtime`. @@ -46,6 +138,21 @@ Converts a `DateTime` to a UNIX timestamp. Can be used on the right hand side of fn unixtime(input: DateTime) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> datetime("2022-07-20 21:52 +0200") -> unixtime + + unixtime(datetime("2022-07-20 21:52 +0200")) + + = 1_658_346_720 + + ``` +
+ ### `from_unixtime` Converts a UNIX timestamp to a `DateTime` object. @@ -53,6 +160,21 @@ Converts a UNIX timestamp to a `DateTime` object. fn from_unixtime(input: Scalar) -> DateTime ``` +
+Examples + +* Run this example + + ```nbt + >>> from_unixtime(2^31) + + from_unixtime(2^31) + + = 2038-01-19 03:14:08 UTC [DateTime] + + ``` +
+ ### `today` Returns the current date at midnight (in the local time). @@ -60,6 +182,8 @@ Returns the current date at midnight (in the local time). fn today() -> DateTime ``` + + ### `date` Parses a string (only date) into a `DateTime` object. @@ -67,6 +191,21 @@ Parses a string (only date) into a `DateTime` object. fn date(input: String) -> DateTime ``` +
+Examples + +* Run this example + + ```nbt + >>> date("2022-07-20") + + date("2022-07-20") + + = 2022-07-20 00:00:00 UTC [DateTime] + + ``` +
+ ### `time` Parses a string (time only) into a `DateTime` object. @@ -74,6 +213,21 @@ Parses a string (time only) into a `DateTime` object. fn time(input: String) -> DateTime ``` +
+Examples + +* Run this example + + ```nbt + >>> time("21:52") + + time("21:52") + + = 2024-09-15 21:52:00 UTC [DateTime] + + ``` +
+ ### `calendar_add` Adds the given time span to a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years. @@ -81,6 +235,21 @@ Adds the given time span to a `DateTime`. This uses leap-year and DST-aware cale fn calendar_add(dt: DateTime, span: Time) -> DateTime ``` +
+Examples + +* Run this example + + ```nbt + >>> calendar_add(datetime("2022-07-20 21:52 +0200"), 2 years) + + calendar_add(datetime("2022-07-20 21:52 +0200"), 2 year) + + = 2024-07-20 21:52:00 (UTC +02) [DateTime] + + ``` +
+ ### `calendar_sub` Subtract the given time span from a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years. @@ -88,6 +257,21 @@ Subtract the given time span from a `DateTime`. This uses leap-year and DST-awar fn calendar_sub(dt: DateTime, span: Time) -> DateTime ``` +
+Examples + +* Run this example + + ```nbt + >>> calendar_sub(datetime("2022-07-20 21:52 +0200"), 3 years) + + calendar_sub(datetime("2022-07-20 21:52 +0200"), 3 year) + + = 2019-07-20 21:52:00 (UTC +02) [DateTime] + + ``` +
+ ### `weekday` Get the day of the week from a given `DateTime`. @@ -95,6 +279,21 @@ Get the day of the week from a given `DateTime`. fn weekday(dt: DateTime) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> weekday(datetime("2022-07-20 21:52 +0200")) + + weekday(datetime("2022-07-20 21:52 +0200")) + + = "Wednesday" [String] + + ``` +
+ ### `julian_date` (Julian date) Convert a `DateTime` to a Julian date, the number of days since the origin of the Julian date system (noon on November 24, 4714 BC in the proleptic Gregorian calendar). More information [here](https://en.wikipedia.org/wiki/Julian_day). @@ -103,6 +302,21 @@ More information [here](https://en.wikipedia.org/wiki/Julian_day). fn julian_date(dt: DateTime) -> Time ``` +
+Examples + +* Run this example + + ```nbt + >>> julian_date(datetime("2022-07-20 21:52 +0200")) + + julian_date(datetime("2022-07-20 21:52 +0200")) + + = 2.45978e+6 day [Time] + + ``` +
+ ### `human` (Human-readable time duration) Converts a time duration to a human-readable string in days, hours, minutes and seconds. More information [here](https://numbat.dev/doc/date-and-time.html). @@ -111,3 +325,19 @@ More information [here](https://numbat.dev/doc/date-and-time.html). fn human(time: Time) -> String ``` +
+Examples + +* How long is a microcentury? + + Run this example + ```nbt + >>> century/1e6 -> human + + human(century / 1_000_000) + + = "52 minutes + 35.692505184 seconds" [String] + + ``` +
+ diff --git a/book/src/list-functions-lists.md b/book/src/list-functions-lists.md index 10723fa0..7a9659b1 100644 --- a/book/src/list-functions-lists.md +++ b/book/src/list-functions-lists.md @@ -9,6 +9,21 @@ Get the length of a list. fn len(xs: List) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> len([3, 2, 1]) + + len([3, 2, 1]) + + = 3 + + ``` +
+ ### `head` Get the first element of a list. Yields a runtime error if the list is empty. @@ -16,6 +31,21 @@ Get the first element of a list. Yields a runtime error if the list is empty. fn head(xs: List) -> A ``` +
+Examples + +* Run this example + + ```nbt + >>> head([3, 2, 1]) + + head([3, 2, 1]) + + = 3 + + ``` +
+ ### `tail` Get everything but the first element of a list. Yields a runtime error if the list is empty. @@ -23,6 +53,21 @@ Get everything but the first element of a list. Yields a runtime error if the li fn tail(xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> tail([3, 2, 1]) + + tail([3, 2, 1]) + + = [2, 1] [List] + + ``` +
+ ### `cons` Prepend an element to a list. @@ -30,6 +75,21 @@ Prepend an element to a list. fn cons(x: A, xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> cons(77, [3, 2, 1]) + + cons(77, [3, 2, 1]) + + = [77, 3, 2, 1] [List] + + ``` +
+ ### `cons_end` Append an element to the end of a list. @@ -37,6 +97,21 @@ Append an element to the end of a list. fn cons_end(x: A, xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> cons_end(77, [3, 2, 1]) + + cons_end(77, [3, 2, 1]) + + = [3, 2, 1, 77] [List] + + ``` +
+ ### `is_empty` Check if a list is empty. @@ -44,6 +119,31 @@ Check if a list is empty. fn is_empty(xs: List) -> Bool ``` +
+Examples + +* Run this example + + ```nbt + >>> is_empty([3, 2, 1]) + + is_empty([3, 2, 1]) + + = false [Bool] + + ``` +* Run this example + + ```nbt + >>> is_empty([]) + + is_empty([]) + + = true [Bool] + + ``` +
+ ### `concat` Concatenate two lists. @@ -51,6 +151,21 @@ Concatenate two lists. fn concat(xs1: List, xs2: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> concat([3, 2, 1], [10, 11]) + + concat([3, 2, 1], [10, 11]) + + = [3, 2, 1, 10, 11] [List] + + ``` +
+ ### `take` Get the first `n` elements of a list. @@ -58,6 +173,21 @@ Get the first `n` elements of a list. fn take(n: Scalar, xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> take(2, [3, 2, 1, 0]) + + take(2, [3, 2, 1, 0]) + + = [3, 2] [List] + + ``` +
+ ### `drop` Get everything but the first `n` elements of a list. @@ -65,6 +195,21 @@ Get everything but the first `n` elements of a list. fn drop(n: Scalar, xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> drop(2, [3, 2, 1, 0]) + + drop(2, [3, 2, 1, 0]) + + = [1, 0] [List] + + ``` +
+ ### `element_at` Get the element at index `i` in a list. @@ -72,6 +217,21 @@ Get the element at index `i` in a list. fn element_at(i: Scalar, xs: List) -> A ``` +
+Examples + +* Run this example + + ```nbt + >>> element_at(2, [3, 2, 1, 0]) + + element_at(2, [3, 2, 1, 0]) + + = 1 + + ``` +
+ ### `range` Generate a range of integer numbers from `start` to `end` (inclusive). @@ -79,6 +239,21 @@ Generate a range of integer numbers from `start` to `end` (inclusive). fn range(start: Scalar, end: Scalar) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> range(2, 12) + + range(2, 12) + + = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] [List] + + ``` +
+ ### `reverse` Reverse the order of a list. @@ -86,6 +261,21 @@ Reverse the order of a list. fn reverse(xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> reverse([3, 2, 1]) + + reverse([3, 2, 1]) + + = [1, 2, 3] [List] + + ``` +
+ ### `map` Generate a new list by applying a function to each element of the input list. @@ -93,6 +283,22 @@ Generate a new list by applying a function to each element of the input list. fn map(f: Fn[(A) -> B], xs: List) -> List ``` +
+Examples + +* Square all elements of a list. + + Run this example + ```nbt + >>> map(sqr, [3, 2, 1]) + + map(sqr, [3, 2, 1]) + + = [9, 4, 1] [List] + + ``` +
+ ### `filter` Filter a list by a predicate. @@ -100,6 +306,25 @@ Filter a list by a predicate. fn filter(p: Fn[(A) -> Bool], xs: List) -> List ``` +
+Examples + +* Filter all elements greater than \\( 1 \\). + + Run this example + ```nbt + >>> fn filter_fn(x) = x > 1 + filter(filter_fn, [3, 2, 1, 0]) + + fn filter_fn(x: Scalar) -> Bool = x > 1 + + filter(filter_fn, [3, 2, 1, 0]) + + = [3, 2] [List] + + ``` +
+ ### `foldl` Fold a function over a list. @@ -107,6 +332,22 @@ Fold a function over a list. fn foldl(f: Fn[(A, B) -> A], acc: A, xs: List) -> A ``` +
+Examples + +* Join a list of strings by folding. + + Run this example + ```nbt + >>> foldl(str_append, "", ["Num", "bat", "!"]) + + foldl(str_append, "", ["Num", "bat", "!"]) + + = "Numbat!" [String] + + ``` +
+ ### `sort_by_key` Sort a list of elements, using the given key function that maps the element to a quantity. @@ -114,6 +355,25 @@ Sort a list of elements, using the given key function that maps the element to a fn sort_by_key(key: Fn[(A) -> D], xs: List) -> List ``` +
+Examples + +* Sort by last digit. + + Run this example + ```nbt + >>> fn map_fn(x) = mod(x, 10) + sort_by_key(map_fn, [701, 313, 9999, 4]) + + fn map_fn(x: Scalar) -> Scalar = mod(x, 10) + + sort_by_key(map_fn, [701, 313, 9999, 4]) + + = [701, 313, 4, 9999] [List] + + ``` +
+ ### `sort` Sort a list of quantities. @@ -121,6 +381,21 @@ Sort a list of quantities. fn sort(xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> sort([3, 2, 7, 8, -4, 0, -5]) + + sort([3, 2, 7, 8, -4, 0, -5]) + + = [-5, -4, 0, 2, 3, 7, 8] [List] + + ``` +
+ ### `intersperse` Add an element between each pair of elements in a list. @@ -128,6 +403,21 @@ Add an element between each pair of elements in a list. fn intersperse(sep: A, xs: List) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> intersperse(0, [1, 1, 1, 1]) + + intersperse(0, [1, 1, 1, 1]) + + = [1, 0, 1, 0, 1, 0, 1] [List] + + ``` +
+ ### `sum` Sum all elements of a list. @@ -135,6 +425,21 @@ Sum all elements of a list. fn sum(xs: List) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> sum([3, 2, 1]) + + sum([3, 2, 1]) + + = 6 + + ``` +
+ ### `linspace` Generate a list of `n_steps` evenly spaced numbers from `start` to `end` (inclusive). @@ -142,6 +447,21 @@ Generate a list of `n_steps` evenly spaced numbers from `start` to `end` (inclus fn linspace(start: D, end: D, n_steps: Scalar) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> linspace(0, 10, 11) + + linspace(0, 10, 11) + + = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [List] + + ``` +
+ ### `join` Convert a list of strings into a single string by concatenating them with a separator. @@ -149,6 +469,21 @@ Convert a list of strings into a single string by concatenating them with a sepa fn join(xs: List, sep: String) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> join(["snake", "case"], "_") + + join(["snake", "case"], "_") + + = "snake_case" [String] + + ``` +
+ ### `split` Split a string into a list of strings using a separator. @@ -156,3 +491,18 @@ Split a string into a list of strings using a separator. fn split(input: String, separator: String) -> List ``` +
+Examples + +* Run this example + + ```nbt + >>> split("Numbat is a statically typed programming language.", " ") + + split("Numbat is a statically typed programming language.", " ") + + = ["Numbat", "is", "a", "statically", "typed", "programming", "language."] [List] + + ``` +
+ diff --git a/book/src/list-functions-math.md b/book/src/list-functions-math.md index 07d7d79f..891d1bfb 100644 --- a/book/src/list-functions-math.md +++ b/book/src/list-functions-math.md @@ -13,6 +13,21 @@ Return the input value. fn id(x: A) -> A ``` +
+Examples + +* Run this example + + ```nbt + >>> id(8kg) + + id(8 kilogram) + + = 8 kg [Mass] + + ``` +
+ ### `abs` (Absolute value) Return the absolute value \\( |x| \\) of the input. This works for quantities, too: `abs(-5 m) = 5 m`. More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.abs). @@ -21,6 +36,8 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn abs(x: T) -> T ``` + + ### `sqrt` (Square root) Return the square root \\( \sqrt{x} \\) of the input: `sqrt(121 m^2) = 11 m`. More information [here](https://en.wikipedia.org/wiki/Square_root). @@ -29,6 +46,21 @@ More information [here](https://en.wikipedia.org/wiki/Square_root). fn sqrt(x: D^2) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> sqrt(4are) -> m + + sqrt(4 are) ➞ metre + + = 20 m [Length] + + ``` +
+ ### `cbrt` (Cube root) Return the cube root \\( \sqrt[3]{x} \\) of the input: `cbrt(8 m^3) = 2 m`. More information [here](https://en.wikipedia.org/wiki/Cube_root). @@ -37,6 +69,21 @@ More information [here](https://en.wikipedia.org/wiki/Cube_root). fn cbrt(x: D^3) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> cbrt(8l) -> cm + + cbrt(8 litre) ➞ centimetre + + = 20.0 cm [Length] + + ``` +
+ ### `sqr` (Square function) Return the square of the input, \\( x^2 \\): `sqr(5 m) = 25 m^2`. @@ -44,6 +91,21 @@ Return the square of the input, \\( x^2 \\): `sqr(5 m) = 25 m^2`. fn sqr(x: D) -> D^2 ``` +
+Examples + +* Run this example + + ```nbt + >>> sqr(7) + + sqr(7) + + = 49 + + ``` +
+ ### `round` (Rounding) Round to the nearest integer. If the value is half-way between two integers, round away from \\( 0 \\). See also: `round_in`. More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.round). @@ -52,13 +114,65 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn round(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> round(5.5) + + round(5.5) + + = 6 + + ``` +* Run this example + + ```nbt + >>> round(-5.5) + + round(-5.5) + + = -6 + + ``` +
+ ### `round_in` (Rounding) -Round to the nearest multiple of `base`. For example: `round_in(m, 5.3 m) == 5 m`. +Round to the nearest multiple of `base`. ```nbt fn round_in(base: D, value: D) -> D ``` +
+Examples + +* Round in meters. + + Run this example + ```nbt + >>> round_in(m, 5.3 m) + + round_in(metre, 5.3 metre) + + = 5 m [Length] + + ``` +* Round in centimeters. + + Run this example + ```nbt + >>> round_in(cm, 5.3 m) + + round_in(centimetre, 5.3 metre) + + = 530 cm [Length] + + ``` +
+ ### `floor` (Floor function) Returns the largest integer less than or equal to \\( x \\). See also: `floor_in`. More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.floor). @@ -67,13 +181,55 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn floor(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> floor(5.5) + + floor(5.5) + + = 5 + + ``` +
+ ### `floor_in` (Floor function) -Returns the largest integer multiple of `base` less than or equal to `value`. For example: `floor_in(m, 5.7 m) == 5 m`. +Returns the largest integer multiple of `base` less than or equal to `value`. ```nbt fn floor_in(base: D, value: D) -> D ``` +
+Examples + +* Floor in meters. + + Run this example + ```nbt + >>> floor_in(m, 5.7 m) + + floor_in(metre, 5.7 metre) + + = 5 m [Length] + + ``` +* Floor in centimeters. + + Run this example + ```nbt + >>> floor_in(cm, 5.7 m) + + floor_in(centimetre, 5.7 metre) + + = 570 cm [Length] + + ``` +
+ ### `ceil` (Ceil function) Returns the smallest integer greater than or equal to \\( x \\). See also: `ceil_in`. More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.ceil). @@ -82,13 +238,55 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn ceil(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> ceil(5.5) + + ceil(5.5) + + = 6 + + ``` +
+ ### `ceil_in` (Ceil function) -Returns the smallest integer multuple of `base` greater than or equal to `value`. For example: `ceil_in(m, 5.3 m) == 6 m`. +Returns the smallest integer multuple of `base` greater than or equal to `value`. ```nbt fn ceil_in(base: D, value: D) -> D ``` +
+Examples + +* Ceil in meters. + + Run this example + ```nbt + >>> ceil_in(m, 5.3 m) + + ceil_in(metre, 5.3 metre) + + = 6 m [Length] + + ``` +* Ceil in centimeters. + + Run this example + ```nbt + >>> ceil_in(cm, 5.3 m) + + ceil_in(centimetre, 5.3 metre) + + = 530 cm [Length] + + ``` +
+ ### `trunc` (Truncation) Returns the integer part of \\( x \\). Non-integer numbers are always truncated towards zero. See also: `trunc_in`. More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.trunc). @@ -97,13 +295,65 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn trunc(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> trunc(5.5) + + trunc(5.5) + + = 5 + + ``` +* Run this example + + ```nbt + >>> trunc(-5.5) + + trunc(-5.5) + + = -5 + + ``` +
+ ### `trunc_in` (Truncation) -Truncates to an integer multiple of `base` (towards zero). For example: `trunc_in(m, -5.7 m) == -5 m`. +Truncates to an integer multiple of `base` (towards zero). ```nbt fn trunc_in(base: D, value: D) -> D ``` +
+Examples + +* Truncate in meters. + + Run this example + ```nbt + >>> trunc_in(m, 5.7 m) + + trunc_in(metre, 5.7 metre) + + = 5 m [Length] + + ``` +* Truncate in centimeters. + + Run this example + ```nbt + >>> trunc_in(cm, 5.7 m) + + trunc_in(centimetre, 5.7 metre) + + = 570 cm [Length] + + ``` +
+ ### `mod` (Modulo) Calculates the least nonnegative remainder of \\( a (\mod b) \\). More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.rem_euclid). @@ -112,6 +362,21 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn mod(a: T, b: T) -> T ``` +
+Examples + +* Run this example + + ```nbt + >>> mod(27, 5) + + mod(27, 5) + + = 2 + + ``` +
+ ## Transcendental functions Defined in: `math::transcendental` @@ -124,6 +389,21 @@ More information [here](https://en.wikipedia.org/wiki/Exponential_function). fn exp(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> exp(4) + + exp(4) + + = 54.5982 + + ``` +
+ ### `ln` (Natural logarithm) The natural logarithm with base \\( e \\). More information [here](https://en.wikipedia.org/wiki/Natural_logarithm). @@ -132,6 +412,21 @@ More information [here](https://en.wikipedia.org/wiki/Natural_logarithm). fn ln(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> ln(20) + + ln(20) + + = 2.99573 + + ``` +
+ ### `log` (Natural logarithm) The natural logarithm with base \\( e \\). More information [here](https://en.wikipedia.org/wiki/Natural_logarithm). @@ -140,6 +435,21 @@ More information [here](https://en.wikipedia.org/wiki/Natural_logarithm). fn log(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> log(20) + + log(20) + + = 2.99573 + + ``` +
+ ### `log10` (Common logarithm) The common logarithm with base \\( 10 \\). More information [here](https://en.wikipedia.org/wiki/Common_logarithm). @@ -148,6 +458,21 @@ More information [here](https://en.wikipedia.org/wiki/Common_logarithm). fn log10(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> log10(100) + + log10(100) + + = 2 + + ``` +
+ ### `log2` (Binary logarithm) The binary logarithm with base \\( 2 \\). More information [here](https://en.wikipedia.org/wiki/Binary_logarithm). @@ -156,6 +481,21 @@ More information [here](https://en.wikipedia.org/wiki/Binary_logarithm). fn log2(x: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> log2(256) + + log2(256) + + = 8 + + ``` +
+ ### `gamma` (Gamma function) The gamma function, \\( \Gamma(x) \\). More information [here](https://en.wikipedia.org/wiki/Gamma_function). @@ -164,6 +504,8 @@ More information [here](https://en.wikipedia.org/wiki/Gamma_function). fn gamma(x: Scalar) -> Scalar ``` + + ## Trigonometry Defined in: `math::trigonometry` @@ -175,6 +517,8 @@ More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). fn sin(x: Scalar) -> Scalar ``` + + ### `cos` (Cosine) More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). @@ -182,6 +526,8 @@ More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). fn cos(x: Scalar) -> Scalar ``` + + ### `tan` (Tangent) More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). @@ -189,6 +535,8 @@ More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). fn tan(x: Scalar) -> Scalar ``` + + ### `asin` (Arc sine) More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions). @@ -196,6 +544,8 @@ More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_func fn asin(x: Scalar) -> Scalar ``` + + ### `acos` (Arc cosine) More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions). @@ -203,6 +553,8 @@ More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_func fn acos(x: Scalar) -> Scalar ``` + + ### `atan` (Arc tangent) More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions). @@ -210,6 +562,8 @@ More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_func fn atan(x: Scalar) -> Scalar ``` + + ### `atan2` More information [here](https://en.wikipedia.org/wiki/Atan2). @@ -217,6 +571,8 @@ More information [here](https://en.wikipedia.org/wiki/Atan2). fn atan2(y: T, x: T) -> Scalar ``` + + ### `sinh` (Hyperbolic sine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -224,6 +580,8 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn sinh(x: Scalar) -> Scalar ``` + + ### `cosh` (Hyperbolic cosine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -231,6 +589,8 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn cosh(x: Scalar) -> Scalar ``` + + ### `tanh` (Hyperbolic tangent) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -238,6 +598,8 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn tanh(x: Scalar) -> Scalar ``` + + ### `asinh` (Area hyperbolic sine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -245,6 +607,8 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn asinh(x: Scalar) -> Scalar ``` + + ### `acosh` (Area hyperbolic cosine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -252,6 +616,8 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn acosh(x: Scalar) -> Scalar ``` + + ### `atanh` (Area hyperbolic tangent ) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -259,32 +625,79 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn atanh(x: Scalar) -> Scalar ``` + + ## Statistics Defined in: `math::statistics` ### `maximum` (Maxmimum) -Get the largest element of a list: `maximum([30 cm, 2 m]) = 2 m`. +Get the largest element of a list. ```nbt fn maximum(xs: List) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> maximum([30 cm, 2 m]) + + maximum([30 centimetre, 2 metre]) + + = 2 m [Length] + + ``` +
+ ### `minimum` (Minimum) -Get the smallest element of a list: `minimum([30 cm, 2 m]) = 30 cm`. +Get the smallest element of a list. ```nbt fn minimum(xs: List) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> minimum([30 cm, 2 m]) + + minimum([30 centimetre, 2 metre]) + + = 30 cm [Length] + + ``` +
+ ### `mean` (Arithmetic mean) -Calculate the arithmetic mean of a list of quantities: `mean([1 m, 2 m, 300 cm]) = 2 m`. +Calculate the arithmetic mean of a list of quantities. More information [here](https://en.wikipedia.org/wiki/Arithmetic_mean). ```nbt fn mean(xs: List) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> mean([1 m, 2 m, 300 cm]) + + mean([1 metre, 2 metre, 300 centimetre]) + + = 2 m [Length] + + ``` +
+ ### `variance` (Variance) Calculate the population variance of a list of quantities. More information [here](https://en.wikipedia.org/wiki/Variance). @@ -293,6 +706,21 @@ More information [here](https://en.wikipedia.org/wiki/Variance). fn variance(xs: List) -> D^2 ``` +
+Examples + +* Run this example + + ```nbt + >>> variance([1 m, 2 m, 300 cm]) + + variance([1 metre, 2 metre, 300 centimetre]) + + = 0.666667 m² [Area] + + ``` +
+ ### `stdev` (Standard deviation) Calculate the population standard deviation of a list of quantities. More information [here](https://en.wikipedia.org/wiki/Standard_deviation). @@ -301,6 +729,21 @@ More information [here](https://en.wikipedia.org/wiki/Standard_deviation). fn stdev(xs: List) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> stdev([1 m, 2 m, 300 cm]) + + stdev([1 metre, 2 metre, 300 centimetre]) + + = 0.816497 m [Length] + + ``` +
+ ### `median` (Median) Calculate the median of a list of quantities. More information [here](https://en.wikipedia.org/wiki/Median). @@ -309,6 +752,21 @@ More information [here](https://en.wikipedia.org/wiki/Median). fn median(xs: List) -> D ``` +
+Examples + +* Run this example + + ```nbt + >>> median([1 m, 2 m, 400 cm]) + + median([1 metre, 2 metre, 400 centimetre]) + + = 2 m [Length] + + ``` +
+ ## Random sampling, distributions Defined in: `core::random`, `math::distributions` @@ -320,6 +778,8 @@ Uniformly samples the interval \\( [0,1) \\). fn random() -> Scalar ``` + + ### `rand_uniform` (Continuous uniform distribution sampling) Uniformly samples the interval \\( [a,b) \\) if \\( a \le b \\) or \\( [b,a) \\) if \\( b(a: T, b: T) -> T ``` + + ### `rand_int` (Discrete uniform distribution sampling) Uniformly samples integers from the interval \\( [a, b] \\). More information [here](https://en.wikipedia.org/wiki/Discrete_uniform_distribution). @@ -336,6 +798,8 @@ More information [here](https://en.wikipedia.org/wiki/Discrete_uniform_distribut fn rand_int(a: Scalar, b: Scalar) -> Scalar ``` + + ### `rand_bernoulli` (Bernoulli distribution sampling) Samples a Bernoulli random variable. That is, \\( 1 \\) with probability \\( p \\) and \\( 0 \\) with probability \\( 1-p \\). The parameter \\( p \\) must be a probability (\\( 0 \le p \le 1 \\)). More information [here](https://en.wikipedia.org/wiki/Bernoulli_distribution). @@ -344,6 +808,8 @@ More information [here](https://en.wikipedia.org/wiki/Bernoulli_distribution). fn rand_bernoulli(p: Scalar) -> Scalar ``` + + ### `rand_binom` (Binomial distribution sampling) Samples a binomial distribution by doing \\( n \\) Bernoulli trials with probability \\( p \\). The parameter \\( n \\) must be a positive integer, the parameter \\( p \\) must be a probability (\\( 0 \le p \le 1 \\)). @@ -353,6 +819,8 @@ More information [here](https://en.wikipedia.org/wiki/Binomial_distribution). fn rand_binom(n: Scalar, p: Scalar) -> Scalar ``` + + ### `rand_norm` (Normal distribution sampling) Samples a normal distribution with mean \\( \mu \\) and standard deviation \\( \sigma \\) using the Box-Muller transform. More information [here](https://en.wikipedia.org/wiki/Normal_distribution). @@ -361,6 +829,8 @@ More information [here](https://en.wikipedia.org/wiki/Normal_distribution). fn rand_norm(μ: T, σ: T) -> T ``` + + ### `rand_geom` (Geometric distribution sampling) Samples a geometric distribution (the distribution of the number of Bernoulli trials with probability \\( p \\) needed to get one success) by inversion sampling. The parameter \\( p \\) must be a probability (\\( 0 \le p \le 1 \\)). More information [here](https://en.wikipedia.org/wiki/Geometric_distribution). @@ -369,6 +839,8 @@ More information [here](https://en.wikipedia.org/wiki/Geometric_distribution). fn rand_geom(p: Scalar) -> Scalar ``` + + ### `rand_poisson` (Poisson distribution sampling) Sampling a poisson distribution with rate \\( \lambda \\), that is, the distribution of the number of events occurring in a fixed interval if these events occur with mean rate \\( \lambda \\). The rate parameter \\( \lambda \\) must be non-negative. More information [here](https://en.wikipedia.org/wiki/Poisson_distribution). @@ -377,6 +849,8 @@ More information [here](https://en.wikipedia.org/wiki/Poisson_distribution). fn rand_poisson(λ: Scalar) -> Scalar ``` + + ### `rand_expon` (Exponential distribution sampling) Sampling an exponential distribution (the distribution of the distance between events in a Poisson process with rate \\( \lambda \\)) using inversion sampling. The rate parameter \\( \lambda \\) must be positive. More information [here](https://en.wikipedia.org/wiki/Exponential_distribution). @@ -385,6 +859,8 @@ More information [here](https://en.wikipedia.org/wiki/Exponential_distribution). fn rand_expon(λ: T) -> 1 / T ``` + + ### `rand_lognorm` (Log-normal distribution sampling) Sampling a log-normal distribution, that is, a distribution whose logarithm is a normal distribution with mean \\( \mu \\) and standard deviation \\( \sigma \\). More information [here](https://en.wikipedia.org/wiki/Log-normal_distribution). @@ -393,6 +869,8 @@ More information [here](https://en.wikipedia.org/wiki/Log-normal_distribution). fn rand_lognorm(μ: Scalar, σ: Scalar) -> Scalar ``` + + ### `rand_pareto` (Pareto distribution sampling) Sampling a Pareto distribution with minimum value `min` and shape parameter \\( \alpha \\) using inversion sampling. Both parameters must be positive. More information [here](https://en.wikipedia.org/wiki/Pareto_distribution). @@ -401,6 +879,8 @@ More information [here](https://en.wikipedia.org/wiki/Pareto_distribution). fn rand_pareto(α: Scalar, min: T) -> T ``` + + ## Number theory Defined in: `math::number_theory` @@ -413,6 +893,21 @@ More information [here](https://en.wikipedia.org/wiki/Greatest_common_divisor). fn gcd(a: Scalar, b: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> gcd(60,42) + + gcd(60, 42) + + = 6 + + ``` +
+ ### `lcm` (Least common multiple) The smallest positive integer that is divisible by both \\( a \\) and \\( b \\). More information [here](https://en.wikipedia.org/wiki/Least_common_multiple). @@ -421,6 +916,21 @@ More information [here](https://en.wikipedia.org/wiki/Least_common_multiple). fn lcm(a: Scalar, b: Scalar) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> lcm(14, 4) + + lcm(14, 4) + + = 28 + + ``` +
+ ## Numerical methods Defined in: `numerics::diff`, `numerics::solve`, `numerics::fixed_point` @@ -433,6 +943,25 @@ More information [here](https://en.wikipedia.org/wiki/Numerical_differentiation) fn diff(f: Fn[(X) -> Y], x: X) -> Y / X ``` +
+Examples + +* Compute the drivative of \\( f(x) = x² -x -1 \\) at \\( x=1 \\). + + Run this example + ```nbt + >>> fn polynomial(x) = x² -x -1 + diff(polynomial, 1) + + fn polynomial(x: Scalar) -> Scalar = (x² - x) - 1 + + diff(polynomial, 1) + + = 1.0 + + ``` +
+ ### `root_bisect` (Bisection method) Find the root of the function \\( f \\) in the interval \\( [x_1, x_2] \\) using the bisection method. The function \\( f \\) must be continuous and \\( f(x_1) \cdot f(x_2) < 0 \\). More information [here](https://en.wikipedia.org/wiki/Bisection_method). @@ -441,6 +970,25 @@ More information [here](https://en.wikipedia.org/wiki/Bisection_method). fn root_bisect(f: Fn[(X) -> Y], x1: X, x2: X, x_tol: X, y_tol: Y) -> X ``` +
+Examples + +* Find the root of \\( f(x) = x² +x -2 \\) in the interval \\( [0, 100] \\). + + Run this example + ```nbt + >>> fn f(x) = x² +x -2 + root_bisect(f, 0, 100, 0.01, 0.01) + + fn f(x: Scalar) -> Scalar = (x² + x) - 2 + + root_bisect(f, 0, 100, 0.01, 0.01) + + = 1.00098 + + ``` +
+ ### `root_newton` (Newton's method) Find the root of the function \\( f(x) \\) and its derivative \\( f'(x) \\) using Newton's method. More information [here](https://en.wikipedia.org/wiki/Newton%27s_method). @@ -449,6 +997,28 @@ More information [here](https://en.wikipedia.org/wiki/Newton%27s_method). fn root_newton(f: Fn[(X) -> Y], f_prime: Fn[(X) -> Y / X], x0: X, y_tol: Y) -> X ``` +
+Examples + +* Find a root of \\( f(x) = x² -3x +2 \\) using Newton's method. + + Run this example + ```nbt + >>> fn f(x) = x² -3x +2 + fn f_prime(x) = 2x -3 + root_newton(f, f_prime, 0 , 0.01) + + fn f(x: Scalar) -> Scalar = (x² - 3 x) + 2 + + fn f_prime(x: Scalar) -> Scalar = 2 x - 3 + + root_newton(f, f_prime, 0, 0.01) + + = 0.996078 + + ``` +
+ ### `fixed_point` (Fixed-point iteration) Compute the approximate fixed point of a function \\( f: X \rightarrow X \\) starting from \\( x_0 \\), until \\( |f(x) - x| < ε \\). More information [here](https://en.wikipedia.org/wiki/Fixed-point_iteration). @@ -457,6 +1027,25 @@ More information [here](https://en.wikipedia.org/wiki/Fixed-point_iteration). fn fixed_point(f: Fn[(X) -> X], x0: X, ε: X) -> X ``` +
+Examples + +* Compute the fixed poin of \\( f(x) = x/2 -1 \\). + + Run this example + ```nbt + >>> fn function(x) = x/2 - 1 + fixed_point(function, 0, 0.01) + + fn function(x: Scalar) -> Scalar = (x / 2) - 1 + + fixed_point(function, 0, 0.01) + + = -1.99219 + + ``` +
+ ## Geometry Defined in: `math::geometry` @@ -468,6 +1057,21 @@ The length of the hypotenuse of a right-angled triangle \\( \sqrt{x^2+y^2} \\). fn hypot2(x: T, y: T) -> T ``` +
+Examples + +* Run this example + + ```nbt + >>> hypot2(3, 4) + + hypot2(3, 4) + + = 5 + + ``` +
+ ### `hypot3` The Euclidean norm of a 3D vector \\( \sqrt{x^2+y^2+z^2} \\). @@ -475,6 +1079,21 @@ The Euclidean norm of a 3D vector \\( \sqrt{x^2+y^2+z^2} \\). fn hypot3(x: T, y: T, z: T) -> T ``` +
+Examples + +* Run this example + + ```nbt + >>> hypot3(4, 1, 4) + + hypot3(4, 1, 4) + + = 5.74456 + + ``` +
+ ### `circle_area` The area of a circle, \\( \pi r^2 \\). @@ -482,6 +1101,8 @@ The area of a circle, \\( \pi r^2 \\). fn circle_area(radius: L) -> L^2 ``` + + ### `circle_circumference` The circumference of a circle, \\( 2\pi r \\). @@ -489,6 +1110,8 @@ The circumference of a circle, \\( 2\pi r \\). fn circle_circumference(radius: L) -> L ``` + + ### `sphere_area` The surface area of a sphere, \\( 4\pi r^2 \\). @@ -496,6 +1119,8 @@ The surface area of a sphere, \\( 4\pi r^2 \\). fn sphere_area(radius: L) -> L^2 ``` + + ### `sphere_volume` The volume of a sphere, \\( \frac{4}{3}\pi r^3 \\). @@ -503,6 +1128,8 @@ The volume of a sphere, \\( \frac{4}{3}\pi r^3 \\). fn sphere_volume(radius: L) -> L^3 ``` + + ## Algebra Defined in: `extra::algebra` @@ -515,6 +1142,22 @@ More information [here](https://en.wikipedia.org/wiki/Quadratic_equation). fn quadratic_equation(a: A, b: B, c: B^2 / A) -> List ``` +
+Examples + +* Solve the equation \\( 2x² -x -1 = 0 \\) + + Run this example + ```nbt + >>> quadratic_equation(2, -1, -1) + + quadratic_equation(2, -1, -1) + + = [1, -0.5] [List] + + ``` +
+ ## Trigonometry (extra) Defined in: `math::trigonometry_extra` @@ -525,75 +1168,101 @@ Defined in: `math::trigonometry_extra` fn cot(x: Scalar) -> Scalar ``` + + ### `acot` ```nbt fn acot(x: Scalar) -> Scalar ``` + + ### `coth` ```nbt fn coth(x: Scalar) -> Scalar ``` + + ### `acoth` ```nbt fn acoth(x: Scalar) -> Scalar ``` + + ### `secant` ```nbt fn secant(x: Scalar) -> Scalar ``` + + ### `arcsecant` ```nbt fn arcsecant(x: Scalar) -> Scalar ``` + + ### `cosecant` ```nbt fn cosecant(x: Scalar) -> Scalar ``` + + ### `csc` ```nbt fn csc(x: Scalar) -> Scalar ``` + + ### `acsc` ```nbt fn acsc(x: Scalar) -> Scalar ``` + + ### `sech` ```nbt fn sech(x: Scalar) -> Scalar ``` + + ### `asech` ```nbt fn asech(x: Scalar) -> Scalar ``` + + ### `csch` ```nbt fn csch(x: Scalar) -> Scalar ``` + + ### `acsch` ```nbt fn acsch(x: Scalar) -> Scalar ``` + + diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index a8a97830..90363f6f 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -13,6 +13,8 @@ Throw an error with the specified message. Stops the execution of the program. fn error(message: String) -> T ``` + + ## Floating point Defined in: `core::numbers` @@ -25,6 +27,31 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn is_nan(n: T) -> Bool ``` +
+Examples + +* Run this example + + ```nbt + >>> is_nan(37) + + is_nan(37) + + = false [Bool] + + ``` +* Run this example + + ```nbt + >>> is_nan(NaN) + + is_nan(NaN) + + = true [Bool] + + ``` +
+ ### `is_infinite` Returns true if the input is positive infinity or negative infinity. More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite). @@ -33,6 +60,31 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn is_infinite(n: T) -> Bool ``` +
+Examples + +* Run this example + + ```nbt + >>> is_infinite(37) + + is_infinite(37) + + = false [Bool] + + ``` +* Run this example + + ```nbt + >>> is_infinite(-inf) + + is_infinite(-inf) + + = true [Bool] + + ``` +
+ ### `is_finite` Returns true if the input is neither infinite nor `NaN`. @@ -40,6 +92,31 @@ Returns true if the input is neither infinite nor `NaN`. fn is_finite(n: T) -> Bool ``` +
+Examples + +* Run this example + + ```nbt + >>> is_finite(37) + + is_finite(37) + + = true [Bool] + + ``` +* Run this example + + ```nbt + >>> is_finite(-inf) + + is_finite(-inf) + + = false [Bool] + + ``` +
+ ## Quantities Defined in: `core::quantities` @@ -51,6 +128,21 @@ Extract the unit of a quantity (the `km/h` in `20 km/h`). This can be useful in fn unit_of(x: T) -> T ``` +
+Examples + +* Run this example + + ```nbt + >>> unit_of(20 km/h) + + unit_of(20 kilometre / hour) + + = 1 km/h [Velocity] + + ``` +
+ ### `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. @@ -58,17 +150,59 @@ Extract the plain value of a quantity (the `20` in `20 km/h`). This can be usefu fn value_of(x: T) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> value_of(20 km/h) + + value_of(20 kilometre / hour) + + = 20 + + ``` +
+ ## Chemical elements Defined in: `chemistry::elements` ### `element` (Chemical element) -Get properties of a chemical element by its symbol or name (case-insensitive). For example: `element("H")` or `element("hydrogen")`. +Get properties of a chemical element by its symbol or name (case-insensitive). ```nbt fn element(pattern: String) -> ChemicalElement ``` +
+Examples + +* Get the entire element struct for hydrogen. + + Run this example + ```nbt + >>> element("H") + + element("H") + + = ChemicalElement { symbol: "H", name: "Hydrogen", atomic_number: 1, group: 1, group_name: "Alkali metals", period: 1, melting_point: 13.99 K, boiling_point: 20.271 K, density: 0.00008988 g/cm³, electron_affinity: 0.754 eV, ionization_energy: 13.598 eV, vaporization_heat: 0.904 kJ/mol } [ChemicalElement] + + ``` +* Get the ionization energy of hydrogen. + + Run this example + ```nbt + >>> element("hydrogen").ionization_energy + + element("hydrogen").ionization_energy + + = 13.598 eV [Energy or Torque] + + ``` +
+ ## Mixed unit conversion Defined in: `units::mixed` @@ -81,6 +215,21 @@ More information [here](https://en.wikipedia.org/wiki/Sexagesimal_degree). fn DMS(alpha: Angle) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> DMS(46.5858°) + + DMS(46.5858 degree) + + = "46° 35′ 9″" [String] + + ``` +
+ ### `DM` (Degrees, decimal minutes) Convert an angle to a mixed degrees and decimal minutes representation. More information [here](https://en.wikipedia.org/wiki/Decimal_degrees). @@ -89,6 +238,21 @@ More information [here](https://en.wikipedia.org/wiki/Decimal_degrees). fn DM(alpha: Angle) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> DM(46.5858°) + + DM(46.5858 degree) + + = "46° 35.148′" [String] + + ``` +
+ ### `feet_and_inches` (Feet and inches) Convert a length to a mixed feet and inches representation. More information [here](https://en.wikipedia.org/wiki/Foot_(unit)). @@ -97,6 +261,21 @@ More information [here](https://en.wikipedia.org/wiki/Foot_(unit)). fn feet_and_inches(length: Length) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> feet_and_inches(180cm) + + feet_and_inches(180 centimetre) + + = "5 ft 10.8661 in" [String] + + ``` +
+ ### `pounds_and_ounces` (Pounds and ounces) Convert a mass to a mixed pounds and ounces representation. More information [here](https://en.wikipedia.org/wiki/Pound_(mass)). @@ -105,6 +284,21 @@ More information [here](https://en.wikipedia.org/wiki/Pound_(mass)). fn pounds_and_ounces(mass: Mass) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> pounds_and_ounces(1kg) + + pounds_and_ounces(1 kilogram) + + = "2 lb 3.27396 oz" [String] + + ``` +
+ ## Temperature conversion Defined in: `physics::temperature_conversion` @@ -117,6 +311,22 @@ More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_te fn from_celsius(t_celsius: Scalar) -> Temperature ``` +
+Examples + +* \\( 300 °C \\) in Kelvin. + + Run this example + ```nbt + >>> from_celsius(300) + + from_celsius(300) + + = 573.15 K [Temperature] + + ``` +
+ ### `celsius` Converts from Kelvin to degree Celcius (°C). This can be used on the right hand side of a conversion operator: `200 K -> celsius`. More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature). @@ -125,6 +335,22 @@ More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_te fn celsius(t_kelvin: Temperature) -> Scalar ``` +
+Examples + +* \\( 300K \\) in degree Celsius. + + Run this example + ```nbt + >>> 300K -> celsius + + celsius(300 kelvin) + + = 26.85 + + ``` +
+ ### `from_fahrenheit` Converts from degree Fahrenheit (°F) to Kelvin. More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature). @@ -133,6 +359,22 @@ More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_te fn from_fahrenheit(t_fahrenheit: Scalar) -> Temperature ``` +
+Examples + +* \\( 300 °F \\) in Kelvin. + + Run this example + ```nbt + >>> from_fahrenheit(300) + + from_fahrenheit(300) + + = 422.039 K [Temperature] + + ``` +
+ ### `fahrenheit` Converts from Kelvin to degree Fahrenheit (°F). This can be used on the right hand side of a conversion operator: `200 K -> fahrenheit`. More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature). @@ -141,6 +383,22 @@ More information [here](https://en.wikipedia.org/wiki/Conversion_of_scales_of_te fn fahrenheit(t_kelvin: Temperature) -> Scalar ``` +
+Examples + +* \\( 300K \\) in degree Fahrenheit. + + Run this example + ```nbt + >>> 300K -> fahrenheit + + fahrenheit(300 kelvin) + + = 80.33 + + ``` +
+ ## Color format conversion Defined in: `extra::color` @@ -152,31 +410,106 @@ Create a `Color` from RGB (red, green, blue) values in the range \\( [0, 256) \\ fn rgb(red: Scalar, green: Scalar, blue: Scalar) -> Color ``` +
+Examples + +* Run this example + + ```nbt + >>> rgb(125, 128, 218) + + rgb(125, 128, 218) + + = Color { red: 125, green: 128, blue: 218 } [Color] + + ``` +
+ ### `color` -Create a `Color` from a (hexadecimal) value, e.g. `color(0xff7700)`. +Create a `Color` from a (hexadecimal) value. ```nbt fn color(rgb_hex: Scalar) -> Color ``` +
+Examples + +* Run this example + + ```nbt + >>> color(0xff7700) + + color(16_742_144) + + = Color { red: 255, green: 119, blue: 0 } [Color] + + ``` +
+ ### `color_rgb` -Convert a color to its RGB representation, e.g. `cyan -> color_rgb`. +Convert a color to its RGB representation. ```nbt fn color_rgb(color: Color) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> cyan -> color_rgb + + color_rgb(cyan) + + = "rgb(0, 255, 255)" [String] + + ``` +
+ ### `color_rgb_float` -Convert a color to its RGB floating point representation, e.g. `cyan -> color_rgb_float`. +Convert a color to its RGB floating point representation. ```nbt fn color_rgb_float(color: Color) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> cyan -> color_rgb_float + + color_rgb_float(cyan) + + = "rgb(0.000, 1.000, 1.000)" [String] + + ``` +
+ ### `color_hex` -Convert a color to its hexadecimal representation, e.g. `rgb(225, 36, 143) -> color_hex`. +Convert a color to its hexadecimal representation. ```nbt fn color_hex(color: Color) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> rgb(225, 36, 143) -> color_hex + + color_hex(rgb(225, 36, 143)) + + = "#e1248f" [String] + + ``` +
+ diff --git a/book/src/list-functions-strings.md b/book/src/list-functions-strings.md index e955338c..b8ca8b78 100644 --- a/book/src/list-functions-strings.md +++ b/book/src/list-functions-strings.md @@ -9,6 +9,21 @@ The length of a string. fn str_length(s: String) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> str_length("Numbat") + + str_length("Numbat") + + = 6 + + ``` +
+ ### `str_slice` Subslice of a string. @@ -16,20 +31,65 @@ Subslice of a string. fn str_slice(s: String, start: Scalar, end: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> str_slice("Numbat", 0, 2) + + str_slice("Numbat", 0, 2) + + = "Nu" [String] + + ``` +
+ ### `chr` -Get a single-character string from a Unicode code point. Example: `0x2764 -> chr`. +Get a single-character string from a Unicode code point. ```nbt fn chr(n: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> 0x2764 -> chr + + chr(10084) + + = "❤" [String] + + ``` +
+ ### `ord` -Get the Unicode code point of the first character in a string. Example: `"❤" -> ord`. +Get the Unicode code point of the first character in a string. ```nbt fn ord(s: String) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> "❤" -> ord + + ord("❤") + + = 10084 + + ``` +
+ ### `lowercase` Convert a string to lowercase. @@ -37,6 +97,21 @@ Convert a string to lowercase. fn lowercase(s: String) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> lowercase("Numbat") + + lowercase("Numbat") + + = "numbat" [String] + + ``` +
+ ### `uppercase` Convert a string to uppercase. @@ -44,6 +119,21 @@ Convert a string to uppercase. fn uppercase(s: String) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> uppercase("Numbat") + + uppercase("Numbat") + + = "NUMBAT" [String] + + ``` +
+ ### `str_append` Concatenate two strings. @@ -51,6 +141,21 @@ Concatenate two strings. fn str_append(a: String, b: String) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> str_append("Numbat","!") + + str_append("Numbat", "!") + + = "Numbat!" [String] + + ``` +
+ ### `str_find` Find the first occurrence of a substring in a string. @@ -58,6 +163,21 @@ Find the first occurrence of a substring in a string. fn str_find(haystack: String, needle: String) -> Scalar ``` +
+Examples + +* Run this example + + ```nbt + >>> str_find("Numbat is a statically typed programming language.", "typed") + + str_find("Numbat is a statically typed programming language.", "typed") + + = 23 + + ``` +
+ ### `str_contains` Check if a string contains a substring. @@ -65,6 +185,21 @@ Check if a string contains a substring. fn str_contains(haystack: String, needle: String) -> Bool ``` +
+Examples + +* Run this example + + ```nbt + >>> str_contains("Numbat is a statically typed programming language.", "typed") + + str_contains("Numbat is a statically typed programming language.", "typed") + + = true [Bool] + + ``` +
+ ### `str_replace` Replace all occurrences of a substring in a string. @@ -72,6 +207,21 @@ Replace all occurrences of a substring in a string. fn str_replace(s: String, pattern: String, replacement: String) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> str_replace("Numbat is a statically typed programming language.", "statically typed programming language", "scientific calculator") + + str_replace("Numbat is a statically typed programming language.", "statically typed programming language", "scientific calculator") + + = "Numbat is a scientific calculator." [String] + + ``` +
+ ### `str_repeat` Repeat the input string `n` times. @@ -79,27 +229,87 @@ Repeat the input string `n` times. fn str_repeat(a: String, n: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> str_repeat("abc", 4) + + str_repeat("abc", 4) + + = "abcabcabcabc" [String] + + ``` +
+ ### `base` -Convert a number to the given base. Example: `42 |> base(16)`. +Convert a number to the given base. ```nbt fn base(b: Scalar, x: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> 42 |> base(16) + + base(16, 42) + + = "2a" [String] + + ``` +
+ ### `bin` -Get a binary representation of a number. Example: `42 -> bin`. +Get a binary representation of a number. ```nbt fn bin(x: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> 42 -> bin + + bin(42) + + = "0b101010" [String] + + ``` +
+ ### `oct` -Get an octal representation of a number. Example: `42 -> oct`. +Get an octal representation of a number. ```nbt fn oct(x: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> 42 -> oct + + oct(42) + + = "0o52" [String] + + ``` +
+ ### `dec` Get a decimal representation of a number. @@ -107,10 +317,40 @@ Get a decimal representation of a number. fn dec(x: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> 0b111 -> dec + + dec(7) + + = "7" [String] + + ``` +
+ ### `hex` -Get a hexadecimal representation of a number. Example: `2^31-1 -> hex`. +Get a hexadecimal representation of a number. ```nbt fn hex(x: Scalar) -> String ``` +
+Examples + +* Run this example + + ```nbt + >>> 2^31-1 -> hex + + hex(2^31 - 1) + + = "0x7fffffff" [String] + + ``` +
+ diff --git a/numbat/modules/chemistry/elements.nbt b/numbat/modules/chemistry/elements.nbt index 17d74bb3..e77fed50 100644 --- a/numbat/modules/chemistry/elements.nbt +++ b/numbat/modules/chemistry/elements.nbt @@ -49,6 +49,8 @@ fn _convert_from_raw(raw: _ChemicalElementRaw) -> ChemicalElement = } @name("Chemical element") -@description("Get properties of a chemical element by its symbol or name (case-insensitive). For example: `element(\"H\")` or `element(\"hydrogen\")`.") +@description("Get properties of a chemical element by its symbol or name (case-insensitive).") +@example("element(\"H\")", "Get the entire element struct for hydrogen.") +@example("element(\"hydrogen\").ionization_energy", "Get the ionization energy of hydrogen.") fn element(pattern: String) -> ChemicalElement = _convert_from_raw(_get_chemical_element_data_raw(pattern)) diff --git a/numbat/modules/core/functions.nbt b/numbat/modules/core/functions.nbt index c088f693..a1ee5b76 100644 --- a/numbat/modules/core/functions.nbt +++ b/numbat/modules/core/functions.nbt @@ -2,64 +2,85 @@ use core::scalar @name("Identity function") @description("Return the input value.") +@example("id(8kg)") fn id(x: A) -> A = x @name("Absolute value") @description("Return the absolute value $|x|$ of the input. This works for quantities, too: `abs(-5 m) = 5 m`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.abs") +# @example("abs(-22.2m)") fn abs(x: T) -> T @name("Square root") @description("Return the square root $\\sqrt\{x\}$ of the input: `sqrt(121 m^2) = 11 m`.") @url("https://en.wikipedia.org/wiki/Square_root") +@example("sqrt(4are) -> m") fn sqrt(x: D^2) -> D = x^(1/2) @name("Cube root") @description("Return the cube root $\\sqrt[3]\{x\}$ of the input: `cbrt(8 m^3) = 2 m`.") @url("https://en.wikipedia.org/wiki/Cube_root") +@example("cbrt(8l) -> cm") fn cbrt(x: D^3) -> D = x^(1/3) @name("Square function") @description("Return the square of the input, $x^2$: `sqr(5 m) = 25 m^2`.") +@example("sqr(7)") fn sqr(x: D) -> D^2 = x^2 @name("Rounding") @description("Round to the nearest integer. If the value is half-way between two integers, round away from $0$. See also: `round_in`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.round") +@example("round(5.5)") +@example("round(-5.5)") fn round(x: Scalar) -> Scalar @name("Rounding") -@description("Round to the nearest multiple of `base`. For example: `round_in(m, 5.3 m) == 5 m`.") +@description("Round to the nearest multiple of `base`.") +@example("round_in(m, 5.3 m)", "Round in meters.") +@example("round_in(cm, 5.3 m)", "Round in centimeters.") fn round_in(base: D, value: D) -> D = round(value / base) × base @name("Floor function") @description("Returns the largest integer less than or equal to $x$. See also: `floor_in`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.floor") +@example("floor(5.5)") fn floor(x: Scalar) -> Scalar @name("Floor function") -@description("Returns the largest integer multiple of `base` less than or equal to `value`. For example: `floor_in(m, 5.7 m) == 5 m`.") +@description("Returns the largest integer multiple of `base` less than or equal to `value`.") +@example("floor_in(m, 5.7 m)", "Floor in meters.") +@example("floor_in(cm, 5.7 m)", "Floor in centimeters.") fn floor_in(base: D, value: D) -> D = floor(value / base) × base @name("Ceil function") @description("Returns the smallest integer greater than or equal to $x$. See also: `ceil_in`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.ceil") +@example("ceil(5.5)") fn ceil(x: Scalar) -> Scalar @name("Ceil function") -@description("Returns the smallest integer multuple of `base` greater than or equal to `value`. For example: `ceil_in(m, 5.3 m) == 6 m`.") +@description("Returns the smallest integer multuple of `base` greater than or equal to `value`.") +@example("ceil_in(m, 5.3 m)", "Ceil in meters.") +@example("ceil_in(cm, 5.3 m)", "Ceil in centimeters.") + fn ceil_in(base: D, value: D) -> D = ceil(value / base) × base @name("Truncation") @description("Returns the integer part of $x$. Non-integer numbers are always truncated towards zero. See also: `trunc_in`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.trunc") +@example("trunc(5.5)") +@example("trunc(-5.5)") fn trunc(x: Scalar) -> Scalar @name("Truncation") -@description("Truncates to an integer multiple of `base` (towards zero). For example: `trunc_in(m, -5.7 m) == -5 m`.") +@description("Truncates to an integer multiple of `base` (towards zero).") +@example("trunc_in(m, 5.7 m)", "Truncate in meters.") +@example("trunc_in(cm, 5.7 m)", "Truncate in centimeters.") fn trunc_in(base: D, value: D) -> D = trunc(value / base) × base @name("Modulo") @description("Calculates the least nonnegative remainder of $a (\\mod b)$.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.rem_euclid") +@example("mod(27, 5)") fn mod(a: T, b: T) -> T diff --git a/numbat/modules/core/lists.nbt b/numbat/modules/core/lists.nbt index 8cd81b46..662383da 100644 --- a/numbat/modules/core/lists.nbt +++ b/numbat/modules/core/lists.nbt @@ -3,48 +3,60 @@ use core::error use core::strings @description("Get the length of a list") +@example("len([3, 2, 1])") fn len(xs: List) -> Scalar @description("Get the first element of a list. Yields a runtime error if the list is empty.") +@example("head([3, 2, 1])") fn head(xs: List) -> A @description("Get everything but the first element of a list. Yields a runtime error if the list is empty.") +@example("tail([3, 2, 1])") fn tail(xs: List) -> List @description("Prepend an element to a list") +@example("cons(77, [3, 2, 1])") fn cons(x: A, xs: List) -> List @description("Append an element to the end of a list") +@example("cons_end(77, [3, 2, 1])") fn cons_end(x: A, xs: List) -> List @description("Check if a list is empty") +@example("is_empty([3, 2, 1])") +@example("is_empty([])") fn is_empty(xs: List) -> Bool = xs == [] @description("Concatenate two lists") +@example("concat([3, 2, 1], [10, 11])") fn concat(xs1: List, xs2: List) -> List = if is_empty(xs1) then xs2 else cons(head(xs1), concat(tail(xs1), xs2)) @description("Get the first `n` elements of a list") +@example("take(2, [3, 2, 1, 0])") fn take(n: Scalar, xs: List) -> List = if n == 0 || is_empty(xs) then [] else cons(head(xs), take(n - 1, tail(xs))) @description("Get everything but the first `n` elements of a list") +@example("drop(2, [3, 2, 1, 0])") fn drop(n: Scalar, xs: List) -> List = if n == 0 || is_empty(xs) then xs else drop(n - 1, tail(xs)) @description("Get the element at index `i` in a list") +@example("element_at(2, [3, 2, 1, 0])") fn element_at(i: Scalar, xs: List) -> A = if i == 0 then head(xs) else element_at(i - 1, tail(xs)) @description("Generate a range of integer numbers from `start` to `end` (inclusive)") +@example("range(2, 12)") fn range(start: Scalar, end: Scalar) -> List = if start > end then [] @@ -52,18 +64,21 @@ fn range(start: Scalar, end: Scalar) -> List = @description("Reverse the order of a list") +@example("reverse([3, 2, 1])") fn reverse(xs: List) -> List = if is_empty(xs) then [] else cons_end(head(xs), reverse(tail(xs))) @description("Generate a new list by applying a function to each element of the input list") +@example("map(sqr, [3, 2, 1])", "Square all elements of a list.") fn map(f: Fn[(A) -> B], xs: List) -> List = if is_empty(xs) then [] else cons(f(head(xs)), map(f, tail(xs))) @description("Filter a list by a predicate") +@example("fn filter_fn(x) = x > 1\nfilter(filter_fn, [3, 2, 1, 0])", "Filter all elements greater than $1$.") fn filter(p: Fn[(A) -> Bool], xs: List) -> List = if is_empty(xs) then [] @@ -72,6 +87,7 @@ fn filter(p: Fn[(A) -> Bool], xs: List) -> List = else filter(p, tail(xs)) @description("Fold a function over a list") +@example("foldl(str_append, \"\", [\"Num\", \"bat\", \"!\"])", "Join a list of strings by folding.") fn foldl(f: Fn[(A, B) -> A], acc: A, xs: List) -> A = if is_empty(xs) then acc @@ -88,6 +104,7 @@ fn _merge(xs, ys, cmp) = @description("Sort a list of elements, using the given key function that maps the element to a quantity") +@example("fn map_fn(x) = mod(x, 10)\nsort_by_key(map_fn, [701, 313, 9999, 4])","Sort by last digit.") fn sort_by_key(key: Fn[(A) -> D], xs: List) -> List = if is_empty(xs) then [] @@ -98,9 +115,11 @@ fn sort_by_key(key: Fn[(A) -> D], xs: List) -> List = key) @description("Sort a list of quantities") +@example("sort([3, 2, 7, 8, -4, 0, -5])") fn sort(xs: List) -> List = sort_by_key(id, xs) @description("Add an element between each pair of elements in a list") +@example("intersperse(0, [1, 1, 1, 1])") fn intersperse(sep: A, xs: List) -> List = if is_empty(xs) then [] @@ -110,6 +129,7 @@ fn intersperse(sep: A, xs: List) -> List = fn _add(x, y) = x + y # TODO: replace this with a local function once we support them @description("Sum all elements of a list") +@example("sum([3, 2, 1])") fn sum(xs: List) -> D = foldl(_add, 0, xs) # TODO: implement linspace using `map` or similar once we have closures. This is ugly. @@ -119,12 +139,14 @@ fn _linspace_helper(start, end, n_steps, i) = else cons(start + (end - start) * i / (n_steps - 1), _linspace_helper(start, end, n_steps, i + 1)) @description("Generate a list of `n_steps` evenly spaced numbers from `start` to `end` (inclusive)") +@example("linspace(0, 10, 11)") fn linspace(start: D, end: D, n_steps: Scalar) -> List = if n_steps <= 1 then error("Number of steps must be larger than 1") else _linspace_helper(start, end, n_steps, 0) @description("Convert a list of strings into a single string by concatenating them with a separator") +@example("join([\"snake\", \"case\"], \"_\")") fn join(xs: List, sep: String) = if is_empty(xs) then "" @@ -133,6 +155,7 @@ fn join(xs: List, sep: String) = else "{head(xs)}{sep}{join(tail(xs), sep)}" @description("Split a string into a list of strings using a separator") +@example("split(\"Numbat is a statically typed programming language.\", \" \")") fn split(input: String, separator: String) -> List = if input == "" then [] diff --git a/numbat/modules/core/numbers.nbt b/numbat/modules/core/numbers.nbt index 2dba36c9..3265ea38 100644 --- a/numbat/modules/core/numbers.nbt +++ b/numbat/modules/core/numbers.nbt @@ -1,10 +1,16 @@ @description("Returns true if the input is `NaN`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan") +@example("is_nan(37)") +@example("is_nan(NaN)") fn is_nan(n: T) -> Bool @description("Returns true if the input is positive infinity or negative infinity.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite") +@example("is_infinite(37)") +@example("is_infinite(-inf)") fn is_infinite(n: T) -> Bool @description("Returns true if the input is neither infinite nor `NaN`.") +@example("is_finite(37)") +@example("is_finite(-inf)") fn is_finite(n: T) -> Bool = !is_nan(n) && !is_infinite(n) diff --git a/numbat/modules/core/quantities.nbt b/numbat/modules/core/quantities.nbt index c7cd9b07..3d886bd2 100644 --- a/numbat/modules/core/quantities.nbt +++ b/numbat/modules/core/quantities.nbt @@ -1,7 +1,9 @@ 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(x: T) -> T @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(x: T) -> Scalar = x / unit_of(x) diff --git a/numbat/modules/core/strings.nbt b/numbat/modules/core/strings.nbt index fb1ef77b..609d1cf5 100644 --- a/numbat/modules/core/strings.nbt +++ b/numbat/modules/core/strings.nbt @@ -3,27 +3,35 @@ use core::functions use core::error @description("The length of a string") +@example("str_length(\"Numbat\")") fn str_length(s: String) -> Scalar @description("Subslice of a string") +@example("str_slice(\"Numbat\", 0, 2)") fn str_slice(s: String, start: Scalar, end: Scalar) -> String -@description("Get a single-character string from a Unicode code point. Example: `0x2764 -> chr`") +@description("Get a single-character string from a Unicode code point.") +@example("0x2764 -> chr") fn chr(n: Scalar) -> String -@description("Get the Unicode code point of the first character in a string. Example: `\"❤\" -> ord`") +@description("Get the Unicode code point of the first character in a string.") +@example("\"❤\" -> ord") fn ord(s: String) -> Scalar @description("Convert a string to lowercase") +@example("lowercase(\"Numbat\")") fn lowercase(s: String) -> String @description("Convert a string to uppercase") +@example("uppercase(\"Numbat\")") fn uppercase(s: String) -> String @description("Concatenate two strings") +@example("str_append(\"Numbat\",\"!\")") fn str_append(a: String, b: String) -> String = "{a}{b}" @description("Find the first occurrence of a substring in a string") +@example("str_find(\"Numbat is a statically typed programming language.\", \"typed\")") fn str_find(haystack: String, needle: String) -> Scalar = if len_haystack == 0 then -1 @@ -36,10 +44,12 @@ fn str_find(haystack: String, needle: String) -> Scalar = and tail_haystack = str_slice(haystack, 1, len_haystack) @description("Check if a string contains a substring") +@example("str_contains(\"Numbat is a statically typed programming language.\", \"typed\")") fn str_contains(haystack: String, needle: String) -> Bool = str_find(haystack, needle) != -1 @description("Replace all occurrences of a substring in a string") +@example("str_replace(\"Numbat is a statically typed programming language.\", \"statically typed programming language\", \"scientific calculator\")") fn str_replace(s: String, pattern: String, replacement: String) -> String = if pattern == "" then s @@ -50,6 +60,7 @@ fn str_replace(s: String, pattern: String, replacement: String) -> String = else s @description("Repeat the input string `n` times") +@example("str_repeat(\"abc\", 4)") fn str_repeat(a: String, n: Scalar) -> String = if n > 0 then str_append(a, str_repeat(a, n - 1)) @@ -76,7 +87,8 @@ fn _digit_in_base(base: Scalar, x: Scalar) -> String = # TODO: once we have anonymous functions / closures, we can implement base in a way # that it returns a partially-applied version of '_number_in_base'. This would allow # arbitrary 'x -> base(b)' conversions. -@description("Convert a number to the given base. Example: `42 |> base(16)`") +@description("Convert a number to the given base.") +@example("42 |> base(16)") fn base(b: Scalar, x: Scalar) -> String = if x < 0 then "-{base(b, -x)}" @@ -84,14 +96,18 @@ fn base(b: Scalar, x: Scalar) -> String = then _digit_in_base(b, x) else str_append(base(b, floor(x / b)), _digit_in_base(b, mod(x, b))) -@description("Get a binary representation of a number. Example: `42 -> bin`") +@description("Get a binary representation of a number.") +@example("42 -> bin") fn bin(x: Scalar) -> String = if x < 0 then "-{bin(-x)}" else "0b{base(2, x)}" -@description("Get an octal representation of a number. Example: `42 -> oct`") +@description("Get an octal representation of a number.") +@example("42 -> oct") fn oct(x: Scalar) -> String = if x < 0 then "-{oct(-x)}" else "0o{base(8, x)}" @description("Get a decimal representation of a number.") +@example("0b111 -> dec") fn dec(x: Scalar) -> String = base(10, x) -@description("Get a hexadecimal representation of a number. Example: `2^31-1 -> hex`") +@description("Get a hexadecimal representation of a number.") +@example("2^31-1 -> hex") fn hex(x: Scalar) -> String = if x < 0 then "-{hex(-x)}" else "0x{base(16, x)}" diff --git a/numbat/modules/datetime/functions.nbt b/numbat/modules/datetime/functions.nbt index fe9854ad..878e96c7 100644 --- a/numbat/modules/datetime/functions.nbt +++ b/numbat/modules/datetime/functions.nbt @@ -7,15 +7,22 @@ use units::time fn now() -> DateTime @description("Parses a string (date and time) into a `DateTime` object. See [here](./date-and-time.md#date-time-formats) for an overview of the supported formats.") +@example("datetime(\"2022-07-20T21:52+0200\")") +@example("datetime(\"2022-07-20 21:52 Europe/Berlin\")") +@example("datetime(\"2022/07/20 09:52 PM +0200\")") fn datetime(input: String) -> DateTime @description("Formats a `DateTime` object as a string.") +@example("format_datetime(\"This is a date in %B in the year %Y.\", datetime(\"2022-07-20 21:52 +0200\"))") fn format_datetime(format: String, input: DateTime) -> String @description("Returns the users local timezone.") +@example("get_local_timezone()") fn get_local_timezone() -> String @description("Returns a timezone conversion function, typically used with the conversion operator.") +@example("datetime(\"2022-07-20 21:52 +0200\") -> tz(\"Europe/Amsterdam\")") +@example("datetime(\"2022-07-20 21:52 +0200\") -> tz(\"Asia/Taipei\")") fn tz(tz: String) -> Fn[(DateTime) -> DateTime] @description("Timezone conversion function targeting the users local timezone (`datetime -> local`).") @@ -25,9 +32,11 @@ let local: Fn[(DateTime) -> DateTime] = tz(get_local_timezone()) let UTC: Fn[(DateTime) -> DateTime] = tz("UTC") @description("Converts a `DateTime` to a UNIX timestamp. Can be used on the right hand side of a conversion operator: `now() -> unixtime`.") +@example("datetime(\"2022-07-20 21:52 +0200\") -> unixtime") fn unixtime(input: DateTime) -> Scalar @description("Converts a UNIX timestamp to a `DateTime` object.") +@example("from_unixtime(2^31)") fn from_unixtime(input: Scalar) -> DateTime fn _today_str() = format_datetime("%Y-%m-%d", now()) @@ -36,12 +45,14 @@ fn _today_str() = format_datetime("%Y-%m-%d", now()) fn today() -> DateTime = datetime("{_today_str()} 00:00:00") @description("Parses a string (only date) into a `DateTime` object.") +@example("date(\"2022-07-20\")") fn date(input: String) -> DateTime = if str_contains(input, " ") then datetime(str_replace(input, " ", " 00:00:00 ")) else datetime("{input} 00:00:00") @description("Parses a string (time only) into a `DateTime` object.") +@example("time(\"21:52\")") fn time(input: String) -> DateTime = datetime("{_today_str()} {input}") @@ -50,6 +61,7 @@ fn _add_months(dt: DateTime, n_months: Scalar) -> DateTime fn _add_years(dt: DateTime, n_years: Scalar) -> DateTime @description("Adds the given time span to a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years.") +@example("calendar_add(datetime(\"2022-07-20 21:52 +0200\"), 2 years)") fn calendar_add(dt: DateTime, span: Time) -> DateTime = if span_unit == days then _add_days(dt, span / days) @@ -65,14 +77,17 @@ fn calendar_add(dt: DateTime, span: Time) -> DateTime = span_unit = unit_of(span) @description("Subtract the given time span from a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years.") +@example("calendar_sub(datetime(\"2022-07-20 21:52 +0200\"), 3 years)") fn calendar_sub(dt: DateTime, span: Time) -> DateTime = calendar_add(dt, -span) @description("Get the day of the week from a given `DateTime`.") +@example("weekday(datetime(\"2022-07-20 21:52 +0200\"))") fn weekday(dt: DateTime) -> String = format_datetime("%A", dt) @name("Julian date") @description("Convert a `DateTime` to a Julian date, the number of days since the origin of the Julian date system (noon on November 24, 4714 BC in the proleptic Gregorian calendar).") @url("https://en.wikipedia.org/wiki/Julian_day") +@example("julian_date(datetime(\"2022-07-20 21:52 +0200\"))") fn julian_date(dt: DateTime) -> Time = (dt - datetime("-4713-11-24 12:00:00 +0000")) -> days diff --git a/numbat/modules/datetime/human.nbt b/numbat/modules/datetime/human.nbt index c4b1a282..e6701e99 100644 --- a/numbat/modules/datetime/human.nbt +++ b/numbat/modules/datetime/human.nbt @@ -33,6 +33,7 @@ fn _human_readable_duration(time: Time, dt: DateTime, num_days: Scalar) -> Strin @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.") +@example("century/1e6 -> human", "How long is a microcentury?") fn human(time: Time) = if _human_num_days(time) > 1000 then "{_human_num_days(time)} days" diff --git a/numbat/modules/extra/algebra.nbt b/numbat/modules/extra/algebra.nbt index 60e034d1..fd7bccf3 100644 --- a/numbat/modules/extra/algebra.nbt +++ b/numbat/modules/extra/algebra.nbt @@ -7,6 +7,7 @@ fn _qe_solution(a: A, b: B, c: B² / A, sign: Scalar) -> B / A = @name("Solve quadratic equations") @url("https://en.wikipedia.org/wiki/Quadratic_equation") @description("Returns the solutions of the equation a x² + b x + c = 0") +@example("quadratic_equation(2, -1, -1)", "Solve the equation $2x² -x -1 = 0$") fn quadratic_equation(a: A, b: B, c: B² / A) -> List = if a == 0 then if b == 0 diff --git a/numbat/modules/extra/color.nbt b/numbat/modules/extra/color.nbt index 6b50c20a..89af9e94 100644 --- a/numbat/modules/extra/color.nbt +++ b/numbat/modules/extra/color.nbt @@ -9,10 +9,12 @@ struct Color { } @description("Create a `Color` from RGB (red, green, blue) values in the range $[0, 256)$.") +@example("rgb(125, 128, 218)") fn rgb(red: Scalar, green: Scalar, blue: Scalar) -> Color = Color { red: red, green: green, blue: blue } -@description("Create a `Color` from a (hexadecimal) value, e.g. `color(0xff7700)`") +@description("Create a `Color` from a (hexadecimal) value.") +@example("color(0xff7700)") fn color(rgb_hex: Scalar) -> Color = rgb( floor(rgb_hex / 256^2), @@ -22,15 +24,18 @@ fn color(rgb_hex: Scalar) -> Color = fn _color_to_scalar(color: Color) -> Scalar = color.red * 0x010000 + color.green * 0x000100 + color.blue -@description("Convert a color to its RGB representation, e.g. `cyan -> color_rgb`") +@description("Convert a color to its RGB representation.") +@example("cyan -> color_rgb") fn color_rgb(color: Color) -> String = "rgb({color.red}, {color.green}, {color.blue})" -@description("Convert a color to its RGB floating point representation, e.g. `cyan -> color_rgb_float`") +@description("Convert a color to its RGB floating point representation.") +@example("cyan -> color_rgb_float") fn color_rgb_float(color: Color) -> String = "rgb({color.red / 255:.3}, {color.green / 255:.3}, {color.blue / 255:.3})" -@description("Convert a color to its hexadecimal representation, e.g. `rgb(225, 36, 143) -> color_hex`") +@description("Convert a color to its hexadecimal representation.") +@example("rgb(225, 36, 143) -> color_hex") fn color_hex(color: Color) -> String = str_append("#", str_replace(str_replace("{color -> _color_to_scalar -> hex:>8}", "0x", ""), " ", "0")) diff --git a/numbat/modules/math/geometry.nbt b/numbat/modules/math/geometry.nbt index e2029e20..57b09932 100644 --- a/numbat/modules/math/geometry.nbt +++ b/numbat/modules/math/geometry.nbt @@ -2,9 +2,11 @@ use core::functions use math::constants @description("The length of the hypotenuse of a right-angled triangle $\\sqrt\{x^2+y^2\}$.") +@example("hypot2(3, 4)") fn hypot2(x: T, y: T) -> T = sqrt(x^2 + y^2) @description("The Euclidean norm of a 3D vector $\\sqrt\{x^2+y^2+z^2\}$.") +@example("hypot3(4, 1, 4)") fn hypot3(x: T, y: T, z: T) -> T = sqrt(x^2 + y^2 + z^2) # The following functions use a generic dimension instead of diff --git a/numbat/modules/math/number_theory.nbt b/numbat/modules/math/number_theory.nbt index e3d45420..1bef78c7 100644 --- a/numbat/modules/math/number_theory.nbt +++ b/numbat/modules/math/number_theory.nbt @@ -4,6 +4,7 @@ use core::functions @name("Greatest common divisor") @description("The largest positive integer that divides each of the integers $a$ and $b$.") @url("https://en.wikipedia.org/wiki/Greatest_common_divisor") +@example("gcd(60,42)") fn gcd(a: Scalar, b: Scalar) -> Scalar = if b == 0 then abs(a) @@ -12,4 +13,5 @@ fn gcd(a: Scalar, b: Scalar) -> Scalar = @name("Least common multiple") @description("The smallest positive integer that is divisible by both $a$ and $b$.") @url("https://en.wikipedia.org/wiki/Least_common_multiple") +@example("lcm(14, 4)") fn lcm(a: Scalar, b: Scalar) -> Scalar = abs(a * b) / gcd(a, b) diff --git a/numbat/modules/math/statistics.nbt b/numbat/modules/math/statistics.nbt index 293cb076..c99d3e22 100644 --- a/numbat/modules/math/statistics.nbt +++ b/numbat/modules/math/statistics.nbt @@ -5,38 +5,44 @@ fn _max(x: D, y: D) -> D = if x > y then x else y fn _min(x: D, y: D) -> D = if x < y then x else y @name("Maxmimum") -@description("Get the largest element of a list: `maximum([30 cm, 2 m]) = 2 m`.") +@description("Get the largest element of a list.") +@example("maximum([30 cm, 2 m])") fn maximum(xs: List) -> D = if len(xs) == 1 then head(xs) else _max(head(xs), maximum(tail(xs))) @name("Minimum") -@description("Get the smallest element of a list: `minimum([30 cm, 2 m]) = 30 cm`.") +@description("Get the smallest element of a list.") +@example("minimum([30 cm, 2 m])") fn minimum(xs: List) -> D = if len(xs) == 1 then head(xs) else _min(head(xs), minimum(tail(xs))) @name("Arithmetic mean") -@description("Calculate the arithmetic mean of a list of quantities: `mean([1 m, 2 m, 300 cm]) = 2 m`.") +@description("Calculate the arithmetic mean of a list of quantities.") +@example("mean([1 m, 2 m, 300 cm])") @url("https://en.wikipedia.org/wiki/Arithmetic_mean") fn mean(xs: List) -> D = if is_empty(xs) then 0 else sum(xs) / len(xs) @name("Variance") @url("https://en.wikipedia.org/wiki/Variance") @description("Calculate the population variance of a list of quantities") +@example("variance([1 m, 2 m, 300 cm])") fn variance(xs: List) -> D^2 = mean(map(sqr, xs)) - sqr(mean(xs)) @name("Standard deviation") @url("https://en.wikipedia.org/wiki/Standard_deviation") @description("Calculate the population standard deviation of a list of quantities") +@example("stdev([1 m, 2 m, 300 cm])") fn stdev(xs: List) -> D = sqrt(variance(xs)) @name("Median") @url("https://en.wikipedia.org/wiki/Median") @description("Calculate the median of a list of quantities") +@example("median([1 m, 2 m, 400 cm])") fn median(xs: List) -> D = # TODO: this is extremely inefficient if mod(n, 2) == 1 then element_at((n - 1) / 2, sort(xs)) diff --git a/numbat/modules/math/transcendental.nbt b/numbat/modules/math/transcendental.nbt index f079c78f..6e838e8e 100644 --- a/numbat/modules/math/transcendental.nbt +++ b/numbat/modules/math/transcendental.nbt @@ -3,26 +3,31 @@ use core::scalar @name("Exponential function") @description("The exponential function, $e^x$.") @url("https://en.wikipedia.org/wiki/Exponential_function") +@example("exp(4)") fn exp(x: Scalar) -> Scalar @name("Natural logarithm") @description("The natural logarithm with base $e$.") @url("https://en.wikipedia.org/wiki/Natural_logarithm") +@example("ln(20)") fn ln(x: Scalar) -> Scalar @name("Natural logarithm") @description("The natural logarithm with base $e$.") @url("https://en.wikipedia.org/wiki/Natural_logarithm") +@example("log(20)") fn log(x: Scalar) -> Scalar = ln(x) @name("Common logarithm") @description("The common logarithm with base $10$.") @url("https://en.wikipedia.org/wiki/Common_logarithm") +@example("log10(100)") fn log10(x: Scalar) -> Scalar @name("Binary logarithm") @description("The binary logarithm with base $2$.") @url("https://en.wikipedia.org/wiki/Binary_logarithm") +@example("log2(256)") fn log2(x: Scalar) -> Scalar @name("Gamma function") diff --git a/numbat/modules/numerics/diff.nbt b/numbat/modules/numerics/diff.nbt index 63c3c87b..e96ca391 100644 --- a/numbat/modules/numerics/diff.nbt +++ b/numbat/modules/numerics/diff.nbt @@ -3,6 +3,7 @@ use core::quantities @name("Numerical differentiation") @url("https://en.wikipedia.org/wiki/Numerical_differentiation") @description("Compute the numerical derivative of the function $f$ at point $x$ using the central difference method.") +@example("fn polynomial(x) = x² -x -1\ndiff(polynomial, 1)", "Compute the drivative of $f(x) = x² -x -1$ at $x=1$.") fn diff(f: Fn[(X) -> Y], x: X) -> Y / X = (f(x + Δx) - f(x - Δx)) / 2 Δx where diff --git a/numbat/modules/numerics/fixed_point.nbt b/numbat/modules/numerics/fixed_point.nbt index a046c9e9..8ee9e978 100644 --- a/numbat/modules/numerics/fixed_point.nbt +++ b/numbat/modules/numerics/fixed_point.nbt @@ -15,5 +15,6 @@ fn _fixed_point(f: Fn[(X) -> X], x0: X, ε: X, max_iter: Scalar) = @name("Fixed-point iteration") @url("https://en.wikipedia.org/wiki/Fixed-point_iteration") @description("Compute the approximate fixed point of a function $f: X \\rightarrow X$ starting from $x_0$, until $|f(x) - x| < ε$.") +@example("fn function(x) = x/2 - 1\nfixed_point(function, 0, 0.01)", "Compute the fixed poin of $f(x) = x/2 -1$.") fn fixed_point(f: Fn[(X) -> X], x0: X, ε: X) = _fixed_point(f, x0, ε, 100) diff --git a/numbat/modules/numerics/solve.nbt b/numbat/modules/numerics/solve.nbt index 7335a39b..6bd5703b 100644 --- a/numbat/modules/numerics/solve.nbt +++ b/numbat/modules/numerics/solve.nbt @@ -4,6 +4,7 @@ use core::error @name("Bisection method") @url("https://en.wikipedia.org/wiki/Bisection_method") @description("Find the root of the function $f$ in the interval $[x_1, x_2]$ using the bisection method. The function $f$ must be continuous and $f(x_1) \cdot f(x_2) < 0$.") +@example("fn f(x) = x² +x -2\nroot_bisect(f, 0, 100, 0.01, 0.01)", "Find the root of $f(x) = x² +x -2$ in the interval $[0, 100]$.") fn root_bisect(f: Fn[(X) -> Y], x1: X, x2: X, x_tol: X, y_tol: Y) -> X = if abs(x1 - x2) < x_tol then x_mean @@ -27,5 +28,6 @@ fn _root_newton_helper(f: Fn[(X) -> Y], f_prime: Fn[(X) -> Y / X @name("Newton's method") @url("https://en.wikipedia.org/wiki/Newton%27s_method") @description("Find the root of the function $f(x)$ and its derivative $f'(x)$ using Newton's method.") +@example("fn f(x) = x² -3x +2\nfn f_prime(x) = 2x -3\nroot_newton(f, f_prime, 0 , 0.01)", "Find a root of $f(x) = x² -3x +2$ using Newton's method.") fn root_newton(f: Fn[(X) -> Y], f_prime: Fn[(X) -> Y / X], x0: X, y_tol: Y) -> X = _root_newton_helper(f, f_prime, x0, y_tol, 10_000) diff --git a/numbat/modules/physics/temperature_conversion.nbt b/numbat/modules/physics/temperature_conversion.nbt index 8dac933d..89a2bc16 100644 --- a/numbat/modules/physics/temperature_conversion.nbt +++ b/numbat/modules/physics/temperature_conversion.nbt @@ -5,10 +5,12 @@ use units::si let _offset_celsius = 273.15 @description("Converts from degree Celsius (°C) to Kelvin.") +@example("from_celsius(300)", "$300 °C$ in Kelvin.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn from_celsius(t_celsius: Scalar) -> Temperature = (t_celsius + _offset_celsius) kelvin @description("Converts from Kelvin to degree Celcius (°C). This can be used on the right hand side of a conversion operator: `200 K -> celsius`.") +@example("300K -> celsius", "$300K$ in degree Celsius.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn celsius(t_kelvin: Temperature) -> Scalar = t_kelvin / kelvin - _offset_celsius @@ -16,9 +18,11 @@ let _offset_fahrenheit = 459.67 let _scale_fahrenheit = 5 / 9 @description("Converts from degree Fahrenheit (°F) to Kelvin.") +@example("from_fahrenheit(300)", "$300 °F$ in Kelvin.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn from_fahrenheit(t_fahrenheit: Scalar) -> Temperature = ((t_fahrenheit + _offset_fahrenheit) × _scale_fahrenheit) kelvin @description("Converts from Kelvin to degree Fahrenheit (°F). This can be used on the right hand side of a conversion operator: `200 K -> fahrenheit`.") +@example("300K -> fahrenheit", "$300K$ in degree Fahrenheit.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn fahrenheit(t_kelvin: Temperature) -> Scalar = (t_kelvin / kelvin) / _scale_fahrenheit - _offset_fahrenheit diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index 8ef93e26..9b688cf3 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -5,11 +5,13 @@ use units::imperial @name("Degrees, minutes, seconds") @description("Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation.") @url("https://en.wikipedia.org/wiki/Sexagesimal_degree") +@example("DMS(46.5858°)") fn DMS(alpha: Angle) -> String = _mixed_units(alpha, [deg, arcmin, arcsec], ["° ", "′ ", "″"], true) @name("Degrees, decimal minutes") @description("Convert an angle to a mixed degrees and decimal minutes representation.") +@example("DM(46.5858°)") @url("https://en.wikipedia.org/wiki/Decimal_degrees") fn DM(alpha: Angle) -> String = _mixed_units(alpha, [deg, arcmin], ["° ", "′"], false) @@ -17,11 +19,13 @@ fn DM(alpha: Angle) -> String = @name("Feet and inches") @description("Convert a length to a mixed feet and inches representation.") @url("https://en.wikipedia.org/wiki/Foot_(unit)") +@example("feet_and_inches(180cm)") fn feet_and_inches(length: Length) -> String = _mixed_units(length, [foot, inch], [" ft ", " in"], false) @name("Pounds and ounces") @description("Convert a mass to a mixed pounds and ounces representation.") @url("https://en.wikipedia.org/wiki/Pound_(mass)") +@example("pounds_and_ounces(1kg)") fn pounds_and_ounces(mass: Mass) -> String = _mixed_units(mass, [pound, ounce], [" lb ", " oz"], false) From f6dd12a919aa4f5f721399355ab6b69d1cd12a65 Mon Sep 17 00:00:00 2001 From: Bzero Date: Mon, 16 Sep 2024 19:32:03 +0200 Subject: [PATCH 04/46] Move details closing tag inside conditional --- book/src/list-functions-datetime.md | 6 +- book/src/list-functions-math.md | 86 ---------------- book/src/list-functions-other.md | 2 - numbat/examples/inspect.rs | 150 ++++++++++++++-------------- 4 files changed, 76 insertions(+), 168 deletions(-) diff --git a/book/src/list-functions-datetime.md b/book/src/list-functions-datetime.md index d43ad85b..5f763182 100644 --- a/book/src/list-functions-datetime.md +++ b/book/src/list-functions-datetime.md @@ -11,8 +11,6 @@ Returns the current date and time. fn now() -> DateTime ``` - - ### `datetime` Parses a string (date and time) into a `DateTime` object. See [here](./date-and-time.md#date-time-formats) for an overview of the supported formats. @@ -182,8 +180,6 @@ Returns the current date at midnight (in the local time). fn today() -> DateTime ``` - - ### `date` Parses a string (only date) into a `DateTime` object. @@ -223,7 +219,7 @@ fn time(input: String) -> DateTime time("21:52") - = 2024-09-15 21:52:00 UTC [DateTime] + = 2024-09-16 21:52:00 UTC [DateTime] ``` diff --git a/book/src/list-functions-math.md b/book/src/list-functions-math.md index 891d1bfb..8849b265 100644 --- a/book/src/list-functions-math.md +++ b/book/src/list-functions-math.md @@ -36,8 +36,6 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn abs(x: T) -> T ``` - - ### `sqrt` (Square root) Return the square root \\( \sqrt{x} \\) of the input: `sqrt(121 m^2) = 11 m`. More information [here](https://en.wikipedia.org/wiki/Square_root). @@ -504,8 +502,6 @@ More information [here](https://en.wikipedia.org/wiki/Gamma_function). fn gamma(x: Scalar) -> Scalar ``` - - ## Trigonometry Defined in: `math::trigonometry` @@ -517,8 +513,6 @@ More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). fn sin(x: Scalar) -> Scalar ``` - - ### `cos` (Cosine) More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). @@ -526,8 +520,6 @@ More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). fn cos(x: Scalar) -> Scalar ``` - - ### `tan` (Tangent) More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). @@ -535,8 +527,6 @@ More information [here](https://en.wikipedia.org/wiki/Trigonometric_functions). fn tan(x: Scalar) -> Scalar ``` - - ### `asin` (Arc sine) More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions). @@ -544,8 +534,6 @@ More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_func fn asin(x: Scalar) -> Scalar ``` - - ### `acos` (Arc cosine) More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions). @@ -553,8 +541,6 @@ More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_func fn acos(x: Scalar) -> Scalar ``` - - ### `atan` (Arc tangent) More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_functions). @@ -562,8 +548,6 @@ More information [here](https://en.wikipedia.org/wiki/Inverse_trigonometric_func fn atan(x: Scalar) -> Scalar ``` - - ### `atan2` More information [here](https://en.wikipedia.org/wiki/Atan2). @@ -571,8 +555,6 @@ More information [here](https://en.wikipedia.org/wiki/Atan2). fn atan2(y: T, x: T) -> Scalar ``` - - ### `sinh` (Hyperbolic sine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -580,8 +562,6 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn sinh(x: Scalar) -> Scalar ``` - - ### `cosh` (Hyperbolic cosine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -589,8 +569,6 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn cosh(x: Scalar) -> Scalar ``` - - ### `tanh` (Hyperbolic tangent) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -598,8 +576,6 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn tanh(x: Scalar) -> Scalar ``` - - ### `asinh` (Area hyperbolic sine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -607,8 +583,6 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn asinh(x: Scalar) -> Scalar ``` - - ### `acosh` (Area hyperbolic cosine) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -616,8 +590,6 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn acosh(x: Scalar) -> Scalar ``` - - ### `atanh` (Area hyperbolic tangent ) More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). @@ -625,8 +597,6 @@ More information [here](https://en.wikipedia.org/wiki/Hyperbolic_functions). fn atanh(x: Scalar) -> Scalar ``` - - ## Statistics Defined in: `math::statistics` @@ -778,8 +748,6 @@ Uniformly samples the interval \\( [0,1) \\). fn random() -> Scalar ``` - - ### `rand_uniform` (Continuous uniform distribution sampling) Uniformly samples the interval \\( [a,b) \\) if \\( a \le b \\) or \\( [b,a) \\) if \\( b(a: T, b: T) -> T ``` - - ### `rand_int` (Discrete uniform distribution sampling) Uniformly samples integers from the interval \\( [a, b] \\). More information [here](https://en.wikipedia.org/wiki/Discrete_uniform_distribution). @@ -798,8 +764,6 @@ More information [here](https://en.wikipedia.org/wiki/Discrete_uniform_distribut fn rand_int(a: Scalar, b: Scalar) -> Scalar ``` - - ### `rand_bernoulli` (Bernoulli distribution sampling) Samples a Bernoulli random variable. That is, \\( 1 \\) with probability \\( p \\) and \\( 0 \\) with probability \\( 1-p \\). The parameter \\( p \\) must be a probability (\\( 0 \le p \le 1 \\)). More information [here](https://en.wikipedia.org/wiki/Bernoulli_distribution). @@ -808,8 +772,6 @@ More information [here](https://en.wikipedia.org/wiki/Bernoulli_distribution). fn rand_bernoulli(p: Scalar) -> Scalar ``` - - ### `rand_binom` (Binomial distribution sampling) Samples a binomial distribution by doing \\( n \\) Bernoulli trials with probability \\( p \\). The parameter \\( n \\) must be a positive integer, the parameter \\( p \\) must be a probability (\\( 0 \le p \le 1 \\)). @@ -819,8 +781,6 @@ More information [here](https://en.wikipedia.org/wiki/Binomial_distribution). fn rand_binom(n: Scalar, p: Scalar) -> Scalar ``` - - ### `rand_norm` (Normal distribution sampling) Samples a normal distribution with mean \\( \mu \\) and standard deviation \\( \sigma \\) using the Box-Muller transform. More information [here](https://en.wikipedia.org/wiki/Normal_distribution). @@ -829,8 +789,6 @@ More information [here](https://en.wikipedia.org/wiki/Normal_distribution). fn rand_norm(μ: T, σ: T) -> T ``` - - ### `rand_geom` (Geometric distribution sampling) Samples a geometric distribution (the distribution of the number of Bernoulli trials with probability \\( p \\) needed to get one success) by inversion sampling. The parameter \\( p \\) must be a probability (\\( 0 \le p \le 1 \\)). More information [here](https://en.wikipedia.org/wiki/Geometric_distribution). @@ -839,8 +797,6 @@ More information [here](https://en.wikipedia.org/wiki/Geometric_distribution). fn rand_geom(p: Scalar) -> Scalar ``` - - ### `rand_poisson` (Poisson distribution sampling) Sampling a poisson distribution with rate \\( \lambda \\), that is, the distribution of the number of events occurring in a fixed interval if these events occur with mean rate \\( \lambda \\). The rate parameter \\( \lambda \\) must be non-negative. More information [here](https://en.wikipedia.org/wiki/Poisson_distribution). @@ -849,8 +805,6 @@ More information [here](https://en.wikipedia.org/wiki/Poisson_distribution). fn rand_poisson(λ: Scalar) -> Scalar ``` - - ### `rand_expon` (Exponential distribution sampling) Sampling an exponential distribution (the distribution of the distance between events in a Poisson process with rate \\( \lambda \\)) using inversion sampling. The rate parameter \\( \lambda \\) must be positive. More information [here](https://en.wikipedia.org/wiki/Exponential_distribution). @@ -859,8 +813,6 @@ More information [here](https://en.wikipedia.org/wiki/Exponential_distribution). fn rand_expon(λ: T) -> 1 / T ``` - - ### `rand_lognorm` (Log-normal distribution sampling) Sampling a log-normal distribution, that is, a distribution whose logarithm is a normal distribution with mean \\( \mu \\) and standard deviation \\( \sigma \\). More information [here](https://en.wikipedia.org/wiki/Log-normal_distribution). @@ -869,8 +821,6 @@ More information [here](https://en.wikipedia.org/wiki/Log-normal_distribution). fn rand_lognorm(μ: Scalar, σ: Scalar) -> Scalar ``` - - ### `rand_pareto` (Pareto distribution sampling) Sampling a Pareto distribution with minimum value `min` and shape parameter \\( \alpha \\) using inversion sampling. Both parameters must be positive. More information [here](https://en.wikipedia.org/wiki/Pareto_distribution). @@ -879,8 +829,6 @@ More information [here](https://en.wikipedia.org/wiki/Pareto_distribution). fn rand_pareto(α: Scalar, min: T) -> T ``` - - ## Number theory Defined in: `math::number_theory` @@ -1101,8 +1049,6 @@ The area of a circle, \\( \pi r^2 \\). fn circle_area(radius: L) -> L^2 ``` - - ### `circle_circumference` The circumference of a circle, \\( 2\pi r \\). @@ -1110,8 +1056,6 @@ The circumference of a circle, \\( 2\pi r \\). fn circle_circumference(radius: L) -> L ``` - - ### `sphere_area` The surface area of a sphere, \\( 4\pi r^2 \\). @@ -1119,8 +1063,6 @@ The surface area of a sphere, \\( 4\pi r^2 \\). fn sphere_area(radius: L) -> L^2 ``` - - ### `sphere_volume` The volume of a sphere, \\( \frac{4}{3}\pi r^3 \\). @@ -1128,8 +1070,6 @@ The volume of a sphere, \\( \frac{4}{3}\pi r^3 \\). fn sphere_volume(radius: L) -> L^3 ``` - - ## Algebra Defined in: `extra::algebra` @@ -1168,101 +1108,75 @@ Defined in: `math::trigonometry_extra` fn cot(x: Scalar) -> Scalar ``` - - ### `acot` ```nbt fn acot(x: Scalar) -> Scalar ``` - - ### `coth` ```nbt fn coth(x: Scalar) -> Scalar ``` - - ### `acoth` ```nbt fn acoth(x: Scalar) -> Scalar ``` - - ### `secant` ```nbt fn secant(x: Scalar) -> Scalar ``` - - ### `arcsecant` ```nbt fn arcsecant(x: Scalar) -> Scalar ``` - - ### `cosecant` ```nbt fn cosecant(x: Scalar) -> Scalar ``` - - ### `csc` ```nbt fn csc(x: Scalar) -> Scalar ``` - - ### `acsc` ```nbt fn acsc(x: Scalar) -> Scalar ``` - - ### `sech` ```nbt fn sech(x: Scalar) -> Scalar ``` - - ### `asech` ```nbt fn asech(x: Scalar) -> Scalar ``` - - ### `csch` ```nbt fn csch(x: Scalar) -> Scalar ``` - - ### `acsch` ```nbt fn acsch(x: Scalar) -> Scalar ``` - - diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 90363f6f..ef5173d1 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -13,8 +13,6 @@ Throw an error with the specified message. Stops the execution of the program. fn error(message: String) -> T ``` - - ## Floating point Defined in: `core::numbers` diff --git a/numbat/examples/inspect.rs b/numbat/examples/inspect.rs index 74f65c67..f1fe019c 100644 --- a/numbat/examples/inspect.rs +++ b/numbat/examples/inspect.rs @@ -84,94 +84,94 @@ fn inspect_functions_in_module(ctx: &Context, module: String) { println!("
"); println!("Examples"); println!(); - } - - for (example_code, example_description) in examples { - let mut example_ctx = prepare_context(); - let _result = example_ctx - .interpret("use prelude", CodeSource::Internal) - .unwrap(); - - let extra_import = if !example_ctx - .resolver() - .imported_modules - .contains(&module_path) - { - format!("use {}\n", module) - } else { - "".into() - }; - let _result = example_ctx - .interpret(&extra_import, CodeSource::Internal) - .unwrap(); - - if let Ok((statements, results)) = - example_ctx.interpret(&example_code, CodeSource::Internal) - { - //Format the example input - let example_input = format!(">>> {}", example_code); - - //Encode the example url - let url_code = extra_import + &example_code; - let example_url = format!( - "https://numbat.dev/?q={}", - percent_encoding::utf8_percent_encode( - &url_code, - percent_encoding::NON_ALPHANUMERIC - ) - ); - - //Assemble the example output - let mut example_output = String::new(); - example_output += "\n"; - - for statement in &statements { - example_output += &plain_text_format(&statement.pretty_print(), true); - example_output += "\n\n"; - } - let result_markup = results.to_markup( - statements.last(), - &example_ctx.dimension_registry(), - true, - true, - ); - example_output += &plain_text_format(&result_markup, false); + for (example_code, example_description) in examples { + let mut example_ctx = prepare_context(); + let _result = example_ctx + .interpret("use prelude", CodeSource::Internal) + .unwrap(); + + let extra_import = if !example_ctx + .resolver() + .imported_modules + .contains(&module_path) + { + format!("use {}\n", module) + } else { + "".into() + }; + let _result = example_ctx + .interpret(&extra_import, CodeSource::Internal) + .unwrap(); + + if let Ok((statements, results)) = + example_ctx.interpret(&example_code, CodeSource::Internal) + { + //Format the example input + let example_input = format!(">>> {}", example_code); + + //Encode the example url + let url_code = extra_import + &example_code; + let example_url = format!( + "https://numbat.dev/?q={}", + percent_encoding::utf8_percent_encode( + &url_code, + percent_encoding::NON_ALPHANUMERIC + ) + ); - if results.is_value() { + //Assemble the example output + let mut example_output = String::new(); example_output += "\n"; - } - //Print the example - if let Some(example_description) = example_description { - println!( + for statement in &statements { + example_output += &plain_text_format(&statement.pretty_print(), true); + example_output += "\n\n"; + } + + let result_markup = results.to_markup( + statements.last(), + &example_ctx.dimension_registry(), + true, + true, + ); + example_output += &plain_text_format(&result_markup, false); + + if results.is_value() { + example_output += "\n"; + } + + //Print the example + if let Some(example_description) = example_description { + println!( "* {}\n\n Run this example", replace_equation_delimiters(example_description), example_url ); + } else { + println!( + "* Run this example\n", + example_url + ); + } + + println!(" ```nbt"); + for l in example_input.lines() { + println!(" {}", l); + } + for l in example_output.lines() { + println!(" {}", l); + } + println!(" ```"); } else { - println!( - "* Run this example\n", - example_url - ); - } - - println!(" ```nbt"); - for l in example_input.lines() { - println!(" {}", l); - } - for l in example_output.lines() { - println!(" {}", l); - } - println!(" ```"); - } else { - eprintln!( + eprintln!( "Warning: Example \"{example_code}\" of function {fn_name} did not run successfully." ); + } } + println!("
"); + println!(); } - println!(""); - println!(); } } From b32bb203d133f04c707d2d7d9dd7d8c14b4e0fa6 Mon Sep 17 00:00:00 2001 From: Bzero Date: Tue, 24 Sep 2024 16:33:05 +0200 Subject: [PATCH 05/46] Remove example for the time function --- book/src/list-functions-datetime.md | 15 --------------- numbat/modules/datetime/functions.nbt | 1 - 2 files changed, 16 deletions(-) diff --git a/book/src/list-functions-datetime.md b/book/src/list-functions-datetime.md index 5f763182..ebedccc8 100644 --- a/book/src/list-functions-datetime.md +++ b/book/src/list-functions-datetime.md @@ -209,21 +209,6 @@ Parses a string (time only) into a `DateTime` object. fn time(input: String) -> DateTime ``` -
-Examples - -* Run this example - - ```nbt - >>> time("21:52") - - time("21:52") - - = 2024-09-16 21:52:00 UTC [DateTime] - - ``` -
- ### `calendar_add` Adds the given time span to a `DateTime`. This uses leap-year and DST-aware calendar arithmetic with variable-length days, months, and years. diff --git a/numbat/modules/datetime/functions.nbt b/numbat/modules/datetime/functions.nbt index 878e96c7..4e350a4d 100644 --- a/numbat/modules/datetime/functions.nbt +++ b/numbat/modules/datetime/functions.nbt @@ -52,7 +52,6 @@ fn date(input: String) -> DateTime = else datetime("{input} 00:00:00") @description("Parses a string (time only) into a `DateTime` object.") -@example("time(\"21:52\")") fn time(input: String) -> DateTime = datetime("{_today_str()} {input}") From 42f29767186de0b34a4a5825cf9868068492a30d Mon Sep 17 00:00:00 2001 From: Bzero Date: Tue, 24 Sep 2024 17:26:15 +0200 Subject: [PATCH 06/46] Apply suggested example formatting --- book/src/list-functions-lists.md | 8 ++++---- book/src/list-functions-math.md | 27 +++++++++++++++++++++------ book/src/list-functions-strings.md | 4 ++-- numbat/modules/core/functions.nbt | 8 ++++---- numbat/modules/core/lists.nbt | 2 +- numbat/modules/core/strings.nbt | 2 +- 6 files changed, 33 insertions(+), 18 deletions(-) diff --git a/book/src/list-functions-lists.md b/book/src/list-functions-lists.md index 7a9659b1..dfad0612 100644 --- a/book/src/list-functions-lists.md +++ b/book/src/list-functions-lists.md @@ -450,14 +450,14 @@ fn linspace(start: D, end: D, n_steps: Scalar) -> List
Examples -* Run this example +* Run this example ```nbt - >>> linspace(0, 10, 11) + >>> linspace(-5 m, 5 m, 11) - linspace(0, 10, 11) + linspace(-(5 metre), 5 metre, 11) - = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [List] + = [-5 m, -4 m, -3 m, -2 m, -1 m, 0 m, 1 m, 2 m, 3 m, 4 m, 5 m] [List] ```
diff --git a/book/src/list-functions-math.md b/book/src/list-functions-math.md index 8849b265..e55cdafe 100644 --- a/book/src/list-functions-math.md +++ b/book/src/list-functions-math.md @@ -16,10 +16,10 @@ fn id(x: A) -> A
Examples -* Run this example +* Run this example ```nbt - >>> id(8kg) + >>> id(8 kg) id(8 kilogram) @@ -36,6 +36,21 @@ More information [here](https://doc.rust-lang.org/std/primitive.f64.html#method. fn abs(x: T) -> T ``` +
+Examples + +* Run this example + + ```nbt + >>> abs(-22.2 m) + + abs(-(22.2 metre)) + + = 22.2 m [Length] + + ``` +
+ ### `sqrt` (Square root) Return the square root \\( \sqrt{x} \\) of the input: `sqrt(121 m^2) = 11 m`. More information [here](https://en.wikipedia.org/wiki/Square_root). @@ -47,10 +62,10 @@ fn sqrt(x: D^2) -> D
Examples -* Run this example +* Run this example ```nbt - >>> sqrt(4are) -> m + >>> sqrt(4 are) -> m sqrt(4 are) ➞ metre @@ -70,10 +85,10 @@ fn cbrt(x: D^3) -> D
Examples -* Run this example +* Run this example ```nbt - >>> cbrt(8l) -> cm + >>> cbrt(8 L) -> cm cbrt(8 litre) ➞ centimetre diff --git a/book/src/list-functions-strings.md b/book/src/list-functions-strings.md index b8ca8b78..cb1e1497 100644 --- a/book/src/list-functions-strings.md +++ b/book/src/list-functions-strings.md @@ -144,10 +144,10 @@ fn str_append(a: String, b: String) -> String
Examples -* Run this example +* Run this example ```nbt - >>> str_append("Numbat","!") + >>> str_append("Numbat", "!") str_append("Numbat", "!") diff --git a/numbat/modules/core/functions.nbt b/numbat/modules/core/functions.nbt index a1ee5b76..07c0e757 100644 --- a/numbat/modules/core/functions.nbt +++ b/numbat/modules/core/functions.nbt @@ -2,25 +2,25 @@ use core::scalar @name("Identity function") @description("Return the input value.") -@example("id(8kg)") +@example("id(8 kg)") fn id(x: A) -> A = x @name("Absolute value") @description("Return the absolute value $|x|$ of the input. This works for quantities, too: `abs(-5 m) = 5 m`.") @url("https://doc.rust-lang.org/std/primitive.f64.html#method.abs") -# @example("abs(-22.2m)") +@example("abs(-22.2 m)") fn abs(x: T) -> T @name("Square root") @description("Return the square root $\\sqrt\{x\}$ of the input: `sqrt(121 m^2) = 11 m`.") @url("https://en.wikipedia.org/wiki/Square_root") -@example("sqrt(4are) -> m") +@example("sqrt(4 are) -> m") fn sqrt(x: D^2) -> D = x^(1/2) @name("Cube root") @description("Return the cube root $\\sqrt[3]\{x\}$ of the input: `cbrt(8 m^3) = 2 m`.") @url("https://en.wikipedia.org/wiki/Cube_root") -@example("cbrt(8l) -> cm") +@example("cbrt(8 L) -> cm") fn cbrt(x: D^3) -> D = x^(1/3) @name("Square function") diff --git a/numbat/modules/core/lists.nbt b/numbat/modules/core/lists.nbt index 662383da..ff52b5f7 100644 --- a/numbat/modules/core/lists.nbt +++ b/numbat/modules/core/lists.nbt @@ -139,7 +139,7 @@ fn _linspace_helper(start, end, n_steps, i) = else cons(start + (end - start) * i / (n_steps - 1), _linspace_helper(start, end, n_steps, i + 1)) @description("Generate a list of `n_steps` evenly spaced numbers from `start` to `end` (inclusive)") -@example("linspace(0, 10, 11)") +@example("linspace(-5 m, 5 m, 11)") fn linspace(start: D, end: D, n_steps: Scalar) -> List = if n_steps <= 1 then error("Number of steps must be larger than 1") diff --git a/numbat/modules/core/strings.nbt b/numbat/modules/core/strings.nbt index 609d1cf5..7fd7250f 100644 --- a/numbat/modules/core/strings.nbt +++ b/numbat/modules/core/strings.nbt @@ -27,7 +27,7 @@ fn lowercase(s: String) -> String fn uppercase(s: String) -> String @description("Concatenate two strings") -@example("str_append(\"Numbat\",\"!\")") +@example("str_append(\"Numbat\", \"!\")") fn str_append(a: String, b: String) -> String = "{a}{b}" @description("Find the first occurrence of a substring in a string") From 17c2a9feabdd07cf01d944983541db1b6b974fc8 Mon Sep 17 00:00:00 2001 From: Bzero Date: Tue, 24 Sep 2024 17:38:59 +0200 Subject: [PATCH 07/46] Clone example context instead of re-interpreting --- numbat/examples/inspect.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/numbat/examples/inspect.rs b/numbat/examples/inspect.rs index f1fe019c..c046d057 100644 --- a/numbat/examples/inspect.rs +++ b/numbat/examples/inspect.rs @@ -45,7 +45,7 @@ and — where sensible — units allow for [binary prefixes](https://en.wikipedi } } -fn inspect_functions_in_module(ctx: &Context, module: String) { +fn inspect_functions_in_module(ctx: &Context, prelude_ctx: &Context, module: String) { for (fn_name, name, signature, description, url, examples, code_source) in ctx.functions() { let CodeSource::Module(module_path, _) = code_source else { unreachable!(); @@ -86,11 +86,7 @@ fn inspect_functions_in_module(ctx: &Context, module: String) { println!(); for (example_code, example_description) in examples { - let mut example_ctx = prepare_context(); - let _result = example_ctx - .interpret("use prelude", CodeSource::Internal) - .unwrap(); - + let mut example_ctx = prelude_ctx.clone(); let extra_import = if !example_ctx .resolver() .imported_modules @@ -201,6 +197,11 @@ fn main() { let mut ctx = prepare_context(); let _result = ctx.interpret("use all", CodeSource::Internal).unwrap(); + let mut example_ctx = prepare_context(); + let _result = example_ctx + .interpret("use prelude", CodeSource::Internal) + .unwrap(); + let mut args = std::env::args(); args.next(); if let Some(arg) = args.next() { @@ -208,7 +209,7 @@ fn main() { "units" => inspect_units(&ctx), "functions" => { let module = args.next().unwrap(); - inspect_functions_in_module(&ctx, module) + inspect_functions_in_module(&ctx, &example_ctx, module) } _ => eprintln!("USAGE: inspect [units|functions ]"), } From 45cba264965c79a5622422bed590462c2b263d17 Mon Sep 17 00:00:00 2001 From: Bzero Date: Sun, 29 Sep 2024 14:12:38 +0200 Subject: [PATCH 08/46] Apply example suggestions from code review Co-authored-by: David Peter --- book/src/list-functions-lists.md | 30 ++++++------- book/src/list-functions-math.md | 45 +++++++++++++------ book/src/list-functions-other.md | 24 +++++----- book/src/list-functions-strings.md | 8 ++-- numbat/modules/core/functions.nbt | 2 +- numbat/modules/core/lists.nbt | 6 +-- numbat/modules/core/strings.nbt | 2 +- numbat/modules/math/geometry.nbt | 4 +- numbat/modules/math/number_theory.nbt | 2 +- numbat/modules/numerics/diff.nbt | 3 +- .../physics/temperature_conversion.nbt | 8 ++-- numbat/modules/units/mixed.nbt | 8 ++-- 12 files changed, 78 insertions(+), 64 deletions(-) diff --git a/book/src/list-functions-lists.md b/book/src/list-functions-lists.md index dfad0612..d4416e93 100644 --- a/book/src/list-functions-lists.md +++ b/book/src/list-functions-lists.md @@ -309,18 +309,14 @@ fn filter(p: Fn[(A) -> Bool], xs: List) -> List
Examples -* Filter all elements greater than \\( 1 \\). +* Run this example - Run this example ```nbt - >>> fn filter_fn(x) = x > 1 - filter(filter_fn, [3, 2, 1, 0]) + >>> filter(is_finite, [0, 1e10, NaN, -inf]) - fn filter_fn(x: Scalar) -> Bool = x > 1 + filter(is_finite, [0, 10_000_000_000, NaN, -inf]) - filter(filter_fn, [3, 2, 1, 0]) - - = [3, 2] [List] + = [0, 10_000_000_000] [List] ```
@@ -360,14 +356,14 @@ fn sort_by_key(key: Fn[(A) -> D], xs: List) -> List * Sort by last digit. - Run this example + Run this example ```nbt - >>> fn map_fn(x) = mod(x, 10) - sort_by_key(map_fn, [701, 313, 9999, 4]) + >>> fn last_digit(x) = mod(x, 10) + sort_by_key(last_digit, [701, 313, 9999, 4]) - fn map_fn(x: Scalar) -> Scalar = mod(x, 10) + fn last_digit(x: Scalar) -> Scalar = mod(x, 10) - sort_by_key(map_fn, [701, 313, 9999, 4]) + sort_by_key(last_digit, [701, 313, 9999, 4]) = [701, 313, 4, 9999] [List] @@ -428,14 +424,14 @@ fn sum(xs: List) -> D
Examples -* Run this example +* Run this example ```nbt - >>> sum([3, 2, 1]) + >>> sum([3 m, 200 cm, 1000 mm]) - sum([3, 2, 1]) + sum([3 metre, 200 centimetre, 1000 millimetre]) - = 6 + = 6 m [Length] ```
diff --git a/book/src/list-functions-math.md b/book/src/list-functions-math.md index e55cdafe..6b224d44 100644 --- a/book/src/list-functions-math.md +++ b/book/src/list-functions-math.md @@ -267,7 +267,7 @@ fn ceil(x: Scalar) -> Scalar
### `ceil_in` (Ceil function) -Returns the smallest integer multuple of `base` greater than or equal to `value`. +Returns the smallest integer multiple of `base` greater than or equal to `value`. ```nbt fn ceil_in(base: D, value: D) -> D @@ -859,10 +859,10 @@ fn gcd(a: Scalar, b: Scalar) -> Scalar
Examples -* Run this example +* Run this example ```nbt - >>> gcd(60,42) + >>> gcd(60, 42) gcd(60, 42) @@ -909,11 +909,11 @@ fn diff(f: Fn[(X) -> Y], x: X) -> Y / X
Examples -* Compute the drivative of \\( f(x) = x² -x -1 \\) at \\( x=1 \\). +* Compute the derivative of \\( f(x) = x² -x -1 \\) at \\( x=1 \\). - Run this example + Run this example ```nbt - >>> fn polynomial(x) = x² -x -1 + >>> fn polynomial(x) = x² - x - 1 diff(polynomial, 1) fn polynomial(x: Scalar) -> Scalar = (x² - x) - 1 @@ -922,6 +922,23 @@ fn diff(f: Fn[(X) -> Y], x: X) -> Y / X = 1.0 + ``` +* Compute the free fall velocity after \\( t=2 s \\). + + Run this example + ```nbt + >>> fn distance(t) = 0.5 g0 t² + fn velocity(t) = diff(distance, t) + velocity(2 s) + + fn distance(t: A) -> A² × Length / Time² = 0.5 g0 × t² + + fn velocity(t: A) -> A × Length / Time² = diff(distance, t) + + velocity(2 second) + + = 19.6133 m/s [Velocity] + ```
@@ -1023,14 +1040,14 @@ fn hypot2(x: T, y: T) -> T
Examples -* Run this example +* Run this example ```nbt - >>> hypot2(3, 4) + >>> hypot2(3 m, 4 m) - hypot2(3, 4) + hypot2(3 metre, 4 metre) - = 5 + = 5 m [Length] ```
@@ -1045,14 +1062,14 @@ fn hypot3(x: T, y: T, z: T) -> T
Examples -* Run this example +* Run this example ```nbt - >>> hypot3(4, 1, 4) + >>> hypot3(8, 9, 12) - hypot3(4, 1, 4) + hypot3(8, 9, 12) - = 5.74456 + = 17 ```
diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index ef5173d1..804d93c4 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -216,10 +216,10 @@ fn DMS(alpha: Angle) -> String
Examples -* Run this example +* Run this example ```nbt - >>> DMS(46.5858°) + >>> 46.5858° -> DMS DMS(46.5858 degree) @@ -239,10 +239,10 @@ fn DM(alpha: Angle) -> String
Examples -* Run this example +* Run this example ```nbt - >>> DM(46.5858°) + >>> 46.5858° -> DM DM(46.5858 degree) @@ -262,10 +262,10 @@ fn feet_and_inches(length: Length) -> String
Examples -* Run this example +* Run this example ```nbt - >>> feet_and_inches(180cm) + >>> 180 cm -> feet_and_inches feet_and_inches(180 centimetre) @@ -285,10 +285,10 @@ fn pounds_and_ounces(mass: Mass) -> String
Examples -* Run this example +* Run this example ```nbt - >>> pounds_and_ounces(1kg) + >>> 1 kg -> pounds_and_ounces pounds_and_ounces(1 kilogram) @@ -312,7 +312,7 @@ fn from_celsius(t_celsius: Scalar) -> Temperature
Examples -* \\( 300 °C \\) in Kelvin. +* 300 °C in Kelvin. Run this example ```nbt @@ -336,7 +336,7 @@ fn celsius(t_kelvin: Temperature) -> Scalar
Examples -* \\( 300K \\) in degree Celsius. +* 300 K in degree Celsius. Run this example ```nbt @@ -360,7 +360,7 @@ fn from_fahrenheit(t_fahrenheit: Scalar) -> Temperature
Examples -* \\( 300 °F \\) in Kelvin. +* 300 °F in Kelvin. Run this example ```nbt @@ -384,7 +384,7 @@ fn fahrenheit(t_kelvin: Temperature) -> Scalar
Examples -* \\( 300K \\) in degree Fahrenheit. +* 300 K in degree Fahrenheit. Run this example ```nbt diff --git a/book/src/list-functions-strings.md b/book/src/list-functions-strings.md index cb1e1497..59df2308 100644 --- a/book/src/list-functions-strings.md +++ b/book/src/list-functions-strings.md @@ -34,14 +34,14 @@ fn str_slice(s: String, start: Scalar, end: Scalar) -> String
Examples -* Run this example +* Run this example ```nbt - >>> str_slice("Numbat", 0, 2) + >>> str_slice("Numbat", 3, 6) - str_slice("Numbat", 0, 2) + str_slice("Numbat", 3, 6) - = "Nu" [String] + = "bat" [String] ```
diff --git a/numbat/modules/core/functions.nbt b/numbat/modules/core/functions.nbt index 07c0e757..75d36b52 100644 --- a/numbat/modules/core/functions.nbt +++ b/numbat/modules/core/functions.nbt @@ -60,7 +60,7 @@ fn floor_in(base: D, value: D) -> D = floor(value / base) × base fn ceil(x: Scalar) -> Scalar @name("Ceil function") -@description("Returns the smallest integer multuple of `base` greater than or equal to `value`.") +@description("Returns the smallest integer multiple of `base` greater than or equal to `value`.") @example("ceil_in(m, 5.3 m)", "Ceil in meters.") @example("ceil_in(cm, 5.3 m)", "Ceil in centimeters.") diff --git a/numbat/modules/core/lists.nbt b/numbat/modules/core/lists.nbt index ff52b5f7..8c56dc49 100644 --- a/numbat/modules/core/lists.nbt +++ b/numbat/modules/core/lists.nbt @@ -78,7 +78,7 @@ fn map(f: Fn[(A) -> B], xs: List) -> List = else cons(f(head(xs)), map(f, tail(xs))) @description("Filter a list by a predicate") -@example("fn filter_fn(x) = x > 1\nfilter(filter_fn, [3, 2, 1, 0])", "Filter all elements greater than $1$.") +@example("filter(is_finite, [0, 1e10, NaN, -inf])") fn filter(p: Fn[(A) -> Bool], xs: List) -> List = if is_empty(xs) then [] @@ -104,7 +104,7 @@ fn _merge(xs, ys, cmp) = @description("Sort a list of elements, using the given key function that maps the element to a quantity") -@example("fn map_fn(x) = mod(x, 10)\nsort_by_key(map_fn, [701, 313, 9999, 4])","Sort by last digit.") +@example("fn last_digit(x) = mod(x, 10)\nsort_by_key(last_digit, [701, 313, 9999, 4])","Sort by last digit.") fn sort_by_key(key: Fn[(A) -> D], xs: List) -> List = if is_empty(xs) then [] @@ -129,7 +129,7 @@ fn intersperse(sep: A, xs: List) -> List = fn _add(x, y) = x + y # TODO: replace this with a local function once we support them @description("Sum all elements of a list") -@example("sum([3, 2, 1])") +@example("sum([3 m, 200 cm, 1000 mm])") fn sum(xs: List) -> D = foldl(_add, 0, xs) # TODO: implement linspace using `map` or similar once we have closures. This is ugly. diff --git a/numbat/modules/core/strings.nbt b/numbat/modules/core/strings.nbt index 7fd7250f..aa21dab5 100644 --- a/numbat/modules/core/strings.nbt +++ b/numbat/modules/core/strings.nbt @@ -7,7 +7,7 @@ use core::error fn str_length(s: String) -> Scalar @description("Subslice of a string") -@example("str_slice(\"Numbat\", 0, 2)") +@example("str_slice(\"Numbat\", 3, 6)") fn str_slice(s: String, start: Scalar, end: Scalar) -> String @description("Get a single-character string from a Unicode code point.") diff --git a/numbat/modules/math/geometry.nbt b/numbat/modules/math/geometry.nbt index 57b09932..942ca671 100644 --- a/numbat/modules/math/geometry.nbt +++ b/numbat/modules/math/geometry.nbt @@ -2,11 +2,11 @@ use core::functions use math::constants @description("The length of the hypotenuse of a right-angled triangle $\\sqrt\{x^2+y^2\}$.") -@example("hypot2(3, 4)") +@example("hypot2(3 m, 4 m)") fn hypot2(x: T, y: T) -> T = sqrt(x^2 + y^2) @description("The Euclidean norm of a 3D vector $\\sqrt\{x^2+y^2+z^2\}$.") -@example("hypot3(4, 1, 4)") +@example("hypot3(8, 9, 12)") fn hypot3(x: T, y: T, z: T) -> T = sqrt(x^2 + y^2 + z^2) # The following functions use a generic dimension instead of diff --git a/numbat/modules/math/number_theory.nbt b/numbat/modules/math/number_theory.nbt index 1bef78c7..a9a4a917 100644 --- a/numbat/modules/math/number_theory.nbt +++ b/numbat/modules/math/number_theory.nbt @@ -4,7 +4,7 @@ use core::functions @name("Greatest common divisor") @description("The largest positive integer that divides each of the integers $a$ and $b$.") @url("https://en.wikipedia.org/wiki/Greatest_common_divisor") -@example("gcd(60,42)") +@example("gcd(60, 42)") fn gcd(a: Scalar, b: Scalar) -> Scalar = if b == 0 then abs(a) diff --git a/numbat/modules/numerics/diff.nbt b/numbat/modules/numerics/diff.nbt index e96ca391..820555e2 100644 --- a/numbat/modules/numerics/diff.nbt +++ b/numbat/modules/numerics/diff.nbt @@ -3,7 +3,8 @@ use core::quantities @name("Numerical differentiation") @url("https://en.wikipedia.org/wiki/Numerical_differentiation") @description("Compute the numerical derivative of the function $f$ at point $x$ using the central difference method.") -@example("fn polynomial(x) = x² -x -1\ndiff(polynomial, 1)", "Compute the drivative of $f(x) = x² -x -1$ at $x=1$.") +@example("fn polynomial(x) = x² - x - 1\ndiff(polynomial, 1)", "Compute the derivative of $f(x) = x² -x -1$ at $x=1$.") +@example("fn distance(t) = 0.5 g0 t²\nfn velocity(t) = diff(distance, t)\nvelocity(2 s)", "Compute the free fall velocity after $t=2 s$.") fn diff(f: Fn[(X) -> Y], x: X) -> Y / X = (f(x + Δx) - f(x - Δx)) / 2 Δx where diff --git a/numbat/modules/physics/temperature_conversion.nbt b/numbat/modules/physics/temperature_conversion.nbt index 89a2bc16..8a5ec884 100644 --- a/numbat/modules/physics/temperature_conversion.nbt +++ b/numbat/modules/physics/temperature_conversion.nbt @@ -5,12 +5,12 @@ use units::si let _offset_celsius = 273.15 @description("Converts from degree Celsius (°C) to Kelvin.") -@example("from_celsius(300)", "$300 °C$ in Kelvin.") +@example("from_celsius(300)", "300 °C in Kelvin.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn from_celsius(t_celsius: Scalar) -> Temperature = (t_celsius + _offset_celsius) kelvin @description("Converts from Kelvin to degree Celcius (°C). This can be used on the right hand side of a conversion operator: `200 K -> celsius`.") -@example("300K -> celsius", "$300K$ in degree Celsius.") +@example("300K -> celsius", "300 K in degree Celsius.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn celsius(t_kelvin: Temperature) -> Scalar = t_kelvin / kelvin - _offset_celsius @@ -18,11 +18,11 @@ let _offset_fahrenheit = 459.67 let _scale_fahrenheit = 5 / 9 @description("Converts from degree Fahrenheit (°F) to Kelvin.") -@example("from_fahrenheit(300)", "$300 °F$ in Kelvin.") +@example("from_fahrenheit(300)", "300 °F in Kelvin.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn from_fahrenheit(t_fahrenheit: Scalar) -> Temperature = ((t_fahrenheit + _offset_fahrenheit) × _scale_fahrenheit) kelvin @description("Converts from Kelvin to degree Fahrenheit (°F). This can be used on the right hand side of a conversion operator: `200 K -> fahrenheit`.") -@example("300K -> fahrenheit", "$300K$ in degree Fahrenheit.") +@example("300K -> fahrenheit", "300 K in degree Fahrenheit.") @url("https://en.wikipedia.org/wiki/Conversion_of_scales_of_temperature") fn fahrenheit(t_kelvin: Temperature) -> Scalar = (t_kelvin / kelvin) / _scale_fahrenheit - _offset_fahrenheit diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index 9b688cf3..ba9515d0 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -5,13 +5,13 @@ use units::imperial @name("Degrees, minutes, seconds") @description("Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation.") @url("https://en.wikipedia.org/wiki/Sexagesimal_degree") -@example("DMS(46.5858°)") +@example("46.5858° -> DMS") fn DMS(alpha: Angle) -> String = _mixed_units(alpha, [deg, arcmin, arcsec], ["° ", "′ ", "″"], true) @name("Degrees, decimal minutes") @description("Convert an angle to a mixed degrees and decimal minutes representation.") -@example("DM(46.5858°)") +@example("46.5858° -> DM") @url("https://en.wikipedia.org/wiki/Decimal_degrees") fn DM(alpha: Angle) -> String = _mixed_units(alpha, [deg, arcmin], ["° ", "′"], false) @@ -19,13 +19,13 @@ fn DM(alpha: Angle) -> String = @name("Feet and inches") @description("Convert a length to a mixed feet and inches representation.") @url("https://en.wikipedia.org/wiki/Foot_(unit)") -@example("feet_and_inches(180cm)") +@example("180 cm -> feet_and_inches") fn feet_and_inches(length: Length) -> String = _mixed_units(length, [foot, inch], [" ft ", " in"], false) @name("Pounds and ounces") @description("Convert a mass to a mixed pounds and ounces representation.") @url("https://en.wikipedia.org/wiki/Pound_(mass)") -@example("pounds_and_ounces(1kg)") +@example("1 kg -> pounds_and_ounces") fn pounds_and_ounces(mass: Mass) -> String = _mixed_units(mass, [pound, ounce], [" lb ", " oz"], false) From 73168a4b825f618bc731d2a0cd8c5415b79a3abe Mon Sep 17 00:00:00 2001 From: Goju-Ryu Date: Thu, 22 Aug 2024 22:12:15 +0200 Subject: [PATCH 09/46] Add `unit_list` function --- numbat/modules/core/mixed_units.nbt | 10 ++++++++++ numbat/modules/units/mixed.nbt | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/numbat/modules/core/mixed_units.nbt b/numbat/modules/core/mixed_units.nbt index 8b339327..86cbd506 100644 --- a/numbat/modules/core/mixed_units.nbt +++ b/numbat/modules/core/mixed_units.nbt @@ -25,3 +25,13 @@ fn _mixed_units(q: D, units: List, names: List, round_last: B if q < 0 then str_append("-", _mixed_units(-q, units, names, round_last)) else join(_mixed_units_helper(q, units, names, round_last), "") + + +fn _mixed_unit_list(val: A, units: List, acc: List) -> List = + if len(units) == 1 + then reverse(cons(val -> head(units), acc)) + else _mixed_unit_list(val - unit_val, tail(units), cons(unit_val, acc)) + where unit_val: A = + if (len(units) > 0) + then trunc_in(head(units), val) + else error("Units list cannot be empty") \ No newline at end of file diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index 8ef93e26..10482b78 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -25,3 +25,8 @@ fn feet_and_inches(length: Length) -> String = @url("https://en.wikipedia.org/wiki/Pound_(mass)") fn pounds_and_ounces(mass: Mass) -> String = _mixed_units(mass, [pound, ounce], [" lb ", " oz"], false) + + +@name("Unit list") +@description("Convert a value to a mixed representation using the provided units.) +fn unit_list(units: List, value: A) -> List = _mixed_unit_list(value, units, []) From 5844b4e2d5bdd340f22c5713096416043806d066 Mon Sep 17 00:00:00 2001 From: Goju-Ryu Date: Tue, 27 Aug 2024 18:09:17 +0200 Subject: [PATCH 10/46] Potential fix of 0.0 float bug --- examples/tests/mixed_units.nbt | 7 +++++++ numbat/modules/core/mixed_units.nbt | 18 ++++++++++++------ numbat/modules/units/mixed.nbt | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/examples/tests/mixed_units.nbt b/examples/tests/mixed_units.nbt index 3dba44b7..6aa98d2a 100644 --- a/examples/tests/mixed_units.nbt +++ b/examples/tests/mixed_units.nbt @@ -34,3 +34,10 @@ assert_eq(5 lb -> pounds_and_ounces, "5 lb 0 oz") assert_eq(5.5 lb -> pounds_and_ounces, "5 lb 8 oz") assert_eq(6.75 lb -> pounds_and_ounces, "6 lb 12 oz") assert_eq(-5.5 lb -> pounds_and_ounces, "-5 lb 8 oz") + +# Unit list + +assert_eq(unit_list([m, cm, mm], 1m + 23cm + 4mm), [1m, 23cm, 4mm]) +assert_eq(unit_list([deg, arcmin], 1deg + 23arcmin + 4arcsec), [1deg, 23arcmin, 4arcsec]) +assert_eq(unit_list([hour], 1.5hour), [1.5hour]) + diff --git a/numbat/modules/core/mixed_units.nbt b/numbat/modules/core/mixed_units.nbt index 86cbd506..e5d2c527 100644 --- a/numbat/modules/core/mixed_units.nbt +++ b/numbat/modules/core/mixed_units.nbt @@ -27,11 +27,17 @@ fn _mixed_units(q: D, units: List, names: List, round_last: B else join(_mixed_units_helper(q, units, names, round_last), "") -fn _mixed_unit_list(val: A, units: List, acc: List) -> List = - if len(units) == 1 - then reverse(cons(val -> head(units), acc)) - else _mixed_unit_list(val - unit_val, tail(units), cons(unit_val, acc)) +fn _zero_length(val: A) -> A = val * 0 -> val + + +fn _mixed_unit_list(val: A, units: List, min_unit: A, acc: List) -> List = + if trunc_in(min_unit, val) == 0 + then concat(acc, map(_zero_length, units)) + else if len(units) == 1 + then cons_end(val -> head(units), acc) + else _mixed_unit_list(val - unit_val, tail(units), min_unit, cons_end(unit_val, acc)) where unit_val: A = if (len(units) > 0) - then trunc_in(head(units), val) - else error("Units list cannot be empty") \ No newline at end of file + then (val |> round_in(min_unit) |> trunc_in(head(units))) + else error("Units list cannot be empty") + diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index 10482b78..c18a178d 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -29,4 +29,4 @@ fn pounds_and_ounces(mass: Mass) -> String = @name("Unit list") @description("Convert a value to a mixed representation using the provided units.) -fn unit_list(units: List, value: A) -> List = _mixed_unit_list(value, units, []) +fn unit_list(units: List, value: A) -> List = _mixed_unit_list(value, units, minimum(units) / 100, []) \ No newline at end of file From 41bdafc29cd0f22033ace31d6f2be75c613fa604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Tue, 17 Sep 2024 21:23:07 +0200 Subject: [PATCH 11/46] Fix formatting and simplify Since @irevoire fixed problems arrising from calls to simplify, a lot of rounding/conversion bugs have been solved by extension. This lets the function work with a far simpler implementation. --- numbat/modules/core/mixed_units.nbt | 20 +++++++++----------- numbat/modules/units/mixed.nbt | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/numbat/modules/core/mixed_units.nbt b/numbat/modules/core/mixed_units.nbt index e5d2c527..6f9917ef 100644 --- a/numbat/modules/core/mixed_units.nbt +++ b/numbat/modules/core/mixed_units.nbt @@ -29,15 +29,13 @@ fn _mixed_units(q: D, units: List, names: List, round_last: B fn _zero_length(val: A) -> A = val * 0 -> val - -fn _mixed_unit_list(val: A, units: List, min_unit: A, acc: List) -> List = - if trunc_in(min_unit, val) == 0 - then concat(acc, map(_zero_length, units)) - else if len(units) == 1 - then cons_end(val -> head(units), acc) - else _mixed_unit_list(val - unit_val, tail(units), min_unit, cons_end(unit_val, acc)) - where unit_val: A = +fn _mixed_unit_list(val: D, units: List, acc: List) -> List = + if val == 0 + then concat(acc, map(_zero_length, units)) + else if len(units) == 1 + then cons_end(val -> head(units), acc) + else _mixed_unit_list(val - unit_val, tail(units), cons_end(unit_val, acc)) + where unit_val: D = if (len(units) > 0) - then (val |> round_in(min_unit) |> trunc_in(head(units))) - else error("Units list cannot be empty") - + then (val |> trunc_in(head(units))) + else error("Units list cannot be empty") diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index c18a178d..08fdf5ed 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -28,5 +28,5 @@ fn pounds_and_ounces(mass: Mass) -> String = @name("Unit list") -@description("Convert a value to a mixed representation using the provided units.) -fn unit_list(units: List, value: A) -> List = _mixed_unit_list(value, units, minimum(units) / 100, []) \ No newline at end of file +@description("Convert a value to a mixed representation using the provided units.") +fn unit_list(units: List, value: D) -> List = _mixed_unit_list(value, units, []) \ No newline at end of file From 29bf74fdc09ce9c81f0c9f49fe7994ff0c8ceebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Tue, 17 Sep 2024 22:19:08 +0200 Subject: [PATCH 12/46] Add tests and documentation --- book/src/list-functions-other.md | 8 ++++++++ examples/tests/mixed_units.nbt | 16 +++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index a8a97830..17142c35 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -105,6 +105,14 @@ More information [here](https://en.wikipedia.org/wiki/Pound_(mass)). fn pounds_and_ounces(mass: Mass) -> String ``` +### `unit_list` (Unit list) +Converts a value to a mixed unit representation. +More information [here](https://www.gnu.org/software/units/manual/html_node/Unit-Lists.html) + +```nbt +fn unit_list(units: List, value: D) -> List +``` + ## Temperature conversion Defined in: `physics::temperature_conversion` diff --git a/examples/tests/mixed_units.nbt b/examples/tests/mixed_units.nbt index 6aa98d2a..e6c2326a 100644 --- a/examples/tests/mixed_units.nbt +++ b/examples/tests/mixed_units.nbt @@ -37,7 +37,17 @@ assert_eq(-5.5 lb -> pounds_and_ounces, "-5 lb 8 oz") # Unit list -assert_eq(unit_list([m, cm, mm], 1m + 23cm + 4mm), [1m, 23cm, 4mm]) -assert_eq(unit_list([deg, arcmin], 1deg + 23arcmin + 4arcsec), [1deg, 23arcmin, 4arcsec]) -assert_eq(unit_list([hour], 1.5hour), [1.5hour]) +fn _add(a: D, b: D) -> D = a + b + +let test1 = 12 m + 34 cm + 5 mm + 678 µm +assert_eq(test1 |> unit_list([m]) |> head, test1) +assert_eq(test1 |> unit_list([m, cm]) |> foldl(_add, 0), test1) +assert_eq(test1 |> unit_list([m, cm, mm]) |> foldl(_add, 0), test1) +assert_eq(test1 |> unit_list([m, cm, mm, µm]) |> foldl(_add, 0), test1) + +let test2 = 12 degree + 34 arcminute + 5 arcsec +assert_eq(test2 |> unit_list([degree]) |> head, test2) +assert_eq(test2 |> unit_list([degree, arcmin]) |> foldl(_add, 0), test2) +assert_eq(test2 |> unit_list([degree, arcmin, arcsec]) |> foldl(_add, 0), test2) + From 136114f26a773bc4eb10555a8c303f1216a1e572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Sun, 29 Sep 2024 19:41:49 +0200 Subject: [PATCH 13/46] Replace string based mixed unit functions --- numbat/modules/core/mixed_units.nbt | 24 ------------------------ numbat/modules/units/mixed.nbt | 24 ++++++++++++------------ 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/numbat/modules/core/mixed_units.nbt b/numbat/modules/core/mixed_units.nbt index 6f9917ef..f87da4ef 100644 --- a/numbat/modules/core/mixed_units.nbt +++ b/numbat/modules/core/mixed_units.nbt @@ -3,30 +3,6 @@ use core::lists # Helper functions for mixed-unit conversions. See units::mixed for more. -fn _mixed_units_helper(q: D, units: List, names: List, round_last: Bool) -> List = - if is_empty(units) - then - [] - else - cons( - if len(units) == 1 - then - if round_last - then "{round(q / head(units))}{head(names)}" - else "{q / head(units)}{head(names)}" - else "{trunc(q / head(units))}{head(names)}", - _mixed_units_helper( - q - trunc(q / head(units)) * head(units), - tail(units), - tail(names), - round_last)) - -fn _mixed_units(q: D, units: List, names: List, round_last: Bool) -> String = - if q < 0 - then str_append("-", _mixed_units(-q, units, names, round_last)) - else join(_mixed_units_helper(q, units, names, round_last), "") - - fn _zero_length(val: A) -> A = val * 0 -> val fn _mixed_unit_list(val: D, units: List, acc: List) -> List = diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index 08fdf5ed..1a1d1310 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -2,31 +2,31 @@ use core::mixed_units use units::si use units::imperial +@name("Unit list") +@description("Convert a value to a mixed representation using the provided units.") +fn unit_list(units: List, value: D) -> List = _mixed_unit_list(value, units, []) + @name("Degrees, minutes, seconds") @description("Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation.") @url("https://en.wikipedia.org/wiki/Sexagesimal_degree") -fn DMS(alpha: Angle) -> String = - _mixed_units(alpha, [deg, arcmin, arcsec], ["° ", "′ ", "″"], true) +fn DMS(alpha: Angle) -> List = + unit_list([degree, arcminute, arcsecond], alpha) @name("Degrees, decimal minutes") @description("Convert an angle to a mixed degrees and decimal minutes representation.") @url("https://en.wikipedia.org/wiki/Decimal_degrees") -fn DM(alpha: Angle) -> String = - _mixed_units(alpha, [deg, arcmin], ["° ", "′"], false) +fn DM(alpha: Angle) -> List = + unit_list([degree, arcminute], alpha) @name("Feet and inches") @description("Convert a length to a mixed feet and inches representation.") @url("https://en.wikipedia.org/wiki/Foot_(unit)") -fn feet_and_inches(length: Length) -> String = - _mixed_units(length, [foot, inch], [" ft ", " in"], false) +fn feet_and_inches(length: Length) -> List = + unit_list([foot, inch], length) @name("Pounds and ounces") @description("Convert a mass to a mixed pounds and ounces representation.") @url("https://en.wikipedia.org/wiki/Pound_(mass)") -fn pounds_and_ounces(mass: Mass) -> String = - _mixed_units(mass, [pound, ounce], [" lb ", " oz"], false) +fn pounds_and_ounces(mass: Mass) -> List = + unit_list([pound, ounce], mass) - -@name("Unit list") -@description("Convert a value to a mixed representation using the provided units.") -fn unit_list(units: List, value: D) -> List = _mixed_unit_list(value, units, []) \ No newline at end of file From 9c94957731018824d256d80068ef416d72906041 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 29 Sep 2024 21:46:02 +0200 Subject: [PATCH 14/46] Add comparison with other calculators/languages --- book/src/SUMMARY.md | 6 ++++- book/src/comparison.md | 50 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 book/src/comparison.md diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 9a1d0e10..a66f2703 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -21,7 +21,6 @@ - [XKCD 687](./example-xkcd_687.md) - [XKCD 2585](./example-xkcd_2585.md) - [XKCD 2812](./example-xkcd_2812.md) -- [IDE / editor integration](./editor-integration.md) # Numbat language reference @@ -69,6 +68,11 @@ - [Type system](./type-system.md) +# Other topics + +- [IDE / editor integration](./editor-integration.md) +- [Comparison with other tools](./comparison.md) + # Support - [Contact us](./contact-us.md) diff --git a/book/src/comparison.md b/book/src/comparison.md new file mode 100644 index 00000000..a30fff27 --- /dev/null +++ b/book/src/comparison.md @@ -0,0 +1,50 @@ +# Comparison with other tools + +The following table provides a comparison of Numbat with other scientific calculators and programming languages. This comparison +is certainly *not* objective, as we only list criteria that we consider important. If you think that a tool or language is missing +or misrepresented, please [let us know](https://github.com/sharkdp/numbat/issues). + +| | Numbat | [Qalculate](https://qalculate.github.io/) | [Kalker](https://github.com/PaddiM8/kalker) | [GNU Units](https://www.gnu.org/software/units/) | [Frink](https://frinklang.org/) | [Wolfram Alpha](https://www.wolframalpha.com/) | +|----------------------------------------|-----------------|-----------|--------|-----------|-------|---------------| +| FOSS License | MIT, Apache-2.0 | GPL-2.0 | MIT | GPL-3.0 | ❌ | ❌ | +| **Interfaces** | | | | | | | +| Command-line | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Web version | ✓ | ❌ | ✓ | ❌ | ❌ | ✓ | +| Graphical | ❌ | ✓ | ❌ | ❌ | (✓) | ✓ | +| **Units** | | | | | | | +| Comprehensive list of units | ✓ | ✓ | ❌ | ✓ | ✓ | ✓ | +| Custom units | ✓ | ✓ | ❌ | ✓ | ✓ | ❌ | +| Physical dimensions | ✓ | ❌ | ❌ | ❌ | ❌ | ❌ | +| Currency conversions | ✓ | ✓ | ❌ | ❌ | ✓ | ✓ | +| Date and time calculations | ✓ | ✓ | ❌ | ❌ | ✓ | ✓ | +| **Language features** | | | | | | | +| Custom functions | ✓ | ✓ | ✓ | ❌ | ✓ | ❌ | +| Real programming language | ✓ | ❌ | ❌ | ❌ | ✓ | ? | +| Strongly typed | ✓ | ❌ | ❌ | ❌ | ❌ | ❌ | +| **Calculator features** | | | | | | | +| Symbolic calculations | ❌ | (✓) | ❌ | ❌ | (✓) | ✓ | +| Hex/Oct/Bin mode | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Complex numbers | ❌ ([#180](https://github.com/sharkdp/numbat/issues/180)) | ✓ | ✓ | ❌ | ✓ | ✓ | +| Vectors, Matrices | ❌ | ✓ | ✓ | ❌ | ✓ | ✓ | + +## Detailed comparison + +- [Qalculate](https://qalculate.github.io/) is a fantastic calculator with a strong support for units and conversions. + If you don't need the full power of a programming language, Qalculate is probably more feature-complete than Numbat. +- [Frink](https://frinklang.org/) is a special-purpose programming language with a focus on scientific calculations + and units of measurement. The language is probably more powerful than Numbat, but lacks a static type system. It's also + a imperative/OOP language, while Numbat is a functional/declarative language. Frink is not open-source. +- [GNU Units](https://www.gnu.org/software/units/) is probably the most comprehensive tool in terms of pre-defined units. + Numbat makes it very easy to define [custom units](./unit-definitions.md). If you think that a unit should be part + of the standard library, please [let us know](https://github.com/sharkdp/numbat/issues). +- [Wolfram Alpha](https://www.wolframalpha.com/) is a very powerful tool, but it's focused on single-line queries instead + of longer computations. The query language lacks a strict syntax (which some might consider a feature). The tool is not + open source and sometimes has limitations with respect to the number/size of queries you can make. + +## Other interesting tools / languages + +- [F#](https://fsharp.org/) is the only programming language that we know of that comes close in terms of having an + expressive type system that is based on units of measure. In fact, Numbats type system is heavily inspired by F#, + except that it uses physical dimensions instead of physical units on the type level. Both languages have feature + full [type inference](./function-definitions.md#type-inference). F# is not listed above, as it's not really suitable + as a scientific calculator. From 7be6aef4ac9370743795d7e1c7f0a967dcc2e771 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Sun, 29 Sep 2024 22:04:29 +0200 Subject: [PATCH 15/46] Update mixed-unit tests --- examples/tests/mixed_units.nbt | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/examples/tests/mixed_units.nbt b/examples/tests/mixed_units.nbt index e6c2326a..781de70e 100644 --- a/examples/tests/mixed_units.nbt +++ b/examples/tests/mixed_units.nbt @@ -4,36 +4,36 @@ assert_eq(38° + 53′ + 23″, 38.8897°, 1e-4°) assert_eq(-(77° + 0′ + 32″), -77.0089°, 1e-4°) -assert_eq(38.8897° -> DMS, "38° 53′ 23″") -assert_eq(-77.0089° -> DMS, "-77° 0′ 32″") +assert_eq("{38.88972222° -> DMS}", "{[38°, 53′, 23″]}") +assert_eq("{-77.0089° -> DMS}", "{[-77°, -0′, -32.04″]}") ## Stuttgart assert_eq(48° + 46′ + 32″, 48.7756°, 1e-4°) assert_eq(9° + 10′ + 58″, 9.1828°, 1e-4°) -assert_eq(48.7756° -> DMS, "48° 46′ 32″") -assert_eq(9.1828° -> DMS, "9° 10′ 58″") +assert_eq("{48.775555555° -> DMS}", "{[48°, 46′, 32″]}") +assert_eq("{9.18277777° -> DMS}", "{[9°, 10′, 58″]}") # Degrees, decimal minutes (DM) -assert_eq(38.8897° -> DM, "38° 53.382′") -assert_eq(-77.0089° -> DM, "-77° 0.534′") +assert_eq("{38.8897° -> DM}", "{[38°, 53.382′]}") +assert_eq("{-77.0089° -> DM}", "{[-77°, -0.534′]}") # Feet and inches -assert_eq(5.5 ft -> feet_and_inches, "5 ft 6 in") -assert_eq(6.75 ft -> feet_and_inches, "6 ft 9 in") -assert_eq(-5.5 ft -> feet_and_inches, "-5 ft 6 in") -assert_eq(0 -> feet_and_inches, "0 ft 0 in") -assert_eq(1 ft -> feet_and_inches, "1 ft 0 in") -assert_eq(2.345 inch -> feet_and_inches, "0 ft 2.345 in") +assert_eq("{5.5 ft -> feet_and_inches}", "{[5 ft, 6 in]}") +assert_eq("{6.75 ft -> feet_and_inches}", "{[6 ft, 9 in]}") +assert_eq("{-5.5 ft -> feet_and_inches}", "{[-5 ft, -6 in]}") +assert_eq("{0 -> feet_and_inches}", "{[0 ft, 0 in]}") +assert_eq("{1 ft -> feet_and_inches}", "{[1 ft, 0 in]}") +assert_eq("{2.345 inch -> feet_and_inches}", "{[0 ft, 2.345 in]}") # Pounds and ounces -assert_eq(5 lb -> pounds_and_ounces, "5 lb 0 oz") -assert_eq(5.5 lb -> pounds_and_ounces, "5 lb 8 oz") -assert_eq(6.75 lb -> pounds_and_ounces, "6 lb 12 oz") -assert_eq(-5.5 lb -> pounds_and_ounces, "-5 lb 8 oz") +assert_eq("{5 lb -> pounds_and_ounces}", "{[5 lb, 0 oz]}") +assert_eq("{5.5 lb -> pounds_and_ounces}", "{[5 lb, 8 oz]}") +assert_eq("{6.75 lb -> pounds_and_ounces}", "{[6 lb, 12 oz]}") +assert_eq("{-5.5 lb -> pounds_and_ounces}", "{[-5 lb, -8 oz]}") # Unit list From e4a3a59a8e55f6a1a80d3d56957e02e4c5aab6d5 Mon Sep 17 00:00:00 2001 From: David Peter Date: Sun, 29 Sep 2024 22:07:44 +0200 Subject: [PATCH 16/46] =?UTF-8?q?Add=20'tau'=20alias=20for=20'=CF=84',=20c?= =?UTF-8?q?loses=20#581?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- numbat/modules/math/constants.nbt | 1 + 1 file changed, 1 insertion(+) diff --git a/numbat/modules/math/constants.nbt b/numbat/modules/math/constants.nbt index c3546448..249ba238 100644 --- a/numbat/modules/math/constants.nbt +++ b/numbat/modules/math/constants.nbt @@ -9,6 +9,7 @@ let π = 3.14159265358979323846264338327950288 @name("Tau") @url("https://en.wikipedia.org/wiki/Turn_(angle)#Tau_proposals") +@aliases(tau) let τ = 2 π @name("Euler's number") From 5b6ee4eebf9cd71cf07aa7f7ba932eb9d9db94b1 Mon Sep 17 00:00:00 2001 From: Robert <766670+rben01@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:22:43 -0400 Subject: [PATCH 17/46] Plumbed spans through alias definitions, allowing for more precise error messages closes #570 --- numbat/src/decorator.rs | 78 ++++++++++++++++++++++---------- numbat/src/parser.rs | 39 +++++++++++++--- numbat/src/prefix_parser.rs | 50 ++++++++++++++------ numbat/src/prefix_transformer.rs | 18 +++++--- numbat/src/typed_ast.rs | 2 +- 5 files changed, 134 insertions(+), 53 deletions(-) diff --git a/numbat/src/decorator.rs b/numbat/src/decorator.rs index 71c94a67..a4355bc1 100644 --- a/numbat/src/decorator.rs +++ b/numbat/src/decorator.rs @@ -1,46 +1,74 @@ -use crate::{prefix_parser::AcceptsPrefix, unit::CanonicalName}; +use crate::{prefix_parser::AcceptsPrefix, span::Span, unit::CanonicalName}; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Decorator<'a> { MetricPrefixes, BinaryPrefixes, - Aliases(Vec<(&'a str, Option)>), + Aliases(Vec<(&'a str, Option, Span)>), Url(String), Name(String), Description(String), } -pub fn name_and_aliases<'a>( +/// Get an iterator of data computed from a name and/or its alias's `AcceptsPrefix` and +/// `Span`. If `name` itself is in the list of aliases, then it (or more precisely, the +/// data computed from it) will be placed at the front of the iterator +/// +/// `f` says how to turn a triple of data associated with `name` or an alias, `(name, +/// accepts_prefix, Option)`, into a `T`. The generality is really just here to +/// decide whether to yield `(&'a String, AcceptsPrefix)` or a `(&'a String, +/// AcceptsPrefix, Span)`. +fn name_and_aliases_inner<'a, T: 'a>( name: &'a str, - decorators: &[Decorator<'a>], -) -> Box + 'a> { - let aliases = { - let mut aliases_vec = vec![]; - for decorator in decorators { - if let Decorator::Aliases(aliases) = decorator { - aliases_vec = aliases - .iter() - .map(|(name, accepts_prefix)| { - (*name, accepts_prefix.unwrap_or(AcceptsPrefix::only_long())) - }) - .collect(); + decorators: &'a [Decorator], + f: impl 'a + Fn(&'a str, AcceptsPrefix, Option) -> T, +) -> impl 'a + Iterator { + // contains all the aliases of `name`, starting with `name` itself + let mut aliases_vec = vec![f(name, AcceptsPrefix::only_long(), None)]; + + for decorator in decorators { + if let Decorator::Aliases(aliases) = decorator { + for (n, ap, span) in aliases { + let ap = ap.unwrap_or(AcceptsPrefix::only_long()); + if *n == name { + // use the AcceptsPrefix from the alias, but the span from `name` + // itself; this way we always report a conflicting `name` first + // before reporting any of its aliases. in effect we swallow aliases + // equal to `name` itself (but keep their metadata) + aliases_vec[0] = f(n, ap, None); + } else { + aliases_vec.push(f(n, ap, Some(*span))); + } } } - aliases_vec - }; - - if !aliases.iter().any(|(n, _)| n == &name) { - let name_iter = std::iter::once((name, AcceptsPrefix::only_long())); - Box::new(name_iter.chain(aliases)) - } else { - Box::new(aliases.into_iter()) } + + aliases_vec.into_iter() +} + +/// Returns iterator of `(name_or_alias, accepts_prefix)` for the given name +pub fn name_and_aliases<'a>( + name: &'a str, + decorators: &'a [Decorator], +) -> impl 'a + Iterator { + name_and_aliases_inner(name, decorators, |n, accepts_prefix, _| (n, accepts_prefix)) +} + +/// Returns iterator of `(name_or_alias, accepts_prefix, span)` for the given name +pub fn name_and_aliases_spans<'a>( + name: &'a str, + name_span: Span, + decorators: &'a [Decorator], +) -> impl 'a + Iterator { + name_and_aliases_inner(name, decorators, move |n, accepts_prefix, span| { + (n, accepts_prefix, span.unwrap_or(name_span)) + }) } pub fn get_canonical_unit_name(unit_name: &str, decorators: &[Decorator]) -> CanonicalName { for decorator in decorators { if let Decorator::Aliases(aliases) = decorator { - for (alias, accepts_prefix) in aliases { + for (alias, accepts_prefix, _) in aliases { match accepts_prefix { &Some(ap) if ap.short => { return CanonicalName::new(alias, ap); @@ -92,7 +120,7 @@ pub fn description(decorators: &[Decorator]) -> Option { pub fn contains_aliases_with_prefixes(decorates: &[Decorator]) -> bool { for decorator in decorates { if let Decorator::Aliases(aliases) = decorator { - if aliases.iter().any(|(_, prefixes)| prefixes.is_some()) { + if aliases.iter().any(|(_, prefixes, _)| prefixes.is_some()) { return true; } } diff --git a/numbat/src/parser.rs b/numbat/src/parser.rs index e9da7e02..946ba9be 100644 --- a/numbat/src/parser.rs +++ b/numbat/src/parser.rs @@ -369,14 +369,17 @@ impl<'a> Parser<'a> { fn list_of_aliases( &mut self, tokens: &[Token<'a>], - ) -> Result)>> { + ) -> Result, Span)>> { if self.match_exact(tokens, TokenKind::RightParen).is_some() { return Ok(vec![]); } - let mut identifiers = vec![(self.identifier(tokens)?, self.accepts_prefix(tokens)?)]; + let span = self.peek(tokens).span; + let mut identifiers = vec![(self.identifier(tokens)?, self.accepts_prefix(tokens)?, span)]; + while self.match_exact(tokens, TokenKind::Comma).is_some() { - identifiers.push((self.identifier(tokens)?, self.accepts_prefix(tokens)?)); + let span = self.peek(tokens).span; + identifiers.push((self.identifier(tokens)?, self.accepts_prefix(tokens)?, span)); } if self.match_exact(tokens, TokenKind::RightParen).is_none() { @@ -1987,9 +1990,12 @@ mod tests { use std::fmt::Write; use super::*; - use crate::ast::{ - binop, boolean, conditional, factorial, identifier, list, logical_neg, negate, scalar, - struct_, ReplaceSpans, + use crate::{ + ast::{ + binop, boolean, conditional, factorial, identifier, list, logical_neg, negate, scalar, + struct_, ReplaceSpans, + }, + span::ByteIndex, }; #[track_caller] @@ -2438,7 +2444,26 @@ mod tests { )), decorators: vec![ decorator::Decorator::Name("myvar".into()), - decorator::Decorator::Aliases(vec![("foo", None), ("bar", None)]), + decorator::Decorator::Aliases(vec![ + ( + "foo", + None, + Span { + start: ByteIndex(24), + end: ByteIndex(27), + code_source_id: 0, + }, + ), + ( + "bar", + None, + Span { + start: ByteIndex(29), + end: ByteIndex(32), + code_source_id: 0, + }, + ), + ]), ], }), ); diff --git a/numbat/src/prefix_parser.rs b/numbat/src/prefix_parser.rs index 9f11e600..2a21e555 100644 --- a/numbat/src/prefix_parser.rs +++ b/numbat/src/prefix_parser.rs @@ -52,6 +52,25 @@ impl AcceptsPrefix { } } +/// The spans associated with an alias passed to `@aliases` +#[derive(Debug, Clone, Copy)] +pub(crate) struct AliasSpanInfo { + /// The span of the name to which the alias refers + pub(crate) name_span: Span, + /// The span of the alias itself (in an `@aliases` decorator) + pub(crate) alias_span: Span, +} + +impl AliasSpanInfo { + #[cfg(test)] + fn dummy() -> Self { + Self { + name_span: Span::dummy(), + alias_span: Span::dummy(), + } + } +} + #[derive(Debug, Clone)] struct UnitInfo { definition_span: Span, @@ -152,23 +171,23 @@ impl PrefixParser { fn ensure_name_is_available( &self, name: &str, - conflict_span: Span, + definition_span: Span, clash_with_other_identifiers: bool, ) -> Result<()> { if self.reserved_identifiers.contains(&name) { - return Err(NameResolutionError::ReservedIdentifier(conflict_span)); + return Err(NameResolutionError::ReservedIdentifier(definition_span)); } if clash_with_other_identifiers { if let Some(original_span) = self.other_identifiers.get(name) { - return Err(self.identifier_clash_error(name, conflict_span, *original_span)); + return Err(self.identifier_clash_error(name, definition_span, *original_span)); } } match self.parse(name) { PrefixParserResult::Identifier(_) => Ok(()), PrefixParserResult::UnitIdentifier(original_span, _, _, _) => { - Err(self.identifier_clash_error(name, conflict_span, original_span)) + Err(self.identifier_clash_error(name, definition_span, original_span)) } } } @@ -180,9 +199,12 @@ impl PrefixParser { metric: bool, binary: bool, full_name: &str, - definition_span: Span, + AliasSpanInfo { + name_span, + alias_span, + }: AliasSpanInfo, ) -> Result<()> { - self.ensure_name_is_available(unit_name, definition_span, true)?; + self.ensure_name_is_available(unit_name, alias_span, true)?; for (prefix_long, prefixes_short, prefix) in Self::prefixes() { if !(prefix.is_metric() && metric || prefix.is_binary() && binary) { @@ -192,7 +214,7 @@ impl PrefixParser { if accepts_prefix.long { self.ensure_name_is_available( &format!("{prefix_long}{unit_name}"), - definition_span, + alias_span, true, )?; } @@ -200,7 +222,7 @@ impl PrefixParser { for prefix_short in *prefixes_short { self.ensure_name_is_available( &format!("{prefix_short}{unit_name}"), - definition_span, + alias_span, true, )?; } @@ -208,7 +230,7 @@ impl PrefixParser { } let unit_info = UnitInfo { - definition_span, + definition_span: name_span, accepts_prefix, metric_prefixes: metric, binary_prefixes: binary, @@ -294,7 +316,7 @@ mod tests { true, false, "meter", - Span::dummy(), + AliasSpanInfo::dummy(), ) .unwrap(); prefix_parser @@ -304,7 +326,7 @@ mod tests { true, false, "meter", - Span::dummy(), + AliasSpanInfo::dummy(), ) .unwrap(); @@ -315,7 +337,7 @@ mod tests { true, true, "byte", - Span::dummy(), + AliasSpanInfo::dummy(), ) .unwrap(); prefix_parser @@ -325,7 +347,7 @@ mod tests { true, true, "byte", - Span::dummy(), + AliasSpanInfo::dummy(), ) .unwrap(); @@ -336,7 +358,7 @@ mod tests { false, false, "me", - Span::dummy(), + AliasSpanInfo::dummy(), ) .unwrap(); diff --git a/numbat/src/prefix_transformer.rs b/numbat/src/prefix_transformer.rs index a24c3dbc..5db124c6 100644 --- a/numbat/src/prefix_transformer.rs +++ b/numbat/src/prefix_transformer.rs @@ -2,7 +2,7 @@ use crate::{ ast::{DefineVariable, Expression, Statement, StringPart}, decorator::{self, Decorator}, name_resolution::NameResolutionError, - prefix_parser::{PrefixParser, PrefixParserResult}, + prefix_parser::{AliasSpanInfo, PrefixParser, PrefixParserResult}, span::Span, }; @@ -135,20 +135,26 @@ impl Transformer { pub(crate) fn register_name_and_aliases( &mut self, name: &str, + name_span: Span, decorators: &[Decorator], - conflict_span: Span, ) -> Result<()> { let mut unit_names = vec![]; let metric_prefixes = Self::has_decorator(decorators, Decorator::MetricPrefixes); let binary_prefixes = Self::has_decorator(decorators, Decorator::BinaryPrefixes); - for (alias, accepts_prefix) in decorator::name_and_aliases(name, decorators) { + + for (alias, accepts_prefix, alias_span) in + decorator::name_and_aliases_spans(name, name_span, decorators) + { self.prefix_parser.add_unit( alias, accepts_prefix, metric_prefixes, binary_prefixes, name, - conflict_span, + AliasSpanInfo { + name_span, + alias_span, + }, )?; unit_names.push(alias.to_string()); } @@ -189,7 +195,7 @@ impl Transformer { Ok(match statement { Statement::Expression(expr) => Statement::Expression(self.transform_expression(expr)), Statement::DefineBaseUnit(span, name, dexpr, decorators) => { - self.register_name_and_aliases(name, &decorators, span)?; + self.register_name_and_aliases(name, span, &decorators)?; Statement::DefineBaseUnit(span, name, dexpr, decorators) } Statement::DefineDerivedUnit { @@ -200,7 +206,7 @@ impl Transformer { type_annotation, decorators, } => { - self.register_name_and_aliases(identifier, &decorators, identifier_span)?; + self.register_name_and_aliases(identifier, identifier_span, &decorators)?; Statement::DefineDerivedUnit { identifier_span, identifier, diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index ccc49895..44c65cf8 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -846,7 +846,7 @@ fn decorator_markup(decorators: &Vec) -> Markup { m::decorator("@aliases") + m::operator("(") + Itertools::intersperse( - names.iter().map(|(name, accepts_prefix)| { + names.iter().map(|(name, accepts_prefix, _)| { m::unit(name) + accepts_prefix_markup(accepts_prefix) }), m::operator(", "), From 61664de174c487523da0d171e21a2bee52419de3 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Sat, 28 Sep 2024 00:30:13 -0400 Subject: [PATCH 18/46] Remove allocations in `Markup` by switching to `Cow<'static, str>` Now strings are only allocated if non-static (The vast majority of markup strings are `&'static`) --- numbat-cli/src/highlighter.rs | 14 ++++-- numbat-cli/src/main.rs | 2 +- numbat/src/ast.rs | 2 +- numbat/src/column_formatter.rs | 4 +- numbat/src/help.rs | 2 +- numbat/src/lib.rs | 44 +++++++++++-------- numbat/src/markup.rs | 56 ++++++++++++------------ numbat/src/product.rs | 2 +- numbat/src/typechecker/type_scheme.rs | 4 +- numbat/src/typed_ast.rs | 63 +++++++++++++++------------ numbat/src/value.rs | 4 +- 11 files changed, 109 insertions(+), 88 deletions(-) diff --git a/numbat-cli/src/highlighter.rs b/numbat-cli/src/highlighter.rs index 6f4d5410..a8e98e2b 100644 --- a/numbat-cli/src/highlighter.rs +++ b/numbat-cli/src/highlighter.rs @@ -40,17 +40,23 @@ impl Highlighter for NumbatHighlighter { if ctx.variable_names().any(|n| n == candidate) || ctx.function_names().any(|n| format!("{n}(") == candidate) { - Cow::Owned(ansi_format(&markup::identifier(candidate), false)) + Cow::Owned(ansi_format( + &markup::identifier(candidate.to_string()), + false, + )) } else if ctx .unit_names() .iter() .any(|un| un.iter().any(|n| n == candidate)) { - Cow::Owned(ansi_format(&markup::unit(candidate), false)) + Cow::Owned(ansi_format(&markup::unit(candidate.to_string()), false)) } else if ctx.dimension_names().iter().any(|n| n == candidate) { - Cow::Owned(ansi_format(&markup::type_identifier(candidate), false)) + Cow::Owned(ansi_format( + &markup::type_identifier(candidate.to_string()), + false, + )) } else if KEYWORDS.iter().any(|k| k == &candidate) { - Cow::Owned(ansi_format(&markup::keyword(candidate), false)) + Cow::Owned(ansi_format(&markup::keyword(candidate.to_string()), false)) } else { Cow::Borrowed(candidate) } diff --git a/numbat-cli/src/main.rs b/numbat-cli/src/main.rs index ff5fa868..47b44829 100644 --- a/numbat-cli/src/main.rs +++ b/numbat-cli/src/main.rs @@ -418,7 +418,7 @@ impl Cli { let m = m::text( "successfully saved session history to", ) + m::space() - + m::string(dst); + + m::string(dst.to_string()); println!("{}", ansi_format(&m, interactive)); } Err(err) => { diff --git a/numbat/src/ast.rs b/numbat/src/ast.rs index 2741e392..3a12655d 100644 --- a/numbat/src/ast.rs +++ b/numbat/src/ast.rs @@ -365,7 +365,7 @@ impl PrettyPrint for TypeExpression { fn pretty_print(&self) -> Markup { match self { TypeExpression::Unity(_) => m::type_identifier("1"), - TypeExpression::TypeIdentifier(_, ident) => m::type_identifier(ident), + TypeExpression::TypeIdentifier(_, ident) => m::type_identifier(ident.clone()), TypeExpression::Multiply(_, lhs, rhs) => { lhs.pretty_print() + m::space() + m::operator("×") + m::space() + rhs.pretty_print() } diff --git a/numbat/src/column_formatter.rs b/numbat/src/column_formatter.rs index a025fd99..be0e4b88 100644 --- a/numbat/src/column_formatter.rs +++ b/numbat/src/column_formatter.rs @@ -37,7 +37,7 @@ impl ColumnFormatter { if min_num_columns < 1 { for entry in entries { - result += Markup::from(FormattedString(OutputType::Normal, format, entry)) + result += Markup::from(FormattedString(OutputType::Normal, format, entry.into())) + m::whitespace(" ".repeat(self.padding)); } return result; @@ -81,7 +81,7 @@ impl ColumnFormatter { result += Markup::from(FormattedString( OutputType::Normal, format, - (*entry).into(), + entry.to_string().into(), )); result += m::whitespace(" ".repeat(whitespace_length)); } else { diff --git a/numbat/src/help.rs b/numbat/src/help.rs index ea26c297..4dac43b1 100644 --- a/numbat/src/help.rs +++ b/numbat/src/help.rs @@ -62,7 +62,7 @@ pub fn help_markup() -> m::Markup { let mut example_context = Context::new(BuiltinModuleImporter::default()); let _use_prelude_output = evaluate_example(&mut example_context, "use prelude"); for example in examples.iter() { - output += m::text(">>> ") + m::text(example) + m::nl(); + output += m::text(">>> ") + m::text(*example) + m::nl(); output += evaluate_example(&mut example_context, example) + m::nl(); } output diff --git a/numbat/src/lib.rs b/numbat/src/lib.rs index ec56a233..20055bd7 100644 --- a/numbat/src/lib.rs +++ b/numbat/src/lib.rs @@ -336,7 +336,8 @@ impl Context { .ok() .map(|(_, md)| md) { - let mut help = m::text("Unit: ") + m::unit(md.name.as_deref().unwrap_or(keyword)); + let mut help = + m::text("Unit: ") + m::unit(md.name.unwrap_or_else(|| keyword.to_string())); if let Some(url) = &md.url { help += m::text(" (") + m::string(url_encode(url)) + m::text(")"); } @@ -357,12 +358,13 @@ impl Context { let desc = "Description: "; let mut lines = description.lines(); help += m::text(desc) - + m::text(lines.by_ref().next().unwrap_or("").trim()) + + m::text(lines.by_ref().next().unwrap_or("").trim().to_string()) + m::nl(); for line in lines { - help += - m::whitespace(" ".repeat(desc.len())) + m::text(line.trim()) + m::nl(); + help += m::whitespace(" ".repeat(desc.len())) + + m::text(line.trim().to_string()) + + m::nl(); } } @@ -385,17 +387,17 @@ impl Context { if !prefix.is_none() { help += m::nl() + m::value("1 ") - + m::unit(keyword) + + m::unit(keyword.to_string()) + m::text(" = ") + m::value(prefix.factor().pretty_print()) + m::space() - + m::unit(&full_name); + + m::unit(full_name.clone()); } if let Some(BaseUnitAndFactor(prod, num)) = x { help += m::nl() + m::value("1 ") - + m::unit(&full_name) + + m::unit(full_name.clone()) + m::text(" = ") + m::value(num.pretty_print()) + m::space() @@ -407,7 +409,7 @@ impl Context { Some(m::FormatType::Unit), ); } else { - help += m::nl() + m::unit(&full_name) + m::text(" is a base unit"); + help += m::nl() + m::unit(full_name.clone()) + m::text(" is a base unit"); } }; @@ -420,9 +422,9 @@ impl Context { if let Some(l) = self.interpreter.lookup_global(keyword) { let mut help = m::text("Variable: "); if let Some(name) = &l.metadata.name { - help += m::text(name); + help += m::text(name.clone()); } else { - help += m::identifier(keyword); + help += m::identifier(keyword.to_string()); } if let Some(url) = &l.metadata.url { help += m::text(" (") + m::string(url_encode(url)) + m::text(")"); @@ -432,11 +434,14 @@ impl Context { if let Some(description) = &l.metadata.description { let desc = "Description: "; let mut lines = description.lines(); - help += - m::text(desc) + m::text(lines.by_ref().next().unwrap_or("").trim()) + m::nl(); + help += m::text(desc) + + m::text(lines.by_ref().next().unwrap_or("").trim().to_string()) + + m::nl(); for line in lines { - help += m::whitespace(" ".repeat(desc.len())) + m::text(line.trim()) + m::nl(); + help += m::whitespace(" ".repeat(desc.len())) + + m::text(line.trim().to_string()) + + m::nl(); } } @@ -465,9 +470,9 @@ impl Context { let mut help = m::text("Function: "); if let Some(name) = &metadata.name { - help += m::text(name); + help += m::text(name.to_string()); } else { - help += m::identifier(keyword); + help += m::identifier(keyword.to_string()); } if let Some(url) = &metadata.url { help += m::text(" (") + m::string(url_encode(url)) + m::text(")"); @@ -482,11 +487,14 @@ impl Context { if let Some(description) = &metadata.description { let desc = "Description: "; let mut lines = description.lines(); - help += - m::text(desc) + m::text(lines.by_ref().next().unwrap_or("").trim()) + m::nl(); + help += m::text(desc) + + m::text(lines.by_ref().next().unwrap_or("").trim().to_string()) + + m::nl(); for line in lines { - help += m::whitespace(" ".repeat(desc.len())) + m::text(line.trim()) + m::nl(); + help += m::whitespace(" ".repeat(desc.len())) + + m::text(line.trim().to_string()) + + m::nl(); } } diff --git a/numbat/src/markup.rs b/numbat/src/markup.rs index 850e3908..e2f5f342 100644 --- a/numbat/src/markup.rs +++ b/numbat/src/markup.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{borrow::Cow, fmt::Display}; #[derive(Debug, Copy, Clone, PartialEq)] pub enum FormatType { @@ -23,7 +23,7 @@ pub enum OutputType { } #[derive(Debug, Clone, PartialEq)] -pub struct FormattedString(pub OutputType, pub FormatType, pub String); +pub struct FormattedString(pub OutputType, pub FormatType, pub Cow<'static, str>); #[derive(Debug, Clone, Default, PartialEq)] pub struct Markup(pub Vec); @@ -66,7 +66,7 @@ pub fn space() -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Whitespace, - " ".to_string(), + " ".into(), )) } @@ -74,99 +74,99 @@ pub fn empty() -> Markup { Markup::default() } -pub fn whitespace(text: impl AsRef) -> Markup { +pub fn whitespace(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Whitespace, - text.as_ref().to_string(), + text.into(), )) } -pub fn emphasized(text: impl AsRef) -> Markup { +pub fn emphasized(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Emphasized, - text.as_ref().to_string(), + text.into(), )) } -pub fn dimmed(text: impl AsRef) -> Markup { +pub fn dimmed(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Dimmed, - text.as_ref().to_string(), + text.into(), )) } -pub fn text(text: impl AsRef) -> Markup { +pub fn text(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Text, - text.as_ref().to_string(), + text.into(), )) } -pub fn string(text: impl AsRef) -> Markup { +pub fn string(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::String, - text.as_ref().to_string(), + text.into(), )) } -pub fn keyword(text: impl AsRef) -> Markup { +pub fn keyword(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Keyword, - text.as_ref().to_string(), + text.into(), )) } -pub fn value(text: impl AsRef) -> Markup { +pub fn value(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Value, - text.as_ref().to_string(), + text.into(), )) } -pub fn unit(text: impl AsRef) -> Markup { +pub fn unit(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Unit, - text.as_ref().to_string(), + text.into(), )) } -pub fn identifier(text: impl AsRef) -> Markup { +pub fn identifier(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Identifier, - text.as_ref().to_string(), + text.into(), )) } -pub fn type_identifier(text: impl AsRef) -> Markup { +pub fn type_identifier(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::TypeIdentifier, - text.as_ref().to_string(), + text.into(), )) } -pub fn operator(text: impl AsRef) -> Markup { +pub fn operator(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Operator, - text.as_ref().to_string(), + text.into(), )) } -pub fn decorator(text: impl AsRef) -> Markup { +pub fn decorator(text: impl Into>) -> Markup { Markup::from(FormattedString( OutputType::Normal, FormatType::Decorator, - text.as_ref().to_string(), + text.into(), )) } @@ -206,6 +206,6 @@ pub struct PlainTextFormatter; impl Formatter for PlainTextFormatter { fn format_part(&self, FormattedString(_, _, text): &FormattedString) -> String { - text.clone() + text.to_string() } } diff --git a/numbat/src/product.rs b/numbat/src/product.rs index 2e907dc5..ef3fb031 100644 --- a/numbat/src/product.rs +++ b/numbat/src/product.rs @@ -54,7 +54,7 @@ impl self.pretty_print(), - [single] => m::type_identifier(single), - multiple => { - Itertools::intersperse(multiple.iter().map(m::type_identifier), m::dimmed(" or ")) - .sum() - } + [single] => m::type_identifier(single.to_string()), + multiple => Itertools::intersperse( + multiple.iter().cloned().map(m::type_identifier), + m::dimmed(" or "), + ) + .sum(), } } @@ -325,11 +326,11 @@ impl std::fmt::Display for Type { impl PrettyPrint for Type { fn pretty_print(&self) -> Markup { match self { - Type::TVar(TypeVariable::Named(name)) => m::type_identifier(name), + Type::TVar(TypeVariable::Named(name)) => m::type_identifier(name.clone()), Type::TVar(TypeVariable::Quantified(_)) => { unreachable!("Quantified types should not be printed") } - Type::TPar(name) => m::type_identifier(name), + Type::TPar(name) => m::type_identifier(name.clone()), Type::Dimension(d) => d.pretty_print(), Type::Boolean => m::type_identifier("Bool"), Type::String => m::type_identifier("String"), @@ -349,7 +350,7 @@ impl PrettyPrint for Type { + return_type.pretty_print() + m::operator("]") } - Type::Struct(info) => m::type_identifier(&info.name), + Type::Struct(info) => m::type_identifier(info.name.clone()), Type::List(element_type) => { m::type_identifier("List") + m::operator("<") @@ -472,7 +473,7 @@ impl PrettyPrint for StringPart { let mut markup = m::operator("{") + expr.pretty_print(); if let Some(format_specifiers) = format_specifiers { - markup += m::text(format_specifiers); + markup += m::text(format_specifiers.clone()); } markup += m::operator("}"); @@ -847,7 +848,7 @@ fn decorator_markup(decorators: &Vec) -> Markup { + m::operator("(") + Itertools::intersperse( names.iter().map(|(name, accepts_prefix, _)| { - m::unit(name) + accepts_prefix_markup(accepts_prefix) + m::unit(name.to_string()) + accepts_prefix_markup(accepts_prefix) }), m::operator(", "), ) @@ -855,15 +856,21 @@ fn decorator_markup(decorators: &Vec) -> Markup { + m::operator(")") } Decorator::Url(url) => { - m::decorator("@url") + m::operator("(") + m::string(url) + m::operator(")") + m::decorator("@url") + + m::operator("(") + + m::string(url.clone()) + + m::operator(")") } Decorator::Name(name) => { - m::decorator("@name") + m::operator("(") + m::string(name) + m::operator(")") + m::decorator("@name") + + m::operator("(") + + m::string(name.clone()) + + m::operator(")") } Decorator::Description(description) => { m::decorator("@description") + m::operator("(") - + m::string(description) + + m::string(description.clone()) + m::operator(")") } } @@ -890,7 +897,7 @@ pub fn pretty_print_function_signature<'a>( m::operator("<") + Itertools::intersperse( type_parameters.iter().map(|tv| { - m::type_identifier(tv.unsafe_name()) + m::type_identifier(tv.unsafe_name().to_string()) + if fn_type.bounds.is_dtype_bound(tv) { m::operator(":") + m::space() + m::type_identifier("Dim") } else { @@ -905,7 +912,7 @@ pub fn pretty_print_function_signature<'a>( let markup_parameters = Itertools::intersperse( parameters.map(|(name, parameter_type)| { - m::identifier(name) + m::operator(":") + m::space() + parameter_type.clone() + m::identifier(name.to_string()) + m::operator(":") + m::space() + parameter_type }), m::operator(", "), ) @@ -916,7 +923,7 @@ pub fn pretty_print_function_signature<'a>( m::keyword("fn") + m::space() - + m::identifier(function_name) + + m::identifier(function_name.to_string()) + markup_type_parameters + m::operator("(") + markup_parameters @@ -937,7 +944,7 @@ impl PrettyPrint for Statement<'_> { )) => { m::keyword("let") + m::space() - + m::identifier(identifier) + + m::identifier(identifier.to_string()) + m::operator(":") + m::space() + readable_type.clone() @@ -984,7 +991,7 @@ impl PrettyPrint for Statement<'_> { plv += m::nl() + introducer_keyword + m::space() - + m::identifier(identifier) + + m::identifier(identifier.clone()) + m::operator(":") + m::space() + readable_type.clone() @@ -1012,12 +1019,12 @@ impl PrettyPrint for Statement<'_> { } Statement::Expression(expr) => expr.pretty_print(), Statement::DefineDimension(identifier, dexprs) if dexprs.is_empty() => { - m::keyword("dimension") + m::space() + m::type_identifier(identifier) + m::keyword("dimension") + m::space() + m::type_identifier(identifier.clone()) } Statement::DefineDimension(identifier, dexprs) => { m::keyword("dimension") + m::space() - + m::type_identifier(identifier) + + m::type_identifier(identifier.clone()) + m::space() + m::operator("=") + m::space() @@ -1031,7 +1038,7 @@ impl PrettyPrint for Statement<'_> { decorator_markup(decorators) + m::keyword("unit") + m::space() - + m::unit(identifier) + + m::unit(identifier.clone()) + m::operator(":") + m::space() + annotation @@ -1050,7 +1057,7 @@ impl PrettyPrint for Statement<'_> { decorator_markup(decorators) + m::keyword("unit") + m::space() - + m::unit(identifier) + + m::unit(identifier.clone()) + m::operator(":") + m::space() + readable_type.clone() @@ -1087,7 +1094,7 @@ impl PrettyPrint for Statement<'_> { m::space() + Itertools::intersperse( fields.iter().map(|(n, (_, t))| { - m::identifier(n) + m::identifier(n.clone()) + m::operator(":") + m::space() + t.pretty_print() @@ -1158,7 +1165,7 @@ fn pretty_print_binop(op: &BinaryOperator, lhs: &Expression, rhs: &Expression) - } (Expression::Scalar(_, s, _), Expression::Identifier(_, name, _type)) => { // Fuse multiplication of a scalar and identifier - pretty_scalar(*s) + m::space() + m::identifier(name) + pretty_scalar(*s) + m::space() + m::identifier(name.clone()) } _ => { let add_parens_if_needed = |expr: &Expression| { @@ -1248,7 +1255,7 @@ impl PrettyPrint for Expression { match self { Scalar(_, n, _) => pretty_scalar(*n), - Identifier(_, name, _type) => m::identifier(name), + Identifier(_, name, _type) => m::identifier(name.clone()), UnitIdentifier(_, prefix, _name, full_name, _type) => { m::unit(format!("{}{}", prefix.as_string_long(), full_name)) } @@ -1264,7 +1271,7 @@ impl PrettyPrint for Expression { BinaryOperator(_, op, lhs, rhs, _type) => pretty_print_binop(op, lhs, rhs), BinaryOperatorForDate(_, op, lhs, rhs, _type) => pretty_print_binop(op, lhs, rhs), FunctionCall(_, _, name, args, _type) => { - m::identifier(name) + m::identifier(name.clone()) + m::operator("(") + itertools::Itertools::intersperse( args.iter().map(|e| e.pretty_print()), @@ -1308,7 +1315,7 @@ impl PrettyPrint for Expression { m::space() + itertools::Itertools::intersperse( exprs.iter().map(|(n, e)| { - m::identifier(n) + m::identifier(n.clone()) + m::operator(":") + m::space() + e.pretty_print() @@ -1321,7 +1328,7 @@ impl PrettyPrint for Expression { + m::operator("}") } AccessField(_, _, expr, attr, _, _) => { - expr.pretty_print() + m::operator(".") + m::identifier(attr) + expr.pretty_print() + m::operator(".") + m::identifier(attr.clone()) } List(_, elements, _) => { m::operator("[") diff --git a/numbat/src/value.rs b/numbat/src/value.rs index adee05fb..741ddf23 100644 --- a/numbat/src/value.rs +++ b/numbat/src/value.rs @@ -156,7 +156,7 @@ impl PrettyPrint for Value { Value::String(s) => s.pretty_print(), Value::DateTime(dt) => crate::markup::string(crate::datetime::to_string(dt)), Value::FunctionReference(r) => crate::markup::string(r.to_string()), - Value::FormatSpecifiers(Some(s)) => crate::markup::string(s), + Value::FormatSpecifiers(Some(s)) => crate::markup::string(s.clone()), Value::FormatSpecifiers(None) => crate::markup::empty(), Value::StructInstance(struct_info, values) => { crate::markup::type_identifier(struct_info.name.clone()) @@ -168,7 +168,7 @@ impl PrettyPrint for Value { crate::markup::space() + itertools::Itertools::intersperse( struct_info.fields.keys().zip(values).map(|(name, val)| { - crate::markup::identifier(name) + crate::markup::identifier(name.clone()) + crate::markup::operator(":") + crate::markup::space() + val.pretty_print() From d9a164d059ca754c067d10fe30bbe0afc8334cfc Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Sat, 28 Sep 2024 00:33:11 -0400 Subject: [PATCH 19/46] Deduped some code --- numbat/src/ast.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/numbat/src/ast.rs b/numbat/src/ast.rs index 3a12655d..8e48ccc3 100644 --- a/numbat/src/ast.rs +++ b/numbat/src/ast.rs @@ -36,21 +36,26 @@ impl PrettyPrint for BinaryOperator { fn pretty_print(&self) -> Markup { use BinaryOperator::*; + let operator = match self { + Add => "+", + Sub => "-", + Mul => "×", + Div => "/", + Power => "^", + ConvertTo => "➞", + LessThan => "<", + GreaterThan => ">", + LessOrEqual => "≤", + GreaterOrEqual => "≥", + Equal => "==", + NotEqual => "≠", + LogicalAnd => "&&", + LogicalOr => "||", + }; + match self { - Add => m::space() + m::operator("+") + m::space(), - Sub => m::space() + m::operator("-") + m::space(), - Mul => m::space() + m::operator("×") + m::space(), - Div => m::space() + m::operator("/") + m::space(), Power => m::operator("^"), - ConvertTo => m::space() + m::operator("➞") + m::space(), - LessThan => m::space() + m::operator("<") + m::space(), - GreaterThan => m::space() + m::operator(">") + m::space(), - LessOrEqual => m::space() + m::operator("≤") + m::space(), - GreaterOrEqual => m::space() + m::operator("≥") + m::space(), - Equal => m::space() + m::operator("==") + m::space(), - NotEqual => m::space() + m::operator("≠") + m::space(), - LogicalAnd => m::space() + m::operator("&&") + m::space(), - LogicalOr => m::space() + m::operator("||") + m::space(), + _ => m::space() + m::operator(operator) + m::space(), } } } From 9d426db128c91f001d02a196a5f629435072f7b2 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Sat, 28 Sep 2024 00:39:23 -0400 Subject: [PATCH 20/46] Formatting --- numbat/src/column_formatter.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/numbat/src/column_formatter.rs b/numbat/src/column_formatter.rs index be0e4b88..380d3fef 100644 --- a/numbat/src/column_formatter.rs +++ b/numbat/src/column_formatter.rs @@ -37,8 +37,9 @@ impl ColumnFormatter { if min_num_columns < 1 { for entry in entries { - result += Markup::from(FormattedString(OutputType::Normal, format, entry.into())) - + m::whitespace(" ".repeat(self.padding)); + result += + Markup::from(FormattedString(OutputType::Normal, format, entry.into())) + + m::whitespace(" ".repeat(self.padding)); } return result; } From 6cc1dc33f5c259f9e6eda7ec08525cd0efa4a811 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Sat, 28 Sep 2024 11:06:14 -0400 Subject: [PATCH 21/46] Minor code cleanup --- numbat/src/ast.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/numbat/src/ast.rs b/numbat/src/ast.rs index 8e48ccc3..dbe40fcb 100644 --- a/numbat/src/ast.rs +++ b/numbat/src/ast.rs @@ -36,7 +36,7 @@ impl PrettyPrint for BinaryOperator { fn pretty_print(&self) -> Markup { use BinaryOperator::*; - let operator = match self { + let operator = m::operator(match self { Add => "+", Sub => "-", Mul => "×", @@ -51,11 +51,11 @@ impl PrettyPrint for BinaryOperator { NotEqual => "≠", LogicalAnd => "&&", LogicalOr => "||", - }; + }); match self { - Power => m::operator("^"), - _ => m::space() + m::operator(operator) + m::space(), + Power => operator, + _ => m::space() + operator + m::space(), } } } From f3fba196bece9af47bb1ea5c8137ae9f1869e973 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Sat, 28 Sep 2024 12:22:02 -0400 Subject: [PATCH 22/46] Code cleanup reducing clones (in theory) --- numbat/src/markup.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/numbat/src/markup.rs b/numbat/src/markup.rs index e2f5f342..84941254 100644 --- a/numbat/src/markup.rs +++ b/numbat/src/markup.rs @@ -43,10 +43,9 @@ impl Display for Markup { impl std::ops::Add for Markup { type Output = Markup; - fn add(self, rhs: Self) -> Self::Output { - let mut res = self.0; - res.extend_from_slice(&rhs.0); - Markup(res) + fn add(mut self, rhs: Self) -> Self::Output { + self.0.extend(rhs.0); + self } } From a951c12a6ee391133ba34e27b268512085d54e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Mon, 30 Sep 2024 00:04:14 +0200 Subject: [PATCH 23/46] Add explicit file encoding to `open` calls --- book/build.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/book/build.py b/book/build.py index 61ab370f..ec09684c 100644 --- a/book/build.py +++ b/book/build.py @@ -15,14 +15,14 @@ def generate_example( print(path_out) code = [] - with open(path_in, "r") as fin: + with open(path_in, "r", encoding="utf-8") as fin: for line in fin: if not (strip_asserts and "assert_eq" in line): code.append(line) url = f"https://numbat.dev/?q={urllib.parse.quote_plus(''.join(code))}" - with open(path_out, "w") as fout: + with open(path_out, "w", encoding="utf-8") as fout: fout.write("\n") fout.write("\n") fout.write(f"# {title}\n") @@ -80,7 +80,7 @@ def xkcd_footer(number, img_name): ) path_units = SCRIPT_DIR / "src" / "list-units.md" -with open(path_units, "w") as f: +with open(path_units, "w", encoding="utf-8") as f: print("Generating list of units...", flush=True) subprocess.run( ["cargo", "run", "--release", "--quiet", "--example=inspect", "units"], @@ -91,7 +91,7 @@ def xkcd_footer(number, img_name): def list_of_functions(file_name, document): path = SCRIPT_DIR / "src" / f"list-functions-{file_name}.md" - with open(path, "w") as f: + with open(path, "w", encoding="utf-8") as f: print(f"# {document['title']}\n", file=f, flush=True) if introduction := document.get("introduction"): From dfaae9fbe3fa64f4d2968095ce3cbd74b867bd67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Mon, 30 Sep 2024 00:04:55 +0200 Subject: [PATCH 24/46] Update documentation --- book/src/list-functions-other.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 17142c35..7e427634 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -73,12 +73,19 @@ fn element(pattern: String) -> ChemicalElement Defined in: `units::mixed` +### `unit_list` (Unit list) +Convert a value to a mixed representation using the provided units. + +```nbt +fn unit_list(units: List, value: D) -> List +``` + ### `DMS` (Degrees, minutes, seconds) Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation. More information [here](https://en.wikipedia.org/wiki/Sexagesimal_degree). ```nbt -fn DMS(alpha: Angle) -> String +fn DMS(alpha: Angle) -> List ``` ### `DM` (Degrees, decimal minutes) @@ -86,7 +93,7 @@ Convert an angle to a mixed degrees and decimal minutes representation. More information [here](https://en.wikipedia.org/wiki/Decimal_degrees). ```nbt -fn DM(alpha: Angle) -> String +fn DM(alpha: Angle) -> List ``` ### `feet_and_inches` (Feet and inches) @@ -94,7 +101,7 @@ Convert a length to a mixed feet and inches representation. More information [here](https://en.wikipedia.org/wiki/Foot_(unit)). ```nbt -fn feet_and_inches(length: Length) -> String +fn feet_and_inches(length: Length) -> List ``` ### `pounds_and_ounces` (Pounds and ounces) @@ -102,15 +109,7 @@ Convert a mass to a mixed pounds and ounces representation. More information [here](https://en.wikipedia.org/wiki/Pound_(mass)). ```nbt -fn pounds_and_ounces(mass: Mass) -> String -``` - -### `unit_list` (Unit list) -Converts a value to a mixed unit representation. -More information [here](https://www.gnu.org/software/units/manual/html_node/Unit-Lists.html) - -```nbt -fn unit_list(units: List, value: D) -> List +fn pounds_and_ounces(mass: Mass) -> List ``` ## Temperature conversion From 4245e752f66df95e095c0cb2cb67eda94a5da8a2 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Mon, 30 Sep 2024 18:03:26 -0400 Subject: [PATCH 25/46] Corrected swapped epsilon/varepsilon unicode input shortcuts --- numbat/src/unicode_input.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numbat/src/unicode_input.rs b/numbat/src/unicode_input.rs index ff9ce271..3e052b69 100644 --- a/numbat/src/unicode_input.rs +++ b/numbat/src/unicode_input.rs @@ -54,8 +54,8 @@ pub const UNICODE_INPUT: &[(&[&str], &str)] = &[ (&["beta"], "β"), (&["gamma"], "γ"), (&["delta"], "δ"), - (&["epsilon"], "ε"), - (&["varepsilon"], "ϵ"), + (&["epsilon"], "ϵ"), + (&["varepsilon"], "ε"), (&["zeta"], "ζ"), (&["eta"], "η"), (&["theta"], "θ"), From ec34895e9f18c655f5a0e4ab4c3314f2c567e235 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 15:05:29 -0400 Subject: [PATCH 26/46] Removed allocations in `typed_ast::Statement::Define*` variants (`String` -> `&'a str`) --- numbat/src/bytecode_interpreter.rs | 14 +++++--------- numbat/src/interpreter/mod.rs | 2 +- numbat/src/lib.rs | 2 +- numbat/src/typechecker/mod.rs | 21 ++++++++++++--------- numbat/src/typechecker/tests/mod.rs | 2 +- numbat/src/typed_ast.rs | 16 ++++++++-------- 6 files changed, 28 insertions(+), 29 deletions(-) diff --git a/numbat/src/bytecode_interpreter.rs b/numbat/src/bytecode_interpreter.rs index cc1dfd44..7a5d054a 100644 --- a/numbat/src/bytecode_interpreter.rs +++ b/numbat/src/bytecode_interpreter.rs @@ -335,7 +335,7 @@ impl BytecodeInterpreter { let current_depth = self.current_depth(); for parameter in parameters { self.locals[current_depth].push(Local { - identifier: parameter.1.clone(), + identifier: parameter.1.to_string(), depth: current_depth, metadata: LocalMetadata::default(), }); @@ -352,7 +352,7 @@ impl BytecodeInterpreter { self.vm.end_function(); - self.functions.insert(name.clone(), false); + self.functions.insert(name.to_string(), false); } Statement::DefineFunction( name, @@ -371,7 +371,7 @@ impl BytecodeInterpreter { self.vm .add_foreign_function(name, parameters.len()..=parameters.len()); - self.functions.insert(name.clone(), true); + self.functions.insert(name.to_string(), true); } Statement::DefineDimension(_name, _dexprs) => { // Declaring a dimension is like introducing a new type. The information @@ -407,7 +407,7 @@ impl BytecodeInterpreter { let constant_idx = self.vm.add_constant(Constant::Unit(Unit::new_base( unit_name, - crate::decorator::get_canonical_unit_name(unit_name.as_str(), &decorators[..]), + crate::decorator::get_canonical_unit_name(unit_name, &decorators[..]), ))); for (name, _) in decorator::name_and_aliases(unit_name, decorators) { self.unit_name_to_constant_index @@ -436,11 +436,7 @@ impl BytecodeInterpreter { let unit_information_idx = self.vm.add_unit_information( unit_name, Some( - &crate::decorator::get_canonical_unit_name( - unit_name.as_str(), - &decorators[..], - ) - .name, + &crate::decorator::get_canonical_unit_name(unit_name, &decorators[..]).name, ), UnitMetadata { type_: type_.to_concrete_type(), // We guarantee that derived-unit definitions do not contain generics, so no TGen(..)s can escape diff --git a/numbat/src/interpreter/mod.rs b/numbat/src/interpreter/mod.rs index 998f2f39..b5e93dda 100644 --- a/numbat/src/interpreter/mod.rs +++ b/numbat/src/interpreter/mod.rs @@ -210,7 +210,7 @@ mod tests { .expect("No name resolution errors for inputs in this test suite"); let mut typechecker = crate::typechecker::TypeChecker::default(); let statements_typechecked = typechecker - .check(statements_transformed) + .check(&statements_transformed) .expect("No type check errors for inputs in this test suite"); BytecodeInterpreter::new().interpret_statements( &mut InterpreterSettings::default(), diff --git a/numbat/src/lib.rs b/numbat/src/lib.rs index 20055bd7..175bc987 100644 --- a/numbat/src/lib.rs +++ b/numbat/src/lib.rs @@ -592,7 +592,7 @@ impl Context { let result = self .typechecker - .check(transformed_statements) + .check(&transformed_statements) .map_err(|err| NumbatError::TypeCheckError(*err)); if result.is_err() { diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index 052cc240..ad2eb926 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -1284,7 +1284,7 @@ impl TypeChecker { } typed_ast::Statement::DefineBaseUnit( - unit_name.to_string(), + unit_name, decorators.clone(), type_annotation.clone().map(TypeAnnotation::TypeExpression), TypeScheme::concrete(Type::Dimension(type_specified)), @@ -1320,7 +1320,7 @@ impl TypeChecker { ); } typed_ast::Statement::DefineDerivedUnit( - identifier.to_string(), + identifier, expr_checked, decorators.clone(), type_annotation.clone(), @@ -1424,7 +1424,7 @@ impl TypeChecker { ); typed_parameters.push(( *parameter_span, - parameter.to_string(), + *parameter, parameter_type, type_annotation, )); @@ -1463,7 +1463,10 @@ impl TypeChecker { .iter() .map(|(span, name, tpb)| (*span, name.to_string(), tpb.clone()).clone()) .collect(), - parameters, + parameters: parameters + .into_iter() + .map(|(span, s, o)| (span, s.to_string(), o)) + .collect(), return_type_annotation: return_type_annotation.clone(), fn_type: fn_type.clone(), }, @@ -1579,7 +1582,7 @@ impl TypeChecker { ); typed_ast::Statement::DefineFunction( - function_name.to_string(), + function_name, decorators.clone(), type_parameters .iter() @@ -1590,7 +1593,7 @@ impl TypeChecker { .map(|(span, name, _, type_annotation)| { ( *span, - name.clone(), + *name, (*type_annotation).clone(), crate::markup::empty(), ) @@ -1894,12 +1897,12 @@ impl TypeChecker { pub fn check<'a>( &mut self, - statements: impl IntoIterator>, + statements: &[ast::Statement<'a>], ) -> Result>> { let mut checked_statements = vec![]; - for statement in statements.into_iter() { - checked_statements.push(self.check_statement(&statement)?); + for statement in statements { + checked_statements.push(self.check_statement(statement)?); } Ok(checked_statements) diff --git a/numbat/src/typechecker/tests/mod.rs b/numbat/src/typechecker/tests/mod.rs index 7d6014c0..ae71a29c 100644 --- a/numbat/src/typechecker/tests/mod.rs +++ b/numbat/src/typechecker/tests/mod.rs @@ -60,7 +60,7 @@ fn run_typecheck(input: &str) -> Result> { .map_err(|err| Box::new(err.into()))?; TypeChecker::default() - .check(transformed_statements) + .check(&transformed_statements) .map(|mut statements_checked| statements_checked.pop().unwrap()) } diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 11b1a414..304bbcd8 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -584,13 +584,13 @@ pub enum Statement<'a> { Expression(Expression), DefineVariable(DefineVariable<'a>), DefineFunction( - String, + &'a str, Vec>, // decorators Vec<(String, Option)>, // type parameters Vec<( // parameters: Span, // span of the parameter - String, // parameter name + &'a str, // parameter name Option, // parameter type annotation Markup, // readable parameter type )>, @@ -602,13 +602,13 @@ pub enum Statement<'a> { ), DefineDimension(String, Vec), DefineBaseUnit( - String, + &'a str, Vec>, Option, TypeScheme, ), DefineDerivedUnit( - String, + &'a str, Expression, Vec>, Option, @@ -1009,7 +1009,7 @@ impl PrettyPrint for Statement<'_> { &type_parameters, parameters .iter() - .map(|(_, name, _, type_)| (name.as_str(), type_.clone())), + .map(|(_, name, _, type_)| (*name, type_.clone())), readable_return_type, ) + body .as_ref() @@ -1038,7 +1038,7 @@ impl PrettyPrint for Statement<'_> { decorator_markup(decorators) + m::keyword("unit") + m::space() - + m::unit(identifier.clone()) + + m::unit(identifier.to_string()) + m::operator(":") + m::space() + annotation @@ -1057,7 +1057,7 @@ impl PrettyPrint for Statement<'_> { decorator_markup(decorators) + m::keyword("unit") + m::space() - + m::unit(identifier.clone()) + + m::unit(identifier.to_string()) + m::operator(":") + m::space() + readable_type.clone() @@ -1417,7 +1417,7 @@ mod tests { let transformed_statements = transformer.transform(statements).unwrap().replace_spans(); crate::typechecker::TypeChecker::default() - .check(transformed_statements) + .check(&transformed_statements) .unwrap() .last() .unwrap() From b4bf43471e6b755116c1da882921578970aec4a5 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 15:17:31 -0400 Subject: [PATCH 27/46] DefineVariable name `String` -> `&'a str` --- numbat/src/typechecker/mod.rs | 2 +- numbat/src/typed_ast.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index ad2eb926..e3c8cfbc 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -1219,7 +1219,7 @@ impl TypeChecker { } Ok(typed_ast::DefineVariable( - identifier.to_string(), + identifier, decorators.to_owned(), expr_checked, type_annotation.clone(), diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 304bbcd8..fd4f8fe5 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -571,7 +571,7 @@ impl Expression { #[derive(Debug, Clone, PartialEq)] pub struct DefineVariable<'a>( - pub String, + pub &'a str, pub Vec>, pub Expression, pub Option, @@ -991,7 +991,7 @@ impl PrettyPrint for Statement<'_> { plv += m::nl() + introducer_keyword + m::space() - + m::identifier(identifier.clone()) + + m::identifier(identifier.to_string()) + m::operator(":") + m::space() + readable_type.clone() From cb706319388621ece01f256a18af6f74635da629 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 15:19:46 -0400 Subject: [PATCH 28/46] Borrowed type parameter names in `typed_ast::Statement::DefineFunction` --- numbat/src/typechecker/mod.rs | 2 +- numbat/src/typed_ast.rs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index e3c8cfbc..94422028 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -1586,7 +1586,7 @@ impl TypeChecker { decorators.clone(), type_parameters .iter() - .map(|(_, name, bound)| (name.to_string(), bound.clone())) + .map(|(_, name, bound)| (*name, bound.clone())) .collect(), typed_parameters .iter() diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index fd4f8fe5..1e6491a6 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -585,8 +585,8 @@ pub enum Statement<'a> { DefineVariable(DefineVariable<'a>), DefineFunction( &'a str, - Vec>, // decorators - Vec<(String, Option)>, // type parameters + Vec>, // decorators + Vec<(&'a str, Option)>, // type parameters Vec<( // parameters: Span, // span of the parameter @@ -669,9 +669,8 @@ impl Statement<'_> { return_type_annotation, readable_return_type, ) => { - let (fn_type, _) = fn_type.instantiate_for_printing(Some( - type_parameters.iter().map(|(n, _)| n.as_str()), - )); + let (fn_type, _) = + fn_type.instantiate_for_printing(Some(type_parameters.iter().map(|(n, _)| *n))); for DefineVariable(_, _, _, type_annotation, type_, readable_type) in local_variables @@ -964,9 +963,8 @@ impl PrettyPrint for Statement<'_> { _return_type_annotation, readable_return_type, ) => { - let (fn_type, type_parameters) = fn_type.instantiate_for_printing(Some( - type_parameters.iter().map(|(n, _)| n.as_str()), - )); + let (fn_type, type_parameters) = + fn_type.instantiate_for_printing(Some(type_parameters.iter().map(|(n, _)| *n))); let mut pretty_local_variables = None; let mut first = true; From d4bac249a0f4173dceaf2a7a99f3c5535ae177ac Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 15:22:05 -0400 Subject: [PATCH 29/46] Borrowed name in `typed_ast::Statement::DefineDimension` --- numbat/src/typechecker/mod.rs | 4 ++-- numbat/src/typed_ast.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index 94422028..fa576715 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -1444,7 +1444,7 @@ impl TypeChecker { let parameters: Vec<_> = typed_parameters .iter() - .map(|(span, name, _, annotation)| (*span, name.clone(), (*annotation).clone())) + .map(|(span, name, _, annotation)| (*span, name, (*annotation).clone())) .collect(); let parameter_types = typed_parameters .iter() @@ -1643,7 +1643,7 @@ impl TypeChecker { .add_base_dimension(name) .map_err(TypeCheckError::RegistryError)?; } - typed_ast::Statement::DefineDimension(name.to_string(), dexprs.clone()) + typed_ast::Statement::DefineDimension(name, dexprs.clone()) } ast::Statement::ProcedureCall(span, kind @ ProcedureKind::Type, args) => { if args.len() != 1 { diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 1e6491a6..35e99e97 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -600,7 +600,7 @@ pub enum Statement<'a> { Option, // return type annotation Markup, // readable return type ), - DefineDimension(String, Vec), + DefineDimension(&'a str, Vec), DefineBaseUnit( &'a str, Vec>, @@ -1017,12 +1017,12 @@ impl PrettyPrint for Statement<'_> { } Statement::Expression(expr) => expr.pretty_print(), Statement::DefineDimension(identifier, dexprs) if dexprs.is_empty() => { - m::keyword("dimension") + m::space() + m::type_identifier(identifier.clone()) + m::keyword("dimension") + m::space() + m::type_identifier(identifier.to_string()) } Statement::DefineDimension(identifier, dexprs) => { m::keyword("dimension") + m::space() - + m::type_identifier(identifier.clone()) + + m::type_identifier(identifier.to_string()) + m::space() + m::operator("=") + m::space() From 2fb73e605a3dccf6a5c7aa7a724c5abc0e4b8dae Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 16:55:20 -0400 Subject: [PATCH 30/46] Replaced many `String`s in `typed_ast::Expression` with `&'a str` (generally anywhere we could borrow from the input) --- numbat/src/ast.rs | 4 +- numbat/src/bytecode_interpreter.rs | 16 +++--- numbat/src/lib.rs | 7 ++- numbat/src/parser.rs | 4 +- numbat/src/prefix_parser.rs | 2 +- numbat/src/traversal.rs | 4 +- numbat/src/typechecker/mod.rs | 57 ++++++++++--------- numbat/src/typechecker/substitutions.rs | 2 +- numbat/src/typed_ast.rs | 73 +++++++++++++------------ 9 files changed, 89 insertions(+), 80 deletions(-) diff --git a/numbat/src/ast.rs b/numbat/src/ast.rs index dbe40fcb..0e8d2358 100644 --- a/numbat/src/ast.rs +++ b/numbat/src/ast.rs @@ -66,7 +66,7 @@ pub enum StringPart<'a> { Interpolation { span: Span, expr: Box>, - format_specifiers: Option, + format_specifiers: Option<&'a str>, }, } @@ -512,7 +512,7 @@ impl ReplaceSpans for StringPart<'_> { } => StringPart::Interpolation { span: Span::dummy(), expr: Box::new(expr.replace_spans()), - format_specifiers: format_specifiers.clone(), + format_specifiers: *format_specifiers, }, } } diff --git a/numbat/src/bytecode_interpreter.rs b/numbat/src/bytecode_interpreter.rs index 7a5d054a..636a65b3 100644 --- a/numbat/src/bytecode_interpreter.rs +++ b/numbat/src/bytecode_interpreter.rs @@ -69,15 +69,15 @@ impl BytecodeInterpreter { .rposition(|l| &l.identifier == identifier) { self.vm.add_op1(Op::GetUpvalue, upvalue_position as u16); - } else if LAST_RESULT_IDENTIFIERS.contains(&identifier.as_str()) { + } else if LAST_RESULT_IDENTIFIERS.contains(identifier) { self.vm.add_op(Op::GetLastResult); - } else if let Some(is_foreign) = self.functions.get(identifier) { + } else if let Some(is_foreign) = self.functions.get(*identifier) { let index = self .vm .add_constant(Constant::FunctionReference(if *is_foreign { - FunctionReference::Foreign(identifier.clone()) + FunctionReference::Foreign(identifier.to_string()) } else { - FunctionReference::Normal(identifier.clone()) + FunctionReference::Normal(identifier.to_string()) })); self.vm.add_op1(Op::LoadConstant, index); } else { @@ -178,7 +178,7 @@ impl BytecodeInterpreter { let sorted_exprs = exprs .iter() - .sorted_by_key(|(n, _)| struct_info.fields.get_index_of(n).unwrap()); + .sorted_by_key(|(n, _)| struct_info.fields.get_index_of(*n).unwrap()); for (_, expr) in sorted_exprs.rev() { self.compile_expression(expr)?; @@ -198,7 +198,7 @@ impl BytecodeInterpreter { ); }; - let idx = struct_info.fields.get_index_of(attr).unwrap(); + let idx = struct_info.fields.get_index_of(*attr).unwrap(); self.vm.add_op1(Op::AccessStructField, idx as u16); } @@ -221,7 +221,7 @@ impl BytecodeInterpreter { for part in string_parts { match part { StringPart::Fixed(s) => { - let index = self.vm.add_constant(Constant::String(s.clone())); + let index = self.vm.add_constant(Constant::String(s.to_string())); self.vm.add_op1(Op::LoadConstant, index) } StringPart::Interpolation { @@ -231,7 +231,7 @@ impl BytecodeInterpreter { } => { self.compile_expression(expr)?; let index = self.vm.add_constant(Constant::FormatSpecifiers( - format_specifiers.clone(), + format_specifiers.map(|s| s.to_string()), )); self.vm.add_op1(Op::LoadConstant, index) } diff --git a/numbat/src/lib.rs b/numbat/src/lib.rs index 175bc987..8e985843 100644 --- a/numbat/src/lib.rs +++ b/numbat/src/lib.rs @@ -391,13 +391,13 @@ impl Context { + m::text(" = ") + m::value(prefix.factor().pretty_print()) + m::space() - + m::unit(full_name.clone()); + + m::unit(full_name.to_string()); } if let Some(BaseUnitAndFactor(prod, num)) = x { help += m::nl() + m::value("1 ") - + m::unit(full_name.clone()) + + m::unit(full_name.to_string()) + m::text(" = ") + m::value(num.pretty_print()) + m::space() @@ -409,7 +409,8 @@ impl Context { Some(m::FormatType::Unit), ); } else { - help += m::nl() + m::unit(full_name.clone()) + m::text(" is a base unit"); + help += + m::nl() + m::unit(full_name.to_string()) + m::text(" is a base unit"); } }; diff --git a/numbat/src/parser.rs b/numbat/src/parser.rs index 946ba9be..5c021729 100644 --- a/numbat/src/parser.rs +++ b/numbat/src/parser.rs @@ -1581,7 +1581,7 @@ impl<'a> Parser<'a> { let format_specifiers = self .match_exact(tokens, TokenKind::StringInterpolationSpecifiers) - .map(|token| token.lexeme.to_owned()); + .map(|token| token.lexeme); parts.push(StringPart::Interpolation { span: expr.full_span(), @@ -3304,7 +3304,7 @@ mod tests { StringPart::Interpolation { span: Span::dummy(), expr: Box::new(binop!(scalar!(1.0), Add, scalar!(2.0))), - format_specifiers: Some(":0.2".to_string()), + format_specifiers: Some(":0.2"), }, ], ), diff --git a/numbat/src/prefix_parser.rs b/numbat/src/prefix_parser.rs index 2a21e555..c8f93165 100644 --- a/numbat/src/prefix_parser.rs +++ b/numbat/src/prefix_parser.rs @@ -255,7 +255,7 @@ impl PrefixParser { return PrefixParserResult::UnitIdentifier( info.definition_span, Prefix::none(), - input.into(), + input.to_string(), info.full_name.clone(), ); } diff --git a/numbat/src/traversal.rs b/numbat/src/traversal.rs index c648ba32..a7c883c1 100644 --- a/numbat/src/traversal.rs +++ b/numbat/src/traversal.rs @@ -11,7 +11,7 @@ impl ForAllTypeSchemes for StructInfo { } } -impl ForAllTypeSchemes for Expression { +impl ForAllTypeSchemes for Expression<'_> { fn for_all_type_schemes(&mut self, f: &mut dyn FnMut(&mut TypeScheme)) { match self { Expression::Scalar(_, _, type_) => f(type_), @@ -143,7 +143,7 @@ impl ForAllExpressions for Statement<'_> { } } -impl ForAllExpressions for Expression { +impl ForAllExpressions for Expression<'_> { fn for_all_expressions(&self, f: &mut dyn FnMut(&Expression)) { f(self); match self { diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index fa576715..6aa8994c 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -65,16 +65,16 @@ pub struct TypeChecker { constraints: ConstraintSet, } -struct ElaborationDefinitionArgs<'a> { +struct ElaborationDefinitionArgs<'a, 'b> { identifier_span: Span, - expr: &'a ast::Expression<'a>, + expr: &'b ast::Expression<'a>, type_annotation_span: Option, - type_annotation: Option<&'a TypeAnnotation>, - operation: &'a str, + type_annotation: Option<&'b TypeAnnotation>, + operation: &'b str, expected_name: &'static str, actual_name: &'static str, actual_name_for_fix: &'static str, - elaboration_kind: &'a str, + elaboration_kind: &'b str, } impl TypeChecker { @@ -172,28 +172,28 @@ impl TypeChecker { })?) } - fn get_proper_function_reference( + fn get_proper_function_reference<'a>( &self, - expr: &ast::Expression, - ) -> Option<(String, &FunctionSignature)> { + expr: &ast::Expression<'a>, + ) -> Option<(&'a str, &FunctionSignature)> { match expr { ast::Expression::Identifier(_, name) => self .env .get_function_info(name) - .map(|(signature, _)| (name.to_string(), signature)), + .map(|(signature, _)| (*name, signature)), _ => None, } } - fn proper_function_call( + fn proper_function_call<'a>( &mut self, span: &Span, full_span: &Span, - function_name: &str, + function_name: &'a str, signature: &FunctionSignature, - arguments: Vec, + arguments: Vec>, argument_types: Vec, - ) -> Result { + ) -> Result> { let FunctionSignature { name: _, definition_span, @@ -288,13 +288,16 @@ impl TypeChecker { Ok(typed_ast::Expression::FunctionCall( *span, *full_span, - function_name.into(), + function_name, arguments, TypeScheme::concrete(return_type.as_ref().clone()), )) } - fn elaborate_expression(&mut self, ast: &ast::Expression) -> Result { + fn elaborate_expression<'a>( + &mut self, + ast: &ast::Expression<'a>, + ) -> Result> { Ok(match ast { ast::Expression::Scalar(span, n) if n.to_f64().is_zero() || n.to_f64().is_infinite() || n.to_f64().is_nan() => @@ -325,7 +328,7 @@ impl TypeChecker { } }; - typed_ast::Expression::Identifier(*span, name.to_string(), TypeScheme::concrete(ty)) + typed_ast::Expression::Identifier(*span, name, TypeScheme::concrete(ty)) } ast::Expression::UnitIdentifier(span, prefix, name, full_name) => { let type_scheme = self.identifier_type(*span, name)?.clone(); @@ -780,7 +783,7 @@ impl TypeChecker { self.proper_function_call( span, full_span, - &name, + name, &signature, arguments_checked, argument_types, @@ -880,7 +883,7 @@ impl TypeChecker { format_specifiers, } => Ok(typed_ast::StringPart::Interpolation { span: *span, - format_specifiers: format_specifiers.clone(), + format_specifiers: format_specifiers.as_ref().copied(), expr: Box::new(self.elaborate_expression(expr)?), }), }) @@ -933,7 +936,7 @@ impl TypeChecker { let name = *name; let fields_checked = fields .iter() - .map(|(_, n, v)| Ok((n.to_string(), self.elaborate_expression(v)?))) + .map(|(_, n, v)| Ok((*n, self.elaborate_expression(v)?))) .collect::>>()?; let Some(struct_info) = self.structs.get(name).cloned() else { @@ -958,12 +961,12 @@ impl TypeChecker { )); } - let Some((expected_field_span, expected_type)) = struct_info.fields.get(field) + let Some((expected_field_span, expected_type)) = struct_info.fields.get(*field) else { return Err(Box::new(TypeCheckError::UnknownFieldInStructInstantiation( *span, struct_info.definition_span, - field.clone(), + field.to_string(), struct_info.name.clone(), ))); }; @@ -986,7 +989,7 @@ impl TypeChecker { let missing_fields = { let mut fields = struct_info.fields.clone(); - fields.retain(|f, _| !seen_fields.contains_key(f)); + fields.retain(|f, _| !seen_fields.contains_key(&f.as_str())); fields.into_iter().map(|(n, (_, t))| (n, t)).collect_vec() }; @@ -1050,7 +1053,7 @@ impl TypeChecker { *ident_span, *full_span, Box::new(expr_checked), - field_name.to_owned(), + field_name, TypeScheme::concrete(type_), TypeScheme::concrete(field_type), ) @@ -1105,10 +1108,10 @@ impl TypeChecker { }) } - fn _elaborate_inner( + fn _elaborate_inner<'a>( &mut self, - definition: ElaborationDefinitionArgs, - ) -> Result<(typed_ast::Expression, typed_ast::Type)> { + definition: ElaborationDefinitionArgs<'a, '_>, + ) -> Result<(typed_ast::Expression<'a>, typed_ast::Type)> { let ElaborationDefinitionArgs { identifier_span, expr, @@ -1220,7 +1223,7 @@ impl TypeChecker { Ok(typed_ast::DefineVariable( identifier, - decorators.to_owned(), + decorators.clone(), expr_checked, type_annotation.clone(), TypeScheme::concrete(type_deduced), diff --git a/numbat/src/typechecker/substitutions.rs b/numbat/src/typechecker/substitutions.rs index 400e4a29..94e2b170 100644 --- a/numbat/src/typechecker/substitutions.rs +++ b/numbat/src/typechecker/substitutions.rs @@ -148,7 +148,7 @@ impl ApplySubstitution for StructInfo { } } -impl ApplySubstitution for Expression { +impl ApplySubstitution for Expression<'_> { fn apply(&mut self, s: &Substitution) -> Result<(), SubstitutionError> { match self { Expression::Scalar(_, _, type_) => type_.apply(s), diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 35e99e97..480c55e3 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -452,16 +452,16 @@ impl Type { } #[derive(Debug, Clone, PartialEq)] -pub enum StringPart { +pub enum StringPart<'a> { Fixed(String), Interpolation { span: Span, - expr: Box, - format_specifiers: Option, + expr: Box>, + format_specifiers: Option<&'a str>, }, } -impl PrettyPrint for StringPart { +impl PrettyPrint for StringPart<'_> { fn pretty_print(&self) -> Markup { match self { StringPart::Fixed(s) => m::string(escape_numbat_string(s)), @@ -473,7 +473,7 @@ impl PrettyPrint for StringPart { let mut markup = m::operator("{") + expr.pretty_print(); if let Some(format_specifiers) = format_specifiers { - markup += m::text(format_specifiers.clone()); + markup += m::text(format_specifiers.to_string()); } markup += m::operator("}"); @@ -484,23 +484,23 @@ impl PrettyPrint for StringPart { } } -impl PrettyPrint for &Vec { +impl PrettyPrint for &Vec> { fn pretty_print(&self) -> Markup { m::operator("\"") + self.iter().map(|p| p.pretty_print()).sum() + m::operator("\"") } } #[derive(Debug, Clone, PartialEq)] -pub enum Expression { +pub enum Expression<'a> { Scalar(Span, Number, TypeScheme), - Identifier(Span, String, TypeScheme), + Identifier(Span, &'a str, TypeScheme), UnitIdentifier(Span, Prefix, String, String, TypeScheme), - UnaryOperator(Span, UnaryOperator, Box, TypeScheme), + UnaryOperator(Span, UnaryOperator, Box>, TypeScheme), BinaryOperator( Option, BinaryOperator, - Box, - Box, + Box>, + Box>, TypeScheme, ), /// A special binary operator that has a DateTime as one (or both) of the operands @@ -508,32 +508,37 @@ pub enum Expression { Option, BinaryOperator, /// LHS must evaluate to a DateTime - Box, + Box>, /// RHS can evaluate to a DateTime or a quantity of type Time - Box, + Box>, TypeScheme, ), // A 'proper' function call - FunctionCall(Span, Span, String, Vec, TypeScheme), + FunctionCall(Span, Span, &'a str, Vec>, TypeScheme), // A call via a function object - CallableCall(Span, Box, Vec, TypeScheme), + CallableCall(Span, Box>, Vec>, TypeScheme), Boolean(Span, bool), - Condition(Span, Box, Box, Box), - String(Span, Vec), - InstantiateStruct(Span, Vec<(String, Expression)>, StructInfo), + Condition( + Span, + Box>, + Box>, + Box>, + ), + String(Span, Vec>), + InstantiateStruct(Span, Vec<(&'a str, Expression<'a>)>, StructInfo), AccessField( Span, Span, - Box, - String, // field name + Box>, + &'a str, // field name TypeScheme, // struct type TypeScheme, // resulting field type ), - List(Span, Vec, TypeScheme), + List(Span, Vec>, TypeScheme), TypedHole(Span, TypeScheme), } -impl Expression { +impl Expression<'_> { pub fn full_span(&self) -> Span { match self { Expression::Scalar(span, ..) => *span, @@ -573,7 +578,7 @@ impl Expression { pub struct DefineVariable<'a>( pub &'a str, pub Vec>, - pub Expression, + pub Expression<'a>, pub Option, pub TypeScheme, pub Markup, @@ -581,7 +586,7 @@ pub struct DefineVariable<'a>( #[derive(Debug, Clone, PartialEq)] pub enum Statement<'a> { - Expression(Expression), + Expression(Expression<'a>), DefineVariable(DefineVariable<'a>), DefineFunction( &'a str, @@ -594,7 +599,7 @@ pub enum Statement<'a> { Option, // parameter type annotation Markup, // readable parameter type )>, - Option, // function body + Option>, // function body Vec>, // local variables TypeScheme, // function type Option, // return type annotation @@ -609,13 +614,13 @@ pub enum Statement<'a> { ), DefineDerivedUnit( &'a str, - Expression, + Expression<'a>, Vec>, Option, TypeScheme, Markup, ), - ProcedureCall(crate::ast::ProcedureKind, Vec), + ProcedureCall(crate::ast::ProcedureKind, Vec>), DefineStruct(StructInfo), } @@ -751,7 +756,7 @@ impl Statement<'_> { } } -impl Expression { +impl Expression<'_> { pub fn get_type(&self) -> Type { match self { Expression::Scalar(_, _, type_) => type_.unsafe_as_concrete(), @@ -1163,7 +1168,7 @@ fn pretty_print_binop(op: &BinaryOperator, lhs: &Expression, rhs: &Expression) - } (Expression::Scalar(_, s, _), Expression::Identifier(_, name, _type)) => { // Fuse multiplication of a scalar and identifier - pretty_scalar(*s) + m::space() + m::identifier(name.clone()) + pretty_scalar(*s) + m::space() + m::identifier(name.to_string()) } _ => { let add_parens_if_needed = |expr: &Expression| { @@ -1247,13 +1252,13 @@ fn pretty_print_binop(op: &BinaryOperator, lhs: &Expression, rhs: &Expression) - } } -impl PrettyPrint for Expression { +impl PrettyPrint for Expression<'_> { fn pretty_print(&self) -> Markup { use Expression::*; match self { Scalar(_, n, _) => pretty_scalar(*n), - Identifier(_, name, _type) => m::identifier(name.clone()), + Identifier(_, name, _type) => m::identifier(name.to_string()), UnitIdentifier(_, prefix, _name, full_name, _type) => { m::unit(format!("{}{}", prefix.as_string_long(), full_name)) } @@ -1269,7 +1274,7 @@ impl PrettyPrint for Expression { BinaryOperator(_, op, lhs, rhs, _type) => pretty_print_binop(op, lhs, rhs), BinaryOperatorForDate(_, op, lhs, rhs, _type) => pretty_print_binop(op, lhs, rhs), FunctionCall(_, _, name, args, _type) => { - m::identifier(name.clone()) + m::identifier(name.to_string()) + m::operator("(") + itertools::Itertools::intersperse( args.iter().map(|e| e.pretty_print()), @@ -1313,7 +1318,7 @@ impl PrettyPrint for Expression { m::space() + itertools::Itertools::intersperse( exprs.iter().map(|(n, e)| { - m::identifier(n.clone()) + m::identifier(n.to_string()) + m::operator(":") + m::space() + e.pretty_print() @@ -1326,7 +1331,7 @@ impl PrettyPrint for Expression { + m::operator("}") } AccessField(_, _, expr, attr, _, _) => { - expr.pretty_print() + m::operator(".") + m::identifier(attr.clone()) + expr.pretty_print() + m::operator(".") + m::identifier(attr.to_string()) } List(_, elements, _) => { m::operator("[") From 17334441e87698274f174cefbaeeeb96c1ee1542 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 17:20:02 -0400 Subject: [PATCH 31/46] Comment --- numbat/src/ast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numbat/src/ast.rs b/numbat/src/ast.rs index 0e8d2358..f857eeac 100644 --- a/numbat/src/ast.rs +++ b/numbat/src/ast.rs @@ -74,7 +74,7 @@ pub enum StringPart<'a> { pub enum Expression<'a> { Scalar(Span, Number), Identifier(Span, &'a str), - UnitIdentifier(Span, Prefix, String, String), + UnitIdentifier(Span, Prefix, String, String), // can't easily be made &'a str TypedHole(Span), UnaryOperator { op: UnaryOperator, From aa564a3c4e80e01f9f2a7c50a32f71fc0c80a7b4 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Mon, 30 Sep 2024 23:58:42 -0400 Subject: [PATCH 32/46] Improved completion performance Removed non-matching candidates from consideration earlier, obviating need to filter them out Removed clones of candidates that didn't match (before: clone then check; after: check then clone) --- numbat-cli/src/completer.rs | 2 +- numbat/src/lib.rs | 82 ++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/numbat-cli/src/completer.rs b/numbat-cli/src/completer.rs index 43dfd24a..b249bbd7 100644 --- a/numbat-cli/src/completer.rs +++ b/numbat-cli/src/completer.rs @@ -131,7 +131,7 @@ impl Completer for NumbatCompleter { candidates .map(|w| Pair { display: w.to_string(), - replacement: w.to_string(), + replacement: w, }) .collect(), )) diff --git a/numbat/src/lib.rs b/numbat/src/lib.rs index 8e985843..e6e1495a 100644 --- a/numbat/src/lib.rs +++ b/numbat/src/lib.rs @@ -47,6 +47,8 @@ mod unit_registry; pub mod value; mod vm; +use std::borrow::Cow; + use bytecode_interpreter::BytecodeInterpreter; use column_formatter::ColumnFormatter; use currency::ExchangeRatesCache; @@ -200,15 +202,6 @@ impl Context { } pub fn print_environment(&self) -> Markup { - let mut functions: Vec<_> = self.function_names().collect(); - functions.sort(); - let mut dimensions = Vec::from(self.dimension_names()); - dimensions.sort(); - let mut units = Vec::from(self.unit_names()); - units.sort(); - let mut variables: Vec<_> = self.variable_names().collect(); - variables.sort(); - let mut output = m::empty(); output += m::emphasized("List of functions:") + m::nl(); @@ -253,11 +246,11 @@ impl Context { /// Gets completions for the given word_part /// /// If `add_paren` is true, then an opening paren will be added to the end of function names - pub fn get_completions_for<'a>( + pub fn get_completions_for( &self, - word_part: &'a str, + word_part: &str, add_paren: bool, - ) -> impl Iterator + 'a { + ) -> impl Iterator { const COMMON_METRIC_PREFIXES: &[&str] = &[ "pico", "nano", "micro", "milli", "centi", "kilo", "mega", "giga", "tera", ]; @@ -270,53 +263,60 @@ impl Context { }) .collect(); - let mut words: Vec<_> = KEYWORDS.iter().map(|k| k.to_string()).collect(); + let mut words = Vec::new(); + + let mut add_if_valid = |word: Cow<'_, str>| { + if word.starts_with(word_part) { + words.push(word.into_owned()); + } + }; + + for kw in KEYWORDS { + add_if_valid((*kw).into()); + } for (patterns, _) in UNICODE_INPUT { for pattern in *patterns { - words.push(pattern.to_string()); + add_if_valid((*pattern).into()); } } - { - for variable in self.variable_names() { - words.push(variable.clone()); - } + for variable in self.variable_names() { + add_if_valid(variable.into()); + } - for function in self.function_names() { - if add_paren { - words.push(format!("{function}(")); - } else { - words.push(function.to_string()); - } + for mut function in self.function_names() { + if add_paren { + function.push('('); } + add_if_valid(function.into()); + } - for dimension in self.dimension_names() { - words.push(dimension.clone()); - } + for dimension in self.dimension_names() { + add_if_valid(dimension.into()); + } - for (_, (_, meta)) in self.unit_representations() { - for (unit, accepts_prefix) in meta.aliases { - words.push(unit.clone()); - - // Add some of the common long prefixes for units that accept them. - // We do not add all possible prefixes here in order to keep the - // number of completions to a reasonable size. Also, we do not add - // short prefixes for units that accept them, as that leads to lots - // and lots of 2-3 character words. - if accepts_prefix.long && meta.metric_prefixes { - for prefix in &metric_prefixes { - words.push(format!("{prefix}{unit}")); - } + for (_, (_, meta)) in self.unit_representations() { + for (unit, accepts_prefix) in meta.aliases { + // Add some of the common long prefixes for units that accept them. + // We do not add all possible prefixes here in order to keep the + // number of completions to a reasonable size. Also, we do not add + // short prefixes for units that accept them, as that leads to lots + // and lots of 2-3 character words. + if accepts_prefix.long && meta.metric_prefixes { + for prefix in &metric_prefixes { + add_if_valid(format!("{prefix}{unit}").into()); } } + + add_if_valid(unit.into()); } } words.sort(); words.dedup(); - words.into_iter().filter(move |w| w.starts_with(word_part)) + words.into_iter() } pub fn print_info_for_keyword(&mut self, keyword: &str) -> Markup { From d9e0e2c3e4ab6347ed34b3ab9bce4468b75abe75 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 1 Oct 2024 17:41:53 -0400 Subject: [PATCH 33/46] Shrunk `TokenKind` from 16 bytes to 2 by replacing `IntegerWithBase(usize)` with `IntegerWithBase(u8)` (max base is 16, no way do we exceed 255) Removed Box from base-n digit predicate function by switching to function pointers --- numbat/src/tokenizer.rs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/numbat/src/tokenizer.rs b/numbat/src/tokenizer.rs index d5521a6d..eaa8e227 100644 --- a/numbat/src/tokenizer.rs +++ b/numbat/src/tokenizer.rs @@ -22,10 +22,7 @@ pub enum TokenizerErrorKind { ExpectedDigit { character: Option }, #[error("Expected base-{base} digit")] - ExpectedDigitInBase { - base: usize, - character: Option, - }, + ExpectedDigitInBase { base: u8, character: Option }, #[error("Unterminated string")] UnterminatedString, @@ -125,7 +122,7 @@ pub enum TokenKind { // Variable-length tokens Number, - IntegerWithBase(usize), + IntegerWithBase(u8), Identifier, // A normal string without interpolation: `"hello world"` @@ -378,6 +375,18 @@ impl Tokenizer { } fn scan_single_token<'a>(&mut self, input: &'a str) -> Result>> { + fn is_ascii_hex_digit(c: char) -> bool { + c.is_ascii_hexdigit() + } + + fn is_ascii_octal_digit(c: char) -> bool { + ('0'..='7').contains(&c) + } + + fn is_ascii_binary_digit(c: char) -> bool { + c == '0' || c == '1' + } + static KEYWORDS: OnceLock> = OnceLock::new(); let keywords = KEYWORDS.get_or_init(|| { let mut m = HashMap::new(); @@ -463,18 +472,17 @@ impl Tokenizer { .map(|c| c == 'x' || c == 'o' || c == 'b') .unwrap_or(false) => { - let (base, is_digit_in_base): (_, Box bool>) = - match self.peek(input).unwrap() { - 'x' => (16, Box::new(|c| c.is_ascii_hexdigit())), - 'o' => (8, Box::new(|c| ('0'..='7').contains(&c))), - 'b' => (2, Box::new(|c| c == '0' || c == '1')), - _ => unreachable!(), - }; + let (base, is_digit_in_base) = match self.peek(input).unwrap() { + 'x' => (16, is_ascii_hex_digit as fn(char) -> bool), + 'o' => (8, is_ascii_octal_digit as _), + 'b' => (2, is_ascii_binary_digit as _), + _ => unreachable!(), + }; self.advance(input); // skip over the x/o/b - // If the first character is not a digits, that's an error. - if !self.peek(input).map(&is_digit_in_base).unwrap_or(false) { + // If the first character is not a digit, that's an error. + if !self.peek(input).map(is_digit_in_base).unwrap_or(false) { return tokenizer_error( self.current, TokenizerErrorKind::ExpectedDigitInBase { From f87aecc6efc00cfc8acf05bb04fdaa3b1d099172 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mads=20M=C3=B8ller=20Jensen?= Date: Thu, 3 Oct 2024 21:40:30 +0200 Subject: [PATCH 34/46] Make tests use different values on the right side instead of the left of asserts --- examples/tests/mixed_units.nbt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/tests/mixed_units.nbt b/examples/tests/mixed_units.nbt index 781de70e..ae276701 100644 --- a/examples/tests/mixed_units.nbt +++ b/examples/tests/mixed_units.nbt @@ -4,15 +4,15 @@ assert_eq(38° + 53′ + 23″, 38.8897°, 1e-4°) assert_eq(-(77° + 0′ + 32″), -77.0089°, 1e-4°) -assert_eq("{38.88972222° -> DMS}", "{[38°, 53′, 23″]}") +assert_eq("{38.8897° -> DMS}", "{[38°, 53′, 22.92″]}") assert_eq("{-77.0089° -> DMS}", "{[-77°, -0′, -32.04″]}") ## Stuttgart assert_eq(48° + 46′ + 32″, 48.7756°, 1e-4°) assert_eq(9° + 10′ + 58″, 9.1828°, 1e-4°) -assert_eq("{48.775555555° -> DMS}", "{[48°, 46′, 32″]}") -assert_eq("{9.18277777° -> DMS}", "{[9°, 10′, 58″]}") +assert_eq("{48.7756° -> DMS}", "{[48°, 46′, 32.16″]}") +assert_eq("{9.1828° -> DMS}", "{[9°, 10′, 58.08″]}") # Degrees, decimal minutes (DM) From 024f2d3c0a8421cb267939852d7df87efa59dfb0 Mon Sep 17 00:00:00 2001 From: Bzero Date: Fri, 4 Oct 2024 20:52:04 +0200 Subject: [PATCH 35/46] More compact examples --- book/src/list-functions-datetime.md | 209 +++------- book/src/list-functions-lists.md | 321 +++++--------- book/src/list-functions-math.md | 627 +++++++++------------------- book/src/list-functions-other.md | 335 +++++---------- book/src/list-functions-strings.md | 210 +++------- numbat/examples/inspect.rs | 43 +- 6 files changed, 563 insertions(+), 1182 deletions(-) diff --git a/book/src/list-functions-datetime.md b/book/src/list-functions-datetime.md index ebedccc8..8319a503 100644 --- a/book/src/list-functions-datetime.md +++ b/book/src/list-functions-datetime.md @@ -21,36 +21,21 @@ fn datetime(input: String) -> DateTime
Examples -* Run this example - - ```nbt - >>> datetime("2022-07-20T21:52+0200") - - datetime("2022-07-20T21:52+0200") - - = 2022-07-20 19:52:00 UTC [DateTime] - - ``` -* Run this example - - ```nbt - >>> datetime("2022-07-20 21:52 Europe/Berlin") - - datetime("2022-07-20 21:52 Europe/Berlin") - - = 2022-07-20 21:52:00 CEST (UTC +02), Europe/Berlin [DateTime] - - ``` -* Run this example - - ```nbt - >>> datetime("2022/07/20 09:52 PM +0200") - - datetime("2022/07/20 09:52 PM +0200") - - = 2022-07-20 21:52:00 (UTC +02) [DateTime] - - ``` +
>>> datetime("2022-07-20T21:52+0200") + + = 2022-07-20 19:52:00 UTC [DateTime] +
+ +
>>> datetime("2022-07-20 21:52 Europe/Berlin") + + = 2022-07-20 21:52:00 CEST (UTC +02), Europe/Berlin [DateTime] +
+ +
>>> datetime("2022/07/20 09:52 PM +0200") + + = 2022-07-20 21:52:00 (UTC +02) [DateTime] +
+
### `format_datetime` @@ -63,16 +48,11 @@ fn format_datetime(format: String, input: DateTime) -> String
Examples -* Run this example +
>>> format_datetime("This is a date in %B in the year %Y.", datetime("2022-07-20 21:52 +0200")) + + = "This is a date in July in the year 2022." [String] +
- ```nbt - >>> format_datetime("This is a date in %B in the year %Y.", datetime("2022-07-20 21:52 +0200")) - - format_datetime("This is a date in %B in the year %Y.", datetime("2022-07-20 21:52 +0200")) - - = "This is a date in July in the year 2022." [String] - - ```
### `get_local_timezone` @@ -85,16 +65,11 @@ fn get_local_timezone() -> String
Examples -* Run this example +
>>> get_local_timezone() + + = "UTC" [String] +
- ```nbt - >>> get_local_timezone() - - get_local_timezone() - - = "UTC" [String] - - ```
### `tz` @@ -107,26 +82,16 @@ fn tz(tz: String) -> Fn[(DateTime) -> DateTime]
Examples -* Run this example - - ```nbt - >>> datetime("2022-07-20 21:52 +0200") -> tz("Europe/Amsterdam") - - tz("Europe/Amsterdam")(datetime("2022-07-20 21:52 +0200")) - - = 2022-07-20 21:52:00 CEST (UTC +02), Europe/Amsterdam [DateTime] - - ``` -* Run this example - - ```nbt - >>> datetime("2022-07-20 21:52 +0200") -> tz("Asia/Taipei") - - tz("Asia/Taipei")(datetime("2022-07-20 21:52 +0200")) - - = 2022-07-21 03:52:00 CST (UTC +08), Asia/Taipei [DateTime] - - ``` +
>>> datetime("2022-07-20 21:52 +0200") -> tz("Europe/Amsterdam") + + = 2022-07-20 21:52:00 CEST (UTC +02), Europe/Amsterdam [DateTime] +
+ +
>>> datetime("2022-07-20 21:52 +0200") -> tz("Asia/Taipei") + + = 2022-07-21 03:52:00 CST (UTC +08), Asia/Taipei [DateTime] +
+
### `unixtime` @@ -139,16 +104,11 @@ fn unixtime(input: DateTime) -> Scalar
Examples -* Run this example +
>>> datetime("2022-07-20 21:52 +0200") -> unixtime + + = 1_658_346_720 +
- ```nbt - >>> datetime("2022-07-20 21:52 +0200") -> unixtime - - unixtime(datetime("2022-07-20 21:52 +0200")) - - = 1_658_346_720 - - ```
### `from_unixtime` @@ -161,16 +121,11 @@ fn from_unixtime(input: Scalar) -> DateTime
Examples -* Run this example +
>>> from_unixtime(2^31) + + = 2038-01-19 03:14:08 UTC [DateTime] +
- ```nbt - >>> from_unixtime(2^31) - - from_unixtime(2^31) - - = 2038-01-19 03:14:08 UTC [DateTime] - - ```
### `today` @@ -190,16 +145,11 @@ fn date(input: String) -> DateTime
Examples -* Run this example +
>>> date("2022-07-20") + + = 2022-07-20 00:00:00 UTC [DateTime] +
- ```nbt - >>> date("2022-07-20") - - date("2022-07-20") - - = 2022-07-20 00:00:00 UTC [DateTime] - - ```
### `time` @@ -219,16 +169,11 @@ fn calendar_add(dt: DateTime, span: Time) -> DateTime
Examples -* Run this example +
>>> calendar_add(datetime("2022-07-20 21:52 +0200"), 2 years) + + = 2024-07-20 21:52:00 (UTC +02) [DateTime] +
- ```nbt - >>> calendar_add(datetime("2022-07-20 21:52 +0200"), 2 years) - - calendar_add(datetime("2022-07-20 21:52 +0200"), 2 year) - - = 2024-07-20 21:52:00 (UTC +02) [DateTime] - - ```
### `calendar_sub` @@ -241,16 +186,11 @@ fn calendar_sub(dt: DateTime, span: Time) -> DateTime
Examples -* Run this example +
>>> calendar_sub(datetime("2022-07-20 21:52 +0200"), 3 years) + + = 2019-07-20 21:52:00 (UTC +02) [DateTime] +
- ```nbt - >>> calendar_sub(datetime("2022-07-20 21:52 +0200"), 3 years) - - calendar_sub(datetime("2022-07-20 21:52 +0200"), 3 year) - - = 2019-07-20 21:52:00 (UTC +02) [DateTime] - - ```
### `weekday` @@ -263,16 +203,11 @@ fn weekday(dt: DateTime) -> String
Examples -* Run this example +
>>> weekday(datetime("2022-07-20 21:52 +0200")) + + = "Wednesday" [String] +
- ```nbt - >>> weekday(datetime("2022-07-20 21:52 +0200")) - - weekday(datetime("2022-07-20 21:52 +0200")) - - = "Wednesday" [String] - - ```
### `julian_date` (Julian date) @@ -286,16 +221,11 @@ fn julian_date(dt: DateTime) -> Time
Examples -* Run this example +
>>> julian_date(datetime("2022-07-20 21:52 +0200")) + + = 2.45978e+6 day [Time] +
- ```nbt - >>> julian_date(datetime("2022-07-20 21:52 +0200")) - - julian_date(datetime("2022-07-20 21:52 +0200")) - - = 2.45978e+6 day [Time] - - ```
### `human` (Human-readable time duration) @@ -309,16 +239,11 @@ fn human(time: Time) -> String
Examples -* How long is a microcentury? - - Run this example - ```nbt - >>> century/1e6 -> human - - human(century / 1_000_000) - - = "52 minutes + 35.692505184 seconds" [String] - - ``` +How long is a microcentury? +
>>> century/1e6 -> human + + = "52 minutes + 35.692505184 seconds" [String] +
+
diff --git a/book/src/list-functions-lists.md b/book/src/list-functions-lists.md index d4416e93..c3919ea0 100644 --- a/book/src/list-functions-lists.md +++ b/book/src/list-functions-lists.md @@ -12,16 +12,11 @@ fn len(xs: List) -> Scalar
Examples -* Run this example - - ```nbt - >>> len([3, 2, 1]) - - len([3, 2, 1]) - - = 3 - - ``` +
>>> len([3, 2, 1]) + + = 3 +
+
### `head` @@ -34,16 +29,11 @@ fn head(xs: List) -> A
Examples -* Run this example +
>>> head([3, 2, 1]) + + = 3 +
- ```nbt - >>> head([3, 2, 1]) - - head([3, 2, 1]) - - = 3 - - ```
### `tail` @@ -56,16 +46,11 @@ fn tail(xs: List) -> List
Examples -* Run this example +
>>> tail([3, 2, 1]) + + = [2, 1] [List] +
- ```nbt - >>> tail([3, 2, 1]) - - tail([3, 2, 1]) - - = [2, 1] [List] - - ```
### `cons` @@ -78,16 +63,11 @@ fn cons(x: A, xs: List) -> List
Examples -* Run this example +
>>> cons(77, [3, 2, 1]) + + = [77, 3, 2, 1] [List] +
- ```nbt - >>> cons(77, [3, 2, 1]) - - cons(77, [3, 2, 1]) - - = [77, 3, 2, 1] [List] - - ```
### `cons_end` @@ -100,16 +80,11 @@ fn cons_end(x: A, xs: List) -> List
Examples -* Run this example +
>>> cons_end(77, [3, 2, 1]) + + = [3, 2, 1, 77] [List] +
- ```nbt - >>> cons_end(77, [3, 2, 1]) - - cons_end(77, [3, 2, 1]) - - = [3, 2, 1, 77] [List] - - ```
### `is_empty` @@ -122,26 +97,16 @@ fn is_empty(xs: List) -> Bool
Examples -* Run this example - - ```nbt - >>> is_empty([3, 2, 1]) - - is_empty([3, 2, 1]) - - = false [Bool] - - ``` -* Run this example - - ```nbt - >>> is_empty([]) - - is_empty([]) - - = true [Bool] - - ``` +
>>> is_empty([3, 2, 1]) + + = false [Bool] +
+ +
>>> is_empty([]) + + = true [Bool] +
+
### `concat` @@ -154,16 +119,11 @@ fn concat(xs1: List, xs2: List) -> List
Examples -* Run this example +
>>> concat([3, 2, 1], [10, 11]) + + = [3, 2, 1, 10, 11] [List] +
- ```nbt - >>> concat([3, 2, 1], [10, 11]) - - concat([3, 2, 1], [10, 11]) - - = [3, 2, 1, 10, 11] [List] - - ```
### `take` @@ -176,16 +136,11 @@ fn take(n: Scalar, xs: List) -> List
Examples -* Run this example +
>>> take(2, [3, 2, 1, 0]) + + = [3, 2] [List] +
- ```nbt - >>> take(2, [3, 2, 1, 0]) - - take(2, [3, 2, 1, 0]) - - = [3, 2] [List] - - ```
### `drop` @@ -198,16 +153,11 @@ fn drop(n: Scalar, xs: List) -> List
Examples -* Run this example +
>>> drop(2, [3, 2, 1, 0]) + + = [1, 0] [List] +
- ```nbt - >>> drop(2, [3, 2, 1, 0]) - - drop(2, [3, 2, 1, 0]) - - = [1, 0] [List] - - ```
### `element_at` @@ -220,16 +170,11 @@ fn element_at(i: Scalar, xs: List) -> A
Examples -* Run this example +
>>> element_at(2, [3, 2, 1, 0]) + + = 1 +
- ```nbt - >>> element_at(2, [3, 2, 1, 0]) - - element_at(2, [3, 2, 1, 0]) - - = 1 - - ```
### `range` @@ -242,16 +187,11 @@ fn range(start: Scalar, end: Scalar) -> List
Examples -* Run this example +
>>> range(2, 12) + + = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] [List] +
- ```nbt - >>> range(2, 12) - - range(2, 12) - - = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] [List] - - ```
### `reverse` @@ -264,16 +204,11 @@ fn reverse(xs: List) -> List
Examples -* Run this example +
>>> reverse([3, 2, 1]) + + = [1, 2, 3] [List] +
- ```nbt - >>> reverse([3, 2, 1]) - - reverse([3, 2, 1]) - - = [1, 2, 3] [List] - - ```
### `map` @@ -286,17 +221,12 @@ fn map(f: Fn[(A) -> B], xs: List) -> List
Examples -* Square all elements of a list. - - Run this example - ```nbt - >>> map(sqr, [3, 2, 1]) - - map(sqr, [3, 2, 1]) - - = [9, 4, 1] [List] - - ``` +Square all elements of a list. +
>>> map(sqr, [3, 2, 1]) + + = [9, 4, 1] [List] +
+
### `filter` @@ -309,16 +239,11 @@ fn filter(p: Fn[(A) -> Bool], xs: List) -> List
Examples -* Run this example +
>>> filter(is_finite, [0, 1e10, NaN, -inf]) + + = [0, 10_000_000_000] [List] +
- ```nbt - >>> filter(is_finite, [0, 1e10, NaN, -inf]) - - filter(is_finite, [0, 10_000_000_000, NaN, -inf]) - - = [0, 10_000_000_000] [List] - - ```
### `foldl` @@ -331,17 +256,12 @@ fn foldl(f: Fn[(A, B) -> A], acc: A, xs: List) -> A
Examples -* Join a list of strings by folding. - - Run this example - ```nbt - >>> foldl(str_append, "", ["Num", "bat", "!"]) - - foldl(str_append, "", ["Num", "bat", "!"]) - - = "Numbat!" [String] - - ``` +Join a list of strings by folding. +
>>> foldl(str_append, "", ["Num", "bat", "!"]) + + = "Numbat!" [String] +
+
### `sort_by_key` @@ -354,20 +274,13 @@ fn sort_by_key(key: Fn[(A) -> D], xs: List) -> List
Examples -* Sort by last digit. - - Run this example - ```nbt - >>> fn last_digit(x) = mod(x, 10) - sort_by_key(last_digit, [701, 313, 9999, 4]) - - fn last_digit(x: Scalar) -> Scalar = mod(x, 10) - - sort_by_key(last_digit, [701, 313, 9999, 4]) - - = [701, 313, 4, 9999] [List] - - ``` +Sort by last digit. +
>>> fn last_digit(x) = mod(x, 10) +sort_by_key(last_digit, [701, 313, 9999, 4]) + + = [701, 313, 4, 9999] [List] +
+
### `sort` @@ -380,16 +293,11 @@ fn sort(xs: List) -> List
Examples -* Run this example +
>>> sort([3, 2, 7, 8, -4, 0, -5]) + + = [-5, -4, 0, 2, 3, 7, 8] [List] +
- ```nbt - >>> sort([3, 2, 7, 8, -4, 0, -5]) - - sort([3, 2, 7, 8, -4, 0, -5]) - - = [-5, -4, 0, 2, 3, 7, 8] [List] - - ```
### `intersperse` @@ -402,16 +310,11 @@ fn intersperse(sep: A, xs: List) -> List
Examples -* Run this example +
>>> intersperse(0, [1, 1, 1, 1]) + + = [1, 0, 1, 0, 1, 0, 1] [List] +
- ```nbt - >>> intersperse(0, [1, 1, 1, 1]) - - intersperse(0, [1, 1, 1, 1]) - - = [1, 0, 1, 0, 1, 0, 1] [List] - - ```
### `sum` @@ -424,16 +327,11 @@ fn sum(xs: List) -> D
Examples -* Run this example +
>>> sum([3 m, 200 cm, 1000 mm]) + + = 6 m [Length] +
- ```nbt - >>> sum([3 m, 200 cm, 1000 mm]) - - sum([3 metre, 200 centimetre, 1000 millimetre]) - - = 6 m [Length] - - ```
### `linspace` @@ -446,16 +344,11 @@ fn linspace(start: D, end: D, n_steps: Scalar) -> List
Examples -* Run this example +
>>> linspace(-5 m, 5 m, 11) + + = [-5 m, -4 m, -3 m, -2 m, -1 m, 0 m, 1 m, 2 m, 3 m, 4 m, 5 m] [List] +
- ```nbt - >>> linspace(-5 m, 5 m, 11) - - linspace(-(5 metre), 5 metre, 11) - - = [-5 m, -4 m, -3 m, -2 m, -1 m, 0 m, 1 m, 2 m, 3 m, 4 m, 5 m] [List] - - ```
### `join` @@ -468,16 +361,11 @@ fn join(xs: List, sep: String) -> String
Examples -* Run this example +
>>> join(["snake", "case"], "_") + + = "snake_case" [String] +
- ```nbt - >>> join(["snake", "case"], "_") - - join(["snake", "case"], "_") - - = "snake_case" [String] - - ```
### `split` @@ -490,15 +378,10 @@ fn split(input: String, separator: String) -> List
Examples -* Run this example +
>>> split("Numbat is a statically typed programming language.", " ") + + = ["Numbat", "is", "a", "statically", "typed", "programming", "language."] [List] +
- ```nbt - >>> split("Numbat is a statically typed programming language.", " ") - - split("Numbat is a statically typed programming language.", " ") - - = ["Numbat", "is", "a", "statically", "typed", "programming", "language."] [List] - - ```
diff --git a/book/src/list-functions-math.md b/book/src/list-functions-math.md index 6b224d44..62464657 100644 --- a/book/src/list-functions-math.md +++ b/book/src/list-functions-math.md @@ -16,16 +16,11 @@ fn id(x: A) -> A
Examples -* Run this example - - ```nbt - >>> id(8 kg) - - id(8 kilogram) - - = 8 kg [Mass] - - ``` +
>>> id(8 kg) + + = 8 kg [Mass] +
+
### `abs` (Absolute value) @@ -39,16 +34,11 @@ fn abs(x: T) -> T
Examples -* Run this example +
>>> abs(-22.2 m) + + = 22.2 m [Length] +
- ```nbt - >>> abs(-22.2 m) - - abs(-(22.2 metre)) - - = 22.2 m [Length] - - ```
### `sqrt` (Square root) @@ -62,16 +52,11 @@ fn sqrt(x: D^2) -> D
Examples -* Run this example +
>>> sqrt(4 are) -> m + + = 20 m [Length] +
- ```nbt - >>> sqrt(4 are) -> m - - sqrt(4 are) ➞ metre - - = 20 m [Length] - - ```
### `cbrt` (Cube root) @@ -85,16 +70,11 @@ fn cbrt(x: D^3) -> D
Examples -* Run this example +
>>> cbrt(8 L) -> cm + + = 20.0 cm [Length] +
- ```nbt - >>> cbrt(8 L) -> cm - - cbrt(8 litre) ➞ centimetre - - = 20.0 cm [Length] - - ```
### `sqr` (Square function) @@ -107,16 +87,11 @@ fn sqr(x: D) -> D^2
Examples -* Run this example +
>>> sqr(7) + + = 49 +
- ```nbt - >>> sqr(7) - - sqr(7) - - = 49 - - ```
### `round` (Rounding) @@ -130,26 +105,16 @@ fn round(x: Scalar) -> Scalar
Examples -* Run this example - - ```nbt - >>> round(5.5) - - round(5.5) - - = 6 - - ``` -* Run this example - - ```nbt - >>> round(-5.5) - - round(-5.5) - - = -6 - - ``` +
>>> round(5.5) + + = 6 +
+ +
>>> round(-5.5) + + = -6 +
+
### `round_in` (Rounding) @@ -162,28 +127,18 @@ fn round_in(base: D, value: D) -> D
Examples -* Round in meters. - - Run this example - ```nbt - >>> round_in(m, 5.3 m) - - round_in(metre, 5.3 metre) - - = 5 m [Length] - - ``` -* Round in centimeters. - - Run this example - ```nbt - >>> round_in(cm, 5.3 m) - - round_in(centimetre, 5.3 metre) - - = 530 cm [Length] - - ``` +Round in meters. +
>>> round_in(m, 5.3 m) + + = 5 m [Length] +
+ +Round in centimeters. +
>>> round_in(cm, 5.3 m) + + = 530 cm [Length] +
+
### `floor` (Floor function) @@ -197,16 +152,11 @@ fn floor(x: Scalar) -> Scalar
Examples -* Run this example +
>>> floor(5.5) + + = 5 +
- ```nbt - >>> floor(5.5) - - floor(5.5) - - = 5 - - ```
### `floor_in` (Floor function) @@ -219,28 +169,18 @@ fn floor_in(base: D, value: D) -> D
Examples -* Floor in meters. - - Run this example - ```nbt - >>> floor_in(m, 5.7 m) - - floor_in(metre, 5.7 metre) - - = 5 m [Length] - - ``` -* Floor in centimeters. - - Run this example - ```nbt - >>> floor_in(cm, 5.7 m) - - floor_in(centimetre, 5.7 metre) - - = 570 cm [Length] - - ``` +Floor in meters. +
>>> floor_in(m, 5.7 m) + + = 5 m [Length] +
+ +Floor in centimeters. +
>>> floor_in(cm, 5.7 m) + + = 570 cm [Length] +
+
### `ceil` (Ceil function) @@ -254,16 +194,11 @@ fn ceil(x: Scalar) -> Scalar
Examples -* Run this example +
>>> ceil(5.5) + + = 6 +
- ```nbt - >>> ceil(5.5) - - ceil(5.5) - - = 6 - - ```
### `ceil_in` (Ceil function) @@ -276,28 +211,18 @@ fn ceil_in(base: D, value: D) -> D
Examples -* Ceil in meters. - - Run this example - ```nbt - >>> ceil_in(m, 5.3 m) - - ceil_in(metre, 5.3 metre) - - = 6 m [Length] - - ``` -* Ceil in centimeters. - - Run this example - ```nbt - >>> ceil_in(cm, 5.3 m) - - ceil_in(centimetre, 5.3 metre) - - = 530 cm [Length] - - ``` +Ceil in meters. +
>>> ceil_in(m, 5.3 m) + + = 6 m [Length] +
+ +Ceil in centimeters. +
>>> ceil_in(cm, 5.3 m) + + = 530 cm [Length] +
+
### `trunc` (Truncation) @@ -311,26 +236,16 @@ fn trunc(x: Scalar) -> Scalar
Examples -* Run this example - - ```nbt - >>> trunc(5.5) - - trunc(5.5) - - = 5 - - ``` -* Run this example - - ```nbt - >>> trunc(-5.5) - - trunc(-5.5) - - = -5 - - ``` +
>>> trunc(5.5) + + = 5 +
+ +
>>> trunc(-5.5) + + = -5 +
+
### `trunc_in` (Truncation) @@ -343,28 +258,18 @@ fn trunc_in(base: D, value: D) -> D
Examples -* Truncate in meters. - - Run this example - ```nbt - >>> trunc_in(m, 5.7 m) - - trunc_in(metre, 5.7 metre) - - = 5 m [Length] - - ``` -* Truncate in centimeters. - - Run this example - ```nbt - >>> trunc_in(cm, 5.7 m) - - trunc_in(centimetre, 5.7 metre) - - = 570 cm [Length] - - ``` +Truncate in meters. +
>>> trunc_in(m, 5.7 m) + + = 5 m [Length] +
+ +Truncate in centimeters. +
>>> trunc_in(cm, 5.7 m) + + = 570 cm [Length] +
+
### `mod` (Modulo) @@ -378,16 +283,11 @@ fn mod(a: T, b: T) -> T
Examples -* Run this example +
>>> mod(27, 5) + + = 2 +
- ```nbt - >>> mod(27, 5) - - mod(27, 5) - - = 2 - - ```
## Transcendental functions @@ -405,16 +305,11 @@ fn exp(x: Scalar) -> Scalar
Examples -* Run this example +
>>> exp(4) + + = 54.5982 +
- ```nbt - >>> exp(4) - - exp(4) - - = 54.5982 - - ```
### `ln` (Natural logarithm) @@ -428,16 +323,11 @@ fn ln(x: Scalar) -> Scalar
Examples -* Run this example +
>>> ln(20) + + = 2.99573 +
- ```nbt - >>> ln(20) - - ln(20) - - = 2.99573 - - ```
### `log` (Natural logarithm) @@ -451,16 +341,11 @@ fn log(x: Scalar) -> Scalar
Examples -* Run this example +
>>> log(20) + + = 2.99573 +
- ```nbt - >>> log(20) - - log(20) - - = 2.99573 - - ```
### `log10` (Common logarithm) @@ -474,16 +359,11 @@ fn log10(x: Scalar) -> Scalar
Examples -* Run this example +
>>> log10(100) + + = 2 +
- ```nbt - >>> log10(100) - - log10(100) - - = 2 - - ```
### `log2` (Binary logarithm) @@ -497,16 +377,11 @@ fn log2(x: Scalar) -> Scalar
Examples -* Run this example +
>>> log2(256) + + = 8 +
- ```nbt - >>> log2(256) - - log2(256) - - = 8 - - ```
### `gamma` (Gamma function) @@ -626,16 +501,11 @@ fn maximum(xs: List) -> D
Examples -* Run this example +
>>> maximum([30 cm, 2 m]) + + = 2 m [Length] +
- ```nbt - >>> maximum([30 cm, 2 m]) - - maximum([30 centimetre, 2 metre]) - - = 2 m [Length] - - ```
### `minimum` (Minimum) @@ -648,16 +518,11 @@ fn minimum(xs: List) -> D
Examples -* Run this example +
>>> minimum([30 cm, 2 m]) + + = 30 cm [Length] +
- ```nbt - >>> minimum([30 cm, 2 m]) - - minimum([30 centimetre, 2 metre]) - - = 30 cm [Length] - - ```
### `mean` (Arithmetic mean) @@ -671,16 +536,11 @@ fn mean(xs: List) -> D
Examples -* Run this example +
>>> mean([1 m, 2 m, 300 cm]) + + = 2 m [Length] +
- ```nbt - >>> mean([1 m, 2 m, 300 cm]) - - mean([1 metre, 2 metre, 300 centimetre]) - - = 2 m [Length] - - ```
### `variance` (Variance) @@ -694,16 +554,11 @@ fn variance(xs: List) -> D^2
Examples -* Run this example +
>>> variance([1 m, 2 m, 300 cm]) + + = 0.666667 m² [Area] +
- ```nbt - >>> variance([1 m, 2 m, 300 cm]) - - variance([1 metre, 2 metre, 300 centimetre]) - - = 0.666667 m² [Area] - - ```
### `stdev` (Standard deviation) @@ -717,16 +572,11 @@ fn stdev(xs: List) -> D
Examples -* Run this example +
>>> stdev([1 m, 2 m, 300 cm]) + + = 0.816497 m [Length] +
- ```nbt - >>> stdev([1 m, 2 m, 300 cm]) - - stdev([1 metre, 2 metre, 300 centimetre]) - - = 0.816497 m [Length] - - ```
### `median` (Median) @@ -740,16 +590,11 @@ fn median(xs: List) -> D
Examples -* Run this example +
>>> median([1 m, 2 m, 400 cm]) + + = 2 m [Length] +
- ```nbt - >>> median([1 m, 2 m, 400 cm]) - - median([1 metre, 2 metre, 400 centimetre]) - - = 2 m [Length] - - ```
## Random sampling, distributions @@ -859,16 +704,11 @@ fn gcd(a: Scalar, b: Scalar) -> Scalar
Examples -* Run this example +
>>> gcd(60, 42) + + = 6 +
- ```nbt - >>> gcd(60, 42) - - gcd(60, 42) - - = 6 - - ```
### `lcm` (Least common multiple) @@ -882,16 +722,11 @@ fn lcm(a: Scalar, b: Scalar) -> Scalar
Examples -* Run this example +
>>> lcm(14, 4) + + = 28 +
- ```nbt - >>> lcm(14, 4) - - lcm(14, 4) - - = 28 - - ```
## Numerical methods @@ -909,37 +744,21 @@ fn diff(f: Fn[(X) -> Y], x: X) -> Y / X
Examples -* Compute the derivative of \\( f(x) = x² -x -1 \\) at \\( x=1 \\). - - Run this example - ```nbt - >>> fn polynomial(x) = x² - x - 1 - diff(polynomial, 1) - - fn polynomial(x: Scalar) -> Scalar = (x² - x) - 1 - - diff(polynomial, 1) - - = 1.0 - - ``` -* Compute the free fall velocity after \\( t=2 s \\). - - Run this example - ```nbt - >>> fn distance(t) = 0.5 g0 t² - fn velocity(t) = diff(distance, t) - velocity(2 s) - - fn distance(t: A) -> A² × Length / Time² = 0.5 g0 × t² - - fn velocity(t: A) -> A × Length / Time² = diff(distance, t) - - velocity(2 second) - - = 19.6133 m/s [Velocity] - - ``` +Compute the derivative of \\( f(x) = x² -x -1 \\) at \\( x=1 \\). +
>>> fn polynomial(x) = x² - x - 1 +diff(polynomial, 1) + + = 1.0 +
+ +Compute the free fall velocity after \\( t=2 s \\). +
>>> fn distance(t) = 0.5 g0 t² +fn velocity(t) = diff(distance, t) +velocity(2 s) + + = 19.6133 m/s [Velocity] +
+
### `root_bisect` (Bisection method) @@ -953,20 +772,13 @@ fn root_bisect(f: Fn[(X) -> Y], x1: X, x2: X, x_tol: X, y_tol: Y
Examples -* Find the root of \\( f(x) = x² +x -2 \\) in the interval \\( [0, 100] \\). - - Run this example - ```nbt - >>> fn f(x) = x² +x -2 - root_bisect(f, 0, 100, 0.01, 0.01) - - fn f(x: Scalar) -> Scalar = (x² + x) - 2 - - root_bisect(f, 0, 100, 0.01, 0.01) - - = 1.00098 - - ``` +Find the root of \\( f(x) = x² +x -2 \\) in the interval \\( [0, 100] \\). +
>>> fn f(x) = x² +x -2 +root_bisect(f, 0, 100, 0.01, 0.01) + + = 1.00098 +
+
### `root_newton` (Newton's method) @@ -980,23 +792,14 @@ fn root_newton(f: Fn[(X) -> Y], f_prime: Fn[(X) -> Y / X], x0: X
Examples -* Find a root of \\( f(x) = x² -3x +2 \\) using Newton's method. - - Run this example - ```nbt - >>> fn f(x) = x² -3x +2 - fn f_prime(x) = 2x -3 - root_newton(f, f_prime, 0 , 0.01) - - fn f(x: Scalar) -> Scalar = (x² - 3 x) + 2 - - fn f_prime(x: Scalar) -> Scalar = 2 x - 3 - - root_newton(f, f_prime, 0, 0.01) - - = 0.996078 - - ``` +Find a root of \\( f(x) = x² -3x +2 \\) using Newton's method. +
>>> fn f(x) = x² -3x +2 +fn f_prime(x) = 2x -3 +root_newton(f, f_prime, 0 , 0.01) + + = 0.996078 +
+
### `fixed_point` (Fixed-point iteration) @@ -1010,20 +813,13 @@ fn fixed_point(f: Fn[(X) -> X], x0: X, ε: X) -> X
Examples -* Compute the fixed poin of \\( f(x) = x/2 -1 \\). - - Run this example - ```nbt - >>> fn function(x) = x/2 - 1 - fixed_point(function, 0, 0.01) - - fn function(x: Scalar) -> Scalar = (x / 2) - 1 - - fixed_point(function, 0, 0.01) - - = -1.99219 - - ``` +Compute the fixed poin of \\( f(x) = x/2 -1 \\). +
>>> fn function(x) = x/2 - 1 +fixed_point(function, 0, 0.01) + + = -1.99219 +
+
## Geometry @@ -1040,16 +836,11 @@ fn hypot2(x: T, y: T) -> T
Examples -* Run this example +
>>> hypot2(3 m, 4 m) + + = 5 m [Length] +
- ```nbt - >>> hypot2(3 m, 4 m) - - hypot2(3 metre, 4 metre) - - = 5 m [Length] - - ```
### `hypot3` @@ -1062,16 +853,11 @@ fn hypot3(x: T, y: T, z: T) -> T
Examples -* Run this example +
>>> hypot3(8, 9, 12) + + = 17 +
- ```nbt - >>> hypot3(8, 9, 12) - - hypot3(8, 9, 12) - - = 17 - - ```
### `circle_area` @@ -1117,17 +903,12 @@ fn quadratic_equation(a: A, b: B, c: B^2 / A) -> List
Examples -* Solve the equation \\( 2x² -x -1 = 0 \\) - - Run this example - ```nbt - >>> quadratic_equation(2, -1, -1) - - quadratic_equation(2, -1, -1) - - = [1, -0.5] [List] - - ``` +Solve the equation \\( 2x² -x -1 = 0 \\) +
>>> quadratic_equation(2, -1, -1) + + = [1, -0.5] [List] +
+
## Trigonometry (extra) diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 804d93c4..71ba20c6 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -28,26 +28,16 @@ fn is_nan(n: T) -> Bool
Examples -* Run this example - - ```nbt - >>> is_nan(37) - - is_nan(37) - - = false [Bool] - - ``` -* Run this example - - ```nbt - >>> is_nan(NaN) - - is_nan(NaN) - - = true [Bool] - - ``` +
>>> is_nan(37) + + = false [Bool] +
+ +
>>> is_nan(NaN) + + = true [Bool] +
+
### `is_infinite` @@ -61,26 +51,16 @@ fn is_infinite(n: T) -> Bool
Examples -* Run this example - - ```nbt - >>> is_infinite(37) - - is_infinite(37) - - = false [Bool] - - ``` -* Run this example - - ```nbt - >>> is_infinite(-inf) - - is_infinite(-inf) - - = true [Bool] - - ``` +
>>> is_infinite(37) + + = false [Bool] +
+ +
>>> is_infinite(-inf) + + = true [Bool] +
+
### `is_finite` @@ -93,26 +73,16 @@ fn is_finite(n: T) -> Bool
Examples -* Run this example - - ```nbt - >>> is_finite(37) - - is_finite(37) - - = true [Bool] - - ``` -* Run this example - - ```nbt - >>> is_finite(-inf) - - is_finite(-inf) - - = false [Bool] - - ``` +
>>> is_finite(37) + + = true [Bool] +
+ +
>>> is_finite(-inf) + + = false [Bool] +
+
## Quantities @@ -129,16 +99,11 @@ fn unit_of(x: T) -> T
Examples -* Run this example +
>>> unit_of(20 km/h) + + = 1 km/h [Velocity] +
- ```nbt - >>> unit_of(20 km/h) - - unit_of(20 kilometre / hour) - - = 1 km/h [Velocity] - - ```
### `value_of` @@ -151,16 +116,11 @@ fn value_of(x: T) -> Scalar
Examples -* Run this example +
>>> value_of(20 km/h) + + = 20 +
- ```nbt - >>> value_of(20 km/h) - - value_of(20 kilometre / hour) - - = 20 - - ```
## Chemical elements @@ -177,28 +137,18 @@ fn element(pattern: String) -> ChemicalElement
Examples -* Get the entire element struct for hydrogen. - - Run this example - ```nbt - >>> element("H") - - element("H") - - = ChemicalElement { symbol: "H", name: "Hydrogen", atomic_number: 1, group: 1, group_name: "Alkali metals", period: 1, melting_point: 13.99 K, boiling_point: 20.271 K, density: 0.00008988 g/cm³, electron_affinity: 0.754 eV, ionization_energy: 13.598 eV, vaporization_heat: 0.904 kJ/mol } [ChemicalElement] - - ``` -* Get the ionization energy of hydrogen. - - Run this example - ```nbt - >>> element("hydrogen").ionization_energy - - element("hydrogen").ionization_energy - - = 13.598 eV [Energy or Torque] - - ``` +Get the entire element struct for hydrogen. +
>>> element("H") + + = ChemicalElement { symbol: "H", name: "Hydrogen", atomic_number: 1, group: 1, group_name: "Alkali metals", period: 1, melting_point: 13.99 K, boiling_point: 20.271 K, density: 0.00008988 g/cm³, electron_affinity: 0.754 eV, ionization_energy: 13.598 eV, vaporization_heat: 0.904 kJ/mol } [ChemicalElement] +
+ +Get the ionization energy of hydrogen. +
>>> element("hydrogen").ionization_energy + + = 13.598 eV [Energy or Torque] +
+
## Mixed unit conversion @@ -216,16 +166,11 @@ fn DMS(alpha: Angle) -> String
Examples -* Run this example +
>>> 46.5858° -> DMS + + = "46° 35′ 9″" [String] +
- ```nbt - >>> 46.5858° -> DMS - - DMS(46.5858 degree) - - = "46° 35′ 9″" [String] - - ```
### `DM` (Degrees, decimal minutes) @@ -239,16 +184,11 @@ fn DM(alpha: Angle) -> String
Examples -* Run this example +
>>> 46.5858° -> DM + + = "46° 35.148′" [String] +
- ```nbt - >>> 46.5858° -> DM - - DM(46.5858 degree) - - = "46° 35.148′" [String] - - ```
### `feet_and_inches` (Feet and inches) @@ -262,16 +202,11 @@ fn feet_and_inches(length: Length) -> String
Examples -* Run this example +
>>> 180 cm -> feet_and_inches + + = "5 ft 10.8661 in" [String] +
- ```nbt - >>> 180 cm -> feet_and_inches - - feet_and_inches(180 centimetre) - - = "5 ft 10.8661 in" [String] - - ```
### `pounds_and_ounces` (Pounds and ounces) @@ -285,16 +220,11 @@ fn pounds_and_ounces(mass: Mass) -> String
Examples -* Run this example +
>>> 1 kg -> pounds_and_ounces + + = "2 lb 3.27396 oz" [String] +
- ```nbt - >>> 1 kg -> pounds_and_ounces - - pounds_and_ounces(1 kilogram) - - = "2 lb 3.27396 oz" [String] - - ```
## Temperature conversion @@ -312,17 +242,12 @@ fn from_celsius(t_celsius: Scalar) -> Temperature
Examples -* 300 °C in Kelvin. - - Run this example - ```nbt - >>> from_celsius(300) - - from_celsius(300) - - = 573.15 K [Temperature] - - ``` +300 °C in Kelvin. +
>>> from_celsius(300) + + = 573.15 K [Temperature] +
+
### `celsius` @@ -336,17 +261,12 @@ fn celsius(t_kelvin: Temperature) -> Scalar
Examples -* 300 K in degree Celsius. - - Run this example - ```nbt - >>> 300K -> celsius - - celsius(300 kelvin) - - = 26.85 - - ``` +300 K in degree Celsius. +
>>> 300K -> celsius + + = 26.85 +
+
### `from_fahrenheit` @@ -360,17 +280,12 @@ fn from_fahrenheit(t_fahrenheit: Scalar) -> Temperature
Examples -* 300 °F in Kelvin. - - Run this example - ```nbt - >>> from_fahrenheit(300) - - from_fahrenheit(300) - - = 422.039 K [Temperature] - - ``` +300 °F in Kelvin. +
>>> from_fahrenheit(300) + + = 422.039 K [Temperature] +
+
### `fahrenheit` @@ -384,17 +299,12 @@ fn fahrenheit(t_kelvin: Temperature) -> Scalar
Examples -* 300 K in degree Fahrenheit. - - Run this example - ```nbt - >>> 300K -> fahrenheit - - fahrenheit(300 kelvin) - - = 80.33 - - ``` +300 K in degree Fahrenheit. +
>>> 300K -> fahrenheit + + = 80.33 +
+
## Color format conversion @@ -411,16 +321,11 @@ fn rgb(red: Scalar, green: Scalar, blue: Scalar) -> Color
Examples -* Run this example +
>>> rgb(125, 128, 218) + + = Color { red: 125, green: 128, blue: 218 } [Color] +
- ```nbt - >>> rgb(125, 128, 218) - - rgb(125, 128, 218) - - = Color { red: 125, green: 128, blue: 218 } [Color] - - ```
### `color` @@ -433,16 +338,11 @@ fn color(rgb_hex: Scalar) -> Color
Examples -* Run this example +
>>> color(0xff7700) + + = Color { red: 255, green: 119, blue: 0 } [Color] +
- ```nbt - >>> color(0xff7700) - - color(16_742_144) - - = Color { red: 255, green: 119, blue: 0 } [Color] - - ```
### `color_rgb` @@ -455,16 +355,11 @@ fn color_rgb(color: Color) -> String
Examples -* Run this example +
>>> cyan -> color_rgb + + = "rgb(0, 255, 255)" [String] +
- ```nbt - >>> cyan -> color_rgb - - color_rgb(cyan) - - = "rgb(0, 255, 255)" [String] - - ```
### `color_rgb_float` @@ -477,16 +372,11 @@ fn color_rgb_float(color: Color) -> String
Examples -* Run this example +
>>> cyan -> color_rgb_float + + = "rgb(0.000, 1.000, 1.000)" [String] +
- ```nbt - >>> cyan -> color_rgb_float - - color_rgb_float(cyan) - - = "rgb(0.000, 1.000, 1.000)" [String] - - ```
### `color_hex` @@ -499,15 +389,10 @@ fn color_hex(color: Color) -> String
Examples -* Run this example +
>>> rgb(225, 36, 143) -> color_hex + + = "#e1248f" [String] +
- ```nbt - >>> rgb(225, 36, 143) -> color_hex - - color_hex(rgb(225, 36, 143)) - - = "#e1248f" [String] - - ```
diff --git a/book/src/list-functions-strings.md b/book/src/list-functions-strings.md index 59df2308..c233d39b 100644 --- a/book/src/list-functions-strings.md +++ b/book/src/list-functions-strings.md @@ -12,16 +12,11 @@ fn str_length(s: String) -> Scalar
Examples -* Run this example - - ```nbt - >>> str_length("Numbat") - - str_length("Numbat") - - = 6 - - ``` +
>>> str_length("Numbat") + + = 6 +
+
### `str_slice` @@ -34,16 +29,11 @@ fn str_slice(s: String, start: Scalar, end: Scalar) -> String
Examples -* Run this example +
>>> str_slice("Numbat", 3, 6) + + = "bat" [String] +
- ```nbt - >>> str_slice("Numbat", 3, 6) - - str_slice("Numbat", 3, 6) - - = "bat" [String] - - ```
### `chr` @@ -56,16 +46,11 @@ fn chr(n: Scalar) -> String
Examples -* Run this example +
>>> 0x2764 -> chr + + = "❤" [String] +
- ```nbt - >>> 0x2764 -> chr - - chr(10084) - - = "❤" [String] - - ```
### `ord` @@ -78,16 +63,11 @@ fn ord(s: String) -> Scalar
Examples -* Run this example +
>>> "❤" -> ord + + = 10084 +
- ```nbt - >>> "❤" -> ord - - ord("❤") - - = 10084 - - ```
### `lowercase` @@ -100,16 +80,11 @@ fn lowercase(s: String) -> String
Examples -* Run this example +
>>> lowercase("Numbat") + + = "numbat" [String] +
- ```nbt - >>> lowercase("Numbat") - - lowercase("Numbat") - - = "numbat" [String] - - ```
### `uppercase` @@ -122,16 +97,11 @@ fn uppercase(s: String) -> String
Examples -* Run this example +
>>> uppercase("Numbat") + + = "NUMBAT" [String] +
- ```nbt - >>> uppercase("Numbat") - - uppercase("Numbat") - - = "NUMBAT" [String] - - ```
### `str_append` @@ -144,16 +114,11 @@ fn str_append(a: String, b: String) -> String
Examples -* Run this example +
>>> str_append("Numbat", "!") + + = "Numbat!" [String] +
- ```nbt - >>> str_append("Numbat", "!") - - str_append("Numbat", "!") - - = "Numbat!" [String] - - ```
### `str_find` @@ -166,16 +131,11 @@ fn str_find(haystack: String, needle: String) -> Scalar
Examples -* Run this example +
>>> str_find("Numbat is a statically typed programming language.", "typed") + + = 23 +
- ```nbt - >>> str_find("Numbat is a statically typed programming language.", "typed") - - str_find("Numbat is a statically typed programming language.", "typed") - - = 23 - - ```
### `str_contains` @@ -188,16 +148,11 @@ fn str_contains(haystack: String, needle: String) -> Bool
Examples -* Run this example +
>>> str_contains("Numbat is a statically typed programming language.", "typed") + + = true [Bool] +
- ```nbt - >>> str_contains("Numbat is a statically typed programming language.", "typed") - - str_contains("Numbat is a statically typed programming language.", "typed") - - = true [Bool] - - ```
### `str_replace` @@ -210,16 +165,11 @@ fn str_replace(s: String, pattern: String, replacement: String) -> String
Examples -* Run this example +
>>> str_replace("Numbat is a statically typed programming language.", "statically typed programming language", "scientific calculator") + + = "Numbat is a scientific calculator." [String] +
- ```nbt - >>> str_replace("Numbat is a statically typed programming language.", "statically typed programming language", "scientific calculator") - - str_replace("Numbat is a statically typed programming language.", "statically typed programming language", "scientific calculator") - - = "Numbat is a scientific calculator." [String] - - ```
### `str_repeat` @@ -232,16 +182,11 @@ fn str_repeat(a: String, n: Scalar) -> String
Examples -* Run this example +
>>> str_repeat("abc", 4) + + = "abcabcabcabc" [String] +
- ```nbt - >>> str_repeat("abc", 4) - - str_repeat("abc", 4) - - = "abcabcabcabc" [String] - - ```
### `base` @@ -254,16 +199,11 @@ fn base(b: Scalar, x: Scalar) -> String
Examples -* Run this example +
>>> 42 |> base(16) + + = "2a" [String] +
- ```nbt - >>> 42 |> base(16) - - base(16, 42) - - = "2a" [String] - - ```
### `bin` @@ -276,16 +216,11 @@ fn bin(x: Scalar) -> String
Examples -* Run this example +
>>> 42 -> bin + + = "0b101010" [String] +
- ```nbt - >>> 42 -> bin - - bin(42) - - = "0b101010" [String] - - ```
### `oct` @@ -298,16 +233,11 @@ fn oct(x: Scalar) -> String
Examples -* Run this example +
>>> 42 -> oct + + = "0o52" [String] +
- ```nbt - >>> 42 -> oct - - oct(42) - - = "0o52" [String] - - ```
### `dec` @@ -320,16 +250,11 @@ fn dec(x: Scalar) -> String
Examples -* Run this example +
>>> 0b111 -> dec + + = "7" [String] +
- ```nbt - >>> 0b111 -> dec - - dec(7) - - = "7" [String] - - ```
### `hex` @@ -342,15 +267,10 @@ fn hex(x: Scalar) -> String
Examples -* Run this example +
>>> 2^31-1 -> hex + + = "0x7fffffff" [String] +
- ```nbt - >>> 2^31-1 -> hex - - hex(2^31 - 1) - - = "0x7fffffff" [String] - - ```
diff --git a/numbat/examples/inspect.rs b/numbat/examples/inspect.rs index c046d057..0edd14f0 100644 --- a/numbat/examples/inspect.rs +++ b/numbat/examples/inspect.rs @@ -1,7 +1,6 @@ use itertools::Itertools; use numbat::markup::plain_text_format; use numbat::module_importer::FileSystemImporter; -use numbat::pretty_print::PrettyPrint; use numbat::resolver::CodeSource; use numbat::Context; use percent_encoding; @@ -117,48 +116,36 @@ fn inspect_functions_in_module(ctx: &Context, prelude_ctx: &Context, module: Str ); //Assemble the example output - let mut example_output = String::new(); - example_output += "\n"; - - for statement in &statements { - example_output += &plain_text_format(&statement.pretty_print(), true); - example_output += "\n\n"; - } - let result_markup = results.to_markup( statements.last(), &example_ctx.dimension_registry(), true, true, ); - example_output += &plain_text_format(&result_markup, false); - - if results.is_value() { - example_output += "\n"; - } + let example_output = &plain_text_format(&result_markup, false); //Print the example if let Some(example_description) = example_description { - println!( - "* {}\n\n Run this example", - replace_equation_delimiters(example_description), - example_url - ); - } else { - println!( - "* Run this example\n", - example_url - ); + println!("{}", replace_equation_delimiters(example_description)); } - println!(" ```nbt"); + print!("
");
+                    print!("
"); + print!("", + "Run this code", + "Run this code", + example_url); + print!("
"); + print!(""); for l in example_input.lines() { - println!(" {}", l); + println!("{}", l); } + println!(); for l in example_output.lines() { - println!(" {}", l); + println!("{}", l); } - println!(" ```"); + println!("
"); + println!(); } else { eprintln!( "Warning: Example \"{example_code}\" of function {fn_name} did not run successfully." From b9d8c54a5beb70b6a78eca741a767aa741b4c2f2 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Sun, 6 Oct 2024 17:30:15 -0400 Subject: [PATCH 36/46] Cleaned up some todos and clippy warnings --- numbat/src/column_formatter.rs | 4 +--- numbat/src/datetime.rs | 2 +- numbat/src/prefix_parser.rs | 13 ++++--------- numbat/src/quantity.rs | 3 ++- numbat/src/typechecker/constraints.rs | 1 + numbat/src/typechecker/mod.rs | 2 +- numbat/src/typechecker/substitutions.rs | 2 +- numbat/src/typed_ast.rs | 14 +++++++++++++- 8 files changed, 24 insertions(+), 17 deletions(-) diff --git a/numbat/src/column_formatter.rs b/numbat/src/column_formatter.rs index 380d3fef..6165e988 100644 --- a/numbat/src/column_formatter.rs +++ b/numbat/src/column_formatter.rs @@ -45,9 +45,7 @@ impl ColumnFormatter { } for num_columns in min_num_columns..=self.terminal_width { - // TODO: once we have Rust 1.73, use the div_ceil implementation: - // let num_rows = entries.len().div_ceil(num_columns); - let num_rows = (entries.len() + num_columns - 1) / num_columns; + let num_rows = entries.len().div_ceil(num_columns); let mut table: Vec>> = vec![vec![None; num_columns]; num_rows]; for (idx, entry) in entries.iter().enumerate() { diff --git a/numbat/src/datetime.rs b/numbat/src/datetime.rs index 028fd491..ece5056d 100644 --- a/numbat/src/datetime.rs +++ b/numbat/src/datetime.rs @@ -37,7 +37,7 @@ pub fn parse_datetime(input: &str) -> Result { for format in FORMATS { // Try to match the given format plus an additional UTC offset (%z) - if let Ok(dt) = Zoned::strptime(&format!("{format} %z"), input) { + if let Ok(dt) = Zoned::strptime(format!("{format} %z"), input) { return Ok(dt); } diff --git a/numbat/src/prefix_parser.rs b/numbat/src/prefix_parser.rs index c8f93165..93d92019 100644 --- a/numbat/src/prefix_parser.rs +++ b/numbat/src/prefix_parser.rs @@ -1,5 +1,5 @@ +use indexmap::IndexMap; use std::collections::HashMap; - use std::sync::OnceLock; use crate::span::Span; @@ -82,10 +82,7 @@ struct UnitInfo { #[derive(Debug, Clone)] pub struct PrefixParser { - units: HashMap, - // This is the exact same information as in the "units" hashmap, only faster to iterate over. - // TODO: maybe use an external crate for this (e.g. indexmap?) - units_vec: Vec<(String, UnitInfo)>, + units: IndexMap, other_identifiers: HashMap, @@ -95,8 +92,7 @@ pub struct PrefixParser { impl PrefixParser { pub fn new() -> Self { Self { - units: HashMap::new(), - units_vec: Vec::new(), + units: IndexMap::new(), other_identifiers: HashMap::new(), reserved_identifiers: &["_", "ans"], } @@ -237,7 +233,6 @@ impl PrefixParser { full_name: full_name.into(), }; self.units.insert(unit_name.into(), unit_info.clone()); - self.units_vec.push((unit_name.into(), unit_info)); Ok(()) } @@ -260,7 +255,7 @@ impl PrefixParser { ); } - for (unit_name, info) in &self.units_vec { + for (unit_name, info) in &self.units { if !input.ends_with(unit_name.as_str()) { continue; } diff --git a/numbat/src/quantity.rs b/numbat/src/quantity.rs index eb3afcb3..e0509a2a 100644 --- a/numbat/src/quantity.rs +++ b/numbat/src/quantity.rs @@ -192,7 +192,8 @@ impl Quantity { let group_representative = group_as_unit .iter() .max_by(|&f1, &f2| { - // TODO: describe this heuristic + // prefer base units over non-base. if multiple base units, prefer + // those with a larger exponent (f1.unit_id.is_base().cmp(&f2.unit_id.is_base())) .then(f1.exponent.cmp(&f2.exponent)) }) diff --git a/numbat/src/typechecker/constraints.rs b/numbat/src/typechecker/constraints.rs index d6481cd4..2187f6a0 100644 --- a/numbat/src/typechecker/constraints.rs +++ b/numbat/src/typechecker/constraints.rs @@ -160,6 +160,7 @@ pub enum TrivialResolution { } impl TrivialResolution { + #[allow(clippy::wrong_self_convention)] pub fn is_trivially_violated(self) -> bool { matches!(self, TrivialResolution::Violated) } diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index 430974fd..ec9e8b03 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -125,7 +125,7 @@ impl TypeChecker { .map_err(TypeCheckError::RegistryError)?; // Replace BaseDimension("D") with TVar("D") for all type parameters - for (factor, _) in dtype.factors.iter_mut() { + for (factor, _) in dtype.factors_mut() { *factor = match factor { DTypeFactor::BaseDimension(ref n) if self diff --git a/numbat/src/typechecker/substitutions.rs b/numbat/src/typechecker/substitutions.rs index 94e2b170..2cb353f1 100644 --- a/numbat/src/typechecker/substitutions.rs +++ b/numbat/src/typechecker/substitutions.rs @@ -92,7 +92,7 @@ impl ApplySubstitution for Type { impl ApplySubstitution for DType { fn apply(&mut self, substitution: &Substitution) -> Result<(), SubstitutionError> { let mut new_dtype = self.clone(); - for (f, power) in &self.factors { + for (f, power) in self.factors() { match f { DTypeFactor::TVar(tv) => { if let Some(type_) = substitution.lookup(tv) { diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 4b746ea9..88567f83 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -41,10 +41,22 @@ type DtypeFactorPower = (DTypeFactor, Exponent); #[derive(Clone, Debug, PartialEq, Eq)] pub struct DType { // Always in canonical form - pub factors: Vec, // TODO make this private + factors: Vec, } impl DType { + pub fn new(factors: Vec) -> Self { + Self { factors } + } + + pub fn factors(&self) -> &[DtypeFactorPower] { + &self.factors + } + + pub fn factors_mut(&mut self) -> &mut [DtypeFactorPower] { + &mut self.factors + } + pub fn from_factors(factors: &[DtypeFactorPower]) -> DType { let mut dtype = DType { factors: factors.into(), From 5350576eac45c624c99099b05c649f4fb461af52 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Mon, 7 Oct 2024 00:20:27 -0400 Subject: [PATCH 37/46] Removed some code duplication --- numbat/src/unit.rs | 47 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/numbat/src/unit.rs b/numbat/src/unit.rs index b94cd74d..b9292c5b 100644 --- a/numbat/src/unit.rs +++ b/numbat/src/unit.rs @@ -269,33 +269,26 @@ impl Unit { } pub fn to_base_unit_representation(&self) -> (Self, ConversionFactor) { - // TODO: reduce wrapping/unwrapping and duplication. - - let base_unit_representation = self - .iter() - .map( - |UnitFactor { - prefix: _, - unit_id: base_unit, - exponent, - }| { base_unit.base_unit_and_factor().0.power(*exponent) }, - ) - .product::() - .canonicalized(); - - let factor = self - .iter() - .map( - |UnitFactor { - prefix, - unit_id: base_unit, - exponent, - }| { - (prefix.factor() * base_unit.base_unit_and_factor().1) - .pow(&Number::from_f64(exponent.to_f64().unwrap())) // TODO do we want to use exponent.to_f64? - }, - ) - .product(); + // TODO: reduce wrapping/unwrapping + let (mut base_unit_representation, factor) = self.iter().fold( + (Product::unity(), Number::from_f64(1.0)), + |(acc_product, acc_factor), + UnitFactor { + unit_id: base_unit, + prefix, + exponent, + }| { + ( + acc_product * base_unit.base_unit_and_factor().0.power(*exponent), + acc_factor + * (prefix.factor() * base_unit.base_unit_and_factor().1) + // TODO do we want to use exponent.to_f64? + .pow(&Number::from_f64(exponent.to_f64().unwrap())), + ) + }, + ); + + base_unit_representation.canonicalize(); (base_unit_representation, factor) } From 3a25ac736e1a1f5aca36cd7a5930146b01d9cee1 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Mon, 7 Oct 2024 12:10:23 -0400 Subject: [PATCH 38/46] Added consuming `DType::into_factors`, removing need for mutable access to factors Changed `from_factors` to take an owned `Vec` --- numbat/src/typechecker/constraints.rs | 5 +--- numbat/src/typechecker/mod.rs | 11 ++++---- numbat/src/typed_ast.rs | 36 +++++++++++---------------- 3 files changed, 22 insertions(+), 30 deletions(-) diff --git a/numbat/src/typechecker/constraints.rs b/numbat/src/typechecker/constraints.rs index 2187f6a0..24546782 100644 --- a/numbat/src/typechecker/constraints.rs +++ b/numbat/src/typechecker/constraints.rs @@ -297,10 +297,7 @@ impl Constraint { Constraint::EqualScalar(dtype) => match dtype.split_first_factor() { Some(((DTypeFactor::TVar(tv), k), rest)) => { let result = DType::from_factors( - &rest - .iter() - .map(|(f, j)| (f.clone(), -j / k)) - .collect::>(), + rest.iter().map(|(f, j)| (f.clone(), -j / k)).collect(), ); Some(Satisfied::with_substitution(Substitution::single( tv.clone(), diff --git a/numbat/src/typechecker/mod.rs b/numbat/src/typechecker/mod.rs index ec9e8b03..faa13588 100644 --- a/numbat/src/typechecker/mod.rs +++ b/numbat/src/typechecker/mod.rs @@ -118,14 +118,15 @@ impl TypeChecker { } } - let mut dtype: DType = self + let mut factors = self .registry .get_base_representation(dexpr) - .map(|br| br.into()) - .map_err(TypeCheckError::RegistryError)?; + .map(DType::from) + .map_err(TypeCheckError::RegistryError)? + .into_factors(); // Replace BaseDimension("D") with TVar("D") for all type parameters - for (factor, _) in dtype.factors_mut() { + for (factor, _) in &mut factors { *factor = match factor { DTypeFactor::BaseDimension(ref n) if self @@ -140,7 +141,7 @@ impl TypeChecker { } } - Ok(Type::Dimension(dtype)) + Ok(Type::Dimension(DType::from_factors(factors))) } TypeAnnotation::Bool(_) => Ok(Type::Boolean), TypeAnnotation::String(_) => Ok(Type::String), diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index 88567f83..f1221bb2 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -45,28 +45,22 @@ pub struct DType { } impl DType { - pub fn new(factors: Vec) -> Self { - Self { factors } - } - pub fn factors(&self) -> &[DtypeFactorPower] { &self.factors } - pub fn factors_mut(&mut self) -> &mut [DtypeFactorPower] { - &mut self.factors + pub fn into_factors(self) -> Vec { + self.factors } - pub fn from_factors(factors: &[DtypeFactorPower]) -> DType { - let mut dtype = DType { - factors: factors.into(), - }; + pub fn from_factors(factors: Vec) -> DType { + let mut dtype = DType { factors }; dtype.canonicalize(); dtype } pub fn scalar() -> DType { - DType::from_factors(&[]) + DType::from_factors(vec![]) } pub fn is_scalar(&self) -> bool { @@ -105,11 +99,11 @@ impl DType { } pub fn from_type_variable(v: TypeVariable) -> DType { - DType::from_factors(&[(DTypeFactor::TVar(v), Exponent::from_integer(1))]) + DType::from_factors(vec![(DTypeFactor::TVar(v), Exponent::from_integer(1))]) } pub fn from_type_parameter(name: String) -> DType { - DType::from_factors(&[(DTypeFactor::TPar(name), Exponent::from_integer(1))]) + DType::from_factors(vec![(DTypeFactor::TPar(name), Exponent::from_integer(1))]) } pub fn deconstruct_as_single_type_variable(&self) -> Option { @@ -122,14 +116,14 @@ impl DType { } pub fn from_tgen(i: usize) -> DType { - DType::from_factors(&[( + DType::from_factors(vec![( DTypeFactor::TVar(TypeVariable::Quantified(i)), Exponent::from_integer(1), )]) } pub fn base_dimension(name: &str) -> DType { - DType::from_factors(&[( + DType::from_factors(vec![( DTypeFactor::BaseDimension(name.into()), Exponent::from_integer(1), )]) @@ -170,16 +164,16 @@ impl DType { pub fn multiply(&self, other: &DType) -> DType { let mut factors = self.factors.clone(); factors.extend(other.factors.clone()); - DType::from_factors(&factors) + DType::from_factors(factors) } pub fn power(&self, n: Exponent) -> DType { - let factors: Vec<_> = self + let factors = self .factors .iter() .map(|(f, m)| (f.clone(), n * m)) .collect(); - DType::from_factors(&factors) + DType::from_factors(factors) } pub fn inverse(&self) -> DType { @@ -233,7 +227,7 @@ impl DType { } } } - Self::from_factors(&factors) + Self::from_factors(factors) } pub fn to_base_representation(&self) -> BaseRepresentation { @@ -272,11 +266,11 @@ impl std::fmt::Display for DType { impl From for DType { fn from(base_representation: BaseRepresentation) -> Self { - let factors: Vec<_> = base_representation + let factors = base_representation .into_iter() .map(|BaseRepresentationFactor(name, exp)| (DTypeFactor::BaseDimension(name), exp)) .collect(); - DType::from_factors(&factors) + DType::from_factors(factors) } } From 1a5a73152ba0679b46cc4b58ea609a89cfe96017 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Mon, 7 Oct 2024 14:12:09 -0400 Subject: [PATCH 39/46] Switched to a for loop (simpler) --- numbat/src/unit.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/numbat/src/unit.rs b/numbat/src/unit.rs index b9292c5b..c2653137 100644 --- a/numbat/src/unit.rs +++ b/numbat/src/unit.rs @@ -270,23 +270,22 @@ impl Unit { pub fn to_base_unit_representation(&self) -> (Self, ConversionFactor) { // TODO: reduce wrapping/unwrapping - let (mut base_unit_representation, factor) = self.iter().fold( - (Product::unity(), Number::from_f64(1.0)), - |(acc_product, acc_factor), - UnitFactor { - unit_id: base_unit, - prefix, - exponent, - }| { - ( - acc_product * base_unit.base_unit_and_factor().0.power(*exponent), - acc_factor - * (prefix.factor() * base_unit.base_unit_and_factor().1) - // TODO do we want to use exponent.to_f64? - .pow(&Number::from_f64(exponent.to_f64().unwrap())), - ) - }, - ); + let mut base_unit_representation = Product::unity(); + let mut factor = Number::from_f64(1.0); + + for UnitFactor { + unit_id: base_unit, + prefix, + exponent, + } in self.iter() + { + base_unit_representation = + base_unit_representation * base_unit.base_unit_and_factor().0.power(*exponent); + factor = factor + * (prefix.factor() * base_unit.base_unit_and_factor().1) + // TODO do we want to use exponent.to_f64? + .pow(&Number::from_f64(exponent.to_f64().unwrap())); + } base_unit_representation.canonicalize(); From c2e97cc6d27841928ba09cde77cc1b47a4dfc997 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Mon, 7 Oct 2024 15:51:46 -0400 Subject: [PATCH 40/46] Removed needless `Product[Into]Iter` types --- numbat/src/product.rs | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/numbat/src/product.rs b/numbat/src/product.rs index ef3fb031..ce0fb9d1 100644 --- a/numbat/src/product.rs +++ b/numbat/src/product.rs @@ -145,10 +145,8 @@ impl Product ProductIter { - ProductIter { - inner: self.factors.iter(), - } + pub fn iter(&self) -> std::slice::Iter<'_, Factor> { + self.factors.iter() } #[cfg(test)] @@ -245,13 +243,11 @@ impl Eq } impl IntoIterator for Product { - type IntoIter = ProductIntoIter; + type IntoIter = as IntoIterator>::IntoIter; type Item = Factor; fn into_iter(self) -> Self::IntoIter { - ProductIntoIter { - inner: self.factors.into_iter(), - } + self.factors.into_iter() } } @@ -277,30 +273,6 @@ impl std::iter::Pr } } -pub struct ProductIter<'a, Factor> { - inner: std::slice::Iter<'a, Factor>, -} - -impl<'a, Factor> Iterator for ProductIter<'a, Factor> { - type Item = &'a Factor; - - fn next(&mut self) -> Option { - self.inner.next() - } -} - -pub struct ProductIntoIter { - inner: std::vec::IntoIter, -} - -impl Iterator for ProductIntoIter { - type Item = Factor; - - fn next(&mut self) -> Option { - self.inner.next() - } -} - #[cfg(test)] mod tests { use super::*; From 7620bea4f4dc49c05ea906c6aa41de380c60f37b Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Thu, 3 Oct 2024 13:28:15 -0400 Subject: [PATCH 41/46] Improved error message when encountering a trailing equals sign, adding heuristic to check whether user might have been trying to define a function --- numbat/src/parser.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/numbat/src/parser.rs b/numbat/src/parser.rs index 56e16afc..01074020 100644 --- a/numbat/src/parser.rs +++ b/numbat/src/parser.rs @@ -96,6 +96,9 @@ pub enum ParseErrorKind { #[error("Trailing '=' sign. Use `let {0} = …` if you intended to define a new constant.")] TrailingEqualSign(String), + #[error("Trailing '=' sign. Use `fn {0} = …` if you intended to define a function.")] + TrailingEqualSignFunction(String), + #[error("Expected identifier after 'let' keyword")] ExpectedIdentifierAfterLet, @@ -313,12 +316,22 @@ impl<'a> Parser<'a> { break; } TokenKind::Equal => { + let last_token = self.last(tokens).unwrap(); + + let mut input = String::new(); + for token in tokens.iter().take(self.current) { + input.push_str(token.lexeme); + } + errors.push(ParseError { - kind: ParseErrorKind::TrailingEqualSign( - self.last(tokens).unwrap().lexeme.to_owned(), - ), + kind: if last_token.kind == TokenKind::RightParen { + ParseErrorKind::TrailingEqualSignFunction(input) + } else { + ParseErrorKind::TrailingEqualSign(input) + }, span: self.peek(tokens).span, }); + self.recover_from_error(tokens); } _ => { From c3e021b820a623b3b760d5f9383dfd5f3668bee2 Mon Sep 17 00:00:00 2001 From: Robert Bennett Date: Tue, 8 Oct 2024 13:30:36 -0400 Subject: [PATCH 42/46] Fixed some clippy warnings --- numbat/src/decorator.rs | 2 +- numbat/src/parser.rs | 6 +++--- numbat/src/registry.rs | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/numbat/src/decorator.rs b/numbat/src/decorator.rs index 76174ea7..c7fa234f 100644 --- a/numbat/src/decorator.rs +++ b/numbat/src/decorator.rs @@ -125,7 +125,7 @@ pub fn examples(decorators: &[Decorator]) -> Vec<(String, Option)> { examples.push((example_code.clone(), example_description.clone())); } } - return examples; + examples } pub fn contains_aliases_with_prefixes(decorates: &[Decorator]) -> bool { diff --git a/numbat/src/parser.rs b/numbat/src/parser.rs index 01074020..e2146c6e 100644 --- a/numbat/src/parser.rs +++ b/numbat/src/parser.rs @@ -777,8 +777,8 @@ impl<'a> Parser<'a> { } Decorator::Example( - strip_and_escape(&token_code.lexeme), - Some(strip_and_escape(&token_description.lexeme)), + strip_and_escape(token_code.lexeme), + Some(strip_and_escape(token_description.lexeme)), ) } else { return Err(ParseError { @@ -795,7 +795,7 @@ impl<'a> Parser<'a> { )); } - Decorator::Example(strip_and_escape(&token_code.lexeme), None) + Decorator::Example(strip_and_escape(token_code.lexeme), None) } } else { return Err(ParseError { diff --git a/numbat/src/registry.rs b/numbat/src/registry.rs index 7ef0e24f..2e8fa086 100644 --- a/numbat/src/registry.rs +++ b/numbat/src/registry.rs @@ -24,9 +24,6 @@ pub type Result = std::result::Result; pub type BaseEntry = String; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct BaseIndex(isize); - #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct BaseRepresentationFactor(pub BaseEntry, pub Exponent); From a823d6201460db062e561a9024f6ae2aa92e66f1 Mon Sep 17 00:00:00 2001 From: Bzero Date: Tue, 8 Oct 2024 18:51:28 +0200 Subject: [PATCH 43/46] Add explicit use statement for non-prelude functions to examples --- book/src/list-functions-math.md | 18 ++++++++++++------ book/src/list-functions-other.md | 15 ++++++++++----- numbat/examples/inspect.rs | 7 ++++--- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/book/src/list-functions-math.md b/book/src/list-functions-math.md index 62464657..53cb49c2 100644 --- a/book/src/list-functions-math.md +++ b/book/src/list-functions-math.md @@ -745,14 +745,16 @@ fn diff(f: Fn[(X) -> Y], x: X) -> Y / X Examples Compute the derivative of \\( f(x) = x² -x -1 \\) at \\( x=1 \\). -
>>> fn polynomial(x) = x² - x - 1 +
>>> use numerics::diff +fn polynomial(x) = x² - x - 1 diff(polynomial, 1) = 1.0
Compute the free fall velocity after \\( t=2 s \\). -
>>> fn distance(t) = 0.5 g0 t² +
>>> use numerics::diff +fn distance(t) = 0.5 g0 t² fn velocity(t) = diff(distance, t) velocity(2 s) @@ -773,7 +775,8 @@ fn root_bisect(f: Fn[(X) -> Y], x1: X, x2: X, x_tol: X, y_tol: Y Examples Find the root of \\( f(x) = x² +x -2 \\) in the interval \\( [0, 100] \\). -
>>> fn f(x) = x² +x -2 +
>>> use numerics::solve +fn f(x) = x² +x -2 root_bisect(f, 0, 100, 0.01, 0.01) = 1.00098 @@ -793,7 +796,8 @@ fn root_newton(f: Fn[(X) -> Y], f_prime: Fn[(X) -> Y / X], x0: X Examples Find a root of \\( f(x) = x² -3x +2 \\) using Newton's method. -
>>> fn f(x) = x² -3x +2 +
>>> use numerics::solve +fn f(x) = x² -3x +2 fn f_prime(x) = 2x -3 root_newton(f, f_prime, 0 , 0.01) @@ -814,7 +818,8 @@ fn fixed_point(f: Fn[(X) -> X], x0: X, ε: X) -> X Examples Compute the fixed poin of \\( f(x) = x/2 -1 \\). -
>>> fn function(x) = x/2 - 1 +
>>> use numerics::fixed_point +fn function(x) = x/2 - 1 fixed_point(function, 0, 0.01) = -1.99219 @@ -904,7 +909,8 @@ fn quadratic_equation(a: A, b: B, c: B^2 / A) -> List Examples Solve the equation \\( 2x² -x -1 = 0 \\) -
>>> quadratic_equation(2, -1, -1) +
>>> use extra::algebra +quadratic_equation(2, -1, -1) = [1, -0.5] [List]
diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 71ba20c6..6da3bbc5 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -321,7 +321,8 @@ fn rgb(red: Scalar, green: Scalar, blue: Scalar) -> Color
Examples -
>>> rgb(125, 128, 218) +
>>> use extra::color +rgb(125, 128, 218) = Color { red: 125, green: 128, blue: 218 } [Color]
@@ -338,7 +339,8 @@ fn color(rgb_hex: Scalar) -> Color
Examples -
>>> color(0xff7700) +
>>> use extra::color +color(0xff7700) = Color { red: 255, green: 119, blue: 0 } [Color]
@@ -355,7 +357,8 @@ fn color_rgb(color: Color) -> String
Examples -
>>> cyan -> color_rgb +
>>> use extra::color +cyan -> color_rgb = "rgb(0, 255, 255)" [String]
@@ -372,7 +375,8 @@ fn color_rgb_float(color: Color) -> String
Examples -
>>> cyan -> color_rgb_float +
>>> use extra::color +cyan -> color_rgb_float = "rgb(0.000, 1.000, 1.000)" [String]
@@ -389,7 +393,8 @@ fn color_hex(color: Color) -> String
Examples -
>>> rgb(225, 36, 143) -> color_hex +
>>> use extra::color +rgb(225, 36, 143) -> color_hex = "#e1248f" [String]
diff --git a/numbat/examples/inspect.rs b/numbat/examples/inspect.rs index 0edd14f0..0342c5f5 100644 --- a/numbat/examples/inspect.rs +++ b/numbat/examples/inspect.rs @@ -102,15 +102,16 @@ fn inspect_functions_in_module(ctx: &Context, prelude_ctx: &Context, module: Str if let Ok((statements, results)) = example_ctx.interpret(&example_code, CodeSource::Internal) { + let code = extra_import + &example_code; + //Format the example input - let example_input = format!(">>> {}", example_code); + let example_input = format!(">>> {}", code); //Encode the example url - let url_code = extra_import + &example_code; let example_url = format!( "https://numbat.dev/?q={}", percent_encoding::utf8_percent_encode( - &url_code, + &code, percent_encoding::NON_ALPHANUMERIC ) ); From 11e3ee9ca948f99323768e30b78fc4b47254f9f1 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 8 Oct 2024 23:02:34 +0200 Subject: [PATCH 44/46] Add @example for unit_list --- book/src/list-functions-other.md | 18 ++++++++++++++---- numbat/modules/units/mixed.nbt | 1 + 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 1d238a3a..360213bf 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -162,6 +162,16 @@ Convert a value to a mixed representation using the provided units. fn unit_list(units: List, value: D) -> List ``` +
+Examples + +
>>> 5500 m |> unit_list([miles, yards, feet, inches]) + + = [3 mi, 734 yd, 2 ft, 7.43307 in] [List] +
+ +
+ ### `DMS` (Degrees, minutes, seconds) Convert an angle to a mixed degrees, (arc)minutes, and (arc)seconds representation. Also called sexagesimal degree notation. More information [here](https://en.wikipedia.org/wiki/Sexagesimal_degree). @@ -175,7 +185,7 @@ fn DMS(alpha: Angle) -> List
>>> 46.5858° -> DMS - = "46° 35′ 9″" [String] + = [46°, 35 arcminute, 8.88 arcsecond] [List]
@@ -193,7 +203,7 @@ fn DM(alpha: Angle) -> List
>>> 46.5858° -> DM - = "46° 35.148′" [String] + = [46°, 35.148 arcminute] [List]
@@ -211,7 +221,7 @@ fn feet_and_inches(length: Length) -> List
>>> 180 cm -> feet_and_inches - = "5 ft 10.8661 in" [String] + = [5 ft, 10.8661 in] [List]
@@ -229,7 +239,7 @@ fn pounds_and_ounces(mass: Mass) -> List
>>> 1 kg -> pounds_and_ounces - = "2 lb 3.27396 oz" [String] + = [2 lb, 3.27396 oz] [List]
diff --git a/numbat/modules/units/mixed.nbt b/numbat/modules/units/mixed.nbt index 6725ee0b..d0aa1639 100644 --- a/numbat/modules/units/mixed.nbt +++ b/numbat/modules/units/mixed.nbt @@ -4,6 +4,7 @@ use units::imperial @name("Unit list") @description("Convert a value to a mixed representation using the provided units.") +@example("5500 m |> unit_list([miles, yards, feet, inches])") fn unit_list(units: List, value: D) -> List = _mixed_unit_list(value, units, []) @name("Degrees, minutes, seconds") From ca13e43344d21a25009baca24f900121bc8c7ef5 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 8 Oct 2024 23:06:02 +0200 Subject: [PATCH 45/46] =?UTF-8?q?Use=20=E2=80=B2=20and=20=E2=80=B3=20as=20?= =?UTF-8?q?default=20short=20names=20for=20arcminute/arcsecond?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/src/list-functions-other.md | 4 ++-- numbat/modules/units/si.nbt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/book/src/list-functions-other.md b/book/src/list-functions-other.md index 360213bf..179f4360 100644 --- a/book/src/list-functions-other.md +++ b/book/src/list-functions-other.md @@ -185,7 +185,7 @@ fn DMS(alpha: Angle) -> List
>>> 46.5858° -> DMS - = [46°, 35 arcminute, 8.88 arcsecond] [List] + = [46°, 35′, 8.88″] [List]
@@ -203,7 +203,7 @@ fn DM(alpha: Angle) -> List
>>> 46.5858° -> DM - = [46°, 35.148 arcminute] [List] + = [46°, 35.148′] [List]
diff --git a/numbat/modules/units/si.nbt b/numbat/modules/units/si.nbt index 2ef8ffce..8ceddaee 100644 --- a/numbat/modules/units/si.nbt +++ b/numbat/modules/units/si.nbt @@ -202,12 +202,12 @@ unit degree: Angle = π / 180 × radian @name("Minute of arc") @url("https://en.wikipedia.org/wiki/Minute_and_second_of_arc") -@aliases(arcminutes, arcmin, ′) +@aliases(arcminutes, arcmin, ′: short) unit arcminute: Angle = 1 / 60 × degree @name("Second of arc") @url("https://en.wikipedia.org/wiki/Minute_and_second_of_arc") -@aliases(arcseconds, arcsec, ″) +@aliases(arcseconds, arcsec, ″: short) unit arcsecond: Angle = 1 / 60 × arcminute @name("Are") From 1a6c1477eb64c85dc47d602fee202dcbe434131b Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 8 Oct 2024 23:10:06 +0200 Subject: [PATCH 46/46] Replace 'foldl(_add, 0)' by 'sum' --- examples/tests/mixed_units.nbt | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/tests/mixed_units.nbt b/examples/tests/mixed_units.nbt index ae276701..2bb71ddd 100644 --- a/examples/tests/mixed_units.nbt +++ b/examples/tests/mixed_units.nbt @@ -37,17 +37,15 @@ assert_eq("{-5.5 lb -> pounds_and_ounces}", "{[-5 lb, -8 oz]}") # Unit list -fn _add(a: D, b: D) -> D = a + b - let test1 = 12 m + 34 cm + 5 mm + 678 µm assert_eq(test1 |> unit_list([m]) |> head, test1) -assert_eq(test1 |> unit_list([m, cm]) |> foldl(_add, 0), test1) -assert_eq(test1 |> unit_list([m, cm, mm]) |> foldl(_add, 0), test1) -assert_eq(test1 |> unit_list([m, cm, mm, µm]) |> foldl(_add, 0), test1) +assert_eq(test1 |> unit_list([m, cm]) |> sum, test1) +assert_eq(test1 |> unit_list([m, cm, mm]) |> sum, test1) +assert_eq(test1 |> unit_list([m, cm, mm, µm]) |> sum, test1) let test2 = 12 degree + 34 arcminute + 5 arcsec assert_eq(test2 |> unit_list([degree]) |> head, test2) -assert_eq(test2 |> unit_list([degree, arcmin]) |> foldl(_add, 0), test2) -assert_eq(test2 |> unit_list([degree, arcmin, arcsec]) |> foldl(_add, 0), test2) +assert_eq(test2 |> unit_list([degree, arcmin]) |> sum, test2) +assert_eq(test2 |> unit_list([degree, arcmin, arcsec]) |> sum, test2)