use crate::*;
use std::collections::HashMap;
pub struct BytecodeGenerator<'a> {
tokens: &'a [AssembledToken],
addresses: HashMap<String, Tracked<usize>>,
words: Vec<Word>,
errors: Vec<BytecodeError>,
}
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) => {
self.assemble_word(assembled_word);
}
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.clone());
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(word) => {
i += word.count();
}
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<String>) -> 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
}
}
fn assemble_word(&mut self, assembled_word: &AssembledWord) {
let mut field_values = Vec::new();
for field in &assembled_word.fields {
match &field.value {
IntegerArgument::Expression(expr) => {
let source = expr.source.clone();
let value = self.resolve_expression(expr);
field_values.push(vec![Tracked::from(value, source)])
}
IntegerArgument::LabelReference(name) => {
let source = name.source.clone();
let value = self.resolve_label_reference(name);
field_values.push(vec![Tracked::from(value, source)])
}
IntegerArgument::Integer(integer) => {
let source = integer.source.clone();
let value = integer.value;
field_values.push(vec![Tracked::from(value, source)])
}
IntegerArgument::String(string) => {
let values = string.chars.iter()
.map(|c| Tracked::from(c.value as isize, c.source.clone()))
.collect();
field_values.push(values);
}
};
}
for i in 0..assembled_word.count() {
let mut value = assembled_word.value;
for (f, field) in assembled_word.fields.iter().enumerate() {
let (field_value, source) = match field_values[f].get(i) {
Some(tracked) => (tracked.value, Some(tracked.source.clone())),
None => (0, None),
};
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: source.unwrap(), variant });
} else {
value |= (field_value << field.shift) as usize;
}
}
self.words.push(Word { bits: assembled_word.bits, value });
}
}
}