gbas 0.1.0

An assembler for Game Boy
Documentation
use super::resolve::Value;
use std::borrow::Borrow;
use std::collections::HashMap;

pub struct SymbolTable {
    symbols: Vec<Value>,
    names: HashMap<String, SymbolId>,
    sizes: Vec<SymbolId>,
}

#[derive(Clone, Copy)]
pub struct SymbolId(usize);

pub trait SymbolKey
where
    Self: Copy,
{
    fn associate(&self, context: &mut SymbolTable, id: SymbolId);
    fn to_symbol_id(&self, table: &SymbolTable) -> Option<SymbolId>;
}

impl SymbolKey for SymbolId {
    fn associate(&self, _: &mut SymbolTable, _: SymbolId) {}

    fn to_symbol_id(&self, _: &SymbolTable) -> Option<SymbolId> {
        Some(*self)
    }
}

impl<Q: Borrow<str> + Copy> SymbolKey for Q {
    fn associate(&self, context: &mut SymbolTable, id: SymbolId) {
        context.names.insert(self.borrow().to_string(), id);
    }

    fn to_symbol_id(&self, table: &SymbolTable) -> Option<SymbolId> {
        table.names.get(self.borrow()).cloned()
    }
}

#[derive(Clone, Copy)]
pub struct ChunkSize(pub usize);

impl SymbolKey for ChunkSize {
    fn associate(&self, context: &mut SymbolTable, id: SymbolId) {
        let ChunkSize(index) = *self;
        assert_eq!(index, context.sizes.len());
        context.sizes.push(id)
    }

    fn to_symbol_id(&self, context: &SymbolTable) -> Option<SymbolId> {
        let ChunkSize(index) = *self;
        context.sizes.get(index).cloned()
    }
}

impl SymbolTable {
    pub fn new() -> SymbolTable {
        SymbolTable {
            symbols: Vec::new(),
            names: HashMap::new(),
            sizes: Vec::new(),
        }
    }

    pub fn define(&mut self, key: impl SymbolKey, value: Value) {
        let id = SymbolId(self.symbols.len());
        self.symbols.push(value);
        key.associate(self, id)
    }

    pub fn get(&self, key: impl SymbolKey) -> Option<&Value> {
        key.to_symbol_id(self).map(|SymbolId(id)| &self.symbols[id])
    }

    fn get_mut(&mut self, key: impl SymbolKey) -> Option<&mut Value> {
        key.to_symbol_id(self)
            .map(move |SymbolId(id)| &mut self.symbols[id])
    }

    pub fn refine(&mut self, key: impl SymbolKey, value: Value) -> bool {
        let stored_value = self.get_mut(key).unwrap();
        let old_value = stored_value.clone();
        let was_refined = match (old_value, &value) {
            (Value::Unknown, new_value) => *new_value != Value::Unknown,
            (
                Value::Range {
                    min: old_min,
                    max: old_max,
                },
                Value::Range {
                    min: new_min,
                    max: new_max,
                },
            ) => {
                assert!(*new_min >= old_min);
                assert!(*new_max <= old_max);
                *new_min > old_min || *new_max < old_max
            }
            (Value::Range { .. }, Value::Unknown) => {
                panic!("a symbol previously approximated is now unknown")
            }
        };
        *stored_value = value;
        was_refined
    }
}

pub struct EvalContext<ST> {
    pub symbols: ST,
    pub location: Value,
}