diff --git a/src/labels/parse.rs b/src/labels/parse.rs index 730afae..cf76ca6 100644 --- a/src/labels/parse.rs +++ b/src/labels/parse.rs @@ -8,8 +8,11 @@ use crate::{peg::ParseState, world::Stat}; pub type ParsedStat = Vec; pub enum Chunk { Verbatim(String), - Label(LabelName), - Reference(LabelName), + Label { + is_ref: bool, + is_anon: bool, + name: LabelName, + }, } #[derive(Clone, Debug, Default)] @@ -44,22 +47,22 @@ pub fn parse_stat_labels(stat: &Stat) -> ParsedStat { // Convert #Labels into (span, chunk) pairs let span_chunks = label_captures.iter().map(|(tag, cap)| { let mut name = LabelName::default(); + let mut is_anon = false; for child in cap.children() { match child.kind() { - Tag::Namespace => { - name.namespace = Some(child.text().into()); - } - Tag::Global => { + Tag::Namespace => name.namespace = Some(child.text().into()), + Tag::Anon | Tag::Global => { name.name = child.text().into(); + is_anon = child.kind() == Tag::Anon; } Tag::Local => name.local = Some(child.text().into()), - _ => unimplemented!(), + _ => unreachable!(), } } - let chunk = match tag { - Tag::Label => Chunk::Label(name), - Tag::Reference => Chunk::Reference(name), - _ => unimplemented!(), + let chunk = Chunk::Label { + is_ref: *tag == Tag::Reference, + is_anon, + name, }; (cap.span(), chunk) }); diff --git a/src/labels/process.rs b/src/labels/process.rs index 796a5ed..c3786b8 100644 --- a/src/labels/process.rs +++ b/src/labels/process.rs @@ -19,7 +19,11 @@ pub fn process_labels(board: &mut Board) { for chunk in stat.iter_mut() { match chunk { Chunk::Verbatim(_) => {} - Chunk::Label(name) | Chunk::Reference(name) => { + Chunk::Label { + name, + is_ref: _, + is_anon: _, + } => { let sanitized = registry.sanitize(name); *chunk = Chunk::Verbatim(sanitized.into()); } @@ -27,15 +31,78 @@ pub fn process_labels(board: &mut Board) { } } + anonymous_forward_pass(&mut stats, &mut registry); + anonymous_backward_pass(&mut stats); + // Join chunks together and replace old stats' code for (old_stat, parsed_stat) in board.stats.iter_mut().zip(stats.into_iter()) { let new_code = parsed_stat .into_iter() .map(|chunk| match chunk { Chunk::Verbatim(s) => s, - _ => unreachable!(), + _ => unimplemented!(), }) .collect(); old_stat.code = new_code; } } + +fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) { + let mut label_names = vec![]; + for stat in stats.iter_mut() { + let mut i = 0; + for chunk in stat.iter_mut() { + match chunk { + Chunk::Label { + is_ref: false, + is_anon: true, + name, + } => { + if i == label_names.len() { + label_names.push(registry.gen_anonymous()); + } + name.name = label_names[i].clone(); + i += 1; + } + Chunk::Label { + is_ref: true, + is_anon: true, + name, + } => { + if name.name == "@b" { + // TODO: a more explicit check that a "before" label exists + name.name = label_names[i - 1].clone(); + } + } + _ => {} + } + } + } +} + +fn anonymous_backward_pass(stats: &mut [ParsedStat]) { + for stat in stats.iter_mut() { + let mut last_name = None; + for chunk in stat.iter_mut().rev() { + match chunk { + Chunk::Label { + is_ref: false, + is_anon: true, + name, + } => { + last_name = Some(name.name.clone()); + } + Chunk::Label { + is_ref: true, + is_anon: true, + name, + } => { + if name.name == "@f" { + name.name = last_name.clone().unwrap(); + } + } + _ => {} + } + } + } +}