Refactor context so that parents are a Weak reference
This commit is contained in:
parent
0aebd81665
commit
d6ab891d7f
@ -2,7 +2,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
|
sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Constructor, Identifier, StructType, Type, Value};
|
use crate::{Constructor, Identifier, StructType, Type, Value};
|
||||||
@ -12,8 +12,7 @@ pub type Associations = HashMap<Identifier, (ContextData, usize)>;
|
|||||||
/// Garbage-collecting context for variables.
|
/// Garbage-collecting context for variables.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
associations: Arc<RwLock<Associations>>,
|
inner: Arc<ContextInner>,
|
||||||
parent: Option<Box<Context>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
@ -23,33 +22,183 @@ impl Context {
|
|||||||
|
|
||||||
pub fn with_data(data: Associations) -> Self {
|
pub fn with_data(data: Associations) -> Self {
|
||||||
Self {
|
Self {
|
||||||
associations: Arc::new(RwLock::new(data)),
|
inner: Arc::new(ContextInner {
|
||||||
|
associations: RwLock::new(data),
|
||||||
parent: None,
|
parent: None,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a deep copy of another context.
|
/// Creates a deep copy of another context.
|
||||||
pub fn with_data_from(other: &Self) -> Result<Self, ContextError> {
|
pub fn with_data_from(other: &Self) -> Result<Self, ContextError> {
|
||||||
Ok(Self::with_data(other.associations.read()?.clone()))
|
let mut associations = HashMap::new();
|
||||||
|
|
||||||
|
for (identifier, (context_data, position)) in other.inner.associations.read()?.iter() {
|
||||||
|
associations.insert(identifier.clone(), (context_data.clone(), *position));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self::with_data(associations))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_child(&self) -> Self {
|
pub fn create_child(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
associations: Arc::new(RwLock::new(HashMap::new())),
|
inner: Arc::new(ContextInner {
|
||||||
parent: Some(Box::new(self.clone())),
|
associations: RwLock::new(HashMap::new()),
|
||||||
|
parent: Some(Arc::downgrade(&self.inner)),
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of associated identifiers in the context.
|
/// Returns the number of associated identifiers in the context.
|
||||||
pub fn association_count(&self) -> Result<usize, ContextError> {
|
pub fn association_count(&self) -> Result<usize, ContextError> {
|
||||||
let own_count = self.associations.read()?.len();
|
self.inner.association_count()
|
||||||
let ancestor_count = if let Some(parent) = &self.parent {
|
}
|
||||||
parent.association_count()?
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(own_count + ancestor_count)
|
/// Returns a boolean indicating whether the identifier is in the context.
|
||||||
|
pub fn contains(&self, identifier: &Identifier) -> Result<bool, ContextError> {
|
||||||
|
self.inner.contains(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the full ContextData and Span if the context contains the given identifier.
|
||||||
|
pub fn get(
|
||||||
|
&self,
|
||||||
|
identifier: &Identifier,
|
||||||
|
) -> Result<Option<(ContextData, usize)>, ContextError> {
|
||||||
|
self.inner.get(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the type associated with the given identifier.
|
||||||
|
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
|
||||||
|
self.inner.get_type(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the ContextData associated with the identifier.
|
||||||
|
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
|
||||||
|
self.inner.get_data(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the value associated with the identifier.
|
||||||
|
pub fn get_variable_value(
|
||||||
|
&self,
|
||||||
|
identifier: &Identifier,
|
||||||
|
) -> Result<Option<Value>, ContextError> {
|
||||||
|
self.inner.get_variable_value(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the constructor associated with the identifier.
|
||||||
|
pub fn get_constructor(
|
||||||
|
&self,
|
||||||
|
identifier: &Identifier,
|
||||||
|
) -> Result<Option<Constructor>, ContextError> {
|
||||||
|
self.inner.get_constructor(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the constructor type associated with the identifier.
|
||||||
|
pub fn get_constructor_type(
|
||||||
|
&self,
|
||||||
|
identifier: &Identifier,
|
||||||
|
) -> Result<Option<StructType>, ContextError> {
|
||||||
|
self.inner.get_constructor_type(identifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates an identifier with a variable type, with a position given for garbage collection.
|
||||||
|
pub fn set_variable_type(
|
||||||
|
&self,
|
||||||
|
identifier: Identifier,
|
||||||
|
r#type: Type,
|
||||||
|
) -> Result<(), ContextError> {
|
||||||
|
self.inner.set_variable_type(identifier, r#type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates an identifier with a variable value.
|
||||||
|
pub fn set_variable_value(
|
||||||
|
&self,
|
||||||
|
identifier: Identifier,
|
||||||
|
value: Value,
|
||||||
|
) -> Result<(), ContextError> {
|
||||||
|
self.inner.set_variable_value(identifier, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates an identifier with a constructor.
|
||||||
|
pub fn set_constructor(
|
||||||
|
&self,
|
||||||
|
identifier: Identifier,
|
||||||
|
constructor: Constructor,
|
||||||
|
) -> Result<(), ContextError> {
|
||||||
|
self.inner.set_constructor(identifier, constructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Associates an identifier with a constructor type, with a position given for garbage
|
||||||
|
/// collection.
|
||||||
|
pub fn set_constructor_type(
|
||||||
|
&self,
|
||||||
|
identifier: Identifier,
|
||||||
|
struct_type: StructType,
|
||||||
|
) -> Result<(), ContextError> {
|
||||||
|
self.inner.set_constructor_type(identifier, struct_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collects garbage up to the given position, removing all variables with lesser positions.
|
||||||
|
pub fn collect_garbage(&self, position: usize) -> Result<(), ContextError> {
|
||||||
|
self.inner.collect_garbage(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates an associated identifier's last known position, allowing it to live longer in the
|
||||||
|
/// program. Returns a boolean indicating whether the identifier was found. If the identifier is
|
||||||
|
/// not found in the current context, the parent context is searched but parent context's
|
||||||
|
/// position is not updated.
|
||||||
|
pub fn update_last_position(
|
||||||
|
&self,
|
||||||
|
identifier: &Identifier,
|
||||||
|
position: usize,
|
||||||
|
) -> Result<bool, ContextError> {
|
||||||
|
self.inner.update_last_position(identifier, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recovers the context from a poisoned state by recovering data from an error.
|
||||||
|
///
|
||||||
|
/// This method is not used.
|
||||||
|
pub fn _recover_from_poison(&mut self, error: &ContextError) {
|
||||||
|
log::debug!("Context is recovering from poison error");
|
||||||
|
|
||||||
|
let ContextError::PoisonErrorRecovered(recovered) = error;
|
||||||
|
|
||||||
|
let mut new_associations = HashMap::new();
|
||||||
|
|
||||||
|
for (identifier, (context_data, position)) in recovered.as_ref() {
|
||||||
|
new_associations.insert(identifier.clone(), (context_data.clone(), *position));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner = Arc::new(ContextInner {
|
||||||
|
associations: RwLock::new(new_associations),
|
||||||
|
parent: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Context {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ContextInner {
|
||||||
|
pub associations: RwLock<Associations>,
|
||||||
|
pub parent: Option<Weak<ContextInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextInner {
|
||||||
|
pub fn new(associations: RwLock<Associations>, parent: Option<Weak<ContextInner>>) -> Self {
|
||||||
|
Self {
|
||||||
|
associations,
|
||||||
|
parent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of associated identifiers in the context.
|
||||||
|
pub fn association_count(&self) -> Result<usize, ContextError> {
|
||||||
|
Ok(self.associations.read()?.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a boolean indicating whether the identifier is in the context.
|
/// Returns a boolean indicating whether the identifier is in the context.
|
||||||
@ -57,10 +206,14 @@ impl Context {
|
|||||||
if self.associations.read()?.contains_key(identifier) {
|
if self.associations.read()?.contains_key(identifier) {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
|
if let Some(parent) = parent.upgrade() {
|
||||||
parent.contains(identifier)
|
parent.contains(identifier)
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the full ContextData and Span if the context contains the given identifier.
|
/// Returns the full ContextData and Span if the context contains the given identifier.
|
||||||
@ -69,14 +222,16 @@ impl Context {
|
|||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
) -> Result<Option<(ContextData, usize)>, ContextError> {
|
) -> Result<Option<(ContextData, usize)>, ContextError> {
|
||||||
if let Some((variable_data, position)) = self.associations.read()?.get(identifier) {
|
if let Some((variable_data, position)) = self.associations.read()?.get(identifier) {
|
||||||
Ok(Some((variable_data.clone(), *position)))
|
return Ok(Some((variable_data.clone(), *position)));
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
parent.get(identifier)
|
if let Some(parent) = parent.upgrade() {
|
||||||
} else {
|
return parent.get(identifier);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the type associated with the given identifier.
|
/// Returns the type associated with the given identifier.
|
||||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
|
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ContextError> {
|
||||||
match self.associations.read()?.get(identifier) {
|
match self.associations.read()?.get(identifier) {
|
||||||
@ -89,23 +244,27 @@ impl Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent) = &self.parent {
|
if let Some(parent) = &self.parent {
|
||||||
parent.get_type(identifier)
|
if let Some(parent) = parent.upgrade() {
|
||||||
} else {
|
return parent.get_type(identifier);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the ContextData associated with the identifier.
|
/// Returns the ContextData associated with the identifier.
|
||||||
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
|
pub fn get_data(&self, identifier: &Identifier) -> Result<Option<ContextData>, ContextError> {
|
||||||
if let Some((variable_data, _)) = self.associations.read()?.get(identifier) {
|
if let Some((variable_data, _)) = self.associations.read()?.get(identifier) {
|
||||||
Ok(Some(variable_data.clone()))
|
return Ok(Some(variable_data.clone()));
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
parent.get_data(identifier)
|
if let Some(parent) = parent.upgrade() {
|
||||||
} else {
|
return parent.get_data(identifier);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the value associated with the identifier.
|
/// Returns the value associated with the identifier.
|
||||||
pub fn get_variable_value(
|
pub fn get_variable_value(
|
||||||
&self,
|
&self,
|
||||||
@ -114,14 +273,16 @@ impl Context {
|
|||||||
if let Some((ContextData::VariableValue(value), _)) =
|
if let Some((ContextData::VariableValue(value), _)) =
|
||||||
self.associations.read()?.get(identifier)
|
self.associations.read()?.get(identifier)
|
||||||
{
|
{
|
||||||
Ok(Some(value.clone()))
|
return Ok(Some(value.clone()));
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
parent.get_variable_value(identifier)
|
if let Some(parent) = parent.upgrade() {
|
||||||
} else {
|
return parent.get_variable_value(identifier);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the constructor associated with the identifier.
|
/// Returns the constructor associated with the identifier.
|
||||||
pub fn get_constructor(
|
pub fn get_constructor(
|
||||||
&self,
|
&self,
|
||||||
@ -130,14 +291,16 @@ impl Context {
|
|||||||
if let Some((ContextData::Constructor(constructor), _)) =
|
if let Some((ContextData::Constructor(constructor), _)) =
|
||||||
self.associations.read()?.get(identifier)
|
self.associations.read()?.get(identifier)
|
||||||
{
|
{
|
||||||
Ok(Some(constructor.clone()))
|
return Ok(Some(constructor.clone()));
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
parent.get_constructor(identifier)
|
if let Some(parent) = parent.upgrade() {
|
||||||
} else {
|
return parent.get_constructor(identifier);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the constructor type associated with the identifier.
|
/// Returns the constructor type associated with the identifier.
|
||||||
pub fn get_constructor_type(
|
pub fn get_constructor_type(
|
||||||
&self,
|
&self,
|
||||||
@ -146,18 +309,20 @@ impl Context {
|
|||||||
let read_associations = self.associations.read()?;
|
let read_associations = self.associations.read()?;
|
||||||
|
|
||||||
if let Some((context_data, _)) = read_associations.get(identifier) {
|
if let Some((context_data, _)) = read_associations.get(identifier) {
|
||||||
match context_data {
|
return match context_data {
|
||||||
ContextData::Constructor(constructor) => Ok(Some(constructor.struct_type.clone())),
|
ContextData::Constructor(constructor) => Ok(Some(constructor.struct_type.clone())),
|
||||||
ContextData::ConstructorType(struct_type) => Ok(Some(struct_type.clone())),
|
ContextData::ConstructorType(struct_type) => Ok(Some(struct_type.clone())),
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
}
|
};
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
parent.get_constructor_type(identifier)
|
if let Some(parent) = parent.upgrade() {
|
||||||
} else {
|
return parent.get_constructor_type(identifier);
|
||||||
Ok(None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
/// Associates an identifier with a variable type, with a position given for garbage collection.
|
/// Associates an identifier with a variable type, with a position given for garbage collection.
|
||||||
pub fn set_variable_type(
|
pub fn set_variable_type(
|
||||||
&self,
|
&self,
|
||||||
@ -279,8 +444,9 @@ impl Context {
|
|||||||
let found = self.update_position_if_found(identifier, position)?;
|
let found = self.update_position_if_found(identifier, position)?;
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
Ok(true)
|
return Ok(true);
|
||||||
} else if let Some(parent) = &self.parent {
|
} else if let Some(parent) = &self.parent {
|
||||||
|
if let Some(parent) = parent.upgrade() {
|
||||||
let found_in_ancestor = parent.update_position_if_found(identifier, position)?;
|
let found_in_ancestor = parent.update_position_if_found(identifier, position)?;
|
||||||
|
|
||||||
if !found_in_ancestor {
|
if !found_in_ancestor {
|
||||||
@ -290,11 +456,10 @@ impl Context {
|
|||||||
|
|
||||||
associations.insert(identifier.clone(), (ContextData::Reserved, position));
|
associations.insert(identifier.clone(), (ContextData::Reserved, position));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_position_if_found(
|
fn update_position_if_found(
|
||||||
@ -314,29 +479,6 @@ impl Context {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recovers the context from a poisoned state by recovering data from an error.
|
|
||||||
///
|
|
||||||
/// This method is not used.
|
|
||||||
pub fn _recover_from_poison(&mut self, error: &ContextError) {
|
|
||||||
log::debug!("Context is recovering from poison error");
|
|
||||||
|
|
||||||
let ContextError::PoisonErrorRecovered(recovered) = error;
|
|
||||||
|
|
||||||
let mut new_associations = HashMap::new();
|
|
||||||
|
|
||||||
for (identifier, (context_data, position)) in recovered.as_ref() {
|
|
||||||
new_associations.insert(identifier.clone(), (context_data.clone(), *position));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.associations = Arc::new(RwLock::new(new_associations));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Context {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
Loading…
Reference in New Issue
Block a user