1
0
dust/dust-lang/src/type.rs

433 lines
13 KiB
Rust
Raw Normal View History

2024-07-15 19:49:34 +00:00
/**
Description of a kind of value.
Most types are concrete and specific, the exceptions are the Generic and Any types.
Generic types are temporary placeholders that describe a type that will be defined later. The
interpreter should use the validation phase to enforce that all Generic types have a concrete
type assigned to them before the program is run.
The Any type is used in cases where a value's type does not matter. For example, the standard
library's "length" function does not care about the type of item in the list, only the list
itself. So the input is defined as `[any]`, i.e. `Type::ListOf(Box::new(Type::Any))`.
**/
2024-06-24 08:02:44 +00:00
use std::{
collections::BTreeMap,
fmt::{self, Display, Formatter},
};
2024-03-07 03:15:35 +00:00
2024-06-04 18:47:15 +00:00
use serde::{Deserialize, Serialize};
2024-03-20 12:36:18 +00:00
2024-08-04 00:23:52 +00:00
use crate::identifier::Identifier;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct TypeConflict {
pub expected: Type,
pub actual: Type,
}
2024-06-04 18:47:15 +00:00
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
2024-07-15 19:49:34 +00:00
/// Description of a kind of value.
///
/// See the [module documentation](index.html) for more information.
pub enum Type {
2024-03-06 17:15:03 +00:00
Any,
Boolean,
Enum {
name: Identifier,
type_parameters: Option<Vec<Type>>,
variants: Vec<(Identifier, Option<Vec<Type>>)>,
},
Float,
2024-03-09 02:26:49 +00:00
Function {
2024-06-17 14:10:06 +00:00
type_parameters: Option<Vec<Identifier>>,
2024-06-24 02:39:33 +00:00
value_parameters: Option<Vec<(Identifier, Type)>>,
2024-06-22 04:58:30 +00:00
return_type: Option<Box<Type>>,
2024-03-09 02:26:49 +00:00
},
2024-06-19 04:05:58 +00:00
Generic {
identifier: Identifier,
concrete_type: Option<Box<Type>>,
},
Integer,
2024-06-17 14:10:06 +00:00
List {
length: usize,
item_type: Box<Type>,
},
ListOf(Box<Type>),
2024-06-24 08:02:44 +00:00
Map(BTreeMap<Identifier, Type>),
Range,
String,
2024-03-19 21:18:36 +00:00
Structure {
name: Identifier,
2024-06-17 14:10:06 +00:00
fields: Vec<(Identifier, Type)>,
2024-03-19 21:18:36 +00:00
},
}
2024-03-06 17:15:03 +00:00
impl Type {
/// Returns a concrete type, either the type itself or the concrete type of a generic type.
pub fn concrete_type(&self) -> &Type {
match self {
Type::Generic {
concrete_type: Some(concrete_type),
..
} => concrete_type.concrete_type(),
_ => self,
}
}
/// Checks that the type is compatible with another type.
2024-03-17 04:49:01 +00:00
pub fn check(&self, other: &Type) -> Result<(), TypeConflict> {
match (self.concrete_type(), other.concrete_type()) {
2024-03-06 17:15:03 +00:00
(Type::Any, _)
| (_, Type::Any)
| (Type::Boolean, Type::Boolean)
| (Type::Float, Type::Float)
| (Type::Integer, Type::Integer)
| (Type::Range, Type::Range)
2024-03-20 05:29:07 +00:00
| (Type::String, Type::String) => return Ok(()),
2024-06-19 04:05:58 +00:00
(
Type::Generic {
concrete_type: left,
..
},
Type::Generic {
concrete_type: right,
..
},
) => match (left, right) {
2024-06-17 21:38:24 +00:00
(Some(left), Some(right)) => {
2024-07-12 14:20:52 +00:00
if left.check(right).is_ok() {
2024-06-17 21:38:24 +00:00
return Ok(());
}
}
(None, None) => {
return Ok(());
}
_ => {}
},
2024-06-19 04:05:58 +00:00
(Type::Generic { concrete_type, .. }, other)
| (other, Type::Generic { concrete_type, .. }) => {
if let Some(concrete_type) = concrete_type {
if other == concrete_type.as_ref() {
return Ok(());
}
}
}
2024-03-06 17:15:03 +00:00
(Type::ListOf(left), Type::ListOf(right)) => {
2024-07-12 14:20:52 +00:00
if left.check(right).is_ok() {
2024-03-20 05:29:07 +00:00
return Ok(());
2024-03-06 17:15:03 +00:00
}
}
(
2024-03-20 12:36:18 +00:00
Type::Structure {
name: left_name,
fields: left_fields,
},
2024-03-20 12:36:18 +00:00
Type::Structure {
name: right_name,
fields: right_fields,
},
) => {
2024-03-20 12:36:18 +00:00
if left_name == right_name {
for ((left_field_name, left_type), (right_field_name, right_type)) in
left_fields.iter().zip(right_fields.iter())
{
2024-06-17 14:10:06 +00:00
if left_field_name != right_field_name || left_type != right_type {
2024-03-20 12:36:18 +00:00
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
}
}
2024-03-20 05:29:07 +00:00
return Ok(());
}
}
2024-06-17 15:02:13 +00:00
(
Type::List {
length: left_length,
item_type: left_type,
},
Type::List {
length: right_length,
item_type: right_type,
},
) => {
if left_length != right_length || left_type != right_type {
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
}
return Ok(());
}
(
Type::ListOf(left_type),
Type::List {
item_type: right_type,
..
},
)
| (
Type::List {
item_type: right_type,
..
},
Type::ListOf(left_type),
) => {
2024-07-12 14:20:52 +00:00
if right_type.check(left_type).is_err() {
2024-06-17 15:02:13 +00:00
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
} else {
return Ok(());
}
}
2024-03-20 05:29:07 +00:00
(
2024-03-20 12:36:18 +00:00
Type::Function {
2024-06-17 14:10:06 +00:00
type_parameters: left_type_parameters,
value_parameters: left_value_parameters,
2024-03-20 12:36:18 +00:00
return_type: left_return,
2024-03-20 05:29:07 +00:00
},
2024-03-20 12:36:18 +00:00
Type::Function {
2024-06-17 14:10:06 +00:00
type_parameters: right_type_parameters,
value_parameters: right_value_parameters,
2024-03-20 12:36:18 +00:00
return_type: right_return,
2024-03-20 05:29:07 +00:00
},
) => {
2024-06-17 14:10:06 +00:00
if left_return == right_return {
for (left_parameter, right_parameter) in left_type_parameters
.iter()
.zip(right_type_parameters.iter())
2024-03-24 19:47:23 +00:00
{
2024-06-17 14:10:06 +00:00
if left_parameter != right_parameter {
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
}
}
2024-06-17 15:02:13 +00:00
2024-06-17 14:10:06 +00:00
for (left_parameter, right_parameter) in left_value_parameters
.iter()
.zip(right_value_parameters.iter())
{
if left_parameter != right_parameter {
2024-03-24 19:47:23 +00:00
return Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
});
}
}
2024-03-20 05:29:07 +00:00
return Ok(());
}
}
2024-06-24 08:02:44 +00:00
(Type::Map(left), Type::Map(right)) => {
if left == right {
return Ok(());
}
}
2024-03-20 05:29:07 +00:00
_ => {}
2024-03-06 17:15:03 +00:00
}
2024-03-20 05:29:07 +00:00
Err(TypeConflict {
actual: other.clone(),
expected: self.clone(),
})
2024-03-06 17:15:03 +00:00
}
}
2024-03-07 03:15:35 +00:00
impl Display for Type {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Type::Any => write!(f, "any"),
2024-03-23 21:07:41 +00:00
Type::Boolean => write!(f, "bool"),
Type::Enum { variants, .. } => {
write!(f, "enum ")?;
write!(f, " {{")?;
for (identifier, types) in variants {
writeln!(f, "{identifier}")?;
if let Some(types) = types {
write!(f, "(")?;
for r#type in types {
write!(f, "{}", r#type)?;
}
}
write!(f, ")")?;
}
write!(f, "}}")
}
2024-03-07 03:15:35 +00:00
Type::Float => write!(f, "float"),
2024-06-19 04:05:58 +00:00
Type::Generic { concrete_type, .. } => {
2024-06-26 15:35:39 +00:00
match concrete_type.clone().map(|r#box| *r#box) {
Some(Type::Generic { identifier, .. }) => write!(f, "{identifier}"),
Some(concrete_type) => write!(f, "implied to be {concrete_type}"),
None => write!(f, "unknown"),
2024-06-17 21:38:24 +00:00
}
}
2024-03-23 21:07:41 +00:00
Type::Integer => write!(f, "int"),
2024-06-17 14:10:06 +00:00
Type::List { length, item_type } => write!(f, "[{length}; {}]", item_type),
2024-06-24 08:02:44 +00:00
Type::ListOf(item_type) => write!(f, "[{}]", item_type),
2024-06-24 08:16:05 +00:00
Type::Map(map) => {
write!(f, "{{ ")?;
2024-06-24 08:02:44 +00:00
2024-07-12 14:20:52 +00:00
for (index, (key, r#type)) in map.iter().enumerate() {
2024-06-24 08:16:05 +00:00
write!(f, "{key}: {type}")?;
if index != map.len() - 1 {
write!(f, ", ")?;
}
2024-06-24 08:02:44 +00:00
}
2024-06-24 08:16:05 +00:00
write!(f, " }}")
2024-06-24 08:02:44 +00:00
}
2024-03-07 03:15:35 +00:00
Type::Range => write!(f, "range"),
2024-03-23 21:07:41 +00:00
Type::String => write!(f, "str"),
2024-03-09 02:26:49 +00:00
Type::Function {
2024-06-17 14:10:06 +00:00
type_parameters,
value_parameters,
2024-03-09 02:26:49 +00:00
return_type,
} => {
2024-06-26 18:44:23 +00:00
write!(f, "fn ")?;
2024-03-09 02:26:49 +00:00
2024-06-17 14:10:06 +00:00
if let Some(type_parameters) = type_parameters {
2024-06-26 18:44:23 +00:00
write!(f, "<")?;
2024-06-17 14:10:06 +00:00
for identifier in type_parameters {
2024-06-26 18:44:23 +00:00
write!(f, "{}, ", identifier)?;
2024-06-17 14:10:06 +00:00
}
2024-06-26 18:44:23 +00:00
write!(f, ">")?;
2024-06-17 14:10:06 +00:00
}
2024-06-26 18:44:23 +00:00
write!(f, "(")?;
2024-06-24 02:39:33 +00:00
if let Some(value_parameters) = value_parameters {
for (identifier, r#type) in value_parameters {
write!(f, "{identifier}: {type}")?;
}
2024-03-09 02:26:49 +00:00
}
2024-06-22 04:58:30 +00:00
write!(f, ")")?;
if let Some(r#type) = return_type {
2024-06-24 02:39:33 +00:00
write!(f, " -> {type}")
2024-06-22 04:58:30 +00:00
} else {
Ok(())
}
2024-03-09 02:26:49 +00:00
}
2024-03-20 12:36:18 +00:00
Type::Structure { name, .. } => write!(f, "{name}"),
2024-03-07 03:15:35 +00:00
}
}
}
2024-03-06 17:15:03 +00:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_same_types() {
assert_eq!(Type::Any.check(&Type::Any), Ok(()));
assert_eq!(Type::Boolean.check(&Type::Boolean), Ok(()));
assert_eq!(Type::Float.check(&Type::Float), Ok(()));
assert_eq!(Type::Integer.check(&Type::Integer), Ok(()));
assert_eq!(
2024-06-17 14:10:06 +00:00
Type::List {
length: 4,
item_type: Box::new(Type::Boolean),
}
.check(&Type::List {
length: 4,
item_type: Box::new(Type::Boolean),
}),
2024-03-06 17:15:03 +00:00
Ok(())
);
assert_eq!(
2024-06-17 14:10:06 +00:00
Type::ListOf(Box::new(Type::Integer)).check(&Type::ListOf(Box::new(Type::Integer))),
2024-03-06 17:15:03 +00:00
Ok(())
);
2024-06-17 14:10:06 +00:00
2024-06-24 08:02:44 +00:00
let mut map = BTreeMap::new();
2024-07-04 18:39:45 +00:00
map.insert(Identifier::from("x"), Type::Integer);
map.insert(Identifier::from("y"), Type::String);
map.insert(Identifier::from("z"), Type::Map(map.clone()));
2024-06-24 08:02:44 +00:00
assert_eq!(Type::Map(map.clone()).check(&Type::Map(map)), Ok(()));
2024-03-06 17:15:03 +00:00
assert_eq!(Type::Range.check(&Type::Range), Ok(()));
assert_eq!(Type::String.check(&Type::String), Ok(()));
}
2024-03-06 17:15:03 +00:00
#[test]
fn errors() {
2024-03-19 21:18:36 +00:00
let foo = Type::Integer;
let bar = Type::String;
2024-03-06 17:15:03 +00:00
assert_eq!(
foo.check(&bar),
2024-03-17 04:49:01 +00:00
Err(TypeConflict {
2024-03-06 17:15:03 +00:00
actual: bar.clone(),
expected: foo.clone()
})
);
assert_eq!(
bar.check(&foo),
2024-03-17 04:49:01 +00:00
Err(TypeConflict {
2024-03-06 17:15:03 +00:00
actual: foo.clone(),
expected: bar.clone()
})
);
let types = [
Type::Boolean,
Type::Float,
Type::Integer,
2024-06-17 14:10:06 +00:00
Type::List {
length: 10,
item_type: Box::new(Type::Integer),
},
Type::ListOf(Box::new(Type::Boolean)),
2024-06-24 08:02:44 +00:00
Type::Map(BTreeMap::new()),
2024-03-06 17:15:03 +00:00
Type::Range,
Type::String,
];
2024-06-22 04:58:30 +00:00
for left in types.clone() {
for right in types.clone() {
if left == right {
continue;
}
2024-03-06 17:15:03 +00:00
2024-06-22 04:58:30 +00:00
assert_eq!(
left.check(&right),
Err(TypeConflict {
actual: right.clone(),
expected: left.clone()
})
);
}
2024-03-06 17:15:03 +00:00
}
}
2024-03-06 17:15:03 +00:00
#[test]
fn check_list_types() {
2024-06-17 14:10:06 +00:00
let list = Type::List {
length: 42,
item_type: Box::new(Type::Integer),
};
let list_of = Type::ListOf(Box::new(Type::Integer));
2024-03-06 17:15:03 +00:00
assert_eq!(list.check(&list_of), Ok(()));
assert_eq!(list_of.check(&list), Ok(()));
}
}