Add context stacks

This commit is contained in:
Jeff 2024-08-29 12:32:48 -04:00
parent d032334635
commit 21b63b5942
5 changed files with 187 additions and 108 deletions

View File

@ -17,7 +17,7 @@ use crate::{
PrimitiveValueExpression, RangeExpression, Span, Statement, StructDefinition,
StructExpression, TupleAccessExpression,
},
core_library, parse, Context, ContextError, DustError, Expression, Identifier, RangeableType,
parse, ContextError, ContextStack, DustError, Expression, Identifier, RangeableType,
StructType, Type, TypeConflict, TypeEvaluation,
};
@ -34,8 +34,8 @@ use crate::{
/// ```
pub fn analyze(source: &str) -> Result<(), DustError> {
let abstract_tree = parse(source)?;
let context = core_library().create_child();
let mut analyzer = Analyzer::new(&abstract_tree, context);
let context_stack = ContextStack::with_core_library();
let mut analyzer = Analyzer::new(&abstract_tree, context_stack);
analyzer.analyze();
@ -54,22 +54,22 @@ pub fn analyze(source: &str) -> Result<(), DustError> {
/// # use dust_lang::*;
/// let input = "x = 1 + false";
/// let abstract_tree = parse(input).unwrap();
/// let context = Context::new();
/// let mut analyzer = Analyzer::new(&abstract_tree, context);
/// let context_stack = ContextStack::new();
/// let mut analyzer = Analyzer::new(&abstract_tree, context_stack);
/// let result = analyzer.analyze();
///
/// assert!(!analyzer.errors.is_empty());
pub struct Analyzer<'a> {
abstract_tree: &'a AbstractSyntaxTree,
context: Context,
context_stack: ContextStack,
pub errors: Vec<AnalysisError>,
}
impl<'a> Analyzer<'a> {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context: Context) -> Self {
pub fn new(abstract_tree: &'a AbstractSyntaxTree, context_stack: ContextStack) -> Self {
Self {
abstract_tree,
context,
context_stack,
errors: Vec::new(),
}
}
@ -81,6 +81,8 @@ impl<'a> Analyzer<'a> {
}
fn analyze_statement(&mut self, statement: &Statement) {
let context = self.context_stack.top();
match statement {
Statement::Expression(expression) => {
self.analyze_expression(expression, statement.position())
@ -91,14 +93,14 @@ impl<'a> Analyzer<'a> {
Statement::Let(let_statement) => match &let_statement.inner {
LetStatement::Let { identifier, value }
| LetStatement::LetMut { identifier, value } => {
let r#type = match value.type_evaluation(&self.context) {
let r#type = match value.type_evaluation(&context) {
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
Ok(TypeEvaluation::Constructor(StructType::Unit { name })) => {
let set_type = self.context.set_variable_type(
let set_type = context.set_variable_type(
identifier.inner.clone(),
Type::Struct(StructType::Unit { name }),
statement.position(),
@ -119,7 +121,7 @@ impl<'a> Analyzer<'a> {
};
if let Some(r#type) = r#type {
let set_type = self.context.set_variable_type(
let set_type = context.set_variable_type(
identifier.inner.clone(),
r#type.clone(),
statement.position(),
@ -145,7 +147,7 @@ impl<'a> Analyzer<'a> {
},
Statement::StructDefinition(struct_definition) => {
let set_constructor_type = match &struct_definition.inner {
StructDefinition::Unit { name } => self.context.set_constructor_type(
StructDefinition::Unit { name } => context.set_constructor_type(
name.inner.clone(),
StructType::Unit {
name: name.inner.clone(),
@ -155,7 +157,7 @@ impl<'a> Analyzer<'a> {
StructDefinition::Tuple { name, items } => {
let fields = items.iter().map(|item| item.inner.clone()).collect();
self.context.set_constructor_type(
context.set_constructor_type(
name.inner.clone(),
StructType::Tuple {
name: name.inner.clone(),
@ -172,7 +174,7 @@ impl<'a> Analyzer<'a> {
})
.collect();
self.context.set_constructor_type(
context.set_constructor_type(
name.inner.clone(),
StructType::Fields {
name: name.inner.clone(),
@ -194,6 +196,8 @@ impl<'a> Analyzer<'a> {
}
fn analyze_expression(&mut self, expression: &Expression, statement_position: Span) {
let context = self.context_stack.top();
match expression {
Expression::Block(block_expression) => self.analyze_block(&block_expression.inner),
Expression::Break(break_node) => {
@ -206,7 +210,7 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(invoker, statement_position);
let invoker_evaluation = match invoker.type_evaluation(&self.context) {
let invoker_evaluation = match invoker.type_evaluation(&context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -219,7 +223,7 @@ impl<'a> Analyzer<'a> {
invoker_evaluation
{
for (expected_type, argument) in fields.iter().zip(arguments.iter()) {
let actual_type = match argument.type_evaluation(&self.context) {
let actual_type = match argument.type_evaluation(&context) {
Ok(evaluation) => evaluation.r#type(),
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -282,7 +286,7 @@ impl<'a> Analyzer<'a> {
for ((_, expected_type), argument) in value_parameters.iter().zip(arguments) {
self.analyze_expression(argument, statement_position);
let argument_evaluation = match argument.type_evaluation(&self.context) {
let argument_evaluation = match argument.type_evaluation(&context) {
Ok(evaluation) => evaluation,
Err(error) => {
self.errors.push(AnalysisError::AstError(error));
@ -321,7 +325,7 @@ impl<'a> Analyzer<'a> {
let FieldAccessExpression { container, field } =
field_access_expression.inner.as_ref();
let evaluation = match container.type_evaluation(&self.context) {
let evaluation = match container.type_evaluation(&context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -355,7 +359,8 @@ impl<'a> Analyzer<'a> {
}
Expression::Identifier(identifier) => {
let find_identifier = self
.context
.context_stack
.top()
.update_last_position(&identifier.inner, statement_position);
if let Ok(false) = find_identifier {
@ -394,7 +399,7 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(list, statement_position);
self.analyze_expression(index, statement_position);
let list_type_evaluation = match list.type_evaluation(&self.context) {
let list_type_evaluation = match list.type_evaluation(&context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -413,7 +418,7 @@ impl<'a> Analyzer<'a> {
return;
}
};
let index_type_evaluation = match index.type_evaluation(&self.context) {
let index_type_evaluation = match index.type_evaluation(&context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -540,22 +545,24 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(assignee, statement_position);
self.analyze_expression(modifier, statement_position);
let assignee_type_evaluation = match assignee.type_evaluation(&self.context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
let assignee_type_evaluation =
match assignee.type_evaluation(&self.context_stack.top()) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
let modifier_type_evaluation = match modifier.type_evaluation(&self.context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
let modifier_type_evaluation =
match modifier.type_evaluation(&self.context_stack.top()) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
return;
}
};
let (expected_type, actual_type) = match (
assignee_type_evaluation.r#type(),
@ -608,7 +615,8 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(left, statement_position);
self.analyze_expression(right, statement_position);
let left_type_evaluation = match left.type_evaluation(&self.context) {
let left_type_evaluation = match left.type_evaluation(&self.context_stack.top())
{
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -616,14 +624,15 @@ impl<'a> Analyzer<'a> {
return;
}
};
let right_type_evaluation = match right.type_evaluation(&self.context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
let right_type_evaluation =
match right.type_evaluation(&self.context_stack.top()) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
return;
}
};
let (left_type, right_type) = match (
left_type_evaluation.r#type(),
@ -704,7 +713,8 @@ impl<'a> Analyzer<'a> {
self.analyze_expression(left, statement_position);
self.analyze_expression(right, statement_position);
let left_type_evaluation = match left.type_evaluation(&self.context) {
let left_type_evaluation = match left.type_evaluation(&self.context_stack.top())
{
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
@ -712,14 +722,15 @@ impl<'a> Analyzer<'a> {
return;
}
};
let right_type_evaluation = match right.type_evaluation(&self.context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
let right_type_evaluation =
match right.type_evaluation(&self.context_stack.top()) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));
return;
}
};
return;
}
};
let (left_type, right_type) = match (
left_type_evaluation.r#type(),
@ -775,7 +786,8 @@ impl<'a> Analyzer<'a> {
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
StructExpression::Fields { name, fields } => {
let update_position = self
.context
.context_stack
.top()
.update_last_position(&name.inner, statement_position);
if let Err(error) = update_position {
@ -795,7 +807,7 @@ impl<'a> Analyzer<'a> {
Expression::TupleAccess(tuple_access) => {
let TupleAccessExpression { tuple, index } = tuple_access.inner.as_ref();
let type_evaluation = match tuple.type_evaluation(&self.context) {
let type_evaluation = match tuple.type_evaluation(&context) {
Ok(evaluation) => evaluation,
Err(ast_error) => {
self.errors.push(AnalysisError::AstError(ast_error));

View File

@ -5,7 +5,57 @@ use std::{
sync::{Arc, PoisonError, RwLock, RwLockReadGuard, RwLockWriteGuard},
};
use crate::{ast::Span, Constructor, Identifier, StructType, Type, Value};
use crate::{ast::Span, core_library, Constructor, Identifier, StructType, Type, Value};
#[derive(Debug, Clone)]
pub struct ContextStack {
stack: Arc<RwLock<Vec<Context>>>,
}
impl ContextStack {
pub fn new() -> Self {
Self {
stack: Arc::new(RwLock::new(vec![Context::new()])),
}
}
pub fn with_context(context: Context) -> Self {
Self {
stack: Arc::new(RwLock::new(vec![context])),
}
}
pub fn with_core_library() -> Self {
Self::with_context(core_library().create_child())
}
pub fn push(&self) {
let mut stack = self.stack.write().unwrap();
let top = stack.last().unwrap().clone();
stack.push(top.create_child());
}
pub fn pop(&self) {
let removed = self.stack.write().unwrap().pop();
if let Some(removed) = removed {
for (identifier, _) in removed.associations.read().unwrap().iter() {
log::trace!("Dropping context with: {}", identifier);
}
}
}
pub fn top(&self) -> Context {
self.stack.read().unwrap().last().unwrap().clone()
}
}
impl Default for ContextStack {
fn default() -> Self {
Self::new()
}
}
pub type Associations = HashMap<Identifier, (ContextData, Span)>;
@ -372,10 +422,33 @@ impl Display for ContextError {
#[cfg(test)]
mod tests {
use crate::vm::run_with_context;
use crate::{parse, Analyzer, DustError, Vm};
use super::*;
fn run_vm_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
let context_stack = ContextStack::with_context(context);
let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context_stack.clone());
analyzer.analyze();
if !analyzer.errors.is_empty() {
return Err(DustError::Analysis {
analysis_errors: analyzer.errors,
source,
});
}
let vm = Vm::new(context_stack);
vm.run(abstract_syntax_tree)
.map_err(|runtime_error| DustError::Runtime {
runtime_error,
source,
})
}
#[test]
fn context_removes_variables() {
let source = "
@ -387,7 +460,7 @@ mod tests {
let context = Context::new();
assert_eq!(
run_with_context(source, context.clone()),
run_vm_with_context(source, context.clone()),
Ok(Some(Value::integer(15)))
);
@ -405,7 +478,7 @@ mod tests {
";
let context = Context::new();
run_with_context(source, context.clone()).unwrap();
run_vm_with_context(source, context.clone()).unwrap();
assert_eq!(context.association_count().unwrap(), 0);
}

View File

@ -35,7 +35,7 @@ pub use analyzer::{analyze, AnalysisError, Analyzer};
pub use ast::{AbstractSyntaxTree, AstError, Expression, Node, Span, Statement};
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
pub use constructor::Constructor;
pub use context::{Context, ContextData, ContextError};
pub use context::{Context, ContextData, ContextError, ContextStack};
pub use core_library::core_library;
pub use dust_error::DustError;
pub use evaluation::{Evaluation, TypeEvaluation};
@ -45,4 +45,4 @@ pub use parser::{parse, ParseError, Parser};
pub use r#type::*;
pub use token::{Token, TokenKind, TokenOwned};
pub use value::*;
pub use vm::{run, run_with_context, RuntimeError, Vm};
pub use vm::{run, RuntimeError, Vm};

View File

@ -15,8 +15,8 @@ use serde::{
};
use crate::{
AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, EnumType,
FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm,
AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, ContextStack,
EnumType, FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm,
};
/// Dust value representation
@ -1405,20 +1405,22 @@ impl Function {
.call(_type_arguments, value_arguments)
.map_err(FunctionCallError::BuiltInFunction),
Function::Parsed { r#type, body, .. } => {
let new_context =
Context::with_data_from(context).map_err(FunctionCallError::Context)?;
let base_context = context.create_child();
let new_context_stack = ContextStack::with_context(base_context.clone());
if let (Some(value_parameters), Some(value_arguments)) =
(&r#type.value_parameters, value_arguments)
{
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
new_context
base_context
.set_variable_value(identifier.clone(), value)
.map_err(FunctionCallError::Context)?;
}
}
let vm = Vm::new(new_context);
drop(base_context);
let vm = Vm::new(new_context_stack);
vm.run(body)
.map_err(|error| FunctionCallError::Runtime(Box::new(error)))

View File

@ -21,9 +21,10 @@ use crate::{
StructDefinition, StructExpression,
},
constructor::ConstructError,
core_library, parse, Analyzer, BuiltInFunctionError, Context, ContextData, ContextError,
DustError, Evaluation, Expression, Function, FunctionCallError, Identifier, ParseError,
StructType, Type, Value, ValueData, ValueError,
context::ContextStack,
parse, Analyzer, BuiltInFunctionError, Context, ContextData, ContextError, DustError,
Evaluation, Expression, Function, FunctionCallError, Identifier, ParseError, StructType, Type,
Value, ValueData, ValueError,
};
/// Run the source code and return the result.
@ -37,28 +38,9 @@ use crate::{
/// assert_eq!(result, Ok(Some(Value::integer(42))));
/// ```
pub fn run(source: &str) -> Result<Option<Value>, DustError> {
let context = core_library().create_child();
run_with_context(source, context)
}
/// Run the source code with a context and return the result.
///
/// # Example
/// ```
/// # use dust_lang::*;
/// let context = Context::new();
///
/// context.set_variable_value(Identifier::new("foo"), Value::integer(40));
/// context.update_last_position(&Identifier::new("foo"), (100, 100));
///
/// let result = run_with_context("foo + 2", context);
///
/// assert_eq!(result, Ok(Some(Value::integer(42))));
/// ```
pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>, DustError> {
let context_stack = ContextStack::with_core_library();
let abstract_syntax_tree = parse(source)?;
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context.clone());
let mut analyzer = Analyzer::new(&abstract_syntax_tree, context_stack.clone());
analyzer.analyze();
@ -69,7 +51,7 @@ pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>,
});
}
let vm = Vm::new(context);
let vm = Vm::new(context_stack);
vm.run(abstract_syntax_tree)
.map_err(|runtime_error| DustError::Runtime {
@ -86,12 +68,12 @@ pub fn run_with_context(source: &str, context: Context) -> Result<Option<Value>,
///
/// See the `run_with_context` function for an example of how to use the Analyzer and the VM.
pub struct Vm {
context: Context,
context_stack: ContextStack,
}
impl Vm {
pub fn new(context: Context) -> Self {
Self { context }
pub fn new(context_stack: ContextStack) -> Self {
Self { context_stack }
}
pub fn run(&self, mut tree: AbstractSyntaxTree) -> Result<Option<Value>, RuntimeError> {
@ -209,7 +191,8 @@ impl Vm {
};
let constructor = struct_type.constructor();
self.context
self.context_stack
.top()
.set_constructor(name, constructor)
.map_err(|error| RuntimeError::ContextError {
error,
@ -221,7 +204,8 @@ impl Vm {
};
if collect_garbage {
self.context
self.context_stack
.top()
.collect_garbage(position.1)
.map_err(|error| RuntimeError::ContextError { error, position })?;
}
@ -250,7 +234,8 @@ impl Vm {
_ => value,
};
self.context
self.context_stack
.top()
.set_variable_value(identifier.inner, new_value)
.map_err(|error| RuntimeError::ContextError { error, position })?;
@ -263,7 +248,8 @@ impl Vm {
.expect_value(position)?
.into_mutable();
self.context
self.context_stack
.top()
.set_variable_value(identifier.inner, mutable_value)
.map_err(|error| RuntimeError::ContextError { error, position })?;
@ -364,12 +350,14 @@ impl Vm {
}
fn run_identifier(&self, identifier: Node<Identifier>) -> Result<Evaluation, RuntimeError> {
let get_data = self.context.get_data(&identifier.inner).map_err(|error| {
RuntimeError::ContextError {
let get_data = self
.context_stack
.top()
.get_data(&identifier.inner)
.map_err(|error| RuntimeError::ContextError {
error,
position: identifier.position,
}
})?;
})?;
if let Some(ContextData::VariableValue(value)) = get_data {
return Ok(Evaluation::Return(Some(value)));
@ -403,7 +391,8 @@ impl Vm {
let position = name.position;
let constructor = self
.context
.context_stack
.top()
.get_constructor(&name.inner)
.map_err(|error| RuntimeError::ContextError { error, position })?;
@ -1020,18 +1009,17 @@ impl Vm {
block: BlockExpression,
collect_garbage: bool,
) -> Result<Evaluation, RuntimeError> {
let block_context = self.context.create_child();
let vm = Vm::new(block_context);
self.context_stack.push();
match block {
BlockExpression::Async(statements) => vm
let evaluation = match block {
BlockExpression::Async(statements) => self
.run_async(AbstractSyntaxTree::with_statements(statements))
.map(Evaluation::Return),
BlockExpression::Sync(statements) => {
let mut evaluation = Evaluation::Return(None);
for statement in statements {
evaluation = vm.run_statement(statement, collect_garbage)?;
evaluation = self.run_statement(statement, collect_garbage)?;
if let Evaluation::Break(_) = evaluation {
return Ok(evaluation);
@ -1040,7 +1028,11 @@ impl Vm {
Ok(evaluation)
}
}
};
self.context_stack.pop();
evaluation
}
fn run_if(