From 6c699ec900029f86ca1e2f3de95ef07b8da9f13d Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 17 Feb 2024 23:43:00 -0500 Subject: [PATCH] Improve context API --- src/abstract_tree/assignment.rs | 2 + src/abstract_tree/identifier.rs | 18 +++------ src/context/mod.rs | 69 +++++++++++++++++++++------------ src/interpret.rs | 10 +---- 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/src/abstract_tree/assignment.rs b/src/abstract_tree/assignment.rs index 3dc7830..d6fa062 100644 --- a/src/abstract_tree/assignment.rs +++ b/src/abstract_tree/assignment.rs @@ -63,6 +63,8 @@ impl AbstractTree for Assignment { context.set_type(self.identifier.clone(), r#type)?; } + self.identifier.validate(source, context)?; + if let Some(type_specification) = &self.type_specification { match self.operator { AssignmentOperator::Equal => { diff --git a/src/abstract_tree/identifier.rs b/src/abstract_tree/identifier.rs index 37bda95..1215ca6 100644 --- a/src/abstract_tree/identifier.rs +++ b/src/abstract_tree/identifier.rs @@ -9,7 +9,7 @@ use crate::{ built_in_identifiers::all_built_in_identifiers, built_in_values::all_built_in_values, error::{RuntimeError, SyntaxError, ValidationError}, - AbstractTree, Context, Format, SyntaxNode, Type, Value, ValueData, + AbstractTree, Context, Format, SyntaxNode, Type, Value, }; /// A string by which a variable is known to a context. @@ -61,9 +61,9 @@ impl AbstractTree for Identifier { } fn validate(&self, _source: &str, context: &Context) -> Result<(), ValidationError> { - if let Some((_, counter)) = context.get_data_and_counter(self)? { - counter.add_allowance()?; + let variable_exists = context.add_allowance(self)?; + if variable_exists { Ok(()) } else { for built_in_value in all_built_in_values() { @@ -91,16 +91,8 @@ impl AbstractTree for Identifier { } fn run(&self, _source: &str, context: &Context) -> Result { - if let Some((value_data, counter)) = context.get_data_and_counter(self)? { - if let ValueData::Value(value) = value_data { - counter.add_runtime_use()?; - - if counter.runtime_uses() == counter.allowances() { - context.unset(self)?; - } - - return Ok(value.clone()); - } + if let Some(value) = context.get_value(self)? { + return Ok(value); } else { for built_in_value in all_built_in_values() { if built_in_value.name() == self.inner().as_ref() { diff --git a/src/context/mod.rs b/src/context/mod.rs index eb9ffee..11a92b4 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -1,5 +1,6 @@ -//! An execution context that stores variables and type data during the -//! [Interpreter][crate::Interpreter]'s abstraction and execution process. +//! A garbage-collecting execution context that stores variables and type data +//! during the [Interpreter][crate::Interpreter]'s abstraction and execution +//! process. //! //! ## Setting values //! @@ -27,6 +28,15 @@ //! has been explicitly set. If nothing is found, it will then check the built- //! in values and type definitions for a match. This means that the user can //! override the built-ins. +//! +//! ## Garbage Collection +//! +//! Every item stored in a Context has a counter attached to it. You must use +//! [Context::add_allowance][] to let the Context know not to drop the value. +//! Every time you use [Context::get_value][] it checks the number of times it +//! has been used and compares it to the number of allowances. If the limit +//! has been reached, the value will be removed from the context and can no +//! longer be found. mod usage_counter; mod value_data; @@ -45,8 +55,8 @@ use crate::{ error::rw_lock_error::RwLockError, Identifier, Type, TypeDefinition, Value, }; -/// An execution context that variable and type data during the [Interpreter]'s -/// abstraction and execution process. +/// An execution context stores that variable and type data during the +/// [Interpreter]'s abstraction and execution process. /// /// See the [module-level docs][self] for more info. #[derive(Clone, Debug)] @@ -166,33 +176,42 @@ impl Context { Ok(()) } - /// Get a [Value] and its [UsageCounter] from the context. - pub fn get_data_and_counter( - &self, - identifier: &Identifier, - ) -> Result, RwLockError> { - if let Some((value_data, counter)) = self.inner.read()?.get(identifier) { - return Ok(Some((value_data.clone(), counter.clone()))); - } + /// Increment the number of allowances a variable has. Return a boolean + /// representing whether or not the variable was found. + pub fn add_allowance(&self, identifier: &Identifier) -> Result { + if let Some((_value_data, counter)) = self.inner.read()?.get(identifier) { + counter.add_allowance()?; - Ok(None) + Ok(true) + } else { + Ok(false) + } } /// Get a [Value] from the context. pub fn get_value(&self, identifier: &Identifier) -> Result, RwLockError> { - if let Some((value_data, _counter)) = self.inner.read()?.get(identifier) { - if let ValueData::Value(value) = value_data { - return Ok(Some(value.clone())); - } + let (value, counter) = + if let Some((value_data, counter)) = self.inner.read()?.get(identifier) { + if let ValueData::Value(value) = value_data { + (value.clone(), counter.clone()) + } else { + return Ok(None); + } + } else { + for built_in_value in all_built_in_values() { + if built_in_value.name() == identifier.inner().as_ref() { + return Ok(Some(built_in_value.get().clone())); + } + } + + return Ok(None); + }; + + if counter.allowances() == counter.runtime_uses() { + self.unset(identifier)?; } - for built_in_value in all_built_in_values() { - if built_in_value.name() == identifier.inner().as_ref() { - return Ok(Some(built_in_value.get().clone())); - } - } - - Ok(None) + Ok(Some(value)) } /// Get a [Type] from the context. @@ -273,7 +292,7 @@ impl Context { /// Set a type definition. /// /// This allows defined types (i.e. structs and enums) to be instantiated - /// later while using this context. + /// later while running the interpreter using this context. pub fn set_definition( &self, key: Identifier, diff --git a/src/interpret.rs b/src/interpret.rs index de75b3d..5c8b93c 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -49,11 +49,6 @@ pub fn interpret(source: &str) -> Result { /// Interpret the given source code with the given context. /// -/// A context is a [Map] instance, which is dust's -/// [BTreeMap][std::collections::btree_map::BTreeMap] that is used internally -/// for the `` type. Any value can be set, including functions and nested -/// maps. -/// /// See the [module-level docs][self] for more info. pub fn interpret_with_context(source: &str, context: Context) -> Result { let mut interpreter = Interpreter::new(context); @@ -93,7 +88,7 @@ impl Interpreter { .expect("Language version is incompatible with tree sitter version."); parser.set_logger(Some(Box::new(|_log_type, message| { - log::info!("{}", message) + log::debug!("{}", message) }))); Interpreter { parser, context } @@ -118,9 +113,8 @@ impl Interpreter { /// The order in which this function works is: /// /// - parse the source into a syntax tree - /// - check the syntax tree for errors /// - generate an abstract tree from the source and syntax tree - /// - check the abstract tree for type errors + /// - check the abstract tree for errors pub fn validate(&mut self, source: &str) -> Result { let syntax_tree = self.parse(source)?; let abstract_tree = Root::from_syntax(syntax_tree.root_node(), source, &self.context)?;