diff --git a/src/abstract_tree/block.rs b/src/abstract_tree/block.rs index 5f19584..1c855e3 100644 --- a/src/abstract_tree/block.rs +++ b/src/abstract_tree/block.rs @@ -66,8 +66,8 @@ mod tests { ]); assert_eq!( - block.run(&Context::new()), - Ok(Action::Return(Value::integer(42))) + block.run(&Context::new()).unwrap(), + Action::Return(Value::integer(42)) ) } diff --git a/src/abstract_tree/if_else.rs b/src/abstract_tree/if_else.rs index 078bddc..215d4c9 100644 --- a/src/abstract_tree/if_else.rs +++ b/src/abstract_tree/if_else.rs @@ -105,8 +105,9 @@ mod tests { .with_position((0, 0))]), None ) - .run(&Context::new()), - Ok(Action::Return(Value::string("foo".to_string()))) + .run(&Context::new()) + .unwrap(), + Action::Return(Value::string("foo".to_string())) ) } @@ -124,8 +125,9 @@ mod tests { )) .with_position((0, 0))])) ) - .run(&Context::new()), - Ok(Action::Return(Value::string("bar".to_string()))) + .run(&Context::new()) + .unwrap(), + Action::Return(Value::string("bar".to_string())) ) } } diff --git a/src/abstract_tree/item.rs b/src/abstract_tree/item.rs deleted file mode 100644 index 06ed81f..0000000 --- a/src/abstract_tree/item.rs +++ /dev/null @@ -1,39 +0,0 @@ -use super::{AbstractTree, Expression, Positioned, Statement}; - -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub enum Item { - Expression(Positioned), - Statement(Positioned), -} - -impl Item { - pub fn position(&self) -> &(usize, usize) { - match self { - Item::Expression((_, position)) => position, - Item::Statement((_, position)) => position, - } - } -} - -impl AbstractTree for Item { - fn expected_type( - &self, - context: &crate::context::Context, - ) -> Result { - todo!() - } - - fn validate( - &self, - context: &crate::context::Context, - ) -> Result<(), crate::error::ValidationError> { - todo!() - } - - fn run( - self, - context: &crate::context::Context, - ) -> Result { - todo!() - } -} diff --git a/src/abstract_tree/logic.rs b/src/abstract_tree/logic.rs index 32adbef..86ebb02 100644 --- a/src/abstract_tree/logic.rs +++ b/src/abstract_tree/logic.rs @@ -47,25 +47,36 @@ impl AbstractTree for Logic { Ok(()) } Logic::And(left, right) | Logic::Or(left, right) => { - let left = left.node.expected_type(context)?; - let right = right.node.expected_type(context)?; + let left_type = left.node.expected_type(context)?; + let right_type = right.node.expected_type(context)?; - if let (Type::Boolean, Type::Boolean) = (left, right) { - Ok(()) + if let Type::Boolean = left_type { } else { - Err(ValidationError::ExpectedBoolean { - actual: todo!(), - position: todo!(), - }) + return Err(ValidationError::ExpectedBoolean { + actual: left_type, + position: left.position, + }); } + + if let Type::Boolean = right_type { + } else { + return Err(ValidationError::ExpectedBoolean { + actual: right_type, + position: right.position, + }); + } + + Ok(()) } Logic::Not(expression) => { - if let Type::Boolean = expression.node.expected_type(context)? { + let expression_type = expression.node.expected_type(context)?; + + if let Type::Boolean = expression_type { Ok(()) } else { Err(ValidationError::ExpectedBoolean { - actual: todo!(), - position: todo!(), + actual: expression_type, + position: expression.position, }) } } diff --git a/src/abstract_tree/loop.rs b/src/abstract_tree/loop.rs index 2b3223c..b17b3fc 100644 --- a/src/abstract_tree/loop.rs +++ b/src/abstract_tree/loop.rs @@ -79,14 +79,16 @@ mod tests { #[test] fn basic_loop() { - let result = Loop::new(vec![Statement::Break.with_position((0, 0))]).run(&Context::new()); + let action = Loop::new(vec![Statement::Break.with_position((0, 0))]) + .run(&Context::new()) + .unwrap(); - assert_eq!(result, Ok(Action::Break)) + assert_eq!(action, Action::Break) } #[test] fn complex_loop() { - let result = Block::new(vec![ + let action = Block::new(vec![ Statement::Assignment(Assignment::new( Identifier::new("i"), None, @@ -116,8 +118,9 @@ mod tests { Statement::Expression(Expression::Identifier(Identifier::new("i"))) .with_position((0, 0)), ]) - .run(&Context::new()); + .run(&Context::new()) + .unwrap(); - assert_eq!(result, Ok(Action::Return(Value::integer(3)))) + assert_eq!(action, Action::Return(Value::integer(3))) } } diff --git a/src/abstract_tree/while.rs b/src/abstract_tree/while.rs index cbb5cae..f64d341 100644 --- a/src/abstract_tree/while.rs +++ b/src/abstract_tree/while.rs @@ -75,7 +75,7 @@ mod tests { #[test] fn simple_while_loop() { - let result = Statement::Block(Block::new(vec![ + let action = Statement::Block(Block::new(vec![ Statement::Assignment(Assignment::new( Identifier::new("i"), None, @@ -103,8 +103,9 @@ mod tests { Statement::Expression(Expression::Identifier(Identifier::new("i"))) .with_position((0, 0)), ])) - .run(&Context::new()); + .run(&Context::new()) + .unwrap(); - assert_eq!(result, Ok(Action::Return(Value::integer(3)))) + assert_eq!(action, Action::Return(Value::integer(3))) } } diff --git a/src/error.rs b/src/error.rs index 17acd1e..e8335b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use std::{ops::Range, sync::PoisonError}; +use std::{io, ops::Range, sync::PoisonError}; use ariadne::{Label, ReportBuilder}; use chumsky::{prelude::Rich, span::Span}; @@ -62,6 +62,7 @@ impl Error { "The interpreter failed to catch this error during validation.", )); } + RuntimeError::Io(_) => todo!(), }, Error::Validation { error, position } => match error { ValidationError::ExpectedBoolean { actual, position } => { @@ -101,7 +102,7 @@ impl Error { ValidationError::CannotIndex(_) => todo!(), ValidationError::CannotIndexWith(_, _) => todo!(), ValidationError::InterpreterExpectedReturn => todo!(), - ValidationError::ExpectedFunction { actual, position } => todo!(), + ValidationError::ExpectedFunction { .. } => todo!(), ValidationError::ExpectedValue => todo!(), }, } @@ -128,8 +129,9 @@ impl<'src> From>> for Error { } } -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum RuntimeError { + Io(io::Error), RwLockPoison(RwLockPoisonError), ValidationFailure(ValidationError), } @@ -146,6 +148,25 @@ impl From for RuntimeError { } } +impl From for RuntimeError { + fn from(error: io::Error) -> Self { + RuntimeError::Io(error) + } +} + +impl PartialEq for RuntimeError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (RuntimeError::Io(_), RuntimeError::Io(_)) => false, + (RuntimeError::RwLockPoison(_), RuntimeError::RwLockPoison(_)) => true, + (RuntimeError::ValidationFailure(left), RuntimeError::ValidationFailure(right)) => { + left == right + } + _ => false, + } + } +} + #[derive(Debug, PartialEq)] pub enum ValidationError { CannotIndex(Type), diff --git a/src/value.rs b/src/value.rs index 1de6bac..ab4e74f 100644 --- a/src/value.rs +++ b/src/value.rs @@ -2,6 +2,7 @@ use std::{ cmp::Ordering, collections::BTreeMap, fmt::{self, Display, Formatter}, + io::stdin, ops::Range, sync::{Arc, OnceLock}, }; @@ -322,6 +323,7 @@ pub struct ParsedFunction { #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum BuiltInFunction { Output, + ReadLine, } impl BuiltInFunction { @@ -333,12 +335,24 @@ impl BuiltInFunction { .clone() } + pub fn read_line() -> Value { + static READ_LINE: OnceLock = OnceLock::new(); + + READ_LINE + .get_or_init(|| Value::built_in_function(BuiltInFunction::ReadLine)) + .clone() + } + pub fn r#type(&self) -> Type { match self { BuiltInFunction::Output => Type::Function { parameter_types: vec![Type::Any], return_type: Box::new(Type::None), }, + BuiltInFunction::ReadLine => Type::Function { + parameter_types: Vec::with_capacity(0), + return_type: Box::new(Type::String), + }, } } @@ -349,6 +363,13 @@ impl BuiltInFunction { Ok(Action::None) } + BuiltInFunction::ReadLine => { + let mut input = String::new(); + + stdin().read_line(&mut input)?; + + Ok(Action::Return(Value::string(input))) + } } } } @@ -357,6 +378,23 @@ impl Display for BuiltInFunction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { BuiltInFunction::Output => write!(f, "(to_output : any) : none rust_magic();"), + BuiltInFunction::ReadLine => todo!(), } } } + +pub enum BuiltInValue { + Io, +} + +impl BuiltInValue { + pub fn io() -> Value { + static IO: OnceLock = OnceLock::new(); + + let mut properties = BTreeMap::new(); + + properties.insert(Identifier::new("read_line"), BuiltInFunction::read_line()); + + IO.get_or_init(|| Value::map(properties)).clone() + } +} diff --git a/tests/expressions.rs b/tests/expressions.rs index 70bd8ad..0f16a98 100644 --- a/tests/expressions.rs +++ b/tests/expressions.rs @@ -2,26 +2,26 @@ use dust_lang::*; #[test] fn logic() { - assert_eq!(interpret("1 == 1"), Ok(Some(Value::boolean(true)))); + assert_eq!(interpret("1 == 1").unwrap(), Some(Value::boolean(true))); assert_eq!( - interpret("('42' == '42') && (42 != 0)"), - Ok(Some(Value::boolean(true))) + interpret("('42' == '42') && (42 != 0)").unwrap(), + Some(Value::boolean(true)) ); } #[test] fn math() { - assert_eq!(interpret("1 + 1"), Ok(Some(Value::integer(2)))); + assert_eq!(interpret("1 + 1").unwrap(), Some(Value::integer(2))); assert_eq!( - interpret("2 * (21 + 19 + 1 * 2) / 2"), - Ok(Some(Value::integer(42))) + interpret("2 * (21 + 19 + 1 * 2) / 2").unwrap(), + Some(Value::integer(42)) ); } #[test] fn list_index() { assert_eq!( - interpret("foo = [1, 2, 3]; foo.2"), - Ok(Some(Value::integer(3))) + interpret("foo = [1, 2, 3]; foo.2").unwrap(), + Some(Value::integer(3)) ); }