Add structure errors
This commit is contained in:
parent
896a0855e0
commit
413add3ba8
@ -48,15 +48,10 @@ impl Type {
|
|||||||
| (Type::Map, Type::Map)
|
| (Type::Map, Type::Map)
|
||||||
| (Type::None, Type::None)
|
| (Type::None, Type::None)
|
||||||
| (Type::Range, Type::Range)
|
| (Type::Range, Type::Range)
|
||||||
| (Type::String, Type::String) => Ok(()),
|
| (Type::String, Type::String) => return Ok(()),
|
||||||
(Type::ListOf(left), Type::ListOf(right)) => {
|
(Type::ListOf(left), Type::ListOf(right)) => {
|
||||||
if let Ok(()) = left.check(right) {
|
if let Ok(()) = left.check(right) {
|
||||||
Ok(())
|
return Ok(());
|
||||||
} else {
|
|
||||||
Err(TypeConflict {
|
|
||||||
actual: left.as_ref().clone(),
|
|
||||||
expected: right.as_ref().clone(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Type::ListOf(list_of), Type::ListExact(list_exact)) => {
|
(Type::ListOf(list_of), Type::ListExact(list_exact)) => {
|
||||||
@ -64,27 +59,50 @@ impl Type {
|
|||||||
list_of.check(r#type)?;
|
list_of.check(r#type)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
(Type::ListExact(list_exact), Type::ListOf(list_of)) => {
|
(Type::ListExact(list_exact), Type::ListOf(list_of)) => {
|
||||||
for r#type in list_exact {
|
for r#type in list_exact {
|
||||||
r#type.check(&list_of)?;
|
r#type.check(&list_of)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
(Type::ListExact(left), Type::ListExact(right)) => {
|
(Type::ListExact(left), Type::ListExact(right)) => {
|
||||||
for (left, right) in left.iter().zip(right.iter()) {
|
for (left, right) in left.iter().zip(right.iter()) {
|
||||||
left.check(right)?;
|
left.check(right)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => Err(TypeConflict {
|
(Type::Named(left), Type::Named(right)) => {
|
||||||
|
if left == right {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(
|
||||||
|
Type::Named(named),
|
||||||
|
Type::Structure {
|
||||||
|
name: struct_name, ..
|
||||||
|
},
|
||||||
|
)
|
||||||
|
| (
|
||||||
|
Type::Structure {
|
||||||
|
name: struct_name, ..
|
||||||
|
},
|
||||||
|
Type::Named(named),
|
||||||
|
) => {
|
||||||
|
if named == struct_name {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(TypeConflict {
|
||||||
actual: other.clone(),
|
actual: other.clone(),
|
||||||
expected: self.clone(),
|
expected: self.clone(),
|
||||||
}),
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::{cmp::Ordering, collections::BTreeMap, ops::Range};
|
use std::{cmp::Ordering, collections::BTreeMap, ops::Range};
|
||||||
|
|
||||||
|
use chumsky::container::Container;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::Context,
|
context::Context,
|
||||||
error::{RuntimeError, ValidationError},
|
error::{RuntimeError, ValidationError},
|
||||||
@ -63,7 +65,29 @@ impl AbstractNode for ValueNode {
|
|||||||
.collect(),
|
.collect(),
|
||||||
return_type: Box::new(return_type.node.clone()),
|
return_type: Box::new(return_type.node.clone()),
|
||||||
},
|
},
|
||||||
ValueNode::Structure { name, fields } => todo!(),
|
ValueNode::Structure {
|
||||||
|
name,
|
||||||
|
fields: expressions,
|
||||||
|
} => {
|
||||||
|
let mut types = Vec::with_capacity(expressions.len());
|
||||||
|
|
||||||
|
for (identifier, expression) in expressions {
|
||||||
|
let r#type = expression.node.expected_type(_context)?;
|
||||||
|
|
||||||
|
types.push((
|
||||||
|
identifier.clone(),
|
||||||
|
WithPosition {
|
||||||
|
node: r#type,
|
||||||
|
position: expression.position,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Type::Structure {
|
||||||
|
name: name.clone(),
|
||||||
|
fields: types,
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(r#type)
|
Ok(r#type)
|
||||||
@ -114,12 +138,36 @@ impl AbstractNode for ValueNode {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ValueNode::Structure { name, fields } = self {
|
if let ValueNode::Structure {
|
||||||
let r#type = if let Some(r#type) = context.get_type(name)? {
|
name,
|
||||||
r#type
|
fields: expressions,
|
||||||
|
} = self
|
||||||
|
{
|
||||||
|
let types = if let Some(r#type) = context.get_type(name)? {
|
||||||
|
if let Type::Structure {
|
||||||
|
name,
|
||||||
|
fields: types,
|
||||||
|
} = r#type
|
||||||
|
{
|
||||||
|
types
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ValidationError::TypeNotFound(name.clone()));
|
return Err(ValidationError::TypeNotFound(name.clone()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for ((_, expression), (_, expected_type)) in expressions.iter().zip(types.iter()) {
|
||||||
|
let actual_type = expression.node.expected_type(context)?;
|
||||||
|
|
||||||
|
expected_type.node.check(&actual_type).map_err(|conflict| {
|
||||||
|
ValidationError::TypeCheck {
|
||||||
|
conflict,
|
||||||
|
actual_position: expression.position,
|
||||||
|
expected_position: expected_type.position,
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -73,7 +73,13 @@ impl Context {
|
|||||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> {
|
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> {
|
||||||
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
||||||
let r#type = match value_data {
|
let r#type = match value_data {
|
||||||
ValueData::Type(r#type) => r#type.clone(),
|
ValueData::Type(r#type) => {
|
||||||
|
if let Type::Named(name) = r#type {
|
||||||
|
return self.get_type(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
r#type.clone()
|
||||||
|
}
|
||||||
ValueData::Value(value) => value.r#type(self)?,
|
ValueData::Value(value) => value.r#type(self)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
11
src/error.rs
11
src/error.rs
@ -33,7 +33,7 @@ impl Error {
|
|||||||
let (mut builder, validation_error, error_position) = match self {
|
let (mut builder, validation_error, error_position) = match self {
|
||||||
Error::Parse { expected, span } => {
|
Error::Parse { expected, span } => {
|
||||||
let message = if expected.is_empty() {
|
let message = if expected.is_empty() {
|
||||||
"Invalid token.".to_string()
|
"Invalid character.".to_string()
|
||||||
} else {
|
} else {
|
||||||
format!("Expected {expected}.")
|
format!("Expected {expected}.")
|
||||||
};
|
};
|
||||||
@ -77,7 +77,7 @@ impl Error {
|
|||||||
}
|
}
|
||||||
Error::Runtime { error, position } => (
|
Error::Runtime { error, position } => (
|
||||||
Report::build(
|
Report::build(
|
||||||
ReportKind::Custom("Dust Error", Color::White),
|
ReportKind::Custom("Runtime Error", Color::White),
|
||||||
"input",
|
"input",
|
||||||
position.1,
|
position.1,
|
||||||
),
|
),
|
||||||
@ -90,10 +90,11 @@ impl Error {
|
|||||||
),
|
),
|
||||||
Error::Validation { error, position } => (
|
Error::Validation { error, position } => (
|
||||||
Report::build(
|
Report::build(
|
||||||
ReportKind::Custom("Dust Error", Color::White),
|
ReportKind::Custom("Validation Error", Color::White),
|
||||||
"input",
|
"input",
|
||||||
position.1,
|
position.1,
|
||||||
),
|
)
|
||||||
|
.with_note("This error was detected by the interpreter before running the code."),
|
||||||
Some(error),
|
Some(error),
|
||||||
position,
|
position,
|
||||||
),
|
),
|
||||||
@ -130,6 +131,8 @@ impl Error {
|
|||||||
} => {
|
} => {
|
||||||
let TypeConflict { actual, expected } = conflict;
|
let TypeConflict { actual, expected } = conflict;
|
||||||
|
|
||||||
|
builder = builder.with_message("A type conflict was found.");
|
||||||
|
|
||||||
builder.add_labels([
|
builder.add_labels([
|
||||||
Label::new(("input", expected_postion.0..expected_postion.1)).with_message(
|
Label::new(("input", expected_postion.0..expected_postion.1)).with_message(
|
||||||
format!("Type {} established here.", expected.fg(type_color)),
|
format!("Type {} established here.", expected.fg(type_color)),
|
||||||
|
@ -106,7 +106,7 @@ pub fn parser<'src>() -> impl Parser<
|
|||||||
just(Token::Keyword("range")).to(Type::Range),
|
just(Token::Keyword("range")).to(Type::Range),
|
||||||
just(Token::Keyword("str")).to(Type::String),
|
just(Token::Keyword("str")).to(Type::String),
|
||||||
just(Token::Keyword("list")).to(Type::List),
|
just(Token::Keyword("list")).to(Type::List),
|
||||||
identifier.clone().map(|name| Type::Named(name)),
|
identifier.clone().map(|identifier| Type::Named(identifier)),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.map_with(|r#type, state| r#type.with_position(state.span()));
|
.map_with(|r#type, state| r#type.with_position(state.span()));
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
use dust_lang::{abstract_tree::Identifier, error::Error, *};
|
use dust_lang::{
|
||||||
|
abstract_tree::{Identifier, Type},
|
||||||
|
error::{Error, TypeConflict, ValidationError},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_structure() {
|
fn simple_structure() {
|
||||||
@ -26,6 +30,34 @@ fn simple_structure() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn field_type_error() {
|
||||||
|
assert_eq!(
|
||||||
|
interpret(
|
||||||
|
"
|
||||||
|
struct Foo {
|
||||||
|
bar : int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Foo {
|
||||||
|
bar = 'hiya',
|
||||||
|
}
|
||||||
|
"
|
||||||
|
),
|
||||||
|
Err(vec![Error::Validation {
|
||||||
|
error: ValidationError::TypeCheck {
|
||||||
|
conflict: TypeConflict {
|
||||||
|
actual: Type::String,
|
||||||
|
expected: Type::Integer
|
||||||
|
},
|
||||||
|
actual_position: (128, 134).into(),
|
||||||
|
expected_position: (56, 59).into()
|
||||||
|
},
|
||||||
|
position: (96, 166).into()
|
||||||
|
}])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_structure() {
|
fn nested_structure() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
Loading…
Reference in New Issue
Block a user