Flesh out support for namespaces
This commit is contained in:
Generated
+7
@@ -19,6 +19,7 @@ dependencies = [
|
|||||||
"nom",
|
"nom",
|
||||||
"peg_macro",
|
"peg_macro",
|
||||||
"pest",
|
"pest",
|
||||||
|
"rustc-hash",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -231,6 +232,12 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "2.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ insta = "1.39.0"
|
|||||||
nom = "7.1.3"
|
nom = "7.1.3"
|
||||||
pest = "2.7.15"
|
pest = "2.7.15"
|
||||||
compact_str = "0.9.0"
|
compact_str = "0.9.0"
|
||||||
|
rustc-hash = "2.1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = "1.39.0"
|
insta = "1.39.0"
|
||||||
|
|||||||
+53
-14
@@ -1,4 +1,5 @@
|
|||||||
use compact_str::CompactString;
|
use compact_str::CompactString;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::world::Board;
|
use crate::world::Board;
|
||||||
|
|
||||||
@@ -43,8 +44,17 @@ pub fn process_labels(board: &mut Board) {
|
|||||||
/// Resolve ".local" labels to "name.local" form.
|
/// Resolve ".local" labels to "name.local" form.
|
||||||
fn resolve_local_labels(stats: &mut [ParsedStat]) {
|
fn resolve_local_labels(stats: &mut [ParsedStat]) {
|
||||||
for stat in stats.iter_mut() {
|
for stat in stats.iter_mut() {
|
||||||
let mut section_counter = 0;
|
// Helper: Generate unique section strings like "touch$0"
|
||||||
let mut section = CompactString::const_new("$0");
|
let mut i = 0;
|
||||||
|
let mut make_section_id = |label_name: &str| -> CompactString {
|
||||||
|
let result = format!("{}${}", &label_name, i);
|
||||||
|
i += 1;
|
||||||
|
result.into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut namespace_to_section: FxHashMap<Option<CompactString>, CompactString> =
|
||||||
|
FxHashMap::default();
|
||||||
|
|
||||||
for chunk in stat.iter_mut() {
|
for chunk in stat.iter_mut() {
|
||||||
match chunk {
|
match chunk {
|
||||||
Chunk::Label {
|
Chunk::Label {
|
||||||
@@ -58,13 +68,21 @@ fn resolve_local_labels(stats: &mut [ParsedStat]) {
|
|||||||
if label.name.is_empty() {
|
if label.name.is_empty() {
|
||||||
// Expand :.local to :name.local
|
// Expand :.local to :name.local
|
||||||
assert!(label.local.is_some());
|
assert!(label.local.is_some());
|
||||||
label.name = section.clone();
|
label.name = if let Some(section) =
|
||||||
|
namespace_to_section.get(&label.namespace)
|
||||||
|
{
|
||||||
|
section.clone()
|
||||||
|
} else {
|
||||||
|
let section = make_section_id("");
|
||||||
|
namespace_to_section.insert(label.namespace.clone(), section.clone());
|
||||||
|
section
|
||||||
|
}
|
||||||
} else if label.local.is_none() {
|
} else if label.local.is_none() {
|
||||||
// Interpret label :name as start of new section.
|
// Interpret label :name as start of new section.
|
||||||
// Only label definitions do this; label references have no effect.
|
// Only label definitions do this; label references have no effect.
|
||||||
if !*is_ref {
|
if !*is_ref {
|
||||||
section_counter += 1;
|
namespace_to_section
|
||||||
section = format!("{}${}", &label.name, section_counter).into();
|
.insert(label.namespace.clone(), make_section_id(&label.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,9 +124,25 @@ fn assign_named_labels(stats: &mut [ParsedStat], registry: &mut Registry) {
|
|||||||
/// 1. Assign names to anonymous labels.
|
/// 1. Assign names to anonymous labels.
|
||||||
/// 2. Resolve anonymous backward references to their label names.
|
/// 2. Resolve anonymous backward references to their label names.
|
||||||
fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) {
|
fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) {
|
||||||
|
// Save generated label names so they can be reused across multiple objects
|
||||||
let mut label_names = vec![];
|
let mut label_names = vec![];
|
||||||
|
|
||||||
for stat in stats.iter_mut() {
|
for stat in stats.iter_mut() {
|
||||||
|
// Helper: Get the next label name that hasn't been used in this object yet
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
|
let mut get_next_name = || -> CompactString {
|
||||||
|
if i == label_names.len() {
|
||||||
|
label_names.push(registry.gen_anonymous());
|
||||||
|
}
|
||||||
|
let result = label_names[i].clone();
|
||||||
|
i += 1;
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
// Track each namespace's most recently defined anonymous label
|
||||||
|
let mut namespace_to_latest: FxHashMap<Option<CompactString>, CompactString> =
|
||||||
|
FxHashMap::default();
|
||||||
|
|
||||||
for chunk in stat.iter_mut() {
|
for chunk in stat.iter_mut() {
|
||||||
match chunk {
|
match chunk {
|
||||||
Chunk::Label {
|
Chunk::Label {
|
||||||
@@ -116,11 +150,9 @@ fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) {
|
|||||||
is_anon: true,
|
is_anon: true,
|
||||||
name,
|
name,
|
||||||
} => {
|
} => {
|
||||||
if i == label_names.len() {
|
let assigned = get_next_name();
|
||||||
label_names.push(registry.gen_anonymous());
|
namespace_to_latest.insert(name.namespace.clone(), assigned.clone());
|
||||||
}
|
name.name = assigned;
|
||||||
name.name = label_names[i].clone();
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
Chunk::Label {
|
Chunk::Label {
|
||||||
is_ref: true,
|
is_ref: true,
|
||||||
@@ -129,7 +161,7 @@ fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) {
|
|||||||
} => {
|
} => {
|
||||||
if name.name == "@b" {
|
if name.name == "@b" {
|
||||||
// TODO: a more explicit check that a "before" label exists
|
// TODO: a more explicit check that a "before" label exists
|
||||||
name.name = label_names[i - 1].clone();
|
name.name = namespace_to_latest.get(&name.namespace).unwrap().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -141,7 +173,7 @@ fn anonymous_forward_pass(stats: &mut [ParsedStat], registry: &mut Registry) {
|
|||||||
/// Resolve anonymous forward references to their label names.
|
/// Resolve anonymous forward references to their label names.
|
||||||
fn anonymous_backward_pass(stats: &mut [ParsedStat]) {
|
fn anonymous_backward_pass(stats: &mut [ParsedStat]) {
|
||||||
for stat in stats.iter_mut() {
|
for stat in stats.iter_mut() {
|
||||||
let mut last_name = None;
|
let mut namespace_to_latest = FxHashMap::default();
|
||||||
for chunk in stat.iter_mut().rev() {
|
for chunk in stat.iter_mut().rev() {
|
||||||
match chunk {
|
match chunk {
|
||||||
Chunk::Label {
|
Chunk::Label {
|
||||||
@@ -149,7 +181,7 @@ fn anonymous_backward_pass(stats: &mut [ParsedStat]) {
|
|||||||
is_anon: true,
|
is_anon: true,
|
||||||
name,
|
name,
|
||||||
} => {
|
} => {
|
||||||
last_name = Some(name.name.clone());
|
namespace_to_latest.insert(name.namespace.clone(), name.name.clone());
|
||||||
}
|
}
|
||||||
Chunk::Label {
|
Chunk::Label {
|
||||||
is_ref: true,
|
is_ref: true,
|
||||||
@@ -157,7 +189,7 @@ fn anonymous_backward_pass(stats: &mut [ParsedStat]) {
|
|||||||
name,
|
name,
|
||||||
} => {
|
} => {
|
||||||
if name.name == "@f" {
|
if name.name == "@f" {
|
||||||
name.name = last_name.clone().unwrap();
|
name.name = namespace_to_latest.get(&name.namespace).unwrap().clone();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -229,4 +261,11 @@ mod test {
|
|||||||
process_labels(&mut board);
|
process_labels(&mut board);
|
||||||
assert_snapshot!(board_to_text(board));
|
assert_snapshot!(board_to_text(board));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_namespaces() {
|
||||||
|
let mut board = board_from_text("tests/labels/namespaces.txt");
|
||||||
|
process_labels(&mut board);
|
||||||
|
assert_snapshot!(board_to_text(board));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
source: src/labels/process.rs
|
||||||
|
expression: board_to_text(board)
|
||||||
|
---
|
||||||
|
'Make sure anonymous references respect namespaces
|
||||||
|
#_
|
||||||
|
#a
|
||||||
|
#b
|
||||||
|
:_
|
||||||
|
:a
|
||||||
|
:b
|
||||||
|
#_
|
||||||
|
#a
|
||||||
|
#b
|
||||||
|
---
|
||||||
|
'Make sure namespaces don't interfere with anonymous label reuse.
|
||||||
|
'These three should match the ones above.
|
||||||
|
:_
|
||||||
|
:a
|
||||||
|
:b
|
||||||
|
---
|
||||||
|
'Make sure locals exist in separate namespaces
|
||||||
|
:do_stuff
|
||||||
|
#lock
|
||||||
|
:_loop
|
||||||
|
'
|
||||||
|
:_do_stuff
|
||||||
|
:_loop0
|
||||||
|
/i#if blocked rndne _loop0
|
||||||
|
'
|
||||||
|
#take time 1 _break
|
||||||
|
#_loop
|
||||||
|
:_break
|
||||||
|
#unlock
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
'Make sure anonymous references respect namespaces
|
||||||
|
#@f
|
||||||
|
#foo~@f
|
||||||
|
#bar~@f
|
||||||
|
:@
|
||||||
|
:foo~@
|
||||||
|
:bar~@
|
||||||
|
#@b
|
||||||
|
#foo~@b
|
||||||
|
#bar~@b
|
||||||
|
---
|
||||||
|
'Make sure namespaces don't interfere with anonymous label reuse.
|
||||||
|
'These three should match the ones above.
|
||||||
|
:@
|
||||||
|
:@
|
||||||
|
:@
|
||||||
|
---
|
||||||
|
'Make sure locals exist in separate namespaces
|
||||||
|
:do_stuff
|
||||||
|
#lock
|
||||||
|
:.loop
|
||||||
|
'
|
||||||
|
:foo~do_stuff
|
||||||
|
:foo~.loop
|
||||||
|
/i#if blocked rndne foo~.loop
|
||||||
|
'
|
||||||
|
#take time 1 .break
|
||||||
|
#.loop
|
||||||
|
:.break
|
||||||
|
#unlock
|
||||||
Reference in New Issue
Block a user