use crate::dict::dict_to_string;
use crate::dict::list_to_dict;
use crate::expr::Datum;
use crate::list::get_list;
use crate::list::list_to_string;
use crate::parser;
use crate::parser::Script;
use crate::types::Exception;
use crate::types::MoltDict;
use crate::types::MoltFloat;
use crate::types::MoltInt;
use crate::types::MoltList;
use crate::types::VarName;
use std::any::Any;
use std::any::TypeId;
use std::cell::RefCell;
use std::cell::UnsafeCell;
use std::fmt::Debug;
use std::fmt::Display;
use std::hash::Hash;
use std::hash::Hasher;
use std::rc::Rc;
use std::str::FromStr;
#[derive(Clone)]
pub struct Value {
inner: Rc<InnerValue>,
}
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
#[derive(Debug)]
struct InnerValue {
string_rep: UnsafeCell<Option<String>>,
data_rep: RefCell<DataRep>,
}
impl std::fmt::Debug for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Value[{}]", self.as_str())
}
}
impl Value {
fn inner_from_string(str: String) -> Self {
let inner = InnerValue {
string_rep: UnsafeCell::new(Some(str)),
data_rep: RefCell::new(DataRep::None),
};
Self {
inner: Rc::new(inner),
}
}
fn inner_from_data(data: DataRep) -> Self {
let inner = InnerValue {
string_rep: UnsafeCell::new(None),
data_rep: RefCell::new(data),
};
Self {
inner: Rc::new(inner),
}
}
}
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Eq for Value {}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl From<String> for Value {
fn from(str: String) -> Self {
Value::inner_from_string(str)
}
}
impl From<&str> for Value {
fn from(str: &str) -> Self {
Value::inner_from_string(str.to_string())
}
}
impl From<&String> for Value {
fn from(str: &String) -> Self {
Value::inner_from_string(str.to_string())
}
}
impl From<bool> for Value {
fn from(flag: bool) -> Self {
Value::inner_from_data(DataRep::Bool(flag))
}
}
impl From<MoltDict> for Value {
fn from(dict: MoltDict) -> Self {
Value::inner_from_data(DataRep::Dict(Rc::new(dict)))
}
}
impl From<MoltInt> for Value {
fn from(int: MoltInt) -> Self {
Value::inner_from_data(DataRep::Int(int))
}
}
impl From<MoltFloat> for Value {
fn from(flt: MoltFloat) -> Self {
Value::inner_from_data(DataRep::Flt(flt))
}
}
impl From<MoltList> for Value {
fn from(list: MoltList) -> Self {
Value::inner_from_data(DataRep::List(Rc::new(list)))
}
}
impl From<&[Value]> for Value {
fn from(list: &[Value]) -> Self {
Value::inner_from_data(DataRep::List(Rc::new(list.to_vec())))
}
}
impl Value {
pub fn empty() -> Value {
Value::inner_from_string("".into())
}
pub fn as_str(&self) -> &str {
let slot = unsafe { &*self.inner.string_rep.get() };
if let Some(inner) = slot {
return inner;
}
let slot = unsafe { &mut *self.inner.string_rep.get() };
*slot = Some((self.inner.data_rep.borrow()).to_string());
slot.as_ref().expect("string rep")
}
pub fn try_as_str(&self) -> Option<&str> {
unsafe { &*self.inner.string_rep.get() }.as_ref().map(|x| x.as_ref())
}
pub fn as_bool(&self) -> Result<bool, Exception> {
{
let data_ref = self.inner.data_rep.borrow();
if let DataRep::Bool(flag) = *data_ref {
return Ok(flag);
}
if let DataRep::Int(int) = *data_ref {
return Ok(int != 0);
}
if let DataRep::Flt(flt) = *data_ref {
return Ok(flt != 0.0);
}
}
let str = self.as_str();
let flag = Value::get_bool(str)?;
*(self.inner.data_rep.borrow_mut()) = DataRep::Bool(flag);
Ok(flag)
}
pub fn get_bool(arg: &str) -> Result<bool, Exception> {
let orig = arg;
let value: &str = &arg.trim().to_lowercase();
match value {
"1" | "true" | "yes" | "on" => Ok(true),
"0" | "false" | "no" | "off" => Ok(false),
_ => molt_err!("expected boolean but got \"{}\"", orig),
}
}
pub fn as_dict(&self) -> Result<Rc<MoltDict>, Exception> {
if let DataRep::Dict(dict) = &*self.inner.data_rep.borrow() {
return Ok(dict.clone());
}
let str = self.as_str();
let list = get_list(str)?;
if list.len() % 2 != 0 {
return molt_err!("missing value to go with key");
}
let dict = Rc::new(list_to_dict(&list));
*self.inner.data_rep.borrow_mut() = DataRep::Dict(dict.clone());
Ok(dict)
}
pub fn to_dict(&self) -> Result<MoltDict, Exception> {
Ok((&*self.as_dict()?).to_owned())
}
pub fn as_int(&self) -> Result<MoltInt, Exception> {
if let DataRep::Int(int) = *self.inner.data_rep.borrow() {
return Ok(int);
}
let str = self.as_str();
let int = Value::get_int(str)?;
*self.inner.data_rep.borrow_mut() = DataRep::Int(int);
Ok(int)
}
pub fn get_int(arg: &str) -> Result<MoltInt, Exception> {
let orig = arg;
let mut arg = arg.trim();
let mut minus = 1;
if arg.starts_with('+') {
arg = &arg[1..];
} else if arg.starts_with('-') {
minus = -1;
arg = &arg[1..];
}
let parse_result = if arg.starts_with("0x") {
MoltInt::from_str_radix(&arg[2..], 16)
} else {
arg.parse::<MoltInt>()
};
match parse_result {
Ok(int) => Ok(minus * int),
Err(_) => molt_err!("expected integer but got \"{}\"", orig),
}
}
pub fn as_float(&self) -> Result<MoltFloat, Exception> {
if let DataRep::Flt(flt) = *self.inner.data_rep.borrow() {
return Ok(flt);
}
let str = self.as_str();
let flt = Value::get_float(str)?;
*self.inner.data_rep.borrow_mut() = DataRep::Flt(flt);
Ok(flt)
}
pub fn get_float(arg: &str) -> Result<MoltFloat, Exception> {
let arg_trim = arg.trim().to_lowercase();
match arg_trim.parse::<MoltFloat>() {
Ok(flt) => Ok(flt),
Err(_) => molt_err!("expected floating-point number but got \"{}\"", arg),
}
}
fn fmt_float(f: &mut std::fmt::Formatter, flt: MoltFloat) -> std::fmt::Result {
if flt == std::f64::INFINITY {
write!(f, "Inf")
} else if flt == std::f64::NEG_INFINITY {
write!(f, "-Inf")
} else if flt.is_nan() {
write!(f, "NaN")
} else {
write!(f, "{}", flt)
}
}
pub fn as_list(&self) -> Result<Rc<MoltList>, Exception> {
if let DataRep::List(list) = &*self.inner.data_rep.borrow() {
return Ok(list.clone());
}
let str = self.as_str();
let list = Rc::new(get_list(str)?);
*self.inner.data_rep.borrow_mut() = DataRep::List(list.clone());
Ok(list)
}
pub fn to_list(&self) -> Result<MoltList, Exception> {
Ok((&*self.as_list()?).to_owned())
}
pub(crate) fn as_script(&self) -> Result<Rc<Script>, Exception> {
if let DataRep::Script(script) = &*self.inner.data_rep.borrow() {
return Ok(script.clone());
}
let str = self.as_str();
let script = Rc::new(parser::parse(str)?);
*self.inner.data_rep.borrow_mut() = DataRep::Script(script.clone());
Ok(script)
}
pub fn as_var_name(&self) -> Rc<VarName> {
if let DataRep::VarName(var_name) = &*self.inner.data_rep.borrow() {
return var_name.clone();
}
let var_name = Rc::new(parser::parse_varname_literal(self.as_str()));
*self.inner.data_rep.borrow_mut() = DataRep::VarName(var_name.clone());
var_name
}
pub fn from_other<T: 'static>(value: T) -> Value
where
T: Display + Debug,
{
Value::inner_from_data(DataRep::Other(Rc::new(value)))
}
pub fn as_other<T: 'static>(&self) -> Option<Rc<T>>
where
T: Display + Debug + FromStr,
{
if let DataRep::Other(other) = &*self.inner.data_rep.borrow() {
if let Ok(out) = other.clone().downcast::<T>() {
return Some(out);
}
}
let str = self.as_str();
if let Ok(tval) = str.parse::<T>() {
let tval = Rc::new(tval);
let out = tval.clone();
*self.inner.data_rep.borrow_mut() = DataRep::Other(Rc::new(tval));
return Some(out);
}
None
}
pub fn as_copy<T: 'static>(&self) -> Option<T>
where
T: Display + Debug + FromStr + Copy,
{
if let DataRep::Other(other) = &*self.inner.data_rep.borrow() {
if let Ok(out) = other.clone().downcast::<T>() {
return Some(*out);
}
}
let str = self.as_str();
if let Ok(tval) = str.parse::<T>() {
let tval = Rc::new(tval);
let out = tval.clone();
*self.inner.data_rep.borrow_mut() = DataRep::Other(Rc::new(tval));
return Some(*out);
}
None
}
pub(crate) fn already_number(&self) -> Option<Datum> {
let iref = self.inner.data_rep.borrow();
match *iref {
DataRep::Flt(flt) => Some(Datum::float(flt)),
DataRep::Int(int) => Some(Datum::int(int)),
_ => None,
}
}
}
trait MoltAny: Any + Display + Debug {
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
}
impl dyn MoltAny {
pub fn is<T: 'static>(&self) -> bool {
TypeId::of::<T>() == self.type_id()
}
fn downcast<T: 'static>(self: Rc<Self>) -> Result<Rc<T>, Rc<Self>> {
if self.is::<T>() {
unsafe { Ok(Rc::from_raw(Rc::into_raw(self) as _)) }
} else {
Err(self)
}
}
}
impl<T: Any + Display + Debug> MoltAny for T {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
#[derive(Clone, Debug)]
enum DataRep {
Bool(bool),
Dict(Rc<MoltDict>),
Int(MoltInt),
Flt(MoltFloat),
List(Rc<MoltList>),
Script(Rc<Script>),
VarName(Rc<VarName>),
Other(Rc<dyn MoltAny>),
None,
}
impl Display for DataRep {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
DataRep::Bool(flag) => write!(f, "{}", if *flag { 1 } else { 0 }),
DataRep::Dict(dict) => write!(f, "{}", dict_to_string(&*dict)),
DataRep::Int(int) => write!(f, "{}", int),
DataRep::Flt(flt) => Value::fmt_float(f, *flt),
DataRep::List(list) => write!(f, "{}", list_to_string(&*list)),
DataRep::Script(script) => write!(f, "{:?}", script),
DataRep::VarName(var_name) => write!(f, "{:?}", var_name),
DataRep::Other(other) => write!(f, "{}", other),
DataRep::None => write!(f, ""),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dict::dict_new;
use std::fmt;
use std::str::FromStr;
#[test]
fn from_string() {
let val = Value::from("xyz".to_string());
assert_eq!(&val.to_string(), "xyz");
let val: Value = String::from("Fred").into();
assert_eq!(&val.to_string(), "Fred");
}
#[test]
fn from_str_ref() {
let val = Value::from("xyz");
assert_eq!(&val.to_string(), "xyz");
let val: Value = "Fred".into();
assert_eq!(&val.to_string(), "Fred");
}
#[test]
fn clone_string() {
let val = Value::from("abc");
let val2 = val.clone();
assert_eq!(*val.to_string(), *val2.to_string());
}
#[test]
fn as_str() {
let val = Value::from("abc");
assert_eq!(val.as_str(), "abc");
let val2 = val.clone();
assert_eq!(val.as_str(), val2.as_str());
}
#[test]
fn compare() {
let val = Value::from("123");
let val2 = Value::from(123);
let val3 = Value::from(456);
assert_eq!(val, val2);
assert_ne!(val, val3);
}
#[test]
fn from_bool() {
let val = Value::from(true);
assert_eq!(&val.to_string(), "1");
let val = Value::from(false);
assert_eq!(&val.to_string(), "0");
}
#[test]
fn as_bool() {
let val = Value::from("true");
assert_eq!(val.as_bool(), Ok(true));
let val = Value::from(5);
assert_eq!(val.as_bool(), Ok(true));
let val = Value::from(0);
assert_eq!(val.as_bool(), Ok(false));
let val = Value::from(5.5);
assert_eq!(val.as_bool(), Ok(true));
let val = Value::from(0.0);
assert_eq!(val.as_bool(), Ok(false));
}
#[test]
fn get_bool() {
assert_eq!(Ok(true), Value::get_bool("1"));
assert_eq!(Ok(true), Value::get_bool("true"));
assert_eq!(Ok(true), Value::get_bool("yes"));
assert_eq!(Ok(true), Value::get_bool("on"));
assert_eq!(Ok(true), Value::get_bool("TRUE"));
assert_eq!(Ok(true), Value::get_bool("YES"));
assert_eq!(Ok(true), Value::get_bool("ON"));
assert_eq!(Ok(false), Value::get_bool("0"));
assert_eq!(Ok(false), Value::get_bool("false"));
assert_eq!(Ok(false), Value::get_bool("no"));
assert_eq!(Ok(false), Value::get_bool("off"));
assert_eq!(Ok(false), Value::get_bool("FALSE"));
assert_eq!(Ok(false), Value::get_bool("NO"));
assert_eq!(Ok(false), Value::get_bool("OFF"));
assert_eq!(Ok(true), Value::get_bool(" true "));
assert_eq!(
Value::get_bool("nonesuch"),
molt_err!("expected boolean but got \"nonesuch\"")
);
assert_eq!(
Value::get_bool(" Nonesuch "),
molt_err!("expected boolean but got \" Nonesuch \"")
);
}
#[test]
fn from_as_dict() {
let mut dict = dict_new();
dict.insert(Value::from("abc"), Value::from("def"));
let dictval = Value::from(dict);
assert_eq!(dictval.as_str(), "abc def");
let dictval = Value::from("qrs xyz");
let result = dictval.as_dict();
assert!(result.is_ok());
if let Ok(rcdict) = result {
assert_eq!(rcdict.len(), 1);
assert_eq!(rcdict.get(&Value::from("qrs")), Some(&Value::from("xyz")));
}
}
#[test]
fn to_dict() {
let dictval = Value::from("qrs xyz");
let result = dictval.to_dict();
assert!(result.is_ok());
if let Ok(dict) = result {
assert_eq!(dict.len(), 1);
assert_eq!(dict.get(&Value::from("qrs")), Some(&Value::from("xyz")));
}
}
#[test]
fn from_as_int() {
let val = Value::from(5);
assert_eq!(val.as_str(), "5");
assert_eq!(val.as_int(), Ok(5));
assert_eq!(val.as_float(), Ok(5.0));
let val = Value::from("7");
assert_eq!(val.as_str(), "7");
assert_eq!(val.as_int(), Ok(7));
assert_eq!(val.as_float(), Ok(7.0));
let val = Value::from(7.0);
assert_eq!(val.as_str(), "7");
assert_eq!(val.as_int(), Ok(7));
assert_eq!(val.as_float(), Ok(7.0));
let val = Value::from("abc");
assert_eq!(val.as_int(), molt_err!("expected integer but got \"abc\""));
}
#[test]
fn get_int() {
assert_eq!(Value::get_int("1"), Ok(1));
assert_eq!(Value::get_int("-1"), Ok(-1));
assert_eq!(Value::get_int("+1"), Ok(1));
assert_eq!(Value::get_int("0xFF"), Ok(255));
assert_eq!(Value::get_int("+0xFF"), Ok(255));
assert_eq!(Value::get_int("-0xFF"), Ok(-255));
assert_eq!(Value::get_int(" 1 "), Ok(1));
assert_eq!(
Value::get_int(""),
molt_err!("expected integer but got \"\"")
);
assert_eq!(
Value::get_int("a"),
molt_err!("expected integer but got \"a\"")
);
assert_eq!(
Value::get_int("0x"),
molt_err!("expected integer but got \"0x\"")
);
assert_eq!(
Value::get_int("0xABGG"),
molt_err!("expected integer but got \"0xABGG\"")
);
assert_eq!(
Value::get_int(" abc "),
molt_err!("expected integer but got \" abc \"")
);
}
#[test]
fn from_as_float() {
let val = Value::from(12.5);
assert_eq!(val.as_str(), "12.5");
assert_eq!(val.as_int(), molt_err!("expected integer but got \"12.5\""));
assert_eq!(val.as_float(), Ok(12.5));
let val = Value::from("7.8");
assert_eq!(val.as_str(), "7.8");
assert_eq!(val.as_int(), molt_err!("expected integer but got \"7.8\""));
assert_eq!(val.as_float(), Ok(7.8));
let val = Value::from(5);
assert_eq!(val.as_float(), Ok(5.0));
let val = Value::from("abc");
assert_eq!(
val.as_float(),
molt_err!("expected floating-point number but got \"abc\"")
);
}
#[test]
fn get_float() {
assert_eq!(Value::get_float("1"), Ok(1.0));
assert_eq!(Value::get_float("2.3"), Ok(2.3));
assert_eq!(Value::get_float(" 4.5 "), Ok(4.5));
assert_eq!(Value::get_float("Inf"), Ok(std::f64::INFINITY));
assert_eq!(
Value::get_float("abc"),
molt_err!("expected floating-point number but got \"abc\"")
);
assert_eq!(
Value::get_float(" abc "),
molt_err!("expected floating-point number but got \" abc \"")
);
}
#[test]
fn from_as_list() {
let listval = Value::from(vec![Value::from("abc"), Value::from("def")]);
assert_eq!(listval.as_str(), "abc def");
let listval = Value::from("qrs xyz");
let result = listval.as_list();
assert!(result.is_ok());
if let Ok(rclist) = result {
assert_eq!(rclist.len(), 2);
assert_eq!(rclist[0].to_string(), "qrs".to_string());
assert_eq!(rclist[1].to_string(), "xyz".to_string());
}
}
#[test]
fn to_list() {
let listval = Value::from(vec![Value::from("abc"), Value::from("def")]);
let result = listval.to_list();
assert!(result.is_ok());
let list: MoltList = result.expect("an owned list");
assert_eq!(list.len(), 2);
assert_eq!(list[0].to_string(), "abc".to_string());
assert_eq!(list[1].to_string(), "def".to_string());
}
#[test]
fn as_script() {
let val = Value::from("a");
assert!(val.as_script().is_ok());
let val = Value::from("a {b");
assert_eq!(val.as_script(), molt_err!("missing close-brace"));
}
#[test]
fn as_var_name() {
let val = Value::from("a");
assert_eq!(val.as_var_name().name(), "a");
assert_eq!(val.as_var_name().index(), None);
let val = Value::from("a(b)");
assert_eq!(val.as_var_name().name(), "a");
assert_eq!(val.as_var_name().index(), Some("b"));
}
#[test]
fn from_value_slice() {
let array = [Value::from("abc"), Value::from("def")];
let listval = Value::from(&array[..]);
assert_eq!(listval.as_str(), "abc def");
}
#[test]
fn from_to_flavor() {
let myval = Value::from_other(Flavor::SALTY);
let result = myval.as_other::<Flavor>();
assert!(result.is_some());
let out = result.unwrap();
assert_eq!(*out, Flavor::SALTY);
let myval = Value::from("sweet");
let result = myval.as_other::<Flavor>();
assert!(result.is_some());
let out = result.unwrap();
assert_eq!(*out, Flavor::SWEET);
let myval = Value::from_other(Flavor::SALTY);
let result = myval.as_copy::<Flavor>();
assert!(result.is_some());
let out = result.unwrap();
assert_eq!(out, Flavor::SALTY);
}
#[test]
fn already_number() {
let value = Value::from(123);
let out = value.already_number();
assert!(out.is_some());
assert_eq!(out.unwrap(), Datum::int(123));
let value = Value::from(45.6);
let out = value.already_number();
assert!(out.is_some());
assert_eq!(out.unwrap(), Datum::float(45.6));
let value = Value::from("123");
assert!(value.already_number().is_none());
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Flavor {
SALTY,
SWEET,
}
impl FromStr for Flavor {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let value = value.to_lowercase();
if value == "salty" {
Ok(Flavor::SALTY)
} else if value == "sweet" {
Ok(Flavor::SWEET)
} else {
Err("Not a flavor string".to_string())
}
}
}
impl fmt::Display for Flavor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if *self == Flavor::SALTY {
write!(f, "salty")
} else {
write!(f, "sweet")
}
}
}
}