use crate::core::Value;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct StackFrame {
variables: HashMap<String, Value>,
#[allow(dead_code)]
name: String,
}
impl StackFrame {
pub fn new(name: impl Into<String>) -> Self {
Self {
variables: HashMap::new(),
name: name.into(),
}
}
}
#[derive(Debug)]
pub struct Environment {
frames: Vec<StackFrame>,
}
impl Environment {
pub fn new() -> Self {
Self {
frames: vec![StackFrame::new("global")],
}
}
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct DapVariable {
pub name: String,
pub value: String,
pub type_hint: String,
}
#[derive(Debug, Clone)]
pub struct DapScope {
pub name: String,
pub variables: Vec<DapVariable>,
}
impl Environment {
pub fn push_frame(&mut self, name: impl Into<String>) {
self.frames.push(StackFrame::new(name));
}
pub fn pop_frame(&mut self) {
if self.frames.len() > 1 {
self.frames.pop();
}
}
pub fn define(&mut self, name: &str, value: Value) {
if let Some(frame) = self.frames.last_mut() {
frame.variables.insert(name.trim().to_lowercase(), value);
}
}
pub fn define_global(&mut self, name: &str, value: Value) {
if let Some(frame) = self.frames.first_mut() {
frame.variables.insert(name.trim().to_lowercase(), value);
}
}
pub fn assign(&mut self, name: &str, value: Value) -> Result<(), String> {
let key = name.trim().to_lowercase();
for frame in self.frames.iter_mut().rev() {
if let std::collections::hash_map::Entry::Occupied(mut e) =
frame.variables.entry(key.clone())
{
e.insert(value);
return Ok(());
}
}
Err(format!("Undefined variable '{}'", name))
}
pub fn get(&self, name: &str) -> Option<&Value> {
let key = name.trim().to_lowercase();
for frame in self.frames.iter().rev() {
if let Some(val) = frame.variables.get(&key) {
return Some(val);
}
}
None
}
pub fn frames(&self) -> &[StackFrame] {
&self.frames
}
pub fn to_dap_scopes(&self) -> Vec<DapScope> {
self.frames
.iter()
.map(|f| {
let mut variables = f
.variables
.iter()
.map(|(k, v)| DapVariable {
name: k.clone(),
value: match v {
Value::Text(t) => t.to_string(),
Value::Integer(i) => i.to_string(),
Value::Float(f) => f.to_string(),
Value::Boolean(b) => b.to_string(),
Value::Null(_) => "NULL".to_string(),
_ => format!("{:?}", v),
},
type_hint: format!("{:?}", v)
.split('(')
.next()
.unwrap_or("")
.to_string(),
})
.collect::<Vec<_>>();
variables.sort_by(|a, b| a.name.cmp(&b.name));
DapScope {
name: f.name.clone(),
variables,
}
})
.collect()
}
}