Refine errors and error propagation

This commit is contained in:
Jeff 2024-08-20 11:07:13 -04:00
parent cf9a9837c8
commit 83f856385b
13 changed files with 587 additions and 353 deletions

View File

@ -11,11 +11,12 @@ use std::{
use crate::{
ast::{
AbstractSyntaxTree, BlockExpression, CallExpression, ElseExpression, FieldAccessExpression,
IfExpression, LetStatement, ListExpression, ListIndexExpression, LoopExpression,
MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
AbstractSyntaxTree, AstError, BlockExpression, CallExpression, ElseExpression,
FieldAccessExpression, IfExpression, LetStatement, ListExpression, ListIndexExpression,
LoopExpression, MapExpression, Node, OperatorExpression, RangeExpression, Span, Statement,
StructDefinition, StructExpression, TupleAccessExpression,
},
context::ContextError,
parse, Context, DustError, Expression, Identifier, StructType, Type,
};
@ -33,7 +34,7 @@ use crate::{
pub fn analyze(source: &str) -> Result<(), DustError> {
let abstract_tree = parse(source)?;
let context = Context::new();
let mut analyzer = Analyzer::new(&abstract_tree, &context);
let analyzer = Analyzer::new(&abstract_tree, context);
analyzer
.analyze()
@ -58,18 +59,18 @@ pub fn analyze(source: &str) -> Result<(), DustError> {
/// assert!(result.is_err());
pub struct Analyzer<'a> {
abstract_tree: &'a AbstractSyntaxTree,
context: &'a Context,
context: Context,
}
impl<'a> Analyzer<'a> {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a Context) -> Self {
impl<'recovered, 'a: 'recovered> Analyzer<'a> {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self {
Self {
abstract_tree,
context,
}
}
pub fn analyze(&mut self) -> Result<(), AnalysisError> {
pub fn analyze(&'recovered self) -> Result<(), AnalysisError> {
for statement in &self.abstract_tree.statements {
self.analyze_statement(statement)?;
}
@ -77,7 +78,7 @@ impl<'a> Analyzer<'a> {
Ok(())
}
fn analyze_statement(&mut self, statement: &Statement) -> Result<(), AnalysisError> {
fn analyze_statement(&'recovered self, statement: &Statement) -> Result<(), AnalysisError> {
match statement {
Statement::Expression(expression) => self.analyze_expression(expression)?,
Statement::ExpressionNullified(expression_node) => {
@ -86,7 +87,7 @@ impl<'a> Analyzer<'a> {
Statement::Let(let_statement) => match &let_statement.inner {
LetStatement::Let { identifier, value }
| LetStatement::LetMut { identifier, value } => {
let r#type = value.return_type(self.context);
let r#type = value.return_type(&self.context)?;
if let Some(r#type) = r#type {
self.context.set_variable_type(
@ -113,7 +114,7 @@ impl<'a> Analyzer<'a> {
name: name.inner.clone(),
},
name.position,
),
)?,
StructDefinition::Tuple { name, items } => {
let fields = items.iter().map(|item| item.inner.clone()).collect();
@ -124,7 +125,7 @@ impl<'a> Analyzer<'a> {
fields,
},
name.position,
);
)?;
}
StructDefinition::Fields { name, fields } => {
let fields = fields
@ -141,7 +142,7 @@ impl<'a> Analyzer<'a> {
fields,
},
name.position,
);
)?;
}
},
}
@ -149,7 +150,7 @@ impl<'a> Analyzer<'a> {
Ok(())
}
fn analyze_expression(&mut self, expression: &Expression) -> Result<(), AnalysisError> {
fn analyze_expression(&'recovered self, expression: &Expression) -> Result<(), AnalysisError> {
match expression {
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner)?,
Expression::Call(call_expression) => {
@ -235,8 +236,8 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(assignee)?;
self.analyze_expression(modifier)?;
let expected_type = assignee.return_type(self.context);
let actual_type = modifier.return_type(self.context);
let expected_type = assignee.return_type(&self.context)?;
let actual_type = modifier.return_type(&self.context)?;
if expected_type.is_none() {
return Err(AnalysisError::ExpectedValueFromExpression {
@ -308,7 +309,10 @@ impl<'a> Analyzer<'a> {
Ok(())
}
fn analyze_block(&mut self, block_expression: &BlockExpression) -> Result<(), AnalysisError> {
fn analyze_block(
&'recovered self,
block_expression: &BlockExpression,
) -> Result<(), AnalysisError> {
match block_expression {
BlockExpression::Async(statements) => {
for statement in statements {
@ -325,7 +329,7 @@ impl<'a> Analyzer<'a> {
Ok(())
}
fn analyze_if(&mut self, if_expression: &IfExpression) -> Result<(), AnalysisError> {
fn analyze_if(&'recovered self, if_expression: &IfExpression) -> Result<(), AnalysisError> {
match if_expression {
IfExpression::If {
condition,
@ -359,6 +363,8 @@ impl<'a> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)]
pub enum AnalysisError {
AstError(AstError),
ContextError(ContextError),
ExpectedBoolean {
actual: Statement,
},
@ -418,9 +424,24 @@ pub enum AnalysisError {
},
}
impl From<AstError> for AnalysisError {
fn from(v: AstError) -> Self {
Self::AstError(v)
}
}
impl From<ContextError> for AnalysisError {
fn from(context_error: ContextError) -> Self {
Self::ContextError(context_error)
}
}
impl AnalysisError {
pub fn position(&self) -> Span {
match self {
pub fn position(&self) -> Option<Span> {
let position = match self {
AnalysisError::AstError(ast_error) => return None,
AnalysisError::ContextError(context_error) => return None,
AnalysisError::ExpectedBoolean { actual } => actual.position(),
AnalysisError::ExpectedIdentifier { actual } => actual.position(),
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
@ -439,7 +460,9 @@ impl AnalysisError {
AnalysisError::UndefinedVariable { identifier } => identifier.position,
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
AnalysisError::UnexectedString { actual } => actual.position(),
}
};
Some(position)
}
}
@ -448,6 +471,8 @@ impl Error for AnalysisError {}
impl Display for AnalysisError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
AnalysisError::ContextError(context_error) => write!(f, "{}", context_error),
AnalysisError::ExpectedBoolean { actual, .. } => {
write!(f, "Expected boolean, found {}", actual)
}

View File

@ -6,9 +6,12 @@ use std::{
use serde::{Deserialize, Serialize};
use crate::{BuiltInFunction, Context, FunctionType, Identifier, RangeableType, StructType, Type};
use crate::{
BuiltInFunction, Context, ContextError, FunctionType, Identifier, RangeableType, StructType,
Type,
};
use super::{Node, Span, Statement};
use super::{AstError, Node, Span, Statement};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Expression {
@ -263,15 +266,18 @@ impl Expression {
}
}
pub fn return_type(&self, context: &Context) -> Option<Type> {
match self {
pub fn return_type<'recovered>(
&self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
let return_type = match self {
Expression::Block(block_expression) => {
Some(block_expression.inner.return_type(context)?)
return block_expression.inner.return_type(context)
}
Expression::Call(call_expression) => {
let CallExpression { invoker, .. } = call_expression.inner.as_ref();
let invoker_type = invoker.return_type(context);
let invoker_type = invoker.return_type(context)?;
if let Some(Type::Function(FunctionType { return_type, .. })) = invoker_type {
return_type.map(|r#type| *r#type)
@ -285,7 +291,7 @@ impl Expression {
let FieldAccessExpression { container, field } =
field_access_expression.inner.as_ref();
let container_type = container.return_type(context);
let container_type = container.return_type(context)?;
if let Some(Type::Struct(StructType::Fields { fields, .. })) = container_type {
fields
@ -296,28 +302,41 @@ impl Expression {
None
}
}
Expression::Grouped(expression) => expression.inner.return_type(context),
Expression::Identifier(identifier) => context.get_type(&identifier.inner),
Expression::If(if_expression) => {
return match if_expression.inner.as_ref() {
Expression::Grouped(expression) => expression.inner.return_type(context)?,
Expression::Identifier(identifier) => context.get_type(&identifier.inner)?,
Expression::If(if_expression) => match if_expression.inner.as_ref() {
IfExpression::If { .. } => None,
IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context),
}
}
IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context)?,
},
Expression::List(list_expression) => match list_expression.inner.as_ref() {
ListExpression::AutoFill { repeat_operand, .. } => {
let item_type = repeat_operand.return_type(context)?;
if let Some(r#type) = item_type {
Some(Type::ListOf {
item_type: Box::new(item_type),
item_type: Box::new(r#type),
})
} else {
return Err(AstError::ExpectedType {
position: repeat_operand.position(),
});
}
}
ListExpression::Ordered(expressions) => {
if expressions.is_empty() {
return Some(Type::ListEmpty);
return Ok(Some(Type::ListEmpty));
}
let item_type = expressions.last().unwrap().return_type(context)?;
let item_type = expressions
.first()
.ok_or_else(|| AstError::ExpectedNonEmptyList {
position: self.position(),
})?
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: expressions.first().unwrap().position(),
})?;
let length = expressions.len();
Some(Type::List {
@ -329,7 +348,11 @@ impl Expression {
Expression::ListIndex(list_index_expression) => {
let ListIndexExpression { list, .. } = list_index_expression.inner.as_ref();
let list_type = list.return_type(context)?;
let list_type =
list.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: list.position(),
})?;
if let Type::List { item_type, .. } = list_type {
Some(*item_type)
@ -350,9 +373,9 @@ impl Expression {
LiteralExpression::String(_) => Some(Type::String),
},
Expression::Loop(loop_expression) => match loop_expression.inner.as_ref() {
LoopExpression::For { block, .. } => block.inner.return_type(context),
LoopExpression::For { block, .. } => block.inner.return_type(context)?,
LoopExpression::Infinite { .. } => None,
LoopExpression::While { block, .. } => block.inner.return_type(context),
LoopExpression::While { block, .. } => block.inner.return_type(context)?,
},
Expression::Map(map_expression) => {
let MapExpression { pairs } = map_expression.inner.as_ref();
@ -360,7 +383,12 @@ impl Expression {
let mut types = HashMap::with_capacity(pairs.len());
for (key, value) in pairs {
let value_type = value.return_type(context)?;
let value_type =
value
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: value.position(),
})?;
types.insert(key.inner.clone(), value_type);
}
@ -371,10 +399,12 @@ impl Expression {
OperatorExpression::Assignment { .. } => None,
OperatorExpression::Comparison { .. } => Some(Type::Boolean),
OperatorExpression::CompoundAssignment { .. } => None,
OperatorExpression::ErrorPropagation(expression) => expression.return_type(context),
OperatorExpression::Negation(expression) => expression.return_type(context),
OperatorExpression::ErrorPropagation(expression) => {
expression.return_type(context)?
}
OperatorExpression::Negation(expression) => expression.return_type(context)?,
OperatorExpression::Not(_) => Some(Type::Boolean),
OperatorExpression::Math { left, .. } => left.return_type(context),
OperatorExpression::Math { left, .. } => left.return_type(context)?,
OperatorExpression::Logic { .. } => Some(Type::Boolean),
},
Expression::Range(range_expression) => {
@ -382,13 +412,22 @@ impl Expression {
RangeExpression::Exclusive { start, .. } => start,
RangeExpression::Inclusive { start, .. } => start,
};
let start_type = start.return_type(context)?;
let start_type =
start
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: start.position(),
})?;
let rangeable_type = match start_type {
Type::Byte => RangeableType::Byte,
Type::Character => RangeableType::Character,
Type::Float => RangeableType::Float,
Type::Integer => RangeableType::Integer,
_ => return None,
_ => {
return Err(AstError::ExpectedRangeableType {
position: start.position(),
})
}
};
Some(Type::Range {
@ -400,7 +439,11 @@ impl Expression {
let mut types = HashMap::with_capacity(fields.len());
for (field, expression) in fields {
let r#type = expression.return_type(context)?;
let r#type = expression.return_type(context)?.ok_or_else(|| {
AstError::ExpectedType {
position: expression.position(),
}
})?;
types.insert(field.inner.clone(), r#type);
}
@ -413,15 +456,24 @@ impl Expression {
},
Expression::TupleAccess(tuple_access_expression) => {
let TupleAccessExpression { tuple, index } = tuple_access_expression.inner.as_ref();
let tuple_value = tuple.return_type(context)?;
let tuple_value =
tuple
.return_type(context)?
.ok_or_else(|| AstError::ExpectedType {
position: tuple.position(),
})?;
if let Type::Tuple(fields) = tuple_value {
fields.get(index.inner).cloned()
} else {
None
}
Err(AstError::ExpectedTupleType {
position: tuple.position(),
})?
}
}
};
Ok(return_type)
}
pub fn position(&self) -> Span {
@ -975,13 +1027,16 @@ pub enum BlockExpression {
}
impl BlockExpression {
fn return_type(&self, context: &Context) -> Option<Type> {
fn return_type<'recovered>(
&self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
match self {
BlockExpression::Async(statements) | BlockExpression::Sync(statements) => {
if let Some(statement) = statements.last() {
statement.return_type(context)
} else {
None
Ok(None)
}
}
}

View File

@ -12,6 +12,8 @@ use std::{
use serde::{Deserialize, Serialize};
use crate::{ContextError, Type};
pub type Span = (usize, usize);
/// In-memory representation of a Dust program.
@ -57,3 +59,48 @@ impl<T: Display> Display for Node<T> {
write!(f, "{}", self.inner)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AstError {
ContextError(ContextError),
ExpectedType { position: Span },
ExpectedTupleType { position: Span },
ExpectedNonEmptyList { position: Span },
ExpectedRangeableType { position: Span },
}
impl AstError {
pub fn position(&self) -> Option<Span> {
match self {
AstError::ContextError(error) => None,
AstError::ExpectedType { position } => Some(*position),
AstError::ExpectedTupleType { position } => Some(*position),
AstError::ExpectedNonEmptyList { position } => Some(*position),
AstError::ExpectedRangeableType { position } => Some(*position),
}
}
}
impl From<ContextError> for AstError {
fn from(v: ContextError) -> Self {
Self::ContextError(v)
}
}
impl Display for AstError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
AstError::ContextError(error) => write!(f, "{}", error),
AstError::ExpectedType { position } => write!(f, "Expected a type at {:?}", position),
AstError::ExpectedTupleType { position } => {
write!(f, "Expected a tuple type at {:?}", position)
}
AstError::ExpectedNonEmptyList { position } => {
write!(f, "Expected a non-empty list at {:?}", position)
}
AstError::ExpectedRangeableType { position } => {
write!(f, "Expected a rangeable type at {:?}", position)
}
}
}
}

View File

@ -2,9 +2,9 @@ use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::{Context, Identifier, Type};
use crate::{Context, ContextError, Identifier, Type};
use super::{Expression, Node, Span};
use super::{AstError, Expression, Node, Span};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Statement {
@ -28,12 +28,15 @@ impl Statement {
}
}
pub fn return_type(&self, context: &Context) -> Option<Type> {
pub fn return_type<'recovered>(
&self,
context: &'recovered Context,
) -> Result<Option<Type>, AstError> {
match self {
Statement::Expression(expression) => expression.return_type(context),
Statement::ExpressionNullified(_) => None,
Statement::Let(_) => None,
Statement::StructDefinition(_) => None,
Statement::ExpressionNullified(_) => Ok(None),
Statement::Let(_) => Ok(None),
Statement::StructDefinition(_) => Ok(None),
}
}
}

View File

@ -2,7 +2,7 @@
use std::{
error::Error,
fmt::{self, Display, Formatter},
io::{self, stdin},
io::{self, stdin, stdout, Write},
};
use serde::{Deserialize, Serialize};
@ -13,7 +13,7 @@ use crate::{Identifier, Type, Value};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BuiltInFunction {
// String tools
ToString { argument: Box<Value> },
ToString,
// Integer and float tools
IsEven,
@ -82,75 +82,57 @@ impl BuiltInFunction {
_type_arguments: Option<Vec<Type>>,
value_arguments: Option<Vec<Value>>,
) -> Result<Option<Value>, BuiltInFunctionError> {
match (self.value_parameters(), &value_arguments) {
(Some(value_parameters), Some(value_arguments)) => {
if value_parameters.len() != value_arguments.len() {
return Err(BuiltInFunctionError::WrongNumberOfValueArguments);
}
}
(Some(_), None) | (None, Some(_)) => {
return Err(BuiltInFunctionError::WrongNumberOfValueArguments);
}
(None, None) => {}
}
let value_arguments = value_arguments.unwrap();
match self {
BuiltInFunction::ToString { argument } => Ok(Some(Value::string(argument))),
BuiltInFunction::ToString => Ok(Some(Value::String(value_arguments[0].to_string()))),
BuiltInFunction::IsEven => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
if let Some(integer) = value_arguments[0].as_integer() {
Ok(Some(Value::Boolean(integer % 2 == 0)))
} else {
Err(BuiltInFunctionError::ExpectedInteger)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
}
BuiltInFunction::IsOdd => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
if let Some(integer) = value_arguments[0].as_integer() {
Ok(Some(Value::Boolean(integer % 2 != 0)))
} else {
Err(BuiltInFunctionError::ExpectedInteger)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
}
BuiltInFunction::Length => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
if let Value::List(list) = &value_arguments[0] {
Ok(Some(Value::Integer(list.len() as i64)))
} else {
Err(BuiltInFunctionError::ExpectedInteger)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
Err(BuiltInFunctionError::ExpectedList)
}
}
BuiltInFunction::ReadLine => {
if value_arguments.is_none() {
let mut input = String::new();
stdin().read_line(&mut input)?;
Ok(Some(Value::string(input.trim_end_matches('\n'))))
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
}
BuiltInFunction::WriteLine => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
println!("{}", value_arguments[0]);
if let Value::String(string) = &value_arguments[0] {
stdout().write_all(string.as_bytes())?;
Ok(None)
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
Err(BuiltInFunctionError::ExpectedString)
}
}
}
@ -165,8 +147,12 @@ impl Display for BuiltInFunction {
#[derive(Debug, Clone, PartialEq)]
pub enum BuiltInFunctionError {
ExpectedInteger,
Io(io::ErrorKind),
ExpectedString,
ExpectedList,
ExpectedInteger,
WrongNumberOfValueArguments,
}
@ -181,8 +167,10 @@ impl Error for BuiltInFunctionError {}
impl Display for BuiltInFunctionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
BuiltInFunctionError::Io(error_kind) => write!(f, "I/O error: {}", error_kind),
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
BuiltInFunctionError::ExpectedString => write!(f, "Expected a string"),
BuiltInFunctionError::ExpectedList => write!(f, "Expected a list"),
BuiltInFunctionError::WrongNumberOfValueArguments => {
write!(f, "Wrong number of value arguments")
}

View File

@ -1,150 +1,187 @@
//! Garbage-collecting context for variables.
use std::{
collections::HashMap,
sync::{Arc, PoisonError as StdPoisonError, RwLock, RwLockWriteGuard},
fmt::{self, Display, Formatter},
sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
};
use crate::{ast::Span, Constructor, Identifier, StructType, Type, Value};
pub type Variables = HashMap<Identifier, (ContextData, Span)>;
pub type Associations = HashMap<Identifier, (ContextData, Span)>;
/// Garbage-collecting context for variables.
#[derive(Debug, Clone)]
pub struct Context {
variables: Arc<RwLock<Variables>>,
associations: Arc<RwLock<Associations>>,
}
impl Context {
pub fn new() -> Self {
Self::with_data(HashMap::new())
}
pub fn with_data(data: Associations) -> Self {
Self {
variables: Arc::new(RwLock::new(HashMap::new())),
associations: Arc::new(RwLock::new(data)),
}
}
/// Creates a deep copy of another context.
pub fn with_data_from(other: &Self) -> Self {
Self {
variables: Arc::new(RwLock::new(other.variables.read().unwrap().clone())),
}
pub fn with_data_from(other: &Self) -> Result<Self, ContextError> {
Ok(Self::with_data(other.associations.read()?.clone()))
}
/// Returns the number of variables in the context.
pub fn variable_count(&self) -> usize {
self.variables.read().unwrap().len()
/// Returns the number of associated identifiers in the context.
pub fn association_count(&self) -> Result<usize, ContextError> {
Ok(self.associations.read()?.len())
}
/// Returns a boolean indicating whether the context contains the variable.
pub fn contains(&self, identifier: &Identifier) -> bool {
self.variables.read().unwrap().contains_key(identifier)
/// Returns a boolean indicating whether the identifier is in the context.
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
Ok(self.associations.read()?.contains_key(identifier))
}
/// Returns the full VariableData and Span if the context contains the given identifier.
pub fn get(&self, identifier: &Identifier) -> Option<(ContextData, Span)> {
self.variables.read().unwrap().get(identifier).cloned()
/// Returns the full ContextData and Span if the context contains the given identifier.
pub fn get(
&self,
identifier: &Identifier,
) -> Result<Option<(ContextData, Span)>, ContextError> {
let associations = self.associations.read()?;
Ok(associations.get(identifier).cloned())
}
/// Returns the type of the variable with the given identifier.
pub fn get_type(&self, identifier: &Identifier) -> Option<Type> {
match self.variables.read().unwrap().get(identifier) {
Some((ContextData::VariableType(r#type), _)) => Some(r#type.clone()),
Some((ContextData::VariableValue(value), _)) => Some(value.r#type()),
/// Returns the type associated with the given identifier.
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
let r#type = match self.associations.read()?.get(identifier) {
Some((ContextData::VariableType(r#type), _)) => r#type.clone(),
Some((ContextData::VariableValue(value), _)) => value.r#type(),
Some((ContextData::ConstructorType(struct_type), _)) => {
Some(Type::Struct(struct_type.clone()))
Type::Struct(struct_type.clone())
}
_ => None,
_ => return Ok(None),
};
Ok(Some(r#type))
}
/// Returns the ContextData associated with the identifier.
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
match self.associations.read()?.get(identifier) {
Some((variable_data, _)) => Ok(Some(variable_data.clone())),
_ => Ok(None),
}
}
/// Returns the VariableData of the variable with the given identifier.
pub fn get_data(&self, identifier: &Identifier) -> Option<ContextData> {
match self.variables.read().unwrap().get(identifier) {
Some((variable_data, _)) => Some(variable_data.clone()),
_ => None,
/// Returns the value associated with the identifier.
pub fn get_variable_value(
&self,
identifier: &Identifier,
) -> Result<Option<Value>, ContextError> {
match self.associations.read().unwrap().get(identifier) {
Some((ContextData::VariableValue(value), _)) => Ok(Some(value.clone())),
_ => Ok(None),
}
}
/// Returns the value of the variable with the given identifier.
pub fn get_variable_value(&self, identifier: &Identifier) -> Option<Value> {
match self.variables.read().unwrap().get(identifier) {
Some((ContextData::VariableValue(value), _)) => Some(value.clone()),
_ => None,
/// Returns the constructor associated with the identifier.
pub fn get_constructor(
&self,
identifier: &Identifier,
) -> Result<Option<Constructor>, ContextError> {
match self.associations.read().unwrap().get(identifier) {
Some((ContextData::Constructor(constructor), _)) => Ok(Some(constructor.clone())),
_ => Ok(None),
}
}
/// Returns the constructor associated with the given identifier.
pub fn get_constructor(&self, identifier: &Identifier) -> Option<Constructor> {
match self.variables.read().unwrap().get(identifier) {
Some((ContextData::Constructor(constructor), _)) => Some(constructor.clone()),
_ => None,
}
}
/// Sets a variable to a type, with a position given for garbage collection.
pub fn set_variable_type(&self, identifier: Identifier, r#type: Type, position: Span) {
/// Associates an identifier with a variable type, with a position given for garbage collection.
pub fn set_variable_type(
&self,
identifier: Identifier,
r#type: Type,
position: Span,
) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to type {type} at {position:?}");
self.variables
.write()
.unwrap()
self.associations
.write()?
.insert(identifier, (ContextData::VariableType(r#type), position));
Ok(())
}
/// Sets a variable to a value.
pub fn set_variable_value(&self, identifier: Identifier, value: Value) {
/// Associates an identifier with a variable value.
pub fn set_variable_value(
&self,
identifier: Identifier,
value: Value,
) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to value {value}");
let mut variables = self.variables.write().unwrap();
let mut associations = self.associations.write()?;
let last_position = variables
let last_position = associations
.get(&identifier)
.map(|(_, last_position)| *last_position)
.unwrap_or_default();
variables.insert(
associations.insert(
identifier,
(ContextData::VariableValue(value), last_position),
);
Ok(())
}
/// Associates a constructor with an identifier.
pub fn set_constructor(&self, identifier: Identifier, constructor: Constructor) {
/// Associates an identifier with a constructor.
pub fn set_constructor(
&self,
identifier: Identifier,
constructor: Constructor,
) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to constructor {constructor}");
let mut variables = self.variables.write().unwrap();
let mut associations = self.associations.write()?;
let last_position = variables
let last_position = associations
.get(&identifier)
.map(|(_, last_position)| *last_position)
.unwrap_or_default();
variables.insert(
associations.insert(
identifier,
(ContextData::Constructor(constructor), last_position),
);
Ok(())
}
/// Associates a constructor type with an identifier.
/// Associates an identifier with a constructor type, with a position given for garbage
/// collection.
pub fn set_constructor_type(
&self,
identifier: Identifier,
struct_type: StructType,
position: Span,
) {
) -> Result<(), ContextError> {
log::trace!("Setting {identifier} to constructor of type {struct_type}");
let mut variables = self.variables.write().unwrap();
let mut variables = self.associations.write()?;
variables.insert(
identifier,
(ContextData::ConstructorType(struct_type), position),
);
Ok(())
}
/// Collects garbage up to the given position, removing all variables with lesser positions.
pub fn collect_garbage(&self, position: Span) {
pub fn collect_garbage(&self, position: Span) -> Result<(), ContextError> {
log::trace!("Collecting garbage up to {position:?}");
let mut variables = self.variables.write().unwrap();
let mut variables = self.associations.write()?;
variables.retain(|identifier, (_, last_used)| {
let should_drop = position.0 > last_used.0 && position.1 > last_used.1;
@ -156,19 +193,25 @@ impl Context {
!should_drop
});
variables.shrink_to_fit();
Ok(())
}
/// Updates a variable's last known position, allowing it to live longer in the program.
/// Returns a boolean indicating whether the variable was found.
pub fn update_last_position(&self, identifier: &Identifier, position: Span) -> bool {
if let Some((_, last_position)) = self.variables.write().unwrap().get_mut(identifier) {
/// Updates an associated identifier's last known position, allowing it to live longer in the
/// program. Returns a boolean indicating whether the identifier.
pub fn update_last_position(
&self,
identifier: &Identifier,
position: Span,
) -> Result<bool, ContextError> {
if let Some((_, last_position)) = self.associations.write()?.get_mut(identifier) {
*last_position = position;
log::trace!("Updating {identifier}'s last position to {position:?}");
true
Ok(true)
} else {
false
Ok(false)
}
}
@ -176,17 +219,18 @@ impl Context {
///
/// This method is not used. The context's other methods do not return poison errors because
/// they are infallible.
pub fn _recover_from_poison(&mut self, error: &ContextPoisonError) {
pub fn recover_from_poison(&mut self, error: &ContextError) {
log::debug!("Context is recovering from poison error");
let recovered = error.get_ref();
let mut new_variables = HashMap::new();
let ContextError::PoisonErrorRecovered(recovered) = error;
for (identifier, (variable_data, position)) in recovered.iter() {
new_variables.insert(identifier.clone(), (variable_data.clone(), *position));
let mut new_associations = HashMap::new();
for (identifier, (context_data, position)) in recovered.as_ref() {
new_associations.insert(identifier.clone(), (context_data.clone(), *position));
}
self.variables = Arc::new(RwLock::new(new_variables));
self.associations = Arc::new(RwLock::new(new_associations));
}
}
@ -204,7 +248,50 @@ pub enum ContextData {
VariableType(Type),
}
pub type ContextPoisonError<'err> = StdPoisonError<RwLockWriteGuard<'err, Variables>>;
#[derive(Debug, Clone)]
pub enum ContextError {
PoisonErrorRecovered(Arc<Associations>),
}
impl From<PoisonError<RwLockWriteGuard<'_, Associations>>> for ContextError {
fn from(error: PoisonError<RwLockWriteGuard<'_, Associations>>) -> Self {
let associations = error.into_inner().clone();
Self::PoisonErrorRecovered(Arc::new(associations))
}
}
impl From<PoisonError<RwLockReadGuard<'_, Associations>>> for ContextError {
fn from(error: PoisonError<RwLockReadGuard<'_, Associations>>) -> Self {
let associations = error.into_inner().clone();
Self::PoisonErrorRecovered(Arc::new(associations))
}
}
impl PartialEq for ContextError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::PoisonErrorRecovered(left), Self::PoisonErrorRecovered(right)) => {
Arc::ptr_eq(left, right)
}
}
}
}
impl Display for ContextError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::PoisonErrorRecovered(associations) => {
write!(
f,
"Context poisoned with {} associations recovered",
associations.len()
)
}
}
}
}
#[cfg(test)]
mod tests {
@ -226,7 +313,7 @@ mod tests {
run_with_context(source, context.clone()).unwrap();
assert_eq!(context.variable_count(), 0);
assert_eq!(context.association_count().unwrap(), 0);
}
#[test]
@ -242,6 +329,6 @@ mod tests {
run_with_context(source, context.clone()).unwrap();
assert_eq!(context.variable_count(), 0);
assert_eq!(context.association_count().unwrap(), 0);
}
}

View File

@ -0,0 +1,30 @@
use std::{collections::HashMap, sync::OnceLock};
use crate::{BuiltInFunction, Context, ContextData, Function, Identifier, Value};
static CORE_LIBRARY: OnceLock<Context> = OnceLock::new();
pub fn core_library() -> &'static Context {
CORE_LIBRARY.get_or_init(|| {
Context::with_data(HashMap::from([
(
Identifier::new("to_string"),
(
ContextData::VariableValue(Value::Function(Function::BuiltIn(
BuiltInFunction::ToString,
))),
(0, 0),
),
),
(
Identifier::new("is_even"),
(
ContextData::VariableValue(Value::Function(Function::BuiltIn(
BuiltInFunction::IsEven,
))),
(0, 0),
),
),
]))
})
}

View File

@ -64,7 +64,7 @@ impl<'src> DustError<'src> {
pub fn position(&self) -> Option<Span> {
match self {
DustError::Runtime { runtime_error, .. } => runtime_error.position(),
DustError::Analysis { analysis_error, .. } => Some(analysis_error.position()),
DustError::Analysis { analysis_error, .. } => analysis_error.position(),
DustError::Parse { parse_error, .. } => Some(parse_error.position()),
DustError::Lex { lex_error, .. } => Some(lex_error.position()),
}

View File

@ -429,17 +429,12 @@ impl<'src> Lexer<'src> {
"float" => Token::FloatKeyword,
"if" => Token::If,
"int" => Token::Int,
"is_even" => Token::IsEven,
"is_odd" => Token::IsOdd,
"length" => Token::Length,
"let" => Token::Let,
"map" => Token::Map,
"mut" => Token::Mut,
"read_line" => Token::ReadLine,
"struct" => Token::Struct,
"true" => Token::Boolean("true"),
"while" => Token::While,
"write_line" => Token::WriteLine,
_ => Token::Identifier(string),
};
@ -1023,7 +1018,7 @@ mod tests {
assert_eq!(
lex(input),
Ok(vec![
(Token::ReadLine, (0, 9)),
(Token::Identifier("read_line"), (0, 9)),
(Token::LeftParenthesis, (9, 10)),
(Token::RightParenthesis, (10, 11)),
(Token::Eof, (11, 11)),
@ -1038,7 +1033,7 @@ mod tests {
assert_eq!(
lex(input),
Ok(vec![
(Token::WriteLine, (0, 10)),
(Token::Identifier("write_line"), (0, 10)),
(Token::LeftParenthesis, (10, 11)),
(Token::String("Hello, world!"), (11, 26)),
(Token::RightParenthesis, (26, 27)),
@ -1107,7 +1102,7 @@ mod tests {
Ok(vec![
(Token::Integer("42"), (0, 2)),
(Token::Dot, (2, 3)),
(Token::IsEven, (3, 10)),
(Token::Identifier("is_even"), (3, 10)),
(Token::LeftParenthesis, (10, 11)),
(Token::RightParenthesis, (11, 12)),
(Token::Eof, (12, 12)),
@ -1128,7 +1123,10 @@ mod tests {
assert_eq!(
lex(input),
Ok(vec![(Token::Length, (0, 6)), (Token::Eof, (6, 6)),])
Ok(vec![
(Token::Identifier("length"), (0, 6)),
(Token::Eof, (6, 6)),
])
)
}

View File

@ -20,6 +20,7 @@ pub mod ast;
pub mod built_in_function;
pub mod constructor;
pub mod context;
pub mod core_library;
pub mod dust_error;
pub mod identifier;
pub mod lexer;
@ -30,10 +31,10 @@ pub mod value;
pub mod vm;
pub use analyzer::{analyze, AnalysisError, Analyzer};
pub use ast::{AbstractSyntaxTree, Expression, Statement};
pub use ast::{AbstractSyntaxTree, Expression, Span, Statement};
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
pub use constructor::Constructor;
pub use context::{Context, ContextData};
pub use context::{Context, ContextData, ContextError};
pub use dust_error::DustError;
pub use identifier::Identifier;
pub use lexer::{lex, LexError, Lexer};

View File

@ -27,17 +27,12 @@ pub enum Token<'src> {
FloatKeyword,
If,
Int,
IsEven,
IsOdd,
Length,
Let,
Map,
Mut,
ReadLine,
Str,
Struct,
While,
WriteLine,
// Symbols
BangEqual,
@ -96,12 +91,9 @@ impl<'src> Token<'src> {
Token::If => TokenOwned::If,
Token::Int => TokenOwned::Int,
Token::Integer(integer) => TokenOwned::Integer(integer.to_string()),
Token::IsEven => TokenOwned::IsEven,
Token::IsOdd => TokenOwned::IsOdd,
Token::LeftCurlyBrace => TokenOwned::LeftCurlyBrace,
Token::LeftParenthesis => TokenOwned::LeftParenthesis,
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
Token::Length => TokenOwned::Length,
Token::Let => TokenOwned::Let,
Token::Less => TokenOwned::Less,
Token::LessEqual => TokenOwned::LessOrEqual,
@ -112,7 +104,6 @@ impl<'src> Token<'src> {
Token::Percent => TokenOwned::Percent,
Token::Plus => TokenOwned::Plus,
Token::PlusEqual => TokenOwned::PlusEqual,
Token::ReadLine => TokenOwned::ReadLine,
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
Token::RightParenthesis => TokenOwned::RightParenthesis,
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
@ -123,7 +114,6 @@ impl<'src> Token<'src> {
Token::Str => TokenOwned::Str,
Token::Struct => TokenOwned::Struct,
Token::While => TokenOwned::While,
Token::WriteLine => TokenOwned::WriteLine,
}
}
@ -154,13 +144,10 @@ impl<'src> Token<'src> {
Token::GreaterEqual => ">=",
Token::If => "if",
Token::Int => "int",
Token::IsEven => "is_even",
Token::IsOdd => "is_odd",
Token::LeftCurlyBrace => "{",
Token::LeftParenthesis => "(",
Token::LeftSquareBrace => "[",
Token::Let => "let",
Token::Length => "length",
Token::Less => "<",
Token::LessEqual => "<=",
Token::Map => "map",
@ -170,7 +157,6 @@ impl<'src> Token<'src> {
Token::Percent => "%",
Token::Plus => "+",
Token::PlusEqual => "+=",
Token::ReadLine => "read_line",
Token::RightCurlyBrace => "}",
Token::RightParenthesis => ")",
Token::RightSquareBrace => "]",
@ -180,7 +166,6 @@ impl<'src> Token<'src> {
Token::Str => "str",
Token::Struct => "struct",
Token::While => "while",
Token::WriteLine => "write_line",
}
}
@ -209,13 +194,10 @@ impl<'src> Token<'src> {
Token::If => TokenKind::If,
Token::Int => TokenKind::Int,
Token::Integer(_) => TokenKind::Integer,
Token::IsEven => TokenKind::IsEven,
Token::IsOdd => TokenKind::IsOdd,
Token::LeftCurlyBrace => TokenKind::LeftCurlyBrace,
Token::LeftParenthesis => TokenKind::LeftParenthesis,
Token::LeftSquareBrace => TokenKind::LeftSquareBrace,
Token::Let => TokenKind::Let,
Token::Length => TokenKind::Length,
Token::Less => TokenKind::Less,
Token::LessEqual => TokenKind::LessOrEqual,
Token::Map => TokenKind::Map,
@ -225,7 +207,6 @@ impl<'src> Token<'src> {
Token::Percent => TokenKind::Percent,
Token::Plus => TokenKind::Plus,
Token::PlusEqual => TokenKind::PlusEqual,
Token::ReadLine => TokenKind::ReadLine,
Token::RightCurlyBrace => TokenKind::RightCurlyBrace,
Token::RightParenthesis => TokenKind::RightParenthesis,
Token::RightSquareBrace => TokenKind::RightSquareBrace,
@ -236,7 +217,6 @@ impl<'src> Token<'src> {
Token::String(_) => TokenKind::String,
Token::Struct => TokenKind::Struct,
Token::While => TokenKind::While,
Token::WriteLine => TokenKind::WriteLine,
}
}
@ -320,16 +300,11 @@ pub enum TokenOwned {
FloatKeyword,
If,
Int,
IsEven,
IsOdd,
Let,
Length,
Map,
Mut,
ReadLine,
Str,
While,
WriteLine,
// Symbols
Async,
@ -371,7 +346,7 @@ impl Display for TokenOwned {
TokenOwned::Bang => Token::Bang.fmt(f),
TokenOwned::BangEqual => Token::BangEqual.fmt(f),
TokenOwned::Bool => Token::Bool.fmt(f),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
TokenOwned::Boolean(boolean) => Token::Boolean(boolean).fmt(f),
TokenOwned::Colon => Token::Colon.fmt(f),
TokenOwned::Comma => Token::Comma.fmt(f),
TokenOwned::Dot => Token::Dot.fmt(f),
@ -382,20 +357,17 @@ impl Display for TokenOwned {
TokenOwned::Else => Token::Else.fmt(f),
TokenOwned::Eof => Token::Eof.fmt(f),
TokenOwned::Equal => Token::Equal.fmt(f),
TokenOwned::Float(float) => write!(f, "{float}"),
TokenOwned::Float(float) => Token::Float(float).fmt(f),
TokenOwned::FloatKeyword => Token::FloatKeyword.fmt(f),
TokenOwned::Greater => Token::Greater.fmt(f),
TokenOwned::GreaterOrEqual => Token::GreaterEqual.fmt(f),
TokenOwned::Identifier(text) => write!(f, "{text}"),
TokenOwned::Identifier(text) => Token::Identifier(text).fmt(f),
TokenOwned::If => Token::If.fmt(f),
TokenOwned::Int => Token::Int.fmt(f),
TokenOwned::Integer(integer) => write!(f, "{integer}"),
TokenOwned::IsEven => Token::IsEven.fmt(f),
TokenOwned::IsOdd => Token::IsOdd.fmt(f),
TokenOwned::Integer(integer) => Token::Integer(integer).fmt(f),
TokenOwned::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
TokenOwned::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenOwned::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenOwned::Length => Token::Length.fmt(f),
TokenOwned::Let => Token::Let.fmt(f),
TokenOwned::Less => Token::Less.fmt(f),
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
@ -406,7 +378,6 @@ impl Display for TokenOwned {
TokenOwned::Percent => Token::Percent.fmt(f),
TokenOwned::Plus => Token::Plus.fmt(f),
TokenOwned::PlusEqual => Token::PlusEqual.fmt(f),
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenOwned::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenOwned::RightSquareBrace => Token::RightSquareBrace.fmt(f),
@ -417,7 +388,6 @@ impl Display for TokenOwned {
TokenOwned::String(string) => write!(f, "{string}"),
TokenOwned::Struct => Token::Struct.fmt(f),
TokenOwned::While => Token::While.fmt(f),
TokenOwned::WriteLine => Token::WriteLine.fmt(f),
}
}
}
@ -442,15 +412,10 @@ pub enum TokenKind {
FloatKeyword,
If,
Int,
IsEven,
IsOdd,
Length,
Let,
Map,
ReadLine,
Str,
While,
WriteLine,
// Symbols
BangEqual,
@ -511,12 +476,9 @@ impl Display for TokenKind {
TokenKind::If => Token::If.fmt(f),
TokenKind::Int => Token::Int.fmt(f),
TokenKind::Integer => write!(f, "integer value"),
TokenKind::IsEven => Token::IsEven.fmt(f),
TokenKind::IsOdd => Token::IsOdd.fmt(f),
TokenKind::LeftCurlyBrace => Token::LeftCurlyBrace.fmt(f),
TokenKind::LeftParenthesis => Token::LeftParenthesis.fmt(f),
TokenKind::LeftSquareBrace => Token::LeftSquareBrace.fmt(f),
TokenKind::Length => Token::Length.fmt(f),
TokenKind::Let => Token::Let.fmt(f),
TokenKind::Less => Token::Less.fmt(f),
TokenKind::LessOrEqual => Token::LessEqual.fmt(f),
@ -527,7 +489,6 @@ impl Display for TokenKind {
TokenKind::Percent => Token::Percent.fmt(f),
TokenKind::Plus => Token::Plus.fmt(f),
TokenKind::PlusEqual => Token::PlusEqual.fmt(f),
TokenKind::ReadLine => Token::ReadLine.fmt(f),
TokenKind::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
TokenKind::RightParenthesis => Token::RightParenthesis.fmt(f),
TokenKind::RightSquareBrace => Token::RightSquareBrace.fmt(f),
@ -538,7 +499,6 @@ impl Display for TokenKind {
TokenKind::String => write!(f, "string value"),
TokenKind::Struct => Token::Struct.fmt(f),
TokenKind::While => Token::While.fmt(f),
TokenKind::WriteLine => Token::WriteLine.fmt(f),
}
}
}
@ -547,7 +507,7 @@ impl Display for TokenKind {
pub(crate) mod tests {
use super::*;
pub fn all_tokens<'src>() -> [Token<'src>; 51] {
pub fn all_tokens<'src>() -> [Token<'src>; 46] {
[
Token::Identifier("foobar"),
Token::Boolean("true"),
@ -573,12 +533,9 @@ pub(crate) mod tests {
Token::GreaterEqual,
Token::If,
Token::Int,
Token::IsEven,
Token::IsOdd,
Token::LeftCurlyBrace,
Token::LeftParenthesis,
Token::LeftSquareBrace,
Token::Length,
Token::Less,
Token::LessEqual,
Token::Let,
@ -589,7 +546,6 @@ pub(crate) mod tests {
Token::Percent,
Token::Plus,
Token::PlusEqual,
Token::ReadLine,
Token::RightCurlyBrace,
Token::RightParenthesis,
Token::RightSquareBrace,
@ -599,7 +555,6 @@ pub(crate) mod tests {
Token::Str,
Token::Struct,
Token::While,
Token::WriteLine,
]
}

View File

@ -4,16 +4,11 @@ use std::{
collections::HashMap,
error::Error,
fmt::{self, Display, Formatter},
ops::{Index, Range, RangeInclusive},
rc::Weak,
ops::{Range, RangeInclusive},
sync::{Arc, RwLock},
};
use serde::{
de::{self, MapAccess, Visitor},
ser::{SerializeMap, SerializeStruct, SerializeStructVariant},
Deserialize, Deserializer, Serialize, Serializer,
};
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
use crate::{
AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier,
@ -84,36 +79,12 @@ impl Value {
Value::Mutable(Arc::new(RwLock::new(into_value.into())))
}
pub fn byte_range(start: u8, end: u8) -> Value {
Value::Range(Rangeable::Byte(start)..Rangeable::Byte(end))
pub fn range<T: Into<Rangeable>>(start: T, end: T) -> Value {
Value::Range(start.into()..end.into())
}
pub fn byte_range_inclusive(start: u8, end: u8) -> Value {
Value::RangeInclusive(Rangeable::Byte(start)..=Rangeable::Byte(end))
}
pub fn character_range(start: char, end: char) -> Value {
Value::Range(Rangeable::Character(start)..Rangeable::Character(end))
}
pub fn character_range_inclusive(start: char, end: char) -> Value {
Value::RangeInclusive(Rangeable::Character(start)..=Rangeable::Character(end))
}
pub fn float_range(start: f64, end: f64) -> Value {
Value::Range(Rangeable::Float(start)..Rangeable::Float(end))
}
pub fn float_range_inclusive(start: f64, end: f64) -> Value {
Value::RangeInclusive(Rangeable::Float(start)..=Rangeable::Float(end))
}
pub fn integer_range(start: i64, end: i64) -> Value {
Value::Range(Rangeable::Integer(start)..Rangeable::Integer(end))
}
pub fn integer_range_inclusive(start: i64, end: i64) -> Value {
Value::RangeInclusive(Rangeable::Integer(start)..=Rangeable::Integer(end))
pub fn range_inclusive<T: Into<Rangeable>>(start: T, end: T) -> Value {
Value::RangeInclusive(start.into()..=end.into())
}
pub fn string<T: ToString>(to_string: T) -> Value {
@ -200,9 +171,7 @@ impl Value {
value_parameters: built_in_function.value_parameters(),
return_type: built_in_function.return_type().map(Box::new),
}),
Value::Function(Function::Parsed { name, r#type, body }) => {
Type::Function(r#type.clone())
}
Value::Function(Function::Parsed { r#type, .. }) => Type::Function(r#type.clone()),
Value::Integer(_) => Type::Integer,
Value::List(values) => {
let item_type = values.first().unwrap().r#type();
@ -265,9 +234,7 @@ impl Value {
pub fn get_field(&self, field: &Identifier) -> Option<Value> {
if let "to_string" = field.as_str() {
return Some(Value::Function(Function::BuiltIn(
BuiltInFunction::ToString {
argument: Box::new(self.clone()),
},
BuiltInFunction::ToString,
)));
}
@ -1097,13 +1064,13 @@ impl Function {
.call(_type_arguments, value_arguments)
.map_err(|error| RuntimeError::BuiltInFunctionError { error }),
Function::Parsed { r#type, body, .. } => {
let new_context = Context::with_data_from(context);
let new_context = Context::with_data_from(context)?;
if let (Some(value_parameters), Some(value_arguments)) =
(&r#type.value_parameters, value_arguments)
{
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
new_context.set_variable_value(identifier.clone(), value);
new_context.set_variable_value(identifier.clone(), value)?;
}
}
@ -1221,7 +1188,7 @@ impl Ord for Struct {
return type_cmp;
}
left_fields.into_iter().cmp(right_fields.into_iter())
left_fields.iter().cmp(right_fields.iter())
}
(Struct::Fields { .. }, _) => Ordering::Greater,
}
@ -1258,7 +1225,6 @@ impl Display for Struct {
write!(f, " }}")
}
_ => Ok(()),
}
}
}
@ -1271,6 +1237,36 @@ pub enum Rangeable {
Integer(i64),
}
impl From<u8> for Rangeable {
fn from(value: u8) -> Self {
Rangeable::Byte(value)
}
}
impl From<char> for Rangeable {
fn from(value: char) -> Self {
Rangeable::Character(value)
}
}
impl From<f64> for Rangeable {
fn from(value: f64) -> Self {
Rangeable::Float(value)
}
}
impl From<i32> for Rangeable {
fn from(value: i32) -> Self {
Rangeable::Integer(value as i64)
}
}
impl From<i64> for Rangeable {
fn from(value: i64) -> Self {
Rangeable::Integer(value)
}
}
impl Rangeable {
fn r#type(&self) -> RangeableType {
match self {

View File

@ -20,8 +20,8 @@ use crate::{
OperatorExpression, PrimitiveValueExpression, RangeExpression, Span, Statement,
StructDefinition, StructExpression,
},
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, DustError,
Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError,
parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, ContextError,
DustError, Expression, Function, Identifier, ParseError, StructType, Type, Value, ValueError,
};
/// Run the source code and return the result.
@ -56,7 +56,7 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
/// ```
pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context);
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context.clone());
analyzer
.analyze()
@ -224,28 +224,7 @@ impl Vm {
Expression::Grouped(expression) => {
self.run_expression(*expression.inner, collect_garbage)
}
Expression::Identifier(identifier) => {
log::debug!("Running identifier: {}", identifier.inner);
let get_data = self.context.get_data(&identifier.inner);
if let Some(ContextData::VariableValue(value)) = get_data {
return Ok(Evaluation::Return(Some(value)));
}
if let Some(ContextData::Constructor(constructor)) = get_data {
if let Constructor::Unit(unit_constructor) = constructor {
return Ok(Evaluation::Return(Some(unit_constructor.construct())));
}
return Ok(Evaluation::Constructor(constructor));
}
Err(RuntimeError::UndefinedValue {
identifier: identifier.inner,
position,
})
}
Expression::Identifier(identifier) => self.run_identifier(identifier.inner),
Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage),
Expression::List(list_expression) => {
self.run_list(*list_expression.inner, collect_garbage)
@ -274,6 +253,26 @@ impl Vm {
})
}
fn run_identifier(&self, identifier: Identifier) -> Result<Evaluation, RuntimeError> {
log::debug!("Running identifier: {}", identifier);
let get_data = self.context.get_data(&identifier)?;
if let Some(ContextData::VariableValue(value)) = get_data {
return Ok(Evaluation::Return(Some(value)));
}
if let Some(ContextData::Constructor(constructor)) = get_data {
if let Constructor::Unit(unit_constructor) = constructor {
return Ok(Evaluation::Return(Some(unit_constructor.construct())));
}
return Ok(Evaluation::Constructor(constructor));
}
Err(RuntimeError::UnassociatedIdentifier { identifier })
}
fn run_struct(
&self,
struct_expression: StructExpression,
@ -284,7 +283,7 @@ impl Vm {
let StructExpression::Fields { name, fields } = struct_expression;
let position = name.position;
let constructor = self.context.get_constructor(&name.inner);
let constructor = self.context.get_constructor(&name.inner)?;
if let Some(constructor) = constructor {
if let Constructor::Fields(fields_constructor) = constructor {
@ -328,16 +327,16 @@ impl Vm {
match (start, end) {
(Value::Byte(start), Value::Byte(end)) => {
Ok(Evaluation::Return(Some(Value::byte_range(start, end))))
Ok(Evaluation::Return(Some(Value::range(start, end))))
}
(Value::Character(start), Value::Character(end)) => {
Ok(Evaluation::Return(Some(Value::character_range(start, end))))
Ok(Evaluation::Return(Some(Value::range(start, end))))
}
(Value::Float(start), Value::Float(end)) => {
Ok(Evaluation::Return(Some(Value::float_range(start, end))))
Ok(Evaluation::Return(Some(Value::range(start, end))))
}
(Value::Integer(start), Value::Integer(end)) => {
Ok(Evaluation::Return(Some(Value::integer_range(start, end))))
Ok(Evaluation::Return(Some(Value::range(start, end))))
}
_ => Err(RuntimeError::InvalidRange {
start_position,
@ -356,18 +355,18 @@ impl Vm {
.expect_value(end_position)?;
match (start, end) {
(Value::Byte(start), Value::Byte(end)) => Ok(Evaluation::Return(Some(
Value::byte_range_inclusive(start, end),
))),
(Value::Character(start), Value::Character(end)) => Ok(Evaluation::Return(
Some(Value::character_range_inclusive(start, end)),
)),
(Value::Float(start), Value::Float(end)) => Ok(Evaluation::Return(Some(
Value::float_range_inclusive(start, end),
))),
(Value::Integer(start), Value::Integer(end)) => Ok(Evaluation::Return(Some(
Value::integer_range_inclusive(start, end),
))),
(Value::Byte(start), Value::Byte(end)) => {
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
}
(Value::Character(start), Value::Character(end)) => {
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
}
(Value::Float(start), Value::Float(end)) => {
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
}
(Value::Integer(start), Value::Integer(end)) => {
Ok(Evaluation::Return(Some(Value::range_inclusive(start, end))))
}
_ => Err(RuntimeError::InvalidRange {
start_position,
end_position,
@ -661,6 +660,50 @@ impl Vm {
let CallExpression { invoker, arguments } = call_expression;
if let Expression::FieldAccess(field_access) = invoker {
let FieldAccessExpression { container, field } = *field_access.inner;
let container_position = container.position();
let container_value = self
.run_expression(container, collect_garbage)?
.expect_value(container_position)?;
let function = if let Some(value) = container_value.get_field(&field.inner) {
if let Value::Function(function) = value {
function
} else {
return Err(RuntimeError::ExpectedFunction {
actual: value,
position: container_position,
});
}
} else {
return Err(RuntimeError::UndefinedProperty {
value: container_value,
value_position: container_position,
property: field.inner,
property_position: field.position,
});
};
let mut value_arguments = vec![container_value];
for argument in arguments {
let position = argument.position();
let value = self
.run_expression(argument, collect_garbage)?
.expect_value(position)?;
value_arguments.push(value);
}
let context = Context::new();
return function
.call(None, Some(value_arguments), &context)
.map(Evaluation::Return);
}
let invoker_position = invoker.position();
let run_invoker = self.run_expression(invoker, collect_garbage)?;
@ -917,6 +960,7 @@ impl Evaluation {
#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError {
ContextError(ContextError),
ParseError(ParseError),
Expression {
error: Box<RuntimeError>,
@ -990,11 +1034,10 @@ pub enum RuntimeError {
start_position: Span,
end_position: Span,
},
UndefinedType {
UnassociatedIdentifier {
identifier: Identifier,
position: Span,
},
UndefinedValue {
UndefinedType {
identifier: Identifier,
position: Span,
},
@ -1006,10 +1049,19 @@ pub enum RuntimeError {
},
}
impl From<ContextError> for RuntimeError {
fn from(error: ContextError) -> Self {
Self::ContextError(error)
}
}
impl RuntimeError {
pub fn position(&self) -> Option<Span> {
let position = match self {
Self::ContextError(_) => return None,
Self::BuiltInFunctionError { .. } => return None,
Self::UnassociatedIdentifier { .. } => return None,
Self::ParseError(parse_error) => parse_error.position(),
Self::Expression { position, .. } => *position,
Self::Statement { position, .. } => *position,
@ -1032,14 +1084,14 @@ impl RuntimeError {
Self::ExpectedNumber { position } => *position,
Self::ExpectedType { position, .. } => *position,
Self::ExpectedValue { position } => *position,
Self::ExpectedValueOrConstructor { position } => *position,
Self::InvalidRange {
start_position,
end_position,
..
} => (start_position.0, end_position.1),
Self::UndefinedType { position, .. } => *position,
Self::UndefinedValue { position, .. } => *position,
Self::ExpectedValueOrConstructor { position } => *position,
Self::UndefinedProperty {
property_position, ..
} => *property_position,
@ -1058,6 +1110,7 @@ impl From<ParseError> for RuntimeError {
impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::ContextError(context_error) => write!(f, "{}", context_error),
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
Self::Expression { error, position } => {
write!(
@ -1181,14 +1234,10 @@ impl Display for RuntimeError {
start_position, end_position
)
}
Self::UndefinedValue {
identifier,
position,
} => {
Self::UnassociatedIdentifier { identifier } => {
write!(
f,
"Undefined value {} at position: {:?}",
identifier, position
"Identifier \"{identifier}\" is not associated with a value or constructor"
)
}
Self::UndefinedProperty {
@ -1343,7 +1392,7 @@ mod tests {
fn range() {
let input = "1..5";
assert_eq!(run(input), Ok(Some(Value::integer_range(1, 5))));
assert_eq!(run(input), Ok(Some(Value::range(1, 5))));
}
#[test]