From a5390c51508a5f2ae6001f9549bf27467ec39f7a Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 26 Oct 2023 16:00:06 -0400 Subject: [PATCH] Add reference counting for map values --- src/main.rs | 26 ++++++++++++--- src/value/map.rs | 71 +++++++++++++++++++++++++++++++++-------- src/value/table.rs | 18 +++++++---- src/value/value_type.rs | 7 ++-- 4 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index 46d5adc..9c88860 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -//! Command line interface for the whale programming language. +//! Command line interface for the dust programming language. use clap::Parser; use rustyline::{ completion::FilenameCompleter, @@ -17,10 +17,18 @@ use dust_lang::{evaluate, evaluate_with_context, Map, Value}; #[derive(Parser, Debug)] #[command(author, version, about, long_about = None)] struct Args { - /// Whale source code to evaluate. + /// Dust source code to evaluate. #[arg(short, long)] command: Option, + /// Data to assign to the "input" variable. + #[arg(short, long)] + input: Option, + + /// A path to file whose contents will be assigned to the "input" variable. + #[arg(short = 'p', long)] + input_path: Option, + /// Location of the file to run. path: Option, } @@ -32,12 +40,22 @@ fn main() { 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 file_contents = read_to_string(path).unwrap(); - evaluate(&file_contents) + evaluate_with_context(&file_contents, &mut context) } else if let Some(command) = args.command { - evaluate(&command) + evaluate_with_context(&command, &mut context) } else { Ok(Value::Empty) }; diff --git a/src/value/map.rs b/src/value/map.rs index c64b148..168e786 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -1,7 +1,12 @@ +use rayon::collections::btree_map; +use reqwest::header::ACCESS_CONTROL_EXPOSE_HEADERS; use serde::{Deserialize, Serialize}; use std::{ + cmp::Ordering, collections::BTreeMap, fmt::{self, Display, Formatter}, + marker::PhantomData, + sync::{Arc, RwLock, RwLockReadGuard}, }; 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 /// to one another. -#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Eq, Deserialize)] +#[derive(Clone, Debug)] pub struct Map { - variables: BTreeMap, + variables: Arc>>, } impl Map { /// Creates a new instace. pub fn new() -> Self { 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. pub fn get_value(&self, identifier: &str) -> Result> { + let variables = self.variables.read().unwrap(); + let split = identifier.rsplit_once('.'); let (found_value, next_identifier) = if let Some((identifier, next_identifier)) = split { if identifier.contains('.') { (self.get_value(identifier)?, next_identifier) } else { - (self.variables.get(identifier).cloned(), next_identifier) + (variables.get(identifier).cloned(), next_identifier) } } else { - return Ok(self.variables.get(identifier).cloned()); + return Ok(variables.get(identifier).cloned()); }; if let Some(value) = found_value { @@ -63,7 +70,8 @@ impl Map { let split = key.split_once('.'); 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 Value::List(list) = found_value { @@ -97,12 +105,17 @@ impl Map { new_map.set_value(next_identifier.to_string(), value)?; self.variables + .write() + .unwrap() .insert(identifier.to_string(), Value::Map(new_map)); Ok(()) } } else { - self.variables.insert(key.to_string(), value); + self.variables + .write() + .unwrap() + .insert(key.to_string(), value); Ok(()) } @@ -112,22 +125,22 @@ impl Map { /// /// TODO: Support dot notation. pub fn remove(&mut self, key: &str) -> Option { - self.variables.remove(key) + self.variables.write().unwrap().remove(key) } /// Returns a reference to the inner BTreeMap. - pub fn inner(&self) -> &BTreeMap { - &self.variables + pub fn inner(&self) -> Arc>> { + Arc::clone(&self.variables) } /// Returns the number of stored variables. pub fn len(&self) -> usize { - self.variables.len() + self.variables.read().unwrap().len() } /// Returns true if the length is zero. 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 { + 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 { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 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, "}}") diff --git a/src/value/table.rs b/src/value/table.rs index 26f70af..917c6cd 100644 --- a/src/value/table.rs +++ b/src/value/table.rs @@ -282,8 +282,10 @@ impl From<&mut Vec> for Table { impl From for Table { fn from(map: Map) -> Self { - let keys = map.inner().keys().cloned().collect(); - let values = map.inner().values().cloned().collect(); + let inner_map = map.inner(); + 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); table @@ -296,8 +298,10 @@ impl From for Table { impl From<&Map> for Table { fn from(map: &Map) -> Self { - let keys = map.inner().keys().cloned().collect(); - let values = map.inner().values().cloned().collect(); + let inner_map = map.inner(); + 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); table @@ -310,8 +314,10 @@ impl From<&Map> for Table { impl From<&mut Map> for Table { fn from(map: &mut Map) -> Self { - let keys = map.inner().keys().cloned().collect(); - let values = map.inner().values().cloned().collect(); + let inner_map = map.inner(); + 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); table diff --git a/src/value/value_type.rs b/src/value/value_type.rs index 92d66e8..4b88631 100644 --- a/src/value/value_type.rs +++ b/src/value/value_type.rs @@ -113,8 +113,11 @@ impl From<&Value> for ValueType { Value::Map(map) => { let mut value_nodes = BTreeMap::new(); - for (key, value) in map.inner() { - let value_type = ValueType::from(value); + let inner_map = map.inner().clone(); + 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 expression = Expression::Value(value_node);