From d5f24efd7946a56d6e6dd4dda7bdd7253ce3bb0d Mon Sep 17 00:00:00 2001 From: David Peter Date: Thu, 16 May 2024 22:22:04 +0200 Subject: [PATCH] Add some initial examples --- numbat/modules/core/lists.nbt | 52 +++++++++++++++++++++++++++++++++++ numbat/src/ffi.rs | 16 +++++++++++ numbat/src/typed_ast.rs | 9 +++--- 3 files changed, 73 insertions(+), 4 deletions(-) diff --git a/numbat/modules/core/lists.nbt b/numbat/modules/core/lists.nbt index 6b23e8de..13a9b838 100644 --- a/numbat/modules/core/lists.nbt +++ b/numbat/modules/core/lists.nbt @@ -1,3 +1,55 @@ +use core::scalar + +fn len(xs: List) -> Scalar fn head(xs: List) -> A fn tail(xs: List) -> List fn cons(x: A, xs: List) -> List + +fn is_empty(xs: List) -> Bool = len(xs) == 0 + +# We definitely want to make the following functions generic, but this is not yet possible: +# Also, we want the base case to be the empty list, but this is also not yet possible. + +fn generate(n: Scalar, f: Fn[() -> Scalar]) -> List = + if n == 1 + then [f()] + else cons(f(), generate(n - 1, f)) + +fn map(f: Fn[(Scalar) -> Scalar], xs: List) -> List = + if len(xs) == 1 + then [f(head(xs))] + else cons(f(head(xs)), map(f, tail(xs))) + +fn cons_end(xs: List, x: Scalar) -> List = + if is_empty(xs) + then [x] + else cons(head(xs), cons_end(tail(xs), x)) + +fn reverse(xs: List) -> List = + if len(xs) == 1 + then xs + else cons_end(reverse(tail(xs)), head(xs)) + +fn sequence(n: Scalar) -> List = + if n == 1 + then [0] + else cons_end(sequence(n - 1), n - 1) + +fn foldl(f: Fn[(Scalar, Scalar) -> Scalar], acc: Scalar, xs: List) -> Scalar = + if len(xs) == 1 + then f(acc, head(xs)) + else foldl(f, f(acc, head(xs)), tail(xs)) + +fn const_5() -> Scalar = 5 + +fn inc(x: Scalar) -> Scalar = x + 1 + +fn add(x: Scalar, y: Scalar) -> Scalar = x + y +fn mul(x: Scalar, y: Scalar) -> Scalar = x * y + +assert(sequence(5) == [0, 1, 2, 3, 4]) +assert(generate(3, const_5) == [5, 5, 5]) +assert(map(inc, [1, 2, 3]) == [2, 3, 4]) +assert(reverse([1, 2, 3]) == [3, 2, 1]) +assert(foldl(add, 0, [1, 2, 3, 4, 5]) == 15) +assert(foldl(mul, 1, [1, 2, 3, 4, 5]) == 120) diff --git a/numbat/src/ffi.rs b/numbat/src/ffi.rs index 78b3ab1f..e2d7f358 100644 --- a/numbat/src/ffi.rs +++ b/numbat/src/ffi.rs @@ -325,6 +325,14 @@ pub(crate) fn functions() -> &'static HashMap { }, ); + m.insert( + "len".to_string(), + ForeignFunction { + name: "len".into(), + arity: 1..=1, + callable: Callable::Function(Box::new(len)), + }, + ); m.insert( "head".to_string(), ForeignFunction { @@ -864,6 +872,14 @@ fn exchange_rate(args: &[Value]) -> Result { ))) } +fn len(args: &[Value]) -> Result { + assert!(args.len() == 1); + + let list = args[0].unsafe_as_list(); + + Ok(Value::Quantity(Quantity::from_scalar(list.len() as f64))) +} + fn head(args: &[Value]) -> Result { assert!(args.len() == 1); diff --git a/numbat/src/typed_ast.rs b/numbat/src/typed_ast.rs index c89e3385..8900f903 100644 --- a/numbat/src/typed_ast.rs +++ b/numbat/src/typed_ast.rs @@ -105,11 +105,11 @@ impl std::fmt::Display for Type { impl PrettyPrint for Type { fn pretty_print(&self) -> Markup { match self { - Type::Never => m::keyword("!"), + Type::Never => m::type_identifier("!"), Type::Dimension(d) => d.pretty_print(), - Type::Boolean => m::keyword("Bool"), - Type::String => m::keyword("String"), - Type::DateTime => m::keyword("DateTime"), + Type::Boolean => m::type_identifier("Bool"), + Type::String => m::type_identifier("String"), + Type::DateTime => m::type_identifier("DateTime"), Type::Fn(param_types, return_type) => { m::type_identifier("Fn") + m::operator("[(") @@ -164,6 +164,7 @@ impl Type { match (self, other) { (Type::Never, _) => true, (_, Type::Never) => false, + (Type::List(el1), Type::List(el2)) => el1.is_subtype_of(el2), (t1, t2) => t1 == t2, } }