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 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)]
pub struct Assignment {
@ -9,6 +11,7 @@ pub struct Assignment {
type_definition: Option<TypeDefinition>,
operator: AssignmentOperator,
statement: Statement,
syntax_position: SyntaxPosition,
}
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -19,15 +22,15 @@ pub enum AssignmentOperator {
}
impl AbstractTree for Assignment {
fn from_syntax_node(source: &str, node: Node, context: &Map) -> Result<Self> {
Error::expect_syntax_node(source, "assignment", node)?;
fn from_syntax_node(source: &str, syntax_node: SyntaxNode, context: &Map) -> Result<Self> {
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 type_node = node.child(1).unwrap();
let type_node = syntax_node.child(1).unwrap();
let type_definition = if type_node.kind() == "type_definition" {
Some(TypeDefinition::from_syntax_node(
source, type_node, context,
@ -36,7 +39,11 @@ impl AbstractTree for Assignment {
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() {
"=" => AssignmentOperator::Equal,
"+=" => 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_type = statement.expected_type(context)?;
@ -71,16 +78,20 @@ impl AbstractTree for Assignment {
type_definition,
operator,
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)?;
if let Some(type_definition) = &self.type_definition {
match self.operator {
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 => {
if let Type::List(item_type) = type_definition.inner() {
@ -88,7 +99,10 @@ impl AbstractTree for Assignment {
} else {
type_definition
.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!(),
@ -98,14 +112,16 @@ impl AbstractTree for Assignment {
AssignmentOperator::Equal => {}
AssignmentOperator::PlusEqual => {
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!(),
}
}
self.statement.check_type(context)?;
self.statement.check_type(source, context)?;
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 {
if let Statement::Return(inner_statement) = statement {
return inner_statement.check_type(_context);
return inner_statement.check_type(_source, _context);
} else {
statement.check_type(_context)?;
statement.check_type(_source, _context)?;
}
}

View File

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

View File

@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use tree_sitter::Node;
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)]
@ -10,14 +11,21 @@ pub struct FunctionNode {
parameters: Vec<Identifier>,
body: Block,
r#type: Type,
syntax_position: SyntaxPosition,
}
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 {
parameters,
body,
r#type,
syntax_position,
}
}
@ -114,13 +122,15 @@ impl AbstractTree for FunctionNode {
let body = Block::from_syntax_node(source, body_node, &function_context)?;
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()
.check(&self.body.expected_type(context)?)?;
.check(&self.body.expected_type(context)?)
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
Ok(())
}

View File

@ -35,10 +35,34 @@ pub use {
r#match::*, r#while::*, r#yield::*, statement::*, type_definition::*, value_node::*,
};
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
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 {
statements: Vec<Statement>,
}
@ -60,12 +84,12 @@ impl AbstractTree for Root {
Ok(Root { statements })
}
fn check_type(&self, _context: &Map) -> Result<()> {
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
for statement in &self.statements {
if let Statement::Return(inner_statement) = statement {
return inner_statement.check_type(_context);
return inner_statement.check_type(_source, _context);
} 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>;
/// Verify the type integrity of the node.
fn check_type(&self, _context: &Map) -> Result<()> {
fn check_type(&self, _source: &str, _context: &Map) -> Result<()> {
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 {
Statement::Assignment(assignment) => assignment.check_type(_context),
Statement::Expression(expression) => expression.check_type(_context),
Statement::IfElse(if_else) => if_else.check_type(_context),
Statement::Match(r#match) => r#match.check_type(_context),
Statement::While(r#while) => r#while.check_type(_context),
Statement::Block(block) => block.check_type(_context),
Statement::For(r#for) => r#for.check_type(_context),
Statement::IndexAssignment(index_assignment) => index_assignment.check_type(_context),
Statement::Return(statement) => statement.check_type(_context),
Statement::Assignment(assignment) => assignment.check_type(_source, _context),
Statement::Expression(expression) => expression.check_type(_source, _context),
Statement::IfElse(if_else) => if_else.check_type(_source, _context),
Statement::Match(r#match) => r#match.check_type(_source, _context),
Statement::While(r#while) => r#while.check_type(_source, _context),
Statement::Block(block) => block.check_type(_source, _context),
Statement::For(r#for) => r#for.check_type(_source, _context),
Statement::IndexAssignment(index_assignment) => {
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 tree_sitter::{LanguageError, Node, Point};
use crate::{value::Value, Type};
use crate::{value::Value, SyntaxPosition, Type};
use std::{
fmt::{self, Formatter},
@ -21,11 +21,13 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum Error {
WithContext {
AtSourcePosition {
error: Box<Error>,
#[serde(skip)]
location: Point,
source: String,
start_row: usize,
start_column: usize,
end_row: usize,
end_column: usize,
},
UnexpectedSyntaxNode {
@ -181,11 +183,16 @@ pub enum Error {
}
impl Error {
pub fn at_node(self, node: Node, source: &str) -> Self {
Error::WithContext {
pub fn at_source_position(self, source: &str, position: SyntaxPosition) -> Self {
let byte_range = position.start_byte..position.end_byte;
Error::AtSourcePosition {
error: Box::new(self),
location: node.start_position(),
source: source[node.byte_range()].to_string(),
source: source[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 {
match self {
Error::WithContext { error, .. } => error.as_ref() == other,
Error::AtSourcePosition { error, .. } => error.as_ref() == other,
_ => self == other,
}
}
@ -427,14 +434,18 @@ impl fmt::Display for Error {
TypeCheckExpectedFunction { actual } => {
write!(f, "Type check error. Expected a function but got {actual}.")
}
WithContext {
AtSourcePosition {
error,
location,
source,
start_row,
start_column,
end_row,
end_column,
} => {
let location = get_position(location);
write!(f, "{error} Occured at {location}: \"{source}\"")
write!(
f,
"{error} Occured at ({start_row}, {start_column}) to ({end_row}, {end_column}). Source: {source}"
)
}
SerdeJson(message) => write!(f, "JSON processing error: {message}"),
ParserCancelled => write!(

View File

@ -115,7 +115,7 @@ impl Interpreter {
};
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)
} else {
Ok(Value::none())

View File

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

View File

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