From e9bfd9f1f8da14b3198a53b0efc002111d56effb Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 17 Mar 2024 00:49:01 -0400 Subject: [PATCH] Begin using Positioned type --- src/abstract_tree/assignment.rs | 71 ++++++++++++++++++++++---------- src/abstract_tree/block.rs | 13 +++--- src/abstract_tree/if_else.rs | 65 +++++++++++++++++------------ src/abstract_tree/item.rs | 39 ++++++++++++++++++ src/abstract_tree/mod.rs | 15 ++++++- src/abstract_tree/statement.rs | 73 ++------------------------------- src/abstract_tree/type.rs | 16 ++++---- src/error.rs | 61 ++++++++++++++------------- src/lib.rs | 5 +-- tests/values.rs | 4 +- tests/variables.rs | 4 +- 11 files changed, 197 insertions(+), 169 deletions(-) create mode 100644 src/abstract_tree/item.rs diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index ae1b137..4b1b112 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -3,14 +3,14 @@ use crate::{ Context, }; -use super::{AbstractTree, Action, Identifier, Statement, Type}; +use super::{AbstractTree, Action, Identifier, Positioned, Statement, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct Assignment { identifier: Identifier, - r#type: Option, + r#type: Option>, operator: AssignmentOperator, - statement: Box, + statement: Box>, } #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] @@ -23,9 +23,9 @@ pub enum AssignmentOperator { impl Assignment { pub fn new( identifier: Identifier, - r#type: Option, + r#type: Option>, operator: AssignmentOperator, - statement: Statement, + statement: Positioned, ) -> Self { Self { identifier, @@ -42,24 +42,34 @@ impl AbstractTree for Assignment { } fn validate(&self, context: &Context) -> Result<(), ValidationError> { - let statement_type = self.statement.expected_type(context)?; + let statement_type = self.statement.node.expected_type(context)?; - if let Some(expected) = &self.r#type { - expected.check(&statement_type)?; + if let Some(Positioned { + node: expected_type, + position: expected_position, + }) = &self.r#type + { + expected_type.check(&statement_type).map_err(|conflict| { + ValidationError::TypeCheck { + conflict, + actual_position: self.statement.position, + expected_position: expected_position.clone(), + } + })?; - context.set_type(self.identifier.clone(), expected.clone())?; + context.set_type(self.identifier.clone(), expected_type.clone())?; } else { context.set_type(self.identifier.clone(), statement_type)?; } self.identifier.validate(context)?; - self.statement.validate(context)?; + self.statement.node.validate(context)?; Ok(()) } fn run(self, context: &Context) -> Result { - let action = self.statement.run(context)?; + let action = self.statement.node.run(context)?; let value = match action { Action::Return(value) => value, r#break => return Ok(r#break), @@ -101,7 +111,7 @@ impl AbstractTree for Assignment { mod tests { use crate::{ abstract_tree::{Expression, ValueNode}, - error::TypeCheckError, + error::TypeConflict, Value, }; @@ -115,7 +125,10 @@ mod tests { Identifier::new("foobar"), None, AssignmentOperator::Assign, - Statement::expression(Expression::Value(ValueNode::Integer(42)), (0..0).into()), + Positioned { + node: Statement::Expression(Expression::Value(ValueNode::Integer(42))), + position: (0, 0), + }, ) .run(&context) .unwrap(); @@ -138,7 +151,10 @@ mod tests { Identifier::new("foobar"), None, AssignmentOperator::AddAssign, - Statement::expression(Expression::Value(ValueNode::Integer(41)), (0..0).into()), + Positioned { + node: Statement::Expression(Expression::Value(ValueNode::Integer(41))), + position: (0, 0), + }, ) .run(&context) .unwrap(); @@ -161,7 +177,10 @@ mod tests { Identifier::new("foobar"), None, AssignmentOperator::SubAssign, - Statement::expression(Expression::Value(ValueNode::Integer(1)), (0..0).into()), + Positioned { + node: Statement::Expression(Expression::Value(ValueNode::Integer(1))), + position: (0, 0), + }, ) .run(&context) .unwrap(); @@ -176,18 +195,28 @@ mod tests { fn type_check() { let validation = Assignment::new( Identifier::new("foobar"), - Some(Type::Boolean), + Some(Positioned { + node: Type::Boolean, + position: (0, 0), + }), AssignmentOperator::Assign, - Statement::expression(Expression::Value(ValueNode::Integer(42)), (0..0).into()), + Positioned { + node: Statement::Expression(Expression::Value(ValueNode::Integer(42))), + position: (0, 0), + }, ) .validate(&Context::new()); assert_eq!( validation, - Err(ValidationError::TypeCheck(TypeCheckError { - actual: Type::Integer, - expected: Type::Boolean - })) + Err(ValidationError::TypeCheck { + conflict: TypeConflict { + actual: Type::Integer, + expected: Type::Boolean + }, + actual_position: (0, 0), + expected_position: (0, 0), + }) ) } } diff --git a/src/abstract_tree/block.rs b/src/abstract_tree/block.rs index 21a3a24..57e62a8 100644 --- a/src/abstract_tree/block.rs +++ b/src/abstract_tree/block.rs @@ -61,9 +61,9 @@ mod tests { #[test] fn run_returns_value_of_final_statement() { let block = Block::new(vec![ - Statement::expression(Expression::Value(ValueNode::Integer(1)), (0..0).into()), - Statement::expression(Expression::Value(ValueNode::Integer(2)), (0..0).into()), - Statement::expression(Expression::Value(ValueNode::Integer(42)), (0..0).into()), + Statement::Expression(Expression::Value(ValueNode::Integer(1))), + Statement::Expression(Expression::Value(ValueNode::Integer(2))), + Statement::Expression(Expression::Value(ValueNode::Integer(42))), ]); assert_eq!( @@ -75,11 +75,8 @@ mod tests { #[test] fn expected_type_returns_type_of_final_statement() { let block = Block::new(vec![ - Statement::expression( - Expression::Value(ValueNode::String("42".to_string())), - (0..0).into(), - ), - Statement::expression(Expression::Value(ValueNode::Integer(42)), (0..0).into()), + Statement::Expression(Expression::Value(ValueNode::String("42".to_string()))), + Statement::Expression(Expression::Value(ValueNode::Integer(42))), ]); assert_eq!(block.expected_type(&Context::new()), Ok(Type::Integer)) diff --git a/src/abstract_tree/if_else.rs b/src/abstract_tree/if_else.rs index aee0d58..bfea19e 100644 --- a/src/abstract_tree/if_else.rs +++ b/src/abstract_tree/if_else.rs @@ -3,17 +3,21 @@ use crate::{ error::{RuntimeError, ValidationError}, }; -use super::{AbstractTree, Action, Block, Expression, Type}; +use super::{AbstractTree, Action, Block, Expression, Positioned, Type}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct IfElse { - if_expression: Expression, - if_block: Block, - else_block: Option, + if_expression: Positioned, + if_block: Positioned, + else_block: Option>, } impl IfElse { - pub fn new(if_expression: Expression, if_block: Block, else_block: Option) -> Self { + pub fn new( + if_expression: Positioned, + if_block: Positioned, + else_block: Option>, + ) -> Self { Self { if_expression, if_block, @@ -24,16 +28,22 @@ impl IfElse { impl AbstractTree for IfElse { fn expected_type(&self, _context: &Context) -> Result { - self.if_block.expected_type(_context) + self.if_block.node.expected_type(_context) } fn validate(&self, context: &Context) -> Result<(), ValidationError> { - if let Type::Boolean = self.if_expression.expected_type(context)? { + if let Type::Boolean = self.if_expression.node.expected_type(context)? { if let Some(else_block) = &self.else_block { - let expected = self.if_block.expected_type(context)?; - let actual = else_block.expected_type(context)?; + let expected = self.if_block.node.expected_type(context)?; + let actual = else_block.node.expected_type(context)?; - expected.check(&actual)?; + expected + .check(&actual) + .map_err(|conflict| ValidationError::TypeCheck { + conflict, + actual_position: self.if_block.position, + expected_position: self.if_expression.position, + })?; } Ok(()) @@ -45,14 +55,15 @@ impl AbstractTree for IfElse { fn run(self, _context: &Context) -> Result { let if_boolean = self .if_expression + .node .run(_context)? .as_return_value()? .as_boolean()?; if if_boolean { - self.if_block.run(_context) + self.if_block.node.run(_context) } else if let Some(else_statement) = self.else_block { - else_statement.run(_context) + else_statement.node.run(_context) } else { Ok(Action::None) } @@ -72,11 +83,11 @@ mod tests { fn simple_if() { assert_eq!( IfElse::new( - Expression::Value(ValueNode::Boolean(true)), - Block::new(vec![Statement::expression( - Expression::Value(ValueNode::String("foo".to_string())), - (0..0).into() - )]), + Expression::Value(ValueNode::Boolean(true)).positioned((0, 0)), + Block::new(vec![Statement::Expression(Expression::Value( + ValueNode::String("foo".to_string()) + ),)]) + .positioned((0, 0)), None ) .run(&Context::new()), @@ -88,15 +99,17 @@ mod tests { fn simple_if_else() { assert_eq!( IfElse::new( - Expression::Value(ValueNode::Boolean(false)), - Block::new(vec![Statement::expression( - Expression::Value(ValueNode::String("foo".to_string())), - (0..0).into() - ),]), - Some(Block::new(vec![Statement::expression( - Expression::Value(ValueNode::String("bar".to_string())), - (0..0).into() - )])) + Expression::Value(ValueNode::Boolean(false)).positioned((0, 0)), + Block::new(vec![Statement::Expression(Expression::Value( + ValueNode::String("foo".to_string()) + ))]) + .positioned((0, 0)), + Some( + Block::new(vec![Statement::Expression(Expression::Value( + ValueNode::String("bar".to_string()) + ))]) + .positioned((0, 0)) + ) ) .run(&Context::new()), Ok(Action::Return(Value::string("bar".to_string()))) diff --git a/src/abstract_tree/item.rs b/src/abstract_tree/item.rs new file mode 100644 index 0000000..06ed81f --- /dev/null +++ b/src/abstract_tree/item.rs @@ -0,0 +1,39 @@ +use super::{AbstractTree, Expression, Positioned, Statement}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub enum Item { + Expression(Positioned), + Statement(Positioned), +} + +impl Item { + pub fn position(&self) -> &(usize, usize) { + match self { + Item::Expression((_, position)) => position, + Item::Statement((_, position)) => position, + } + } +} + +impl AbstractTree for Item { + fn expected_type( + &self, + context: &crate::context::Context, + ) -> Result { + todo!() + } + + fn validate( + &self, + context: &crate::context::Context, + ) -> Result<(), crate::error::ValidationError> { + todo!() + } + + fn run( + self, + context: &crate::context::Context, + ) -> Result { + todo!() + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index dd9ff4a..695bb13 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -36,10 +36,23 @@ use crate::{ Value, }; -pub trait AbstractTree { +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct Positioned { + pub node: T, + pub position: (usize, usize), +} + +pub trait AbstractTree: Sized { fn expected_type(&self, context: &Context) -> Result; fn validate(&self, context: &Context) -> Result<(), ValidationError>; fn run(self, context: &Context) -> Result; + + fn positioned(self, position: (usize, usize)) -> Positioned { + Positioned { + node: self, + position, + } + } } #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)] diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index ec21a65..ea40868 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -8,72 +8,7 @@ use crate::{ use super::{AbstractTree, Action, Assignment, Block, Expression, IfElse, Loop, Type, While}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct Statement { - pub inner: StatementInner, - pub span: (usize, usize), -} - -impl Statement { - pub fn assignment(assignment: Assignment, span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::Assignment(assignment), - span: (span.start(), span.end()), - } - } - - pub fn block(block: Block, span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::Block(block), - span: (span.start(), span.end()), - } - } - - pub fn r#break(span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::Break, - span: (span.start(), span.end()), - } - } - - pub fn expression(expression: Expression, span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::Expression(expression), - span: (span.start(), span.end()), - } - } - - pub fn if_else(if_else: IfElse, span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::IfElse(if_else), - span: (span.start(), span.end()), - } - } - - pub fn r#loop(r#loop: Loop, span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::Loop(r#loop), - span: (span.start(), span.end()), - } - } - - pub fn r#while(r#while: While, span: SimpleSpan) -> Self { - Statement { - inner: StatementInner::While(r#while), - span: (span.start(), span.end()), - } - } - - pub fn span(&self) -> (usize, usize) { - self.span - } - - pub fn inner(&self) -> &StatementInner { - &self.inner - } -} - -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] -pub enum StatementInner { +pub enum Statement { Assignment(Assignment), Block(Block), Break, @@ -85,7 +20,7 @@ pub enum StatementInner { impl AbstractTree for Statement { fn expected_type(&self, _context: &Context) -> Result { - match &self.inner { + match self { StatementInner::Assignment(assignment) => assignment.expected_type(_context), StatementInner::Block(block) => block.expected_type(_context), StatementInner::Break => Ok(Type::None), @@ -97,7 +32,7 @@ impl AbstractTree for Statement { } fn validate(&self, _context: &Context) -> Result<(), ValidationError> { - match &self.inner { + match self { StatementInner::Assignment(assignment) => assignment.validate(_context), StatementInner::Block(block) => block.validate(_context), StatementInner::Break => Ok(()), @@ -109,7 +44,7 @@ impl AbstractTree for Statement { } fn run(self, _context: &Context) -> Result { - match self.inner { + match self { StatementInner::Assignment(assignment) => assignment.run(_context), StatementInner::Block(block) => block.run(_context), StatementInner::Break => Ok(Action::Break), diff --git a/src/abstract_tree/type.rs b/src/abstract_tree/type.rs index 213562b..d8d6181 100644 --- a/src/abstract_tree/type.rs +++ b/src/abstract_tree/type.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter}; use crate::{ abstract_tree::Identifier, context::Context, - error::{RuntimeError, TypeCheckError, ValidationError}, + error::{RuntimeError, TypeConflict, ValidationError}, }; use super::{AbstractTree, Action}; @@ -29,7 +29,7 @@ pub enum Type { } impl Type { - pub fn check(&self, other: &Type) -> Result<(), TypeCheckError> { + pub fn check(&self, other: &Type) -> Result<(), TypeConflict> { match (self, other) { (Type::Any, _) | (_, Type::Any) @@ -49,7 +49,7 @@ impl Type { if left == right { Ok(()) } else { - Err(TypeCheckError { + Err(TypeConflict { actual: other.clone(), expected: self.clone(), }) @@ -59,7 +59,7 @@ impl Type { if let Ok(()) = left.check(right) { Ok(()) } else { - Err(TypeCheckError { + Err(TypeConflict { actual: left.as_ref().clone(), expected: right.as_ref().clone(), }) @@ -86,7 +86,7 @@ impl Type { Ok(()) } - _ => Err(TypeCheckError { + _ => Err(TypeConflict { actual: other.clone(), expected: self.clone(), }), @@ -188,14 +188,14 @@ mod tests { assert_eq!( foo.check(&bar), - Err(TypeCheckError { + Err(TypeConflict { actual: bar.clone(), expected: foo.clone() }) ); assert_eq!( bar.check(&foo), - Err(TypeCheckError { + Err(TypeConflict { actual: foo.clone(), expected: bar.clone() }) @@ -222,7 +222,7 @@ mod tests { assert_eq!( left.check(right), - Err(TypeCheckError { + Err(TypeConflict { actual: right.clone(), expected: left.clone() }) diff --git a/src/error.rs b/src/error.rs index b0e3ba9..dd15538 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,10 +19,7 @@ pub enum Error { span: (usize, usize), }, Runtime(RuntimeError), - Validation { - error: ValidationError, - span: (usize, usize), - }, + Validation(ValidationError), } impl Error { @@ -49,33 +46,38 @@ impl Error { .finish() } Error::Runtime(_) => todo!(), - Error::Validation { error, span } => { - let mut report = Report::build( - ReportKind::Custom("Validation Error", Color::White), - (), - span.0, - ); + Error::Validation(validation_error) => { + let mut report = + Report::build(ReportKind::Custom("Validation Error", Color::White), (), 0); - match error { + match validation_error { ValidationError::ExpectedBoolean => { - report = report.with_label( - Label::new(span.0..span.1).with_message("Expected boolean."), - ); + report = + report.with_label(Label::new(0..0).with_message("Expected boolean.")); } ValidationError::ExpectedIntegerOrFloat => { report = report.with_label( - Label::new(span.0..span.1).with_message("Expected integer or float."), + Label::new(0..0).with_message("Expected integer or float."), ); } ValidationError::RwLockPoison(_) => todo!(), - ValidationError::TypeCheck(TypeCheckError { actual, expected }) => { - report = report.with_label(Label::new(span.0..span.1).with_message( - format!("Type error. Expected {expected} but got {actual}."), - )); + ValidationError::TypeCheck { + conflict, + actual_position, + expected_position: expected_postion, + } => { + let TypeConflict { actual, expected } = conflict; + + report = report.with_labels([ + Label::new(expected_postion.0..expected_postion.1) + .with_message(format!("Type {expected} established here.")), + Label::new(actual_position.0..actual_position.1) + .with_message(format!("Got type {actual} here.")), + ]); } ValidationError::VariableNotFound(identifier) => { report = report - .with_label(Label::new(span.0..span.1).with_message(format!( + .with_label(Label::new(0..0).with_message(format!( "The variable {identifier} does not exist." ))); } @@ -144,7 +146,16 @@ pub enum ValidationError { ExpectedValue, InterpreterExpectedReturn, RwLockPoison(RwLockPoisonError), - TypeCheck(TypeCheckError), + TypeCheck { + /// The mismatch that caused the error. + conflict: TypeConflict, + + /// The position of the item that gave the "actual" type. + actual_position: (usize, usize), + + /// The position of the item that gave the "expected" type. + expected_position: (usize, usize), + }, VariableNotFound(Identifier), } @@ -154,12 +165,6 @@ impl From for ValidationError { } } -impl From for ValidationError { - fn from(error: TypeCheckError) -> Self { - ValidationError::TypeCheck(error) - } -} - #[derive(Debug, PartialEq)] pub struct RwLockPoisonError; @@ -170,7 +175,7 @@ impl From> for RwLockPoisonError { } #[derive(Debug, PartialEq)] -pub struct TypeCheckError { +pub struct TypeConflict { pub actual: Type, pub expected: Type, } diff --git a/src/lib.rs b/src/lib.rs index ecbedcd..90a44f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,11 +36,8 @@ impl Interpreter { .filter_map(|statement| { statement .validate(&self.context) - .map_err(|validation_error| Error::Validation { - error: validation_error, - span: statement.span(), - }) .err() + .map(|validation_error| Error::Validation(validation_error)) }) .collect::>(); diff --git a/tests/values.rs b/tests/values.rs index eb46482..73ec94c 100644 --- a/tests/values.rs +++ b/tests/values.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; use dust_lang::{ abstract_tree::{Identifier, Type}, - error::{Error, TypeCheckError, ValidationError}, + error::{Error, TypeConflict, ValidationError}, *, }; @@ -136,7 +136,7 @@ fn map_type_errors() { assert_eq!( interpret("{ foo : bool = 'bar' }"), Err(vec![Error::Validation { - error: ValidationError::TypeCheck(TypeCheckError { + error: ValidationError::TypeCheck(TypeConflict { actual: Type::String, expected: Type::Boolean }), diff --git a/tests/variables.rs b/tests/variables.rs index bf50bea..f7400e6 100644 --- a/tests/variables.rs +++ b/tests/variables.rs @@ -1,6 +1,6 @@ use dust_lang::{ abstract_tree::{Block, Expression, Identifier, Statement, Type}, - error::{Error, TypeCheckError, ValidationError}, + error::{Error, TypeConflict, ValidationError}, *, }; @@ -25,7 +25,7 @@ fn set_variable_with_type_error() { assert_eq!( interpret("foobar: str = true"), Err(vec![Error::Validation { - error: ValidationError::TypeCheck(TypeCheckError { + error: ValidationError::TypeCheck(TypeConflict { actual: Type::Boolean, expected: Type::String }),