1
0

Implement error overhaul

This commit is contained in:
Jeff 2024-01-31 19:07:18 -05:00
parent 7f849f13a3
commit 88ca9c5ea4
33 changed files with 566 additions and 536 deletions

View File

@ -2,8 +2,8 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, AssignmentOperator, Error, Format, Identifier, Map, Statement, SyntaxNode, AbstractTree, AssignmentOperator, Error, Format, Identifier, Map, SourcePosition, Statement,
SyntaxPosition, Type, TypeSpecification, Value, SyntaxNode, Type, TypeSpecification, Value,
}; };
/// Variable assignment, including add-assign and subtract-assign operations. /// Variable assignment, including add-assign and subtract-assign operations.
@ -14,7 +14,7 @@ pub struct Assignment {
operator: AssignmentOperator, operator: AssignmentOperator,
statement: Statement, statement: Statement,
syntax_position: SyntaxPosition, syntax_position: SourcePosition,
} }
impl AbstractTree for Assignment { impl AbstractTree for Assignment {
@ -43,15 +43,15 @@ impl AbstractTree for Assignment {
let statement_node = syntax_node.child(child_count - 1).unwrap(); let statement_node = syntax_node.child(child_count - 1).unwrap();
let statement = Statement::from_syntax(statement_node, source, context)?; let statement = Statement::from_syntax(statement_node, source, context)?;
if let AssignmentOperator::Equal = operator { // if let AssignmentOperator::Equal = operator {
let r#type = if let Some(definition) = &type_specification { // let r#type = if let Some(definition) = &type_specification {
definition.inner().clone() // definition.inner().clone()
} else { // } else {
statement.expected_type(context)? // statement.expected_type(context)?
}; // };
context.set_type(identifier.inner().clone(), r#type)?; // context.set_type(identifier.inner().clone(), r#type)?;
} // }
Ok(Assignment { Ok(Assignment {
identifier, identifier,
@ -63,28 +63,42 @@ impl AbstractTree for Assignment {
} }
fn check_type(&self, source: &str, context: &Map) -> Result<(), ValidationError> { fn check_type(&self, source: &str, context: &Map) -> Result<(), ValidationError> {
let actual_type = self.statement.expected_type(context)?;
if let Some(type_specification) = &self.type_specification { if let Some(type_specification) = &self.type_specification {
match self.operator { match self.operator {
AssignmentOperator::Equal => { AssignmentOperator::Equal => {
type_specification let expected = type_specification.inner();
.inner() let actual = self.statement.expected_type(context)?;
.check(&actual_type)
.map_err(|error| error.at_source_position(source, self.syntax_position))?; if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected: expected.clone(),
actual,
position: self.syntax_position,
});
}
} }
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Type::List(item_type) = type_specification.inner() { if let Type::List(expected) = type_specification.inner() {
item_type.check(&actual_type).map_err(|error| { let actual = self.identifier.expected_type(context)?;
error.at_source_position(source, self.syntax_position)
})?; if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected: expected.as_ref().clone(),
actual,
position: self.syntax_position,
});
}
} else { } else {
type_specification let expected = type_specification.inner();
.inner() let actual = self.identifier.expected_type(context)?;
.check(&self.identifier.expected_type(context)?)
.map_err(|error| { if !expected.accepts(&actual) {
error.at_source_position(source, self.syntax_position) return Err(ValidationError::TypeCheck {
})?; expected: expected.clone(),
actual,
position: self.syntax_position,
});
}
} }
} }
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
@ -93,10 +107,16 @@ impl AbstractTree for Assignment {
match self.operator { match self.operator {
AssignmentOperator::Equal => {} AssignmentOperator::Equal => {}
AssignmentOperator::PlusEqual => { AssignmentOperator::PlusEqual => {
if let Type::List(item_type) = self.identifier.expected_type(context)? { if let Type::List(expected) = self.identifier.expected_type(context)? {
item_type.check(&actual_type).map_err(|error| { let actual = self.statement.expected_type(context)?;
error.at_source_position(source, self.syntax_position)
})?; if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected: expected.as_ref().clone(),
actual,
position: self.syntax_position,
});
}
} }
} }
AssignmentOperator::MinusEqual => todo!(), AssignmentOperator::MinusEqual => todo!(),
@ -118,9 +138,7 @@ impl AbstractTree for Assignment {
previous_value += value; previous_value += value;
previous_value previous_value
} else { } else {
return Err(Error::Runtime(RuntimeError::VariableIdentifierNotFound( return Err(RuntimeError::VariableIdentifierNotFound(key.clone()));
key.clone(),
)));
} }
} }
AssignmentOperator::MinusEqual => { AssignmentOperator::MinusEqual => {
@ -128,9 +146,7 @@ impl AbstractTree for Assignment {
previous_value -= value; previous_value -= value;
previous_value previous_value
} else { } else {
return Err(Error::Runtime(RuntimeError::VariableIdentifierNotFound( return Err(RuntimeError::VariableIdentifierNotFound(key.clone()));
key.clone(),
)));
} }
} }
AssignmentOperator::Equal => value, AssignmentOperator::Equal => value,

View File

@ -27,7 +27,7 @@ impl AbstractTree for AssignmentOperator {
"+=" => AssignmentOperator::PlusEqual, "+=" => AssignmentOperator::PlusEqual,
"-=" => AssignmentOperator::MinusEqual, "-=" => AssignmentOperator::MinusEqual,
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "=, += or -=".to_string(), expected: "=, += or -=".to_string(),
actual: operator_node.kind().to_string(), actual: operator_node.kind().to_string(),
location: operator_node.start_position(), location: operator_node.start_position(),

View File

@ -4,7 +4,7 @@ use rayon::prelude::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{rw_lock_error::RwLockError, RuntimeError, SyntaxError, ValidationError},
AbstractTree, Error, Format, Map, Statement, SyntaxNode, Type, Value, AbstractTree, Error, Format, Map, Statement, SyntaxNode, Type, Value,
}; };
@ -91,13 +91,13 @@ impl AbstractTree for Block {
*final_result = result; *final_result = result;
None None
} }
Err(error) => Some(Err(error.into())), Err(_error) => Some(Err(RuntimeError::RwLock(RwLockError))),
} }
} else { } else {
None None
} }
}) })
.unwrap_or(final_result.into_inner()?) .unwrap_or(final_result.into_inner().map_err(|_| RwLockError)?)
} else { } else {
let mut prev_result = None; let mut prev_result = None;

View File

@ -50,7 +50,7 @@ impl AbstractTree for Expression {
"new" => Expression::New(New::from_syntax(child, source, _context)?), "new" => Expression::New(New::from_syntax(child, source, _context)?),
"command" => Expression::Command(Command::from_syntax(child, source, _context)?), "command" => Expression::Command(Command::from_syntax(child, source, _context)?),
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: expected:
"value, identifier, index, math, logic, function call, new, context or ->" "value, identifier, index, math, logic, function call, new, context or ->"
.to_string(), .to_string(),

View File

@ -24,7 +24,7 @@ impl AbstractTree for For {
"for" => false, "for" => false,
"async for" => true, "async for" => true,
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "for or async for".to_string(), expected: "for or async for".to_string(),
actual: for_node.kind().to_string(), actual: for_node.kind().to_string(),
location: for_node.start_position(), location: for_node.start_position(),

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Error, Expression, Format, FunctionExpression, Map, SyntaxNode, SyntaxPosition, AbstractTree, Error, Expression, Format, FunctionExpression, Map, SourcePosition, SyntaxNode,
Type, Value, Type, Value,
}; };
@ -11,7 +11,7 @@ use crate::{
pub struct FunctionCall { pub struct FunctionCall {
function_expression: FunctionExpression, function_expression: FunctionExpression,
arguments: Vec<Expression>, arguments: Vec<Expression>,
syntax_position: SyntaxPosition, syntax_position: SourcePosition,
} }
impl FunctionCall { impl FunctionCall {
@ -19,7 +19,7 @@ impl FunctionCall {
pub fn new( pub fn new(
function_expression: FunctionExpression, function_expression: FunctionExpression,
arguments: Vec<Expression>, arguments: Vec<Expression>,
syntax_position: SyntaxPosition, syntax_position: SourcePosition,
) -> Self { ) -> Self {
Self { Self {
function_expression, function_expression,
@ -85,7 +85,7 @@ impl AbstractTree for FunctionCall {
} }
} }
fn check_type(&self, source: &str, context: &Map) -> Result<(), ValidationError> { fn check_type(&self, _source: &str, context: &Map) -> Result<(), ValidationError> {
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 {
@ -94,26 +94,32 @@ impl AbstractTree for FunctionCall {
} => parameter_types, } => parameter_types,
Type::Any => return Ok(()), Type::Any => return Ok(()),
_ => { _ => {
return Err(Error::TypeCheckExpectedFunction { return Err(ValidationError::TypeCheckExpectedFunction {
actual: function_expression_type, actual: function_expression_type,
} position: self.syntax_position,
.at_source_position(source, self.syntax_position)) });
} }
}; };
if self.arguments.len() != parameter_types.len() { if self.arguments.len() != parameter_types.len() {
return Err(Error::ExpectedFunctionArgumentAmount { return Err(ValidationError::ExpectedFunctionArgumentAmount {
expected: parameter_types.len(), expected: parameter_types.len(),
actual: self.arguments.len(), actual: self.arguments.len(),
} position: self.syntax_position,
.at_source_position(source, self.syntax_position)); });
} }
for (index, expression) in self.arguments.iter().enumerate() { for (index, expression) in self.arguments.iter().enumerate() {
if let Some(r#type) = parameter_types.get(index) { if let Some(expected) = parameter_types.get(index) {
r#type let actual = expression.expected_type(context)?;
.check(&expression.expected_type(context)?)
.map_err(|error| error.at_source_position(source, self.syntax_position))?; if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected: expected.clone(),
actual,
position: self.syntax_position,
});
}
} }
} }
@ -129,7 +135,7 @@ impl AbstractTree for FunctionCall {
if let Some((value, _)) = variables.get(key) { if let Some((value, _)) = variables.get(key) {
value.clone() value.clone()
} else { } else {
return Err(Error::VariableIdentifierNotFound( return Err(RuntimeError::VariableIdentifierNotFound(
identifier.inner().clone(), identifier.inner().clone(),
)); ));
} }

View File

@ -40,7 +40,7 @@ impl AbstractTree for FunctionExpression {
FunctionExpression::Yield(Box::new(Yield::from_syntax(child, source, context)?)) FunctionExpression::Yield(Box::new(Yield::from_syntax(child, source, context)?))
} }
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "identifier, function call, value, index or yield".to_string(), expected: "identifier, function call, value, index or yield".to_string(),
actual: child.kind().to_string(), actual: child.kind().to_string(),
location: child.start_position(), location: child.start_position(),

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Block, Error, Format, Function, Identifier, Map, SyntaxNode, SyntaxPosition, AbstractTree, Block, Error, Format, Function, Identifier, Map, SourcePosition, SyntaxNode,
Type, TypeSpecification, Value, Type, TypeSpecification, Value,
}; };
@ -13,7 +13,7 @@ pub struct FunctionNode {
parameters: Vec<Identifier>, parameters: Vec<Identifier>,
body: Block, body: Block,
r#type: Type, r#type: Type,
syntax_position: SyntaxPosition, syntax_position: SourcePosition,
} }
impl FunctionNode { impl FunctionNode {
@ -21,7 +21,7 @@ impl FunctionNode {
parameters: Vec<Identifier>, parameters: Vec<Identifier>,
body: Block, body: Block,
r#type: Type, r#type: Type,
syntax_position: SyntaxPosition, syntax_position: SourcePosition,
) -> Self { ) -> Self {
FunctionNode { FunctionNode {
parameters, parameters,
@ -43,7 +43,7 @@ impl FunctionNode {
&self.r#type &self.r#type
} }
pub fn syntax_position(&self) -> &SyntaxPosition { pub fn syntax_position(&self) -> &SourcePosition {
&self.syntax_position &self.syntax_position
} }
@ -164,15 +164,23 @@ impl AbstractTree for FunctionNode {
function_context.set_type(parameter.inner().clone(), parameter_type.clone())?; function_context.set_type(parameter.inner().clone(), parameter_type.clone())?;
} }
return_type let actual = self.body.expected_type(&function_context)?;
.check(&self.body.expected_type(&function_context)?)
.map_err(|error| error.at_source_position(source, self.syntax_position))?; if !return_type.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected: return_type.as_ref().clone(),
actual,
position: self.syntax_position,
});
}
self.body.check_type(source, &function_context)?; self.body.check_type(source, &function_context)?;
Ok(()) Ok(())
} else { } else {
Err(Error::TypeCheckExpectedFunction { Err(ValidationError::TypeCheckExpectedFunction {
actual: self.r#type.clone(), actual: self.r#type.clone(),
position: self.syntax_position,
}) })
} }
} }

View File

@ -47,7 +47,7 @@ impl AbstractTree for Identifier {
if let Some((_value, r#type)) = context.variables()?.get(&self.0) { if let Some((_value, r#type)) = context.variables()?.get(&self.0) {
Ok(r#type.clone()) Ok(r#type.clone())
} else { } else {
Err(Error::VariableIdentifierNotFound(self.0.clone())) Err(ValidationError::VariableIdentifierNotFound(self.clone()))
} }
} }
@ -55,7 +55,7 @@ impl AbstractTree for Identifier {
if let Some((value, _)) = context.variables()?.get(&self.0) { if let Some((value, _)) = context.variables()?.get(&self.0) {
Ok(value.clone()) Ok(value.clone())
} else { } else {
Err(Error::VariableIdentifierNotFound(self.0.clone())) Err(RuntimeError::VariableIdentifierNotFound(self.0.clone()))
} }
} }
} }

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Block, Expression, Format, Map, SyntaxNode, Type, Value, AbstractTree, Block, Expression, Format, Map, SourcePosition, SyntaxNode, Type, Value,
}; };
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -12,6 +12,7 @@ pub struct IfElse {
else_if_expressions: Vec<Expression>, else_if_expressions: Vec<Expression>,
else_if_blocks: Vec<Block>, else_if_blocks: Vec<Block>,
else_block: Option<Block>, else_block: Option<Block>,
source_position: SourcePosition,
} }
impl AbstractTree for IfElse { impl AbstractTree for IfElse {
@ -54,6 +55,7 @@ impl AbstractTree for IfElse {
else_if_expressions, else_if_expressions,
else_if_blocks, else_if_blocks,
else_block, else_block,
source_position: SourcePosition::from(node.range()),
}) })
} }
@ -65,7 +67,7 @@ impl AbstractTree for IfElse {
self.if_expression.check_type(_source, context)?; self.if_expression.check_type(_source, context)?;
self.if_block.check_type(_source, context)?; self.if_block.check_type(_source, context)?;
let expected_type = self.if_block.expected_type(context)?; let expected = self.if_block.expected_type(context)?;
let else_ifs = self let else_ifs = self
.else_if_expressions .else_if_expressions
.iter() .iter()
@ -75,11 +77,27 @@ impl AbstractTree for IfElse {
expression.check_type(_source, context)?; expression.check_type(_source, context)?;
block.check_type(_source, context)?; block.check_type(_source, context)?;
expected_type.check(&block.expected_type(context)?)?; let actual = block.expected_type(context)?;
if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected,
actual,
position: self.source_position,
});
}
} }
if let Some(expression) = self.else_block { if let Some(block) = &self.else_block {
expected_type.check(&expression.expected_type(context)?)?; let actual = block.expected_type(context)?;
if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected,
actual,
position: self.source_position,
});
}
} }
Ok(()) Ok(())

View File

@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Error, Format, IndexExpression, List, Map, SyntaxNode, Type, Value, AbstractTree, Error, Format, IndexExpression, List, Map, SourcePosition, SyntaxNode, Type,
Value,
}; };
/// Abstract representation of an index expression. /// Abstract representation of an index expression.
@ -13,6 +14,7 @@ pub struct Index {
pub collection: IndexExpression, pub collection: IndexExpression,
pub index: IndexExpression, pub index: IndexExpression,
pub index_end: Option<IndexExpression>, pub index_end: Option<IndexExpression>,
source_position: SourcePosition,
} }
impl AbstractTree for Index { impl AbstractTree for Index {
@ -40,6 +42,7 @@ impl AbstractTree for Index {
collection, collection,
index, index,
index_end, index_end,
source_position: SourcePosition::from(node.range()),
}) })
} }
@ -56,7 +59,7 @@ impl AbstractTree for Index {
self.collection.check_type(_source, _context)?; self.collection.check_type(_source, _context)?;
self.index.check_type(_source, _context)?; self.index.check_type(_source, _context)?;
if let Some(index_end) = self.index_end { if let Some(index_end) = &self.index_end {
index_end.check_type(_source, _context)?; index_end.check_type(_source, _context)?;
} }
@ -64,9 +67,9 @@ impl AbstractTree for Index {
} }
fn run(&self, source: &str, context: &Map) -> Result<Value, RuntimeError> { fn run(&self, source: &str, context: &Map) -> Result<Value, RuntimeError> {
let collection = self.collection.run(source, context)?; let value = self.collection.run(source, context)?;
match collection { match value {
Value::List(list) => { Value::List(list) => {
let index = self.index.run(source, context)?.as_integer()? as usize; let index = self.index.run(source, context)?.as_integer()? as usize;
@ -104,7 +107,7 @@ impl AbstractTree for Index {
}; };
if value.is_none() { if value.is_none() {
Err(Error::VariableIdentifierNotFound(key)) Err(RuntimeError::VariableIdentifierNotFound(key))
} else { } else {
Ok(value) Ok(value)
} }
@ -115,7 +118,7 @@ impl AbstractTree for Index {
Ok(Value::string(item.to_string())) Ok(Value::string(item.to_string()))
} }
_ => Err(Error::ExpectedCollection { actual: collection }), _ => Err(RuntimeError::ExpectedCollection { actual: value }),
} }
} }
} }

View File

@ -48,7 +48,7 @@ impl AbstractTree for IndexAssignment {
let index_key = if let IndexExpression::Identifier(identifier) = &self.index.index { let index_key = if let IndexExpression::Identifier(identifier) = &self.index.index {
identifier.inner() identifier.inner()
} else { } else {
return Err(Error::VariableIdentifierNotFound( return Err(RuntimeError::VariableIdentifierNotFound(
self.index.index.run(source, context)?.to_string(), self.index.index.run(source, context)?.to_string(),
)); ));
}; };

View File

@ -37,7 +37,7 @@ impl AbstractTree for IndexExpression {
child, source, context, child, source, context,
)?)), )?)),
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "value, identifier, index or function call".to_string(), expected: "value, identifier, index or function call".to_string(),
actual: child.kind().to_string(), actual: child.kind().to_string(),
location: child.start_position(), location: child.start_position(),

View File

@ -32,7 +32,7 @@ impl AbstractTree for LogicOperator {
">=" => LogicOperator::GreaterOrEqual, ">=" => LogicOperator::GreaterOrEqual,
"<=" => LogicOperator::LessOrEqual, "<=" => LogicOperator::LessOrEqual,
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "==, !=, &&, ||, >, <, >= or <=".to_string(), expected: "==, !=, &&, ||, >, <, >= or <=".to_string(),
actual: operator_node.kind().to_string(), actual: operator_node.kind().to_string(),
location: operator_node.start_position(), location: operator_node.start_position(),

View File

@ -68,12 +68,12 @@ impl AbstractTree for Match {
fn check_type(&self, _source: &str, _context: &Map) -> Result<(), ValidationError> { fn check_type(&self, _source: &str, _context: &Map) -> Result<(), ValidationError> {
self.matcher.check_type(_source, _context)?; self.matcher.check_type(_source, _context)?;
for (expression, statement) in self.options { for (expression, statement) in &self.options {
expression.check_type(_source, _context)?; expression.check_type(_source, _context)?;
statement.check_type(_source, _context)?; statement.check_type(_source, _context)?;
} }
if let Some(statement) = self.fallback { if let Some(statement) = &self.fallback {
statement.check_type(_source, _context)?; statement.check_type(_source, _context)?;
} }

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, Error, Format, Map, SyntaxNode, Type, Value, AbstractTree, Format, Map, SyntaxNode, Type, Value,
}; };
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -24,7 +24,7 @@ impl AbstractTree for MathOperator {
"/" => MathOperator::Divide, "/" => MathOperator::Divide,
"%" => MathOperator::Modulo, "%" => MathOperator::Modulo,
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "+, -, *, / or %".to_string(), expected: "+, -, *, / or %".to_string(),
actual: operator_node.kind().to_string(), actual: operator_node.kind().to_string(),
location: operator_node.start_position(), location: operator_node.start_position(),

View File

@ -50,7 +50,7 @@ use crate::{
}; };
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct SyntaxPosition { pub struct SourcePosition {
pub start_byte: usize, pub start_byte: usize,
pub end_byte: usize, pub end_byte: usize,
pub start_row: usize, pub start_row: usize,
@ -59,9 +59,9 @@ pub struct SyntaxPosition {
pub end_column: usize, pub end_column: usize,
} }
impl From<tree_sitter::Range> for SyntaxPosition { impl From<tree_sitter::Range> for SourcePosition {
fn from(range: tree_sitter::Range) -> Self { fn from(range: tree_sitter::Range) -> Self {
SyntaxPosition { SourcePosition {
start_byte: range.start_byte, start_byte: range.start_byte,
end_byte: range.end_byte, end_byte: range.end_byte,
start_row: range.start_point.row + 1, start_row: range.start_point.row + 1,
@ -143,7 +143,8 @@ impl Format for Root {
/// This trait is implemented by the Evaluator's internal types to form an /// This trait is implemented by the Evaluator's internal types to form an
/// executable tree that resolves to a single value. /// executable tree that resolves to a single value.
pub trait AbstractTree: Sized + Format { pub trait AbstractTree: Sized + Format {
/// Interpret the syntax tree at the given node and return the abstraction. /// Interpret the syntax tree at the given node and return the abstraction. Returns a syntax
/// error if the source is invalid.
/// ///
/// This function is used to convert nodes in the Tree Sitter concrete /// This function is used to convert nodes in the Tree Sitter concrete
/// syntax tree into executable nodes in an abstract tree. This function is /// syntax tree into executable nodes in an abstract tree. This function is
@ -154,13 +155,16 @@ pub trait AbstractTree: Sized + Format {
/// node's byte range. /// node's byte range.
fn from_syntax(node: SyntaxNode, source: &str, context: &Map) -> Result<Self, SyntaxError>; fn from_syntax(node: SyntaxNode, source: &str, context: &Map) -> Result<Self, SyntaxError>;
/// Verify the type integrity of the node. /// Return the type of the value that this abstract node will create when run. Returns a
fn check_type(&self, _source: &str, _context: &Map) -> Result<(), ValidationError>; /// validation error if the tree is invalid.
/// Execute dust code by traversing the tree.
fn run(&self, source: &str, context: &Map) -> Result<Value, RuntimeError>;
fn expected_type(&self, context: &Map) -> Result<Type, ValidationError>; fn expected_type(&self, context: &Map) -> Result<Type, ValidationError>;
/// Verify the type integrity of the node. Returns a validation error if the tree is invalid.
fn check_type(&self, source: &str, context: &Map) -> Result<(), ValidationError>;
/// Execute this node's logic and return a value. Returns a runtime error if the node cannot
/// resolve to a value.
fn run(&self, source: &str, context: &Map) -> Result<Value, RuntimeError>;
} }
pub trait Format { pub trait Format {

View File

@ -56,7 +56,7 @@ impl AbstractTree for Statement {
Ok(Statement::Return(Box::new(Statement::from_syntax(statement_node, source, context)?))) Ok(Statement::Return(Box::new(Statement::from_syntax(statement_node, source, context)?)))
}, },
_ => Err(Error::UnexpectedSyntaxNode { _ => Err(SyntaxError::UnexpectedSyntaxNode {
expected: expected:
"assignment, index assignment, expression, block, return, if...else, while, for or match".to_string(), "assignment, index assignment, expression, block, return, if...else, while, for or match".to_string(),
actual: child.kind().to_string(), actual: child.kind().to_string(),

View File

@ -44,7 +44,11 @@ impl Type {
Type::Option(Box::new(optional_type)) Type::Option(Box::new(optional_type))
} }
pub fn check(&self, other: &Type) -> Result<(), ValidationError> { /// Returns a boolean indicating whether is type is accepting of the other.
///
/// The types do not need to match exactly. For example, the Any variant matches all of the
/// others and the Number variant accepts Number, Integer and Float.
pub fn accepts(&self, other: &Type) -> bool {
log::info!("Checking type {self} against {other}."); log::info!("Checking type {self} against {other}.");
match (self, other) { match (self, other) {
@ -67,41 +71,20 @@ impl Type {
| (Type::Integer, Type::Number) | (Type::Integer, Type::Number)
| (Type::Float, Type::Number) | (Type::Float, Type::Number)
| (Type::None, Type::None) | (Type::None, Type::None)
| (Type::String, Type::String) => Ok(()), | (Type::String, Type::String) => true,
(Type::Custom(left), Type::Custom(right)) => { (Type::Custom(left), Type::Custom(right)) => left == right,
if left == right { (Type::Option(_), Type::None) => true,
Ok(())
} else {
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
}
}
(Type::Option(left), Type::Option(right)) => { (Type::Option(left), Type::Option(right)) => {
if left == right { if let Type::Any = left.as_ref() {
Ok(()) true
} else if let Type::Any = left.as_ref() { } else if left == right {
Ok(()) true
} else if let Type::Any = right.as_ref() {
Ok(())
} else { } else {
Err(Error::TypeCheck { false
expected: self.clone(),
actual: other.clone(),
})
} }
} }
(Type::Option(_), Type::None) | (Type::None, Type::Option(_)) => Ok(()),
(Type::List(self_item_type), Type::List(other_item_type)) => { (Type::List(self_item_type), Type::List(other_item_type)) => {
if self_item_type.check(other_item_type).is_err() { self_item_type.accepts(&other_item_type)
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
} else {
Ok(())
}
} }
( (
Type::Function { Type::Function {
@ -118,27 +101,14 @@ impl Type {
.zip(other_parameter_types.iter()); .zip(other_parameter_types.iter());
for (self_parameter_type, other_parameter_type) in parameter_type_pairs { for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
if self_parameter_type.check(other_parameter_type).is_err() { if self_parameter_type == other_parameter_type {
return Err(Error::TypeCheck { return false;
expected: self.clone(),
actual: other.clone(),
});
} }
} }
if self_return_type.check(other_return_type).is_err() { self_return_type == other_return_type
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
} else {
Ok(())
} }
} _ => false,
_ => Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
}),
} }
} }
@ -210,7 +180,7 @@ impl AbstractTree for Type {
Type::Option(Box::new(inner_type)) Type::Option(Box::new(inner_type))
} }
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "any, bool, float, int, num, str, option, (, [ or {".to_string(), expected: "any, bool, float, int, num, str, option, (, [ or {".to_string(),
actual: type_node.kind().to_string(), actual: type_node.kind().to_string(),
location: type_node.start_position(), location: type_node.start_position(),

View File

@ -79,10 +79,6 @@ impl AbstractTree for ValueNode {
if child.kind() == "statement" { if child.kind() == "statement" {
let statement = Statement::from_syntax(child, source, context)?; let statement = Statement::from_syntax(child, source, context)?;
if let Some(type_specification) = &current_type {
type_specification.check(&statement.expected_type(context)?)?;
}
child_nodes.insert(current_key.clone(), (statement, current_type.clone())); child_nodes.insert(current_key.clone(), (statement, current_type.clone()));
} }
} }
@ -145,20 +141,20 @@ impl AbstractTree for ValueNode {
current_statement = current_statement =
Some(Statement::from_syntax(child_syntax_node, source, context)?); Some(Statement::from_syntax(child_syntax_node, source, context)?);
if let Some(identifier) = &current_identifier { // if let Some(identifier) = &current_identifier {
let r#type = if let Some(r#type) = &current_type { // let r#type = if let Some(r#type) = &current_type {
r#type.clone() // r#type.clone()
} else if let Some(statement) = &current_statement { // } else if let Some(statement) = &current_statement {
statement.expected_type(context)? // statement.expected_type(context)?
} else { // } else {
Type::None // Type::None
}; // };
btree_map.insert( // btree_map.insert(
identifier.inner().clone(), // identifier.inner().clone(),
(current_statement.clone(), r#type.clone()), // (current_statement.clone(), r#type.clone()),
); // );
} // }
} }
} }
@ -172,7 +168,7 @@ impl AbstractTree for ValueNode {
ValueNode::Range(start..=end) ValueNode::Range(start..=end)
} }
_ => { _ => {
return Err(Error::UnexpectedSyntaxNode { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: expected:
"string, integer, float, boolean, range, list, map, option, function or structure" "string, integer, float, boolean, range, list, map, option, function or structure"
.to_string(), .to_string(),

View File

@ -3,7 +3,7 @@ use std::fs::read_to_string;
use enum_iterator::{all, Sequence}; use enum_iterator::{all, Sequence};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, Map, Type, Value}; use crate::{error::RuntimeError, Map, Type, Value};
use super::Callable; use super::Callable;
@ -43,7 +43,7 @@ impl Callable for Fs {
) -> Result<Value, RuntimeError> { ) -> Result<Value, RuntimeError> {
match self { match self {
Fs::ReadFile => { Fs::ReadFile => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let path = arguments.first().unwrap().as_string()?; let path = arguments.first().unwrap().as_string()?;
let file_content = read_to_string(path.as_str())?; let file_content = read_to_string(path.as_str())?;

View File

@ -1,7 +1,7 @@
use enum_iterator::Sequence; use enum_iterator::Sequence;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, Map, Type, Value}; use crate::{error::RuntimeError, Map, Type, Value};
use super::Callable; use super::Callable;
@ -49,7 +49,7 @@ impl Callable for Json {
) -> Result<Value, RuntimeError> { ) -> Result<Value, RuntimeError> {
match self { match self {
Json::Create => { Json::Create => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
let json_string = serde_json::to_string(value)?; let json_string = serde_json::to_string(value)?;
@ -57,7 +57,7 @@ impl Callable for Json {
Ok(Value::String(json_string)) Ok(Value::String(json_string))
} }
Json::CreatePretty => { Json::CreatePretty => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
let json_string = serde_json::to_string_pretty(value)?; let json_string = serde_json::to_string_pretty(value)?;
@ -65,7 +65,7 @@ impl Callable for Json {
Ok(Value::String(json_string)) Ok(Value::String(json_string))
} }
Json::Parse => { Json::Parse => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let json_string = arguments.first().unwrap().as_string()?; let json_string = arguments.first().unwrap().as_string()?;
let value = serde_json::from_str(json_string)?; let value = serde_json::from_str(json_string)?;

View File

@ -7,7 +7,7 @@ use std::fmt::{self, Display, Formatter};
use rand::{random, thread_rng, Rng}; use rand::{random, thread_rng, Rng};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, Format, Map, Type, Value}; use crate::{error::RuntimeError, Format, Map, Type, Value};
use self::{fs::Fs, json::Json, str::StrFunction}; use self::{fs::Fs, json::Json, str::StrFunction};
@ -91,7 +91,7 @@ impl Callable for BuiltInFunction {
) -> Result<Value, RuntimeError> { ) -> Result<Value, RuntimeError> {
match self { match self {
BuiltInFunction::AssertEqual => { BuiltInFunction::AssertEqual => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let left = arguments.first().unwrap(); let left = arguments.first().unwrap();
let right = arguments.get(1).unwrap(); let right = arguments.get(1).unwrap();
@ -105,7 +105,7 @@ impl Callable for BuiltInFunction {
json_function.call(arguments, _source, _outer_context) json_function.call(arguments, _source, _outer_context)
} }
BuiltInFunction::Length => { BuiltInFunction::Length => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
let length = if let Ok(list) = value.as_list() { let length = if let Ok(list) = value.as_list() {
@ -115,7 +115,7 @@ impl Callable for BuiltInFunction {
} else if let Ok(str) = value.as_string() { } else if let Ok(str) = value.as_string() {
str.chars().count() str.chars().count()
} else { } else {
return Err(Error::ExpectedCollection { return Err(RuntimeError::ExpectedCollection {
actual: value.clone(), actual: value.clone(),
}); });
}; };
@ -123,7 +123,7 @@ impl Callable for BuiltInFunction {
Ok(Value::Integer(length as i64)) Ok(Value::Integer(length as i64))
} }
BuiltInFunction::Output => { BuiltInFunction::Output => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
@ -132,17 +132,17 @@ impl Callable for BuiltInFunction {
Ok(Value::none()) Ok(Value::none())
} }
BuiltInFunction::RandomBoolean => { BuiltInFunction::RandomBoolean => {
Error::expect_argument_amount(self.name(), 0, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 0, arguments.len())?;
Ok(Value::Boolean(random())) Ok(Value::Boolean(random()))
} }
BuiltInFunction::RandomFloat => { BuiltInFunction::RandomFloat => {
Error::expect_argument_amount(self.name(), 0, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 0, arguments.len())?;
Ok(Value::Float(random())) Ok(Value::Float(random()))
} }
BuiltInFunction::RandomFrom => { BuiltInFunction::RandomFrom => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let value = arguments.first().unwrap(); let value = arguments.first().unwrap();
@ -162,7 +162,7 @@ impl Callable for BuiltInFunction {
} }
} }
BuiltInFunction::RandomInteger => { BuiltInFunction::RandomInteger => {
Error::expect_argument_amount(self.name(), 0, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 0, arguments.len())?;
Ok(Value::Integer(random())) Ok(Value::Integer(random()))
} }

View File

@ -1,7 +1,7 @@
use enum_iterator::Sequence; use enum_iterator::Sequence;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, List, Map, Type, Value}; use crate::{error::RuntimeError, List, Map, Type, Value};
use super::Callable; use super::Callable;
@ -206,7 +206,7 @@ impl Callable for StrFunction {
) -> Result<Value, RuntimeError> { ) -> Result<Value, RuntimeError> {
let value = match self { let value = match self {
StrFunction::AsBytes => { StrFunction::AsBytes => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let bytes = string let bytes = string
@ -217,7 +217,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(bytes)) Value::List(List::with_items(bytes))
} }
StrFunction::EndsWith => { StrFunction::EndsWith => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -226,7 +226,7 @@ impl Callable for StrFunction {
Value::Boolean(string.ends_with(pattern)) Value::Boolean(string.ends_with(pattern))
} }
StrFunction::Find => { StrFunction::Find => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -238,21 +238,21 @@ impl Callable for StrFunction {
Value::Option(find) Value::Option(find)
} }
StrFunction::IsAscii => { StrFunction::IsAscii => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
Value::Boolean(string.is_ascii()) Value::Boolean(string.is_ascii())
} }
StrFunction::IsEmpty => { StrFunction::IsEmpty => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
Value::Boolean(string.is_empty()) Value::Boolean(string.is_empty())
} }
StrFunction::Insert => { StrFunction::Insert => {
Error::expect_argument_amount(self.name(), 3, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 3, arguments.len())?;
let mut string = arguments.first().unwrap().as_string()?.clone(); let mut string = arguments.first().unwrap().as_string()?.clone();
let index = arguments.get(1).unwrap().as_integer()? as usize; let index = arguments.get(1).unwrap().as_integer()? as usize;
@ -263,7 +263,7 @@ impl Callable for StrFunction {
Value::String(string) Value::String(string)
} }
StrFunction::Lines => { StrFunction::Lines => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let lines = string let lines = string
@ -274,7 +274,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(lines)) Value::List(List::with_items(lines))
} }
StrFunction::Matches => { StrFunction::Matches => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -287,7 +287,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(matches)) Value::List(List::with_items(matches))
} }
StrFunction::Parse => { StrFunction::Parse => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
@ -300,7 +300,7 @@ impl Callable for StrFunction {
} }
} }
StrFunction::Remove => { StrFunction::Remove => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let index = arguments.get(1).unwrap().as_integer()? as usize; let index = arguments.get(1).unwrap().as_integer()? as usize;
@ -318,7 +318,7 @@ impl Callable for StrFunction {
} }
} }
StrFunction::ReplaceRange => { StrFunction::ReplaceRange => {
Error::expect_argument_amount(self.name(), 3, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 3, arguments.len())?;
let mut string = arguments.first().unwrap().as_string()?.clone(); let mut string = arguments.first().unwrap().as_string()?.clone();
let range = arguments.get(1).unwrap().as_list()?.items(); let range = arguments.get(1).unwrap().as_list()?.items();
@ -331,7 +331,7 @@ impl Callable for StrFunction {
Value::String(string) Value::String(string)
} }
StrFunction::Retain => { StrFunction::Retain => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let mut string = arguments.first().unwrap().as_string()?.clone(); let mut string = arguments.first().unwrap().as_string()?.clone();
let predicate = arguments.get(1).unwrap().as_function()?; let predicate = arguments.get(1).unwrap().as_function()?;
@ -347,7 +347,7 @@ impl Callable for StrFunction {
Value::String(string) Value::String(string)
} }
StrFunction::Split => { StrFunction::Split => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -360,7 +360,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections)) Value::List(List::with_items(sections))
} }
StrFunction::SplitAt => { StrFunction::SplitAt => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let index = arguments.get(1).unwrap().as_integer()?; let index = arguments.get(1).unwrap().as_integer()?;
@ -372,7 +372,7 @@ impl Callable for StrFunction {
])) ]))
} }
StrFunction::SplitInclusive => { StrFunction::SplitInclusive => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -385,7 +385,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections)) Value::List(List::with_items(sections))
} }
StrFunction::SplitN => { StrFunction::SplitN => {
Error::expect_argument_amount(self.name(), 3, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 3, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let count = arguments.get(1).unwrap().as_integer()?; let count = arguments.get(1).unwrap().as_integer()?;
@ -399,7 +399,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections)) Value::List(List::with_items(sections))
} }
StrFunction::SplitOnce => { StrFunction::SplitOnce => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -414,7 +414,7 @@ impl Callable for StrFunction {
Value::option(sections) Value::option(sections)
} }
StrFunction::SplitTerminator => { StrFunction::SplitTerminator => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -427,7 +427,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections)) Value::List(List::with_items(sections))
} }
StrFunction::SplitWhitespace => { StrFunction::SplitWhitespace => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let sections = string let sections = string
@ -438,7 +438,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections)) Value::List(List::with_items(sections))
} }
StrFunction::StartsWith => { StrFunction::StartsWith => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -447,7 +447,7 @@ impl Callable for StrFunction {
Value::Boolean(string.starts_with(pattern)) Value::Boolean(string.starts_with(pattern))
} }
StrFunction::StripPrefix => { StrFunction::StripPrefix => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let prefix_string = arguments.get(1).unwrap().as_string()?; let prefix_string = arguments.get(1).unwrap().as_string()?;
@ -459,7 +459,7 @@ impl Callable for StrFunction {
Value::option(stripped) Value::option(stripped)
} }
StrFunction::ToLowercase => { StrFunction::ToLowercase => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let lowercase = string.to_lowercase(); let lowercase = string.to_lowercase();
@ -467,7 +467,7 @@ impl Callable for StrFunction {
Value::string(lowercase) Value::string(lowercase)
} }
StrFunction::ToUppercase => { StrFunction::ToUppercase => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let uppercase = string.to_uppercase(); let uppercase = string.to_uppercase();
@ -475,14 +475,14 @@ impl Callable for StrFunction {
Value::string(uppercase) Value::string(uppercase)
} }
StrFunction::Trim => { StrFunction::Trim => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let trimmed = arguments.first().unwrap().as_string()?.trim().to_string(); let trimmed = arguments.first().unwrap().as_string()?.trim().to_string();
Value::string(trimmed) Value::string(trimmed)
} }
StrFunction::TrimEnd => { StrFunction::TrimEnd => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let trimmed = arguments let trimmed = arguments
.first() .first()
@ -494,7 +494,7 @@ impl Callable for StrFunction {
Value::string(trimmed) Value::string(trimmed)
} }
StrFunction::TrimEndMatches => { StrFunction::TrimEndMatches => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern_string = arguments.get(1).unwrap().as_string()?; let pattern_string = arguments.get(1).unwrap().as_string()?;
@ -504,7 +504,7 @@ impl Callable for StrFunction {
Value::string(trimmed) Value::string(trimmed)
} }
StrFunction::TrimMatches => { StrFunction::TrimMatches => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern = arguments let pattern = arguments
@ -518,7 +518,7 @@ impl Callable for StrFunction {
Value::string(trimmed) Value::string(trimmed)
} }
StrFunction::TrimStart => { StrFunction::TrimStart => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let trimmed = arguments let trimmed = arguments
.first() .first()
@ -530,7 +530,7 @@ impl Callable for StrFunction {
Value::string(trimmed) Value::string(trimmed)
} }
StrFunction::TrimStartMatches => { StrFunction::TrimStartMatches => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let string = arguments.first().unwrap().as_string()?; let string = arguments.first().unwrap().as_string()?;
let pattern = arguments let pattern = arguments
@ -544,7 +544,7 @@ impl Callable for StrFunction {
Value::string(trimmed) Value::string(trimmed)
} }
StrFunction::Truncate => { StrFunction::Truncate => {
Error::expect_argument_amount(self.name(), 2, arguments.len())?; RuntimeError::expect_argument_amount(self.name(), 2, arguments.len())?;
let input_string = arguments.first().unwrap().as_string()?; let input_string = arguments.first().unwrap().as_string()?;
let new_length = arguments.get(1).unwrap().as_integer()? as usize; let new_length = arguments.get(1).unwrap().as_integer()? as usize;

View File

@ -3,6 +3,7 @@
//! To deal with errors from dependencies, either create a new error variant //! To deal with errors from dependencies, either create a new error variant
//! or use the ToolFailure variant if the error can only occur inside a tool. //! or use the ToolFailure variant if the error can only occur inside a tool.
mod runtime_error; mod runtime_error;
pub(crate) mod rw_lock_error;
mod syntax_error; mod syntax_error;
mod validation_error; mod validation_error;
@ -10,77 +11,25 @@ pub use runtime_error::RuntimeError;
pub use syntax_error::SyntaxError; pub use syntax_error::SyntaxError;
pub use validation_error::ValidationError; pub use validation_error::ValidationError;
use serde::{Deserialize, Serialize};
use tree_sitter::{LanguageError, Node, Point}; use tree_sitter::{LanguageError, Node, Point};
use crate::{value::Value, SyntaxPosition}; use crate::{SourcePosition};
use std::{ use std::fmt::{self, Formatter};
fmt::{self, Formatter},
io,
num::ParseFloatError,
string::FromUtf8Error,
sync::PoisonError,
time,
};
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum Error { pub enum Error {
Parsing(SyntaxError), Syntax(SyntaxError),
Verification(ValidationError), Validation(ValidationError),
Runtime(RuntimeError), Runtime(RuntimeError),
AtSourcePosition {
error: Box<Error>,
source: String,
start_row: usize,
start_column: usize,
end_row: usize,
end_column: usize,
},
/// The function failed due to an external error.
External(String),
/// A custom error explained by its message.
CustomMessage(String),
/// Invalid user input.
Syntax {
source: String,
#[serde(skip)]
location: Point,
},
SerdeJson(String),
ParserCancelled, ParserCancelled,
ParseFloat { Language(LanguageError),
reason: String,
},
ExpectedIterable {
actual: Value,
},
} }
impl Error { impl Error {
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),
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,
}
}
pub fn expect_syntax_node( pub fn expect_syntax_node(
source: &str, source: &str,
expected: &str, expected: &str,
@ -91,103 +40,42 @@ impl Error {
if expected == actual.kind() { if expected == actual.kind() {
Ok(()) Ok(())
} else if actual.is_error() { } else if actual.is_error() {
Error::Syntax { Err(SyntaxError::InvalidSource {
source: source[actual.byte_range()].to_string(), source: source[actual.byte_range()].to_string(),
location: actual.start_position(), position: SourcePosition::from(actual.range()),
} })
} else { } else {
SyntaxError::UnexpectedSyntaxNode { Err(SyntaxError::UnexpectedSyntaxNode {
expected: expected.to_string(), expected: expected.to_string(),
actual: actual.kind().to_string(), actual: actual.kind().to_string(),
location: actual.start_position(), location: actual.start_position(),
relevant_source: source[actual.byte_range()].to_string(), relevant_source: source[actual.byte_range()].to_string(),
}
}
}
pub fn expect_argument_amount(
function_name: &str,
expected: usize,
actual: usize,
) -> Result<(), ValidationError> {
if expected == actual {
Ok(())
} else {
Err(Error::ExpectedBuiltInFunctionArgumentAmount {
function_name: function_name.to_string(),
expected,
actual,
}) })
} }
} }
}
pub fn is_error(&self, other: &Error) -> bool { impl From<SyntaxError> for Error {
match self { fn from(error: SyntaxError) -> Self {
Error::AtSourcePosition { error, .. } => error.as_ref() == other, Error::Syntax(error)
_ => self == other,
} }
}
impl From<ValidationError> for Error {
fn from(error: ValidationError) -> Self {
Error::Validation(error)
}
}
impl From<RuntimeError> for Error {
fn from(error: RuntimeError) -> Self {
Error::Runtime(error)
} }
} }
impl From<LanguageError> for Error { impl From<LanguageError> for Error {
fn from(error: LanguageError) -> Self { fn from(error: LanguageError) -> Self {
Error::External(error.to_string()) Error::Language(error)
}
}
impl<T> From<PoisonError<T>> for Error {
fn from(error: PoisonError<T>) -> Self {
Error::External(error.to_string())
}
}
impl From<FromUtf8Error> for Error {
fn from(error: FromUtf8Error) -> Self {
Error::External(error.to_string())
}
}
impl From<ParseFloatError> for Error {
fn from(error: ParseFloatError) -> Self {
Error::ParseFloat {
reason: error.to_string(),
}
}
}
impl From<csv::Error> for Error {
fn from(error: csv::Error) -> Self {
Error::External(error.to_string())
}
}
impl From<io::Error> for Error {
fn from(error: std::io::Error) -> Self {
Error::External(error.to_string())
}
}
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Error::External(error.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Error::SerdeJson(error.to_string())
}
}
impl From<time::SystemTimeError> for Error {
fn from(error: time::SystemTimeError) -> Self {
Error::External(error.to_string())
}
}
impl From<toml::de::Error> for Error {
fn from(error: toml::de::Error) -> Self {
Error::External(error.to_string())
} }
} }
@ -200,44 +88,15 @@ impl fmt::Debug for Error {
} }
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, _f: &mut Formatter) -> fmt::Result {
use Error::*; use Error::*;
match self { match self {
AtSourcePosition { Syntax(_) => todo!(),
error, Validation(_) => todo!(),
source,
start_row,
start_column,
end_row,
end_column,
} => {
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!(
f,
"Parsing was cancelled either manually or because it took too long."
),
ParseFloat { reason } => {
write!(
f,
"Failed to parse a float value. Reason given: {}.",
reason
)
}
ExpectedIterable { actual } => {
write!(f, "Expected an iterable value but got {actual}.")
}
Parsing(_) => todo!(),
Verification(_) => todo!(),
Runtime(_) => todo!(), Runtime(_) => todo!(),
External(_) => todo!(), ParserCancelled => todo!(),
CustomMessage(_) => todo!(), Language(_) => todo!(),
Syntax { source, location } => todo!(),
} }
} }
} }

View File

@ -1,11 +1,28 @@
use std::fmt::{self, Display, Formatter}; use std::{
fmt::{self, Debug, Display, Formatter},
io,
num::ParseFloatError,
string::FromUtf8Error,
time::{self, SystemTimeError},
};
use serde::{Deserialize, Serialize}; use crate::{Value};
use crate::Value; use super::rw_lock_error::RwLockError;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum RuntimeError { pub enum RuntimeError {
Csv(csv::Error),
Io(io::Error),
Reqwest(reqwest::Error),
Json(serde_json::Error),
SystemTime(SystemTimeError),
Toml(toml::de::Error),
ExpectedString { ExpectedString {
actual: Value, actual: Value,
}, },
@ -71,111 +88,145 @@ pub enum RuntimeError {
actual: Value, actual: Value,
}, },
/// A function was called with the wrong amount of arguments. /// Failed to read or write a map.
///
/// See the [MapError] docs for more info.
RwLock(RwLockError),
ParseFloat(ParseFloatError),
Utf8(FromUtf8Error),
/// Failed to find a variable with a value for this key.
VariableIdentifierNotFound(String),
/// A built-in function was called with the wrong amount of arguments.
ExpectedBuiltInFunctionArgumentAmount { ExpectedBuiltInFunctionArgumentAmount {
function_name: String, function_name: String,
expected: usize, expected: usize,
actual: usize, actual: usize,
}, },
}
/// A function was called with the wrong amount of arguments. impl RuntimeError {
ExpectedFunctionArgumentAmount { pub fn expect_argument_amount(
function_name: &str,
expected: usize, expected: usize,
actual: usize, actual: usize,
}, ) -> Result<(), Self> {
if expected == actual {
Ok(())
} else {
Err(RuntimeError::ExpectedBuiltInFunctionArgumentAmount {
function_name: function_name.to_string(),
expected,
actual,
})
}
}
}
/// A function was called with the wrong amount of arguments. impl From<csv::Error> for RuntimeError {
ExpectedFunctionArgumentMinimum { fn from(error: csv::Error) -> Self {
source: String, RuntimeError::Csv(error)
minumum_expected: usize, }
actual: usize, }
},
/// Failed to find a variable with a value for this key. impl From<io::Error> for RuntimeError {
VariableIdentifierNotFound(String), fn from(error: std::io::Error) -> Self {
RuntimeError::Io(error)
}
}
impl From<reqwest::Error> for RuntimeError {
fn from(error: reqwest::Error) -> Self {
RuntimeError::Reqwest(error)
}
}
impl From<serde_json::Error> for RuntimeError {
fn from(error: serde_json::Error) -> Self {
RuntimeError::Json(error)
}
}
impl From<time::SystemTimeError> for RuntimeError {
fn from(error: time::SystemTimeError) -> Self {
RuntimeError::SystemTime(error)
}
}
impl From<toml::de::Error> for RuntimeError {
fn from(error: toml::de::Error) -> Self {
RuntimeError::Toml(error)
}
}
impl From<ParseFloatError> for RuntimeError {
fn from(error: ParseFloatError) -> Self {
RuntimeError::ParseFloat(error)
}
}
impl From<FromUtf8Error> for RuntimeError {
fn from(error: FromUtf8Error) -> Self {
RuntimeError::Utf8(error)
}
} }
impl Display for RuntimeError { impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
use RuntimeError::*; use RuntimeError::*;
match self { match self {
ExpectedString { actual } => {
write!(f, "Expected a string but got {actual}.")
}
ExpectedInteger { actual } => write!(f, "Expected an integer, but got {actual}."),
ExpectedFloat { actual } => write!(f, "Expected a float, but got {actual}."),
ExpectedNumber { actual } => {
write!(f, "Expected a float or integer but got {actual}.",)
}
ExpectedNumberOrString { actual } => {
write!(f, "Expected a number or string, but got {actual}.")
}
ExpectedBoolean { actual } => {
write!(f, "Expected a boolean, but got {actual}.")
}
ExpectedList { actual } => write!(f, "Expected a list, but got {actual}."),
ExpectedMinLengthList {
minimum_len,
actual_len,
} => write!(
f,
"Expected a list of at least {minimum_len} values, but got one with {actual_len}.",
),
ExpectedFixedLenList {
expected_len,
actual,
} => write!(
f,
"Expected a list of len {}, but got {:?}.",
expected_len, actual
),
ExpectedNone { actual } => write!(f, "Expected an empty value, but got {actual}."),
ExpectedMap { actual } => write!(f, "Expected a map, but got {actual}."),
ExpectedTable { actual } => write!(f, "Expected a table, but got {actual}."),
ExpectedFunction { actual } => {
write!(f, "Expected function, but got {actual}.")
}
ExpectedOption { actual } => write!(f, "Expected option, but got {actual}."),
ExpectedCollection { actual } => {
write!(
f,
"Expected a string, list, map or table, but got {actual}.",
)
}
ExpectedBuiltInFunctionArgumentAmount {
function_name,
expected,
actual,
} => todo!(),
ExpectedFunctionArgumentAmount { expected, actual } => todo!(),
ExpectedFunctionArgumentMinimum {
source,
minumum_expected,
actual,
} => todo!(),
ExpectedBuiltInFunctionArgumentAmount {
function_name: tool_name,
expected,
actual,
} => write!(
f,
"{tool_name} expected {expected} arguments, but got {actual}.",
),
ExpectedFunctionArgumentAmount { expected, actual } => {
write!(f, "Expected {expected} arguments, but got {actual}.",)
}
ExpectedFunctionArgumentMinimum {
source,
minumum_expected,
actual,
} => {
write!(
f,
"{source} expected at least {minumum_expected} arguments, but got {actual}."
)
}
VariableIdentifierNotFound(_) => todo!(), VariableIdentifierNotFound(_) => todo!(),
RwLock(_) => todo!(),
Csv(_) => todo!(),
Io(_) => todo!(),
Reqwest(_) => todo!(),
Json(_) => todo!(),
SystemTime(_) => todo!(),
Toml(_) => todo!(),
Utf8(_) => todo!(),
ParseFloat(_) => todo!(),
ExpectedBuiltInFunctionArgumentAmount {
function_name: _,
expected: _,
actual: _,
} => todo!(),
ExpectedString { actual: _ } => todo!(),
ExpectedInteger { actual: _ } => todo!(),
ExpectedFloat { actual: _ } => todo!(),
ExpectedNumber { actual: _ } => todo!(),
ExpectedNumberOrString { actual: _ } => todo!(),
ExpectedBoolean { actual: _ } => todo!(),
ExpectedList { actual: _ } => todo!(),
ExpectedMinLengthList {
minimum_len: _,
actual_len: _,
} => todo!(),
ExpectedFixedLenList {
expected_len: _,
actual: _,
} => todo!(),
ExpectedNone { actual: _ } => todo!(),
ExpectedMap { actual: _ } => todo!(),
ExpectedTable { actual: _ } => todo!(),
ExpectedFunction { actual: _ } => todo!(),
ExpectedOption { actual: _ } => todo!(),
ExpectedCollection { actual: _ } => todo!(),
} }
} }
} }
impl Debug for RuntimeError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
impl From<RwLockError> for RuntimeError {
fn from(error: RwLockError) -> Self {
RuntimeError::RwLock(error)
}
}

View File

@ -0,0 +1,21 @@
use std::fmt::{self, Debug, Display, Formatter};
use serde::{Deserialize, Serialize};
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct RwLockError;
impl Display for RwLockError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(
f,
"Map error: failed to acquire a read/write lock because another thread has panicked."
)
}
}
impl Debug for RwLockError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}

View File

@ -1,8 +1,20 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Point; use tree_sitter::Point;
use crate::SourcePosition;
use super::rw_lock_error::RwLockError;
#[derive(Clone, PartialEq, Serialize, Deserialize)] #[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum SyntaxError { pub enum SyntaxError {
/// Invalid user input.
InvalidSource {
source: String,
position: SourcePosition,
},
RwLock(RwLockError),
UnexpectedSyntaxNode { UnexpectedSyntaxNode {
expected: String, expected: String,
actual: String, actual: String,
@ -13,3 +25,9 @@ pub enum SyntaxError {
relevant_source: String, relevant_source: String,
}, },
} }
impl From<RwLockError> for SyntaxError {
fn from(error: RwLockError) -> Self {
SyntaxError::RwLock(error)
}
}

View File

@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{Type, Value}; use crate::{Identifier, SourcePosition, Type, Value};
use super::rw_lock_error::RwLockError;
#[derive(Clone, PartialEq, Serialize, Deserialize)] #[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum ValidationError { pub enum ValidationError {
@ -8,20 +10,73 @@ pub enum ValidationError {
AssertEqualFailed { AssertEqualFailed {
expected: Value, expected: Value,
actual: Value, actual: Value,
position: SourcePosition,
}, },
/// The 'assert' macro did not resolve successfully. /// The 'assert' macro did not resolve successfully.
AssertFailed, AssertFailed { position: SourcePosition },
/// A built-in function was called with the wrong amount of arguments.
ExpectedBuiltInFunctionArgumentAmount {
function_name: String,
expected: usize,
actual: usize,
},
/// A function was called with the wrong amount of arguments.
ExpectedFunctionArgumentAmount {
expected: usize,
actual: usize,
position: SourcePosition,
},
/// A function was called with the wrong amount of arguments.
ExpectedFunctionArgumentMinimum {
minumum_expected: usize,
actual: usize,
position: SourcePosition,
},
/// Failed to read or write a map.
///
/// See the [MapError] docs for more info.
RwLock(RwLockError),
TypeCheck { TypeCheck {
expected: Type, expected: Type,
actual: Type, actual: Type,
position: SourcePosition,
}, },
TypeCheckExpectedFunction { TypeCheckExpectedFunction {
actual: Type, actual: Type,
position: SourcePosition,
}, },
/// Failed to find a variable with a value for this key. /// Failed to find a variable with a value for this key.
VariableIdentifierNotFound(String), VariableIdentifierNotFound(Identifier),
}
impl ValidationError {
pub fn expect_argument_amount(
function_name: &str,
expected: usize,
actual: usize,
) -> Result<(), Self> {
if expected == actual {
Ok(())
} else {
Err(ValidationError::ExpectedBuiltInFunctionArgumentAmount {
function_name: function_name.to_string(),
expected,
actual,
})
}
}
}
impl From<RwLockError> for ValidationError {
fn from(_error: RwLockError) -> Self {
ValidationError::RwLock(RwLockError)
}
} }

View File

@ -38,8 +38,7 @@
use tree_sitter::{Node as SyntaxNode, Parser, Tree as SyntaxTree, TreeCursor}; use tree_sitter::{Node as SyntaxNode, Parser, Tree as SyntaxTree, TreeCursor};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::SyntaxError, language, AbstractTree, Error, Format, Map, Root, SourcePosition, Value,
language, AbstractTree, Error, Format, Map, Root, Value,
}; };
/// Interpret the given source code. Returns the value of last statement or the /// Interpret the given source code. Returns the value of last statement or the
@ -84,7 +83,7 @@ impl Interpreter {
.set_language(language()) .set_language(language())
.expect("Language version is incompatible with tree sitter version."); .expect("Language version is incompatible with tree sitter version.");
parser.set_logger(Some(Box::new(|log_type, message| { parser.set_logger(Some(Box::new(|_log_type, message| {
log::info!("{}", message) log::info!("{}", message)
}))); })));
@ -117,12 +116,12 @@ impl Interpreter {
node: SyntaxNode, node: SyntaxNode,
source: &str, source: &str,
cursor: &mut TreeCursor, cursor: &mut TreeCursor,
) -> Result<(), ValidationError> { ) -> Result<(), Error> {
if node.is_error() { if node.is_error() {
Err(Error::Syntax { Err(Error::Syntax(SyntaxError::InvalidSource {
source: source[node.byte_range()].to_string(), source: source[node.byte_range()].to_string(),
location: node.start_position(), position: SourcePosition::from(node.range()),
}) }))
} else { } else {
for child in node.children(&mut cursor.clone()) { for child in node.children(&mut cursor.clone()) {
check_for_error(child, source, cursor)?; check_for_error(child, source, cursor)?;
@ -150,7 +149,9 @@ impl Interpreter {
/// This function [parses][Self::parse], [verifies][Self::verify] and [runs][Root::run] using /// This function [parses][Self::parse], [verifies][Self::verify] and [runs][Root::run] using
/// the same source code. /// the same source code.
pub fn run(&mut self, source: &str) -> Result<Value, Error> { pub fn run(&mut self, source: &str) -> Result<Value, Error> {
self.verify(source)?.run(source, &self.context) self.verify(source)?
.run(source, &self.context)
.map_err(|error| Error::Runtime(error))
} }
/// Return an s-expression displaying a syntax tree of the source, or the ParserCancelled error /// Return an s-expression displaying a syntax tree of the source, or the ParserCancelled error

View File

@ -11,7 +11,7 @@ use reedline::{
use std::{borrow::Cow, fs::read_to_string, path::PathBuf, process::Command}; use std::{borrow::Cow, fs::read_to_string, path::PathBuf, process::Command};
use dust_lang::{built_in_values, Interpreter, Map, Result, Value}; use dust_lang::{built_in_values, Error, Interpreter, Map, Value};
/// Command-line arguments to be parsed. /// Command-line arguments to be parsed.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -332,7 +332,7 @@ impl Completer for DustCompleter {
} }
} }
fn run_shell(context: Map) -> Result<()> { fn run_shell(context: Map) -> Result<(), Error> {
let mut interpreter = Interpreter::new(context.clone()); let mut interpreter = Interpreter::new(context.clone());
let mut keybindings = default_emacs_keybindings(); let mut keybindings = default_emacs_keybindings();

View File

@ -13,12 +13,10 @@ use std::{
collections::BTreeMap, collections::BTreeMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
marker::PhantomData, marker::PhantomData,
sync::{Arc, PoisonError, RwLock, RwLockReadGuard}, sync::{Arc, RwLock, RwLockReadGuard},
}; };
use crate::{value::Value, Structure, Type}; use crate::{error::rw_lock_error::RwLockError, value::Value, Structure, Type};
pub type MapAccessError<'a> = PoisonError<RwLockReadGuard<'a, BTreeMap<String, (Value, Type)>>>;
/// A collection dust variables comprised of key-value pairs. /// A collection dust variables comprised of key-value pairs.
/// ///
@ -65,7 +63,7 @@ impl Map {
} }
} }
pub fn clone_from(other: &Self) -> Result<Self, ()> { pub fn clone_from(other: &Self) -> Result<Self, RwLockError> {
let mut new_map = BTreeMap::new(); let mut new_map = BTreeMap::new();
for (key, (value, r#type)) in other.variables()?.iter() { for (key, (value, r#type)) in other.variables()?.iter() {
@ -80,26 +78,35 @@ impl Map {
pub fn variables( pub fn variables(
&self, &self,
) -> Result<RwLockReadGuard<BTreeMap<String, (Value, Type)>>, MapAccessError> { ) -> Result<RwLockReadGuard<BTreeMap<String, (Value, Type)>>, RwLockError> {
self.variables.read() self.variables.read().map_err(|_| RwLockError)
} }
pub fn set(&self, key: String, value: Value) -> Result<Option<(Value, Type)>, MapAccessError> { pub fn set(&self, key: String, value: Value) -> Result<Option<(Value, Type)>, RwLockError> {
log::info!("Setting variable {key} = {value}"); log::info!("Setting variable {key} = {value}");
let value_type = value.r#type(); let value_type = value.r#type();
let previous = self let previous = self
.variables .variables
.write()? .write()
.map_err(|_| RwLockError)?
.insert(key, (value, value_type.clone())); .insert(key, (value, value_type.clone()));
Ok(previous) Ok(previous)
} }
pub fn set_type(&self, key: String, r#type: Type) -> Result<Option<(Value, Type)>, ()> { pub fn set_type(
&self,
key: String,
r#type: Type,
) -> Result<Option<(Value, Type)>, RwLockError> {
log::info!("Setting type {key} = {}", r#type); log::info!("Setting type {key} = {}", r#type);
let previous = self.variables.write()?.insert(key, (Value::none(), r#type)); let previous = self
.variables
.write()
.map_err(|_| RwLockError)?
.insert(key, (Value::none(), r#type));
Ok(previous) Ok(previous)
} }

View File

@ -1,8 +1,5 @@
//! Types that represent runtime values. //! Types that represent runtime values.
use crate::{ use crate::{error::RuntimeError, Identifier, Type, TypeSpecification};
error::{Error, RuntimeError},
Identifier, Type, TypeSpecification,
};
use serde::{ use serde::{
de::{MapAccess, SeqAccess, Visitor}, de::{MapAccess, SeqAccess, Visitor},
@ -167,7 +164,7 @@ impl Value {
pub fn as_string(&self) -> Result<&String, RuntimeError> { pub fn as_string(&self) -> Result<&String, RuntimeError> {
match self { match self {
Value::String(string) => Ok(string), Value::String(string) => Ok(string),
value => Err(Error::ExpectedString { value => Err(RuntimeError::ExpectedString {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -177,7 +174,7 @@ impl Value {
pub fn as_integer(&self) -> Result<i64, RuntimeError> { pub fn as_integer(&self) -> Result<i64, RuntimeError> {
match self { match self {
Value::Integer(i) => Ok(*i), Value::Integer(i) => Ok(*i),
value => Err(Error::ExpectedInteger { value => Err(RuntimeError::ExpectedInteger {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -187,7 +184,7 @@ impl Value {
pub fn as_float(&self) -> Result<f64, RuntimeError> { pub fn as_float(&self) -> Result<f64, RuntimeError> {
match self { match self {
Value::Float(f) => Ok(*f), Value::Float(f) => Ok(*f),
value => Err(Error::ExpectedFloat { value => Err(RuntimeError::ExpectedFloat {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -199,7 +196,7 @@ impl Value {
match self { match self {
Value::Float(f) => Ok(*f), Value::Float(f) => Ok(*f),
Value::Integer(i) => Ok(*i as f64), Value::Integer(i) => Ok(*i as f64),
value => Err(Error::ExpectedNumber { value => Err(RuntimeError::ExpectedNumber {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -209,7 +206,7 @@ impl Value {
pub fn as_boolean(&self) -> Result<bool, RuntimeError> { pub fn as_boolean(&self) -> Result<bool, RuntimeError> {
match self { match self {
Value::Boolean(boolean) => Ok(*boolean), Value::Boolean(boolean) => Ok(*boolean),
value => Err(Error::ExpectedBoolean { value => Err(RuntimeError::ExpectedBoolean {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -219,7 +216,7 @@ impl Value {
pub fn as_list(&self) -> Result<&List, RuntimeError> { pub fn as_list(&self) -> Result<&List, RuntimeError> {
match self { match self {
Value::List(list) => Ok(list), Value::List(list) => Ok(list),
value => Err(Error::ExpectedList { value => Err(RuntimeError::ExpectedList {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -229,7 +226,7 @@ impl Value {
pub fn into_inner_list(self) -> Result<List, RuntimeError> { pub fn into_inner_list(self) -> Result<List, RuntimeError> {
match self { match self {
Value::List(list) => Ok(list), Value::List(list) => Ok(list),
value => Err(Error::ExpectedList { value => Err(RuntimeError::ExpectedList {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -239,7 +236,7 @@ impl Value {
pub fn as_map(&self) -> Result<&Map, RuntimeError> { pub fn as_map(&self) -> Result<&Map, RuntimeError> {
match self { match self {
Value::Map(map) => Ok(map), Value::Map(map) => Ok(map),
value => Err(Error::ExpectedMap { value => Err(RuntimeError::ExpectedMap {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -250,7 +247,7 @@ impl Value {
pub fn as_function(&self) -> Result<&Function, RuntimeError> { pub fn as_function(&self) -> Result<&Function, RuntimeError> {
match self { match self {
Value::Function(function) => Ok(function), Value::Function(function) => Ok(function),
value => Err(Error::ExpectedFunction { value => Err(RuntimeError::ExpectedFunction {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -260,7 +257,7 @@ impl Value {
pub fn as_option(&self) -> Result<&Option<Box<Value>>, RuntimeError> { pub fn as_option(&self) -> Result<&Option<Box<Value>>, RuntimeError> {
match self { match self {
Value::Option(option) => Ok(option), Value::Option(option) => Ok(option),
value => Err(Error::ExpectedOption { value => Err(RuntimeError::ExpectedOption {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -273,12 +270,12 @@ impl Value {
if option.is_none() { if option.is_none() {
Ok(()) Ok(())
} else { } else {
Err(Error::ExpectedNone { Err(RuntimeError::ExpectedNone {
actual: self.clone(), actual: self.clone(),
}) })
} }
} }
value => Err(Error::ExpectedNone { value => Err(RuntimeError::ExpectedNone {
actual: value.clone(), actual: value.clone(),
}), }),
} }
@ -319,7 +316,7 @@ impl Add for Value {
other other
}; };
Err(Error::ExpectedNumberOrString { Err(RuntimeError::ExpectedNumberOrString {
actual: non_number_or_string, actual: non_number_or_string,
}) })
} }
@ -341,7 +338,7 @@ impl Sub for Value {
let non_number = if !self.is_number() { self } else { other }; let non_number = if !self.is_number() { self } else { other };
Err(Error::ExpectedNumber { actual: non_number }) Err(RuntimeError::ExpectedNumber { actual: non_number })
} }
} }
@ -356,7 +353,7 @@ impl Mul for Value {
} else { } else {
let non_number = if !self.is_number() { self } else { other }; let non_number = if !self.is_number() { self } else { other };
Err(Error::ExpectedNumber { actual: non_number }) Err(RuntimeError::ExpectedNumber { actual: non_number })
} }
} }
} }
@ -377,7 +374,7 @@ impl Div for Value {
} else { } else {
let non_number = if !self.is_number() { self } else { other }; let non_number = if !self.is_number() { self } else { other };
Err(Error::ExpectedNumber { actual: non_number }) Err(RuntimeError::ExpectedNumber { actual: non_number })
} }
} }
} }
@ -596,49 +593,49 @@ impl From<()> for Value {
} }
impl TryFrom<Value> for String { impl TryFrom<Value> for String {
type Error = Error; type Error = RuntimeError;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> { fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
if let Value::String(string) = value { if let Value::String(string) = value {
Ok(string) Ok(string)
} else { } else {
Err(Error::ExpectedString { actual: value }) Err(RuntimeError::ExpectedString { actual: value })
} }
} }
} }
impl TryFrom<Value> for f64 { impl TryFrom<Value> for f64 {
type Error = Error; type Error = RuntimeError;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> { fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
if let Value::Float(value) = value { if let Value::Float(value) = value {
Ok(value) Ok(value)
} else { } else {
Err(Error::ExpectedFloat { actual: value }) Err(RuntimeError::ExpectedFloat { actual: value })
} }
} }
} }
impl TryFrom<Value> for i64 { impl TryFrom<Value> for i64 {
type Error = Error; type Error = RuntimeError;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> { fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
if let Value::Integer(value) = value { if let Value::Integer(value) = value {
Ok(value) Ok(value)
} else { } else {
Err(Error::ExpectedInteger { actual: value }) Err(RuntimeError::ExpectedInteger { actual: value })
} }
} }
} }
impl TryFrom<Value> for bool { impl TryFrom<Value> for bool {
type Error = Error; type Error = RuntimeError;
fn try_from(value: Value) -> std::result::Result<Self, Self::Error> { fn try_from(value: Value) -> std::result::Result<Self, Self::Error> {
if let Value::Boolean(value) = value { if let Value::Boolean(value) = value {
Ok(value) Ok(value)
} else { } else {
Err(Error::ExpectedBoolean { actual: value }) Err(RuntimeError::ExpectedBoolean { actual: value })
} }
} }
} }