use crate::types::Exception;
use crate::types::MoltList;
use crate::value::Value;
use std::collections::HashMap;
use std::fmt::Debug;
#[derive(Eq, PartialEq)]
enum Var {
Scalar(Value),
Array(HashMap<String, Value>),
Upvar(usize),
New,
}
impl Var {
fn is_upvar(&self) -> bool {
if let Var::Upvar(_) = self {
true
} else {
false
}
}
}
impl Debug for Var {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Var::Scalar(value) => write!(f, "Var::Scalar({})", value.as_str()),
Var::Array(_) => write!(f, "Var::Array(TODO)"),
Var::Upvar(level) => write!(f, "Var::Upvar({})", level),
Var::New => write!(f, "Var::New"),
}
}
}
#[derive(Default, Debug)]
struct Scope {
map: HashMap<String, Var>,
}
impl Scope {
pub fn new() -> Self {
Scope {
map: HashMap::new(),
}
}
}
#[derive(Default, Debug)]
pub(crate) struct ScopeStack {
stack: Vec<Scope>,
}
impl ScopeStack {
pub fn new() -> Self {
let mut ss = Self { stack: Vec::new() };
ss.stack.push(Scope::new());
ss
}
pub fn get(&self, name: &str) -> Result<Value, Exception> {
match self.var(self.current(), name) {
Some(Var::Scalar(value)) => Ok(value.clone()),
Some(Var::Array(_)) => molt_err!("can't read \"{}\": variable is array", name),
Some(_) => unreachable!(),
None => molt_err!("can't read \"{}\": no such variable", name),
}
}
pub fn get_elem(&self, name: &str, index: &str) -> Result<Value, Exception> {
match self.var(self.current(), name) {
Some(Var::Scalar(_)) => {
molt_err!("can't read \"{}({})\": variable isn't array", name, index)
}
Some(Var::Array(map)) => {
if let Some(val) = map.get(index) {
Ok(val.clone())
} else {
molt_err!(
"can't read \"{}({})\": no such element in array",
name,
index
)
}
}
Some(_) => unreachable!(),
None => molt_err!("can't read \"{}\": no such variable", name),
}
}
pub fn set_global(&mut self, name: &str, val: Value) -> Result<(), Exception> {
match self.var_mut(0, name) {
Some(Var::Upvar(_)) => unreachable!(),
Some(Var::Array(_)) => molt_err!("can't set \"{}\": variable is array", name),
Some(var) => {
*var = Var::Scalar(val);
Ok(())
}
None => unreachable!(),
}
}
pub fn set(&mut self, name: &str, val: Value) -> Result<(), Exception> {
match self.var_mut(self.current(), name) {
Some(Var::Upvar(_)) => unreachable!(),
Some(Var::Array(_)) => molt_err!("can't set \"{}\": variable is array", name),
Some(var) => {
*var = Var::Scalar(val);
Ok(())
}
None => unreachable!(),
}
}
pub fn set_elem(&mut self, name: &str, index: &str, val: Value) -> Result<(), Exception> {
let top = self.current();
match self.var_mut(top, name) {
Some(Var::Upvar(_)) => unreachable!(),
Some(Var::Scalar(_)) => {
molt_err!("can't set \"{}({})\": variable isn't array", name, index)
}
Some(Var::Array(map)) => {
map.insert(index.into(), val);
Ok(())
}
Some(var) => {
assert_eq!(*var, Var::New);
let mut map = HashMap::new();
map.insert(index.into(), val);
*var = Var::Array(map);
Ok(())
}
None => unreachable!(),
}
}
pub fn exists(&self, name: &str) -> bool {
self.var(self.current(), name).is_some()
}
pub fn elem_exists(&self, name: &str, index: &str) -> bool {
self.get_elem(name, index).is_ok()
}
pub fn unset(&mut self, name: &str) {
self.unset_at(self.current(), name, false);
}
fn unset_at(&mut self, level: usize, name: &str, array_only: bool) {
if let Some(Var::Upvar(at)) = self.stack[level].map.get(name) {
let true_level = *at;
self.unset_at(true_level, name, array_only);
}
if array_only {
if let Some(Var::Array(_)) = self.stack[level].map.get(name) {
self.stack[level].map.remove(name);
}
} else {
self.stack[level].map.remove(name);
}
}
pub fn upvar(&mut self, level: usize, name: &str) {
assert!(level < self.current(), "Can't upvar to current stack level");
let top = self.current();
self.stack[top].map.insert(name.into(), Var::Upvar(level));
}
pub fn current(&self) -> usize {
self.stack.len() - 1
}
pub fn push(&mut self) {
self.stack.push(Scope::new());
}
pub fn pop(&mut self) {
self.stack.pop();
assert!(!self.stack.is_empty(), "Popped global scope!");
}
pub fn vars_in_scope(&self) -> MoltList {
self.stack[self.current()]
.map
.keys()
.cloned()
.map(|x| Value::from(&x))
.collect()
}
pub fn vars_in_local_scope(&self) -> MoltList {
if self.current() == 0 {
return Vec::new();
}
self.stack[self.current()]
.map
.iter()
.filter(|(_, v)| !v.is_upvar())
.map(|(k, _)| Value::from(k))
.collect()
}
pub fn vars_in_global_scope(&self) -> MoltList {
self.stack[0]
.map
.keys()
.cloned()
.map(|x| Value::from(&x))
.collect()
}
pub fn array_exists(&self, name: &str) -> bool {
match self.var(self.current(), name) {
Some(Var::Array(_)) => true,
_ => false,
}
}
pub fn array_indices(&self, name: &str) -> MoltList {
match self.var(self.current(), name) {
Some(Var::Array(map)) => map.keys().cloned().map(|x| Value::from(&x)).collect(),
_ => Vec::new(),
}
}
pub fn array_size(&self, name: &str) -> usize {
match self.var(self.current(), name) {
Some(Var::Array(map)) => map.len(),
_ => 0,
}
}
pub fn array_get(&self, name: &str) -> MoltList {
match self.var(self.current(), name) {
Some(Var::Array(map)) => {
let mut list = Vec::new();
for (key, value) in map {
list.push(Value::from(key));
list.push(value.clone());
}
list
}
_ => Vec::new(),
}
}
pub fn unset_element(&mut self, name: &str, index: &str) {
if let Some(Var::Array(map)) = self.var_mut(self.current(), name) {
map.remove(index);
}
}
pub fn array_set(&mut self, name: &str, kvlist: &[Value]) -> Result<(), Exception> {
assert!(kvlist.len() % 2 == 0);
match self.var_mut(self.current(), name) {
Some(Var::Upvar(_)) => unreachable!(),
Some(Var::Scalar(_)) => molt_err!("can't array set \"{}\": variable isn't array", name),
Some(Var::Array(map)) => {
insert_kvlist(map, &kvlist);
Ok(())
}
Some(var) => {
assert_eq!(*var, Var::New);
let mut map = HashMap::new();
insert_kvlist(&mut map, &kvlist);
*var = Var::Array(map);
Ok(())
}
None => unreachable!(),
}
}
pub fn array_unset(&mut self, name: &str) {
self.unset_at(self.current(), name, true);
}
fn var(&self, level: usize, name: &str) -> Option<&Var> {
let var = self.stack[level].map.get(name);
if let Some(Var::Upvar(at)) = var {
self.var(*at, name)
} else {
var
}
}
fn var_mut(&mut self, level: usize, name: &str) -> Option<&mut Var> {
let var = self.stack[level].map.entry(name.into()).or_insert(Var::New);
let var: Option<&mut Var> = unsafe { ::core::mem::transmute(var) };
if let Some(Var::Upvar(at)) = var {
self.var_mut(*at, name)
} else {
var
}
}
}
fn insert_kvlist(map: &mut HashMap<String, Value>, list: &[Value]) {
for kv in list.chunks(2) {
map.insert(kv[0].as_str().into(), kv[1].clone());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let ss = ScopeStack::new();
assert_eq!(ss.stack.len(), 1);
assert_eq!(ss.current(), 0);
}
#[test]
fn test_set_get_basic() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", Value::from("1"));
let out = ss.get("a");
assert_eq!(out.unwrap().as_str(), "1");
assert_eq!(ss.get("b"), molt_err!("can't read \"b\": no such variable"));
let _ = ss.set_elem("c", "1", "one".into());
assert_eq!(
ss.get("c"),
molt_err!("can't read \"c\": variable is array")
);
}
#[test]
fn test_set_get_global() {
let mut ss = ScopeStack::new();
let _ = ss.set_global("a", Value::from("1"));
let out = ss.get("a");
assert_eq!(out.unwrap().as_str(), "1");
ss.push();
let _ = ss.set_global("a", Value::from("2"));
ss.pop();
let out = ss.get("a");
assert_eq!(out.unwrap().as_str(), "2");
}
#[test]
fn test_set_get_elem() {
let mut ss = ScopeStack::new();
let _ = ss.set_elem("a", "1", Value::from("one"));
let out = ss.get_elem("a", "1");
assert_eq!(out.unwrap().as_str(), "one");
assert_eq!(
ss.get_elem("b", "1"),
molt_err!("can't read \"b\": no such variable")
);
let _ = ss.set_elem("c", "1", "one".into());
assert_eq!(
ss.get_elem("c", "2"),
molt_err!("can't read \"c(2)\": no such element in array")
);
let _ = ss.set("d", "".into());
assert_eq!(
ss.get_elem("d", "1"),
molt_err!("can't read \"d(1)\": variable isn't array")
);
}
#[test]
fn test_unset_basic() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", Value::from("1"));
assert!(ss.get("a").is_ok());
ss.unset("a");
assert!(ss.get("a").is_err());
}
#[test]
fn test_push() {
let mut ss = ScopeStack::new();
ss.push();
assert_eq!(ss.stack.len(), 2);
ss.push();
assert_eq!(ss.stack.len(), 3);
}
#[test]
fn test_pop() {
let mut ss = ScopeStack::new();
ss.push();
ss.push();
assert_eq!(ss.stack.len(), 3);
ss.pop();
assert_eq!(ss.stack.len(), 2);
ss.pop();
assert_eq!(ss.stack.len(), 1);
}
#[test]
#[should_panic]
fn test_pop_global_scope() {
let mut ss = ScopeStack::new();
assert_eq!(ss.stack.len(), 1);
ss.pop();
}
#[test]
fn test_current() {
let mut ss = ScopeStack::new();
assert_eq!(ss.current(), 0);
ss.push();
assert_eq!(ss.current(), 1);
ss.push();
assert_eq!(ss.current(), 2);
ss.pop();
assert_eq!(ss.current(), 1);
ss.pop();
assert_eq!(ss.current(), 0);
}
#[test]
fn test_set_levels() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", Value::from("1"));
let _ = ss.set("b", Value::from("2"));
ss.push();
assert!(ss.get("a").is_err());
assert!(ss.get("b").is_err());
assert!(ss.get("c").is_err());
let _ = ss.set("a", Value::from("3"));
let _ = ss.set("b", Value::from("4"));
let _ = ss.set("c", Value::from("5"));
assert_eq!(ss.get("a").unwrap().as_str(), "3");
assert_eq!(ss.get("b").unwrap().as_str(), "4");
assert_eq!(ss.get("c").unwrap().as_str(), "5");
ss.pop();
assert_eq!(ss.get("a").unwrap().as_str(), "1");
assert_eq!(ss.get("b").unwrap().as_str(), "2");
assert!(ss.get("c").is_err());
}
#[test]
fn test_set_get_upvar() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", Value::from("1"));
let _ = ss.set("b", Value::from("2"));
ss.push();
ss.upvar(0, "a");
assert_eq!(ss.get("a").unwrap().as_str(), "1");
assert!(ss.get("b").is_err());
let _ = ss.set("a", Value::from("3"));
let _ = ss.set("b", Value::from("4"));
assert_eq!(ss.get("a").unwrap().as_str(), "3");
assert_eq!(ss.get("b").unwrap().as_str(), "4");
ss.pop();
assert_eq!(ss.get("a").unwrap().as_str(), "3");
assert_eq!(ss.get("b").unwrap().as_str(), "2");
}
#[test]
fn test_unset_levels() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", Value::from("1"));
let _ = ss.set("b", Value::from("2"));
ss.push();
let _ = ss.set("a", Value::from("3"));
ss.unset("a"); ss.unset("b");
ss.pop();
assert_eq!(ss.get("a").unwrap().as_str(), "1");
assert_eq!(ss.get("b").unwrap().as_str(), "2");
}
#[test]
fn test_unset_upvar() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", Value::from("1"));
assert!(ss.get("a").is_ok());
ss.push();
assert!(ss.get("a").is_err());
ss.upvar(0, "a");
assert!(ss.get("a").is_ok());
ss.unset("a");
assert!(ss.get("a").is_err());
ss.pop();
assert!(ss.get("a").is_err());
}
#[test]
fn test_vars_in_scope() {
let mut ss = ScopeStack::new();
assert_eq!(ss.vars_in_scope().len(), 0);
let _ = ss.set("a", Value::from("1"));
let _ = ss.set("b", Value::from("2"));
assert_eq!(ss.vars_in_scope().len(), 2);
assert!(ss.vars_in_scope().contains(&Value::from("a")));
assert!(ss.vars_in_scope().contains(&Value::from("b")));
ss.push();
assert_eq!(ss.vars_in_scope().len(), 0);
let _ = ss.set("c", Value::from("3"));
assert_eq!(ss.vars_in_scope().len(), 1);
assert!(ss.vars_in_scope().contains(&Value::from("c")));
ss.upvar(0, "a");
assert_eq!(ss.vars_in_scope().len(), 2);
assert!(ss.vars_in_scope().contains(&Value::from("a")));
ss.pop();
assert_eq!(ss.vars_in_scope().len(), 2);
assert!(!ss.vars_in_scope().contains(&Value::from("c")));
ss.unset("b");
assert_eq!(ss.vars_in_scope().len(), 1);
assert!(!ss.vars_in_scope().contains(&Value::from("b")));
}
#[test]
fn test_vars_in_local_scope() {
let mut ss = ScopeStack::new();
ss.set("a", Value::from("1")).expect("ok");
assert!(ss.vars_in_local_scope().is_empty());
ss.push();
assert!(ss.vars_in_scope().is_empty());
ss.set("a", Value::from("1")).expect("ok");
ss.set_elem("b", "1", Value::from("1")).expect("ok");
assert_eq!(ss.vars_in_local_scope().len(), 2);
assert!(ss.vars_in_local_scope().contains(&Value::from("a")));
assert!(ss.vars_in_local_scope().contains(&Value::from("b")));
ss.upvar(0, "c");
assert_eq!(ss.vars_in_local_scope().len(), 2);
assert!(!ss.vars_in_local_scope().contains(&Value::from("c")));
ss.push();
assert!(ss.vars_in_scope().is_empty());
}
#[test]
fn test_vars_in_global_scope() {
let mut ss = ScopeStack::new();
assert!(ss.vars_in_global_scope().is_empty());
ss.set("a", Value::from("1")).expect("ok");
ss.set_elem("b", "1", Value::from("1")).expect("ok");
assert!(ss.vars_in_global_scope().len() == 2);
assert!(ss.vars_in_global_scope().contains(&Value::from("a")));
assert!(ss.vars_in_global_scope().contains(&Value::from("b")));
assert!(!ss.vars_in_global_scope().contains(&Value::from("c")));
ss.push();
assert!(ss.vars_in_global_scope().len() == 2);
assert!(ss.vars_in_global_scope().contains(&Value::from("a")));
assert!(ss.vars_in_global_scope().contains(&Value::from("b")));
assert!(!ss.vars_in_global_scope().contains(&Value::from("c")));
ss.set("c", Value::from("1")).expect("ok");
assert!(ss.vars_in_global_scope().len() == 2);
assert!(ss.vars_in_global_scope().contains(&Value::from("a")));
assert!(ss.vars_in_global_scope().contains(&Value::from("b")));
assert!(!ss.vars_in_global_scope().contains(&Value::from("c")));
}
#[test]
fn test_global() {
let mut ss = ScopeStack::new();
ss.push();
ss.upvar(0, "a");
ss.upvar(0, "b");
let _ = dbg!(ss.set("a", Value::from("1")));
let _ = dbg!(ss.set_elem("b", "1", Value::from("2")));
ss.pop();
let out = ss.get("a").unwrap();
assert_eq!(out.as_str(), "1");
let out = ss.get_elem("b", "1").unwrap();
assert_eq!(out.as_str(), "2");
}
#[test]
fn test_array_indices() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", "zero".into());
let _ = ss.set_elem("b", "1", "one".into());
let _ = ss.set_elem("b", "2", "two".into());
assert_eq!(ss.array_indices("x"), Vec::new());
assert_eq!(ss.array_indices("a"), Vec::new());
let list = ss.array_indices("b");
assert!(list.len() == 2);
assert!(list.contains(&"1".into()));
assert!(list.contains(&"2".into()));
}
#[test]
fn test_array_size() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", "zero".into());
let _ = ss.set_elem("b", "1", "one".into());
let _ = ss.set_elem("b", "2", "two".into());
assert_eq!(ss.array_size("x"), 0);
assert_eq!(ss.array_size("a"), 0);
assert_eq!(ss.array_size("b"), 2);
}
#[test]
fn test_array_get() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", "zero".into());
let _ = ss.set_elem("b", "1", "one".into());
let _ = ss.set_elem("b", "2", "two".into());
assert_eq!(ss.array_get("x"), Vec::new());
assert_eq!(ss.array_get("a"), Vec::new());
let list = ss.array_get("b");
assert!(list.len() == 4);
assert!(list.contains(&"1".into()));
assert!(list.contains(&"one".into()));
assert!(list.contains(&"2".into()));
assert!(list.contains(&"two".into()));
}
#[test]
fn test_unset_element() {
let mut ss = ScopeStack::new();
let _ = ss.set("a", "zero".into());
let _ = ss.set_elem("b", "1", "one".into());
let _ = ss.set_elem("b", "2", "two".into());
ss.unset_element("x", "1");
ss.unset_element("a", "1");
let out = ss.get("a");
assert!(out.is_ok());
assert_eq!(out.unwrap().as_str(), "zero");
ss.unset_element("b", "1");
assert!(ss.get_elem("b", "1").is_err());
assert!(ss.get_elem("b", "2").is_ok());
}
#[test]
fn test_array_set() {
let kvlist: MoltList = vec!["a".into(), "1".into(), "b".into(), "2".into()];
let mut ss = ScopeStack::new();
assert!(ss.array_set("x", &kvlist).is_ok());
assert_eq!(ss.get_elem("x", "a").unwrap().as_str(), "1");
assert_eq!(ss.get_elem("x", "b").unwrap().as_str(), "2");
assert!(ss.get_elem("x", "c").is_err());
assert!(ss.set_elem("y", "a", "0".into()).is_ok());
assert!(ss.set_elem("y", "b", "0".into()).is_ok());
assert!(ss.set_elem("y", "c", "0".into()).is_ok());
assert!(ss.array_set("y", &kvlist).is_ok());
assert_eq!(ss.get_elem("y", "a").unwrap().as_str(), "1");
assert_eq!(ss.get_elem("y", "b").unwrap().as_str(), "2");
assert_eq!(ss.get_elem("y", "c").unwrap().as_str(), "0");
assert!(ss.set("z", "0".into()).is_ok());
assert_eq!(
ss.array_set("z", &kvlist),
molt_err!("can't array set \"z\": variable isn't array")
);
}
#[test]
fn test_exists() {
let mut ss = ScopeStack::new();
ss.set("a", "1".into()).expect("success");
ss.set_elem("b", "1", "2".into()).expect("success");
assert!(!ss.exists("nonesuch"));
assert!(!ss.elem_exists("nonesuch", "1"));
assert!(!ss.elem_exists("b", "2"));
assert!(ss.exists("a"));
assert!(ss.exists("b"));
assert!(ss.elem_exists("b", "1"));
}
}