summaryrefslogtreecommitdiff
path: root/src/stages
diff options
context:
space:
mode:
Diffstat (limited to 'src/stages')
-rw-r--r--src/stages/bytecode.rs272
-rw-r--r--src/stages/bytecode_tokens.rs84
-rw-r--r--src/stages/compiler.rs2
-rw-r--r--src/stages/intermediate.rs626
-rw-r--r--src/stages/intermediate_tokens.rs100
-rw-r--r--src/stages/mod.rs4
-rw-r--r--src/stages/semantic.rs72
-rw-r--r--src/stages/semantic_tokens.rs14
8 files changed, 387 insertions, 787 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;
- }
-}
diff --git a/src/stages/bytecode_tokens.rs b/src/stages/bytecode_tokens.rs
deleted file mode 100644
index 17b13b8..0000000
--- a/src/stages/bytecode_tokens.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-use crate::*;
-
-
-pub struct Segment {
- pub address: usize,
- /// Source of the address value.
- pub source: Option<SourceSpan>,
- pub words: Vec<Tracked<Word>>,
-}
-
-pub struct Word {
- pub value: usize,
- pub width: u32,
-}
-
-impl std::fmt::Display for Word {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
- if self.width == 0 {
- write!(f, "0")
- } else {
- for i in (0..self.width).rev() {
- let is_first_bit = i+1 == self.width;
- if !is_first_bit && (i+1) % 4 == 0 {
- write!(f, "_")?;
- }
- match (self.value >> i) & 1 {
- 0 => write!(f, "0")?,
- _ => write!(f, "1")?,
- }
- }
- Ok(())
- }
- }
-}
-
-pub enum BytecodeError {
- /// expected, received
- IncorrectWidth(u32, u32),
- /// pinned, real
- PinnedAddressBacktrack(usize, usize),
- /// expected, received
- ValueTooWide(u32, u32),
- PinnedLabel,
- UnstableLabel(String),
- StackError(Tracked<StackError>),
-}
-
-
-pub fn report_bytecode_errors(errors: &[Tracked<BytecodeError>], source_code: &str) {
- for error in errors {
- report_bytecode_error(error, source_code);
- }
-}
-
-fn report_bytecode_error(error: &Tracked<BytecodeError>, source_code: &str) {
- let context = Context { source_code: &source_code, source: &error.source };
- let message = match &error.value {
- BytecodeError::IncorrectWidth(expected, received) =>
- &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) =>
- &format!("Field is {expected} bits wide, but received a value that is {received} bits wide"),
- };
-
- report_source_issue(LogLevel::Error, &context, message);
-}
-
-
-pub fn print_segment(segment: &Segment) {
- println!("SEGMENT: 0x{:>04x}", segment.address);
- // Find maximum width of all words in the segment.
- let width = segment.words.iter().map(|w| w.to_string().chars().count()).max().unwrap_or(0);
- for word in &segment.words {
- let string = word.to_string();
- println!(" {string:>w$}", w=width as usize);
- }
-}
diff --git a/src/stages/compiler.rs b/src/stages/compiler.rs
index 1476ec8..b4680c2 100644
--- a/src/stages/compiler.rs
+++ b/src/stages/compiler.rs
@@ -16,10 +16,12 @@ pub fn parse_symbols(source_code: &str, path: Option<&Path>) -> Option<Vec<Symbo
Ok(syntactic) => syntactic,
Err(_errors) => return None,
};
+
let semantic = match parse_semantic(syntactic) {
Ok(semantic) => semantic,
Err(_errors) => return None,
};
+
// Convert symbols to the format required by the assembler library.
let parsed = SymbolParser::new().parse(&semantic);
let mut symbols = Vec::new();
diff --git a/src/stages/intermediate.rs b/src/stages/intermediate.rs
index cfe33b7..7523baf 100644
--- a/src/stages/intermediate.rs
+++ b/src/stages/intermediate.rs
@@ -1,163 +1,170 @@
use crate::*;
-use assembler::{DefinitionType, SourceLocation, SourcePosition, SymbolRole};
-
use indexmap::{IndexSet, IndexMap};
-static mut ID: usize = 0;
-macro_rules! next_id { () => { unsafe { let id = ID; ID += 1; id }}; }
+static mut ENVIRONMENT_ID: usize = 0;
+macro_rules! next_id {
+ () => {
+ unsafe {
+ let id = ENVIRONMENT_ID;
+ ENVIRONMENT_ID += 1;
+ id
+ }
+ };
+}
+
+pub struct LabelAddress {
+ pub previous: usize,
+ pub current: usize,
+ pub touched: bool,
+}
+
+#[derive(Eq, Hash, PartialEq, Clone)]
+pub struct SymbolSignature {
+ pub name: String,
+ pub arg_count: usize,
+}
+
+impl std::fmt::Display for SymbolSignature {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(f, "{}::{}", self.name, self.arg_count)
+ }
+}
+
+struct Environment {
+ arguments: IndexMap<String, Tracked<IntermediateValue>>,
+ id: usize,
+}
+
pub fn parse_intermediate(semantic: Vec<Tracked<SemanticToken>>) -> Result<Vec<Tracked<IntermediateToken>>, Vec<Tracked<IntermediateError>>> {
IntermediateParser::new(semantic).parse()
}
-
struct IntermediateParser {
semantic: Vec<Tracked<SemanticToken>>,
- label_names: IndexSet<Tracked<String>>,
- macro_names: IndexSet<Tracked<String>>,
- macro_definitions: IndexMap<String, MacroDefinition>,
+ label_names: IndexSet<Tracked<SymbolSignature>>,
+ label_addresses: IndexMap<Tracked<String>, LabelAddress>,
+ macro_definitions: IndexMap<SymbolSignature, Tracked<MacroDefinition>>,
+ environment_stack: Vec<Environment>,
+ address: usize,
intermediate: Vec<Tracked<IntermediateToken>>,
errors: Vec<Tracked<IntermediateError>>,
}
impl IntermediateParser {
- pub fn new(semantic: Vec<Tracked<SemanticToken>>) -> Self {
+ /// Initialise a new parser.
+ pub fn new(mut semantic: Vec<Tracked<SemanticToken>>) -> Self {
+ // Collect all label and macro definitions ahead of time.
let mut label_names = IndexSet::new();
- let mut macro_names = IndexSet::new();
+ let mut macro_definitions = IndexMap::new();
+ // Get the signatures of all defined labels.
for symbol in SymbolParser::new().parse(&semantic) {
- match symbol.role {
- SymbolRole::Definition(DefinitionType::MustPrecedeReference) => {
- // Only consider macro definitions, not macro argument definitions.
- if symbol.namespace.is_empty() {
- if !macro_names.insert(Tracked::from(symbol.name.clone(), symbol.source)) {
- unreachable!("Uncaught duplicate macro definition '{}'", symbol.name);
- }
- }
- }
- SymbolRole::Definition(DefinitionType::CanFollowReference) => {
- if !label_names.insert(Tracked::from(symbol.name.clone(), symbol.source)) {
- unreachable!("Uncaught duplicate label definition '{}'", symbol.name);
- }
+ if let SymbolRoleDetailed::LabelDefinition = symbol.role {
+ let signature = SymbolSignature { name: symbol.name.clone(), arg_count: symbol.arg_count };
+ if !label_names.insert(Tracked::from(signature.clone(), symbol.source)) {
+ unreachable!("Uncaught duplicate label definition '{signature}'");
}
- SymbolRole::Reference => (),
}
}
-
+ // Strip all semantic macro definitions from the semantic tokens.
+ let definitions = semantic.extract_if(..,
+ |t| if let SemanticToken::MacroDefinition(_) = t.value { true } else { false });
+ for definition in definitions {
+ let source = definition.source;
+ let SemanticToken::MacroDefinition(definition) = definition.value else { unreachable!() };
+ let name = definition.name.value.clone();
+ let arg_count = definition.arguments.len();
+ let signature = SymbolSignature { name, arg_count };
+ if macro_definitions.insert(signature.clone(), Tracked::from(definition, source)).is_some() {
+ unreachable!("Uncaught duplicate macro definition '{signature}'")
+ }
+ }
Self {
semantic,
label_names,
- macro_names,
- macro_definitions: IndexMap::new(),
+ label_addresses: IndexMap::new(),
+ macro_definitions,
+ environment_stack: Vec::new(),
+ address: 0,
intermediate: Vec::new(),
errors: Vec::new(),
}
}
pub fn parse(mut self) -> Result<Vec<Tracked<IntermediateToken>>, Vec<Tracked<IntermediateError>>> {
- for token in self.semantic {
- let source = &token.source;
- match token.value {
- SemanticToken::MacroDefinition(definition) => {
- // Invoke the body to see if it contains undefined macros.
- let error_count = self.errors.len();
- let mut arguments = IndexMap::new();
- // Prepare dummy argument values.
- let null = SourceSpan {
- string: String::new(),
- in_merged: SourceLocation {
- path: None,
- start: SourcePosition::ZERO,
- end: SourcePosition::ZERO,
- },
- in_source: None,
- child: None,
- };
- for argument in &definition.arguments {
- let value = match argument.variant {
- ArgumentType::Integer => {
- let integer = IntermediateInteger::Integer(0);
- let tracked = Tracked::from(integer, null.clone());
- IntermediateValue::Integer(tracked)
- }
- ArgumentType::Block => {
- IntermediateValue::Block(Vec::new())
- }
- ArgumentType::String => {
- let string = String::new();
- let chars = Vec::new();
- let literal = StringLiteral { string, chars };
- let tracked = Tracked::from(literal, null.clone());
- IntermediateValue::String(tracked)
- }
- };
- let tracked = Tracked::from(value, null.clone());
- arguments.insert(argument.name.clone(), tracked);
- }
- // Register macro definition with empty body so that macro can invoke itself.
- let name = definition.name.to_string();
- let dummy_definition = MacroDefinition {
- name: definition.name,
- arguments: definition.arguments,
- body: MacroDefinitionBody::Block(Vec::new()),
- };
- if self.macro_definitions.insert(name.clone(), dummy_definition).is_some() {
- unreachable!("Uncaught duplicate macro definition '{}'", name);
- }
- let mut env = Environment {
- label_names: &self.label_names,
- macro_names: &self.macro_names,
- macro_definitions: &self.macro_definitions,
- arguments,
- errors: &mut self.errors,
- id: next_id!(),
- };
- env.parse_macro_definition_body(&definition.body, source);
- if self.errors.len() != error_count {
- break;
- }
- // Replace dummy macro body with original macro body.
- if let Some(registered) = self.macro_definitions.get_mut(&name) {
- registered.body = definition.body;
- }
- }
- SemanticToken::BlockToken(block_token) => {
- let mut env = Environment {
- label_names: &self.label_names,
- macro_names: &self.macro_names,
- macro_definitions: &self.macro_definitions,
- arguments: IndexMap::new(),
- errors: &mut self.errors,
- id: next_id!(),
- };
- let mut tokens = env.parse_block_token(&block_token, source);
- self.intermediate.append(&mut tokens);
- }
+ let semantic = std::mem::take(&mut self.semantic);
+
+ for i in 0..MAX_ITERATIONS_TO_STABILISE {
+ info!("Attempting iteration {} of intermediate assembly stage", i+1);
+ // Erase the previous parse attempt.
+ self.address = 0;
+ self.intermediate.clear();
+ self.errors.clear();
+ self.environment_stack.clear();
+ unsafe { ENVIRONMENT_ID = 0; }
+ // Update label addresses.
+ for (_, address) in &mut self.label_addresses {
+ address.previous = address.current;
+ address.touched = false;
}
+ // Attempt to parse the program (which is now all block tokens).
+ for token in &semantic {
+ let source = &token.source;
+ let SemanticToken::BlockToken(ref block_token) = token.value else { unreachable!() };
+ let env = Environment { arguments: IndexMap::new(), id: next_id!() };
+ self.environment_stack.push(env);
+ let mut tokens = self.parse_block_token(&block_token, source);
+ self.intermediate.append(&mut tokens);
+ }
+ // Return unrecoverable errors.
+ if !self.errors.is_empty() {
+ return Err(self.errors);
+ }
+ // Check label stability
+ if self.check_for_instability(false) {
+ continue;
+ }
+ // Program is stable, return.
+ info!("Stabilised in iteration {} of intermediate assembly stage", i+1);
+ return Ok(self.intermediate);
}
- match self.errors.is_empty() {
- true => Ok(self.intermediate),
- false => Err(self.errors),
- }
+
+ self.check_for_instability(true);
+ return Err(self.errors);
}
-}
+ /// Check if any label is still stabilising.
+ fn check_for_instability(&mut self, create_error: bool) -> bool {
+ for (name, address) in &self.label_addresses {
+ if address.touched && address.current != address.previous {
+ info!("Label '{name}' was unstable, moving from address 0x{:04x} to 0x{:04x}",
+ address.previous, address.current);
+ if create_error {
+ let error = IntermediateError::LabelNeverStabilised(name.to_string());
+ self.errors.push(Tracked::from(error, name.source.clone()));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
-struct Environment<'a> {
- label_names: &'a IndexSet<Tracked<String>>,
- macro_names: &'a IndexSet<Tracked<String>>,
- macro_definitions: &'a IndexMap<String, MacroDefinition>,
- arguments: IndexMap<String, Tracked<IntermediateValue>>,
- errors: &'a mut Vec<Tracked<IntermediateError>>,
- id: usize,
-}
+ /// Get the current environment (the environment at the top of the stack).
+ fn env(&self) -> &Environment {
+ self.environment_stack.last().unwrap_or_else(||
+ unreachable!("No environment on the stack"))
+ }
-impl<'a> Environment<'a> {
- // Attach the invocation ID to every macro label name
+ // Attach the environment ID to a local label name.
fn tag_name(&self, name: &str) -> String {
+ // If a local label belongs to a macro, the name of that macro
+ // has been prefixed with the local label name in the
+ // resolve_label_name method during the semantic parsing stage,
+ // using a ':' character as a separator.
match name.contains(':') {
- true => format!("{name}:{}", self.id),
+ true => format!("{name}:{}", self.env().id),
false => name.to_string(),
}
}
@@ -166,7 +173,7 @@ impl<'a> Environment<'a> {
match &body {
MacroDefinitionBody::Integer(integer) => {
let token = self.parse_integer_token(&integer, &source)?;
- let integer = IntermediateValue::Integer(token);
+ let integer = IntermediateValue::Integer(token.value);
Some(Tracked::from(integer, source.clone()))
}
MacroDefinitionBody::Invocation(invocation) => {
@@ -180,9 +187,9 @@ impl<'a> Environment<'a> {
let value = IntermediateValue::Block(tokens);
Some(Tracked::from(value, source.clone()))
}
- MacroDefinitionBody::String(string) => {
- let string = self.parse_string_token(string, &source)?;
- let integer = IntermediateValue::String(string);
+ MacroDefinitionBody::List(list) => {
+ let list = self.parse_list_token(list, &source)?;
+ let integer = IntermediateValue::List(list.value);
Some(Tracked::from(integer, source.clone()))
}
}
@@ -192,60 +199,77 @@ impl<'a> Environment<'a> {
let mut intermediate = Vec::new();
match block {
BlockToken::LabelDefinition(name) => {
- let token = IntermediateToken::LabelDefinition(self.tag_name(name));
- intermediate.push(Tracked::from(token, source.clone()));
+ let signature = SymbolSignature { name: name.to_string(), arg_count: 0 };
+ if !self.label_names.contains(&signature) {
+ unreachable!("Unrecognised name for label definition");
+ }
+ let tagged_name = self.tag_name(name);
+ let tracked = Tracked::from(tagged_name.clone(), source.clone());
+ self.label_addresses.entry(tracked)
+ .and_modify(|a| {
+ if a.touched { unreachable!("Label '{tagged_name}' was already touched during this cycle.") }
+ a.previous = a.current;
+ a.current = self.address;
+ a.touched = true;
+ })
+ .or_insert(LabelAddress {
+ previous: 0,
+ current: self.address,
+ touched: true,
+ });
}
- BlockToken::PinnedAddress(address) => {
- if let Some(integer) = self.parse_integer_token(address, &address.source) {
- let token = IntermediateToken::PinnedAddress(integer);
+ BlockToken::PinnedAddress(integer) => {
+ if let Some(pinned) = self.parse_integer_token(integer, &integer.source) {
+ let pinned = **pinned as usize;
+ if pinned < self.address {
+ let error = IntermediateError::PinnedAddressBacktrack(pinned, self.address);
+ self.errors.push(Tracked::from(error, source.clone()));
+ }
+ self.address = pinned;
+ let token = IntermediateToken::PinnedAddress(pinned);
intermediate.push(Tracked::from(token, source.clone()));
}
}
BlockToken::ConditionalBlock(cond) => {
if let Some(predicate) = self.parse_integer_token(&cond.predicate, &cond.predicate.source) {
- if let Some(source) = integer_contains_label_reference(&predicate) {
- let error = IntermediateError::LabelReferenceInConditionPredicate;
- let new_source = cond.predicate.source.clone().wrap(source);
- self.errors.push(Tracked::from(error, new_source));
- } else {
- match evaluate_integer(&predicate, &cond.predicate.source) {
- Ok(value) => if value != 0 {
- let mut body = self.parse_block_token(&cond.body, &cond.body.source);
- if let Some(source) = block_contains_label_definition(&cond.body, &cond.body.source) {
- let error = IntermediateError::LabelDefinitionInConditionBody;
- let new_source = cond.body.source.clone().wrap(source);
- self.errors.push(Tracked::from(error, new_source));
- } else {
- intermediate.append(&mut body);
- }
- },
- Err(error) => self.errors.push(error),
+ if **predicate != 0 {
+ if let Some(label_source) = block_contains_label_definition(&cond.body, &cond.body.source) {
+ let error = IntermediateError::LabelDefinitionInConditionBody;
+ let new_source = cond.body.source.clone().wrap(label_source);
+ self.errors.push(Tracked::from(error, new_source));
+ } else {
+ let mut body = self.parse_block_token(&cond.body, &cond.body.source);
+ intermediate.append(&mut body);
}
- };
+ }
}
}
BlockToken::WordTemplate(word_template) => {
- let mut fields = Vec::new();
- for bit_field in &word_template.fields {
- let name = bit_field.name.to_string();
- let source = &bit_field.source;
+ let word_width = word_template.width;
+ let mut word_value = word_template.value;
+ for field in &word_template.fields {
+ let name = field.name.to_string();
+ let field_source = &field.source;
let invocation = Invocation { name, arguments: Vec::new() };
- if let Some(value) = self.parse_integer_invocation(&invocation, source) {
- let field = IntermediateField {
- width: bit_field.width,
- shift: bit_field.shift,
- value,
- };
- fields.push(Tracked::from(field, bit_field.source.clone()));
+ if let Some(result) = self.parse_integer_invocation(&invocation, field_source) {
+ let field_value = **result;
+ // Inject field value into real value.
+ let value_width = width(field_value);
+ if field.width < value_width {
+ let error = IntermediateError::ValueTooWide(field.width, value_width);
+ let new_source = field_source.wrap(result.source);
+ self.errors.push(Tracked::from(error, new_source));
+ } 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 = IntermediateWord {
- value: word_template.value,
- width: word_template.width,
- fields,
- };
+ let word = IntermediateWord { width: word_width, value: word_value };
let token = IntermediateToken::Word(word);
intermediate.push(Tracked::from(token, source.clone()));
+ self.address += 1;
}
BlockToken::Block(blocks) => {
for block in blocks {
@@ -259,15 +283,14 @@ impl<'a> Environment<'a> {
}
}
}
-
return intermediate;
}
fn parse_integer_token(&mut self, integer: &IntegerToken, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> {
match integer {
IntegerToken::IntegerLiteral(value) => {
- let integer = IntermediateInteger::Integer(*value);
- Some(Tracked::from(integer, source.clone()))
+ let tracked = Tracked::from(*value, source.clone());
+ Some(Tracked::from(tracked, source.clone()))
}
IntegerToken::Expression(expression) => {
self.parse_expression(expression, source)
@@ -278,45 +301,61 @@ impl<'a> Environment<'a> {
}
}
- fn parse_string_token(&mut self, string: &StringToken, source: &SourceSpan) -> Option<Tracked<StringLiteral>> {
- match string {
- StringToken::StringLiteral(literal) => {
- Some(Tracked::from(literal.clone(), source.clone()))
+ fn parse_list_token(&mut self, list: &ListToken, source: &SourceSpan) -> Option<Tracked<IntermediateList>> {
+ match list {
+ ListToken::StringLiteral(literal) => {
+ Some(Tracked::from(literal.chars.clone(), source.clone()))
}
- StringToken::Invocation(invocation) => {
- self.parse_string_invocation(&invocation, source)
+ ListToken::ListLiteral(literal) => {
+ let mut integers = Vec::new();
+ for token in literal {
+ let integer = self.parse_integer_token(&token.value, &token.source)?;
+ integers.push(integer.value);
+ }
+ Some(Tracked::from(integers, source.clone()))
+ }
+ ListToken::Invocation(invocation) => {
+ self.parse_list_invocation(&invocation, source)
}
}
}
fn parse_integer_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> {
- match self.parse_invocation(invocation, source)?.value {
- IntermediateValue::Integer(integer) => Some(integer),
- IntermediateValue::Block(_) | IntermediateValue::String(_) => {
+ let result = self.parse_invocation(invocation, source)?;
+ match result.value {
+ IntermediateValue::Integer(integer) => {
+ let source = integer.source.clone();
+ Some(Tracked::from(integer, source))
+ }
+ IntermediateValue::Block(_) | IntermediateValue::List(_) => {
let error = IntermediateError::ExpectedInteger;
- self.errors.push(Tracked::from(error, source.clone()));
+ self.errors.push(Tracked::from(error, result.source));
None
}
}
}
fn parse_block_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Vec<Tracked<IntermediateToken>>> {
- match self.parse_invocation(invocation, source)?.value {
+ let result = self.parse_invocation(invocation, source)?;
+ let source = result.source;
+ match result.value {
IntermediateValue::Block(tokens) => Some(tokens),
- IntermediateValue::Integer(_) | IntermediateValue::String(_) => {
+ IntermediateValue::Integer(_) | IntermediateValue::List(_) => {
let error = IntermediateError::ExpectedBlock;
- self.errors.push(Tracked::from(error, source.clone()));
+ self.errors.push(Tracked::from(error, source));
None
}
}
}
- fn parse_string_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<StringLiteral>> {
- match self.parse_invocation(invocation, source)?.value {
- IntermediateValue::String(literal) => Some(literal),
+ fn parse_list_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateList>> {
+ let result = self.parse_invocation(invocation, source)?;
+ let source = result.source;
+ match result.value {
+ IntermediateValue::List(literal) => Some(Tracked::from(literal, source)),
IntermediateValue::Integer(_) | IntermediateValue::Block(_) => {
- let error = IntermediateError::ExpectedString;
- self.errors.push(Tracked::from(error, source.clone()));
+ let error = IntermediateError::ExpectedList;
+ self.errors.push(Tracked::from(error, source));
None
}
}
@@ -324,8 +363,13 @@ impl<'a> Environment<'a> {
fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateValue>> {
let received_count = invocation.arguments.len();
- if let Some(argument) = self.arguments.get(&invocation.name) {
- // This invocation is a macro argument
+ let signature = SymbolSignature {
+ name: invocation.name.clone(),
+ arg_count: invocation.arguments.len(),
+ };
+ if let Some(argument) = self.env().arguments.get(&invocation.name) {
+ // This invocation is a macro argument.
+ // Check that no arguments were provided.
if received_count != 0 {
let error = IntermediateError::IncorrectArgumentCount(0, received_count);
self.errors.push(Tracked::from(error, source.clone()));
@@ -333,21 +377,24 @@ impl<'a> Environment<'a> {
} else {
Some(argument.clone())
}
- } else if let Some(label_name) = self.label_names.get(&invocation.name) {
- // This invocation is a label reference
+ } else if let Some(label) = self.label_names.get(&signature) {
+ // This invocation is a label reference.
+ // Check that no arguments were provided.
if received_count != 0 {
let error = IntermediateError::IncorrectArgumentCount(0, received_count);
self.errors.push(Tracked::from(error, source.clone()));
None
} else {
- let name = self.tag_name(label_name);
- let tracked = Tracked::from(name, label_name.source.clone());
- let integer = IntermediateInteger::LabelReference(tracked);
- let tracked = Tracked::from(integer, source.clone());
- let value = IntermediateValue::Integer(tracked);
- Some(Tracked::from(value, source.clone()))
+ let tagged_name = self.tag_name(&signature.name);
+ let address = self.label_addresses.get(&tagged_name)
+ .and_then(|a| Some(a.current)).or(Some(0)).unwrap();
+ let tracked = Tracked::from(address as isize, label.source.clone());
+ let integer = IntermediateValue::Integer(tracked);
+ Some(Tracked::from(integer, source.clone()))
}
- } else if let Some(definition) = self.macro_definitions.get(&invocation.name) {
+ } else if let Some(definition) = self.macro_definitions.get(&signature) {
+ // This invocation is a macro reference.
+ let definition = definition.clone();
// Check that the correct number of arguments were provided.
let expected_count = definition.arguments.len();
if received_count != expected_count {
@@ -363,9 +410,9 @@ impl<'a> Environment<'a> {
arguments.push(received_value);
}
// Invoke the invocation multiple times.
- let repetitions = arguments.iter().map(|a| a.len()).max().unwrap_or(1);
+ let invocations = arguments.iter().map(|a| a.len()).max().unwrap_or(1);
let mut values = Vec::new();
- for i in 0..repetitions {
+ for i in 0..invocations {
// Construct an argument map for this invocation.
let mut argument_map = IndexMap::new();
for (a, argument) in arguments.iter().enumerate() {
@@ -392,16 +439,11 @@ impl<'a> Environment<'a> {
};
}
// Invoke the macro once.
- let mut env = Environment {
- label_names: &self.label_names,
- macro_names: &self.macro_names,
- macro_definitions: &self.macro_definitions,
- arguments: argument_map,
- errors: &mut self.errors,
- id: next_id!(),
- };
- // Save the result of this macro invocation.
- values.push(env.parse_macro_definition_body(&definition.body, source)?);
+ let env = Environment { arguments: argument_map, id: next_id!() };
+ self.environment_stack.push(env);
+ let result = self.parse_macro_definition_body(&definition.body, source);
+ self.environment_stack.pop().unwrap();
+ values.push(result?);
}
if values.len() == 1 {
// If the macro was invoked once, return the value.
@@ -412,7 +454,7 @@ impl<'a> Environment<'a> {
let mut block = Vec::new();
for value in values {
match value.value {
- IntermediateValue::Integer(_) | IntermediateValue::String(_) => {
+ IntermediateValue::Integer(_) | IntermediateValue::List(_) => {
let error = IntermediateError::ExpectedBlock;
self.errors.push(Tracked::from(error, value.source));
return None;
@@ -425,26 +467,21 @@ impl<'a> Environment<'a> {
Some(Tracked::from(IntermediateValue::Block(block), source.clone()))
}
}
- } else if let Some(macro_name) = self.macro_names.get(&invocation.name) {
- let error = IntermediateError::InvocationBeforeDefinition;
- let source = source.clone().wrap(macro_name.source.clone());
- self.errors.push(Tracked::from(error, source));
- None
} else {
- unreachable!("Uncaught unresolved reference '{}'", invocation.name);
+ unreachable!("Uncaught unresolved reference '{signature}'");
}
}
fn parse_invocation_argument(&mut self, argument: &Tracked<InvocationArgument>, expected_type: ArgumentType) -> Option<RepeatedArgument> {
let source = &argument.source;
let (received_value, received_type) = match &argument.value {
- InvocationArgument::StringToken(string) => {
- let string = self.parse_string_token(string, source)?;
- self.parse_invocation_string_argument(string, expected_type)?
+ InvocationArgument::ListToken(list) => {
+ let list = self.parse_list_token(list, source)?;
+ self.parse_invocation_list_argument(list, expected_type)?
}
InvocationArgument::IntegerToken(integer) => {
let tracked = self.parse_integer_token(&integer, &argument.source)?;
- let value = IntermediateValue::Integer(tracked);
+ let value = IntermediateValue::Integer(tracked.value);
(RepeatedArgument::Loop(value), ArgumentType::Integer)
}
InvocationArgument::BlockToken(block) => {
@@ -459,8 +496,8 @@ impl<'a> Environment<'a> {
(RepeatedArgument::Loop(value.value), ArgumentType::Integer),
IntermediateValue::Block(_) =>
(RepeatedArgument::Loop(value.value), ArgumentType::Block),
- IntermediateValue::String(string) =>
- self.parse_invocation_string_argument(string, expected_type)?
+ IntermediateValue::List(list) =>
+ self.parse_invocation_list_argument(Tracked::from(list, value.source), expected_type)?
}
}
};
@@ -472,100 +509,93 @@ impl<'a> Environment<'a> {
return Some(received_value);
}
- fn parse_invocation_string_argument(&mut self, string: Tracked<StringLiteral>, expected_type: ArgumentType) -> Option<(RepeatedArgument, ArgumentType)> {
+ fn parse_invocation_list_argument(&mut self, list: Tracked<IntermediateList>, expected_type: ArgumentType) -> Option<(RepeatedArgument, ArgumentType)> {
if let ArgumentType::Integer = expected_type {
let mut values = Vec::new();
- for c in &string.chars {
- let integer = IntermediateInteger::Integer(**c);
- let tracked = Tracked::from(integer, c.source.clone());
- values.push(IntermediateValue::Integer(tracked));
+ for value in &list.value {
+ values.push(IntermediateValue::Integer(value.clone()));
}
Some((RepeatedArgument::List(values), ArgumentType::Integer))
} else {
- let value = IntermediateValue::String(string);
- Some((RepeatedArgument::Loop(value), ArgumentType::String))
+ let value = IntermediateValue::List(list.value);
+ Some((RepeatedArgument::Loop(value), ArgumentType::List))
}
}
fn parse_expression(&mut self, expression: &Expression, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> {
- let mut intermediate = Vec::new();
- let mut error = false;
-
+ let mut stack = ExpressionStack::new();
for token in &expression.tokens {
let source = &token.source;
match &token.value {
- ExpressionToken::IntegerToken(integer) => {
- let Some(integer) = self.parse_integer_token(integer, source) else {
- error = true; continue;
- };
- let token = IntermediateExpressionToken::Integer(integer.value);
- intermediate.push(Tracked::from(token, integer.source));
+ ExpressionToken::IntegerToken(token) => {
+ let integer = self.parse_integer_token(token, source)?;
+ stack.push(IntermediateValue::Integer(integer.value));
}
- ExpressionToken::Operator(operator) => {
- let token = IntermediateExpressionToken::Operator(*operator);
- intermediate.push(Tracked::from(token, source.clone()));
+ ExpressionToken::ListToken(token) => {
+ let list = self.parse_list_token(token, source)?;
+ stack.push(IntermediateValue::List(list.value));
}
ExpressionToken::Invocation(invocation) => {
- let Some(integer) = self.parse_integer_invocation(invocation, source) else {
- error = true; continue;
- };
- let token = IntermediateExpressionToken::Integer(integer.value);
- intermediate.push(Tracked::from(token, integer.source));
+ if let Some(value) = self.parse_invocation(invocation, source) {
+ if let IntermediateValue::Block(_) = &value.value {
+ let error = IntermediateError::InvalidBlockInExpression;
+ let new_source = source.clone().wrap(value.source.clone());
+ self.errors.push(Tracked::from(error, new_source));
+ return None;
+ }
+ stack.push(value.value);
+ }
+ }
+ ExpressionToken::Operator(operator) => {
+ if let Err(expr_error) = stack.apply(*operator, source) {
+ let error = IntermediateError::ExpressionError(expr_error);
+ self.errors.push(Tracked::from(error, source.clone()));
+ return None;
+ }
}
}
}
-
- if error { return None; }
- let expression = IntermediateExpression { tokens: intermediate };
- let integer = IntermediateInteger::Expression(expression);
- Some(Tracked::from(integer, source.clone()))
+ match stack.pull_result() {
+ Ok(value) => {
+ let tracked = Tracked::from(value, source.clone());
+ Some(Tracked::from(tracked, source.clone()))
+ }
+ Err(expr_error) => {
+ let tracked = Tracked::from(expr_error, source.clone());
+ let error = IntermediateError::ExpressionError(tracked);
+ self.errors.push(Tracked::from(error, source.clone()));
+ None
+ }
+ }
}
}
+
macro_rules! return_some {
($option:expr) => {
if $option.is_some() { return $option; }
};
}
-fn integer_contains_label_reference(integer: &IntermediateInteger) -> Option<SourceSpan> {
- match integer {
- IntermediateInteger::Integer(_) => None,
- IntermediateInteger::LabelReference(label) => Some(label.source.clone()),
- IntermediateInteger::Expression(expr) => expression_contains_label_reference(expr),
- }
-}
-
-fn expression_contains_label_reference(expression: &IntermediateExpression) -> Option<SourceSpan> {
- for token in &expression.tokens {
- if let IntermediateExpressionToken::Integer(integer) = &token.value {
- if let Some(child) = integer_contains_label_reference(&integer) {
- return Some(token.source.clone().wrap(child));
- }
- }
- }
- return None;
-}
+// Check if a block token contains a label definition.
fn block_contains_label_definition(block: &BlockToken, source: &SourceSpan) -> Option<SourceSpan> {
match &block {
- BlockToken::LabelDefinition(_) => {
- return Some(source.clone());
- }
- BlockToken::Invocation(invocation) => {
- return_some!(invocation_contains_label_definition(invocation))
- }
- BlockToken::Block(blocks) => {
+ BlockToken::LabelDefinition(_) =>
+ return Some(source.clone()),
+ BlockToken::Invocation(invocation) =>
+ return_some!(invocation_contains_label_definition(invocation)),
+ BlockToken::Block(blocks) =>
for block in blocks {
return_some!(block_contains_label_definition(block, &block.source))
- }
- }
+ },
_ => (),
}
return None;
}
+// Check if the arguments passed to an invocation contain a label definition.
fn invocation_contains_label_definition(invocation: &Invocation) -> Option<SourceSpan> {
for argument in &invocation.arguments {
match &argument.value {
@@ -580,45 +610,3 @@ fn invocation_contains_label_definition(invocation: &Invocation) -> Option<Sourc
}
return None;
}
-
-fn evaluate_integer(integer: &IntermediateInteger, source: &SourceSpan) -> Result<isize, Tracked<IntermediateError>> {
- match integer {
- IntermediateInteger::Integer(value) => Ok(*value),
- IntermediateInteger::LabelReference(name) =>
- unreachable!("Uncaught label reference '{name}' in condition predicate or pinned address value"),
- IntermediateInteger::Expression(expr) => evaluate_expression(expr, source),
- }
-}
-
-fn evaluate_expression(expression: &IntermediateExpression, source: &SourceSpan) -> Result<isize, Tracked<IntermediateError>> {
- 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(evaluate_expression(&expression, source)?);
- }
- IntermediateInteger::LabelReference(name) => {
- unreachable!("Uncaught label reference '{name}' in condition predicate");
- }
- }
- IntermediateExpressionToken::Operator(operator) => {
- if let Err(stack_error) = stack.apply(*operator, source) {
- let error = IntermediateError::StackError(stack_error);
- return Err(Tracked::from(error, token.source.clone()));
- }
- }
- }
- }
- match stack.pull_result() {
- Ok(value) => Ok(value),
- Err(err) => {
- let error = Tracked::from(err, source.clone());
- Err(Tracked::from(IntermediateError::StackError(error), source.clone()))
- }
- }
-}
diff --git a/src/stages/intermediate_tokens.rs b/src/stages/intermediate_tokens.rs
index 8b7cfaf..71dbd62 100644
--- a/src/stages/intermediate_tokens.rs
+++ b/src/stages/intermediate_tokens.rs
@@ -4,8 +4,7 @@ use crate::*;
#[derive(Clone)]
pub enum IntermediateToken {
Word(IntermediateWord),
- PinnedAddress(Tracked<IntermediateInteger>),
- LabelDefinition(String),
+ PinnedAddress(usize),
}
#[derive(Clone)]
@@ -13,42 +12,18 @@ pub struct IntermediateWord {
pub value: usize,
/// Width of the word in bits.
pub width: u32,
- pub fields: Vec<Tracked<IntermediateField>>,
}
#[derive(Clone)]
-pub struct IntermediateField {
- pub value: Tracked<IntermediateInteger>,
- /// Width of the field in bits.
- pub width: u32,
- /// Number of bits to the right of the field in the word.
- pub shift: u32,
-}
-
-#[derive(Clone)]
-pub enum IntermediateInteger {
- Integer(isize),
- Expression(IntermediateExpression),
- LabelReference(Tracked<String>),
-}
-
-#[derive(Clone)]
-pub struct IntermediateExpression {
- pub tokens: Vec<Tracked<IntermediateExpressionToken>>,
-}
-
-#[derive(Clone)]
-pub enum IntermediateExpressionToken {
+pub enum IntermediateValue {
Integer(IntermediateInteger),
- Operator(Operator),
+ List(IntermediateList),
+ Block(IntermediateBlock),
}
-#[derive(Clone)]
-pub enum IntermediateValue {
- Integer(Tracked<IntermediateInteger>),
- Block(Vec<Tracked<IntermediateToken>>),
- String(Tracked<StringLiteral>),
-}
+pub type IntermediateInteger = Tracked<isize>;
+pub type IntermediateList = Vec<Tracked<isize>>;
+pub type IntermediateBlock = Vec<Tracked<IntermediateToken>>;
pub enum RepeatedArgument {
Loop(IntermediateValue),
@@ -67,17 +42,21 @@ impl RepeatedArgument {
pub enum IntermediateError {
ExpectedInteger,
ExpectedBlock,
- ExpectedString,
+ ExpectedList,
ListExhausted,
- LabelReferenceInConditionPredicate,
LabelDefinitionInConditionBody,
LabelReferenceInPinnedAddress,
- StackError(Tracked<StackError>),
- InvocationBeforeDefinition,
+ InvalidBlockInExpression,
+ ExpressionError(Tracked<ExpressionError>),
/// expected, received
IncorrectArgumentCount(usize, usize),
/// expected, received
IncorrectArgumentType(ArgumentType, ArgumentType),
+ /// pinned, real
+ PinnedAddressBacktrack(usize, usize),
+ /// expected, received
+ ValueTooWide(u32, u32),
+ LabelNeverStabilised(String),
}
pub fn report_intermediate_errors(errors: &[Tracked<IntermediateError>], source_code: &str) {
@@ -93,24 +72,30 @@ fn report_intermediate_error(error: &Tracked<IntermediateError>, source_code: &s
"An integer value was expected here",
IntermediateError::ExpectedBlock =>
"A block value was expected here",
- IntermediateError::ExpectedString =>
+ IntermediateError::ExpectedList =>
"A string value was expected here",
IntermediateError::ListExhausted =>
- "This string is shorter than another string passed to the same invocation",
- IntermediateError::LabelReferenceInConditionPredicate =>
- "The predicate of a conditional block cannot contain a label reference",
+ "This list is shorter than another list passed to the same invocation",
IntermediateError::LabelDefinitionInConditionBody =>
"The body of a conditional block cannot contain a label definition",
IntermediateError::LabelReferenceInPinnedAddress =>
"The value of a pinned address cannot contain a label reference",
- IntermediateError::StackError(stack_error) => {
- report_stack_error(stack_error, source_code); return; },
- IntermediateError::InvocationBeforeDefinition =>
- &format!("Macro cannot be invoked before it has been defined"),
+ IntermediateError::InvalidBlockInExpression =>
+ "Expression cannot contain an invocation that expands to a block value",
+
+ IntermediateError::ExpressionError(expression_error) => {
+ report_expression_error(expression_error, source_code); return; },
IntermediateError::IncorrectArgumentCount(expected, received) =>
&format!("Expected {expected} arguments, but received {received} instead"),
IntermediateError::IncorrectArgumentType(expected, received) =>
&format!("Expected {expected} value but received {received} value instead"),
+ IntermediateError::PinnedAddressBacktrack(pinned, real) =>
+ &format!("Cannot pin to address {pinned} when address is already {real}"),
+ IntermediateError::ValueTooWide(expected, received) =>
+ &format!("Field is {expected} bits wide, but received a value that is {received} bits wide"),
+
+ IntermediateError::LabelNeverStabilised(name) =>
+ &format!("Label '{name}' never stabilised"),
};
report_source_issue(LogLevel::Error, &context, message);
@@ -121,35 +106,10 @@ pub fn print_intermediate_token(i: usize, token: &IntermediateToken) {
match token {
IntermediateToken::Word(word) => {
indent!(i, "Word({:>0w$b})", word.value, w = word.width as usize);
- for field in &word.fields {
- print_intermediate_integer(i+1, &field.value.value);
- }
}
IntermediateToken::PinnedAddress(address) => {
- indent!(i, "PinnedAddress");
- print_intermediate_integer(i+1, address);
+ indent!(i, "PinnedAddress({address})");
}
- IntermediateToken::LabelDefinition(name) =>
- indent!(i, "LabelDefinition({name})"),
}
}
-fn print_intermediate_integer(i: usize, integer: &IntermediateInteger) {
- match integer {
- IntermediateInteger::Integer(value) =>
- indent!(i, "Integer({value})"),
- IntermediateInteger::LabelReference(name) =>
- indent!(i, "LabelReference({name})"),
- IntermediateInteger::Expression(expression) => {
- indent!(i, "Expression");
- for token in &expression.tokens {
- match &token.value {
- IntermediateExpressionToken::Integer(integer) =>
- print_intermediate_integer(i+1, integer),
- IntermediateExpressionToken::Operator(operator) =>
- indent!(i+1, "Operator({operator})"),
- }
- }
- }
- }
-}
diff --git a/src/stages/mod.rs b/src/stages/mod.rs
index 571fd65..bf0d5a6 100644
--- a/src/stages/mod.rs
+++ b/src/stages/mod.rs
@@ -5,8 +5,6 @@ mod semantic;
mod semantic_tokens;
mod intermediate;
mod intermediate_tokens;
-mod bytecode;
-mod bytecode_tokens;
pub use compiler::*;
pub use syntactic::*;
@@ -15,8 +13,6 @@ pub use semantic::*;
pub use semantic_tokens::*;
pub use intermediate::*;
pub use intermediate_tokens::*;
-pub use bytecode::*;
-pub use bytecode_tokens::*;
#[macro_export]
diff --git a/src/stages/semantic.rs b/src/stages/semantic.rs
index fdd1766..96cf7af 100644
--- a/src/stages/semantic.rs
+++ b/src/stages/semantic.rs
@@ -306,22 +306,44 @@ impl SemanticParser {
}
}
- /// Parse the remaining syntactic tokens as the contents of a block.
- fn parse_block(&mut self) -> Vec<Tracked<BlockToken>> {
- let mut tokens = Vec::new();
- while !self.syntactic.is_empty() {
- if let Some(token) = self.pull_block_token(SemanticLocation::BlockLiteral) {
- tokens.push(token);
+ /// Attempt to pull a token that can be used in an expression.
+ fn pull_expression_token(&mut self) -> Option<Tracked<ExpressionToken>> {
+ match self.pull_macro_definition_body_token()? {
+ MacroDefinitionBody::Block(mut tokens) => {
+ assert_eq!(tokens.len(), 1);
+ let token = tokens.pop().unwrap();
+ let error = SemanticError::InvalidBlockInExpression;
+ self.errors.push(Tracked::from(error, token.source));
+ None
+ }
+ MacroDefinitionBody::Invocation(invocation) => {
+ // Attempt to parse the invocation as an operator.
+ if invocation.arguments.is_empty() {
+ if let Some(operator) = Operator::from_str(&invocation.name) {
+ let expr = ExpressionToken::Operator(operator);
+ return Some(Tracked::from(expr, invocation.source))
+ }
+ }
+ // Parse invocation as an invocation.
+ let expr = ExpressionToken::Invocation(invocation.value);
+ Some(Tracked::from(expr, invocation.source))
+ }
+ MacroDefinitionBody::Integer(integer) => {
+ let expr = ExpressionToken::IntegerToken(Box::new(integer.value));
+ Some(Tracked::from(expr, integer.source))
+ }
+ MacroDefinitionBody::List(list) => {
+ let expr = ExpressionToken::ListToken(list.value);
+ Some(Tracked::from(expr, list.source))
}
}
- tokens
}
- /// Parse the remaining syntactic tokens as a list of integer tokens.
- fn parse_integer_list(&mut self, location: SemanticLocation) -> Vec<Tracked<IntegerToken>> {
+ /// Parse the remaining syntactic tokens as the contents of a block.
+ fn parse_block(&mut self) -> Vec<Tracked<BlockToken>> {
let mut tokens = Vec::new();
while !self.syntactic.is_empty() {
- if let Some(token) = self.pull_integer_token(location) {
+ if let Some(token) = self.pull_block_token(SemanticLocation::BlockLiteral) {
tokens.push(token);
}
}
@@ -331,34 +353,8 @@ impl SemanticParser {
/// Parse the remaining syntactic tokens as the contents of an expression.
fn parse_expression(&mut self) -> Expression {
let mut tokens = Vec::new();
- for token in self.parse_integer_list(SemanticLocation::Expression) {
- let source = token.source;
- match token.value {
- IntegerToken::IntegerLiteral(value) => {
- let integer = Box::new(IntegerToken::IntegerLiteral(value));
- let token = ExpressionToken::IntegerToken(integer);
- tokens.push(Tracked::from(token, source));
- }
- IntegerToken::Expression(expression) => {
- let integer = Box::new(IntegerToken::Expression(expression));
- let token = ExpressionToken::IntegerToken(integer);
- tokens.push(Tracked::from(token, source));
- }
- IntegerToken::Invocation(invocation) => {
- // Parse the invocation as an operator instead.
- if invocation.arguments.is_empty() {
- if let Some(operator) = Operator::from_str(&invocation.name) {
- let token = ExpressionToken::Operator(operator);
- tokens.push(Tracked::from(token, source));
- continue;
- }
- }
- // Parse the invocation as an invocation.
- let integer = Box::new(IntegerToken::Invocation(invocation));
- let token = ExpressionToken::IntegerToken(integer);
- tokens.push(Tracked::from(token, source));
- }
- }
+ while let Some(token) = self.pull_expression_token() {
+ tokens.push(token);
}
Expression { tokens }
}
diff --git a/src/stages/semantic_tokens.rs b/src/stages/semantic_tokens.rs
index a873df0..7aa6093 100644
--- a/src/stages/semantic_tokens.rs
+++ b/src/stages/semantic_tokens.rs
@@ -6,12 +6,14 @@ pub enum SemanticToken {
BlockToken(BlockToken),
}
+#[derive(Clone)]
pub struct MacroDefinition {
pub name: Tracked<String>,
pub arguments: Vec<Tracked<ArgumentDefinition>>,
pub body: MacroDefinitionBody,
}
+#[derive(Clone)]
pub struct ArgumentDefinition {
pub name: String,
pub variant: ArgumentType,
@@ -34,6 +36,7 @@ impl std::fmt::Display for ArgumentType {
}
}
+#[derive(Clone)]
pub enum MacroDefinitionBody {
Integer(Tracked<IntegerToken>),
Block(Vec<Tracked<BlockToken>>),
@@ -41,21 +44,25 @@ pub enum MacroDefinitionBody {
Invocation(Tracked<Invocation>),
}
+#[derive(Clone)]
pub struct ConditionalBlock {
pub predicate: Tracked<IntegerToken>,
pub body: Tracked<BlockToken>,
}
+#[derive(Clone)]
pub enum IntegerToken {
IntegerLiteral(isize),
Expression(Expression),
Invocation(Invocation),
}
+#[derive(Clone)]
pub struct Expression {
pub tokens: Vec<Tracked<ExpressionToken>>,
}
+#[derive(Clone)]
pub enum ExpressionToken {
IntegerToken(Box<IntegerToken>),
ListToken(ListToken),
@@ -63,6 +70,7 @@ pub enum ExpressionToken {
Operator(Operator),
}
+#[derive(Clone)]
pub enum BlockToken {
LabelDefinition(String),
PinnedAddress(Tracked<IntegerToken>),
@@ -72,17 +80,20 @@ pub enum BlockToken {
Invocation(Invocation),
}
+#[derive(Clone)]
pub enum ListToken {
StringLiteral(StringLiteral),
ListLiteral(Vec<Tracked<IntegerToken>>),
Invocation(Invocation),
}
+#[derive(Clone)]
pub struct Invocation {
pub name: String,
pub arguments: Vec<Tracked<InvocationArgument>>,
}
+#[derive(Clone)]
pub enum InvocationArgument {
IntegerToken(IntegerToken),
BlockToken(BlockToken),
@@ -137,6 +148,7 @@ pub enum SemanticError {
InvalidArgumentDefinition,
InvalidInvocationArgument,
+ InvalidBlockInExpression,
GlobalLabelInMacroDefinition,
LocalLabelWithoutNamespace,
@@ -202,6 +214,8 @@ fn report_semantic_error(error: &Tracked<SemanticError>, source_code: &str) {
"Argument definition must take one of the following forms: name, {name}, \"name\", or [name]",
SemanticError::InvalidInvocationArgument =>
"This token cannot be used in an invocation argument",
+ SemanticError::InvalidBlockInExpression =>
+ "Expression cannot contain a block token",
SemanticError::GlobalLabelInMacroDefinition =>
&format!("Macro definition cannot contain a global label"),