use crate::*; use std::collections::HashMap; pub struct BytecodeGenerator<'a> { tokens: &'a [AssembledToken], addresses: HashMap>, words: Vec, errors: Vec, } impl<'a> BytecodeGenerator<'a> { pub fn new(tokens: &'a [AssembledToken]) -> Self { Self { tokens, addresses: HashMap::new(), words: Vec::new(), errors: Vec::new(), } } pub fn generate(mut self) -> Bytecode { self.calculate_addresses(); for token in self.tokens { match token { AssembledToken::Word(assembled_word) => { let mut value = assembled_word.value; for field in &assembled_word.fields { let (field_value, source) = match &field.value { IntegerArgument::Expression(expr) => (self.resolve_expression(expr), expr.source.clone()), IntegerArgument::LabelReference(name) => (self.resolve_label_reference(name), name.source.clone()), IntegerArgument::Integer(integer) => (integer.value, integer.source.clone()), }; let bitcount = match field_value { 0 => 0, _ => (field_value.ilog2() + 1) as usize, }; if field.bits < bitcount { let variant = BytecodeErrorVariant::ValueTooLarge(field.bits, bitcount); self.errors.push(BytecodeError { source, variant }); } else { value |= (field_value << field.shift) as usize; } } self.words.push(Word { bits: assembled_word.bits, value }); } AssembledToken::PinnedAddress(pinned) => { if self.words.len() > pinned.address { let variant = BytecodeErrorVariant::PinnedAddressBacktrack( pinned.address, self.words.len()); let source = pinned.source.clone(); self.errors.push(BytecodeError { source, variant }); } else { self.words.resize(pinned.address, Word { bits: 0, value: 0}); } } AssembledToken::LabelDefinition(_) => (), AssembledToken::Error(_) => (), } } return Bytecode { words: self.words, errors: self.errors, } } fn calculate_addresses(&mut self) { let mut i = 0; for token in self.tokens { match token { AssembledToken::LabelDefinition(definition) => { let address = Tracked::from(i, &definition.source); if let Some(_) = self.addresses.insert(definition.name.clone(), address) { let name = definition.name.clone(); let variant = BytecodeErrorVariant::DuplicateLabelDefinition(name); let source = definition.source.clone(); self.errors.push(BytecodeError { source, variant }); } } AssembledToken::Word(_) => { i += 1; } AssembledToken::PinnedAddress(pinned) => { i = pinned.address; } AssembledToken::Error(_) => (), } } } fn resolve_expression(&mut self, expr: &AssembledExpression) -> isize { let mut stack = Vec::new(); macro_rules! push { ($value:expr) => { stack.push($value) }; } macro_rules! pop { ($name:ident) => { let $name = match stack.pop() { Some(value) => value, None => { let variant = BytecodeErrorVariant::StackUnderflow; self.errors.push(BytecodeError { source: expr.source.clone(), variant }); return 0; }, }; }; } macro_rules! truth { ($bool:expr) => { match $bool { true => 1, false => 0 } }; } for token in &expr.tokens { match &token { AssembledExpressionToken::Integer(value) => { push!(value.value) } AssembledExpressionToken::LabelReference(name) => { push!(self.resolve_label_reference(name)) } AssembledExpressionToken::Expression(expr) => { push!(self.resolve_expression(expr)) } AssembledExpressionToken::Operator(operator) => match operator { Operator::Equal => { pop!(b); pop!(a); push!(truth!(a==b)) }, Operator::NotEqual => { pop!(b); pop!(a); push!(truth!(a!=b)) }, Operator::LessThan => { pop!(b); pop!(a); push!(truth!(a < b)) }, Operator::GreaterThan => { pop!(b); pop!(a); push!(truth!(a > b)) }, Operator::Add => { pop!(b); pop!(a); push!(a + b) }, Operator::Subtract => { pop!(b); pop!(a); push!(a - b) }, Operator::LeftShift => { pop!(b); pop!(a); push!(a << b) }, Operator::RightShift => { pop!(b); pop!(a); push!(a >> b) }, Operator::And => { pop!(b); pop!(a); push!(a & b) }, Operator::Or => { pop!(b); pop!(a); push!(a | b) }, Operator::Xor => { pop!(b); pop!(a); push!(a ^ b) }, Operator::Not => { pop!(a); push!(!a) }, } } } let variant = match stack.len() { 0 => BytecodeErrorVariant::NoReturnValue, 1 => return stack[0], _ => BytecodeErrorVariant::MultipleReturnValues, }; self.errors.push(BytecodeError { source: expr.source.clone(), variant}); 0 } fn resolve_label_reference(&mut self, name: &Tracked) -> isize { if let Some(address) = self.addresses.get(&name.value) { address.value as isize } else { let variant = BytecodeErrorVariant::DefinitionNotFound(name.value.clone()); self.errors.push(BytecodeError { source: name.source.clone(), variant }); 0 } } }