From 83f856385beeae97c9c540ab9abf438eba409b72 Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 20 Aug 2024 11:07:13 -0400 Subject: [PATCH] Refine errors and error propagation --- dust-lang/src/analyzer.rs | 67 +++++--- dust-lang/src/ast/expression.rs | 125 ++++++++++----- dust-lang/src/ast/mod.rs | 47 ++++++ dust-lang/src/ast/statement.rs | 15 +- dust-lang/src/built_in_function.rs | 94 +++++------ dust-lang/src/context.rs | 241 ++++++++++++++++++++--------- dust-lang/src/core_library.rs | 30 ++++ dust-lang/src/dust_error.rs | 2 +- dust-lang/src/lexer.rs | 16 +- dust-lang/src/lib.rs | 5 +- dust-lang/src/token.rs | 55 +------ dust-lang/src/value.rs | 86 +++++----- dust-lang/src/vm.rs | 157 ++++++++++++------- 13 files changed, 587 insertions(+), 353 deletions(-) create mode 100644 dust-lang/src/core_library.rs diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index f0cc121..001e5d8 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -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 for AnalysisError { + fn from(v: AstError) -> Self { + Self::AstError(v) + } +} + +impl From 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 { + 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) } diff --git a/dust-lang/src/ast/expression.rs b/dust-lang/src/ast/expression.rs index 1353f08..bb04842 100644 --- a/dust-lang/src/ast/expression.rs +++ b/dust-lang/src/ast/expression.rs @@ -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 { - match self { + pub fn return_type<'recovered>( + &self, + context: &'recovered Context, + ) -> Result, 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() { - IfExpression::If { .. } => None, - IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context), - } - } + 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)?, + }, Expression::List(list_expression) => match list_expression.inner.as_ref() { ListExpression::AutoFill { repeat_operand, .. } => { let item_type = repeat_operand.return_type(context)?; - Some(Type::ListOf { - item_type: Box::new(item_type), - }) + if let Some(r#type) = item_type { + Some(Type::ListOf { + 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 { + fn return_type<'recovered>( + &self, + context: &'recovered Context, + ) -> Result, AstError> { match self { BlockExpression::Async(statements) | BlockExpression::Sync(statements) => { if let Some(statement) = statements.last() { statement.return_type(context) } else { - None + Ok(None) } } } diff --git a/dust-lang/src/ast/mod.rs b/dust-lang/src/ast/mod.rs index a706cf1..564a48a 100644 --- a/dust-lang/src/ast/mod.rs +++ b/dust-lang/src/ast/mod.rs @@ -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 Display for Node { 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 { + 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 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) + } + } + } +} diff --git a/dust-lang/src/ast/statement.rs b/dust-lang/src/ast/statement.rs index 97993cc..ed0e360 100644 --- a/dust-lang/src/ast/statement.rs +++ b/dust-lang/src/ast/statement.rs @@ -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 { + pub fn return_type<'recovered>( + &self, + context: &'recovered Context, + ) -> Result, 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), } } } diff --git a/dust-lang/src/built_in_function.rs b/dust-lang/src/built_in_function.rs index d1779ba..22f418f 100644 --- a/dust-lang/src/built_in_function.rs +++ b/dust-lang/src/built_in_function.rs @@ -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 }, + ToString, // Integer and float tools IsEven, @@ -82,75 +82,57 @@ impl BuiltInFunction { _type_arguments: Option>, value_arguments: Option>, ) -> Result, 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) - } + if let Some(integer) = value_arguments[0].as_integer() { + Ok(Some(Value::Boolean(integer % 2 == 0))) } else { - Err(BuiltInFunctionError::WrongNumberOfValueArguments) + Err(BuiltInFunctionError::ExpectedInteger) } } 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) - } + if let Some(integer) = value_arguments[0].as_integer() { + Ok(Some(Value::Boolean(integer % 2 != 0))) } else { - Err(BuiltInFunctionError::WrongNumberOfValueArguments) + Err(BuiltInFunctionError::ExpectedInteger) } } 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) - } + if let Value::List(list) = &value_arguments[0] { + Ok(Some(Value::Integer(list.len() as i64))) } else { - Err(BuiltInFunctionError::WrongNumberOfValueArguments) + Err(BuiltInFunctionError::ExpectedList) } } BuiltInFunction::ReadLine => { - if value_arguments.is_none() { - let mut input = String::new(); + let mut input = String::new(); - stdin().read_line(&mut input)?; + stdin().read_line(&mut input)?; - Ok(Some(Value::string(input.trim_end_matches('\n')))) - } else { - Err(BuiltInFunctionError::WrongNumberOfValueArguments) - } + Ok(Some(Value::string(input.trim_end_matches('\n')))) } 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) - } + Ok(None) } 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") } diff --git a/dust-lang/src/context.rs b/dust-lang/src/context.rs index fb4501e..ce4c67e 100644 --- a/dust-lang/src/context.rs +++ b/dust-lang/src/context.rs @@ -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; +pub type Associations = HashMap; /// Garbage-collecting context for variables. #[derive(Debug, Clone)] pub struct Context { - variables: Arc>, + associations: Arc>, } 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 { + 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 { + 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 { + 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, 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 { - 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, 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, 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 { - 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, 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 { - 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, 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 { - 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 { + 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>; +#[derive(Debug, Clone)] +pub enum ContextError { + PoisonErrorRecovered(Arc), +} + +impl From>> for ContextError { + fn from(error: PoisonError>) -> Self { + let associations = error.into_inner().clone(); + + Self::PoisonErrorRecovered(Arc::new(associations)) + } +} + +impl From>> for ContextError { + fn from(error: PoisonError>) -> 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); } } diff --git a/dust-lang/src/core_library.rs b/dust-lang/src/core_library.rs new file mode 100644 index 0000000..1b167b6 --- /dev/null +++ b/dust-lang/src/core_library.rs @@ -0,0 +1,30 @@ +use std::{collections::HashMap, sync::OnceLock}; + +use crate::{BuiltInFunction, Context, ContextData, Function, Identifier, Value}; + +static CORE_LIBRARY: OnceLock = 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), + ), + ), + ])) + }) +} diff --git a/dust-lang/src/dust_error.rs b/dust-lang/src/dust_error.rs index 1e9cc64..6ecd46e 100644 --- a/dust-lang/src/dust_error.rs +++ b/dust-lang/src/dust_error.rs @@ -64,7 +64,7 @@ impl<'src> DustError<'src> { pub fn position(&self) -> Option { 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()), } diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index e94db90..930ef0c 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -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)), + ]) ) } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 837d1bd..c078cac 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -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}; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index 91bdcdd..1979c30 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -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, ] } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 2d67c59..a8f049b 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -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>(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>(start: T, end: T) -> Value { + Value::RangeInclusive(start.into()..=end.into()) } pub fn string(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 { 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 for Rangeable { + fn from(value: u8) -> Self { + Rangeable::Byte(value) + } +} + +impl From for Rangeable { + fn from(value: char) -> Self { + Rangeable::Character(value) + } +} + +impl From for Rangeable { + fn from(value: f64) -> Self { + Rangeable::Float(value) + } +} + +impl From for Rangeable { + fn from(value: i32) -> Self { + Rangeable::Integer(value as i64) + } +} + +impl From for Rangeable { + fn from(value: i64) -> Self { + Rangeable::Integer(value) + } +} + impl Rangeable { fn r#type(&self) -> RangeableType { match self { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index d35b003..71351c8 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -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, DustError> { /// ``` pub fn run_with_context(source: &str, context: Context) -> Result, 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 { + 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, @@ -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 for RuntimeError { + fn from(error: ContextError) -> Self { + Self::ContextError(error) + } +} + impl RuntimeError { pub fn position(&self) -> Option { 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 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]