2024-08-10 01:12:36 +00:00
|
|
|
//! Garbage-collecting context for variables.
|
2024-08-10 00:52:13 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use crate::{Identifier, Type, Value};
|
|
|
|
|
2024-08-10 01:12:36 +00:00
|
|
|
/// Garbage-collecting context for variables.
|
|
|
|
#[derive(Debug, Clone)]
|
2024-08-10 00:52:13 +00:00
|
|
|
pub struct Context {
|
2024-08-12 02:41:40 +00:00
|
|
|
variables: HashMap<Identifier, (VariableData, UsageData)>,
|
|
|
|
is_garbage_collected: bool,
|
2024-08-10 00:52:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Context {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
|
|
|
variables: HashMap::new(),
|
2024-08-12 02:41:40 +00:00
|
|
|
is_garbage_collected: true,
|
2024-08-10 00:52:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn contains(&self, identifier: &Identifier) -> bool {
|
|
|
|
self.variables.contains_key(identifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get(&self, identifier: &Identifier) -> Option<&(VariableData, UsageData)> {
|
|
|
|
self.variables.get(identifier)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_type(&self, identifier: &Identifier) -> Option<&Type> {
|
|
|
|
match self.variables.get(identifier) {
|
|
|
|
Some((VariableData::Type(r#type), _)) => Some(r#type),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-12 02:41:40 +00:00
|
|
|
pub fn get_variable_data(&self, identifier: &Identifier) -> Option<&VariableData> {
|
|
|
|
match self.variables.get(identifier) {
|
|
|
|
Some((variable_data, _)) => Some(variable_data),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_value(&self, identifier: &Identifier) -> Option<&Value> {
|
|
|
|
match self.variables.get(identifier) {
|
|
|
|
Some((VariableData::Value(value), _)) => Some(value),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-10 08:45:30 +00:00
|
|
|
pub fn use_value(&mut self, identifier: &Identifier) -> Option<&Value> {
|
2024-08-12 02:41:40 +00:00
|
|
|
self.is_garbage_collected = false;
|
|
|
|
|
2024-08-10 08:45:30 +00:00
|
|
|
match self.variables.get_mut(identifier) {
|
|
|
|
Some((VariableData::Value(value), usage_data)) => {
|
|
|
|
usage_data.used += 1;
|
|
|
|
|
|
|
|
Some(value)
|
|
|
|
}
|
2024-08-10 00:52:13 +00:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_type(&mut self, identifier: Identifier, r#type: Type) {
|
2024-08-12 02:41:40 +00:00
|
|
|
self.is_garbage_collected = false;
|
|
|
|
|
2024-08-10 00:52:13 +00:00
|
|
|
self.variables.insert(
|
|
|
|
identifier,
|
|
|
|
(VariableData::Type(r#type), UsageData::default()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_value(&mut self, identifier: Identifier, value: Value) {
|
2024-08-12 02:41:40 +00:00
|
|
|
self.is_garbage_collected = false;
|
|
|
|
|
2024-08-10 00:52:13 +00:00
|
|
|
self.variables.insert(
|
|
|
|
identifier,
|
|
|
|
(VariableData::Value(value), UsageData::default()),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn collect_garbage(&mut self) {
|
2024-08-12 02:41:40 +00:00
|
|
|
if !self.is_garbage_collected {
|
|
|
|
self.variables
|
|
|
|
.retain(|_, (_, usage_data)| usage_data.used < usage_data.allowed_uses);
|
|
|
|
self.variables.shrink_to_fit();
|
|
|
|
}
|
2024-08-10 00:52:13 +00:00
|
|
|
}
|
2024-08-10 08:45:30 +00:00
|
|
|
|
|
|
|
pub fn add_allowed_use(&mut self, identifier: &Identifier) -> bool {
|
|
|
|
if let Some((_, usage_data)) = self.variables.get_mut(identifier) {
|
|
|
|
usage_data.allowed_uses += 1;
|
|
|
|
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2024-08-10 00:52:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Context {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-10 01:12:36 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2024-08-10 00:52:13 +00:00
|
|
|
pub enum VariableData {
|
|
|
|
Value(Value),
|
|
|
|
Type(Type),
|
|
|
|
}
|
|
|
|
|
2024-08-12 02:41:40 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2024-08-10 00:52:13 +00:00
|
|
|
pub struct UsageData {
|
|
|
|
pub allowed_uses: u16,
|
|
|
|
pub used: u16,
|
|
|
|
}
|
2024-08-12 02:41:40 +00:00
|
|
|
|
|
|
|
impl Default for UsageData {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
allowed_uses: 1,
|
|
|
|
used: 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::vm::run_with_context;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn context_removes_unused_variables() {
|
|
|
|
let source = "
|
|
|
|
x = 5
|
|
|
|
y = 10
|
|
|
|
z = x + y
|
|
|
|
";
|
|
|
|
let mut context = Context::new();
|
|
|
|
|
|
|
|
run_with_context(source, &mut context).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(context.variables.len(), 1);
|
|
|
|
}
|
2024-08-12 02:47:52 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn context_removes_used_variables() {
|
|
|
|
let source = "
|
|
|
|
x = 5
|
|
|
|
y = 10
|
|
|
|
z = x + y
|
|
|
|
z
|
|
|
|
";
|
|
|
|
let mut context = Context::new();
|
|
|
|
|
|
|
|
run_with_context(source, &mut context).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(context.variables.len(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn context_does_not_remove_variables_during_loop() {
|
|
|
|
let source = "
|
|
|
|
x = 5
|
|
|
|
y = 10
|
|
|
|
z = x + y
|
|
|
|
while z < 10 {
|
|
|
|
z = z + 1
|
|
|
|
}
|
|
|
|
";
|
|
|
|
let mut context = Context::new();
|
|
|
|
|
|
|
|
run_with_context(source, &mut context).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(context.variables.len(), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn context_removes_variables_after_loop() {
|
|
|
|
let source = "
|
|
|
|
x = 5
|
|
|
|
y = 10
|
|
|
|
z = x + y
|
|
|
|
while z < 10 {
|
|
|
|
z = z + 1
|
|
|
|
}
|
|
|
|
z
|
|
|
|
";
|
|
|
|
let mut context = Context::new();
|
|
|
|
|
|
|
|
run_with_context(source, &mut context).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(context.variables.len(), 0);
|
|
|
|
}
|
2024-08-12 02:41:40 +00:00
|
|
|
}
|