Add reference counting for map values
This commit is contained in:
parent
86499367fc
commit
a5390c5150
26
src/main.rs
26
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 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)
|
||||||
};
|
};
|
||||||
|
@ -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, "}}")
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user