use crate::error::LinkerError;
use crate::ir::Instr;
use crate::metadata::{SymbolKind, SymbolMetadata};
use crate::num;
use crate::symbol::Symbol;
use crate::symtable::SymTable;
#[derive(Debug)]
pub struct Linker {
bytecode: Vec<Instr>,
symbols: Vec<SymbolMetadata>,
symtable: SymTable,
}
impl Linker {
pub fn new(bytecode: Vec<Instr>, symbols: Vec<SymbolMetadata>, symtable: SymTable) -> Self {
Self {
bytecode,
symbols,
symtable,
}
}
pub fn link(mut self) -> Result<(Vec<Instr>, SymTable), LinkerError> {
self.resolve_symbols()?;
self.rewrite_bytecode();
Ok((self.bytecode, self.symtable))
}
fn resolve_symbols(&mut self) -> Result<(), LinkerError> {
for metadata in &mut self.symbols {
let resolved_idx = if metadata.local {
let idx = self.symtable.symbols().count();
self.symtable
.add_const(metadata.name.to_string(), num!(0), true)?;
idx
} else {
let (idx, symbol) = self.symtable.get_with_index(&metadata.name)?;
Self::validate_symbol_kind(metadata, symbol)?;
idx
};
metadata.index = Some(resolved_idx);
}
Ok(())
}
fn rewrite_bytecode(&mut self) {
for instr in &mut self.bytecode {
match instr {
Instr::Load(idx) | Instr::Store(idx) | Instr::Call(idx, _) => {
*idx = self.symbols[*idx]
.index
.expect("Symbol should have been resolved during linking");
}
_ => {}
}
}
}
fn validate_symbol_kind(metadata: &SymbolMetadata, symbol: &Symbol) -> Result<(), LinkerError> {
match (&metadata.kind, symbol) {
(SymbolKind::Const, Symbol::Const { .. }) => Ok(()),
(
SymbolKind::Func { arity, .. },
Symbol::Func {
args: min_args,
variadic,
..
},
) => {
let valid = if *variadic {
arity >= min_args
} else {
arity == min_args
};
if valid {
Ok(())
} else {
let expected_msg = if *variadic {
format!("at least {} arguments", min_args)
} else {
format!("exactly {} arguments", min_args)
};
Err(LinkerError::TypeMismatch {
name: metadata.name.to_string(),
expected: expected_msg,
found: format!("{} arguments provided", arity),
})
}
}
(SymbolKind::Const, Symbol::Func { .. }) => Err(LinkerError::TypeMismatch {
name: metadata.name.to_string(),
expected: "constant".to_string(),
found: "function".to_string(),
}),
(SymbolKind::Func { .. }, Symbol::Const { .. }) => Err(LinkerError::TypeMismatch {
name: metadata.name.to_string(),
expected: "function".to_string(),
found: "constant".to_string(),
}),
}
}
}