1
0

Test and implement basic garbage collection

This commit is contained in:
Jeff 2024-08-11 22:41:40 -04:00
parent 78228ce8d6
commit 2463e44301
2 changed files with 179 additions and 75 deletions

View File

@ -6,13 +6,15 @@ use crate::{Identifier, Type, Value};
/// Garbage-collecting context for variables. /// Garbage-collecting context for variables.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
pub variables: HashMap<Identifier, (VariableData, UsageData)>, variables: HashMap<Identifier, (VariableData, UsageData)>,
is_garbage_collected: bool,
} }
impl Context { impl Context {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
variables: HashMap::new(), variables: HashMap::new(),
is_garbage_collected: true,
} }
} }
@ -31,7 +33,23 @@ impl Context {
} }
} }
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,
}
}
pub fn use_value(&mut self, identifier: &Identifier) -> Option<&Value> { pub fn use_value(&mut self, identifier: &Identifier) -> Option<&Value> {
self.is_garbage_collected = false;
match self.variables.get_mut(identifier) { match self.variables.get_mut(identifier) {
Some((VariableData::Value(value), usage_data)) => { Some((VariableData::Value(value), usage_data)) => {
usage_data.used += 1; usage_data.used += 1;
@ -42,14 +60,9 @@ impl Context {
} }
} }
pub fn get_variable_data(&self, identifier: &Identifier) -> Option<&VariableData> {
match self.variables.get(identifier) {
Some((variable_data, _)) => Some(variable_data),
_ => None,
}
}
pub fn set_type(&mut self, identifier: Identifier, r#type: Type) { pub fn set_type(&mut self, identifier: Identifier, r#type: Type) {
self.is_garbage_collected = false;
self.variables.insert( self.variables.insert(
identifier, identifier,
(VariableData::Type(r#type), UsageData::default()), (VariableData::Type(r#type), UsageData::default()),
@ -57,6 +70,8 @@ impl Context {
} }
pub fn set_value(&mut self, identifier: Identifier, value: Value) { pub fn set_value(&mut self, identifier: Identifier, value: Value) {
self.is_garbage_collected = false;
self.variables.insert( self.variables.insert(
identifier, identifier,
(VariableData::Value(value), UsageData::default()), (VariableData::Value(value), UsageData::default()),
@ -64,8 +79,11 @@ impl Context {
} }
pub fn collect_garbage(&mut self) { pub fn collect_garbage(&mut self) {
if !self.is_garbage_collected {
self.variables self.variables
.retain(|_, (_, usage_data)| usage_data.used < usage_data.allowed_uses); .retain(|_, (_, usage_data)| usage_data.used < usage_data.allowed_uses);
self.variables.shrink_to_fit();
}
} }
pub fn add_allowed_use(&mut self, identifier: &Identifier) -> bool { pub fn add_allowed_use(&mut self, identifier: &Identifier) -> bool {
@ -91,8 +109,38 @@ pub enum VariableData {
Type(Type), Type(Type),
} }
#[derive(Default, Debug, Clone)] #[derive(Debug, Clone)]
pub struct UsageData { pub struct UsageData {
pub allowed_uses: u16, pub allowed_uses: u16,
pub used: u16, pub used: u16,
} }
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);
}
}

View File

@ -28,6 +28,26 @@ pub fn run(source: &str) -> Result<Option<Value>, DustError> {
.map_err(|vm_error| DustError::VmError { vm_error, source }) .map_err(|vm_error| DustError::VmError { vm_error, source })
} }
pub fn run_with_context<'src>(
source: &'src str,
context: &mut Context,
) -> Result<Option<Value>, DustError<'src>> {
let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context);
analyzer
.analyze()
.map_err(|analyzer_error| DustError::AnalyzerError {
analyzer_error,
source,
})?;
let mut vm = Vm::new(abstract_syntax_tree);
vm.run(context)
.map_err(|vm_error| DustError::VmError { vm_error, source })
}
pub struct Vm { pub struct Vm {
abstract_tree: AbstractSyntaxTree, abstract_tree: AbstractSyntaxTree,
} }
@ -40,17 +60,20 @@ impl Vm {
pub fn run(&mut self, context: &mut Context) -> Result<Option<Value>, VmError> { pub fn run(&mut self, context: &mut Context) -> Result<Option<Value>, VmError> {
let mut previous_value = None; let mut previous_value = None;
while let Some(node) = self.abstract_tree.nodes.pop_front() { while let Some(statement) = self.abstract_tree.nodes.pop_front() {
previous_value = self.run_node(node, context)?; previous_value = self.run_statement(statement, context, true)?;
context.collect_garbage();
} }
Ok(previous_value) Ok(previous_value)
} }
fn run_node( fn run_statement(
&self, &self,
node: Node<Statement>, node: Node<Statement>,
context: &mut Context, context: &mut Context,
collect_garbage: bool,
) -> Result<Option<Value>, VmError> { ) -> Result<Option<Value>, VmError> {
match node.inner { match node.inner {
Statement::BinaryOperation { Statement::BinaryOperation {
@ -69,7 +92,9 @@ impl Vm {
}); });
}; };
let value = if let Some(value) = self.run_node(*right, context)? { let value = if let Some(value) =
self.run_statement(*right, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -90,7 +115,9 @@ impl Vm {
position: left.position, position: left.position,
}); });
}; };
let right_value = if let Some(value) = self.run_node(*right, context)? { let right_value = if let Some(value) =
self.run_statement(*right, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -118,14 +145,16 @@ impl Vm {
} }
let left_position = left.position; let left_position = left.position;
let left_value = if let Some(value) = self.run_node(*left, context)? { let left_value =
if let Some(value) = self.run_statement(*left, context, collect_garbage)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
position: left_position, position: left_position,
}); });
}; };
let right_value = if let Some(value) = self.run_node(*right, context)? { let right_value =
if let Some(value) = self.run_statement(*right, context, collect_garbage)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -160,7 +189,7 @@ impl Vm {
let mut previous_value = None; let mut previous_value = None;
for statement in statements { for statement in statements {
previous_value = self.run_node(statement, context)?; previous_value = self.run_statement(statement, context, collect_garbage)?;
} }
Ok(previous_value) Ok(previous_value)
@ -175,7 +204,9 @@ impl Vm {
for node in nodes { for node in nodes {
let position = node.position; let position = node.position;
let value = if let Some(value) = self.run_node(node, context)? { let value = if let Some(value) =
self.run_statement(node, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -205,7 +236,9 @@ impl Vm {
value_arguments: value_parameter_nodes, value_arguments: value_parameter_nodes,
} => { } => {
let function_position = function_node.position; let function_position = function_node.position;
let function_value = if let Some(value) = self.run_node(*function_node, context)? { let function_value = if let Some(value) =
self.run_statement(*function_node, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -226,7 +259,9 @@ impl Vm {
for node in value_nodes { for node in value_nodes {
let position = node.position; let position = node.position;
let value = if let Some(value) = self.run_node(node, context)? { let value = if let Some(value) =
self.run_statement(node, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -243,7 +278,13 @@ impl Vm {
Ok(function.clone().call(None, value_parameters, context)?) Ok(function.clone().call(None, value_parameters, context)?)
} }
Statement::Identifier(identifier) => { Statement::Identifier(identifier) => {
if let Some(value) = context.use_value(&identifier) { let value_option = if collect_garbage {
context.use_value(&identifier)
} else {
context.get_value(&identifier)
};
if let Some(value) = value_option {
Ok(Some(value.clone())) Ok(Some(value.clone()))
} else { } else {
Err(VmError::UndefinedVariable { Err(VmError::UndefinedVariable {
@ -253,7 +294,9 @@ impl Vm {
} }
Statement::If { condition, body } => { Statement::If { condition, body } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = if let Some(value) = self.run_node(*condition, context)? { let condition_value = if let Some(value) =
self.run_statement(*condition, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -269,7 +312,7 @@ impl Vm {
}; };
if condition { if condition {
self.run_node(*body, context)?; self.run_statement(*body, context, collect_garbage)?;
} }
Ok(None) Ok(None)
@ -280,7 +323,9 @@ impl Vm {
else_body, else_body,
} => { } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = if let Some(value) = self.run_node(*condition, context)? { let condition_value = if let Some(value) =
self.run_statement(*condition, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -290,9 +335,9 @@ impl Vm {
if let Some(condition) = condition_value.as_boolean() { if let Some(condition) = condition_value.as_boolean() {
if condition { if condition {
self.run_node(*if_body, context) self.run_statement(*if_body, context, collect_garbage)
} else { } else {
self.run_node(*else_body, context) self.run_statement(*else_body, context, collect_garbage)
} }
} else { } else {
Err(VmError::ExpectedBoolean { Err(VmError::ExpectedBoolean {
@ -306,7 +351,9 @@ impl Vm {
else_ifs, else_ifs,
} => { } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = if let Some(value) = self.run_node(*condition, context)? { let condition_value = if let Some(value) =
self.run_statement(*condition, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -316,12 +363,13 @@ impl Vm {
if let Some(condition) = condition_value.as_boolean() { if let Some(condition) = condition_value.as_boolean() {
if condition { if condition {
self.run_node(*if_body, context) self.run_statement(*if_body, context, collect_garbage)
} else { } else {
for (condition, body) in else_ifs { for (condition, body) in else_ifs {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = let condition_value = if let Some(value) =
if let Some(value) = self.run_node(condition, context)? { self.run_statement(condition, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -337,7 +385,7 @@ impl Vm {
}; };
if condition { if condition {
self.run_node(body, context)?; self.run_statement(body, context, collect_garbage)?;
} }
} }
@ -356,7 +404,9 @@ impl Vm {
else_body, else_body,
} => { } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = if let Some(value) = self.run_node(*condition, context)? { let condition_value = if let Some(value) =
self.run_statement(*condition, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -366,12 +416,13 @@ impl Vm {
if let Some(condition) = condition_value.as_boolean() { if let Some(condition) = condition_value.as_boolean() {
if condition { if condition {
self.run_node(*if_body, context) self.run_statement(*if_body, context, collect_garbage)
} else { } else {
for (condition, body) in else_ifs { for (condition, body) in else_ifs {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = let condition_value = if let Some(value) =
if let Some(value) = self.run_node(condition, context)? { self.run_statement(condition, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -387,11 +438,11 @@ impl Vm {
}; };
if condition { if condition {
return self.run_node(body, context); return self.run_statement(body, context, collect_garbage);
} }
} }
self.run_node(*else_body, context) self.run_statement(*else_body, context, collect_garbage)
} }
} else { } else {
Err(VmError::ExpectedBoolean { Err(VmError::ExpectedBoolean {
@ -404,7 +455,7 @@ impl Vm {
.into_iter() .into_iter()
.map(|node| { .map(|node| {
let span = node.position; let span = node.position;
if let Some(value) = self.run_node(node, context)? { if let Some(value) = self.run_statement(node, context, collect_garbage)? {
Ok(value) Ok(value)
} else { } else {
Err(VmError::ExpectedValue { position: span }) Err(VmError::ExpectedValue { position: span })
@ -426,7 +477,9 @@ impl Vm {
}); });
}; };
let position = value_node.position; let position = value_node.position;
let value = if let Some(value) = self.run_node(value_node, context)? { let value = if let Some(value) =
self.run_statement(value_node, context, collect_garbage)?
{
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -438,13 +491,14 @@ impl Vm {
Ok(Some(Value::map(values))) Ok(Some(Value::map(values)))
} }
Statement::Nil(node) => { Statement::Nil(node) => {
let _return = self.run_node(*node, context)?; let _return = self.run_statement(*node, context, collect_garbage)?;
Ok(None) Ok(None)
} }
Statement::PropertyAccess(left, right) => { Statement::PropertyAccess(left, right) => {
let left_span = left.position; let left_span = left.position;
let left_value = if let Some(value) = self.run_node(*left, context)? { let left_value =
if let Some(value) = self.run_statement(*left, context, collect_garbage)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -480,7 +534,9 @@ impl Vm {
let condition_position = condition.position; let condition_position = condition.position;
while let Some(condition_value) = self.run_node(*condition.clone(), context)? { while let Some(condition_value) =
self.run_statement(*condition.clone(), context, false)?
{
if let ValueInner::Boolean(condition_value) = condition_value.inner().as_ref() { if let ValueInner::Boolean(condition_value) = condition_value.inner().as_ref() {
if !condition_value { if !condition_value {
break; break;
@ -491,7 +547,7 @@ impl Vm {
}); });
} }
return_value = self.run_node(*body.clone(), context)?; return_value = self.run_statement(*body.clone(), context, false)?;
if return_value.is_some() { if return_value.is_some() {
break; break;