use crate::{Session, SJFLError};
use crate::parse::{Variable, List, InternedString, Table};
use std::collections::HashMap;
use hmath::Ratio;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Value {
None,
Boolean(bool),
String(InternedString),
Number(Ratio),
Variable(Variable),
List(List),
Table(Table),
}
impl Value {
pub(crate) fn substitute_vars(&mut self, session: &Session) -> Result<(), SJFLError> {
match self {
Value::List(l) => {
for e in l.data.iter_mut() {
e.substitute_vars(session)?;
}
}
Value::Table(t) => {
for (k, v) in t.data.iter_mut() {
k.substitute_vars(session)?;
v.substitute_vars(session)?;
}
}
Value::Variable(v) => {
match session.get_value_from_var_index(v.index) {
Some(v) => {
*self = v;
self.substitute_vars(session)?; }
_ => {
return Err(SJFLError::NotDefinedVar(v.clone()))
}
}
}
_ => {}
}
Ok(())
}
pub fn to_string(&self, session: &Session) -> String {
match self {
Value::None => format!("null"),
Value::Boolean(b) => format!("{b}"),
Value::String(s) => format!(
"{:?}",
match session.get_string_from_index(s.index) {
Some(s) => String::from_utf8_lossy(s).to_string(),
None => String::from("\"\"")
}
),
Value::Number(n) => format!("{}", n.to_approx_string(100)),
Value::List(l) => format!(
"[{}]",
l.data.iter().map(|e| e.to_string(session)).collect::<Vec<String>>().join(", ")
),
Value::Table(t) => format!(
"{}{}{}",
"{",
t.data.iter().map(
|(key, value)|
format!("{}: {}", key.to_string(session), value.to_string(session))
).collect::<Vec<String>>().join(", "),
"}"
),
Value::Variable(var) => format!(
"{}",
match session.get_string_from_index(var.index) {
Some(s) => String::from_utf8_lossy(s).to_string(),
None => String::from("UNKNOWN_VARIABLE")
}
),
}
}
pub fn is_none(&self) -> bool {
match self {
Value::None => true,
_ => false
}
}
pub fn is_bool(&self) -> bool {
match self {
Value::Boolean(_) => true,
_ => false
}
}
pub fn try_unwrap_bool(&self) -> Option<&bool> {
match self {
Value::Boolean(b) => Some(b),
_ => None
}
}
pub fn is_string(&self) -> bool {
match self {
Value::String(_) => true,
_ => false
}
}
pub fn is_valid_string(&self, session: &Session) -> bool {
match self {
Value::String(InternedString { index }) if session.is_valid_index(*index) => true,
_ => false
}
}
pub fn try_unwrap_string(&self, session: &Session) -> Option<String> {
match self {
Value::String(InternedString { index }) => match session.get_string_from_index(*index) {
Some(buf) => Some(String::from_utf8_lossy(buf).to_string()),
_ => None
},
_ => None
}
}
pub fn new_string(s: &str, session: &mut Session) -> Self {
let index = session.intern_string(s.as_bytes());
Value::String(InternedString { index })
}
pub fn is_number(&self) -> bool {
match self {
Value::Number(_) => true,
_ => false
}
}
pub fn try_unwrap_number(&self) -> Option<&Ratio> {
match self {
Value::Number(n) => Some(n),
_ => None
}
}
pub fn number_from_string(s: &str) -> Result<Self, hmath::ConversionError> {
let n = Ratio::from_string(s)?;
Ok(Value::Number(n))
}
pub fn is_list(&self) -> bool {
match self {
Value::List(_) => true,
_ => false
}
}
pub fn try_unwrap_list(&self) -> Option<&Vec<Box<Value>>> {
match self {
Value::List(l) => Some(&l.data),
_ => None
}
}
pub fn is_table(&self) -> bool {
match self {
Value::Table(_) => true,
_ => false
}
}
pub fn try_unwrap_table(&self) -> Option<&Vec<(Box<Value>, Box<Value>)>> {
match self {
Value::Table(t) => Some(&t.data),
_ => None
}
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(b)
}
}
impl<T: Into<Value>> From<Vec<T>> for Value {
fn from(v: Vec<T>) -> Self {
Value::List(List { data: v.into_iter().map(|v| Box::new(v.into())).collect() })
}
}
impl<K: Into<Value>, V: Into<Value>> From<HashMap<K, V>> for Value {
fn from(t: HashMap<K, V>) -> Self {
Value::Table(Table { data: t.into_iter().map(|(k, v)| (Box::new(k.into()), Box::new(v.into()))).collect() })
}
}
impl<T1: Into<Value>, T2: Into<Value>> From<(T1, T2)> for Value {
fn from(t: (T1, T2)) -> Self {
Value::List(List { data: vec![Box::new(t.0.into()), Box::new(t.1.into())] })
}
}
macro_rules! impl_into_number {
($t: ty) => (
impl From<$t> for Value {
fn from(n: $t) -> Self {
Value::Number(n.into())
}
}
);
($t: ty, $($u: ty), +) => (
impl_into_number!($t);
impl_into_number!($($u),+);
)
}
impl_into_number!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
impl From<f32> for Value {
fn from(n: f32) -> Self {
Value::Number(n.try_into().unwrap_or(Ratio::zero()))
}
}
impl From<f64> for Value {
fn from(n: f64) -> Self {
Value::Number(n.try_into().unwrap_or(Ratio::zero()))
}
}