summaryrefslogtreecommitdiff
path: root/src/parsers/bytecode.rs
blob: ec19d9f66afd7aac5a3b5b6cb210bc25f9fe6066 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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
        }
    }
}