diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/stages/intermediate.rs | 116 | ||||
| -rw-r--r-- | src/stages/intermediate_tokens.rs | 55 |
2 files changed, 109 insertions, 62 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) => { diff --git a/src/stages/intermediate_tokens.rs b/src/stages/intermediate_tokens.rs index d796299..116cec5 100644 --- a/src/stages/intermediate_tokens.rs +++ b/src/stages/intermediate_tokens.rs @@ -1,5 +1,33 @@ use crate::*; +use indexmap::IndexMap; + + +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) + } +} + +// Arguments stored in the environment are lazily evaluated. See the +// comment on DeferredValue for more information. +#[derive(Clone)] +pub struct Environment { + pub arguments: IndexMap<String, Tracked<DeferredValue>>, + pub id: usize, +} #[derive(Clone)] pub enum IntermediateToken { @@ -41,13 +69,36 @@ pub enum IntermediateValue { Block(IntermediateBlock), } +/// Arguments are passed around as deferred values. This is to prevent +/// block arguments values from being evaluated before they are used, +/// which would cause the address tracker to have an incorrect value +/// (because the address is incremented every time a word is assembled, +/// and the block argument isn't necessarily used at the start of the +/// macro). +/// +/// This will also fix the issue of a block being 'assembled' +/// exactly once when it's passed to a macro invocation, but being used +/// twice or never in that macro, throwing off the address tracker. +#[derive(Clone)] +pub enum DeferredValue { + Integer(IntermediateInteger), + List(IntermediateList), + Block(Vec<DeferredBlock>), +} + pub type IntermediateInteger = Tracked<isize>; pub type IntermediateList = Vec<Tracked<isize>>; pub type IntermediateBlock = Vec<Tracked<IntermediateToken>>; +#[derive(Clone)] +pub struct DeferredBlock { + pub block: Tracked<BlockToken>, + pub env: Environment, +} + pub enum RepeatedArgument { - Loop(IntermediateValue), - List(Vec<IntermediateValue>), + Loop(DeferredValue), + List(Vec<DeferredValue>), } impl RepeatedArgument { |
