From 981bb70e5077bd30ef85a0092117a875dcc614fc Mon Sep 17 00:00:00 2001 From: Ben Bridle Date: Tue, 14 Oct 2025 20:40:39 +1300 Subject: 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. --- src/stages/intermediate.rs | 628 ++++++++++++++++++++++----------------------- 1 file changed, 308 insertions(+), 320 deletions(-) (limited to 'src/stages/intermediate.rs') 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>, + id: usize, +} + pub fn parse_intermediate(semantic: Vec>) -> Result>, Vec>> { IntermediateParser::new(semantic).parse() } - struct IntermediateParser { semantic: Vec>, - label_names: IndexSet>, - macro_names: IndexSet>, - macro_definitions: IndexMap, + label_names: IndexSet>, + label_addresses: IndexMap, LabelAddress>, + macro_definitions: IndexMap>, + environment_stack: Vec, + address: usize, intermediate: Vec>, errors: Vec>, } impl IntermediateParser { - pub fn new(semantic: Vec>) -> Self { + /// Initialise a new parser. + pub fn new(mut semantic: Vec>) -> 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>> { - 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); + } + + 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; } } - match self.errors.is_empty() { - true => Ok(self.intermediate), - false => Err(self.errors), - } + return false; } -} + /// 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")) + } -struct Environment<'a> { - label_names: &'a IndexSet>, - macro_names: &'a IndexSet>, - macro_definitions: &'a IndexMap, - arguments: IndexMap>, - errors: &'a mut Vec>, - id: usize, -} - -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())); - } - BlockToken::PinnedAddress(address) => { - if let Some(integer) = self.parse_integer_token(address, &address.source) { - let token = IntermediateToken::PinnedAddress(integer); + 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(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> { 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> { - match string { - StringToken::StringLiteral(literal) => { - Some(Tracked::from(literal.clone(), source.clone())) + fn parse_list_token(&mut self, list: &ListToken, source: &SourceSpan) -> Option> { + match list { + ListToken::StringLiteral(literal) => { + Some(Tracked::from(literal.chars.clone(), source.clone())) + } + 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())) } - StringToken::Invocation(invocation) => { - self.parse_string_invocation(&invocation, source) + ListToken::Invocation(invocation) => { + self.parse_list_invocation(&invocation, source) } } } fn parse_integer_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option> { - 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>> { - 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> { - match self.parse_invocation(invocation, source)?.value { - IntermediateValue::String(literal) => Some(literal), + fn parse_list_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option> { + 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> { 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, expected_type: ArgumentType) -> Option { 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, expected_type: ArgumentType) -> Option<(RepeatedArgument, ArgumentType)> { + fn parse_invocation_list_argument(&mut self, list: Tracked, 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> { - 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 { - 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 { - 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 { 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 { for argument in &invocation.arguments { match &argument.value { @@ -580,45 +610,3 @@ fn invocation_contains_label_definition(invocation: &Invocation) -> Option Result> { - 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> { - 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())) - } - } -} -- cgit v1.2.3-70-g09d2