From a52e78150eaf70886a4742f707e1e760a7b7f570 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 8 Aug 2024 13:49:40 -0400 Subject: [PATCH] Add lexing and parsing for I/O built-in functions; Refactor built-in function parsing --- dust-lang/src/built_in_function.rs | 55 +++++++++++++-- dust-lang/src/lex.rs | 2 + dust-lang/src/parse.rs | 110 +++++++++++------------------ dust-lang/src/token.rs | 10 ++- dust-lang/src/value.rs | 12 ---- dust-lang/src/vm.rs | 4 +- 6 files changed, 101 insertions(+), 92 deletions(-) diff --git a/dust-lang/src/built_in_function.rs b/dust-lang/src/built_in_function.rs index 8365e46..bcb703f 100644 --- a/dust-lang/src/built_in_function.rs +++ b/dust-lang/src/built_in_function.rs @@ -1,4 +1,7 @@ -use std::fmt::{self, Display, Formatter}; +use std::{ + fmt::{self, Display, Formatter}, + io::{self, stdin}, +}; use serde::{Deserialize, Serialize}; @@ -6,9 +9,16 @@ use crate::{Type, Value}; #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum BuiltInFunction { + // Integer and float tools IsEven, IsOdd, + + // List tools Length, + + // I/O + ReadLine, + WriteLine, } impl BuiltInFunction { @@ -17,6 +27,8 @@ impl BuiltInFunction { BuiltInFunction::IsEven => "is_even", BuiltInFunction::IsOdd => "is_odd", BuiltInFunction::Length => "length", + BuiltInFunction::ReadLine => "read_line", + BuiltInFunction::WriteLine => "write_line", } } @@ -24,13 +36,13 @@ impl BuiltInFunction { &self, _type_arguments: Option>, value_arguments: Option>, - ) -> Result { + ) -> Result, BuiltInFunctionError> { match self { BuiltInFunction::IsEven => { if let Some(value_arguments) = value_arguments { if value_arguments.len() == 1 { if let Some(integer) = value_arguments[0].as_integer() { - Ok(Value::boolean(integer % 2 == 0)) + Ok(Some(Value::boolean(integer % 2 == 0))) } else { Err(BuiltInFunctionError::ExpectedInteger) } @@ -45,7 +57,7 @@ impl BuiltInFunction { if let Some(value_arguments) = value_arguments { if value_arguments.len() == 1 { if let Some(integer) = value_arguments[0].as_integer() { - Ok(Value::boolean(integer % 2 != 0)) + Ok(Some(Value::boolean(integer % 2 != 0))) } else { Err(BuiltInFunctionError::ExpectedInteger) } @@ -60,7 +72,7 @@ impl BuiltInFunction { if let Some(value_arguments) = value_arguments { if value_arguments.len() == 1 { if let Some(list) = value_arguments[0].as_list() { - Ok(Value::integer(list.len() as i64)) + Ok(Some(Value::integer(list.len() as i64))) } else { Err(BuiltInFunctionError::ExpectedInteger) } @@ -71,6 +83,30 @@ impl BuiltInFunction { Err(BuiltInFunctionError::WrongNumberOfValueArguments) } } + BuiltInFunction::ReadLine => { + if value_arguments.is_none() { + let mut input = String::new(); + + stdin().read_line(&mut input)?; + + Ok(Some(Value::string(input))) + } else { + Err(BuiltInFunctionError::WrongNumberOfValueArguments) + } + } + BuiltInFunction::WriteLine => { + if let Some(value_arguments) = value_arguments { + if value_arguments.len() == 1 { + println!("{}", value_arguments[0]); + + Ok(None) + } else { + Err(BuiltInFunctionError::WrongNumberOfValueArguments) + } + } else { + Err(BuiltInFunctionError::WrongNumberOfValueArguments) + } + } } } @@ -79,6 +115,8 @@ impl BuiltInFunction { BuiltInFunction::IsEven => Some(Type::Boolean), BuiltInFunction::IsOdd => Some(Type::Boolean), BuiltInFunction::Length => Some(Type::Integer), + BuiltInFunction::ReadLine => Some(Type::String), + BuiltInFunction::WriteLine => None, } } } @@ -92,5 +130,12 @@ impl Display for BuiltInFunction { #[derive(Debug, Clone, PartialEq)] pub enum BuiltInFunctionError { ExpectedInteger, + Io(io::ErrorKind), WrongNumberOfValueArguments, } + +impl From for BuiltInFunctionError { + fn from(error: io::Error) -> Self { + Self::Io(error.kind()) + } +} diff --git a/dust-lang/src/lex.rs b/dust-lang/src/lex.rs index 8e871e8..25088ba 100644 --- a/dust-lang/src/lex.rs +++ b/dust-lang/src/lex.rs @@ -250,6 +250,8 @@ impl<'a> Lexer<'a> { "is_even" => Token::IsEven, "is_odd" => Token::IsOdd, "length" => Token::Length, + "read_line" => Token::ReadLine, + "write_line" => Token::WriteLine, _ => Token::Identifier(Identifier::new(string)), }; diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index 252c477..fc9e949 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -258,7 +258,19 @@ impl<'src> Parser<'src> { } } } - (Token::IsEven, left_span) => { + ( + Token::IsEven | Token::IsOdd | Token::Length | Token::ReadLine | Token::WriteLine, + left_span, + ) => { + let function = match self.current.0 { + Token::IsEven => BuiltInFunction::IsEven, + Token::IsOdd => BuiltInFunction::IsOdd, + Token::Length => BuiltInFunction::Length, + Token::ReadLine => BuiltInFunction::ReadLine, + Token::WriteLine => BuiltInFunction::WriteLine, + _ => unreachable!(), + }; + self.next_token()?; if let (Token::LeftParenthesis, _) = self.current { @@ -270,80 +282,38 @@ impl<'src> Parser<'src> { }); } - if let (Token::RightParenthesis, _) = self.current { - self.next_token()?; - } else { - return Err(ParseError::ExpectedClosingParenthesis { - actual: self.current.0.clone(), - span: self.current.1, - }); + let mut value_arguments: Option> = None; + + loop { + if let (Token::RightParenthesis, _) = self.current { + self.next_token()?; + break; + } + + if let (Token::Comma, _) = self.current { + self.next_token()?; + continue; + } + + if let Ok(node) = self.parse_node(0) { + if let Some(ref mut arguments) = value_arguments { + arguments.push(node); + } else { + value_arguments = Some(vec![node]); + } + } else { + return Err(ParseError::ExpectedClosingParenthesis { + actual: self.current.0.clone(), + span: self.current.1, + }); + } } Ok(Node::new( Statement::BuiltInFunctionCall { - function: BuiltInFunction::IsEven, + function, type_arguments: None, - value_arguments: None, - }, - left_span, - )) - } - (Token::IsOdd, left_span) => { - self.next_token()?; - - if let (Token::LeftParenthesis, _) = self.current { - self.next_token()?; - } else { - return Err(ParseError::ExpectedOpeningParenthesis { - actual: self.current.0.clone(), - span: self.current.1, - }); - } - - if let (Token::RightParenthesis, _) = self.current { - self.next_token()?; - } else { - return Err(ParseError::ExpectedClosingParenthesis { - actual: self.current.0.clone(), - span: self.current.1, - }); - } - - Ok(Node::new( - Statement::BuiltInFunctionCall { - function: BuiltInFunction::IsOdd, - type_arguments: None, - value_arguments: None, - }, - left_span, - )) - } - (Token::Length, left_span) => { - self.next_token()?; - - if let (Token::LeftParenthesis, _) = self.current { - self.next_token()?; - } else { - return Err(ParseError::ExpectedOpeningParenthesis { - actual: self.current.0.clone(), - span: self.current.1, - }); - } - - if let (Token::RightParenthesis, _) = self.current { - self.next_token()?; - } else { - return Err(ParseError::ExpectedClosingParenthesis { - actual: self.current.0.clone(), - span: self.current.1, - }); - } - - Ok(Node::new( - Statement::BuiltInFunctionCall { - function: BuiltInFunction::Length, - type_arguments: None, - value_arguments: None, + value_arguments, }, left_span, )) diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index f27c79e..9a897ee 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -20,17 +20,19 @@ pub enum Token { IsEven, IsOdd, Length, + ReadLine, + WriteLine, // Symbols Comma, Dot, Equal, - Plus, - Star, LeftParenthesis, - RightParenthesis, LeftSquareBrace, + Plus, + RightParenthesis, RightSquareBrace, + Star, } impl Display for Token { @@ -45,6 +47,8 @@ impl Display for Token { Token::IsEven => write!(f, "is_even"), Token::IsOdd => write!(f, "is_odd"), Token::Length => write!(f, "length"), + Token::ReadLine => write!(f, "read_line"), + Token::WriteLine => write!(f, "write_line"), Token::Comma => write!(f, ","), Token::Dot => write!(f, "."), Token::Equal => write!(f, "="), diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index a713b7f..c610a50 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -527,18 +527,6 @@ impl ValueInner { } } -pub trait ValueProperties<'a> {} - -pub struct IntegerProperties<'a>(&'a Value); - -impl<'a> IntegerProperties<'a> { - pub fn is_even(&self) -> bool { - self.0.as_integer().unwrap() % 2 == 0 - } -} - -impl<'a> ValueProperties<'a> for IntegerProperties<'a> {} - impl Eq for ValueInner {} impl PartialOrd for ValueInner { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 8c695e5..07a2511 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -114,7 +114,7 @@ impl Vm { }; let function_call_return = function.call(None, values)?; - Ok(Some(function_call_return)) + Ok(function_call_return) } Statement::Constant(value) => Ok(Some(value.clone())), Statement::FunctionCall { @@ -226,7 +226,7 @@ impl Vm { let function_call_return = function.call(None, Some(value_arguments))?; - return Ok(Some(function_call_return)); + return Ok(function_call_return); } Err(VmError::ExpectedIdentifierOrInteger {