Begin using Positioned type
This commit is contained in:
parent
15b1808741
commit
e9bfd9f1f8
@ -3,14 +3,14 @@ use crate::{
|
|||||||
Context,
|
Context,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AbstractTree, Action, Identifier, Statement, Type};
|
use super::{AbstractTree, Action, Identifier, Positioned, Statement, Type};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Assignment {
|
pub struct Assignment {
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
r#type: Option<Type>,
|
r#type: Option<Positioned<Type>>,
|
||||||
operator: AssignmentOperator,
|
operator: AssignmentOperator,
|
||||||
statement: Box<Statement>,
|
statement: Box<Positioned<Statement>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
@ -23,9 +23,9 @@ pub enum AssignmentOperator {
|
|||||||
impl Assignment {
|
impl Assignment {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
r#type: Option<Type>,
|
r#type: Option<Positioned<Type>>,
|
||||||
operator: AssignmentOperator,
|
operator: AssignmentOperator,
|
||||||
statement: Statement,
|
statement: Positioned<Statement>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
identifier,
|
identifier,
|
||||||
@ -42,24 +42,34 @@ impl AbstractTree for Assignment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, context: &Context) -> Result<(), ValidationError> {
|
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 {
|
if let Some(Positioned {
|
||||||
expected.check(&statement_type)?;
|
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 {
|
} else {
|
||||||
context.set_type(self.identifier.clone(), statement_type)?;
|
context.set_type(self.identifier.clone(), statement_type)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.identifier.validate(context)?;
|
self.identifier.validate(context)?;
|
||||||
self.statement.validate(context)?;
|
self.statement.node.validate(context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
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 {
|
let value = match action {
|
||||||
Action::Return(value) => value,
|
Action::Return(value) => value,
|
||||||
r#break => return Ok(r#break),
|
r#break => return Ok(r#break),
|
||||||
@ -101,7 +111,7 @@ impl AbstractTree for Assignment {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::{Expression, ValueNode},
|
abstract_tree::{Expression, ValueNode},
|
||||||
error::TypeCheckError,
|
error::TypeConflict,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -115,7 +125,10 @@ mod tests {
|
|||||||
Identifier::new("foobar"),
|
Identifier::new("foobar"),
|
||||||
None,
|
None,
|
||||||
AssignmentOperator::Assign,
|
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)
|
.run(&context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -138,7 +151,10 @@ mod tests {
|
|||||||
Identifier::new("foobar"),
|
Identifier::new("foobar"),
|
||||||
None,
|
None,
|
||||||
AssignmentOperator::AddAssign,
|
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)
|
.run(&context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -161,7 +177,10 @@ mod tests {
|
|||||||
Identifier::new("foobar"),
|
Identifier::new("foobar"),
|
||||||
None,
|
None,
|
||||||
AssignmentOperator::SubAssign,
|
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)
|
.run(&context)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -176,18 +195,28 @@ mod tests {
|
|||||||
fn type_check() {
|
fn type_check() {
|
||||||
let validation = Assignment::new(
|
let validation = Assignment::new(
|
||||||
Identifier::new("foobar"),
|
Identifier::new("foobar"),
|
||||||
Some(Type::Boolean),
|
Some(Positioned {
|
||||||
|
node: Type::Boolean,
|
||||||
|
position: (0, 0),
|
||||||
|
}),
|
||||||
AssignmentOperator::Assign,
|
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());
|
.validate(&Context::new());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
validation,
|
validation,
|
||||||
Err(ValidationError::TypeCheck(TypeCheckError {
|
Err(ValidationError::TypeCheck {
|
||||||
actual: Type::Integer,
|
conflict: TypeConflict {
|
||||||
expected: Type::Boolean
|
actual: Type::Integer,
|
||||||
}))
|
expected: Type::Boolean
|
||||||
|
},
|
||||||
|
actual_position: (0, 0),
|
||||||
|
expected_position: (0, 0),
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,9 +61,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn run_returns_value_of_final_statement() {
|
fn run_returns_value_of_final_statement() {
|
||||||
let block = Block::new(vec![
|
let block = Block::new(vec![
|
||||||
Statement::expression(Expression::Value(ValueNode::Integer(1)), (0..0).into()),
|
Statement::Expression(Expression::Value(ValueNode::Integer(1))),
|
||||||
Statement::expression(Expression::Value(ValueNode::Integer(2)), (0..0).into()),
|
Statement::Expression(Expression::Value(ValueNode::Integer(2))),
|
||||||
Statement::expression(Expression::Value(ValueNode::Integer(42)), (0..0).into()),
|
Statement::Expression(Expression::Value(ValueNode::Integer(42))),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -75,11 +75,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn expected_type_returns_type_of_final_statement() {
|
fn expected_type_returns_type_of_final_statement() {
|
||||||
let block = Block::new(vec![
|
let block = Block::new(vec![
|
||||||
Statement::expression(
|
Statement::Expression(Expression::Value(ValueNode::String("42".to_string()))),
|
||||||
Expression::Value(ValueNode::String("42".to_string())),
|
Statement::Expression(Expression::Value(ValueNode::Integer(42))),
|
||||||
(0..0).into(),
|
|
||||||
),
|
|
||||||
Statement::expression(Expression::Value(ValueNode::Integer(42)), (0..0).into()),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(block.expected_type(&Context::new()), Ok(Type::Integer))
|
assert_eq!(block.expected_type(&Context::new()), Ok(Type::Integer))
|
||||||
|
@ -3,17 +3,21 @@ use crate::{
|
|||||||
error::{RuntimeError, ValidationError},
|
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)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct IfElse {
|
pub struct IfElse {
|
||||||
if_expression: Expression,
|
if_expression: Positioned<Expression>,
|
||||||
if_block: Block,
|
if_block: Positioned<Block>,
|
||||||
else_block: Option<Block>,
|
else_block: Option<Positioned<Block>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IfElse {
|
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 {
|
Self {
|
||||||
if_expression,
|
if_expression,
|
||||||
if_block,
|
if_block,
|
||||||
@ -24,16 +28,22 @@ impl IfElse {
|
|||||||
|
|
||||||
impl AbstractTree for IfElse {
|
impl AbstractTree for IfElse {
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
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> {
|
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 {
|
if let Some(else_block) = &self.else_block {
|
||||||
let expected = self.if_block.expected_type(context)?;
|
let expected = self.if_block.node.expected_type(context)?;
|
||||||
let actual = else_block.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(())
|
Ok(())
|
||||||
@ -45,14 +55,15 @@ impl AbstractTree for IfElse {
|
|||||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||||
let if_boolean = self
|
let if_boolean = self
|
||||||
.if_expression
|
.if_expression
|
||||||
|
.node
|
||||||
.run(_context)?
|
.run(_context)?
|
||||||
.as_return_value()?
|
.as_return_value()?
|
||||||
.as_boolean()?;
|
.as_boolean()?;
|
||||||
|
|
||||||
if if_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 if let Some(else_statement) = self.else_block {
|
||||||
else_statement.run(_context)
|
else_statement.node.run(_context)
|
||||||
} else {
|
} else {
|
||||||
Ok(Action::None)
|
Ok(Action::None)
|
||||||
}
|
}
|
||||||
@ -72,11 +83,11 @@ mod tests {
|
|||||||
fn simple_if() {
|
fn simple_if() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
IfElse::new(
|
IfElse::new(
|
||||||
Expression::Value(ValueNode::Boolean(true)),
|
Expression::Value(ValueNode::Boolean(true)).positioned((0, 0)),
|
||||||
Block::new(vec![Statement::expression(
|
Block::new(vec![Statement::Expression(Expression::Value(
|
||||||
Expression::Value(ValueNode::String("foo".to_string())),
|
ValueNode::String("foo".to_string())
|
||||||
(0..0).into()
|
),)])
|
||||||
)]),
|
.positioned((0, 0)),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
.run(&Context::new()),
|
.run(&Context::new()),
|
||||||
@ -88,15 +99,17 @@ mod tests {
|
|||||||
fn simple_if_else() {
|
fn simple_if_else() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
IfElse::new(
|
IfElse::new(
|
||||||
Expression::Value(ValueNode::Boolean(false)),
|
Expression::Value(ValueNode::Boolean(false)).positioned((0, 0)),
|
||||||
Block::new(vec![Statement::expression(
|
Block::new(vec![Statement::Expression(Expression::Value(
|
||||||
Expression::Value(ValueNode::String("foo".to_string())),
|
ValueNode::String("foo".to_string())
|
||||||
(0..0).into()
|
))])
|
||||||
),]),
|
.positioned((0, 0)),
|
||||||
Some(Block::new(vec![Statement::expression(
|
Some(
|
||||||
Expression::Value(ValueNode::String("bar".to_string())),
|
Block::new(vec![Statement::Expression(Expression::Value(
|
||||||
(0..0).into()
|
ValueNode::String("bar".to_string())
|
||||||
)]))
|
))])
|
||||||
|
.positioned((0, 0))
|
||||||
|
)
|
||||||
)
|
)
|
||||||
.run(&Context::new()),
|
.run(&Context::new()),
|
||||||
Ok(Action::Return(Value::string("bar".to_string())))
|
Ok(Action::Return(Value::string("bar".to_string())))
|
||||||
|
39
src/abstract_tree/item.rs
Normal file
39
src/abstract_tree/item.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
@ -36,10 +36,23 @@ use crate::{
|
|||||||
Value,
|
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 expected_type(&self, context: &Context) -> Result<Type, ValidationError>;
|
||||||
fn validate(&self, context: &Context) -> Result<(), ValidationError>;
|
fn validate(&self, context: &Context) -> Result<(), ValidationError>;
|
||||||
fn run(self, context: &Context) -> Result<Action, RuntimeError>;
|
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)]
|
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
|
@ -8,72 +8,7 @@ use crate::{
|
|||||||
use super::{AbstractTree, Action, Assignment, Block, Expression, IfElse, Loop, Type, While};
|
use super::{AbstractTree, Action, Assignment, Block, Expression, IfElse, Loop, Type, While};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Statement {
|
pub enum 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 {
|
|
||||||
Assignment(Assignment),
|
Assignment(Assignment),
|
||||||
Block(Block),
|
Block(Block),
|
||||||
Break,
|
Break,
|
||||||
@ -85,7 +20,7 @@ pub enum StatementInner {
|
|||||||
|
|
||||||
impl AbstractTree for Statement {
|
impl AbstractTree for Statement {
|
||||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||||
match &self.inner {
|
match self {
|
||||||
StatementInner::Assignment(assignment) => assignment.expected_type(_context),
|
StatementInner::Assignment(assignment) => assignment.expected_type(_context),
|
||||||
StatementInner::Block(block) => block.expected_type(_context),
|
StatementInner::Block(block) => block.expected_type(_context),
|
||||||
StatementInner::Break => Ok(Type::None),
|
StatementInner::Break => Ok(Type::None),
|
||||||
@ -97,7 +32,7 @@ impl AbstractTree for Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
fn validate(&self, _context: &Context) -> Result<(), ValidationError> {
|
||||||
match &self.inner {
|
match self {
|
||||||
StatementInner::Assignment(assignment) => assignment.validate(_context),
|
StatementInner::Assignment(assignment) => assignment.validate(_context),
|
||||||
StatementInner::Block(block) => block.validate(_context),
|
StatementInner::Block(block) => block.validate(_context),
|
||||||
StatementInner::Break => Ok(()),
|
StatementInner::Break => Ok(()),
|
||||||
@ -109,7 +44,7 @@ impl AbstractTree for Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
fn run(self, _context: &Context) -> Result<Action, RuntimeError> {
|
||||||
match self.inner {
|
match self {
|
||||||
StatementInner::Assignment(assignment) => assignment.run(_context),
|
StatementInner::Assignment(assignment) => assignment.run(_context),
|
||||||
StatementInner::Block(block) => block.run(_context),
|
StatementInner::Block(block) => block.run(_context),
|
||||||
StatementInner::Break => Ok(Action::Break),
|
StatementInner::Break => Ok(Action::Break),
|
||||||
|
@ -3,7 +3,7 @@ use std::fmt::{self, Display, Formatter};
|
|||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::Identifier,
|
abstract_tree::Identifier,
|
||||||
context::Context,
|
context::Context,
|
||||||
error::{RuntimeError, TypeCheckError, ValidationError},
|
error::{RuntimeError, TypeConflict, ValidationError},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{AbstractTree, Action};
|
use super::{AbstractTree, Action};
|
||||||
@ -29,7 +29,7 @@ pub enum Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Type {
|
impl Type {
|
||||||
pub fn check(&self, other: &Type) -> Result<(), TypeCheckError> {
|
pub fn check(&self, other: &Type) -> Result<(), TypeConflict> {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Type::Any, _)
|
(Type::Any, _)
|
||||||
| (_, Type::Any)
|
| (_, Type::Any)
|
||||||
@ -49,7 +49,7 @@ impl Type {
|
|||||||
if left == right {
|
if left == right {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(TypeCheckError {
|
Err(TypeConflict {
|
||||||
actual: other.clone(),
|
actual: other.clone(),
|
||||||
expected: self.clone(),
|
expected: self.clone(),
|
||||||
})
|
})
|
||||||
@ -59,7 +59,7 @@ impl Type {
|
|||||||
if let Ok(()) = left.check(right) {
|
if let Ok(()) = left.check(right) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(TypeCheckError {
|
Err(TypeConflict {
|
||||||
actual: left.as_ref().clone(),
|
actual: left.as_ref().clone(),
|
||||||
expected: right.as_ref().clone(),
|
expected: right.as_ref().clone(),
|
||||||
})
|
})
|
||||||
@ -86,7 +86,7 @@ impl Type {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ => Err(TypeCheckError {
|
_ => Err(TypeConflict {
|
||||||
actual: other.clone(),
|
actual: other.clone(),
|
||||||
expected: self.clone(),
|
expected: self.clone(),
|
||||||
}),
|
}),
|
||||||
@ -188,14 +188,14 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
foo.check(&bar),
|
foo.check(&bar),
|
||||||
Err(TypeCheckError {
|
Err(TypeConflict {
|
||||||
actual: bar.clone(),
|
actual: bar.clone(),
|
||||||
expected: foo.clone()
|
expected: foo.clone()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
bar.check(&foo),
|
bar.check(&foo),
|
||||||
Err(TypeCheckError {
|
Err(TypeConflict {
|
||||||
actual: foo.clone(),
|
actual: foo.clone(),
|
||||||
expected: bar.clone()
|
expected: bar.clone()
|
||||||
})
|
})
|
||||||
@ -222,7 +222,7 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
left.check(right),
|
left.check(right),
|
||||||
Err(TypeCheckError {
|
Err(TypeConflict {
|
||||||
actual: right.clone(),
|
actual: right.clone(),
|
||||||
expected: left.clone()
|
expected: left.clone()
|
||||||
})
|
})
|
||||||
|
61
src/error.rs
61
src/error.rs
@ -19,10 +19,7 @@ pub enum Error {
|
|||||||
span: (usize, usize),
|
span: (usize, usize),
|
||||||
},
|
},
|
||||||
Runtime(RuntimeError),
|
Runtime(RuntimeError),
|
||||||
Validation {
|
Validation(ValidationError),
|
||||||
error: ValidationError,
|
|
||||||
span: (usize, usize),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -49,33 +46,38 @@ impl Error {
|
|||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
Error::Runtime(_) => todo!(),
|
Error::Runtime(_) => todo!(),
|
||||||
Error::Validation { error, span } => {
|
Error::Validation(validation_error) => {
|
||||||
let mut report = Report::build(
|
let mut report =
|
||||||
ReportKind::Custom("Validation Error", Color::White),
|
Report::build(ReportKind::Custom("Validation Error", Color::White), (), 0);
|
||||||
(),
|
|
||||||
span.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
match error {
|
match validation_error {
|
||||||
ValidationError::ExpectedBoolean => {
|
ValidationError::ExpectedBoolean => {
|
||||||
report = report.with_label(
|
report =
|
||||||
Label::new(span.0..span.1).with_message("Expected boolean."),
|
report.with_label(Label::new(0..0).with_message("Expected boolean."));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
ValidationError::ExpectedIntegerOrFloat => {
|
ValidationError::ExpectedIntegerOrFloat => {
|
||||||
report = report.with_label(
|
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::RwLockPoison(_) => todo!(),
|
||||||
ValidationError::TypeCheck(TypeCheckError { actual, expected }) => {
|
ValidationError::TypeCheck {
|
||||||
report = report.with_label(Label::new(span.0..span.1).with_message(
|
conflict,
|
||||||
format!("Type error. Expected {expected} but got {actual}."),
|
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) => {
|
ValidationError::VariableNotFound(identifier) => {
|
||||||
report = report
|
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."
|
"The variable {identifier} does not exist."
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -144,7 +146,16 @@ pub enum ValidationError {
|
|||||||
ExpectedValue,
|
ExpectedValue,
|
||||||
InterpreterExpectedReturn,
|
InterpreterExpectedReturn,
|
||||||
RwLockPoison(RwLockPoisonError),
|
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),
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct RwLockPoisonError;
|
pub struct RwLockPoisonError;
|
||||||
|
|
||||||
@ -170,7 +175,7 @@ impl<T> From<PoisonError<T>> for RwLockPoisonError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct TypeCheckError {
|
pub struct TypeConflict {
|
||||||
pub actual: Type,
|
pub actual: Type,
|
||||||
pub expected: Type,
|
pub expected: Type,
|
||||||
}
|
}
|
||||||
|
@ -36,11 +36,8 @@ impl Interpreter {
|
|||||||
.filter_map(|statement| {
|
.filter_map(|statement| {
|
||||||
statement
|
statement
|
||||||
.validate(&self.context)
|
.validate(&self.context)
|
||||||
.map_err(|validation_error| Error::Validation {
|
|
||||||
error: validation_error,
|
|
||||||
span: statement.span(),
|
|
||||||
})
|
|
||||||
.err()
|
.err()
|
||||||
|
.map(|validation_error| Error::Validation(validation_error))
|
||||||
})
|
})
|
||||||
.collect::<Vec<Error>>();
|
.collect::<Vec<Error>>();
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use std::collections::BTreeMap;
|
|||||||
|
|
||||||
use dust_lang::{
|
use dust_lang::{
|
||||||
abstract_tree::{Identifier, Type},
|
abstract_tree::{Identifier, Type},
|
||||||
error::{Error, TypeCheckError, ValidationError},
|
error::{Error, TypeConflict, ValidationError},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ fn map_type_errors() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
interpret("{ foo : bool = 'bar' }"),
|
interpret("{ foo : bool = 'bar' }"),
|
||||||
Err(vec![Error::Validation {
|
Err(vec![Error::Validation {
|
||||||
error: ValidationError::TypeCheck(TypeCheckError {
|
error: ValidationError::TypeCheck(TypeConflict {
|
||||||
actual: Type::String,
|
actual: Type::String,
|
||||||
expected: Type::Boolean
|
expected: Type::Boolean
|
||||||
}),
|
}),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use dust_lang::{
|
use dust_lang::{
|
||||||
abstract_tree::{Block, Expression, Identifier, Statement, Type},
|
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!(
|
assert_eq!(
|
||||||
interpret("foobar: str = true"),
|
interpret("foobar: str = true"),
|
||||||
Err(vec![Error::Validation {
|
Err(vec![Error::Validation {
|
||||||
error: ValidationError::TypeCheck(TypeCheckError {
|
error: ValidationError::TypeCheck(TypeConflict {
|
||||||
actual: Type::Boolean,
|
actual: Type::Boolean,
|
||||||
expected: Type::String
|
expected: Type::String
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user