1
0

Add tests; Support capital "E" in floats

This commit is contained in:
Jeff 2024-12-03 15:26:05 -05:00
parent 827a3df815
commit e660c0acfb
6 changed files with 510 additions and 7 deletions

View File

@ -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 { let r#type = if is_assignment {
Type::None Type::None
@ -663,8 +671,45 @@ impl<'src> Compiler<'src> {
let (right_instruction, right_type, right_position) = self.pop_last_instruction()?; let (right_instruction, right_type, right_position) = self.pop_last_instruction()?;
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?; let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
Compiler::expect_addable_type(&right_type, &right_position)?; match operator {
Compiler::expect_addable_types(&left_type, &left_position, &right_type, &right_position)?; 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 { if push_back_right {
self.instructions self.instructions
@ -1629,7 +1674,8 @@ impl<'src> Compiler<'src> {
) -> Result<(), CompileError> { ) -> Result<(), CompileError> {
if matches!( if matches!(
(left, right), (left, right),
(Type::Character, Type::String) (Type::Byte, Type::Byte)
| (Type::Character, Type::String)
| (Type::Character, Type::Character) | (Type::Character, Type::Character)
| (Type::Float, Type::Float) | (Type::Float, Type::Float)
| (Type::Integer, Type::Integer) | (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. /// Operator precedence levels.
@ -2036,6 +2181,33 @@ pub enum CompileError {
right_type: Type, right_type: Type,
position: Span, 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 { CannotSubtractLeft {
argument_type: Type, argument_type: Type,
position: Span, position: Span,
@ -2105,7 +2277,13 @@ impl AnnotatedError for CompileError {
Self::CannotAddArguments { .. } => "Cannot add these types", Self::CannotAddArguments { .. } => "Cannot add these types",
Self::CannotAddType { .. } => "Cannot add to this type", Self::CannotAddType { .. } => "Cannot add to this type",
Self::CannotChainComparison { .. } => "Cannot chain comparison operations", 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::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable",
Self::CannotMultiplyArguments { .. } => "Cannot multiply these types",
Self::CannotMultiplyType { .. } => "Cannot multiply this type",
Self::CannotResolveRegisterType { .. } => "Cannot resolve register type", Self::CannotResolveRegisterType { .. } => "Cannot resolve register type",
Self::CannotResolveVariableType { .. } => "Cannot resolve type", Self::CannotResolveVariableType { .. } => "Cannot resolve type",
Self::CannotSubtract { .. } => "Cannot subtract these types", Self::CannotSubtract { .. } => "Cannot subtract these types",
@ -2208,7 +2386,13 @@ impl AnnotatedError for CompileError {
Self::CannotAddArguments { position, .. } => *position, Self::CannotAddArguments { position, .. } => *position,
Self::CannotAddType { position, .. } => *position, Self::CannotAddType { position, .. } => *position,
Self::CannotChainComparison { 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::CannotMutateImmutableVariable { position, .. } => *position,
Self::CannotMultiplyArguments { position, .. } => *position,
Self::CannotMultiplyType { position, .. } => *position,
Self::CannotResolveRegisterType { position, .. } => *position, Self::CannotResolveRegisterType { position, .. } => *position,
Self::CannotResolveVariableType { position, .. } => *position, Self::CannotResolveVariableType { position, .. } => *position,
Self::CannotSubtract { position, .. } => *position, Self::CannotSubtract { position, .. } => *position,

View File

@ -211,14 +211,14 @@ impl<'src> Lexer<'src> {
let peek_second_char = self.peek_second_char(); 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();
self.next_char(); self.next_char();
continue; 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();
self.next_char(); self.next_char();
@ -226,7 +226,9 @@ impl<'src> Lexer<'src> {
} }
return Err(LexError::ExpectedCharacterMultiple { 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, actual: peek_char,
position: self.position, position: self.position,
}); });

View File

@ -1,5 +1,69 @@
use dust_lang::*; 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] #[test]
fn add_characters() { fn add_characters() {
let source = "'a' + 'b'"; let source = "'a' + 'b'";
@ -99,6 +163,41 @@ fn add_floats() {
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(3.0)))); 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] #[test]
fn add_integers() { fn add_integers() {
let source = "1 + 2"; let source = "1 + 2";
@ -131,6 +230,38 @@ fn add_integers() {
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3)))); 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] #[test]
fn add_strings() { fn add_strings() {
let source = "\"Hello, \" + \"world!\""; let source = "\"Hello, \" + \"world!\"";

View File

@ -1,5 +1,37 @@
use dust_lang::*; 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] #[test]
fn divide_floats() { fn divide_floats() {
let source = "2.0 / 2.0"; let source = "2.0 / 2.0";

View 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";
}

View File

@ -32,6 +32,41 @@ fn subtract_floats() {
assert_eq!(run(source), Ok(Some(ConcreteValue::Float(0.0)))); 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] #[test]
fn subtract_integers() { fn subtract_integers() {
let source = "1 - 2"; let source = "1 - 2";
@ -63,3 +98,35 @@ fn subtract_integers() {
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-1)))); 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))));
}