Improve context API
This commit is contained in:
parent
4f5ad1e4aa
commit
6c699ec900
@ -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 => {
|
||||
|
@ -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() {
|
||||
|
@ -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,
|
||||
|
@ -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)?;
|
||||
|
Loading…
Reference in New Issue
Block a user