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.rs207
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;