Implement error overhaul
This commit is contained in:
parent
7f849f13a3
commit
88ca9c5ea4
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
));
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
|
@ -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 }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
));
|
||||
};
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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)?;
|
||||
}
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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 {
|
||||
|
@ -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(),
|
||||
|
@ -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(),
|
||||
|
@ -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) = ¤t_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) = ¤t_identifier {
|
||||
let r#type = if let Some(r#type) = ¤t_type {
|
||||
r#type.clone()
|
||||
} else if let Some(statement) = ¤t_statement {
|
||||
statement.expected_type(context)?
|
||||
} else {
|
||||
Type::None
|
||||
};
|
||||
// if let Some(identifier) = ¤t_identifier {
|
||||
// let r#type = if let Some(r#type) = ¤t_type {
|
||||
// r#type.clone()
|
||||
// } else if let Some(statement) = ¤t_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(),
|
||||
|
@ -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())?;
|
||||
|
@ -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)?;
|
||||
|
@ -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()))
|
||||
}
|
||||
|
@ -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;
|
||||
|
203
src/error/mod.rs
203
src/error/mod.rs
@ -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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
21
src/error/rw_lock_error.rs
Normal file
21
src/error/rw_lock_error.rs
Normal 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}")
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user