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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -40,7 +40,7 @@ impl AbstractTree for FunctionExpression {
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(),
actual: child.kind().to_string(),
location: child.start_position(),

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{
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,
};
@ -13,7 +13,7 @@ pub struct FunctionNode {
parameters: Vec<Identifier>,
body: Block,
r#type: Type,
syntax_position: SyntaxPosition,
syntax_position: SourcePosition,
}
impl FunctionNode {
@ -21,7 +21,7 @@ impl FunctionNode {
parameters: Vec<Identifier>,
body: Block,
r#type: Type,
syntax_position: SyntaxPosition,
syntax_position: SourcePosition,
) -> Self {
FunctionNode {
parameters,
@ -43,7 +43,7 @@ impl FunctionNode {
&self.r#type
}
pub fn syntax_position(&self) -> &SyntaxPosition {
pub fn syntax_position(&self) -> &SourcePosition {
&self.syntax_position
}
@ -164,15 +164,23 @@ impl AbstractTree for FunctionNode {
function_context.set_type(parameter.inner().clone(), parameter_type.clone())?;
}
return_type
.check(&self.body.expected_type(&function_context)?)
.map_err(|error| error.at_source_position(source, self.syntax_position))?;
let actual = self.body.expected_type(&function_context)?;
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)?;
Ok(())
} else {
Err(Error::TypeCheckExpectedFunction {
Err(ValidationError::TypeCheckExpectedFunction {
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) {
Ok(r#type.clone())
} 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) {
Ok(value.clone())
} 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::{
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)]
@ -12,6 +12,7 @@ pub struct IfElse {
else_if_expressions: Vec<Expression>,
else_if_blocks: Vec<Block>,
else_block: Option<Block>,
source_position: SourcePosition,
}
impl AbstractTree for IfElse {
@ -54,6 +55,7 @@ impl AbstractTree for IfElse {
else_if_expressions,
else_if_blocks,
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_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
.else_if_expressions
.iter()
@ -75,11 +77,27 @@ impl AbstractTree for IfElse {
expression.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 {
expected_type.check(&expression.expected_type(context)?)?;
if let Some(block) = &self.else_block {
let actual = block.expected_type(context)?;
if !expected.accepts(&actual) {
return Err(ValidationError::TypeCheck {
expected,
actual,
position: self.source_position,
});
}
}
Ok(())

View File

@ -2,7 +2,8 @@ use serde::{Deserialize, Serialize};
use crate::{
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.
@ -13,6 +14,7 @@ pub struct Index {
pub collection: IndexExpression,
pub index: IndexExpression,
pub index_end: Option<IndexExpression>,
source_position: SourcePosition,
}
impl AbstractTree for Index {
@ -40,6 +42,7 @@ impl AbstractTree for Index {
collection,
index,
index_end,
source_position: SourcePosition::from(node.range()),
})
}
@ -56,7 +59,7 @@ impl AbstractTree for Index {
self.collection.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)?;
}
@ -64,9 +67,9 @@ impl AbstractTree for Index {
}
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) => {
let index = self.index.run(source, context)?.as_integer()? as usize;
@ -104,7 +107,7 @@ impl AbstractTree for Index {
};
if value.is_none() {
Err(Error::VariableIdentifierNotFound(key))
Err(RuntimeError::VariableIdentifierNotFound(key))
} else {
Ok(value)
}
@ -115,7 +118,7 @@ impl AbstractTree for Index {
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 {
identifier.inner()
} else {
return Err(Error::VariableIdentifierNotFound(
return Err(RuntimeError::VariableIdentifierNotFound(
self.index.index.run(source, context)?.to_string(),
));
};

View File

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

View File

@ -32,7 +32,7 @@ impl AbstractTree for LogicOperator {
">=" => LogicOperator::GreaterOrEqual,
"<=" => LogicOperator::LessOrEqual,
_ => {
return Err(Error::UnexpectedSyntaxNode {
return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "==, !=, &&, ||, >, <, >= or <=".to_string(),
actual: operator_node.kind().to_string(),
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> {
self.matcher.check_type(_source, _context)?;
for (expression, statement) in self.options {
for (expression, statement) in &self.options {
expression.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)?;
}

View File

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use crate::{
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)]
@ -24,7 +24,7 @@ impl AbstractTree for MathOperator {
"/" => MathOperator::Divide,
"%" => MathOperator::Modulo,
_ => {
return Err(Error::UnexpectedSyntaxNode {
return Err(SyntaxError::UnexpectedSyntaxNode {
expected: "+, -, *, / or %".to_string(),
actual: operator_node.kind().to_string(),
location: operator_node.start_position(),

View File

@ -50,7 +50,7 @@ use crate::{
};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct SyntaxPosition {
pub struct SourcePosition {
pub start_byte: usize,
pub end_byte: usize,
pub start_row: usize,
@ -59,9 +59,9 @@ pub struct SyntaxPosition {
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 {
SyntaxPosition {
SourcePosition {
start_byte: range.start_byte,
end_byte: range.end_byte,
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
/// executable tree that resolves to a single value.
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
/// 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.
fn from_syntax(node: SyntaxNode, source: &str, context: &Map) -> Result<Self, SyntaxError>;
/// Verify the type integrity of the node.
fn check_type(&self, _source: &str, _context: &Map) -> Result<(), ValidationError>;
/// Execute dust code by traversing the tree.
fn run(&self, source: &str, context: &Map) -> Result<Value, RuntimeError>;
/// Return the type of the value that this abstract node will create when run. Returns a
/// validation error if the tree is invalid.
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 {

View File

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

View File

@ -44,7 +44,11 @@ impl 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}.");
match (self, other) {
@ -67,41 +71,20 @@ impl Type {
| (Type::Integer, Type::Number)
| (Type::Float, Type::Number)
| (Type::None, Type::None)
| (Type::String, Type::String) => Ok(()),
(Type::Custom(left), Type::Custom(right)) => {
if left == right {
Ok(())
} else {
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
}
}
| (Type::String, Type::String) => true,
(Type::Custom(left), Type::Custom(right)) => left == right,
(Type::Option(_), Type::None) => true,
(Type::Option(left), Type::Option(right)) => {
if left == right {
Ok(())
} else if let Type::Any = left.as_ref() {
Ok(())
} else if let Type::Any = right.as_ref() {
Ok(())
if let Type::Any = left.as_ref() {
true
} else if left == right {
true
} else {
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
false
}
}
(Type::Option(_), Type::None) | (Type::None, Type::Option(_)) => Ok(()),
(Type::List(self_item_type), Type::List(other_item_type)) => {
if self_item_type.check(other_item_type).is_err() {
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
} else {
Ok(())
}
self_item_type.accepts(&other_item_type)
}
(
Type::Function {
@ -118,27 +101,14 @@ impl Type {
.zip(other_parameter_types.iter());
for (self_parameter_type, other_parameter_type) in parameter_type_pairs {
if self_parameter_type.check(other_parameter_type).is_err() {
return Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
});
if self_parameter_type == other_parameter_type {
return false;
}
}
if self_return_type.check(other_return_type).is_err() {
Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
})
} else {
Ok(())
self_return_type == other_return_type
}
}
_ => Err(Error::TypeCheck {
expected: self.clone(),
actual: other.clone(),
}),
_ => false,
}
}
@ -210,7 +180,7 @@ impl AbstractTree for 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(),
actual: type_node.kind().to_string(),
location: type_node.start_position(),

View File

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

View File

@ -3,7 +3,7 @@ use std::fs::read_to_string;
use enum_iterator::{all, Sequence};
use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, Map, Type, Value};
use crate::{error::RuntimeError, Map, Type, Value};
use super::Callable;
@ -43,7 +43,7 @@ impl Callable for Fs {
) -> Result<Value, RuntimeError> {
match self {
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 file_content = read_to_string(path.as_str())?;

View File

@ -1,7 +1,7 @@
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, Map, Type, Value};
use crate::{error::RuntimeError, Map, Type, Value};
use super::Callable;
@ -49,7 +49,7 @@ impl Callable for Json {
) -> Result<Value, RuntimeError> {
match self {
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 json_string = serde_json::to_string(value)?;
@ -57,7 +57,7 @@ impl Callable for Json {
Ok(Value::String(json_string))
}
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 json_string = serde_json::to_string_pretty(value)?;
@ -65,7 +65,7 @@ impl Callable for Json {
Ok(Value::String(json_string))
}
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 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 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};
@ -91,7 +91,7 @@ impl Callable for BuiltInFunction {
) -> Result<Value, RuntimeError> {
match self {
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 right = arguments.get(1).unwrap();
@ -105,7 +105,7 @@ impl Callable for BuiltInFunction {
json_function.call(arguments, _source, _outer_context)
}
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 length = if let Ok(list) = value.as_list() {
@ -115,7 +115,7 @@ impl Callable for BuiltInFunction {
} else if let Ok(str) = value.as_string() {
str.chars().count()
} else {
return Err(Error::ExpectedCollection {
return Err(RuntimeError::ExpectedCollection {
actual: value.clone(),
});
};
@ -123,7 +123,7 @@ impl Callable for BuiltInFunction {
Ok(Value::Integer(length as i64))
}
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();
@ -132,17 +132,17 @@ impl Callable for BuiltInFunction {
Ok(Value::none())
}
BuiltInFunction::RandomBoolean => {
Error::expect_argument_amount(self.name(), 0, arguments.len())?;
RuntimeError::expect_argument_amount(self.name(), 0, arguments.len())?;
Ok(Value::Boolean(random()))
}
BuiltInFunction::RandomFloat => {
Error::expect_argument_amount(self.name(), 0, arguments.len())?;
RuntimeError::expect_argument_amount(self.name(), 0, arguments.len())?;
Ok(Value::Float(random()))
}
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();
@ -162,7 +162,7 @@ impl Callable for BuiltInFunction {
}
}
BuiltInFunction::RandomInteger => {
Error::expect_argument_amount(self.name(), 0, arguments.len())?;
RuntimeError::expect_argument_amount(self.name(), 0, arguments.len())?;
Ok(Value::Integer(random()))
}

View File

@ -1,7 +1,7 @@
use enum_iterator::Sequence;
use serde::{Deserialize, Serialize};
use crate::{error::RuntimeError, Error, List, Map, Type, Value};
use crate::{error::RuntimeError, List, Map, Type, Value};
use super::Callable;
@ -206,7 +206,7 @@ impl Callable for StrFunction {
) -> Result<Value, RuntimeError> {
let value = match self {
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 bytes = string
@ -217,7 +217,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(bytes))
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -226,7 +226,7 @@ impl Callable for StrFunction {
Value::Boolean(string.ends_with(pattern))
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -238,21 +238,21 @@ impl Callable for StrFunction {
Value::Option(find)
}
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()?;
Value::Boolean(string.is_ascii())
}
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()?;
Value::Boolean(string.is_empty())
}
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 index = arguments.get(1).unwrap().as_integer()? as usize;
@ -263,7 +263,7 @@ impl Callable for StrFunction {
Value::String(string)
}
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 lines = string
@ -274,7 +274,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(lines))
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -287,7 +287,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(matches))
}
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()?;
@ -300,7 +300,7 @@ impl Callable for StrFunction {
}
}
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 index = arguments.get(1).unwrap().as_integer()? as usize;
@ -318,7 +318,7 @@ impl Callable for StrFunction {
}
}
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 range = arguments.get(1).unwrap().as_list()?.items();
@ -331,7 +331,7 @@ impl Callable for StrFunction {
Value::String(string)
}
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 predicate = arguments.get(1).unwrap().as_function()?;
@ -347,7 +347,7 @@ impl Callable for StrFunction {
Value::String(string)
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -360,7 +360,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections))
}
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 index = arguments.get(1).unwrap().as_integer()?;
@ -372,7 +372,7 @@ impl Callable for StrFunction {
]))
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -385,7 +385,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections))
}
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 count = arguments.get(1).unwrap().as_integer()?;
@ -399,7 +399,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections))
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -414,7 +414,7 @@ impl Callable for StrFunction {
Value::option(sections)
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -427,7 +427,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections))
}
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 sections = string
@ -438,7 +438,7 @@ impl Callable for StrFunction {
Value::List(List::with_items(sections))
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -447,7 +447,7 @@ impl Callable for StrFunction {
Value::Boolean(string.starts_with(pattern))
}
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 prefix_string = arguments.get(1).unwrap().as_string()?;
@ -459,7 +459,7 @@ impl Callable for StrFunction {
Value::option(stripped)
}
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 lowercase = string.to_lowercase();
@ -467,7 +467,7 @@ impl Callable for StrFunction {
Value::string(lowercase)
}
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 uppercase = string.to_uppercase();
@ -475,14 +475,14 @@ impl Callable for StrFunction {
Value::string(uppercase)
}
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();
Value::string(trimmed)
}
StrFunction::TrimEnd => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let trimmed = arguments
.first()
@ -494,7 +494,7 @@ impl Callable for StrFunction {
Value::string(trimmed)
}
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 pattern_string = arguments.get(1).unwrap().as_string()?;
@ -504,7 +504,7 @@ impl Callable for StrFunction {
Value::string(trimmed)
}
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 pattern = arguments
@ -518,7 +518,7 @@ impl Callable for StrFunction {
Value::string(trimmed)
}
StrFunction::TrimStart => {
Error::expect_argument_amount(self.name(), 1, arguments.len())?;
RuntimeError::expect_argument_amount(self.name(), 1, arguments.len())?;
let trimmed = arguments
.first()
@ -530,7 +530,7 @@ impl Callable for StrFunction {
Value::string(trimmed)
}
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 pattern = arguments
@ -544,7 +544,7 @@ impl Callable for StrFunction {
Value::string(trimmed)
}
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 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
//! or use the ToolFailure variant if the error can only occur inside a tool.
mod runtime_error;
pub(crate) mod rw_lock_error;
mod syntax_error;
mod validation_error;
@ -10,77 +11,25 @@ pub use runtime_error::RuntimeError;
pub use syntax_error::SyntaxError;
pub use validation_error::ValidationError;
use serde::{Deserialize, Serialize};
use tree_sitter::{LanguageError, Node, Point};
use crate::{value::Value, SyntaxPosition};
use crate::{SourcePosition};
use std::{
fmt::{self, Formatter},
io,
num::ParseFloatError,
string::FromUtf8Error,
sync::PoisonError,
time,
};
use std::fmt::{self, Formatter};
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum Error {
Parsing(SyntaxError),
Syntax(SyntaxError),
Verification(ValidationError),
Validation(ValidationError),
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,
ParseFloat {
reason: String,
},
ExpectedIterable {
actual: Value,
},
Language(LanguageError),
}
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(
source: &str,
expected: &str,
@ -91,103 +40,42 @@ impl Error {
if expected == actual.kind() {
Ok(())
} else if actual.is_error() {
Error::Syntax {
Err(SyntaxError::InvalidSource {
source: source[actual.byte_range()].to_string(),
location: actual.start_position(),
}
position: SourcePosition::from(actual.range()),
})
} else {
SyntaxError::UnexpectedSyntaxNode {
Err(SyntaxError::UnexpectedSyntaxNode {
expected: expected.to_string(),
actual: actual.kind().to_string(),
location: actual.start_position(),
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 {
match self {
Error::AtSourcePosition { error, .. } => error.as_ref() == other,
_ => self == other,
impl From<SyntaxError> for Error {
fn from(error: SyntaxError) -> Self {
Error::Syntax(error)
}
}
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 {
fn from(error: LanguageError) -> Self {
Error::External(error.to_string())
}
}
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())
Error::Language(error)
}
}
@ -200,44 +88,15 @@ impl fmt::Debug 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::*;
match self {
AtSourcePosition {
error,
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!(),
Syntax(_) => todo!(),
Validation(_) => todo!(),
Runtime(_) => todo!(),
External(_) => todo!(),
CustomMessage(_) => todo!(),
Syntax { source, location } => todo!(),
ParserCancelled => todo!(),
Language(_) => 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 {
Csv(csv::Error),
Io(io::Error),
Reqwest(reqwest::Error),
Json(serde_json::Error),
SystemTime(SystemTimeError),
Toml(toml::de::Error),
ExpectedString {
actual: Value,
},
@ -71,111 +88,145 @@ pub enum RuntimeError {
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 {
function_name: String,
expected: usize,
actual: usize,
},
}
/// A function was called with the wrong amount of arguments.
ExpectedFunctionArgumentAmount {
impl RuntimeError {
pub fn expect_argument_amount(
function_name: &str,
expected: 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.
ExpectedFunctionArgumentMinimum {
source: String,
minumum_expected: usize,
actual: usize,
},
impl From<csv::Error> for RuntimeError {
fn from(error: csv::Error) -> Self {
RuntimeError::Csv(error)
}
}
/// Failed to find a variable with a value for this key.
VariableIdentifierNotFound(String),
impl From<io::Error> for RuntimeError {
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 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
use RuntimeError::*;
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!(),
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 tree_sitter::Point;
use crate::SourcePosition;
use super::rw_lock_error::RwLockError;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum SyntaxError {
/// Invalid user input.
InvalidSource {
source: String,
position: SourcePosition,
},
RwLock(RwLockError),
UnexpectedSyntaxNode {
expected: String,
actual: String,
@ -13,3 +25,9 @@ pub enum SyntaxError {
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 crate::{Type, Value};
use crate::{Identifier, SourcePosition, Type, Value};
use super::rw_lock_error::RwLockError;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum ValidationError {
@ -8,20 +10,73 @@ pub enum ValidationError {
AssertEqualFailed {
expected: Value,
actual: Value,
position: SourcePosition,
},
/// 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 {
expected: Type,
actual: Type,
position: SourcePosition,
},
TypeCheckExpectedFunction {
actual: Type,
position: SourcePosition,
},
/// 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 crate::{
error::{RuntimeError, SyntaxError, ValidationError},
language, AbstractTree, Error, Format, Map, Root, Value,
error::SyntaxError, language, AbstractTree, Error, Format, Map, Root, SourcePosition, Value,
};
/// Interpret the given source code. Returns the value of last statement or the
@ -84,7 +83,7 @@ impl Interpreter {
.set_language(language())
.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)
})));
@ -117,12 +116,12 @@ impl Interpreter {
node: SyntaxNode,
source: &str,
cursor: &mut TreeCursor,
) -> Result<(), ValidationError> {
) -> Result<(), Error> {
if node.is_error() {
Err(Error::Syntax {
Err(Error::Syntax(SyntaxError::InvalidSource {
source: source[node.byte_range()].to_string(),
location: node.start_position(),
})
position: SourcePosition::from(node.range()),
}))
} else {
for child in node.children(&mut cursor.clone()) {
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
/// the same source code.
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

View File

@ -11,7 +11,7 @@ use reedline::{
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.
#[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 keybindings = default_emacs_keybindings();

View File

@ -13,12 +13,10 @@ use std::{
collections::BTreeMap,
fmt::{self, Display, Formatter},
marker::PhantomData,
sync::{Arc, PoisonError, RwLock, RwLockReadGuard},
sync::{Arc, RwLock, RwLockReadGuard},
};
use crate::{value::Value, Structure, Type};
pub type MapAccessError<'a> = PoisonError<RwLockReadGuard<'a, BTreeMap<String, (Value, Type)>>>;
use crate::{error::rw_lock_error::RwLockError, value::Value, Structure, Type};
/// 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();
for (key, (value, r#type)) in other.variables()?.iter() {
@ -80,26 +78,35 @@ impl Map {
pub fn variables(
&self,
) -> Result<RwLockReadGuard<BTreeMap<String, (Value, Type)>>, MapAccessError> {
self.variables.read()
) -> Result<RwLockReadGuard<BTreeMap<String, (Value, Type)>>, RwLockError> {
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}");
let value_type = value.r#type();
let previous = self
.variables
.write()?
.write()
.map_err(|_| RwLockError)?
.insert(key, (value, value_type.clone()));
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);
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)
}

View File

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