Add reference counting for map values

This commit is contained in:
Jeff 2023-10-26 16:00:06 -04:00
parent 86499367fc
commit a5390c5150
4 changed files with 97 additions and 25 deletions

View File

@ -1,4 +1,4 @@
//! Command line interface for the whale programming language. //! Command line interface for the dust programming language.
use clap::Parser; use clap::Parser;
use rustyline::{ use rustyline::{
completion::FilenameCompleter, completion::FilenameCompleter,
@ -17,10 +17,18 @@ use dust_lang::{evaluate, evaluate_with_context, Map, Value};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
struct Args { struct Args {
/// Whale source code to evaluate. /// Dust source code to evaluate.
#[arg(short, long)] #[arg(short, long)]
command: Option<String>, command: Option<String>,
/// Data to assign to the "input" variable.
#[arg(short, long)]
input: Option<String>,
/// A path to file whose contents will be assigned to the "input" variable.
#[arg(short = 'p', long)]
input_path: Option<String>,
/// Location of the file to run. /// Location of the file to run.
path: Option<String>, path: Option<String>,
} }
@ -32,12 +40,22 @@ fn main() {
return run_cli_shell(); return run_cli_shell();
} }
let mut context = Map::new();
if let Some(path) = args.input_path {
let file_contents = read_to_string(path).unwrap();
context
.set_value("input".to_string(), Value::String(file_contents))
.unwrap();
}
let eval_result = if let Some(path) = args.path { let eval_result = if let Some(path) = args.path {
let file_contents = read_to_string(path).unwrap(); let file_contents = read_to_string(path).unwrap();
evaluate(&file_contents) evaluate_with_context(&file_contents, &mut context)
} else if let Some(command) = args.command { } else if let Some(command) = args.command {
evaluate(&command) evaluate_with_context(&command, &mut context)
} else { } else {
Ok(Value::Empty) Ok(Value::Empty)
}; };

View File

@ -1,7 +1,12 @@
use rayon::collections::btree_map;
use reqwest::header::ACCESS_CONTROL_EXPOSE_HEADERS;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
cmp::Ordering,
collections::BTreeMap, collections::BTreeMap,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
marker::PhantomData,
sync::{Arc, RwLock, RwLockReadGuard},
}; };
use crate::{value::Value, Error, Result, Table}; use crate::{value::Value, Error, Result, Table};
@ -10,30 +15,32 @@ use crate::{value::Value, Error, Result, Table};
/// ///
/// The inner value is a BTreeMap in order to allow VariableMap instances to be sorted and compared /// The inner value is a BTreeMap in order to allow VariableMap instances to be sorted and compared
/// to one another. /// to one another.
#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Deserialize)] #[derive(Clone, Debug)]
pub struct Map { pub struct Map {
variables: BTreeMap<String, Value>, variables: Arc<RwLock<BTreeMap<String, Value>>>,
} }
impl Map { impl Map {
/// Creates a new instace. /// Creates a new instace.
pub fn new() -> Self { pub fn new() -> Self {
Map { Map {
variables: BTreeMap::new(), variables: Arc::new(RwLock::new(BTreeMap::new())),
} }
} }
/// Returns a Value assigned to the identifer, allowing dot notation to retrieve Values that are /// nested in Lists or Maps. Returns None if there is no variable with a key matching the /// identifier. Returns an error if a Map or List is indexed incorrectly. /// Returns a Value assigned to the identifer, allowing dot notation to retrieve Values that are /// nested in Lists or Maps. Returns None if there is no variable with a key matching the /// identifier. Returns an error if a Map or List is indexed incorrectly.
pub fn get_value(&self, identifier: &str) -> Result<Option<Value>> { pub fn get_value(&self, identifier: &str) -> Result<Option<Value>> {
let variables = self.variables.read().unwrap();
let split = identifier.rsplit_once('.'); let split = identifier.rsplit_once('.');
let (found_value, next_identifier) = if let Some((identifier, next_identifier)) = split { let (found_value, next_identifier) = if let Some((identifier, next_identifier)) = split {
if identifier.contains('.') { if identifier.contains('.') {
(self.get_value(identifier)?, next_identifier) (self.get_value(identifier)?, next_identifier)
} else { } else {
(self.variables.get(identifier).cloned(), next_identifier) (variables.get(identifier).cloned(), next_identifier)
} }
} else { } else {
return Ok(self.variables.get(identifier).cloned()); return Ok(variables.get(identifier).cloned());
}; };
if let Some(value) = found_value { if let Some(value) = found_value {
@ -63,7 +70,8 @@ impl Map {
let split = key.split_once('.'); let split = key.split_once('.');
if let Some((identifier, next_identifier)) = split { if let Some((identifier, next_identifier)) = split {
let get_value = self.variables.get_mut(identifier); let mut variables = self.variables.write().unwrap();
let get_value = variables.get_mut(identifier);
if let Some(found_value) = get_value { if let Some(found_value) = get_value {
if let Value::List(list) = found_value { if let Value::List(list) = found_value {
@ -97,12 +105,17 @@ impl Map {
new_map.set_value(next_identifier.to_string(), value)?; new_map.set_value(next_identifier.to_string(), value)?;
self.variables self.variables
.write()
.unwrap()
.insert(identifier.to_string(), Value::Map(new_map)); .insert(identifier.to_string(), Value::Map(new_map));
Ok(()) Ok(())
} }
} else { } else {
self.variables.insert(key.to_string(), value); self.variables
.write()
.unwrap()
.insert(key.to_string(), value);
Ok(()) Ok(())
} }
@ -112,22 +125,22 @@ impl Map {
/// ///
/// TODO: Support dot notation. /// TODO: Support dot notation.
pub fn remove(&mut self, key: &str) -> Option<Value> { pub fn remove(&mut self, key: &str) -> Option<Value> {
self.variables.remove(key) self.variables.write().unwrap().remove(key)
} }
/// Returns a reference to the inner BTreeMap. /// Returns a reference to the inner BTreeMap.
pub fn inner(&self) -> &BTreeMap<String, Value> { pub fn inner(&self) -> Arc<RwLock<BTreeMap<String, Value>>> {
&self.variables Arc::clone(&self.variables)
} }
/// Returns the number of stored variables. /// Returns the number of stored variables.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.variables.len() self.variables.read().unwrap().len()
} }
/// Returns true if the length is zero. /// Returns true if the length is zero.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.variables.is_empty() self.variables.read().unwrap().is_empty()
} }
} }
@ -137,10 +150,42 @@ impl Default for Map {
} }
} }
impl Eq for Map {}
impl PartialEq for Map {
fn eq(&self, other: &Self) -> bool {
let left = self.variables.read().unwrap().clone().into_iter();
let right = other.variables.read().unwrap().clone().into_iter();
left.eq(right)
}
}
impl Ord for Map {
fn cmp(&self, other: &Self) -> Ordering {
let left = self.variables.read().unwrap().clone().into_iter();
let right = other.variables.read().unwrap().clone().into_iter();
left.cmp(right)
}
}
impl PartialOrd for Map {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let left = self.variables.read().unwrap().clone().into_iter();
let right = other.variables.read().unwrap().clone().into_iter();
left.partial_cmp(right)
}
}
impl Display for Map { impl Display for Map {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{{\n")?; write!(f, "{{\n")?;
for (key, value) in &self.variables {
let variables = self.variables.read().unwrap().clone().into_iter();
for (key, value) in variables {
write!(f, " {key} = {value}\n")?; write!(f, " {key} = {value}\n")?;
} }
write!(f, "}}") write!(f, "}}")

View File

@ -282,8 +282,10 @@ impl From<&mut Vec<Value>> for Table {
impl From<Map> for Table { impl From<Map> for Table {
fn from(map: Map) -> Self { fn from(map: Map) -> Self {
let keys = map.inner().keys().cloned().collect(); let inner_map = map.inner();
let values = map.inner().values().cloned().collect(); let read_map = inner_map.read().unwrap();
let keys = read_map.keys().cloned().collect();
let values = read_map.values().cloned().collect();
let mut table = Table::new(keys); let mut table = Table::new(keys);
table table
@ -296,8 +298,10 @@ impl From<Map> for Table {
impl From<&Map> for Table { impl From<&Map> for Table {
fn from(map: &Map) -> Self { fn from(map: &Map) -> Self {
let keys = map.inner().keys().cloned().collect(); let inner_map = map.inner();
let values = map.inner().values().cloned().collect(); let read_map = inner_map.read().unwrap();
let keys = read_map.keys().cloned().collect();
let values = read_map.values().cloned().collect();
let mut table = Table::new(keys); let mut table = Table::new(keys);
table table
@ -310,8 +314,10 @@ impl From<&Map> for Table {
impl From<&mut Map> for Table { impl From<&mut Map> for Table {
fn from(map: &mut Map) -> Self { fn from(map: &mut Map) -> Self {
let keys = map.inner().keys().cloned().collect(); let inner_map = map.inner();
let values = map.inner().values().cloned().collect(); let read_map = inner_map.read().unwrap();
let keys = read_map.keys().cloned().collect();
let values = read_map.values().cloned().collect();
let mut table = Table::new(keys); let mut table = Table::new(keys);
table table

View File

@ -113,8 +113,11 @@ impl From<&Value> for ValueType {
Value::Map(map) => { Value::Map(map) => {
let mut value_nodes = BTreeMap::new(); let mut value_nodes = BTreeMap::new();
for (key, value) in map.inner() { let inner_map = map.inner().clone();
let value_type = ValueType::from(value); let read_map = inner_map.read().unwrap();
for (key, value) in read_map.clone() {
let value_type = value.value_type();
let value_node = ValueNode::new(value_type, 0, 0); let value_node = ValueNode::new(value_type, 0, 0);
let expression = Expression::Value(value_node); let expression = Expression::Value(value_node);