diff --git a/src/lexer.rs b/src/lexer.rs index d7a8fca..00a19ae 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -131,6 +131,7 @@ pub fn lexer<'src>() -> impl Parser< .or_not() .then(text::int(10)) .then(just('.').then(text::digits(10))) + .then(just('e').then(text::digits(10)).or_not()) .to_slice() .map(|text: &str| Token::Float(text.parse().unwrap())); diff --git a/src/value.rs b/src/value.rs index bb2f978..cc3a5f3 100644 --- a/src/value.rs +++ b/src/value.rs @@ -143,22 +143,54 @@ impl Value { } pub fn add(&self, other: &Self) -> Result { - if let (ValueInner::Integer(left), ValueInner::Integer(right)) = - (self.inner().as_ref(), other.inner().as_ref()) - { - Ok(Value::integer(left + right)) - } else { - Err(ValidationError::ExpectedIntegerOrFloat) + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Integer(left), ValueInner::Integer(right)) => { + let sum = left.saturating_add(*right); + + Ok(Value::integer(sum)) + } + (ValueInner::Float(left), ValueInner::Float(right)) => { + let sum = left + right; + + Ok(Value::float(sum)) + } + (ValueInner::Float(left), ValueInner::Integer(right)) => { + let sum = left + *right as f64; + + Ok(Value::float(sum)) + } + (ValueInner::Integer(left), ValueInner::Float(right)) => { + let sum = *left as f64 + right; + + Ok(Value::float(sum)) + } + _ => Err(ValidationError::ExpectedIntegerOrFloat), } } pub fn subtract(&self, other: &Self) -> Result { - if let (ValueInner::Integer(left), ValueInner::Integer(right)) = - (self.inner().as_ref(), other.inner().as_ref()) - { - Ok(Value::integer(left - right)) - } else { - Err(ValidationError::ExpectedIntegerOrFloat) + match (self.inner().as_ref(), other.inner().as_ref()) { + (ValueInner::Integer(left), ValueInner::Integer(right)) => { + let sum = left.saturating_sub(*right); + + Ok(Value::integer(sum)) + } + (ValueInner::Float(left), ValueInner::Float(right)) => { + let sum = left - right; + + Ok(Value::float(sum)) + } + (ValueInner::Float(left), ValueInner::Integer(right)) => { + let sum = left - *right as f64; + + Ok(Value::float(sum)) + } + (ValueInner::Integer(left), ValueInner::Float(right)) => { + let sum = *left as f64 - right; + + Ok(Value::float(sum)) + } + _ => Err(ValidationError::ExpectedIntegerOrFloat), } } } diff --git a/tests/values.rs b/tests/values.rs new file mode 100644 index 0000000..a0b8698 --- /dev/null +++ b/tests/values.rs @@ -0,0 +1,151 @@ +use std::collections::BTreeMap; + +use dust_lang::{ + abstract_tree::{Identifier, Type}, + error::{Error, TypeCheckError, ValidationError}, + *, +}; + +#[test] +fn none() { + assert_eq!(interpret("x = 9"), Ok(None)); + assert_eq!(interpret("x = 1 + 1"), Ok(None)); +} + +#[test] +fn integer() { + assert_eq!(interpret("1"), Ok(Some(Value::integer(1)))); + assert_eq!(interpret("123"), Ok(Some(Value::integer(123)))); + assert_eq!(interpret("-666"), Ok(Some(Value::integer(-666)))); +} + +#[test] +fn integer_saturation() { + assert_eq!( + interpret("9223372036854775807 + 1"), + Ok(Some(Value::integer(i64::MAX))) + ); + assert_eq!( + interpret("-9223372036854775808 - 1"), + Ok(Some(Value::integer(i64::MIN))) + ); +} + +#[test] +fn float() { + assert_eq!( + interpret("1.7976931348623157e308"), + Ok(Some(Value::float(f64::MAX))) + ); + assert_eq!( + interpret("-1.7976931348623157e308"), + Ok(Some(Value::float(f64::MIN))) + ); +} + +#[test] +fn float_saturation() { + assert_eq!( + interpret("1.7976931348623157e308 + 1"), + Ok(Some(Value::float(f64::MAX))) + ); + assert_eq!( + interpret("-1.7976931348623157e308 - 1"), + Ok(Some(Value::float(f64::MIN))) + ); +} + +#[test] +fn string() { + assert_eq!( + interpret("\"one\""), + Ok(Some(Value::string("one".to_string()))) + ); + assert_eq!( + interpret("'one'"), + Ok(Some(Value::string("one".to_string()))) + ); + assert_eq!( + interpret("`one`"), + Ok(Some(Value::string("one".to_string()))) + ); + assert_eq!( + interpret("`'one'`"), + Ok(Some(Value::string("'one'".to_string()))) + ); + assert_eq!( + interpret("'`one`'"), + Ok(Some(Value::string("`one`".to_string()))) + ); + assert_eq!( + interpret("\"'one'\""), + Ok(Some(Value::string("'one'".to_string()))) + ); +} + +#[test] +fn list() { + assert_eq!( + interpret("[1, 2, 'foobar']"), + Ok(Some(Value::list(vec![ + Value::integer(1), + Value::integer(2), + Value::string("foobar".to_string()), + ]))) + ); +} + +#[test] +fn empty_list() { + assert_eq!(interpret("[]"), Ok(Some(Value::list(Vec::new())))); +} + +#[test] +fn map() { + let mut map = BTreeMap::new(); + + map.insert(Identifier::new("x"), Value::integer(1)); + map.insert(Identifier::new("foo"), Value::string("bar".to_string())); + + assert_eq!( + interpret("{ x = 1, foo = 'bar' }"), + Ok(Some(Value::map(map))) + ); +} + +#[test] +fn empty_map() { + assert_eq!(interpret("{}"), Ok(Some(Value::map(BTreeMap::new())))); +} + +#[test] +fn map_types() { + let mut map = BTreeMap::new(); + + map.insert(Identifier::new("x"), Value::integer(1)); + map.insert(Identifier::new("foo"), Value::string("bar".to_string())); + + assert_eq!( + interpret("{ x = 1, foo = 'bar' }"), + Ok(Some(Value::map(map))) + ); +} + +#[test] +fn map_type_errors() { + assert_eq!( + interpret("{ foo = 'bar' }"), + Err(vec![Error::Validation { + error: ValidationError::TypeCheck(TypeCheckError { + actual: Type::String, + expected: Type::Boolean + }), + span: (0..0).into() + }]) + ); +} + +#[test] +fn range() { + assert_eq!(interpret("0..100"), Ok(Some(Value::range(0..100)))); +} diff --git a/tests/variables.rs b/tests/variables.rs index 12a1d20..32a33ce 100644 --- a/tests/variables.rs +++ b/tests/variables.rs @@ -1,5 +1,5 @@ use dust_lang::{ - abstract_tree::Type, + abstract_tree::{Expression, Identifier, Statement, Type}, error::{Error, TypeCheckError, ValidationError}, *, }; @@ -33,3 +33,15 @@ fn set_variable_with_type_error() { }]) ); } + +#[test] +fn function_variable() { + assert_eq!( + interpret("foobar = (x: int): int x; foobar"), + Ok(Some(Value::function( + vec![(Identifier::new("x"), Type::Integer)], + Type::Integer, + Statement::Expression(Expression::Identifier(Identifier::new("x"))) + ))) + ); +}