summaryrefslogtreecommitdiff
path: root/src/stages/intermediate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/stages/intermediate.rs')
-rw-r--r--src/stages/intermediate.rs145
1 files changed, 99 insertions, 46 deletions
diff --git a/src/stages/intermediate.rs b/src/stages/intermediate.rs
index c28426c..c4bb74d 100644
--- a/src/stages/intermediate.rs
+++ b/src/stages/intermediate.rs
@@ -84,6 +84,13 @@ 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);
@@ -173,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()))
+ }
}
}
@@ -279,10 +291,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
@@ -293,7 +316,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
@@ -301,9 +324,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()));
@@ -312,6 +347,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()));
@@ -335,48 +371,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);
@@ -407,6 +404,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,
@@ -415,16 +413,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;
@@ -447,6 +448,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;