diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 67a7434..b219845 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -647,7 +647,15 @@ impl<'src> Compiler<'src> { }); } - Compiler::expect_addable_type(&left_type, &left_position)?; + match operator { + Token::Plus | Token::PlusEqual => { + Compiler::expect_addable_type(&left_type, &left_position)? + } + Token::Slash | Token::SlashEqual => { + Compiler::expect_dividable_type(&left_type, &left_position)? + } + _ => {} + } let r#type = if is_assignment { Type::None @@ -663,8 +671,45 @@ impl<'src> Compiler<'src> { let (right_instruction, right_type, right_position) = self.pop_last_instruction()?; let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; - Compiler::expect_addable_type(&right_type, &right_position)?; - Compiler::expect_addable_types(&left_type, &left_position, &right_type, &right_position)?; + match operator { + Token::Plus | Token::PlusEqual => { + Compiler::expect_addable_type(&right_type, &right_position)?; + Compiler::expect_addable_types( + &left_type, + &left_position, + &right_type, + &right_position, + )?; + } + Token::Minus | Token::MinusEqual => { + Compiler::expect_addable_type(&right_type, &right_position)?; + Compiler::expect_addable_types( + &left_type, + &left_position, + &right_type, + &right_position, + )?; + } + Token::Slash | Token::SlashEqual => { + Compiler::expect_dividable_type(&right_type, &right_position)?; + Compiler::expect_dividable_types( + &left_type, + &left_position, + &right_type, + &right_position, + )?; + } + Token::Percent | Token::PercentEqual => { + Compiler::expect_modulable_type(&right_type, &right_position)?; + Compiler::expect_modulable_types( + &left_type, + &left_position, + &right_type, + &right_position, + )?; + } + _ => {} + } if push_back_right { self.instructions @@ -1629,7 +1674,8 @@ impl<'src> Compiler<'src> { ) -> Result<(), CompileError> { if matches!( (left, right), - (Type::Character, Type::String) + (Type::Byte, Type::Byte) + | (Type::Character, Type::String) | (Type::Character, Type::Character) | (Type::Float, Type::Float) | (Type::Integer, Type::Integer) @@ -1645,6 +1691,105 @@ impl<'src> Compiler<'src> { }) } } + + fn expect_dividable_type(argument_type: &Type, position: &Span) -> Result<(), CompileError> { + if matches!( + argument_type, + Type::Byte | Type::Character | Type::Float | Type::Integer + ) { + Ok(()) + } else { + Err(CompileError::CannotDivideType { + argument_type: argument_type.clone(), + position: *position, + }) + } + } + + fn expect_dividable_types( + left: &Type, + left_position: &Span, + right: &Type, + right_position: &Span, + ) -> Result<(), CompileError> { + if matches!( + (left, right), + (Type::Float, Type::Float) | (Type::Integer, Type::Integer) + ) { + Ok(()) + } else { + Err(CompileError::CannotDivideArguments { + left_type: left.clone(), + right_type: right.clone(), + position: Span(left_position.0, right_position.1), + }) + } + } + + fn expect_modulable_type(argument_type: &Type, position: &Span) -> Result<(), CompileError> { + if matches!(argument_type, Type::Byte | Type::Integer | Type::Float) { + Ok(()) + } else { + Err(CompileError::CannotModuloType { + argument_type: argument_type.clone(), + position: *position, + }) + } + } + + fn expect_modulable_types( + left: &Type, + left_position: &Span, + right: &Type, + right_position: &Span, + ) -> Result<(), CompileError> { + if matches!( + (left, right), + (Type::Byte, Type::Byte) | (Type::Integer, Type::Integer) | (Type::Float, Type::Float) + ) { + Ok(()) + } else { + Err(CompileError::CannotModuloArguments { + left_type: left.clone(), + right_type: right.clone(), + position: Span(left_position.0, right_position.1), + }) + } + } + + fn expect_multiplicable_type( + argument_type: &Type, + position: &Span, + ) -> Result<(), CompileError> { + if matches!(argument_type, Type::Byte | Type::Float | Type::Integer) { + Ok(()) + } else { + Err(CompileError::CannotMultiplyType { + argument_type: argument_type.clone(), + position: *position, + }) + } + } + + fn expect_multiplicable_types( + left: &Type, + left_position: &Span, + right: &Type, + right_position: &Span, + ) -> Result<(), CompileError> { + if matches!( + (left, right), + (Type::Byte, Type::Byte) | (Type::Float, Type::Float) | (Type::Integer, Type::Integer) + ) { + Ok(()) + } else { + Err(CompileError::CannotMultiplyArguments { + left_type: left.clone(), + right_type: right.clone(), + position: Span(left_position.0, right_position.1), + }) + } + } } /// Operator precedence levels. @@ -2036,6 +2181,33 @@ pub enum CompileError { right_type: Type, position: Span, }, + CannotDivideType { + argument_type: Type, + position: Span, + }, + CannotDivideArguments { + left_type: Type, + right_type: Type, + position: Span, + }, + CannotModuloType { + argument_type: Type, + position: Span, + }, + CannotModuloArguments { + left_type: Type, + right_type: Type, + position: Span, + }, + CannotMultiplyType { + argument_type: Type, + position: Span, + }, + CannotMultiplyArguments { + left_type: Type, + right_type: Type, + position: Span, + }, CannotSubtractLeft { argument_type: Type, position: Span, @@ -2105,7 +2277,13 @@ impl AnnotatedError for CompileError { Self::CannotAddArguments { .. } => "Cannot add these types", Self::CannotAddType { .. } => "Cannot add to this type", Self::CannotChainComparison { .. } => "Cannot chain comparison operations", + Self::CannotDivideArguments { .. } => "Cannot divide these types", + Self::CannotDivideType { .. } => "Cannot divide this type", + Self::CannotModuloArguments { .. } => "Cannot modulo these types", + Self::CannotModuloType { .. } => "Cannot modulo this type", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", + Self::CannotMultiplyArguments { .. } => "Cannot multiply these types", + Self::CannotMultiplyType { .. } => "Cannot multiply this type", Self::CannotResolveRegisterType { .. } => "Cannot resolve register type", Self::CannotResolveVariableType { .. } => "Cannot resolve type", Self::CannotSubtract { .. } => "Cannot subtract these types", @@ -2208,7 +2386,13 @@ impl AnnotatedError for CompileError { Self::CannotAddArguments { position, .. } => *position, Self::CannotAddType { position, .. } => *position, Self::CannotChainComparison { position } => *position, + Self::CannotDivideArguments { position, .. } => *position, + Self::CannotDivideType { position, .. } => *position, + Self::CannotModuloArguments { position, .. } => *position, + Self::CannotModuloType { position, .. } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position, + Self::CannotMultiplyArguments { position, .. } => *position, + Self::CannotMultiplyType { position, .. } => *position, Self::CannotResolveRegisterType { position, .. } => *position, Self::CannotResolveVariableType { position, .. } => *position, Self::CannotSubtract { position, .. } => *position, diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 604b907..258187e 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -211,14 +211,14 @@ impl<'src> Lexer<'src> { let peek_second_char = self.peek_second_char(); - if let ('e', Some('0'..='9')) = (peek_char, peek_second_char) { + if let ('e' | 'E', Some('0'..='9')) = (peek_char, peek_second_char) { self.next_char(); self.next_char(); continue; } - if let ('e', Some('-')) = (peek_char, peek_second_char) { + if let ('e' | 'E', Some('+')) = (peek_char, peek_second_char) { self.next_char(); self.next_char(); @@ -226,7 +226,9 @@ impl<'src> Lexer<'src> { } return Err(LexError::ExpectedCharacterMultiple { - expected: &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', '-'], + expected: &[ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'e', 'E', '-', + ], actual: peek_char, position: self.position, }); diff --git a/dust-lang/tests/math_add.rs b/dust-lang/tests/math_add.rs index be167fd..8a99948 100644 --- a/dust-lang/tests/math_add.rs +++ b/dust-lang/tests/math_add.rs @@ -1,5 +1,69 @@ use dust_lang::*; +#[test] +fn add_bytes() { + let source = "0xfe + 0x01"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Byte), + }, + vec![ + ( + Instruction::add( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(1) + ), + Span(5, 6) + ), + (Instruction::r#return(true), Span(11, 11)) + ], + vec![ConcreteValue::Byte(0xfe), ConcreteValue::Byte(0x01)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Byte(0xff)))); +} + +#[test] +fn add_bytes_saturate() { + let source = "0xff + 0x01"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Byte), + }, + vec![ + ( + Instruction::add( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(1) + ), + Span(5, 6) + ), + (Instruction::r#return(true), Span(11, 11)) + ], + vec![ConcreteValue::Byte(0xff), ConcreteValue::Byte(0x01)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Byte(0xff)))); +} + #[test] fn add_characters() { let source = "'a' + 'b'"; @@ -99,6 +163,41 @@ fn add_floats() { assert_eq!(run(source), Ok(Some(ConcreteValue::Float(3.0)))); } +#[test] +fn add_floats_saturatate() { + let source = "1.7976931348623157E+308 + 0.00000001"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Float), + }, + vec![ + ( + Instruction::add( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(1) + ), + Span(24, 25) + ), + (Instruction::r#return(true), Span(36, 36)) + ], + vec![ + ConcreteValue::Float(f64::MAX), + ConcreteValue::Float(0.00000001) + ], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Float(f64::MAX)))); +} + #[test] fn add_integers() { let source = "1 + 2"; @@ -131,6 +230,38 @@ fn add_integers() { assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3)))); } +#[test] +fn add_integers_saturate() { + let source = "9223372036854775807 + 1"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Integer), + }, + vec![ + ( + Instruction::add( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(1) + ), + Span(20, 21) + ), + (Instruction::r#return(true), Span(23, 23)) + ], + vec![ConcreteValue::Integer(i64::MAX), ConcreteValue::Integer(1)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(i64::MAX)))); +} + #[test] fn add_strings() { let source = "\"Hello, \" + \"world!\""; diff --git a/dust-lang/tests/math_divide.rs b/dust-lang/tests/math_divide.rs index 436179e..8dfb1d2 100644 --- a/dust-lang/tests/math_divide.rs +++ b/dust-lang/tests/math_divide.rs @@ -1,5 +1,37 @@ use dust_lang::*; +#[test] +fn divide_bytes() { + let source = "0xff / 0x01"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Byte), + }, + vec![ + ( + Instruction::divide( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(0) + ), + Span(0, 7) + ), + (Instruction::r#return(true), Span(11, 11)) + ], + vec![ConcreteValue::Byte(0xff), ConcreteValue::Byte(0x01)], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Byte(0xff)))); +} + #[test] fn divide_floats() { let source = "2.0 / 2.0"; diff --git a/dust-lang/tests/math_divide_errors.rs b/dust-lang/tests/math_divide_errors.rs new file mode 100644 index 0000000..1ac865d --- /dev/null +++ b/dust-lang/tests/math_divide_errors.rs @@ -0,0 +1,87 @@ +use dust_lang::*; + +#[test] +fn divide_boolean_left() { + let source = "true / 1"; + + assert_eq!( + compile(source), + Err(DustError::Compile { + error: CompileError::CannotDivideType { + argument_type: Type::Boolean, + position: Span(0, 4) + }, + source, + }) + ); +} + +#[test] +fn divide_boolean_right() { + let source = "1 / true"; +} + +#[test] +fn divide_character_left() { + let source = "'a' / 1"; +} + +#[test] +fn divide_character_right() { + let source = "1 / 'a'"; +} + +#[test] +fn divide_function_left() { + let source = "fn(){} / 1"; +} + +#[test] +fn divide_function_right() { + let source = "1 / fn(){}"; +} + +#[test] +fn divide_list_left() { + let source = "[1, 2] / 1"; +} + +#[test] +fn divide_list_right() { + let source = "1 / [1, 2]"; +} + +// #[test] +// fn add_range_left() { +// todo!("Add ranges") +// } + +// #[test] +// fn add_range_right() { +// todo!("Add ranges") +// } + +#[test] +fn divide_string_left() { + let source = "\"hello\" / 1"; +} + +#[test] +fn divide_string_right() { + let source = "1 / \"hello\""; +} + +#[test] +fn divide_float_and_character() { + let source = "1.0 / 'a'"; +} + +#[test] +fn divide_float_and_integer() { + let source = "1.0 / 1"; +} + +#[test] +fn divide_integer_and_float() { + let source = "1 / 1.0"; +} diff --git a/dust-lang/tests/math_subtract.rs b/dust-lang/tests/math_subtract.rs index 57825ba..9834035 100644 --- a/dust-lang/tests/math_subtract.rs +++ b/dust-lang/tests/math_subtract.rs @@ -32,6 +32,41 @@ fn subtract_floats() { assert_eq!(run(source), Ok(Some(ConcreteValue::Float(0.0)))); } +#[test] +fn subtract_floats_saturate() { + let source = "-1.7976931348623157E+308 - 0.0000001"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Float), + }, + vec![ + ( + Instruction::subtract( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(1) + ), + Span(25, 26) + ), + (Instruction::r#return(true), Span(36, 36)), + ], + vec![ + ConcreteValue::Float(f64::MIN), + ConcreteValue::Float(0.0000001), + ], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Float(f64::MIN)))); +} + #[test] fn subtract_integers() { let source = "1 - 2"; @@ -63,3 +98,35 @@ fn subtract_integers() { assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-1)))); } + +#[test] +fn subtract_integers_saturate() { + let source = "-9223372036854775808 - 1"; + + assert_eq!( + compile(source), + Ok(Chunk::with_data( + None, + FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::Integer), + }, + vec![ + ( + Instruction::subtract( + Destination::Register(0), + Argument::Constant(0), + Argument::Constant(1) + ), + Span(21, 22) + ), + (Instruction::r#return(true), Span(24, 24)), + ], + vec![ConcreteValue::Integer(i64::MIN), ConcreteValue::Integer(1),], + vec![] + )) + ); + + assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(i64::MIN)))); +}