1
0

Improve context API

This commit is contained in:
Jeff 2024-02-17 23:43:00 -05:00
parent 4f5ad1e4aa
commit 6c699ec900
4 changed files with 53 additions and 46 deletions

View File

@ -63,6 +63,8 @@ impl AbstractTree for Assignment {
context.set_type(self.identifier.clone(), r#type)?; context.set_type(self.identifier.clone(), r#type)?;
} }
self.identifier.validate(source, context)?;
if let Some(type_specification) = &self.type_specification { if let Some(type_specification) = &self.type_specification {
match self.operator { match self.operator {
AssignmentOperator::Equal => { AssignmentOperator::Equal => {

View File

@ -9,7 +9,7 @@ use crate::{
built_in_identifiers::all_built_in_identifiers, built_in_identifiers::all_built_in_identifiers,
built_in_values::all_built_in_values, built_in_values::all_built_in_values,
error::{RuntimeError, SyntaxError, ValidationError}, 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. /// 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> { fn validate(&self, _source: &str, context: &Context) -> Result<(), ValidationError> {
if let Some((_, counter)) = context.get_data_and_counter(self)? { let variable_exists = context.add_allowance(self)?;
counter.add_allowance()?;
if variable_exists {
Ok(()) Ok(())
} else { } else {
for built_in_value in all_built_in_values() { 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<Value, RuntimeError> { fn run(&self, _source: &str, context: &Context) -> Result<Value, RuntimeError> {
if let Some((value_data, counter)) = context.get_data_and_counter(self)? { if let Some(value) = context.get_value(self)? {
if let ValueData::Value(value) = value_data { return Ok(value);
counter.add_runtime_use()?;
if counter.runtime_uses() == counter.allowances() {
context.unset(self)?;
}
return Ok(value.clone());
}
} else { } else {
for built_in_value in all_built_in_values() { for built_in_value in all_built_in_values() {
if built_in_value.name() == self.inner().as_ref() { if built_in_value.name() == self.inner().as_ref() {

View File

@ -1,5 +1,6 @@
//! An execution context that stores variables and type data during the //! A garbage-collecting execution context that stores variables and type data
//! [Interpreter][crate::Interpreter]'s abstraction and execution process. //! during the [Interpreter][crate::Interpreter]'s abstraction and execution
//! process.
//! //!
//! ## Setting values //! ## Setting values
//! //!
@ -27,6 +28,15 @@
//! has been explicitly set. If nothing is found, it will then check the built- //! 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 //! in values and type definitions for a match. This means that the user can
//! override the built-ins. //! 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 usage_counter;
mod value_data; mod value_data;
@ -45,8 +55,8 @@ use crate::{
error::rw_lock_error::RwLockError, Identifier, Type, TypeDefinition, Value, error::rw_lock_error::RwLockError, Identifier, Type, TypeDefinition, Value,
}; };
/// An execution context that variable and type data during the [Interpreter]'s /// An execution context stores that variable and type data during the
/// abstraction and execution process. /// [Interpreter]'s abstraction and execution process.
/// ///
/// See the [module-level docs][self] for more info. /// See the [module-level docs][self] for more info.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -166,33 +176,42 @@ impl Context {
Ok(()) Ok(())
} }
/// Get a [Value] and its [UsageCounter] from the context. /// Increment the number of allowances a variable has. Return a boolean
pub fn get_data_and_counter( /// representing whether or not the variable was found.
&self, pub fn add_allowance(&self, identifier: &Identifier) -> Result<bool, RwLockError> {
identifier: &Identifier, if let Some((_value_data, counter)) = self.inner.read()?.get(identifier) {
) -> Result<Option<(ValueData, UsageCounter)>, RwLockError> { counter.add_allowance()?;
if let Some((value_data, counter)) = self.inner.read()?.get(identifier) {
return Ok(Some((value_data.clone(), counter.clone())));
}
Ok(None) Ok(true)
} else {
Ok(false)
}
} }
/// Get a [Value] from the context. /// Get a [Value] from the context.
pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockError> { pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockError> {
if let Some((value_data, _counter)) = self.inner.read()?.get(identifier) { let (value, counter) =
if let Some((value_data, counter)) = self.inner.read()?.get(identifier) {
if let ValueData::Value(value) = value_data { if let ValueData::Value(value) = value_data {
return Ok(Some(value.clone())); (value.clone(), counter.clone())
} else {
return Ok(None);
} }
} } else {
for built_in_value in all_built_in_values() { for built_in_value in all_built_in_values() {
if built_in_value.name() == identifier.inner().as_ref() { if built_in_value.name() == identifier.inner().as_ref() {
return Ok(Some(built_in_value.get().clone())); return Ok(Some(built_in_value.get().clone()));
} }
} }
Ok(None) return Ok(None);
};
if counter.allowances() == counter.runtime_uses() {
self.unset(identifier)?;
}
Ok(Some(value))
} }
/// Get a [Type] from the context. /// Get a [Type] from the context.
@ -273,7 +292,7 @@ impl Context {
/// Set a type definition. /// Set a type definition.
/// ///
/// This allows defined types (i.e. structs and enums) to be instantiated /// 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( pub fn set_definition(
&self, &self,
key: Identifier, key: Identifier,

View File

@ -49,11 +49,6 @@ pub fn interpret(source: &str) -> Result<Value, Error> {
/// Interpret the given source code with the given context. /// 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 `<map>` type. Any value can be set, including functions and nested
/// maps.
///
/// See the [module-level docs][self] for more info. /// See the [module-level docs][self] for more info.
pub fn interpret_with_context(source: &str, context: Context) -> Result<Value, Error> { pub fn interpret_with_context(source: &str, context: Context) -> Result<Value, Error> {
let mut interpreter = Interpreter::new(context); let mut interpreter = Interpreter::new(context);
@ -93,7 +88,7 @@ impl Interpreter {
.expect("Language version is incompatible with tree sitter version."); .expect("Language version is incompatible with tree sitter version.");
parser.set_logger(Some(Box::new(|_log_type, message| { parser.set_logger(Some(Box::new(|_log_type, message| {
log::info!("{}", message) log::debug!("{}", message)
}))); })));
Interpreter { parser, context } Interpreter { parser, context }
@ -118,9 +113,8 @@ impl Interpreter {
/// The order in which this function works is: /// The order in which this function works is:
/// ///
/// - parse the source into a syntax tree /// - parse the source into a syntax tree
/// - check the syntax tree for errors
/// - generate an abstract tree from the source and syntax tree /// - 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<Root, Error> { pub fn validate(&mut self, source: &str) -> Result<Root, Error> {
let syntax_tree = self.parse(source)?; let syntax_tree = self.parse(source)?;
let abstract_tree = Root::from_syntax(syntax_tree.root_node(), source, &self.context)?; let abstract_tree = Root::from_syntax(syntax_tree.root_node(), source, &self.context)?;