Begin using Positioned type

This commit is contained in:
Jeff 2024-03-17 00:49:01 -04:00
parent 15b1808741
commit e9bfd9f1f8
11 changed files with 197 additions and 169 deletions

View File

@ -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<Type>,
r#type: Option<Positioned<Type>>,
operator: AssignmentOperator,
statement: Box<Statement>,
statement: Box<Positioned<Statement>>,
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
@ -23,9 +23,9 @@ pub enum AssignmentOperator {
impl Assignment {
pub fn new(
identifier: Identifier,
r#type: Option<Type>,
r#type: Option<Positioned<Type>>,
operator: AssignmentOperator,
statement: Statement,
statement: Positioned<Statement>,
) -> 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<Action, RuntimeError> {
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 {
Err(ValidationError::TypeCheck {
conflict: TypeConflict {
actual: Type::Integer,
expected: Type::Boolean
}))
},
actual_position: (0, 0),
expected_position: (0, 0),
})
)
}
}

View File

@ -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))

View File

@ -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<Block>,
if_expression: Positioned<Expression>,
if_block: Positioned<Block>,
else_block: Option<Positioned<Block>>,
}
impl IfElse {
pub fn new(if_expression: Expression, if_block: Block, else_block: Option<Block>) -> Self {
pub fn new(
if_expression: Positioned<Expression>,
if_block: Positioned<Block>,
else_block: Option<Positioned<Block>>,
) -> Self {
Self {
if_expression,
if_block,
@ -24,16 +28,22 @@ impl IfElse {
impl AbstractTree for IfElse {
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
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<Action, RuntimeError> {
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())))

39
src/abstract_tree/item.rs Normal file
View File

@ -0,0 +1,39 @@
use super::{AbstractTree, Expression, Positioned, Statement};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum Item {
Expression(Positioned<Expression>),
Statement(Positioned<Statement>),
}
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<super::Type, crate::error::ValidationError> {
todo!()
}
fn validate(
&self,
context: &crate::context::Context,
) -> Result<(), crate::error::ValidationError> {
todo!()
}
fn run(
self,
context: &crate::context::Context,
) -> Result<super::Action, crate::error::RuntimeError> {
todo!()
}
}

View File

@ -36,10 +36,23 @@ use crate::{
Value,
};
pub trait AbstractTree {
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct Positioned<T> {
pub node: T,
pub position: (usize, usize),
}
pub trait AbstractTree: Sized {
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError>;
fn validate(&self, context: &Context) -> Result<(), ValidationError>;
fn run(self, context: &Context) -> Result<Action, RuntimeError>;
fn positioned(self, position: (usize, usize)) -> Positioned<Self> {
Positioned {
node: self,
position,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]

View File

@ -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<Type, ValidationError> {
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<Action, RuntimeError> {
match self.inner {
match self {
StatementInner::Assignment(assignment) => assignment.run(_context),
StatementInner::Block(block) => block.run(_context),
StatementInner::Break => Ok(Action::Break),

View File

@ -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()
})

View File

@ -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<RwLockPoisonError> for ValidationError {
}
}
impl From<TypeCheckError> for ValidationError {
fn from(error: TypeCheckError) -> Self {
ValidationError::TypeCheck(error)
}
}
#[derive(Debug, PartialEq)]
pub struct RwLockPoisonError;
@ -170,7 +175,7 @@ impl<T> From<PoisonError<T>> for RwLockPoisonError {
}
#[derive(Debug, PartialEq)]
pub struct TypeCheckError {
pub struct TypeConflict {
pub actual: Type,
pub expected: Type,
}

View File

@ -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::<Vec<Error>>();

View File

@ -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
}),

View File

@ -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
}),