use super::ast::ExprKind::*;
use super::ast::*;
use crate::error::*;
use std::collections::hash_map::Entry;
use fnv;
#[cfg(test)]
use crate::tests::*;
pub trait Uniquify {
fn uniquify(&mut self) -> WeldResult<()>;
}
impl Uniquify for Expr {
fn uniquify(&mut self) -> WeldResult<()> {
uniquify_helper(self, &mut SymbolStack::new())
}
}
struct SymbolStack {
stack: fnv::FnvHashMap<Symbol, Vec<i32>>,
next_unique_symbol: fnv::FnvHashMap<String, i32>,
}
impl SymbolStack {
fn new() -> SymbolStack {
SymbolStack {
stack: fnv::FnvHashMap::default(),
next_unique_symbol: fnv::FnvHashMap::default(),
}
}
fn symbol(&mut self, sym: Symbol) -> WeldResult<Symbol> {
match self.stack.entry(sym.clone()) {
Entry::Occupied(ref ent) => {
let name = ent.key().name();
let id = ent.get().last().cloned().ok_or_else(|| {
WeldCompileError::new(format!("Symbol {} is out of scope", &sym))
})?;
Ok(Symbol::new(name, id))
}
_ => compile_err!("Undefined symbol {}", sym),
}
}
fn push_symbol(&mut self, sym: Symbol) {
let stack_entry = self.stack.entry(sym.clone()).or_insert_with(Vec::new);
let next_entry = self.next_unique_symbol.entry(sym.name()).or_insert(-1);
*next_entry = if sym.id() > *next_entry {
sym.id()
} else {
*next_entry + 1
};
stack_entry.push(*next_entry);
}
fn pop_symbol(&mut self, sym: Symbol) -> WeldResult<()> {
match self.stack.entry(sym.clone()) {
Entry::Occupied(mut ent) => {
ent.get_mut().pop();
Ok(())
}
_ => compile_err!("Attempting to pop undefined symbol {}", sym),
}
}
}
fn uniquify_helper(expr: &mut Expr, symbol_stack: &mut SymbolStack) -> WeldResult<()> {
match expr.kind {
Lambda {
ref mut params,
ref mut body,
} => {
let original_params = params.clone();
for param in params.iter_mut() {
let sym = &mut param.name;
symbol_stack.push_symbol(sym.clone());
*sym = symbol_stack.symbol(sym.clone())?;
}
uniquify_helper(body, symbol_stack)?;
for param in original_params {
symbol_stack.pop_symbol(param.name)?;
}
}
Let {
ref mut name,
ref mut value,
ref mut body,
} => {
uniquify_helper(value, symbol_stack)?;
symbol_stack.push_symbol(name.clone());
let original_name = name.clone();
*name = symbol_stack.symbol(name.clone())?;
uniquify_helper(body, symbol_stack)?;
symbol_stack.pop_symbol(original_name)?;
}
Ident(ref mut sym) => {
*sym = symbol_stack.symbol(sym.clone())?;
}
_ => {
for child in expr.children_mut() {
uniquify_helper(child, symbol_stack)?;
}
}
}
Ok(())
}
#[test]
fn parse_and_print_uniquified_expressions() {
let mut e = parse_expr("let a = 2; a").unwrap();
let _ = e.uniquify();
assert_eq!(print_expr_without_indent(&e).as_str(), "(let a=(2);a)");
let mut e = parse_expr("let a = 2; let a = 3; a").unwrap();
let _ = e.uniquify();
assert_eq!(
print_expr_without_indent(&e).as_str(),
"(let a=(2);(let a__1=(3);a__1))"
);
let mut e = parse_expr("let a = 2; let a = a+1; a").unwrap();
let _ = e.uniquify();
assert_eq!(
print_expr_without_indent(&e).as_str(),
"(let a=(2);(let a__1=((a+1));a__1))"
);
let mut e = parse_expr("let a = 2; (|a,b|a+b)(1,2) + a").unwrap();
let _ = e.uniquify();
assert_eq!(
print_expr_without_indent(&e).as_str(),
"(let a=(2);((|a__1,b|(a__1+b))(1,2)+a))"
);
let mut e = parse_expr("let b = for([1], appender[i32], |b,i,e| merge(b, e)); b").unwrap();
let _ = e.uniquify();
assert_eq!(
print_expr_without_indent(&e).as_str(),
"(let b__1=(for([1],appender[i32],|b,i,e|merge(b,e)));b__1)"
);
}