Refactor VM to own its context

This commit is contained in:
Jeff 2024-08-12 08:54:21 -04:00
parent bf11bd1f0f
commit c71c4d2d07
4 changed files with 119 additions and 112 deletions

View File

@ -1,10 +1,9 @@
//! Tools for analyzing an abstract syntax tree and catch errors before running the virtual //! Tools for analyzing an abstract syntax tree and catch errors before running the virtual
//! machine. //! machine.
//! //!
//! This module provides to anlysis options, both of which borrow an abstract syntax tree and a //! This module provides two anlysis options:
//! hash map of variables: //! - `analyze` convenience function, which takes a string input
//! - `analyze` convenience function //! - `Analyzer` struct, which borrows an abstract syntax tree and a context
//! - `Analyzer` struct
use std::{ use std::{
error::Error, error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
@ -28,8 +27,8 @@ use crate::{
/// ``` /// ```
pub fn analyze(source: &str) -> Result<(), DustError> { pub fn analyze(source: &str) -> Result<(), DustError> {
let abstract_tree = parse(source)?; let abstract_tree = parse(source)?;
let mut context = Context::new(); let context = Context::new();
let mut analyzer = Analyzer::new(&abstract_tree, &mut context); let mut analyzer = Analyzer::new(&abstract_tree, &context);
analyzer analyzer
.analyze() .analyze()
@ -54,11 +53,11 @@ pub fn analyze(source: &str) -> Result<(), DustError> {
/// assert!(result.is_err()); /// assert!(result.is_err());
pub struct Analyzer<'a> { pub struct Analyzer<'a> {
abstract_tree: &'a AbstractSyntaxTree, abstract_tree: &'a AbstractSyntaxTree,
context: &'a mut Context, context: &'a Context,
} }
impl<'a> Analyzer<'a> { impl<'a> Analyzer<'a> {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a mut Context) -> Self { pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: &'a Context) -> Self {
Self { Self {
abstract_tree, abstract_tree,
context, context,

View File

@ -21,6 +21,15 @@ impl Context {
} }
} }
pub fn with_variables_from(other: &Self) -> Self {
Self {
variables: Arc::new(RwLock::new(other.variables.read().unwrap().clone())),
is_garbage_collected_to: Arc::new(RwLock::new(
*other.is_garbage_collected_to.read().unwrap(),
)),
}
}
pub fn variable_count(&self) -> usize { pub fn variable_count(&self) -> usize {
self.variables.read().unwrap().len() self.variables.read().unwrap().len()
} }
@ -54,7 +63,7 @@ impl Context {
} }
} }
pub fn set_type(&mut self, identifier: Identifier, r#type: Type, position: Span) { pub fn set_type(&self, identifier: Identifier, r#type: Type, position: Span) {
log::trace!("Setting {identifier} to type {type} at {position:?}"); log::trace!("Setting {identifier} to type {type} at {position:?}");
self.variables self.variables
@ -63,7 +72,7 @@ impl Context {
.insert(identifier, (VariableData::Type(r#type), position)); .insert(identifier, (VariableData::Type(r#type), position));
} }
pub fn set_value(&mut self, identifier: Identifier, value: Value) { pub fn set_value(&self, identifier: Identifier, value: Value) {
log::trace!("Setting {identifier} to value {value}"); log::trace!("Setting {identifier} to value {value}");
let mut variables = self.variables.write().unwrap(); let mut variables = self.variables.write().unwrap();
@ -76,7 +85,7 @@ impl Context {
variables.insert(identifier, (VariableData::Value(value), last_position)); variables.insert(identifier, (VariableData::Value(value), last_position));
} }
pub fn collect_garbage(&mut self, current_position: usize) { pub fn collect_garbage(&self, current_position: usize) {
log::trace!("Collecting garbage up to {current_position}"); log::trace!("Collecting garbage up to {current_position}");
let mut is_garbage_collected_to = self.is_garbage_collected_to.write().unwrap(); let mut is_garbage_collected_to = self.is_garbage_collected_to.write().unwrap();
@ -101,7 +110,7 @@ impl Context {
*is_garbage_collected_to = current_position; *is_garbage_collected_to = current_position;
} }
pub fn update_last_position(&mut self, identifier: &Identifier, position: Span) -> bool { pub fn update_last_position(&self, identifier: &Identifier, position: Span) -> bool {
if let Some((_, last_position)) = self.variables.write().unwrap().get_mut(identifier) { if let Some((_, last_position)) = self.variables.write().unwrap().get_mut(identifier) {
*last_position = position; *last_position = position;
@ -142,9 +151,9 @@ mod tests {
z = x + y z = x + y
z z
"; ";
let mut context = Context::new(); let context = Context::new();
run_with_context(source, &mut context).unwrap(); run_with_context(source, context.clone()).unwrap();
assert_eq!(context.variable_count(), 0); assert_eq!(context.variable_count(), 0);
} }
@ -158,9 +167,9 @@ mod tests {
z = z + y z = z + y
} }
"; ";
let mut context = Context::new(); let context = Context::new();
run_with_context(source, &mut context).unwrap(); run_with_context(source, context.clone()).unwrap();
assert_eq!(context.variable_count(), 0); assert_eq!(context.variable_count(), 0);
} }

View File

@ -748,9 +748,9 @@ impl Function {
self, self,
_type_arguments: Option<Vec<Type>>, _type_arguments: Option<Vec<Type>>,
value_arguments: Option<Vec<Value>>, value_arguments: Option<Vec<Value>>,
context: &mut Context, context: &Context,
) -> Result<Option<Value>, VmError> { ) -> Result<Option<Value>, VmError> {
let mut new_context = context.clone(); let new_context = Context::with_variables_from(context);
if let (Some(value_parameters), Some(value_arguments)) = if let (Some(value_parameters), Some(value_arguments)) =
(self.value_parameters, value_arguments) (self.value_parameters, value_arguments)
@ -760,9 +760,9 @@ impl Function {
} }
} }
let mut vm = Vm::new(self.body); let mut vm = Vm::new(self.body, new_context);
vm.run(&mut new_context) vm.run()
} }
pub fn return_type(&self, variables: &Context) -> Option<Type> { pub fn return_type(&self, variables: &Context) -> Option<Type> {

View File

@ -1,27 +1,29 @@
//! Virtual machine for running the abstract syntax tree. //! Virtual machine for running the abstract syntax tree.
//!
//! This module provides three running option:
//! - `run` convenience function that takes a source code string and runs it
//! - `run_with_context` convenience function that takes a source code string and a context
//! - `Vm` struct that can be used to run an abstract syntax tree
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
}; };
use crate::{ use crate::{
abstract_tree::BinaryOperator, parse, value::ValueInner, AbstractSyntaxTree, Analyzer, parse, value::ValueInner, AbstractSyntaxTree, Analyzer, BinaryOperator, BuiltInFunctionError,
BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement, Context, DustError, Identifier, Node, ParseError, Span, Statement, UnaryOperator, Value,
UnaryOperator, Value, ValueError, ValueError,
}; };
pub fn run(source: &str) -> Result<Option<Value>, DustError> { pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let mut context = Context::new(); let context = Context::new();
run_with_context(source, &mut context) run_with_context(source, context)
} }
pub fn run_with_context<'src>( pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
source: &'src str,
context: &mut Context,
) -> Result<Option<Value>, DustError<'src>> {
let abstract_syntax_tree = parse(source)?; let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context); let mut analyzer = Analyzer::new(&abstract_syntax_tree, &context);
analyzer analyzer
.analyze() .analyze()
@ -30,45 +32,45 @@ pub fn run_with_context<'src>(
source, source,
})?; })?;
let mut vm = Vm::new(abstract_syntax_tree); let mut vm = Vm::new(abstract_syntax_tree, context);
vm.run(context) vm.run()
.map_err(|vm_error| DustError::VmError { vm_error, source }) .map_err(|vm_error| DustError::VmError { vm_error, source })
} }
pub struct Vm { pub struct Vm {
abstract_tree: AbstractSyntaxTree, abstract_tree: AbstractSyntaxTree,
context: Context,
} }
impl Vm { impl Vm {
pub fn new(abstract_tree: AbstractSyntaxTree) -> Self { pub fn new(abstract_tree: AbstractSyntaxTree, context: Context) -> Self {
Self { abstract_tree } Self {
abstract_tree,
context,
}
} }
pub fn run(&mut self, context: &mut Context) -> Result<Option<Value>, VmError> { pub fn run(&mut self) -> Result<Option<Value>, VmError> {
let mut previous_position = (0, 0); let mut previous_position = (0, 0);
let mut previous_value = None; let mut previous_value = None;
while let Some(statement) = self.abstract_tree.nodes.pop_front() { while let Some(statement) = self.abstract_tree.nodes.pop_front() {
let new_position = statement.position; let new_position = statement.position;
previous_value = self.run_statement(statement, context)?; previous_value = self.run_statement(statement)?;
context.collect_garbage(previous_position.1); self.context.collect_garbage(previous_position.1);
previous_position = new_position; previous_position = new_position;
} }
context.collect_garbage(previous_position.1); self.context.collect_garbage(previous_position.1);
Ok(previous_value) Ok(previous_value)
} }
fn run_statement( fn run_statement(&self, node: Node<Statement>) -> Result<Option<Value>, VmError> {
&self,
node: Node<Statement>,
context: &mut Context,
) -> Result<Option<Value>, VmError> {
match node.inner { match node.inner {
Statement::BinaryOperation { Statement::BinaryOperation {
left, left,
@ -85,7 +87,7 @@ impl Vm {
position: left.position, position: left.position,
}); });
}; };
let value = if let Some(value) = self.run_statement(*right, context)? { let value = if let Some(value) = self.run_statement(*right)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -93,7 +95,7 @@ impl Vm {
}); });
}; };
context.set_value(identifier, value); self.context.set_value(identifier, value);
return Ok(None); return Ok(None);
} }
@ -101,7 +103,7 @@ impl Vm {
if let BinaryOperator::AddAssign = operator.inner { if let BinaryOperator::AddAssign = operator.inner {
let (identifier, left_value) = let (identifier, left_value) =
if let Statement::Identifier(identifier) = left.inner { if let Statement::Identifier(identifier) = left.inner {
let value = context.get_value(&identifier).ok_or_else(|| { let value = self.context.get_value(&identifier).ok_or_else(|| {
VmError::UndefinedVariable { VmError::UndefinedVariable {
identifier: Node::new( identifier: Node::new(
Statement::Identifier(identifier.clone()), Statement::Identifier(identifier.clone()),
@ -116,7 +118,7 @@ impl Vm {
position: left.position, position: left.position,
}); });
}; };
let right_value = if let Some(value) = self.run_statement(*right, context)? { let right_value = if let Some(value) = self.run_statement(*right)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -130,20 +132,20 @@ impl Vm {
} }
})?; })?;
context.set_value(identifier, new_value); self.context.set_value(identifier, new_value);
return Ok(None); return Ok(None);
} }
let left_position = left.position; let left_position = left.position;
let left_value = if let Some(value) = self.run_statement(*left, context)? { let left_value = if let Some(value) = self.run_statement(*left)? {
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_statement(*right, context)? { let right_value = if let Some(value) = self.run_statement(*right)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -178,7 +180,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_statement(statement, context)?; previous_value = self.run_statement(statement)?;
} }
Ok(previous_value) Ok(previous_value)
@ -193,7 +195,7 @@ 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_statement(node, context)? { let value = if let Some(value) = self.run_statement(node)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -223,14 +225,13 @@ 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 = let function_value = if let Some(value) = self.run_statement(*function_node)? {
if let Some(value) = self.run_statement(*function_node, context)? { value
value } else {
} else { return Err(VmError::ExpectedValue {
return Err(VmError::ExpectedValue { position: function_position,
position: function_position, });
}); };
};
let function = if let Some(function) = function_value.as_function() { let function = if let Some(function) = function_value.as_function() {
function function
} else { } else {
@ -245,7 +246,7 @@ 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_statement(node, context)? { let value = if let Some(value) = self.run_statement(node)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -259,10 +260,12 @@ impl Vm {
None None
}; };
Ok(function.clone().call(None, value_parameters, context)?) Ok(function
.clone()
.call(None, value_parameters, &self.context)?)
} }
Statement::Identifier(identifier) => { Statement::Identifier(identifier) => {
let value_option = context.get_value(&identifier); let value_option = self.context.get_value(&identifier);
if let Some(value) = value_option { if let Some(value) = value_option {
Ok(Some(value.clone())) Ok(Some(value.clone()))
@ -274,14 +277,13 @@ impl Vm {
} }
Statement::If { condition, body } => { Statement::If { condition, body } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = let condition_value = if let Some(value) = self.run_statement(*condition)? {
if let Some(value) = self.run_statement(*condition, context)? { value
value } else {
} else { return Err(VmError::ExpectedValue {
return Err(VmError::ExpectedValue { position: condition_position,
position: condition_position, });
}); };
};
let condition = if let Some(condition) = condition_value.as_boolean() { let condition = if let Some(condition) = condition_value.as_boolean() {
condition condition
} else { } else {
@ -291,7 +293,7 @@ impl Vm {
}; };
if condition { if condition {
self.run_statement(*body, context)?; self.run_statement(*body)?;
} }
Ok(None) Ok(None)
@ -302,20 +304,19 @@ impl Vm {
else_body, else_body,
} => { } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = let condition_value = if let Some(value) = self.run_statement(*condition)? {
if let Some(value) = self.run_statement(*condition, context)? { value
value } else {
} else { return Err(VmError::ExpectedValue {
return Err(VmError::ExpectedValue { position: condition_position,
position: condition_position, });
}); };
};
if let Some(condition) = condition_value.as_boolean() { if let Some(condition) = condition_value.as_boolean() {
if condition { if condition {
self.run_statement(*if_body, context) self.run_statement(*if_body)
} else { } else {
self.run_statement(*else_body, context) self.run_statement(*else_body)
} }
} else { } else {
Err(VmError::ExpectedBoolean { Err(VmError::ExpectedBoolean {
@ -329,23 +330,22 @@ impl Vm {
else_ifs, else_ifs,
} => { } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = let condition_value = if let Some(value) = self.run_statement(*condition)? {
if let Some(value) = self.run_statement(*condition, context)? { value
value } else {
} else { return Err(VmError::ExpectedValue {
return Err(VmError::ExpectedValue { position: condition_position,
position: condition_position, });
}); };
};
if let Some(condition) = condition_value.as_boolean() { if let Some(condition) = condition_value.as_boolean() {
if condition { if condition {
self.run_statement(*if_body, context) self.run_statement(*if_body)
} 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) = self.run_statement(condition, context)? { if let Some(value) = self.run_statement(condition)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -361,7 +361,7 @@ impl Vm {
}; };
if condition { if condition {
self.run_statement(body, context)?; self.run_statement(body)?;
} }
} }
@ -380,23 +380,22 @@ impl Vm {
else_body, else_body,
} => { } => {
let condition_position = condition.position; let condition_position = condition.position;
let condition_value = let condition_value = if let Some(value) = self.run_statement(*condition)? {
if let Some(value) = self.run_statement(*condition, context)? { value
value } else {
} else { return Err(VmError::ExpectedValue {
return Err(VmError::ExpectedValue { position: condition_position,
position: condition_position, });
}); };
};
if let Some(condition) = condition_value.as_boolean() { if let Some(condition) = condition_value.as_boolean() {
if condition { if condition {
self.run_statement(*if_body, context) self.run_statement(*if_body)
} 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) = self.run_statement(condition, context)? { if let Some(value) = self.run_statement(condition)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -412,11 +411,11 @@ impl Vm {
}; };
if condition { if condition {
return self.run_statement(body, context); return self.run_statement(body);
} }
} }
self.run_statement(*else_body, context) self.run_statement(*else_body)
} }
} else { } else {
Err(VmError::ExpectedBoolean { Err(VmError::ExpectedBoolean {
@ -429,7 +428,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_statement(node, context)? { if let Some(value) = self.run_statement(node)? {
Ok(value) Ok(value)
} else { } else {
Err(VmError::ExpectedValue { position: span }) Err(VmError::ExpectedValue { position: span })
@ -451,7 +450,7 @@ impl Vm {
}); });
}; };
let position = value_node.position; let position = value_node.position;
let value = if let Some(value) = self.run_statement(value_node, context)? { let value = if let Some(value) = self.run_statement(value_node)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -463,13 +462,13 @@ impl Vm {
Ok(Some(Value::map(values))) Ok(Some(Value::map(values)))
} }
Statement::Nil(node) => { Statement::Nil(node) => {
let _return = self.run_statement(*node, context)?; let _return = self.run_statement(*node)?;
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_statement(*left, context)? { let left_value = if let Some(value) = self.run_statement(*left)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { return Err(VmError::ExpectedValue {
@ -502,7 +501,7 @@ impl Vm {
} }
Statement::UnaryOperation { operator, operand } => { Statement::UnaryOperation { operator, operand } => {
let position = operand.position; let position = operand.position;
let value = if let Some(value) = self.run_statement(*operand, context)? { let value = if let Some(value) = self.run_statement(*operand)? {
value value
} else { } else {
return Err(VmError::ExpectedValue { position }); return Err(VmError::ExpectedValue { position });
@ -532,7 +531,7 @@ impl Vm {
let condition_position = condition.position; let condition_position = condition.position;
while let Some(condition_value) = self.run_statement(*condition.clone(), context)? { while let Some(condition_value) = self.run_statement(*condition.clone())? {
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;
@ -543,7 +542,7 @@ impl Vm {
}); });
} }
return_value = self.run_statement(*body.clone(), context)?; return_value = self.run_statement(*body.clone())?;
if return_value.is_some() { if return_value.is_some() {
break; break;