1
0

Implement automatic value dropping

This commit is contained in:
Jeff 2024-02-17 22:02:15 -05:00
parent d05b5a8628
commit 4f5ad1e4aa
5 changed files with 188 additions and 121 deletions

View File

@ -7,8 +7,9 @@ use serde::{de::Visitor, Deserialize, Serialize};
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,
AbstractTree, Context, Format, SyntaxNode, Type, Value, ValueData,
};
/// A string by which a variable is known to a context.
@ -60,9 +61,17 @@ impl AbstractTree for Identifier {
}
fn validate(&self, _source: &str, context: &Context) -> Result<(), ValidationError> {
if let Some(_) = context.get_type(self)? {
if let Some((_, counter)) = context.get_data_and_counter(self)? {
counter.add_allowance()?;
Ok(())
} else {
for built_in_value in all_built_in_values() {
if built_in_value.name() == self.inner().as_ref() {
return Ok(());
}
}
Err(ValidationError::VariableIdentifierNotFound(self.clone()))
}
}
@ -71,18 +80,38 @@ impl AbstractTree for Identifier {
if let Some(r#type) = context.get_type(self)? {
Ok(r#type)
} else {
for built_in_value in all_built_in_values() {
if built_in_value.name() == self.inner().as_ref() {
return Ok(built_in_value.get().r#type()?);
}
}
Err(ValidationError::VariableIdentifierNotFound(self.clone()))
}
}
fn run(&self, _source: &str, context: &Context) -> Result<Value, RuntimeError> {
if let Some(value) = context.get_value(self)? {
Ok(value.clone())
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());
}
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::VariableIdentifierNotFound(self.clone()),
));
for built_in_value in all_built_in_values() {
if built_in_value.name() == self.inner().as_ref() {
return Ok(built_in_value.get().clone());
}
}
}
Err(RuntimeError::ValidationFailure(
ValidationError::VariableIdentifierNotFound(self.clone()),
))
}
}

View File

@ -27,6 +27,12 @@
//! 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.
mod usage_counter;
mod value_data;
pub use usage_counter::UsageCounter;
pub use value_data::ValueData;
use std::{
cmp::Ordering,
collections::BTreeMap,
@ -45,7 +51,7 @@ use crate::{
/// See the [module-level docs][self] for more info.
#[derive(Clone, Debug)]
pub struct Context {
inner: Arc<RwLock<BTreeMap<Identifier, ValueData>>>,
inner: Arc<RwLock<BTreeMap<Identifier, (ValueData, UsageCounter)>>>,
}
impl Context {
@ -57,7 +63,9 @@ impl Context {
}
/// Return a lock guard to the inner BTreeMap.
pub fn inner(&self) -> Result<RwLockReadGuard<BTreeMap<Identifier, ValueData>>, RwLockError> {
pub fn inner(
&self,
) -> Result<RwLockReadGuard<BTreeMap<Identifier, (ValueData, UsageCounter)>>, RwLockError> {
Ok(self.inner.read()?)
}
@ -96,21 +104,30 @@ impl Context {
pub fn inherit_from(&self, other: &Context) -> Result<(), RwLockError> {
let mut self_variables = self.inner.write()?;
for (identifier, value_data) in other.inner.read()?.iter() {
if let ValueData::Value { inner, .. } = value_data {
if inner.is_function() {
self_variables.insert(identifier.clone(), value_data.clone());
for (identifier, (value_data, _counter)) in other.inner.read()?.iter() {
if let ValueData::Value(value) = value_data {
if value.is_function() {
self_variables.insert(
identifier.clone(),
(value_data.clone(), UsageCounter::new()),
);
}
}
if let ValueData::TypeHint { inner } = value_data {
if inner.is_function() {
self_variables.insert(identifier.clone(), value_data.clone());
if let ValueData::TypeHint(r#type) = value_data {
if r#type.is_function() {
self_variables.insert(
identifier.clone(),
(value_data.clone(), UsageCounter::new()),
);
}
}
if let ValueData::TypeDefinition(_) = value_data {
self_variables.insert(identifier.clone(), value_data.clone());
self_variables.insert(
identifier.clone(),
(value_data.clone(), UsageCounter::new()),
);
}
}
@ -139,21 +156,33 @@ impl Context {
pub fn inherit_all_from(&self, other: &Context) -> Result<(), RwLockError> {
let mut self_variables = self.inner.write()?;
for (identifier, value_data) in other.inner.read()?.iter() {
self_variables.insert(identifier.clone(), value_data.clone());
for (identifier, (value_data, _counter)) in other.inner.read()?.iter() {
self_variables.insert(
identifier.clone(),
(value_data.clone(), UsageCounter::new()),
);
}
Ok(())
}
/// Get a value from the context.
///
/// This will also return a built-in value if one matches the key. See the
/// [module-level docs][self] for more info.
/// 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())));
}
Ok(None)
}
/// Get a [Value] from the context.
pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockError> {
if let Some(value_data) = self.inner.read()?.get(identifier) {
if let ValueData::Value { inner, .. } = value_data {
return Ok(Some(inner.clone()));
if let Some((value_data, _counter)) = self.inner.read()?.get(identifier) {
if let ValueData::Value(value) = value_data {
return Ok(Some(value.clone()));
}
}
@ -166,15 +195,15 @@ impl Context {
Ok(None)
}
/// Get a type from the context.
/// Get a [Type] from the context.
///
/// If the key matches a stored value, its type will be returned. It if
/// If the key matches a stored [Value], its type will be returned. It if
/// matches a type hint, the type hint will be returned.
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockError> {
if let Some(value_data) = self.inner.read()?.get(identifier) {
if let Some((value_data, _counter)) = self.inner.read()?.get(identifier) {
match value_data {
ValueData::Value { inner, .. } => return Ok(Some(inner.r#type()?)),
ValueData::TypeHint { inner, .. } => return Ok(Some(inner.clone())),
ValueData::Value(value) => return Ok(Some(value.r#type()?)),
ValueData::TypeHint(r#type) => return Ok(Some(r#type.clone())),
ValueData::TypeDefinition(_) => todo!(),
}
}
@ -188,7 +217,7 @@ impl Context {
Ok(None)
}
/// Get a type definition from the context.
/// Get a [TypeDefinition] from the context.
///
/// This will also return a built-in type definition if one matches the key.
/// See the [module-level docs][self] for more info.
@ -196,7 +225,7 @@ impl Context {
&self,
identifier: &Identifier,
) -> Result<Option<TypeDefinition>, RwLockError> {
if let Some(value_data) = self.inner.read()?.get(identifier) {
if let Some((value_data, _counter)) = self.inner.read()?.get(identifier) {
if let ValueData::TypeDefinition(definition) = value_data {
return Ok(Some(definition.clone()));
}
@ -215,13 +244,14 @@ impl Context {
pub fn set_value(&self, key: Identifier, value: Value) -> Result<(), RwLockError> {
log::info!("Setting value: {key} = {value}");
self.inner.write()?.insert(
key,
ValueData::Value {
inner: value,
runtime_uses: Arc::new(RwLock::new(0)),
},
);
let mut map = self.inner.write()?;
let old_data = map.remove(&key);
if let Some((_, old_counter)) = old_data {
map.insert(key, (ValueData::Value(value), old_counter.clone()));
} else {
map.insert(key, (ValueData::Value(value), UsageCounter::new()));
}
Ok(())
}
@ -235,7 +265,7 @@ impl Context {
self.inner
.write()?
.insert(key, ValueData::TypeHint { inner: r#type });
.insert(key, (ValueData::TypeHint(r#type), UsageCounter::new()));
Ok(())
}
@ -249,15 +279,18 @@ impl Context {
key: Identifier,
definition: TypeDefinition,
) -> Result<(), RwLockError> {
self.inner
.write()?
.insert(key, ValueData::TypeDefinition(definition));
self.inner.write()?.insert(
key,
(ValueData::TypeDefinition(definition), UsageCounter::new()),
);
Ok(())
}
/// Remove a key-value pair.
pub fn unset(&self, key: &Identifier) -> Result<(), RwLockError> {
log::debug!("Dropping variable {key}.");
self.inner.write()?.remove(key);
Ok(())
@ -294,7 +327,7 @@ impl PartialEq for Context {
}
impl PartialOrd for Context {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
@ -308,79 +341,6 @@ impl Ord for Context {
}
}
#[derive(Clone, Debug)]
pub enum ValueData {
Value {
inner: Value,
runtime_uses: Arc<RwLock<u16>>,
},
TypeHint {
inner: Type,
},
TypeDefinition(TypeDefinition),
}
impl Eq for ValueData {}
impl PartialEq for ValueData {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
ValueData::Value {
inner: left_inner,
runtime_uses: left_runtime_uses,
},
ValueData::Value {
inner: right_inner,
runtime_uses: right_runtime_uses,
},
) => {
if left_inner != right_inner {
return false;
} else {
*left_runtime_uses.read().unwrap() == *right_runtime_uses.read().unwrap()
}
}
(
ValueData::TypeHint { inner: left_inner },
ValueData::TypeHint { inner: right_inner },
) => left_inner == right_inner,
_ => false,
}
}
}
impl PartialOrd for ValueData {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ValueData {
fn cmp(&self, other: &Self) -> Ordering {
use Ordering::*;
match (self, other) {
(
ValueData::Value {
inner: inner_left, ..
},
ValueData::Value {
inner: inner_right, ..
},
) => inner_left.cmp(inner_right),
(ValueData::Value { .. }, _) => Greater,
(
ValueData::TypeHint { inner: inner_left },
ValueData::TypeHint { inner: inner_right },
) => inner_left.cmp(inner_right),
(ValueData::TypeDefinition(left), ValueData::TypeDefinition(right)) => left.cmp(right),
(ValueData::TypeDefinition(_), _) => Greater,
(ValueData::TypeHint { .. }, _) => Less,
}
}
}
impl Display for Context {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{{")?;

View File

@ -0,0 +1,70 @@
use std::{
cmp::Ordering,
sync::{Arc, RwLock},
};
use crate::error::rw_lock_error::RwLockError;
#[derive(Clone, Debug)]
pub struct UsageCounter(Arc<RwLock<UsageCounterInner>>);
impl UsageCounter {
pub fn new() -> UsageCounter {
UsageCounter(Arc::new(RwLock::new(UsageCounterInner {
allowances: 0,
runtime_uses: 0,
})))
}
pub fn allowances(&self) -> Result<usize, RwLockError> {
Ok(self.0.read()?.allowances)
}
pub fn runtime_uses(&self) -> Result<usize, RwLockError> {
Ok(self.0.read()?.runtime_uses)
}
pub fn add_allowance(&self) -> Result<(), RwLockError> {
self.0.write()?.allowances += 1;
Ok(())
}
pub fn add_runtime_use(&self) -> Result<(), RwLockError> {
self.0.write()?.runtime_uses += 1;
Ok(())
}
}
impl Eq for UsageCounter {}
impl PartialEq for UsageCounter {
fn eq(&self, other: &Self) -> bool {
let left = self.0.read().unwrap();
let right = other.0.read().unwrap();
*left == *right
}
}
impl PartialOrd for UsageCounter {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for UsageCounter {
fn cmp(&self, other: &Self) -> Ordering {
let left = self.0.read().unwrap();
let right = other.0.read().unwrap();
left.cmp(&right)
}
}
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
struct UsageCounterInner {
pub allowances: usize,
pub runtime_uses: usize,
}

View File

@ -0,0 +1,8 @@
use crate::{Type, TypeDefinition, Value};
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum ValueData {
Value(Value),
TypeHint(Type),
TypeDefinition(TypeDefinition),
}

View File

@ -308,10 +308,10 @@ impl Completer for DustCompleter {
}
}
for (key, value_data) in self.context.inner().unwrap().iter() {
for (key, (value_data, _counter)) in self.context.inner().unwrap().iter() {
let value = match value_data {
ValueData::Value { inner, .. } => inner,
ValueData::TypeHint { .. } => continue,
ValueData::Value(value) => value,
ValueData::TypeHint(_) => continue,
ValueData::TypeDefinition(_) => continue,
};