diff options
Diffstat (limited to 'src/stages/intermediate.rs')
-rw-r--r-- | src/stages/intermediate.rs | 207 |
1 files changed, 127 insertions, 80 deletions
diff --git a/src/stages/intermediate.rs b/src/stages/intermediate.rs index 6853f62..cfe33b7 100644 --- a/src/stages/intermediate.rs +++ b/src/stages/intermediate.rs @@ -84,10 +84,27 @@ impl IntermediateParser { 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, @@ -100,10 +117,9 @@ impl IntermediateParser { if self.errors.len() != error_count { break; } - - let name = definition.name.to_string(); - if self.macro_definitions.insert(name.clone(), definition).is_some() { - unreachable!("Uncaught duplicate macro definition '{}'", name); + // 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) => { @@ -164,6 +180,11 @@ 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); + Some(Tracked::from(integer, source.clone())) + } } } @@ -176,46 +197,31 @@ impl<'a> Environment<'a> { } BlockToken::PinnedAddress(address) => { if let Some(integer) = self.parse_integer_token(address, &address.source) { - if let Some(source) = integer_contains_label_reference(&integer) { - let error = IntermediateError::LabelReferenceInPinnedAddress; - let new_source = address.source.clone().wrap(source); - self.errors.push(Tracked::from(error, new_source)); - } else { - match evaluate_integer(&integer, source) { - Ok(value) => { - let value = usize::try_from(value).unwrap_or(0); - let tracked = Tracked::from(value, address.source.clone()); - let token = IntermediateToken::PinnedAddress(tracked); - intermediate.push(Tracked::from(token, source.clone())); - } - Err(error) => self.errors.push(error), - } - } + let token = IntermediateToken::PinnedAddress(integer); + intermediate.push(Tracked::from(token, source.clone())); } } BlockToken::ConditionalBlock(cond) => { - let predicate = self.parse_integer_token(&cond.predicate, &cond.predicate.source); - let mut body = self.parse_block_token(&cond.body, &cond.body.source); - if let Some(predicate) = predicate { - let mut found_error = false; + 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)); - found_error = true; - }; - 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)); - found_error = true; - } - if !found_error { + } else { match evaluate_integer(&predicate, &cond.predicate.source) { - Ok(value) => if value != 0 { intermediate.append(&mut body) }, + 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), } - } + }; } } BlockToken::WordTemplate(word_template) => { @@ -272,10 +278,21 @@ 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())) + } + StringToken::Invocation(invocation) => { + self.parse_string_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::Block(_) | IntermediateValue::String(_) => { let error = IntermediateError::ExpectedInteger; self.errors.push(Tracked::from(error, source.clone())); None @@ -286,7 +303,7 @@ impl<'a> Environment<'a> { fn parse_block_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) -> Option<Vec<Tracked<IntermediateToken>>> { match self.parse_invocation(invocation, source)?.value { IntermediateValue::Block(tokens) => Some(tokens), - IntermediateValue::Integer(_) => { + IntermediateValue::Integer(_) | IntermediateValue::String(_) => { let error = IntermediateError::ExpectedBlock; self.errors.push(Tracked::from(error, source.clone())); None @@ -294,9 +311,21 @@ impl<'a> Environment<'a> { } } + 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), + IntermediateValue::Integer(_) | IntermediateValue::Block(_) => { + let error = IntermediateError::ExpectedString; + self.errors.push(Tracked::from(error, source.clone())); + None + } + } + } + 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 if received_count != 0 { let error = IntermediateError::IncorrectArgumentCount(0, received_count); self.errors.push(Tracked::from(error, source.clone())); @@ -305,6 +334,7 @@ impl<'a> Environment<'a> { Some(argument.clone()) } } else if let Some(label_name) = self.label_names.get(&invocation.name) { + // This invocation is a label reference if received_count != 0 { let error = IntermediateError::IncorrectArgumentCount(0, received_count); self.errors.push(Tracked::from(error, source.clone())); @@ -328,48 +358,9 @@ impl<'a> Environment<'a> { // Gather and type-check the provided arguments. let mut arguments = Vec::new(); for (i, argument) in invocation.arguments.iter().enumerate() { - let received_type = match &argument.value { - InvocationArgument::String(string) => { - 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)); - } - arguments.push(RepeatedArgument::List(values)); - ArgumentType::Integer - } - InvocationArgument::IntegerToken(integer) => { - let tracked = self.parse_integer_token(&integer, &argument.source)?; - let value = IntermediateValue::Integer(tracked); - arguments.push(RepeatedArgument::Loop(value)); - ArgumentType::Integer - } - InvocationArgument::BlockToken(block) => { - let tokens = self.parse_block_token(&block, &argument.source); - let value = IntermediateValue::Block(tokens); - arguments.push(RepeatedArgument::Loop(value)); - ArgumentType::Block - } - InvocationArgument::Invocation(invocation) => { - let value = self.parse_invocation(&invocation, &argument.source)?; - let received_type = match &value.value { - IntermediateValue::Integer(_) => ArgumentType::Integer, - IntermediateValue::Block(_) => ArgumentType::Block, - }; - arguments.push(RepeatedArgument::Loop(value.value)); - received_type - } - }; - let expected_type = match received_type { - ArgumentType::Integer => ArgumentType::Block, - ArgumentType::Block => ArgumentType::Integer, - }; - if definition.arguments[i].variant != received_type { - let error = IntermediateError::IncorrectArgumentType(expected_type, received_type); - self.errors.push(Tracked::from(error, argument.source.clone())); - return None; - } + let expected_type = definition.arguments[i].variant; + let received_value = self.parse_invocation_argument(argument, expected_type)?; + arguments.push(received_value); } // Invoke the invocation multiple times. let repetitions = arguments.iter().map(|a| a.len()).max().unwrap_or(1); @@ -400,6 +391,7 @@ impl<'a> Environment<'a> { unreachable!("Uncaught duplicate macro argument name '{name}'"); }; } + // Invoke the macro once. let mut env = Environment { label_names: &self.label_names, macro_names: &self.macro_names, @@ -408,16 +400,19 @@ impl<'a> Environment<'a> { errors: &mut self.errors, id: next_id!(), }; + // Save the result of this macro invocation. values.push(env.parse_macro_definition_body(&definition.body, source)?); } if values.len() == 1 { + // If the macro was invoked once, return the value. values.pop() } else { - // Flatten all values into a list of block tokens. + // If the macro was invoked multiple times, create a list of + // block tokens from the returned values. let mut block = Vec::new(); for value in values { match value.value { - IntermediateValue::Integer(_) => { + IntermediateValue::Integer(_) | IntermediateValue::String(_) => { let error = IntermediateError::ExpectedBlock; self.errors.push(Tracked::from(error, value.source)); return None; @@ -440,6 +435,58 @@ impl<'a> Environment<'a> { } } + 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::IntegerToken(integer) => { + let tracked = self.parse_integer_token(&integer, &argument.source)?; + let value = IntermediateValue::Integer(tracked); + (RepeatedArgument::Loop(value), ArgumentType::Integer) + } + InvocationArgument::BlockToken(block) => { + let tokens = self.parse_block_token(&block, &argument.source); + let value = IntermediateValue::Block(tokens); + (RepeatedArgument::Loop(value), ArgumentType::Block) + } + InvocationArgument::Invocation(invocation) => { + let value = self.parse_invocation(&invocation, &argument.source)?; + match value.value { + IntermediateValue::Integer(_) => + (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)? + } + } + }; + if expected_type != received_type { + let error = IntermediateError::IncorrectArgumentType(expected_type, received_type); + self.errors.push(Tracked::from(error, argument.source.clone())); + return None; + } + return Some(received_value); + } + + fn parse_invocation_string_argument(&mut self, string: Tracked<StringLiteral>, 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)); + } + Some((RepeatedArgument::List(values), ArgumentType::Integer)) + } else { + let value = IntermediateValue::String(string); + Some((RepeatedArgument::Loop(value), ArgumentType::String)) + } + } + fn parse_expression(&mut self, expression: &Expression, source: &SourceSpan) -> Option<Tracked<IntermediateInteger>> { let mut intermediate = Vec::new(); let mut error = false; |