summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs2
-rw-r--r--src/stages/bytecode.rs150
-rw-r--r--src/stages/bytecode_tokens.rs6
-rw-r--r--src/stages/intermediate.rs17
-rw-r--r--src/stages/intermediate_tokens.rs8
5 files changed, 137 insertions, 46 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 420bb61..f0de207 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,3 +7,5 @@ pub use types::*;
pub use formats::*;
pub use assembler::*;
+
+use log::*;
diff --git a/src/stages/bytecode.rs b/src/stages/bytecode.rs
index 1d27b28..71f1ff0 100644
--- a/src/stages/bytecode.rs
+++ b/src/stages/bytecode.rs
@@ -34,26 +34,82 @@ impl BytecodeParser {
}
pub fn parse(mut self, tokens: Vec<Tracked<IntermediateToken>>) -> Result<Vec<Segment>, Vec<Tracked<BytecodeError>>> {
- // Calculate all label addresses ahead of time.
+ // 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());
- if let Some(_) = self.addresses.insert(name.clone(), tracked) {
- unreachable!("Uncaught duplicate label definition '{name}'");
- }
+ self.addresses.insert(name.clone(), tracked);
}
IntermediateToken::Word(_) => {
address += 1;
}
IntermediateToken::PinnedAddress(pinned) => {
- address = pinned.value;
+ // 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()));
+ }
+ }
}
}
}
- for token in &tokens {
+ // 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) => {
@@ -68,26 +124,37 @@ impl BytecodeParser {
self.words.push(word);
self.address += 1;
}
- IntermediateToken::PinnedAddress(address) => {
- let current = self.address;
- let pinned = address.value;
- if current > pinned {
- let error = BytecodeError::PinnedAddressBacktrack(pinned, current);
- self.errors.push(Tracked::from(error, address.source.clone()));
- } else {
- 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);
+ IntermediateToken::PinnedAddress(integer) => {
+ // Calculate the address of the new segment.
+ let pinned = match &integer.value {
+ IntermediateInteger::Integer(value) => {
+ (*value).try_into().unwrap_or(0)
}
- self.segment_source = Some(address.source.clone());
- self.address = pinned;
- self.segment_address = pinned;
+ 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;
}
- IntermediateToken::LabelDefinition(_) => (),
}
}
// Finish final segment.
@@ -98,11 +165,6 @@ impl BytecodeParser {
let segment = Segment { address, source, words };
self.segments.push(segment);
}
-
- match self.errors.is_empty() {
- true => Ok(self.segments),
- false => Err(self.errors),
- }
}
fn evaluate_expression(&mut self, expression: &IntermediateExpression, source: &SourceSpan) -> isize {
@@ -175,4 +237,36 @@ impl BytecodeParser {
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;
+ }
}
diff --git a/src/stages/bytecode_tokens.rs b/src/stages/bytecode_tokens.rs
index b54cb0e..17b13b8 100644
--- a/src/stages/bytecode_tokens.rs
+++ b/src/stages/bytecode_tokens.rs
@@ -40,6 +40,8 @@ pub enum BytecodeError {
PinnedAddressBacktrack(usize, usize),
/// expected, received
ValueTooWide(u32, u32),
+ PinnedLabel,
+ UnstableLabel(String),
StackError(Tracked<StackError>),
}
@@ -57,6 +59,10 @@ fn report_bytecode_error(error: &Tracked<BytecodeError>, source_code: &str) {
&format!("Word is {received} bits wide, but was expected to have a fixed width of {expected} bits"),
BytecodeError::PinnedAddressBacktrack(pinned, real) =>
&format!("Cannot pin to address {pinned} when address is already {real}"),
+ BytecodeError::PinnedLabel =>
+ &format!("Cannot pin directly to a label"),
+ BytecodeError::UnstableLabel(name) =>
+ &format!("Label '{name}' never stabilised"),
BytecodeError::StackError(stack_error) => {
report_stack_error(stack_error, source_code); return; },
BytecodeError::ValueTooWide(expected, received) =>
diff --git a/src/stages/intermediate.rs b/src/stages/intermediate.rs
index c4bb74d..cfe33b7 100644
--- a/src/stages/intermediate.rs
+++ b/src/stages/intermediate.rs
@@ -197,21 +197,8 @@ impl<'a> Environment<'a> {
}
BlockToken::PinnedAddress(address) => {
if let Some(integer) = self.parse_integer_token(address, &address.source) {
- if let Some(source) = integer_contains_label_reference(&integer) {
- let error = IntermediateError::LabelReferenceInPinnedAddress;
- let new_source = address.source.clone().wrap(source);
- self.errors.push(Tracked::from(error, new_source));
- } else {
- match evaluate_integer(&integer, source) {
- Ok(value) => {
- let value = usize::try_from(value).unwrap_or(0);
- let tracked = Tracked::from(value, address.source.clone());
- let token = IntermediateToken::PinnedAddress(tracked);
- intermediate.push(Tracked::from(token, source.clone()));
- }
- Err(error) => self.errors.push(error),
- }
- }
+ let token = IntermediateToken::PinnedAddress(integer);
+ intermediate.push(Tracked::from(token, source.clone()));
}
}
BlockToken::ConditionalBlock(cond) => {
diff --git a/src/stages/intermediate_tokens.rs b/src/stages/intermediate_tokens.rs
index 4c4a345..8b7cfaf 100644
--- a/src/stages/intermediate_tokens.rs
+++ b/src/stages/intermediate_tokens.rs
@@ -4,7 +4,7 @@ use crate::*;
#[derive(Clone)]
pub enum IntermediateToken {
Word(IntermediateWord),
- PinnedAddress(Tracked<usize>),
+ PinnedAddress(Tracked<IntermediateInteger>),
LabelDefinition(String),
}
@@ -125,8 +125,10 @@ pub fn print_intermediate_token(i: usize, token: &IntermediateToken) {
print_intermediate_integer(i+1, &field.value.value);
}
}
- IntermediateToken::PinnedAddress(address) =>
- indent!(i, "PinnedAddress({address})"),
+ IntermediateToken::PinnedAddress(address) => {
+ indent!(i, "PinnedAddress");
+ print_intermediate_integer(i+1, address);
+ }
IntermediateToken::LabelDefinition(name) =>
indent!(i, "LabelDefinition({name})"),
}