From 572d5a9d180c395da8b7567b6d5d070bc490406b Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 22 Jun 2024 19:44:33 -0400 Subject: [PATCH] Finish built-in function refactoring --- .../src/abstract_tree/built_in_function.rs | 382 ++++++++++++++++-- dust-lang/src/abstract_tree/expression.rs | 12 +- dust-lang/src/abstract_tree/mod.rs | 2 +- dust-lang/src/error.rs | 1 + dust-lang/src/lib.rs | 2 + dust-lang/src/parser/mod.rs | 29 +- dust-lang/src/parser/tests.rs | 10 +- dust-lang/src/value.rs | 27 +- 8 files changed, 399 insertions(+), 66 deletions(-) diff --git a/dust-lang/src/abstract_tree/built_in_function.rs b/dust-lang/src/abstract_tree/built_in_function.rs index 9067427..7f80af1 100644 --- a/dust-lang/src/abstract_tree/built_in_function.rs +++ b/dust-lang/src/abstract_tree/built_in_function.rs @@ -1,41 +1,155 @@ use std::{ array, fs::read_to_string, - io::{stdin, stdout, Write}, - slice, - sync::OnceLock, - thread, - time::Duration, + io::{self, stdin}, }; -use rayon::iter::IntoParallelIterator; use serde::{Deserialize, Serialize}; +use serde_json::from_str; use crate::{ context::Context, error::{RuntimeError, ValidationError}, identifier::Identifier, - value::{Function, ValueInner}, + value::ValueInner, Value, }; -use super::{ - AbstractNode, Block, Evaluation, Expression, Statement, Type, TypeConstructor, WithPos, -}; +use super::{AbstractNode, Evaluation, Expression, Type, TypeConstructor}; -pub enum BuiltInExpression { - Length(BuiltInFunctionCall), +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub enum BuiltInFunctionCall { + Length(BuiltInContextBinding), + ReadFile(BuiltInContextBinding), + ReadLine(BuiltInContextBinding), + Sleep(BuiltInContextBinding), + WriteLine(BuiltInContextBinding), + JsonParse(BuiltInContextBinding), } -pub struct BuiltInFunctionCall { +impl BuiltInFunctionCall { + pub fn length(argument: Expression) -> Self { + BuiltInFunctionCall::Length(BuiltInContextBinding::new(Length(Box::new(argument)))) + } + + pub fn read_file(argument: Expression) -> Self { + BuiltInFunctionCall::ReadFile(BuiltInContextBinding::new(ReadFile(Box::new(argument)))) + } + + pub fn read_line() -> Self { + BuiltInFunctionCall::ReadLine(BuiltInContextBinding::new(ReadLine)) + } + + pub fn sleep(argument: Expression) -> Self { + BuiltInFunctionCall::Sleep(BuiltInContextBinding::new(Sleep(Box::new(argument)))) + } + + pub fn write_line(argument: Expression) -> Self { + BuiltInFunctionCall::WriteLine(BuiltInContextBinding::new(WriteLine(Box::new(argument)))) + } + + pub fn json_parse(r#type: TypeConstructor, argument: Expression) -> Self { + BuiltInFunctionCall::JsonParse(BuiltInContextBinding::new(JsonParse( + r#type, + Box::new(argument), + ))) + } +} + +impl AbstractNode for BuiltInFunctionCall { + fn define_types(&self, _context: &Context) -> Result<(), ValidationError> { + match self { + BuiltInFunctionCall::Length(inner) => inner.define_types(_context), + BuiltInFunctionCall::ReadFile(inner) => inner.define_types(_context), + BuiltInFunctionCall::ReadLine(inner) => inner.define_types(_context), + BuiltInFunctionCall::Sleep(inner) => inner.define_types(_context), + BuiltInFunctionCall::WriteLine(inner) => inner.define_types(_context), + BuiltInFunctionCall::JsonParse(inner) => inner.define_types(_context), + } + } + + fn validate(&self, _context: &Context, _manage_memory: bool) -> Result<(), ValidationError> { + match self { + BuiltInFunctionCall::Length(inner) => inner.validate(_context, _manage_memory), + BuiltInFunctionCall::ReadFile(inner) => inner.validate(_context, _manage_memory), + BuiltInFunctionCall::ReadLine(inner) => inner.validate(_context, _manage_memory), + BuiltInFunctionCall::Sleep(inner) => inner.validate(_context, _manage_memory), + BuiltInFunctionCall::WriteLine(inner) => inner.validate(_context, _manage_memory), + BuiltInFunctionCall::JsonParse(inner) => inner.validate(_context, _manage_memory), + } + } + + fn evaluate( + self, + _context: &Context, + _manage_memory: bool, + ) -> Result, RuntimeError> { + match self { + BuiltInFunctionCall::Length(inner) => inner.evaluate(_context, _manage_memory), + BuiltInFunctionCall::ReadFile(inner) => inner.evaluate(_context, _manage_memory), + BuiltInFunctionCall::ReadLine(inner) => inner.evaluate(_context, _manage_memory), + BuiltInFunctionCall::Sleep(inner) => inner.evaluate(_context, _manage_memory), + BuiltInFunctionCall::WriteLine(inner) => inner.evaluate(_context, _manage_memory), + BuiltInFunctionCall::JsonParse(inner) => inner.evaluate(_context, _manage_memory), + } + } + + fn expected_type(&self, _context: &Context) -> Result, ValidationError> { + match self { + BuiltInFunctionCall::Length(inner) => inner.expected_type(_context), + BuiltInFunctionCall::ReadFile(inner) => inner.expected_type(_context), + BuiltInFunctionCall::ReadLine(inner) => inner.expected_type(_context), + BuiltInFunctionCall::Sleep(inner) => inner.expected_type(_context), + BuiltInFunctionCall::WriteLine(inner) => inner.expected_type(_context), + BuiltInFunctionCall::JsonParse(inner) => inner.expected_type(_context), + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct BuiltInContextBinding { function: F, + #[serde(skip)] context: Context, } -pub trait FunctionLogic { - fn type_parameters() -> Option>; - fn value_parameters() -> impl IntoIterator; - fn return_type() -> Type; +impl BuiltInContextBinding { + pub fn new(function: F) -> Self { + Self { + function, + context: Context::new(None), + } + } +} + +impl Eq for BuiltInContextBinding {} + +impl PartialEq for BuiltInContextBinding { + fn eq(&self, other: &Self) -> bool { + self.function == other.function + } +} + +impl PartialOrd for BuiltInContextBinding { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.function.cmp(&other.function)) + } +} + +impl Ord for BuiltInContextBinding { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.function.cmp(&other.function) + } +} + +trait FunctionLogic { + fn arguments( + self, + ) -> ( + Option>, + Option>, + ); + fn return_type(&self, context: &Context) -> Result, ValidationError>; fn call( self, context: &Context, @@ -43,14 +157,29 @@ pub trait FunctionLogic { ) -> Result, RuntimeError>; } -impl AbstractNode for BuiltInFunctionCall { +impl AbstractNode for BuiltInContextBinding +where + F: FunctionLogic + Clone, +{ fn define_types(&self, _: &Context) -> Result<(), ValidationError> { - if let Some(type_arguments) = F::type_parameters() { - for (identifier, r#type) in type_arguments { + let (type_arguments, value_arguments) = self.function.clone().arguments(); + + if let Some(type_arguments) = type_arguments { + for (identifier, constructor) in type_arguments { + let r#type = constructor.construct(&self.context)?; + self.context.set_type(identifier, r#type)?; } } + if let Some(value_arguments) = value_arguments { + for (identifier, expression) in value_arguments { + if let Some(r#type) = expression.expected_type(&self.context)? { + self.context.set_type(identifier, r#type)?; + } + } + } + Ok(()) } @@ -66,26 +195,29 @@ impl AbstractNode for BuiltInFunctionCall { self.function.call(&self.context, manage_memory) } - fn expected_type(&self, _: &Context) -> Result, ValidationError> { - Ok(Some(F::return_type())) + fn expected_type(&self, context: &Context) -> Result, ValidationError> { + self.function.return_type(context) } } -pub struct Length { - argument: Expression, -} +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Length(Box); impl FunctionLogic for Length { - fn type_parameters() -> Option> { - None::> + fn arguments( + self, + ) -> ( + Option>, + Option>, + ) { + ( + None::>, + Some([(Identifier::new("list"), *self.0)].into_iter()), + ) } - fn value_parameters() -> impl IntoIterator { - [(Identifier::new("list"))].into_iter() - } - - fn return_type() -> Type { - todo!() + fn return_type(&self, _: &Context) -> Result, ValidationError> { + Ok(Some(Type::Integer)) } fn call( @@ -93,8 +225,8 @@ impl FunctionLogic for Length { context: &Context, manage_memory: bool, ) -> Result, RuntimeError> { - let position = self.argument.position(); - let evaluation = self.argument.evaluate(context, manage_memory)?; + let position = self.0.position(); + let evaluation = self.0.evaluate(context, manage_memory)?; let value = if let Some(Evaluation::Return(value)) = evaluation { value } else { @@ -116,3 +248,183 @@ impl FunctionLogic for Length { Ok(Some(Evaluation::Return(Value::integer(list.len() as i64)))) } } + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ReadFile(Box); + +impl FunctionLogic for ReadFile { + fn arguments( + self, + ) -> ( + Option>, + Option>, + ) { + ( + None::>, + Some([(Identifier::new("path"), *self.0)].into_iter()), + ) + } + + fn return_type(&self, _: &Context) -> Result, ValidationError> { + Ok(Some(Type::String)) + } + + fn call(self, context: &Context, _: bool) -> Result, RuntimeError> { + if let Ok(Some(value)) = context.get_value(&Identifier::new("path")) { + if let ValueInner::String(path) = value.inner().as_ref() { + let file_content = read_to_string(path)?; + + return Ok(Some(Evaluation::Return(Value::string(file_content)))); + } + } + + Err(RuntimeError::ValidationFailure( + ValidationError::BuiltInFunctionFailure(self.0.position()), + )) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct ReadLine; + +impl FunctionLogic for ReadLine { + fn arguments( + self, + ) -> ( + Option>, + Option>, + ) { + ( + None::>, + None::>, + ) + } + + fn return_type(&self, _: &Context) -> Result, ValidationError> { + Ok(Some(Type::String)) + } + + fn call(self, _: &Context, _: bool) -> Result, RuntimeError> { + let user_input = io::read_to_string(stdin())?; + + Ok(Some(Evaluation::Return(Value::string(user_input)))) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Sleep(Box); + +impl FunctionLogic for Sleep { + fn arguments( + self, + ) -> ( + Option>, + Option>, + ) { + ( + None::>, + Some([(Identifier::new("milliseconds"), *self.0)].into_iter()), + ) + } + + fn return_type(&self, _: &Context) -> Result, ValidationError> { + Ok(None) + } + + fn call(self, _: &Context, _: bool) -> Result, RuntimeError> { + todo!() + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct WriteLine(Box); + +impl FunctionLogic for WriteLine { + fn arguments( + self, + ) -> ( + Option>, + Option>, + ) { + ( + None::>, + Some([(Identifier::new("message"), *self.0)].into_iter()), + ) + } + + fn return_type(&self, _: &Context) -> Result, ValidationError> { + Ok(None) + } + + fn call(self, context: &Context, _: bool) -> Result, RuntimeError> { + let message = context.get_value(&Identifier::new("message"))?; + + if let Some(message) = message { + println!("{message}"); + + Ok(None) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::BuiltInFunctionFailure(self.0.position()), + )) + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct JsonParse(TypeConstructor, Box); + +impl FunctionLogic for JsonParse { + fn arguments( + self, + ) -> ( + Option>, + Option>, + ) { + ( + Some([(Identifier::new("T"), self.0)].into_iter()), + Some([(Identifier::new("input"), *self.1)].into_iter()), + ) + } + + fn return_type(&self, context: &Context) -> Result, ValidationError> { + self.0.construct(context).map(|r#type| Some(r#type)) + } + + fn call(self, context: &Context, _: bool) -> Result, RuntimeError> { + let target_type = context.get_type(&Identifier::new("T"))?; + let input = context.get_value(&Identifier::new("input"))?; + + if let (Some(r#type), Some(value)) = (target_type, input) { + let input_string = if let ValueInner::String(string) = value.inner().as_ref() { + string + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::BuiltInFunctionFailure(self.0.position()), + )); + }; + + let parsed_value = match r#type { + Type::Any => from_str::(input_string)?, + Type::Boolean => Value::boolean(from_str::(input_string)?), + Type::Enum { .. } => todo!(), + Type::Float => Value::float(from_str::(input_string)?), + Type::Function { .. } => todo!(), + Type::Generic { .. } => todo!(), + Type::Integer => Value::integer(from_str::(input_string)?), + Type::List { .. } => todo!(), + Type::ListOf(_) => todo!(), + Type::Map => todo!(), + Type::Range => todo!(), + Type::String => Value::string(from_str::(input_string)?), + Type::Structure { .. } => todo!(), + }; + + return Ok(Some(Evaluation::Return(parsed_value))); + } + + Err(RuntimeError::ValidationFailure( + ValidationError::BuiltInFunctionFailure(self.0.position()), + )) + } +} diff --git a/dust-lang/src/abstract_tree/expression.rs b/dust-lang/src/abstract_tree/expression.rs index 7ec586a..0efc1ed 100644 --- a/dust-lang/src/abstract_tree/expression.rs +++ b/dust-lang/src/abstract_tree/expression.rs @@ -14,7 +14,7 @@ use super::{ #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Expression { As(WithPosition>), - BuiltInFunctionCall(WithPosition>), + BuiltIn(WithPosition), FunctionCall(WithPosition), Identifier(WithPosition), MapIndex(WithPosition>), @@ -35,7 +35,7 @@ impl Expression { Expression::Logic(inner) => inner.position, Expression::Math(inner) => inner.position, Expression::Value(inner) => inner.position, - Expression::BuiltInFunctionCall(inner) => inner.position, + Expression::BuiltIn(inner) => inner.position, } } } @@ -44,7 +44,7 @@ impl AbstractNode for Expression { fn define_types(&self, _context: &Context) -> Result<(), ValidationError> { match self { Expression::As(inner) => inner.node.define_types(_context), - Expression::BuiltInFunctionCall(inner) => inner.node.define_types(_context), + Expression::BuiltIn(inner) => inner.node.define_types(_context), Expression::FunctionCall(inner) => inner.node.define_types(_context), Expression::Identifier(_) => Ok(()), Expression::MapIndex(inner) => inner.node.define_types(_context), @@ -58,7 +58,7 @@ impl AbstractNode for Expression { fn validate(&self, context: &Context, manage_memory: bool) -> Result<(), ValidationError> { match self { Expression::As(r#as) => r#as.node.validate(context, manage_memory), - Expression::BuiltInFunctionCall(built_in_function_call) => { + Expression::BuiltIn(built_in_function_call) => { built_in_function_call.node.validate(context, manage_memory) } Expression::FunctionCall(function_call) => { @@ -117,7 +117,7 @@ impl AbstractNode for Expression { Expression::Logic(logic) => logic.node.evaluate(context, manage_memory), Expression::Math(math) => math.node.evaluate(context, manage_memory), Expression::Value(value_node) => value_node.node.evaluate(context, manage_memory), - Expression::BuiltInFunctionCall(built_in_function_call) => { + Expression::BuiltIn(built_in_function_call) => { built_in_function_call.node.evaluate(context, manage_memory) } } @@ -144,7 +144,7 @@ impl AbstractNode for Expression { Expression::Logic(logic) => logic.node.expected_type(_context), Expression::Math(math) => math.node.expected_type(_context), Expression::Value(value_node) => value_node.node.expected_type(_context), - Expression::BuiltInFunctionCall(built_in_function_call) => { + Expression::BuiltIn(built_in_function_call) => { built_in_function_call.node.expected_type(_context) } } diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index d7b5a42..dd66215 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -29,7 +29,7 @@ pub use self::{ assignment::{Assignment, AssignmentOperator}, async_block::AsyncBlock, block::Block, - built_in_function::FunctionLogic, + built_in_function::BuiltInFunctionCall, enum_declaration::{EnumDeclaration, EnumVariant}, expression::Expression, function_call::FunctionCall, diff --git a/dust-lang/src/error.rs b/dust-lang/src/error.rs index 279b983..929b628 100644 --- a/dust-lang/src/error.rs +++ b/dust-lang/src/error.rs @@ -103,6 +103,7 @@ impl PartialEq for RuntimeError { #[derive(Debug, PartialEq)] pub enum ValidationError { + BuiltInFunctionFailure(SourcePosition), CannotAssignToNone(SourcePosition), CannotIndex { r#type: Type, diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 13911d5..31c0074 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -470,6 +470,8 @@ impl InterpreterError { } } ValidationError::EnumVariantNotFound { .. } => todo!(), + ValidationError::ExpectedList { .. } => todo!(), + ValidationError::BuiltInFunctionFailure(_) => todo!(), } } diff --git a/dust-lang/src/parser/mod.rs b/dust-lang/src/parser/mod.rs index bd3751b..aaa77fd 100644 --- a/dust-lang/src/parser/mod.rs +++ b/dust-lang/src/parser/mod.rs @@ -13,6 +13,7 @@ use crate::{ }; use self::{ + built_in_function::BuiltInFunctionCall, enum_declaration::EnumVariant, type_constructor::{RawTypeConstructor, TypeInvokationConstructor}, }; @@ -339,46 +340,42 @@ pub fn parser<'src>( _built_in_function(Keyword::Length) .ignore_then(expression.clone()) .map_with(|argument, state| { - Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::Length(argument)) - .with_position(state.span()), + Expression::BuiltIn( + BuiltInFunctionCall::length(argument).with_position(state.span()), ) }), _built_in_function(Keyword::ReadFile) .ignore_then(expression.clone()) .map_with(|argument, state| { - Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::ReadFile(argument)) - .with_position(state.span()), + Expression::BuiltIn( + BuiltInFunctionCall::read_file(argument).with_position(state.span()), ) }), _built_in_function(Keyword::ReadLine).map_with(|_, state| { - Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::ReadLine).with_position(state.span()), + Expression::BuiltIn( + BuiltInFunctionCall::read_line().with_position(state.span()), ) }), _built_in_function(Keyword::Sleep) .ignore_then(expression.clone()) .map_with(|argument, state| { - Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::Sleep(argument)) - .with_position(state.span()), + Expression::BuiltIn( + BuiltInFunctionCall::sleep(argument).with_position(state.span()), ) }), _built_in_function(Keyword::WriteLine) .ignore_then(expression.clone()) .map_with(|argument, state| { - Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::WriteLine(argument)) - .with_position(state.span()), + Expression::BuiltIn( + BuiltInFunctionCall::write_line(argument).with_position(state.span()), ) }), _built_in_function(Keyword::JsonParse) .ignore_then(type_constructor.clone()) .then(expression.clone()) .map_with(|(constructor, argument), state| { - Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::JsonParse(constructor, argument)) + Expression::BuiltIn( + BuiltInFunctionCall::json_parse(constructor, argument) .with_position(state.span()), ) }), diff --git a/dust-lang/src/parser/tests.rs b/dust-lang/src/parser/tests.rs index 71c4d5a..8fd8215 100644 --- a/dust-lang/src/parser/tests.rs +++ b/dust-lang/src/parser/tests.rs @@ -240,8 +240,8 @@ fn built_in_function() { assert_eq!( statements[0], - Statement::Expression(Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::ReadLine).with_position((0, 13)) + Statement::Expression(Expression::BuiltIn( + BuiltInFunctionCall::read_line().with_position((0, 13)) )) ); } @@ -262,10 +262,10 @@ fn built_in_function_with_arg() { assert_eq!( statements[0], - Statement::Expression(Expression::BuiltInFunctionCall( - Box::new(BuiltInFunctionCall::WriteLine(Expression::Value( + Statement::Expression(Expression::BuiltIn( + BuiltInFunctionCall::write_line(Expression::Value( ValueNode::String("hiya".to_string()).with_position((15, 21)) - ))) + )) .with_position((0, 21)) )) ); diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 2dcd773..7f00c28 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -787,18 +787,39 @@ impl Eq for Function {} impl PartialEq for Function { fn eq(&self, other: &Self) -> bool { - todo!() + self.type_parameters == other.type_parameters + && self.value_parameters == other.value_parameters + && self.return_type == other.return_type + && self.body == other.body } } impl PartialOrd for Function { fn partial_cmp(&self, other: &Self) -> Option { - todo!() + Some(self.cmp(other)) } } impl Ord for Function { fn cmp(&self, other: &Self) -> Ordering { - todo!() + let type_params_cmp = self.type_parameters.cmp(&other.type_parameters); + + if type_params_cmp.is_eq() { + let value_params_cmp = self.value_parameters.cmp(&other.value_parameters); + + if value_params_cmp.is_eq() { + let return_cmp = self.return_type.cmp(&other.return_type); + + if return_cmp.is_eq() { + self.body.cmp(&other.body) + } else { + return_cmp + } + } else { + value_params_cmp + } + } else { + type_params_cmp + } } }