summaryrefslogtreecommitdiff
path: root/src/stages/intermediate.rs
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-12-30 13:26:34 +1300
committerBen Bridle <ben@derelict.engineering>2025-12-30 14:29:19 +1300
commita7cfcd4ba9e86dce6d8366dc4122704a49d3909d (patch)
tree471523e09dc8aa913c12b43f836647497846b73b /src/stages/intermediate.rs
parentc5e626d27910e82df125d6d0c09037811488eeea (diff)
downloadtorque-asm-a7cfcd4ba9e86dce6d8366dc4122704a49d3909d.zip
Defer evaluation of block arguments
When a block value is evaluated, the address tracker is immediately incremented for each word in the evaluated block. This was causing an issue when a block value was passed as an argument to a macro invocation, because argument values would all be evaluated before the macro body was evaluated. This meant that the value of every label in the macro body would be pushed forward by the size of the block argument, even if the block argument is inserted later in the macro. Furthermore, the address tracker would be incremented exactly once for each word in the block, even if the block is never inserted into the macro or is inserted multiple times. To fix this, we now defer evaluation of a block argument until exactly when that block argument is inserted into a macro. Because a block argument could be an invocation with a set of arguments passed to it, we also store a copy of the current environment alongside the block value so that it can be evaluated correctly at any time in the future.
Diffstat (limited to 'src/stages/intermediate.rs')
-rw-r--r--src/stages/intermediate.rs116
1 files changed, 56 insertions, 60 deletions
diff --git a/src/stages/intermediate.rs b/src/stages/intermediate.rs
index b890cd4..3144b77 100644
--- a/src/stages/intermediate.rs
+++ b/src/stages/intermediate.rs
@@ -14,29 +14,6 @@ macro_rules! next_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()
@@ -294,14 +271,14 @@ impl IntermediateParser {
fn parse_integer_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> {
let result = self.parse_invocation(invocation, source)?;
match result.value {
- IntermediateValue::Integer(integer) => {
+ DeferredValue::Integer(integer) => {
Some(Tracked::from(integer, source.clone()))
}
- IntermediateValue::List(mut integers) if integers.len() == 1 => {
+ DeferredValue::List(mut integers) if integers.len() == 1 => {
let integer = integers.pop().unwrap();
Some(Tracked::from(integer, source.clone()))
}
- IntermediateValue::Block(_) | IntermediateValue::List(_) => {
+ DeferredValue::Block(_) | DeferredValue::List(_) => {
let error = IntermediateError::ExpectedInteger;
self.errors.push(Tracked::from(error, source.clone()));
None
@@ -313,8 +290,18 @@ impl IntermediateParser {
let result = self.parse_invocation(invocation, source)?;
let source = result.source;
match result.value {
- IntermediateValue::Block(tokens) => Some(tokens),
- IntermediateValue::Integer(_) | IntermediateValue::List(_) => {
+ DeferredValue::Block(blocks) => {
+ // Convert each deferred block value to a concrete value, using
+ // the environment that was stored alongside each value.
+ let mut values = Vec::new();
+ for block in blocks {
+ self.environment_stack.push(block.env);
+ values.append(&mut self.parse_block_token(&block.block, &source));
+ self.environment_stack.pop().unwrap();
+ }
+ Some(values)
+ }
+ DeferredValue::Integer(_) | DeferredValue::List(_) => {
let error = IntermediateError::ExpectedBlock;
self.errors.push(Tracked::from(error, source));
None
@@ -322,7 +309,7 @@ impl IntermediateParser {
}
}
- fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<IntermediateValue>> {
+ fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Tracked<DeferredValue>> {
let received_count = invocation.arguments.len();
let signature = SymbolSignature {
name: invocation.name.clone(),
@@ -350,7 +337,7 @@ impl IntermediateParser {
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);
+ let integer = DeferredValue::Integer(tracked);
Some(Tracked::from(integer, source.clone()))
}
} else if let Some(definition) = self.macro_definitions.get(&signature) {
@@ -416,22 +403,23 @@ impl IntermediateParser {
// If the macro was invoked once, return the value.
values.pop()
} else {
- // If the macro was invoked multiple times, create a list of
- // block tokens from the returned values.
- let mut block = Vec::new();
+ // If the macro was invoked multiple times, create a single
+ // deferred block value from the returned values.
+ let mut deferred_blocks = Vec::new();
for value in values {
match value.value {
- IntermediateValue::Integer(_) | IntermediateValue::List(_) => {
+ DeferredValue::Integer(_) | DeferredValue::List(_) => {
let error = IntermediateError::ExpectedBlock;
self.errors.push(Tracked::from(error, value.source));
return None;
}
- IntermediateValue::Block(mut tokens) => {
- block.append(&mut tokens);
+ DeferredValue::Block(mut blocks) => {
+ deferred_blocks.append(&mut blocks);
}
}
}
- Some(Tracked::from(IntermediateValue::Block(block), source.clone()))
+ let deferred_value = DeferredValue::Block(deferred_blocks);
+ Some(Tracked::from(deferred_value, source.clone()))
}
}
} else {
@@ -448,22 +436,25 @@ impl IntermediateParser {
}
InvocationArgument::IntegerToken(integer) => {
let tracked = self.parse_integer_token(&integer, &argument.source)?;
- let value = IntermediateValue::Integer(tracked.value);
+ let value = DeferredValue::Integer(tracked.value);
(RepeatedArgument::Loop(value), ArgumentType::Integer)
}
InvocationArgument::BlockToken(block) => {
- let tokens = self.parse_block_token(&block, &argument.source);
- let value = IntermediateValue::Block(tokens);
+ // Store each block argument as a deferred value alongside a copy of the
+ // current environment so that they can be converted to concrete values later.
+ let tracked = Tracked::from(block.clone(), source.clone());
+ let deferred_block = DeferredBlock { block: tracked, env: self.env().clone() };
+ let value = DeferredValue::Block(vec![deferred_block]);
(RepeatedArgument::Loop(value), ArgumentType::Block)
}
InvocationArgument::Invocation(invocation) => {
- let value = self.parse_invocation(&invocation, &argument.source)?;
+ let value = self.parse_invocation(&invocation, &source)?;
match value.value {
- IntermediateValue::Integer(_) =>
+ DeferredValue::Integer(_) =>
(RepeatedArgument::Loop(value.value), ArgumentType::Integer),
- IntermediateValue::Block(_) =>
+ DeferredValue::Block(_) =>
(RepeatedArgument::Loop(value.value), ArgumentType::Block),
- IntermediateValue::List(list) =>
+ DeferredValue::List(list) =>
self.parse_invocation_list_argument(Tracked::from(list, value.source), expected_type)?
}
}
@@ -480,36 +471,37 @@ impl IntermediateParser {
if let ArgumentType::Integer = expected_type {
let mut values = Vec::new();
for value in &list.value {
- values.push(IntermediateValue::Integer(value.clone()));
+ values.push(DeferredValue::Integer(value.clone()));
}
Some((RepeatedArgument::List(values), ArgumentType::Integer))
} else {
- let value = IntermediateValue::List(list.value);
+ let value = DeferredValue::List(list.value);
Some((RepeatedArgument::Loop(value), ArgumentType::List))
}
}
- fn parse_macro_definition_body(&mut self, body: &MacroDefinitionBody, source: &SourceSpan) -> Option<Tracked<IntermediateValue>> {
+ fn parse_macro_definition_body(&mut self, body: &MacroDefinitionBody, source: &SourceSpan) -> Option<Tracked<DeferredValue>> {
match &body {
MacroDefinitionBody::Integer(integer) => {
let token = self.parse_integer_token(&integer, &source)?;
- let integer = IntermediateValue::Integer(token.value);
+ let integer = DeferredValue::Integer(token.value);
Some(Tracked::from(integer, source.clone()))
}
MacroDefinitionBody::Invocation(invocation) => {
self.parse_invocation(&invocation, &invocation.source)
}
MacroDefinitionBody::Block(blocks) => {
- let mut tokens = Vec::new();
+ // Store each block value alongside a copy of the current environment
+ // so that they can be converted to concrete values later.
+ let mut values = Vec::new();
for block in blocks {
- tokens.append(&mut self.parse_block_token(block, &block.source));
+ values.push(DeferredBlock { block: block.clone(), env: self.env().clone() });
}
- let value = IntermediateValue::Block(tokens);
- Some(Tracked::from(value, source.clone()))
+ Some(Tracked::from(DeferredValue::Block(values), source.clone()))
}
MacroDefinitionBody::List(list) => {
let list = self.parse_list_token(list, &source)?;
- let integer = IntermediateValue::List(list.value);
+ let integer = DeferredValue::List(list.value);
Some(Tracked::from(integer, source.clone()))
}
}
@@ -530,13 +522,17 @@ impl IntermediateParser {
}
ExpressionToken::Invocation(invocation) => {
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);
+ let value = match value.value {
+ DeferredValue::Block(_) => {
+ let error = IntermediateError::InvalidBlockInExpression;
+ let new_source = source.clone().wrap(value.source.clone());
+ self.errors.push(Tracked::from(error, new_source));
+ return None;
+ }
+ DeferredValue::List(list) => IntermediateValue::List(list),
+ DeferredValue::Integer(integer) => IntermediateValue::Integer(integer),
+ };
+ stack.push(value);
}
}
ExpressionToken::Operator(operator) => {