From c8dfbda44767c7721a094989744e8cbb828c1efa Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 25 May 2024 11:48:43 -0400 Subject: [PATCH] Add fs.read_file to standard library --- .../abstract_tree/built_in_function_call.rs | 24 +++++++++++++++++++ dust-lang/src/abstract_tree/value_node.rs | 2 ++ dust-lang/src/lexer.rs | 3 +++ dust-lang/src/lib.rs | 16 ++++++++++++- dust-lang/src/parser.rs | 8 +++++++ std/fs.ds | 5 ++++ 6 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 std/fs.ds diff --git a/dust-lang/src/abstract_tree/built_in_function_call.rs b/dust-lang/src/abstract_tree/built_in_function_call.rs index 0c5145b..64cb79e 100644 --- a/dust-lang/src/abstract_tree/built_in_function_call.rs +++ b/dust-lang/src/abstract_tree/built_in_function_call.rs @@ -1,4 +1,5 @@ use std::{ + fs::read_to_string, io::{stdin, stdout, Write}, thread, time::Duration, @@ -16,6 +17,7 @@ use super::{AbstractNode, Expression}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum BuiltInFunctionCall { + ReadFile(Expression), ReadLine, Sleep(Expression), WriteLine(Expression), @@ -24,6 +26,7 @@ pub enum BuiltInFunctionCall { impl AbstractNode for BuiltInFunctionCall { fn expected_type(&self, _context: &mut Context) -> Result { match self { + BuiltInFunctionCall::ReadFile(_) => Ok(Type::String), BuiltInFunctionCall::ReadLine => Ok(Type::String), BuiltInFunctionCall::Sleep(_) => Ok(Type::None), BuiltInFunctionCall::WriteLine(_) => Ok(Type::None), @@ -36,6 +39,9 @@ impl AbstractNode for BuiltInFunctionCall { _manage_memory: bool, ) -> Result<(), ValidationError> { match self { + BuiltInFunctionCall::ReadFile(expression) => { + expression.validate(_context, _manage_memory) + } BuiltInFunctionCall::ReadLine => Ok(()), BuiltInFunctionCall::Sleep(expression) => expression.validate(_context, _manage_memory), BuiltInFunctionCall::WriteLine(expression) => { @@ -46,6 +52,24 @@ impl AbstractNode for BuiltInFunctionCall { fn run(self, context: &mut Context, _manage_memory: bool) -> Result { match self { + BuiltInFunctionCall::ReadFile(expression) => { + let action = expression.clone().run(context, _manage_memory)?; + let value = if let Action::Return(value) = action { + value + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::InterpreterExpectedReturn(expression.position()), + )); + }; + + let file_contents = if let ValueInner::String(path) = value.inner().as_ref() { + read_to_string(path)? + } else { + String::with_capacity(0) + }; + + Ok(Action::Return(Value::string(file_contents))) + } BuiltInFunctionCall::ReadLine => { let mut buffer = String::new(); diff --git a/dust-lang/src/abstract_tree/value_node.rs b/dust-lang/src/abstract_tree/value_node.rs index ef4e314..760bf86 100644 --- a/dust-lang/src/abstract_tree/value_node.rs +++ b/dust-lang/src/abstract_tree/value_node.rs @@ -94,6 +94,8 @@ impl AbstractNode for ValueNode { fn validate(&self, context: &mut Context, _manage_memory: bool) -> Result<(), ValidationError> { if let ValueNode::Map(map_assignments) = self { for (_identifier, r#type, expression) in map_assignments { + expression.validate(context, _manage_memory)?; + if let Some(expected_type) = r#type { let actual_type = expression.expected_type(context)?; diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 446546e..dd4d849 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -52,6 +52,7 @@ pub enum Keyword { Map, None, Range, + ReadFile, ReadLine, Sleep, Struct, @@ -84,6 +85,7 @@ impl Display for Keyword { Keyword::Loop => write!(f, "loop"), Keyword::While => write!(f, "while"), Keyword::Type => write!(f, "type"), + Keyword::ReadFile => write!(f, "READ_FILE"), Keyword::ReadLine => write!(f, "READ_LINE"), Keyword::Sleep => write!(f, "SLEEP"), Keyword::WriteLine => write!(f, "WRITE_LINE"), @@ -276,6 +278,7 @@ pub fn lexer<'src>() -> impl Parser< "type" => Token::Keyword(Keyword::Type), "loop" => Token::Keyword(Keyword::Loop), "while" => Token::Keyword(Keyword::While), + "READ_FILE" => Token::Keyword(Keyword::ReadFile), "READ_LINE" => Token::Keyword(Keyword::ReadLine), "SLEEP" => Token::Keyword(Keyword::Sleep), "WRITE_LINE" => Token::Keyword(Keyword::WriteLine), diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 5632d0c..ac3c5f8 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -77,7 +77,11 @@ impl<'a> Interpreter<'a> { } pub fn load_std(&mut self) -> Result<(), InterpreterError> { - let std_sources: [(Arc, Arc); 2] = [ + let std_sources: [(Arc, Arc); 3] = [ + ( + Arc::from("std/fs.ds"), + Arc::from(include_str!("../../std/fs.ds")), + ), ( Arc::from("std/io.ds"), Arc::from(include_str!("../../std/io.ds")), @@ -369,3 +373,13 @@ impl InterpreterError { reports } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn load_standard_library() { + Interpreter::new(Context::new(None)).load_std().unwrap(); + } +} diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 547e297..d28f2cb 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -263,6 +263,14 @@ pub fn parser<'src>( ); let built_in_function_call = choice(( + just(Token::Keyword(Keyword::ReadFile)) + .ignore_then(expression.clone()) + .map_with(|argument, state| { + Expression::BuiltInFunctionCall( + Box::new(BuiltInFunctionCall::ReadFile(argument)) + .with_position(state.span()), + ) + }), just(Token::Keyword(Keyword::ReadLine)).map_with(|_, state| { Expression::BuiltInFunctionCall( Box::new(BuiltInFunctionCall::ReadLine).with_position(state.span()), diff --git a/std/fs.ds b/std/fs.ds new file mode 100644 index 0000000..e170502 --- /dev/null +++ b/std/fs.ds @@ -0,0 +1,5 @@ +fs = { + read_file = fn (path: str) str { + READ_FILE path + } +}