2024-02-15 08:57:13 +00:00
|
|
|
//! An execution context that store variables and type data during the
|
|
|
|
//! [Interpreter][crate::Interpreter]'s abstraction and execution process.
|
|
|
|
//!
|
|
|
|
//! ## Setting values
|
|
|
|
//!
|
|
|
|
//! When data is stored in a context, it can be accessed by dust source code.
|
|
|
|
//! This allows you to insert values and type definitions before any code is
|
|
|
|
//! interpreted.
|
|
|
|
//!
|
|
|
|
//! ```
|
|
|
|
//! # use dust_lang::*;
|
|
|
|
//! let context = Context::new();
|
|
|
|
//!
|
|
|
|
//! context.set_value(
|
2024-02-16 15:58:37 +00:00
|
|
|
//! "foobar".into(),
|
2024-02-15 08:57:13 +00:00
|
|
|
//! Value::String("FOOBAR".to_string())
|
|
|
|
//! ).unwrap();
|
|
|
|
//!
|
|
|
|
//! interpret_with_context("output foobar", context);
|
|
|
|
//!
|
|
|
|
//! // Stdout: "FOOBAR"
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ## Built-in values and type definitions
|
|
|
|
//!
|
|
|
|
//! When looking up values and definitions, the Context will try to use one that
|
|
|
|
//! has been explicitly set. If nothing is found, it will then check the built-
|
|
|
|
//! in values and type definitions for a match. This means that the user can
|
|
|
|
//! override the built-ins.
|
2024-02-10 23:29:11 +00:00
|
|
|
use std::{
|
2024-02-11 19:10:11 +00:00
|
|
|
cmp::Ordering,
|
|
|
|
collections::BTreeMap,
|
2024-02-11 01:50:49 +00:00
|
|
|
sync::{Arc, RwLock, RwLockReadGuard},
|
2024-02-10 23:29:11 +00:00
|
|
|
};
|
|
|
|
|
2024-02-15 06:51:05 +00:00
|
|
|
use crate::{
|
2024-02-15 07:02:48 +00:00
|
|
|
built_in_type_definitions::all_built_in_type_definitions, built_in_values::all_built_in_values,
|
2024-02-15 20:20:29 +00:00
|
|
|
error::rw_lock_error::RwLockError, Identifier, Type, TypeDefinition, Value,
|
2024-02-15 06:51:05 +00:00
|
|
|
};
|
2024-02-10 23:29:11 +00:00
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// An execution context that variable and type data during the [Interpreter]'s
|
|
|
|
/// abstraction and execution process.
|
|
|
|
///
|
|
|
|
/// See the [module-level docs][self] for more info.
|
2024-02-11 20:26:09 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2024-02-10 23:29:11 +00:00
|
|
|
pub struct Context {
|
2024-02-15 20:20:29 +00:00
|
|
|
inner: Arc<RwLock<BTreeMap<Identifier, ValueData>>>,
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Context {
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Return a new, empty Context.
|
2024-02-10 23:29:11 +00:00
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2024-02-11 19:10:11 +00:00
|
|
|
inner: Arc::new(RwLock::new(BTreeMap::new())),
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Return a lock guard to the inner BTreeMap.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn inner(&self) -> Result<RwLockReadGuard<BTreeMap<Identifier, ValueData>>, RwLockError> {
|
2024-02-11 01:50:49 +00:00
|
|
|
Ok(self.inner.read()?)
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Create a new context with all of the data from an existing context.
|
2024-02-11 20:26:09 +00:00
|
|
|
pub fn with_variables_from(other: &Context) -> Result<Context, RwLockError> {
|
2024-02-11 19:10:11 +00:00
|
|
|
let mut new_variables = BTreeMap::new();
|
2024-02-10 23:29:11 +00:00
|
|
|
|
2024-02-11 00:31:47 +00:00
|
|
|
for (identifier, value_data) in other.inner.read()?.iter() {
|
2024-02-10 23:29:11 +00:00
|
|
|
new_variables.insert(identifier.clone(), value_data.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Context {
|
|
|
|
inner: Arc::new(RwLock::new(new_variables)),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Modify a context to take on all of the key-value pairs of another.
|
|
|
|
///
|
|
|
|
/// In the case of the conflict, the inherited value will override the previous
|
|
|
|
/// value.
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use dust_lang::*;
|
|
|
|
/// let first_context = Context::new();
|
|
|
|
/// let second_context = Context::new();
|
|
|
|
///
|
|
|
|
/// second_context.set_value(
|
2024-02-16 15:58:37 +00:00
|
|
|
/// "Foo".into(),
|
|
|
|
/// Value::String("Bar".to_string())
|
2024-02-15 08:57:13 +00:00
|
|
|
/// );
|
|
|
|
///
|
|
|
|
/// first_context.inherit_from(&second_context).unwrap();
|
|
|
|
///
|
|
|
|
/// assert_eq!(first_context, second_context);
|
|
|
|
/// ```
|
2024-02-11 20:26:09 +00:00
|
|
|
pub fn inherit_from(&self, other: &Context) -> Result<(), RwLockError> {
|
|
|
|
let mut self_variables = self.inner.write()?;
|
|
|
|
|
|
|
|
for (identifier, value_data) in other.inner.read()?.iter() {
|
2024-02-12 21:51:06 +00:00
|
|
|
let existing_data = self_variables.get(identifier);
|
|
|
|
|
|
|
|
if let Some(ValueData::Value { .. }) = existing_data {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
self_variables.insert(identifier.clone(), value_data.clone());
|
|
|
|
}
|
2024-02-11 20:26:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Get a value from the context.
|
|
|
|
///
|
|
|
|
/// This will also return a built-in value if one matches the key. See the
|
|
|
|
/// [module-level docs][self] for more info.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn get_value(&self, identifier: &Identifier) -> Result<Option<Value>, RwLockError> {
|
|
|
|
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
2024-02-10 23:29:11 +00:00
|
|
|
if let ValueData::Value { inner, .. } = value_data {
|
2024-02-13 13:10:34 +00:00
|
|
|
return Ok(Some(inner.clone()));
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-13 13:10:34 +00:00
|
|
|
|
2024-02-15 07:02:48 +00:00
|
|
|
for built_in_value in all_built_in_values() {
|
2024-02-15 20:20:29 +00:00
|
|
|
if built_in_value.name() == identifier.inner().as_ref() {
|
2024-02-15 07:02:48 +00:00
|
|
|
return Ok(Some(built_in_value.get().clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-13 13:10:34 +00:00
|
|
|
Ok(None)
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Get a type from the context.
|
|
|
|
///
|
|
|
|
/// If the key matches a stored value, its type will be returned. It if
|
|
|
|
/// matches a type hint, the type hint will be returned.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, RwLockError> {
|
|
|
|
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
2024-02-10 23:29:11 +00:00
|
|
|
match value_data {
|
2024-02-15 08:57:13 +00:00
|
|
|
ValueData::Value { inner, .. } => return Ok(Some(inner.r#type())),
|
|
|
|
ValueData::TypeHint { inner, .. } => return Ok(Some(inner.clone())),
|
2024-02-15 03:38:45 +00:00
|
|
|
ValueData::TypeDefinition(_) => todo!(),
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-15 08:57:13 +00:00
|
|
|
|
|
|
|
Ok(None)
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Get a value from the context.
|
|
|
|
///
|
|
|
|
/// This will also return a built-in type definition if one matches the key.
|
|
|
|
/// See the [module-level docs][self] for more info.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn get_definition(
|
|
|
|
&self,
|
|
|
|
identifier: &Identifier,
|
|
|
|
) -> Result<Option<TypeDefinition>, RwLockError> {
|
|
|
|
if let Some(value_data) = self.inner.read()?.get(identifier) {
|
2024-02-15 03:38:45 +00:00
|
|
|
if let ValueData::TypeDefinition(definition) = value_data {
|
|
|
|
return Ok(Some(definition.clone()));
|
|
|
|
}
|
2024-02-15 06:51:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for built_in_definition in all_built_in_type_definitions() {
|
2024-02-15 20:20:29 +00:00
|
|
|
if built_in_definition.name() == identifier.inner().as_ref() {
|
|
|
|
return Ok(Some(built_in_definition.get(self).clone()?));
|
2024-02-15 06:51:05 +00:00
|
|
|
}
|
2024-02-15 03:38:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Set a value to a key.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn set_value(&self, key: Identifier, value: Value) -> Result<(), RwLockError> {
|
2024-02-10 23:29:11 +00:00
|
|
|
self.inner.write()?.insert(
|
|
|
|
key,
|
|
|
|
ValueData::Value {
|
|
|
|
inner: value,
|
|
|
|
runtime_uses: Arc::new(RwLock::new(0)),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Set a type hint.
|
|
|
|
///
|
|
|
|
/// This allows the interpreter to check a value's type before the value
|
|
|
|
/// actually exists by predicting what the abstract tree will produce.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn set_type(&self, key: Identifier, r#type: Type) -> Result<(), RwLockError> {
|
2024-02-10 23:29:11 +00:00
|
|
|
self.inner
|
|
|
|
.write()?
|
2024-02-15 08:57:13 +00:00
|
|
|
.insert(key, ValueData::TypeHint { inner: r#type });
|
2024-02-10 23:29:11 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-02-12 20:48:43 +00:00
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Set a type definition.
|
|
|
|
///
|
|
|
|
/// This allows defined types (i.e. structs and enums) to be instantiated
|
|
|
|
/// later while using this context.
|
2024-02-15 03:38:45 +00:00
|
|
|
pub fn set_definition(
|
|
|
|
&self,
|
2024-02-15 20:20:29 +00:00
|
|
|
key: Identifier,
|
2024-02-15 03:38:45 +00:00
|
|
|
definition: TypeDefinition,
|
|
|
|
) -> Result<(), RwLockError> {
|
|
|
|
self.inner
|
|
|
|
.write()?
|
|
|
|
.insert(key, ValueData::TypeDefinition(definition));
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-15 08:57:13 +00:00
|
|
|
/// Remove a key-value pair.
|
2024-02-15 20:20:29 +00:00
|
|
|
pub fn unset(&self, key: &Identifier) -> Result<(), RwLockError> {
|
2024-02-12 20:48:43 +00:00
|
|
|
self.inner.write()?.remove(key);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-02-10 23:29:11 +00:00
|
|
|
}
|
2024-02-11 18:54:27 +00:00
|
|
|
|
2024-02-11 19:10:11 +00:00
|
|
|
impl Default for Context {
|
|
|
|
fn default() -> Self {
|
|
|
|
Context::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-11 18:54:27 +00:00
|
|
|
impl Eq for Context {}
|
|
|
|
|
|
|
|
impl PartialEq for Context {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
let self_variables = self.inner().unwrap();
|
|
|
|
let other_variables = other.inner().unwrap();
|
|
|
|
|
|
|
|
if self_variables.len() != other_variables.len() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ((left_key, left_value_data), (right_key, right_value_data)) in
|
|
|
|
self_variables.iter().zip(other_variables.iter())
|
|
|
|
{
|
|
|
|
if left_key != right_key || left_value_data != right_value_data {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialOrd for Context {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
|
|
Some(self.cmp(other))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for Context {
|
2024-02-11 19:10:11 +00:00
|
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
|
|
let left = self.inner().unwrap();
|
|
|
|
let right = other.inner().unwrap();
|
2024-02-11 18:54:27 +00:00
|
|
|
|
2024-02-11 19:10:11 +00:00
|
|
|
left.cmp(&right)
|
2024-02-11 18:54:27 +00:00
|
|
|
}
|
|
|
|
}
|
2024-02-12 20:48:43 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub enum ValueData {
|
|
|
|
Value {
|
|
|
|
inner: Value,
|
|
|
|
runtime_uses: Arc<RwLock<u16>>,
|
|
|
|
},
|
2024-02-15 08:57:13 +00:00
|
|
|
TypeHint {
|
2024-02-12 20:48:43 +00:00
|
|
|
inner: Type,
|
|
|
|
},
|
2024-02-15 03:38:45 +00:00
|
|
|
TypeDefinition(TypeDefinition),
|
2024-02-12 20:48:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Eq for ValueData {}
|
|
|
|
|
|
|
|
impl PartialEq for ValueData {
|
|
|
|
fn eq(&self, other: &Self) -> bool {
|
|
|
|
match (self, other) {
|
|
|
|
(
|
|
|
|
ValueData::Value {
|
|
|
|
inner: left_inner,
|
|
|
|
runtime_uses: left_runtime_uses,
|
|
|
|
},
|
|
|
|
ValueData::Value {
|
|
|
|
inner: right_inner,
|
|
|
|
runtime_uses: right_runtime_uses,
|
|
|
|
},
|
|
|
|
) => {
|
|
|
|
if left_inner != right_inner {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
*left_runtime_uses.read().unwrap() == *right_runtime_uses.read().unwrap()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
(
|
2024-02-15 08:57:13 +00:00
|
|
|
ValueData::TypeHint { inner: left_inner },
|
|
|
|
ValueData::TypeHint { inner: right_inner },
|
2024-02-12 20:48:43 +00:00
|
|
|
) => left_inner == right_inner,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialOrd for ValueData {
|
|
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
|
|
Some(self.cmp(other))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Ord for ValueData {
|
|
|
|
fn cmp(&self, other: &Self) -> Ordering {
|
|
|
|
use Ordering::*;
|
|
|
|
|
|
|
|
match (self, other) {
|
|
|
|
(
|
|
|
|
ValueData::Value {
|
|
|
|
inner: inner_left, ..
|
|
|
|
},
|
|
|
|
ValueData::Value {
|
|
|
|
inner: inner_right, ..
|
|
|
|
},
|
|
|
|
) => inner_left.cmp(inner_right),
|
|
|
|
(ValueData::Value { .. }, _) => Greater,
|
|
|
|
(
|
2024-02-15 08:57:13 +00:00
|
|
|
ValueData::TypeHint { inner: inner_left },
|
|
|
|
ValueData::TypeHint { inner: inner_right },
|
2024-02-12 20:48:43 +00:00
|
|
|
) => inner_left.cmp(inner_right),
|
2024-02-15 03:38:45 +00:00
|
|
|
(ValueData::TypeDefinition(left), ValueData::TypeDefinition(right)) => left.cmp(right),
|
|
|
|
(ValueData::TypeDefinition(_), _) => Greater,
|
2024-02-15 08:57:13 +00:00
|
|
|
(ValueData::TypeHint { .. }, _) => Less,
|
2024-02-12 20:48:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|