diff options
author | Ben Bridle <ben@derelict.engineering> | 2025-10-14 20:40:39 +1300 |
---|---|---|
committer | Ben Bridle <ben@derelict.engineering> | 2025-10-14 21:05:05 +1300 |
commit | 981bb70e5077bd30ef85a0092117a875dcc614fc (patch) | |
tree | 45e614de74d17071ca1e68098df4d32266df85a3 /src/stages | |
parent | ace5677f87c2bc042d8d5e807ccea9ddcd828c9e (diff) | |
download | torque-asm-981bb70e5077bd30ef85a0092117a875dcc614fc.zip |
Implement new intermediate stage
Massive improvement. Label references can be used anywhere in the
program, with the program being assembled repeatedly until all labels
have stabilised. The bytecode stage will just be a tiny stage tacked
onto the end, rather than the old bytecode stage that would resolve
labels and expressions.
Diffstat (limited to 'src/stages')
-rw-r--r-- | src/stages/bytecode.rs | 272 | ||||
-rw-r--r-- | src/stages/bytecode_tokens.rs | 84 | ||||
-rw-r--r-- | src/stages/compiler.rs | 2 | ||||
-rw-r--r-- | src/stages/intermediate.rs | 626 | ||||
-rw-r--r-- | src/stages/intermediate_tokens.rs | 100 | ||||
-rw-r--r-- | src/stages/mod.rs | 4 | ||||
-rw-r--r-- | src/stages/semantic.rs | 72 | ||||
-rw-r--r-- | src/stages/semantic_tokens.rs | 14 |
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"), |