From 5f958c72b8a796d1d88cbb10129d04b38169e161 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 8 Mar 2024 22:34:17 -0500 Subject: [PATCH] Begin implementing built-in functions --- src/value.rs | 66 ++++++++++++++++++++++---- tests/functions.rs | 115 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 10 deletions(-) create mode 100644 tests/functions.rs diff --git a/src/value.rs b/src/value.rs index cc3a5f3..21e6931 100644 --- a/src/value.rs +++ b/src/value.rs @@ -13,8 +13,9 @@ use stanza::{ }; use crate::{ - abstract_tree::{Identifier, Statement, Type}, - error::ValidationError, + abstract_tree::{Action, Identifier, Statement, Type}, + context::Context, + error::{RuntimeError, ValidationError}, }; pub static NONE: OnceLock = OnceLock::new(); @@ -77,11 +78,13 @@ impl Value { return_type: Type, body: Statement, ) -> Self { - Value(Arc::new(ValueInner::Function(Function { - parameters, - return_type, - body, - }))) + Value(Arc::new(ValueInner::Function(Function::Parsed( + ParsedFunction { + parameters, + return_type, + body, + }, + )))) } pub fn r#type(&self) -> Type { @@ -227,11 +230,11 @@ impl Display for Value { ValueInner::String(string) => write!(f, "{string}"), ValueInner::Enum(_, _) => todo!(), - ValueInner::Function(Function { + ValueInner::Function(Function::Parsed(ParsedFunction { parameters, return_type, body, - }) => { + })) => { write!(f, "(")?; for (identifier, r#type) in parameters { @@ -240,6 +243,9 @@ impl Display for Value { write!(f, "): {return_type} {body:?}") } + ValueInner::Function(Function::BuiltIn(built_in_function)) => { + write!(f, "{built_in_function}") + } } } } @@ -323,8 +329,48 @@ impl Ord for ValueInner { } #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct Function { +pub enum Function { + Parsed(ParsedFunction), + BuiltIn(BuiltInFunction), +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct ParsedFunction { parameters: Vec<(Identifier, Type)>, return_type: Type, body: Statement, } + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum BuiltInFunction { + Output(Value), +} + +impl BuiltInFunction { + fn r#type(&self, context: &Context) -> Type { + match self { + BuiltInFunction::Output(_) => Type::Function { + parameter_types: vec![Type::Any], + return_type: Box::new(Type::None), + }, + } + } + + fn call(self, context: &Context) -> Result { + match self { + BuiltInFunction::Output(value) => { + println!("{value}"); + + Ok(Action::None) + } + } + } +} + +impl Display for BuiltInFunction { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + BuiltInFunction::Output(_) => write!(f, "(to_output : any) : none rust_magic();"), + } + } +} diff --git a/tests/functions.rs b/tests/functions.rs new file mode 100644 index 0000000..2c17fbe --- /dev/null +++ b/tests/functions.rs @@ -0,0 +1,115 @@ +use dust_lang::{ + abstract_tree::Identifier, + error::{Error, ValidationError}, + *, +}; + +#[test] +fn function_call() { + assert_eq!( + interpret( + " + foobar = (message : str) : str { message } + foobar('Hiya') + ", + ), + Ok(Some(Value::string("Hiya".to_string()))) + ); +} + +#[test] +fn call_empty_function() { + assert_eq!( + interpret( + " + foobar = (message : str) : none {} + foobar('Hiya') + ", + ), + Ok(None) + ); +} + +#[test] +fn callback() { + assert_eq!( + interpret( + " + foobar = (cb : () -> str) : str { + cb() + } + foobar(() : str { 'Hiya' }) + ", + ), + Ok(Some(Value::string("Hiya".to_string()))) + ); +} + +#[test] +fn built_in_function_call() { + assert_eq!(interpret("output('Hiya')"), Ok(None)); +} + +#[test] +fn function_context_does_not_capture_values() { + assert_eq!( + interpret( + " + x = 1 + + foo = () : any { x } + " + ), + Err(vec![Error::Validation { + error: ValidationError::VariableNotFound(Identifier::new("x")), + span: (0..0).into() + }]) + ); + + assert_eq!( + interpret( + " + x = 1 + foo = (x : int) : int { x } + foo(2) + " + ), + Ok(Some(Value::integer(2))) + ); +} + +#[test] +fn function_context_captures_functions() { + assert_eq!( + interpret( + " + bar = () : int { 2 } + foo = () : int { bar() } + foo() + " + ), + Ok(Some(Value::integer(2))) + ); +} + +#[test] +fn recursion() { + env_logger::builder().is_test(true).try_init().unwrap(); + + assert_eq!( + interpret( + " + fib = (i : int) : int { + if i <= 1 { + 1 + } else { + fib(i - 1) + fib(i - 2) + } + } + + fib(8) + " + ), + Ok(Some(Value::integer(34))) + ); +}