summaryrefslogtreecommitdiff
path: root/src/parsers/bytecode.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsers/bytecode.rs')
-rw-r--r--src/parsers/bytecode.rs161
1 files changed, 161 insertions, 0 deletions
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<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) => {
+ 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<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
+ }
+ }
+}