Add tests; Support capital "E" in floats
This commit is contained in:
parent
827a3df815
commit
e660c0acfb
@ -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,
|
||||
|
@ -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,
|
||||
});
|
||||
|
@ -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!\"";
|
||||
|
@ -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";
|
||||
|
87
dust-lang/tests/math_divide_errors.rs
Normal file
87
dust-lang/tests/math_divide_errors.rs
Normal file
@ -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";
|
||||
}
|
@ -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))));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user