diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index 9f68362..50b092f 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -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, 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 { - Error::expect_syntax_node(source, "assignment", node)?; + fn from_syntax_node(source: &str, syntax_node: SyntaxNode, context: &Map) -> Result { + 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(()) } diff --git a/src/abstract_tree/block.rs b/src/abstract_tree/block.rs index ebe55af..18cb183 100644 --- a/src/abstract_tree/block.rs +++ b/src/abstract_tree/block.rs @@ -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)?; } } diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index 4930236..44b889b 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -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), } } diff --git a/src/abstract_tree/function_call.rs b/src/abstract_tree/function_call.rs index 5fd333e..d97a58f 100644 --- a/src/abstract_tree/function_call.rs +++ b/src/abstract_tree/function_call.rs @@ -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 { diff --git a/src/abstract_tree/function_node.rs b/src/abstract_tree/function_node.rs index 66bac94..f2d8f1e 100644 --- a/src/abstract_tree/function_node.rs +++ b/src/abstract_tree/function_node.rs @@ -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, body: Block, r#type: Type, + syntax_position: SyntaxPosition, } impl FunctionNode { - pub fn new(parameters: Vec, body: Block, r#type: Type) -> Self { + pub fn new( + parameters: Vec, + 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(()) } diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index 18dca33..dd64b8c 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -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 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, } @@ -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; /// Verify the type integrity of the node. - fn check_type(&self, _context: &Map) -> Result<()> { + fn check_type(&self, _source: &str, _context: &Map) -> Result<()> { Ok(()) } diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index 861983b..ba7555d 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -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), } } diff --git a/src/error.rs b/src/error.rs index b3d58ab..84707be 100644 --- a/src/error.rs +++ b/src/error.rs @@ -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 = std::result::Result; #[derive(Clone, PartialEq, Serialize, Deserialize)] pub enum Error { - WithContext { + AtSourcePosition { error: Box, - #[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!( diff --git a/src/interpret.rs b/src/interpret.rs index 2b07ff6..70724bf 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -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()) diff --git a/src/lib.rs b/src/lib.rs index 3878990..92b79fb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/value/function.rs b/src/value/function.rs index be664ce..038e136 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -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) + } } }