summaryrefslogtreecommitdiff
path: root/src/stages/bytecode.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stages/bytecode.rs')
-rw-r--r--src/stages/bytecode.rs272
1 files changed, 0 insertions, 272 deletions
diff --git a/src/stages/bytecode.rs b/src/stages/bytecode.rs
deleted file mode 100644
index 71f1ff0..0000000
--- a/src/stages/bytecode.rs
+++ /dev/null
@@ -1,272 +0,0 @@
-use crate::*;
-
-use std::collections::HashMap;
-
-
-pub fn parse_bytecode(tokens: Vec<Tracked<IntermediateToken>>, width: Option<u32>) -> Result<Vec<Segment>, Vec<Tracked<BytecodeError>>> {
- BytecodeParser::new(width).parse(tokens)
-}
-
-
-pub struct BytecodeParser {
- width: Option<u32>,
- addresses: HashMap<String, Tracked<usize>>,
- address: usize,
- segment_address: usize,
- segment_source: Option<SourceSpan>,
- segments: Vec<Segment>,
- words: Vec<Tracked<Word>>,
- errors: Vec<Tracked<BytecodeError>>,
-}
-
-impl BytecodeParser {
- pub fn new(width: Option<u32>) -> Self {
- Self {
- width,
- addresses: HashMap::new(),
- address: 0,
- segment_address: 0,
- segment_source: None,
- segments: Vec::new(),
- words: Vec::new(),
- errors: Vec::new(),
- }
- }
-
- pub fn parse(mut self, tokens: Vec<Tracked<IntermediateToken>>) -> Result<Vec<Segment>, Vec<Tracked<BytecodeError>>> {
- // Register all labels with address 0.
- for token in &tokens {
- if let IntermediateToken::LabelDefinition(name) = &token.value {
- let tracked = Tracked::from(0, token.source.clone());
- if let Some(_) = self.addresses.insert(name.clone(), tracked) {
- unreachable!("Uncaught duplicate label definition '{name}'");
- }
- }
- }
- // Attempt to calculate all label addresses naively ahead of time.
- // This will give false results if we pin an address calculated from a label address.
- let mut address = 0;
- for token in &tokens {
- let source = &token.source;
- match &token.value {
- IntermediateToken::LabelDefinition(name) => {
- let tracked = Tracked::from(address, source.clone());
- self.addresses.insert(name.clone(), tracked);
- }
- IntermediateToken::Word(_) => {
- address += 1;
- }
- IntermediateToken::PinnedAddress(pinned) => {
- // Attempt to calculate a sane initial value for a pinned address.
- match &pinned.value {
- IntermediateInteger::Integer(value) => {
- address = (*value).try_into().unwrap_or(0);
- }
- IntermediateInteger::Expression(expression) => {
- let result = self.evaluate_expression(&expression, &pinned.source);
- address = result.try_into().unwrap_or(0);
- }
- IntermediateInteger::LabelReference(_) => {
- let error = BytecodeError::PinnedLabel;
- self.errors.push(Tracked::from(error, source.clone()));
- }
- }
- }
- }
- }
- // Return unrecoverable errors.
- if !self.errors.is_empty() {
- return Err(self.errors);
- }
-
- for i in 0..4 {
- info!("Attempting iteration {} of bytecode assembly stage", i+1);
- // Erase the previous parse attempt.
- self.segments.clear();
- self.errors.clear();
- // Attempt to parse the program.
- let previous_addresses = self.addresses.clone();
- self.parse_iteration(&tokens);
- // Return unrecoverable errors.
- if !self.errors.is_empty() {
- return Err(self.errors);
- }
- // Check label stability
- if self.check_for_instability(&previous_addresses) {
- continue;
- }
- // Check for backtrack
- if self.check_for_backtrack() {
- continue;
- };
- // Program is stable, return.
- info!("Stabilised in {} iteration of bytecode assembly stage", i+1);
- return Ok(self.segments);
- }
-
- return Err(self.errors);
- }
-
- /// Attempt to parse the full program using the current label values.
- fn parse_iteration(&mut self, tokens: &[Tracked<IntermediateToken>]) {
- for token in tokens {
- let source = &token.source;
- match &token.value {
- IntermediateToken::Word(word) => {
- let word = self.evaluate_word(word, source);
- // Check that the word width fits the provided width.
- if let Some(width) = self.width {
- if word.width != width {
- let error = BytecodeError::IncorrectWidth(width, word.width);
- self.errors.push(Tracked::from(error, source.clone()));
- }
- }
- self.words.push(word);
- self.address += 1;
- }
- IntermediateToken::PinnedAddress(integer) => {
- // Calculate the address of the new segment.
- let pinned = match &integer.value {
- IntermediateInteger::Integer(value) => {
- (*value).try_into().unwrap_or(0)
- }
- IntermediateInteger::Expression(expression) => {
- let result = self.evaluate_expression(&expression, &integer.source);
- result.try_into().unwrap_or(0)
- }
- IntermediateInteger::LabelReference(_) =>
- // Already handled when registering initial label values.
- unreachable!(),
- };
- // Start a new segment.
- let words = std::mem::take(&mut self.words);
- if !words.is_empty() {
- let address = self.segment_address;
- let source = std::mem::take(&mut self.segment_source);
- let segment = Segment { address, source, words };
- self.segments.push(segment);
- }
- self.segment_source = Some(integer.source.clone());
- self.address = pinned;
- self.segment_address = pinned;
- }
- IntermediateToken::LabelDefinition(name) => {
- // Record the latest known address of this label.
- let address = self.addresses.get_mut(name).unwrap();
- address.value = self.address;
- }
- }
- }
- // Finish final segment.
- let words = std::mem::take(&mut self.words);
- if !words.is_empty() {
- let address = self.segment_address;
- let source = std::mem::take(&mut self.segment_source);
- let segment = Segment { address, source, words };
- self.segments.push(segment);
- }
- }
-
- fn evaluate_expression(&mut self, expression: &IntermediateExpression, source: &SourceSpan) -> isize {
- let mut stack = ExpressionStack::new();
- for token in &expression.tokens {
- let source = &token.source;
- match &token.value {
- IntermediateExpressionToken::Integer(integer) => match integer {
- IntermediateInteger::Integer(value) => {
- stack.push(*value);
- }
- IntermediateInteger::Expression(expression) => {
- stack.push(self.evaluate_expression(expression, source));
- }
- IntermediateInteger::LabelReference(name) => {
- stack.push(self.evaluate_label_reference(name));
- }
- }
- IntermediateExpressionToken::Operator(operator) => {
- if let Err(err) = stack.apply(*operator, source) {
- let error = BytecodeError::StackError(err);
- self.errors.push(Tracked::from(error, source.clone()))
- }
- }
- }
- }
- match stack.pull_result() {
- Ok(value) => value,
- Err(err) => {
- let error = BytecodeError::StackError(Tracked::from(err, source.clone()));
- self.errors.push(Tracked::from(error, source.clone()));
- 0
- }
- }
- }
-
- fn evaluate_label_reference(&mut self, name: &Tracked<String>) -> isize {
- if let Some(address) = self.addresses.get(&name.to_string()) {
- address.value as isize
- } else {
- unreachable!("Uncaught unresolved label reference '{name}'")
- }
- }
-
- fn evaluate_word(&mut self, word: &IntermediateWord, source: &SourceSpan) -> Tracked<Word> {
- let mut word_value = word.value;
- for field in &word.fields {
- let field_source = &field.value.value.source;
- let field_value = match &field.value.value.value {
- IntermediateInteger::Expression(expression) => {
- self.evaluate_expression(expression, source)
- }
- IntermediateInteger::LabelReference(name) => {
- self.evaluate_label_reference(name)
- }
- IntermediateInteger::Integer(value) => {
- *value
- }
- };
- let value_width = width(field_value);
- if field.width < value_width {
- let error = BytecodeError::ValueTooWide(field.width, value_width);
- self.errors.push(Tracked::from(error, field_source.clone()));
- } else {
- let mask = 2_usize.pow(field.width as u32) - 1;
- let clamped_value = (field_value as usize) & mask;
- word_value |= (clamped_value << field.shift) as usize;
- }
- }
- let word = Word { width: word.width, value: word_value };
- return Tracked::from(word, source.clone());
- }
-
- fn check_for_instability(&mut self, previous_addresses: &HashMap<String, Tracked<usize>>) -> bool {
- let mut instability_occurred = false;
- for (name, previous_address) in previous_addresses.iter() {
- let current_address = &self.addresses[name];
- if current_address != previous_address {
- info!("Label '{name}' was unstable, moving from address 0x{:04x} to 0x{:04x}",
- previous_address.value, current_address.value);
- let error = BytecodeError::UnstableLabel(name.to_string());
- self.errors.push(Tracked::from(error, previous_address.source.clone()));
- instability_occurred = true;
- }
- }
- return instability_occurred;
- }
-
- fn check_for_backtrack(&mut self) -> bool {
- let mut backtrack_occurred = false;
- let mut current_address = 0;
- for segment in &self.segments {
- if segment.address < current_address {
- let error = BytecodeError::PinnedAddressBacktrack(segment.address, current_address);
- if let Some(source) = &segment.source {
- self.errors.push(Tracked::from(error, source.clone()));
- }
- info!("Backtrack occurred with segment at address 0x{:04x}", segment.address);
- backtrack_occurred = true;
- }
- current_address = segment.address + segment.words.len();
- }
- return backtrack_occurred;
- }
-}