1
0

Add new means of reporting type check errors

This commit is contained in:
Jeff 2024-01-05 22:26:37 -05:00
parent d4487117eb
commit bb53331b65
11 changed files with 129 additions and 62 deletions

View File

@ -1,7 +1,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Identifier, Map, Result, Statement, Type, TypeDefinition, Value}; use crate::{
AbstractTree, Error, Identifier, Map, Result, Statement, SyntaxNode, SyntaxPosition, Type,
TypeDefinition, Value,
};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Assignment { pub struct Assignment {
@ -9,6 +11,7 @@ pub struct Assignment {
type_definition: Option<TypeDefinition>, type_definition: Option<TypeDefinition>,
operator: AssignmentOperator, operator: AssignmentOperator,
statement: Statement, statement: Statement,
syntax_position: SyntaxPosition,
} }
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -19,15 +22,15 @@ pub enum AssignmentOperator {
} }
impl AbstractTree for Assignment { impl AbstractTree for Assignment {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> { fn from_syntax_node(source: &str, syntax_node: SyntaxNode, context: &Map) -> Result<Self> {
Error::expect_syntax_node(source, "assignment", node)?; Error::expect_syntax_node(source, "assignment", syntax_node)?;
let child_count = node.child_count(); let child_count = syntax_node.child_count();
let identifier_node = node.child(0).unwrap(); let identifier_node = syntax_node.child(0).unwrap();
let identifier = Identifier::from_syntax_node(source, identifier_node, context)?; let identifier = Identifier::from_syntax_node(source, identifier_node, context)?;
let type_node = node.child(1).unwrap(); let type_node = syntax_node.child(1).unwrap();
let type_definition = if type_node.kind() == "type_definition" { let type_definition = if type_node.kind() == "type_definition" {
Some(TypeDefinition::from_syntax_node( Some(TypeDefinition::from_syntax_node(
source, type_node, context, source, type_node, context,
@ -36,7 +39,11 @@ impl AbstractTree for Assignment {
None None
}; };
let operator_node = node.child(child_count - 2).unwrap().child(0).unwrap(); let operator_node = syntax_node
.child(child_count - 2)
.unwrap()
.child(0)
.unwrap();
let operator = match operator_node.kind() { let operator = match operator_node.kind() {
"=" => AssignmentOperator::Equal, "=" => AssignmentOperator::Equal,
"+=" => AssignmentOperator::PlusEqual, "+=" => AssignmentOperator::PlusEqual,
@ -51,7 +58,7 @@ impl AbstractTree for Assignment {
} }
}; };
let statement_node = node.child(child_count - 1).unwrap(); let statement_node = syntax_node.child(child_count - 1).unwrap();
let statement = Statement::from_syntax_node(source, statement_node, context)?; let statement = Statement::from_syntax_node(source, statement_node, context)?;
let statement_type = statement.expected_type(context)?; let statement_type = statement.expected_type(context)?;
@ -71,16 +78,20 @@ impl AbstractTree for Assignment {
type_definition, type_definition,
operator, operator,
statement, statement,
syntax_position: syntax_node.range().into(),
}) })
} }
fn check_type(&self, context: &Map) -> Result<()> { fn check_type(&self, source: &str, context: &Map) -> Result<()> {
let statement_type = self.statement.expected_type(context)?; let statement_type = self.statement.expected_type(context)?;
if let Some(type_definition) = &self.type_definition { if let Some(type_definition) = &self.type_definition {
match self.operator { match self.operator {
AssignmentOperator::Equal => { AssignmentOperator::Equal => {
type_definition.inner().check(&statement_type)?; type_definition
.inner()
.check(&statement_type)
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
} }
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Type::List(item_type) = type_definition.inner() { if let Type::List(item_type) = type_definition.inner() {
@ -88,7 +99,10 @@ impl AbstractTree for Assignment {
} else { } else {
type_definition type_definition
.inner() .inner()
.check(&self.identifier.expected_type(context)?)?; .check(&self.identifier.expected_type(context)?)
.map_err(|error| {
error.at_source_position(source, self.syntax_position)
})?;
} }
} }
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
@ -98,14 +112,16 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => {} AssignmentOperator::Equal => {}
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Type::List(item_type) = self.identifier.expected_type(context)? { if let Type::List(item_type) = self.identifier.expected_type(context)? {
item_type.check(&statement_type)?; item_type.check(&statement_type).map_err(|error| {
error.at_source_position(source, self.syntax_position)
})?;
} }
} }
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
} }
} }
self.statement.check_type(context)?; self.statement.check_type(source, context)?;
Ok(()) Ok(())
} }

View File

@ -50,12 +50,12 @@ impl AbstractTree for Block {
}) })
} }
fn check_type(&self, _context: &Map) -> Result<()> { fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
for statement in &self.statements { for statement in &self.statements {
if let Statement::Return(inner_statement) = statement { if let Statement::Return(inner_statement) = statement {
return inner_statement.check_type(_context); return inner_statement.check_type(_source, _context);
} else { } else {
statement.check_type(_context)?; statement.check_type(_source, _context)?;
} }
} }

View File

@ -65,15 +65,15 @@ impl AbstractTree for Expression {
Ok(expression) Ok(expression)
} }
fn check_type(&self, _context: &Map) -> Result<()> { fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
match self { match self {
Expression::Value(value_node) => value_node.check_type(_context), Expression::Value(value_node) => value_node.check_type(_source, _context),
Expression::Identifier(identifier) => identifier.check_type(_context), Expression::Identifier(identifier) => identifier.check_type(_source, _context),
Expression::Math(math) => math.check_type(_context), Expression::Math(math) => math.check_type(_source, _context),
Expression::Logic(logic) => logic.check_type(_context), Expression::Logic(logic) => logic.check_type(_source, _context),
Expression::FunctionCall(function_call) => function_call.check_type(_context), Expression::FunctionCall(function_call) => function_call.check_type(_source, _context),
Expression::Index(index) => index.check_type(_context), Expression::Index(index) => index.check_type(_source, _context),
Expression::Yield(r#yield) => r#yield.check_type(_context), Expression::Yield(r#yield) => r#yield.check_type(_source, _context),
} }
} }

View File

@ -44,7 +44,7 @@ impl AbstractTree for FunctionCall {
}) })
} }
fn check_type(&self, context: &Map) -> Result<()> { fn check_type(&self, _source: &str, context: &Map) -> Result<()> {
let function_expression_type = self.function_expression.expected_type(context)?; let function_expression_type = self.function_expression.expected_type(context)?;
let parameter_types = match function_expression_type { let parameter_types = match function_expression_type {

View File

@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use tree_sitter::Node; use tree_sitter::Node;
use crate::{ use crate::{
AbstractTree, Block, Error, Function, Identifier, Map, Result, Type, TypeDefinition, Value, AbstractTree, Block, Error, Function, Identifier, Map, Result, SyntaxPosition, Type,
TypeDefinition, Value,
}; };
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
@ -10,14 +11,21 @@ pub struct FunctionNode {
parameters: Vec<Identifier>, parameters: Vec<Identifier>,
body: Block, body: Block,
r#type: Type, r#type: Type,
syntax_position: SyntaxPosition,
} }
impl FunctionNode { impl FunctionNode {
pub fn new(parameters: Vec<Identifier>, body: Block, r#type: Type) -> Self { pub fn new(
parameters: Vec<Identifier>,
body: Block,
r#type: Type,
syntax_position: SyntaxPosition,
) -> Self {
Self { Self {
parameters, parameters,
body, body,
r#type, r#type,
syntax_position,
} }
} }
@ -114,13 +122,15 @@ impl AbstractTree for FunctionNode {
let body = Block::from_syntax_node(source, body_node, &function_context)?; let body = Block::from_syntax_node(source, body_node, &function_context)?;
let r#type = Type::function(parameter_types, return_type.take_inner()); let r#type = Type::function(parameter_types, return_type.take_inner());
let syntax_position = node.range().into();
Ok(FunctionNode::new(parameters, body, r#type)) Ok(FunctionNode::new(parameters, body, r#type, syntax_position))
} }
fn check_type(&self, context: &Map) -> Result<()> { fn check_type(&self, source: &str, context: &Map) -> Result<()> {
self.return_type() self.return_type()
.check(&self.body.expected_type(context)?)?; .check(&self.body.expected_type(context)?)
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
Ok(()) Ok(())
} }

View File

@ -35,10 +35,34 @@ pub use {
r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*, r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
}; };
use serde::{Deserialize, Serialize};
use tree_sitter::Node; use tree_sitter::Node;
use crate::{Error, Map, Result, Value}; use crate::{Error, Map, Result, Value};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct SyntaxPosition {
pub start_byte: usize,
pub end_byte: usize,
pub start_row: usize,
pub start_column: usize,
pub end_row: usize,
pub end_column: usize,
}
impl From<tree_sitter::Range> for SyntaxPosition {
fn from(range: tree_sitter::Range) -> Self {
SyntaxPosition {
start_byte: range.start_byte,
end_byte: range.end_byte,
start_row: range.start_point.row,
start_column: range.start_point.column,
end_row: range.end_point.row,
end_column: range.end_point.column,
}
}
}
pub struct Root { pub struct Root {
statements: Vec<Statement>, statements: Vec<Statement>,
} }
@ -60,12 +84,12 @@ impl AbstractTree for Root {
Ok(Root { statements }) Ok(Root { statements })
} }
fn check_type(&self, _context: &Map) -> Result<()> { fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
for statement in &self.statements { for statement in &self.statements {
if let Statement::Return(inner_statement) = statement { if let Statement::Return(inner_statement) = statement {
return inner_statement.check_type(_context); return inner_statement.check_type(_source, _context);
} else { } else {
statement.check_type(_context)?; statement.check_type(_source, _context)?;
} }
} }
@ -106,7 +130,7 @@ pub trait AbstractTree: Sized {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self>; fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self>;
/// Verify the type integrity of the node. /// Verify the type integrity of the node.
fn check_type(&self, _context: &Map) -> Result<()> { fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
Ok(()) Ok(())
} }

View File

@ -66,17 +66,19 @@ impl AbstractTree for Statement {
} }
} }
fn check_type(&self, _context: &Map) -> Result<()> { fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
match self { match self {
Statement::Assignment(assignment) => assignment.check_type(_context), Statement::Assignment(assignment) => assignment.check_type(_source, _context),
Statement::Expression(expression) => expression.check_type(_context), Statement::Expression(expression) => expression.check_type(_source, _context),
Statement::IfElse(if_else) => if_else.check_type(_context), Statement::IfElse(if_else) => if_else.check_type(_source, _context),
Statement::Match(r#match) => r#match.check_type(_context), Statement::Match(r#match) => r#match.check_type(_source, _context),
Statement::While(r#while) => r#while.check_type(_context), Statement::While(r#while) => r#while.check_type(_source, _context),
Statement::Block(block) => block.check_type(_context), Statement::Block(block) => block.check_type(_source, _context),
Statement::For(r#for) => r#for.check_type(_context), Statement::For(r#for) => r#for.check_type(_source, _context),
Statement::IndexAssignment(index_assignment) => index_assignment.check_type(_context), Statement::IndexAssignment(index_assignment) => {
Statement::Return(statement) => statement.check_type(_context), index_assignment.check_type(_source, _context)
}
Statement::Return(statement) => statement.check_type(_source, _context),
} }
} }

View File

@ -6,7 +6,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::{LanguageError, Node, Point}; use tree_sitter::{LanguageError, Node, Point};
use crate::{value::Value, Type}; use crate::{value::Value, SyntaxPosition, Type};
use std::{ use std::{
fmt::{self, Formatter}, fmt::{self, Formatter},
@ -21,11 +21,13 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, PartialEq, Serialize, Deserialize)] #[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum Error { pub enum Error {
WithContext { AtSourcePosition {
error: Box<Error>, error: Box<Error>,
#[serde(skip)]
location: Point,
source: String, source: String,
start_row: usize,
start_column: usize,
end_row: usize,
end_column: usize,
}, },
UnexpectedSyntaxNode { UnexpectedSyntaxNode {
@ -181,11 +183,16 @@ pub enum Error {
} }
impl Error { impl Error {
pub fn at_node(self, node: Node, source: &str) -> Self { pub fn at_source_position(self, source: &str, position: SyntaxPosition) -> Self {
Error::WithContext { let byte_range = position.start_byte..position.end_byte;
Error::AtSourcePosition {
error: Box::new(self), error: Box::new(self),
location: node.start_position(), source: source[byte_range].to_string(),
source: source[node.byte_range()].to_string(), start_row: position.start_row,
start_column: position.start_column,
end_row: position.end_row,
end_column: position.end_column,
} }
} }
@ -225,7 +232,7 @@ impl Error {
pub fn is_type_check_error(&self, other: &Error) -> bool { pub fn is_type_check_error(&self, other: &Error) -> bool {
match self { match self {
Error::WithContext { error, .. } => error.as_ref() == other, Error::AtSourcePosition { error, .. } => error.as_ref() == other,
_ => self == other, _ => self == other,
} }
} }
@ -427,14 +434,18 @@ impl fmt::Display for Error {
TypeCheckExpectedFunction { actual } => { TypeCheckExpectedFunction { actual } => {
write!(f, "Type check error. Expected a function but got {actual}.") write!(f, "Type check error. Expected a function but got {actual}.")
} }
WithContext { AtSourcePosition {
error, error,
location,
source, source,
start_row,
start_column,
end_row,
end_column,
} => { } => {
let location = get_position(location); write!(
f,
write!(f, "{error} Occured at {location}: \"{source}\"") "{error} Occured at ({start_row}, {start_column}) to ({end_row}, {end_column}). Source: {source}"
)
} }
SerdeJson(message) => write!(f, "JSON processing error: {message}"), SerdeJson(message) => write!(f, "JSON processing error: {message}"),
ParserCancelled => write!( ParserCancelled => write!(

View File

@ -115,7 +115,7 @@ impl Interpreter {
}; };
if let Some(abstract_tree) = &self.abstract_tree { if let Some(abstract_tree) = &self.abstract_tree {
abstract_tree.check_type(&self.context)?; abstract_tree.check_type(source, &self.context)?;
abstract_tree.run(source, &self.context) abstract_tree.run(source, &self.context)
} else { } else {
Ok(Value::none()) Ok(Value::none())

View File

@ -11,6 +11,8 @@ pub use crate::{
value::{function::Function, list::List, map::Map, Value}, value::{function::Function, list::List, map::Map, Value},
}; };
pub use tree_sitter::Node as SyntaxNode;
mod abstract_tree; mod abstract_tree;
pub mod built_in_functions; pub mod built_in_functions;
mod error; mod error;

View File

@ -57,10 +57,12 @@ impl AbstractTree for Function {
Ok(Function::ContextDefined(inner_function)) Ok(Function::ContextDefined(inner_function))
} }
fn check_type(&self, _context: &Map) -> Result<()> { fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
match self { match self {
Function::BuiltIn(_) => Ok(()), Function::BuiltIn(_) => Ok(()),
Function::ContextDefined(defined_function) => defined_function.check_type(_context), Function::ContextDefined(defined_function) => {
defined_function.check_type(_source, _context)
}
} }
} }