From 67470aea034fd46f4bbcfe815c51ad3451043188 Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Thu, 27 Feb 2025 14:53:21 +1300 Subject: Finish first working version of Torque This is a huge and messy commit, worked on piecemeal while traveling and while the language was still being designed. --- src/parsers/bytecode.rs | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 src/parsers/bytecode.rs (limited to 'src/parsers/bytecode.rs') diff --git a/src/parsers/bytecode.rs b/src/parsers/bytecode.rs new file mode 100644 index 0000000..ec19d9f --- /dev/null +++ b/src/parsers/bytecode.rs @@ -0,0 +1,161 @@ +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 + } + } +} -- cgit v1.2.3-70-g09d2