summaryrefslogblamecommitdiff
path: root/src/parsers/bytecode.rs
blob: 6cdfd3ae7ce19ec2ac92c7f306fae0b25d1f78a3 (plain) (tree)

























                                                         
                                                       

























                                                                                     
                                                                              





                                                                                              
                                               








































































                                                                                                 















































                                                                                            
 
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 });
        }
    }
}