1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use std::collections::{ HashMap, VecDeque };
use little::*;
use value::Value;

pub struct Staging<'c, V: LittleValue> {
    next_constant: Constant,
    unique_constants: HashMap<Fingerprint, Constant>,
    pub locals: VecDeque<Basket<'c, Binding>>,
    template: Template<V>,
}

impl<'c, V: LittleValue> Staging<'c, V> {
    pub fn new<'r>() -> Staging<'r, V> {
        let mut st = Staging {
            next_constant: Constant(0),
            unique_constants: HashMap::new(),
            locals: VecDeque::new(),
            template: Template::empty(),
        };

        st.locals.push_front(Basket::new(Binding(0), |Binding(p)| Binding(p + 1)));

        st
    }

    pub fn include_const(&mut self, const_value: V) -> Mem {
        // next constant to insert.
        let mut next = self.next_constant;

        let constant = match const_value.identify_value() {
            Some(fingerprint) => { // constant is identifiable
                let mut added = false;
                // we can ensure that identifiable constant is kept only once
                let identifier = *self.unique_constants.entry(fingerprint).or_insert_with(|| {
                    // when we insert it, we increment identifier.
                    let identifier = next;
                    next = match next {
                        Constant(v) => Constant(v + 1),
                    };
                    added = true;
                    identifier
                });
                // and add it to constant list only once.
                if added {
                    self.template.push_constant(identifier, const_value);
                }
                identifier
            },
            None => { // constant can not be identified
                // always add value to constant list for every constant.
                let identifier = next;
                next = match next {
                    Constant(v) => Constant(v + 1),
                };
                self.template.push_constant(identifier, const_value);
                identifier
            },
        };

        // update next constant.
        self.next_constant = next;

        Mem::Const(constant)
    }

    pub fn use_name(&mut self, name: &'c str) -> Option<Mem> {
        for basket in &self.locals {
            if let Some(ref binding) = basket.get(name) {
                return Some(Mem::Binding(binding.clone()));
            }
        }
        None
    }

    pub fn instr(&mut self, instruction: Instruction) {
        trace!("instr {:?}", &instruction);
        self.template.push_instruction(instruction);
    }
}

impl<'a> Into<Template<Value>> for Staging<'a, Value> {
    fn into(self) -> Template<Value> {
        self.template
    }
}

pub struct Basket<'c, T>  {
    pub map: HashMap<&'c str, T>,
    next: Box<Fn(T) -> T>,
    current: T,
}

impl<'c, T> Basket<'c, T> where T: Eq + Clone {

    pub fn new<'r, N: Fn(T) -> T + 'static>(initial: T, next: N) -> Basket<'r, T> {
        Basket {
            map: HashMap::new(),
            next: Box::new(next),
            current: initial,
        }
    }

    pub fn assign_space(&mut self, name: &'c str) -> T {
        let len = self.map.len();
        let current = self.current.clone();
        let result = self.map.entry(&name).or_insert(current.clone()).clone();
        if self.map.len() > len {
            self.current = (self.next)(current);
        }
        result
    }

    pub fn get(&self, name: &str) -> Option<T> {
        self.map.get(name).cloned()
    }
}