From 21b63b59426b979b79fd3c51d640339f2380cf64 Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 29 Aug 2024 12:32:48 -0400 Subject: [PATCH] Add context stacks --- dust-lang/src/analyzer.rs | 118 +++++++++++++++++++++----------------- dust-lang/src/context.rs | 81 ++++++++++++++++++++++++-- dust-lang/src/lib.rs | 4 +- dust-lang/src/value.rs | 14 +++-- dust-lang/src/vm.rs | 78 +++++++++++-------------- 5 files changed, 187 insertions(+), 108 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 52aca99..3d75fb3 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -17,7 +17,7 @@ use crate::{ PrimitiveValueExpression, RangeExpression, Span, Statement, StructDefinition, StructExpression, TupleAccessExpression, }, - core_library, parse, Context, ContextError, DustError, Expression, Identifier, RangeableType, + parse, ContextError, ContextStack, DustError, Expression, Identifier, RangeableType, StructType, Type, TypeConflict, TypeEvaluation, }; @@ -34,8 +34,8 @@ use crate::{ /// ``` pub fn analyze(source: &str) -> Result<(), DustError> { let abstract_tree = parse(source)?; - let context = core_library().create_child(); - let mut analyzer = Analyzer::new(&abstract_tree, context); + let context_stack = ContextStack::with_core_library(); + let mut analyzer = Analyzer::new(&abstract_tree, context_stack); analyzer.analyze(); @@ -54,22 +54,22 @@ pub fn analyze(source: &str) -> Result<(), DustError> { /// # use dust_lang::*; /// let input = "x = 1 + false"; /// let abstract_tree = parse(input).unwrap(); -/// let context = Context::new(); -/// let mut analyzer = Analyzer::new(&abstract_tree, context); +/// let context_stack = ContextStack::new(); +/// let mut analyzer = Analyzer::new(&abstract_tree, context_stack); /// let result = analyzer.analyze(); /// /// assert!(!analyzer.errors.is_empty()); pub struct Analyzer<'a> { abstract_tree: &'a AbstractSyntaxTree, - context: Context, + context_stack: ContextStack, pub errors: Vec, } impl<'a> Analyzer<'a> { - pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self { + pub fn new(abstract_tree: &'a AbstractSyntaxTree, context_stack: ContextStack) -> Self { Self { abstract_tree, - context, + context_stack, errors: Vec::new(), } } @@ -81,6 +81,8 @@ impl<'a> Analyzer<'a> { } fn analyze_statement(&mut self, statement: &Statement) { + let context = self.context_stack.top(); + match statement { Statement::Expression(expression) => { self.analyze_expression(expression, statement.position()) @@ -91,14 +93,14 @@ impl<'a> Analyzer<'a> { Statement::Let(let_statement) => match &let_statement.inner { LetStatement::Let { identifier, value } | LetStatement::LetMut { identifier, value } => { - let r#type = match value.type_evaluation(&self.context) { + let r#type = match value.type_evaluation(&context) { Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); return; } Ok(TypeEvaluation::Constructor(StructType::Unit { name })) => { - let set_type = self.context.set_variable_type( + let set_type = context.set_variable_type( identifier.inner.clone(), Type::Struct(StructType::Unit { name }), statement.position(), @@ -119,7 +121,7 @@ impl<'a> Analyzer<'a> { }; if let Some(r#type) = r#type { - let set_type = self.context.set_variable_type( + let set_type = context.set_variable_type( identifier.inner.clone(), r#type.clone(), statement.position(), @@ -145,7 +147,7 @@ impl<'a> Analyzer<'a> { }, Statement::StructDefinition(struct_definition) => { let set_constructor_type = match &struct_definition.inner { - StructDefinition::Unit { name } => self.context.set_constructor_type( + StructDefinition::Unit { name } => context.set_constructor_type( name.inner.clone(), StructType::Unit { name: name.inner.clone(), @@ -155,7 +157,7 @@ impl<'a> Analyzer<'a> { StructDefinition::Tuple { name, items } => { let fields = items.iter().map(|item| item.inner.clone()).collect(); - self.context.set_constructor_type( + context.set_constructor_type( name.inner.clone(), StructType::Tuple { name: name.inner.clone(), @@ -172,7 +174,7 @@ impl<'a> Analyzer<'a> { }) .collect(); - self.context.set_constructor_type( + context.set_constructor_type( name.inner.clone(), StructType::Fields { name: name.inner.clone(), @@ -194,6 +196,8 @@ impl<'a> Analyzer<'a> { } fn analyze_expression(&mut self, expression: &Expression, statement_position: Span) { + let context = self.context_stack.top(); + match expression { Expression::Block(block_expression) => self.analyze_block(&block_expression.inner), Expression::Break(break_node) => { @@ -206,7 +210,7 @@ impl<'a> Analyzer<'a> { self.analyze_expression(invoker, statement_position); - let invoker_evaluation = match invoker.type_evaluation(&self.context) { + let invoker_evaluation = match invoker.type_evaluation(&context) { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -219,7 +223,7 @@ impl<'a> Analyzer<'a> { invoker_evaluation { for (expected_type, argument) in fields.iter().zip(arguments.iter()) { - let actual_type = match argument.type_evaluation(&self.context) { + let actual_type = match argument.type_evaluation(&context) { Ok(evaluation) => evaluation.r#type(), Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -282,7 +286,7 @@ impl<'a> Analyzer<'a> { for ((_, expected_type), argument) in value_parameters.iter().zip(arguments) { self.analyze_expression(argument, statement_position); - let argument_evaluation = match argument.type_evaluation(&self.context) { + let argument_evaluation = match argument.type_evaluation(&context) { Ok(evaluation) => evaluation, Err(error) => { self.errors.push(AnalysisError::AstError(error)); @@ -321,7 +325,7 @@ impl<'a> Analyzer<'a> { let FieldAccessExpression { container, field } = field_access_expression.inner.as_ref(); - let evaluation = match container.type_evaluation(&self.context) { + let evaluation = match container.type_evaluation(&context) { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -355,7 +359,8 @@ impl<'a> Analyzer<'a> { } Expression::Identifier(identifier) => { let find_identifier = self - .context + .context_stack + .top() .update_last_position(&identifier.inner, statement_position); if let Ok(false) = find_identifier { @@ -394,7 +399,7 @@ impl<'a> Analyzer<'a> { self.analyze_expression(list, statement_position); self.analyze_expression(index, statement_position); - let list_type_evaluation = match list.type_evaluation(&self.context) { + let list_type_evaluation = match list.type_evaluation(&context) { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -413,7 +418,7 @@ impl<'a> Analyzer<'a> { return; } }; - let index_type_evaluation = match index.type_evaluation(&self.context) { + let index_type_evaluation = match index.type_evaluation(&context) { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -540,22 +545,24 @@ impl<'a> Analyzer<'a> { self.analyze_expression(assignee, statement_position); self.analyze_expression(modifier, statement_position); - let assignee_type_evaluation = match assignee.type_evaluation(&self.context) { - Ok(evaluation) => evaluation, - Err(ast_error) => { - self.errors.push(AnalysisError::AstError(ast_error)); + let assignee_type_evaluation = + match assignee.type_evaluation(&self.context_stack.top()) { + Ok(evaluation) => evaluation, + Err(ast_error) => { + self.errors.push(AnalysisError::AstError(ast_error)); - return; - } - }; - let modifier_type_evaluation = match modifier.type_evaluation(&self.context) { - Ok(evaluation) => evaluation, - Err(ast_error) => { - self.errors.push(AnalysisError::AstError(ast_error)); + return; + } + }; + let modifier_type_evaluation = + match modifier.type_evaluation(&self.context_stack.top()) { + Ok(evaluation) => evaluation, + Err(ast_error) => { + self.errors.push(AnalysisError::AstError(ast_error)); - return; - } - }; + return; + } + }; let (expected_type, actual_type) = match ( assignee_type_evaluation.r#type(), @@ -608,7 +615,8 @@ impl<'a> Analyzer<'a> { self.analyze_expression(left, statement_position); self.analyze_expression(right, statement_position); - let left_type_evaluation = match left.type_evaluation(&self.context) { + let left_type_evaluation = match left.type_evaluation(&self.context_stack.top()) + { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -616,14 +624,15 @@ impl<'a> Analyzer<'a> { return; } }; - let right_type_evaluation = match right.type_evaluation(&self.context) { - Ok(evaluation) => evaluation, - Err(ast_error) => { - self.errors.push(AnalysisError::AstError(ast_error)); + let right_type_evaluation = + match right.type_evaluation(&self.context_stack.top()) { + Ok(evaluation) => evaluation, + Err(ast_error) => { + self.errors.push(AnalysisError::AstError(ast_error)); - return; - } - }; + return; + } + }; let (left_type, right_type) = match ( left_type_evaluation.r#type(), @@ -704,7 +713,8 @@ impl<'a> Analyzer<'a> { self.analyze_expression(left, statement_position); self.analyze_expression(right, statement_position); - let left_type_evaluation = match left.type_evaluation(&self.context) { + let left_type_evaluation = match left.type_evaluation(&self.context_stack.top()) + { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); @@ -712,14 +722,15 @@ impl<'a> Analyzer<'a> { return; } }; - let right_type_evaluation = match right.type_evaluation(&self.context) { - Ok(evaluation) => evaluation, - Err(ast_error) => { - self.errors.push(AnalysisError::AstError(ast_error)); + let right_type_evaluation = + match right.type_evaluation(&self.context_stack.top()) { + Ok(evaluation) => evaluation, + Err(ast_error) => { + self.errors.push(AnalysisError::AstError(ast_error)); - return; - } - }; + return; + } + }; let (left_type, right_type) = match ( left_type_evaluation.r#type(), @@ -775,7 +786,8 @@ impl<'a> Analyzer<'a> { Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() { StructExpression::Fields { name, fields } => { let update_position = self - .context + .context_stack + .top() .update_last_position(&name.inner, statement_position); if let Err(error) = update_position { @@ -795,7 +807,7 @@ impl<'a> Analyzer<'a> { Expression::TupleAccess(tuple_access) => { let TupleAccessExpression { tuple, index } = tuple_access.inner.as_ref(); - let type_evaluation = match tuple.type_evaluation(&self.context) { + let type_evaluation = match tuple.type_evaluation(&context) { Ok(evaluation) => evaluation, Err(ast_error) => { self.errors.push(AnalysisError::AstError(ast_error)); diff --git a/dust-lang/src/context.rs b/dust-lang/src/context.rs index ed32ad1..5008453 100644 --- a/dust-lang/src/context.rs +++ b/dust-lang/src/context.rs @@ -5,7 +5,57 @@ use std::{ sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard}, }; -use crate::{ast::Span, Constructor, Identifier, StructType, Type, Value}; +use crate::{ast::Span, core_library, Constructor, Identifier, StructType, Type, Value}; + +#[derive(Debug, Clone)] +pub struct ContextStack { + stack: Arc>>, +} + +impl ContextStack { + pub fn new() -> Self { + Self { + stack: Arc::new(RwLock::new(vec![Context::new()])), + } + } + + pub fn with_context(context: Context) -> Self { + Self { + stack: Arc::new(RwLock::new(vec![context])), + } + } + + pub fn with_core_library() -> Self { + Self::with_context(core_library().create_child()) + } + + pub fn push(&self) { + let mut stack = self.stack.write().unwrap(); + let top = stack.last().unwrap().clone(); + + stack.push(top.create_child()); + } + + pub fn pop(&self) { + let removed = self.stack.write().unwrap().pop(); + + if let Some(removed) = removed { + for (identifier, _) in removed.associations.read().unwrap().iter() { + log::trace!("Dropping context with: {}", identifier); + } + } + } + + pub fn top(&self) -> Context { + self.stack.read().unwrap().last().unwrap().clone() + } +} + +impl Default for ContextStack { + fn default() -> Self { + Self::new() + } +} pub type Associations = HashMap; @@ -372,10 +422,33 @@ impl Display for ContextError { #[cfg(test)] mod tests { - use crate::vm::run_with_context; + use crate::{parse, Analyzer, DustError, Vm}; use super::*; + fn run_vm_with_context(source: &str, context: Context) -> Result, DustError> { + let context_stack = ContextStack::with_context(context); + let abstract_syntax_tree = parse(source)?; + let mut analyzer = Analyzer::new(&abstract_syntax_tree, context_stack.clone()); + + analyzer.analyze(); + + if !analyzer.errors.is_empty() { + return Err(DustError::Analysis { + analysis_errors: analyzer.errors, + source, + }); + } + + let vm = Vm::new(context_stack); + + vm.run(abstract_syntax_tree) + .map_err(|runtime_error| DustError::Runtime { + runtime_error, + source, + }) + } + #[test] fn context_removes_variables() { let source = " @@ -387,7 +460,7 @@ mod tests { let context = Context::new(); assert_eq!( - run_with_context(source, context.clone()), + run_vm_with_context(source, context.clone()), Ok(Some(Value::integer(15))) ); @@ -405,7 +478,7 @@ mod tests { "; let context = Context::new(); - run_with_context(source, context.clone()).unwrap(); + run_vm_with_context(source, context.clone()).unwrap(); assert_eq!(context.association_count().unwrap(), 0); } diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index e5e0159..0d39dab 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -35,7 +35,7 @@ pub use analyzer::{analyze, AnalysisError, Analyzer}; pub use ast::{AbstractSyntaxTree, AstError, Expression, Node, Span, Statement}; pub use built_in_function::{BuiltInFunction, BuiltInFunctionError}; pub use constructor::Constructor; -pub use context::{Context, ContextData, ContextError}; +pub use context::{Context, ContextData, ContextError, ContextStack}; pub use core_library::core_library; pub use dust_error::DustError; pub use evaluation::{Evaluation, TypeEvaluation}; @@ -45,4 +45,4 @@ pub use parser::{parse, ParseError, Parser}; pub use r#type::*; pub use token::{Token, TokenKind, TokenOwned}; pub use value::*; -pub use vm::{run, run_with_context, RuntimeError, Vm}; +pub use vm::{run, RuntimeError, Vm}; diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index c6827cb..0542684 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -15,8 +15,8 @@ use serde::{ }; use crate::{ - AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, EnumType, - FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm, + AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, ContextStack, + EnumType, FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm, }; /// Dust value representation @@ -1405,20 +1405,22 @@ impl Function { .call(_type_arguments, value_arguments) .map_err(FunctionCallError::BuiltInFunction), Function::Parsed { r#type, body, .. } => { - let new_context = - Context::with_data_from(context).map_err(FunctionCallError::Context)?; + let base_context = context.create_child(); + let new_context_stack = ContextStack::with_context(base_context.clone()); 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 + base_context .set_variable_value(identifier.clone(), value) .map_err(FunctionCallError::Context)?; } } - let vm = Vm::new(new_context); + drop(base_context); + + let vm = Vm::new(new_context_stack); vm.run(body) .map_err(|error| FunctionCallError::Runtime(Box::new(error))) diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 375aaeb..8127ca1 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -21,9 +21,10 @@ use crate::{ StructDefinition, StructExpression, }, constructor::ConstructError, - core_library, parse, Analyzer, BuiltInFunctionError, Context, ContextData, ContextError, - DustError, Evaluation, Expression, Function, FunctionCallError, Identifier, ParseError, - StructType, Type, Value, ValueData, ValueError, + context::ContextStack, + parse, Analyzer, BuiltInFunctionError, Context, ContextData, ContextError, DustError, + Evaluation, Expression, Function, FunctionCallError, Identifier, ParseError, StructType, Type, + Value, ValueData, ValueError, }; /// Run the source code and return the result. @@ -37,28 +38,9 @@ use crate::{ /// assert_eq!(result, Ok(Some(Value::integer(42)))); /// ``` pub fn run(source: &str) -> Result, DustError> { - let context = core_library().create_child(); - - run_with_context(source, context) -} - -/// Run the source code with a context and return the result. -/// -/// # Example -/// ``` -/// # use dust_lang::*; -/// let context = Context::new(); -/// -/// context.set_variable_value(Identifier::new("foo"), Value::integer(40)); -/// context.update_last_position(&Identifier::new("foo"), (100, 100)); -/// -/// let result = run_with_context("foo + 2", context); -/// -/// assert_eq!(result, Ok(Some(Value::integer(42)))); -/// ``` -pub fn run_with_context(source: &str, context: Context) -> Result, DustError> { + let context_stack = ContextStack::with_core_library(); let abstract_syntax_tree = parse(source)?; - let mut analyzer = Analyzer::new(&abstract_syntax_tree, context.clone()); + let mut analyzer = Analyzer::new(&abstract_syntax_tree, context_stack.clone()); analyzer.analyze(); @@ -69,7 +51,7 @@ pub fn run_with_context(source: &str, context: Context) -> Result, }); } - let vm = Vm::new(context); + let vm = Vm::new(context_stack); vm.run(abstract_syntax_tree) .map_err(|runtime_error| DustError::Runtime { @@ -86,12 +68,12 @@ pub fn run_with_context(source: &str, context: Context) -> Result, /// /// See the `run_with_context` function for an example of how to use the Analyzer and the VM. pub struct Vm { - context: Context, + context_stack: ContextStack, } impl Vm { - pub fn new(context: Context) -> Self { - Self { context } + pub fn new(context_stack: ContextStack) -> Self { + Self { context_stack } } pub fn run(&self, mut tree: AbstractSyntaxTree) -> Result, RuntimeError> { @@ -209,7 +191,8 @@ impl Vm { }; let constructor = struct_type.constructor(); - self.context + self.context_stack + .top() .set_constructor(name, constructor) .map_err(|error| RuntimeError::ContextError { error, @@ -221,7 +204,8 @@ impl Vm { }; if collect_garbage { - self.context + self.context_stack + .top() .collect_garbage(position.1) .map_err(|error| RuntimeError::ContextError { error, position })?; } @@ -250,7 +234,8 @@ impl Vm { _ => value, }; - self.context + self.context_stack + .top() .set_variable_value(identifier.inner, new_value) .map_err(|error| RuntimeError::ContextError { error, position })?; @@ -263,7 +248,8 @@ impl Vm { .expect_value(position)? .into_mutable(); - self.context + self.context_stack + .top() .set_variable_value(identifier.inner, mutable_value) .map_err(|error| RuntimeError::ContextError { error, position })?; @@ -364,12 +350,14 @@ impl Vm { } fn run_identifier(&self, identifier: Node) -> Result { - let get_data = self.context.get_data(&identifier.inner).map_err(|error| { - RuntimeError::ContextError { + let get_data = self + .context_stack + .top() + .get_data(&identifier.inner) + .map_err(|error| RuntimeError::ContextError { error, position: identifier.position, - } - })?; + })?; if let Some(ContextData::VariableValue(value)) = get_data { return Ok(Evaluation::Return(Some(value))); @@ -403,7 +391,8 @@ impl Vm { let position = name.position; let constructor = self - .context + .context_stack + .top() .get_constructor(&name.inner) .map_err(|error| RuntimeError::ContextError { error, position })?; @@ -1020,18 +1009,17 @@ impl Vm { block: BlockExpression, collect_garbage: bool, ) -> Result { - let block_context = self.context.create_child(); - let vm = Vm::new(block_context); + self.context_stack.push(); - match block { - BlockExpression::Async(statements) => vm + let evaluation = match block { + BlockExpression::Async(statements) => self .run_async(AbstractSyntaxTree::with_statements(statements)) .map(Evaluation::Return), BlockExpression::Sync(statements) => { let mut evaluation = Evaluation::Return(None); for statement in statements { - evaluation = vm.run_statement(statement, collect_garbage)?; + evaluation = self.run_statement(statement, collect_garbage)?; if let Evaluation::Break(_) = evaluation { return Ok(evaluation); @@ -1040,7 +1028,11 @@ impl Vm { Ok(evaluation) } - } + }; + + self.context_stack.pop(); + + evaluation } fn run_if(