use crate::ast::types::{Effect, Type};
use crate::ast::{TypeDef, Variant};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct Environment {
words: HashMap<String, Effect>,
types: HashMap<String, TypeDef>,
}
impl Environment {
pub fn new() -> Self {
let mut env = Environment {
words: HashMap::new(),
types: HashMap::new(),
};
env.add_builtin_words();
env.add_builtin_types();
env
}
pub fn add_word(&mut self, name: String, effect: Effect) {
self.words.insert(name, effect);
}
pub fn lookup_word(&self, name: &str) -> Option<&Effect> {
self.words.get(name)
}
pub fn add_type(&mut self, typedef: TypeDef) {
self.types.insert(typedef.name.clone(), typedef);
}
pub fn lookup_type(&self, name: &str) -> Option<&TypeDef> {
self.types.get(name)
}
pub fn get_variants(&self, type_name: &str) -> Option<&[Variant]> {
self.types.get(type_name).map(|td| td.variants.as_slice())
}
fn add_builtin_words(&mut self) {
use crate::ast::types::StackType;
self.add_word(
"dup".to_string(),
Effect {
inputs: StackType::empty().push(Type::Var("A".to_string())),
outputs: StackType::empty()
.push(Type::Var("A".to_string()))
.push(Type::Var("A".to_string())),
},
);
self.add_word(
"drop".to_string(),
Effect {
inputs: StackType::empty().push(Type::Var("A".to_string())),
outputs: StackType::empty(),
},
);
self.add_word(
"swap".to_string(),
Effect {
inputs: StackType::empty()
.push(Type::Var("A".to_string()))
.push(Type::Var("B".to_string())),
outputs: StackType::empty()
.push(Type::Var("B".to_string()))
.push(Type::Var("A".to_string())),
},
);
self.add_word(
"over".to_string(),
Effect {
inputs: StackType::empty()
.push(Type::Var("A".to_string()))
.push(Type::Var("B".to_string())),
outputs: StackType::empty()
.push(Type::Var("A".to_string()))
.push(Type::Var("B".to_string()))
.push(Type::Var("A".to_string())),
},
);
self.add_word(
"rot".to_string(),
Effect {
inputs: StackType::empty()
.push(Type::Var("A".to_string()))
.push(Type::Var("B".to_string()))
.push(Type::Var("C".to_string())),
outputs: StackType::empty()
.push(Type::Var("B".to_string()))
.push(Type::Var("C".to_string()))
.push(Type::Var("A".to_string())),
},
);
self.add_word(
"+".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
);
self.add_word(
"-".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
);
self.add_word(
"*".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
);
self.add_word(
"/".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Int]),
);
self.add_word(
"=".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
);
self.add_word(
"<".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
);
self.add_word(
">".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
);
self.add_word(
"<=".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
);
self.add_word(
">=".to_string(),
Effect::from_vecs(vec![Type::Int, Type::Int], vec![Type::Bool]),
);
self.add_word(
"clone".to_string(),
Effect {
inputs: StackType::empty().push(Type::Var("A".to_string())),
outputs: StackType::empty()
.push(Type::Var("A".to_string()))
.push(Type::Var("A".to_string())),
},
);
}
fn add_builtin_types(&mut self) {
self.add_type(TypeDef {
name: "Option".to_string(),
type_params: vec!["T".to_string()],
variants: vec![
Variant {
name: "Some".to_string(),
fields: vec![Type::Var("T".to_string())],
},
Variant {
name: "None".to_string(),
fields: vec![],
},
],
});
self.add_type(TypeDef {
name: "Result".to_string(),
type_params: vec!["T".to_string(), "E".to_string()],
variants: vec![
Variant {
name: "Ok".to_string(),
fields: vec![Type::Var("T".to_string())],
},
Variant {
name: "Err".to_string(),
fields: vec![Type::Var("E".to_string())],
},
],
});
self.add_type(TypeDef {
name: "List".to_string(),
type_params: vec!["T".to_string()],
variants: vec![
Variant {
name: "Cons".to_string(),
fields: vec![
Type::Var("T".to_string()),
Type::Named {
name: "List".to_string(),
args: vec![Type::Var("T".to_string())],
},
],
},
Variant {
name: "Nil".to_string(),
fields: vec![],
},
],
});
}
}
impl Default for Environment {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builtin_words() {
let env = Environment::new();
assert!(env.lookup_word("dup").is_some());
assert!(env.lookup_word("drop").is_some());
assert!(env.lookup_word("swap").is_some());
assert!(env.lookup_word("+").is_some());
assert!(env.lookup_word("*").is_some());
assert!(env.lookup_word("unknown").is_none());
}
#[test]
fn test_builtin_types() {
let env = Environment::new();
let option_def = env.lookup_type("Option");
assert!(option_def.is_some());
assert_eq!(option_def.unwrap().variants.len(), 2);
let result_def = env.lookup_type("Result");
assert!(result_def.is_some());
assert_eq!(result_def.unwrap().variants.len(), 2);
let list_def = env.lookup_type("List");
assert!(list_def.is_some());
assert_eq!(list_def.unwrap().variants.len(), 2);
}
#[test]
fn test_add_word() {
let mut env = Environment::new();
let square_effect = Effect::from_vecs(vec![Type::Int], vec![Type::Int]);
env.add_word("square".to_string(), square_effect.clone());
let looked_up = env.lookup_word("square");
assert!(looked_up.is_some());
assert_eq!(*looked_up.unwrap(), square_effect);
}
}