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)?;
}
self.identifier.validate(source, context)?;
if let Some(type_specification) = &self.type_specification {
match self.operator {
AssignmentOperator::Equal => {

View File

@ -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<Value, RuntimeError> {
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() {

View File

@ -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<Option<(ValueData, UsageCounter)>, 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<bool, RwLockError> {
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<Option<Value>, 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,

View File

@ -49,11 +49,6 @@ pub fn interpret(source: &str) -> Result<Value, Error> {
/// 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.
pub fn interpret_with_context(source: &str, context: Context) -> Result<Value, Error> {
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<Root, Error> {
let syntax_tree = self.parse(source)?;
let abstract_tree = Root::from_syntax(syntax_tree.root_node(), source, &self.context)?;