diff options
author | Rory Dudley | 2024-05-20 11:27:45 -0600 |
---|---|---|
committer | Rory Dudley | 2024-05-20 11:27:45 -0600 |
commit | 8a7af4aacc0d67a9887ca1fd665b1694fd62a8ca (patch) | |
tree | 8bd0ec21d289e558925d15663b490b9873f257ea | |
parent | 1bb25591b77a14a17bd05d79316ce703bcbcc3a6 (diff) | |
download | dwarvish-8a7af4aacc0d67a9887ca1fd665b1694fd62a8ca.tar.gz |
Allow aliases with the same name as their verb
Previously, when trying to add an alias that used the same name as its
command, an infinite recursion loop would occur, and the program would
panic after the call stack got too deep. For instance, something like:
alias grep='grep --color=always'
would cause it to panic.
This patch adds a new field to the Environment struct called 'cs' (for
call stack), which can be used to keep track of how many levels deep
into the Poem::read() function we are in. At the moment, it only allows
going two levels deep, but since it's just a u8 value, this could be
increased in the future. The above example now works correctly, but it
does mean that aliases within aliases are not possible, currently.
-rw-r--r-- | src/compose.rs | 2 | ||||
-rw-r--r-- | src/compose/environment.rs | 2 | ||||
-rw-r--r-- | src/poem.rs | 72 | ||||
-rw-r--r-- | src/poem/read.rs | 24 |
4 files changed, 56 insertions, 44 deletions
diff --git a/src/compose.rs b/src/compose.rs index 46f04a7..dfbbaef 100644 --- a/src/compose.rs +++ b/src/compose.rs @@ -53,7 +53,7 @@ fn rrr(path: PathBuf, env: &mut Environment) { } }; - let poem = match Poem::read(poetry, &Environment::new()) { + let poem = match Poem::read(poetry, &mut Environment::new()) { Ok(poem) => poem, Err(e) => { eprintln!( diff --git a/src/compose/environment.rs b/src/compose/environment.rs index df83e4f..7107d57 100644 --- a/src/compose/environment.rs +++ b/src/compose/environment.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; pub struct Environment { pub aliases: HashMap<String, String>, pub bins: Vec<String>, + pub cs: u8, } impl Environment { @@ -10,6 +11,7 @@ impl Environment { Environment { aliases: HashMap::new(), bins: Vec::new(), + cs: 0, } } } diff --git a/src/poem.rs b/src/poem.rs index db57e55..362eaaa 100644 --- a/src/poem.rs +++ b/src/poem.rs @@ -19,7 +19,7 @@ mod tests { #[test] fn it_parses_a_verse_with_no_meter() { - let poem = Poem::read("cargo build --release".to_string(), &Environment::new()); + let poem = Poem::read("cargo build --release".to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "cargo"); @@ -27,7 +27,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_couplet_meter() { - let poem = Poem::read("ls -la | lolcat".to_string(), &Environment::new()); + let poem = Poem::read("ls -la | lolcat".to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "ls"); @@ -36,7 +36,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_quiet_meter() { - let poem = Poem::read("sleep 20 &".to_string(), &Environment::new()); + let poem = Poem::read("sleep 20 &".to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "sleep"); @@ -45,7 +45,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_and_meter() { - let poem = Poem::read("sleep 2 && ls -la".to_string(), &Environment::new()); + let poem = Poem::read("sleep 2 && ls -la".to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "sleep"); @@ -54,7 +54,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_continue_meter() { - let poem = Poem::read("sleep 2; ls -la".to_string(), &Environment::new()); + let poem = Poem::read("sleep 2; ls -la".to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "sleep"); @@ -63,7 +63,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_read_rune() { - let poem = Poem::read("lolcat < src/main.rs".to_string(), &Environment::new()); + let poem = Poem::read("lolcat < src/main.rs".to_string(), &mut Environment::new()); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); let verse = verses.next().unwrap(); @@ -88,7 +88,7 @@ mod tests { fn it_parses_a_verse_with_the_write_rune() { let poem = Poem::read( "cat src/main.rs > /dev/null".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -114,7 +114,7 @@ mod tests { fn it_parses_a_verse_with_the_write2_rune() { let poem = Poem::read( "cat src/main.rs 2> /dev/null".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -140,7 +140,7 @@ mod tests { fn it_parses_a_verse_with_the_write_all_rune() { let poem = Poem::read( "cat src/main.rs &> /dev/null".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -173,7 +173,7 @@ mod tests { fn it_parses_a_verse_with_the_addendum_rune() { let poem = Poem::read( "cat src/main.rs >> /dev/null".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -199,7 +199,7 @@ mod tests { fn it_parses_a_verse_with_the_addendum2_rune() { let poem = Poem::read( "cat src/main.rs 2>> /dev/null".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -225,7 +225,7 @@ mod tests { fn it_parses_a_verse_with_the_addendum_all_rune() { let poem = Poem::read( "cat src/main.rs &>> /dev/null".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -256,31 +256,31 @@ mod tests { #[test] fn it_throws_a_parse_error_if_no_files_are_specified_for_the_read_rune() { - let poem = Poem::read("lolcat <".to_string(), &Environment::new()); + let poem = Poem::read("lolcat <".to_string(), &mut Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("lolcat <;".to_string(), &Environment::new()); + let poem = Poem::read("lolcat <;".to_string(), &mut Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("lolcat < && ls -la".to_string(), &Environment::new()); + let poem = Poem::read("lolcat < && ls -la".to_string(), &mut Environment::new()); assert!(poem.is_err()); } #[test] fn it_throws_a_parse_error_if_no_files_are_specified_for_the_write_rune() { - let poem = Poem::read("cat src/main.rs >".to_string(), &Environment::new()); + let poem = Poem::read("cat src/main.rs >".to_string(), &mut Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat src/main.rs >;".to_string(), &Environment::new()); + let poem = Poem::read("cat src/main.rs >;".to_string(), &mut Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat > && ls -la".to_string(), &Environment::new()); + let poem = Poem::read("cat > && ls -la".to_string(), &mut Environment::new()); assert!(poem.is_err()); } #[test] fn it_throws_a_parse_error_if_no_files_are_specified_for_the_addendum_rune() { - let poem = Poem::read("cat src/main.rs >>".to_string(), &Environment::new()); + let poem = Poem::read("cat src/main.rs >>".to_string(), &mut Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat src/main.rs >>;".to_string(), &Environment::new()); + let poem = Poem::read("cat src/main.rs >>;".to_string(), &mut Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat >> && ls -la".to_string(), &Environment::new()); + let poem = Poem::read("cat >> && ls -la".to_string(), &mut Environment::new()); assert!(poem.is_err()); } @@ -288,7 +288,7 @@ mod tests { fn it_parses_a_complex_verse_with_lots_of_different_meters() { let poem = Poem::read( "ls -la | lolcat && echo hello | lolcat && sleep 2 &".to_string(), - &Environment::new(), + &mut Environment::new(), ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -319,25 +319,25 @@ mod tests { #[test] fn it_parses_the_continue_meter_without_a_stanza() { - let poem = Poem::read(";;;;;;;".to_string(), &Environment::new()); + let poem = Poem::read(";;;;;;;".to_string(), &mut Environment::new()); assert!(poem.is_ok()); } #[test] fn it_errors_if_the_couplet_meter_is_used_without_a_stanza() { - let poem = Poem::read("|".to_string(), &Environment::new()); + let poem = Poem::read("|".to_string(), &mut Environment::new()); assert!(poem.is_err()); } #[test] fn it_errors_if_the_quiet_meter_is_used_without_a_stanza() { - let poem = Poem::read("&".to_string(), &Environment::new()); + let poem = Poem::read("&".to_string(), &mut Environment::new()); assert!(poem.is_err()); } #[test] fn it_errors_if_the_and_meter_is_used_without_a_stanza() { - let poem = Poem::read("&&".to_string(), &Environment::new()); + let poem = Poem::read("&&".to_string(), &mut Environment::new()); assert!(poem.is_err()); } @@ -348,7 +348,7 @@ mod tests { sleep 2 "; - let poem = Poem::read(file.to_string(), &Environment::new()); + let poem = Poem::read(file.to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); @@ -388,7 +388,7 @@ mod tests { wc -l src/**/*.rs | lolcat; ls -la | grep git "; - let poem = Poem::read(file.to_string(), &Environment::new()); + let poem = Poem::read(file.to_string(), &mut Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); @@ -398,36 +398,36 @@ mod tests { #[test] fn it_catches_parser_errors_related_to_invalid_use_of_special_runes() { let poetry = "cat file.txt &&&".to_string(); - assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); + assert_eq!(Poem::read(poetry, &mut Environment::new()).is_err(), true); let poetry = "cat file.txt&&|".to_string(); - assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); + assert_eq!(Poem::read(poetry, &mut Environment::new()).is_err(), true); let poetry = "cat <".to_string(); - assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); + assert_eq!(Poem::read(poetry, &mut Environment::new()).is_err(), true); } #[test] fn it_catches_parser_errors_related_to_strings() { let poetry = "echo 'hello".to_string(); - assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); + assert_eq!(Poem::read(poetry, &mut Environment::new()).is_err(), true); let poetry = "echo \"hello".to_string(); - assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); + assert_eq!(Poem::read(poetry, &mut Environment::new()).is_err(), true); let poetry = "`true".to_string(); - assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); + assert_eq!(Poem::read(poetry, &mut Environment::new()).is_err(), true); } #[test] fn it_interprets_tilda_as_home() { let poetry = "cd ~".to_string(); - let poem = Poem::read(poetry, &Environment::new()).unwrap(); + let poem = Poem::read(poetry, &mut Environment::new()).unwrap(); assert_eq!(poem[0].verb(), "cd"); assert_eq!(poem[0].clause(), Some(vec![env!("HOME").to_string()])); let poetry = "cd ~/Code/dwarvish".to_string(); - let poem = Poem::read(poetry, &Environment::new()).unwrap(); + let poem = Poem::read(poetry, &mut Environment::new()).unwrap(); assert_eq!(poem[0].verb(), "cd"); assert_eq!( poem[0].clause(), diff --git a/src/poem/read.rs b/src/poem/read.rs index da13f76..3c4a720 100644 --- a/src/poem/read.rs +++ b/src/poem/read.rs @@ -42,8 +42,12 @@ impl fmt::Display for Mishap { /// A [Poem] can add more [Verse]s to itself trait Appendable { type Type; - fn add(&mut self, verse: &mut Self::Type, meter: Rune, env: &Environment) - -> Result<(), Mishap>; + fn add( + &mut self, + verse: &mut Self::Type, + meter: Rune, + env: &mut Environment, + ) -> Result<(), Mishap>; } impl Appendable for Poem { @@ -57,7 +61,7 @@ impl Appendable for Poem { &mut self, verse: &mut Self::Type, meter: Rune, - env: &Environment, + env: &mut Environment, ) -> Result<(), Mishap> { if verse.is_empty() { return Ok(()); @@ -81,10 +85,16 @@ impl Appendable for Poem { // Check for aliases match env.aliases.get(&verse.verb()) { - Some(alias) => { + Some(alias) if env.cs == 0 => { + // Increase the callstack + env.cs = 1; + // Interpret the alias (could be a complex poem) let mut poem = Poem::read(alias.to_string(), env)?; + // Decrease the callstack + env.cs = 0; + // Try and get the last verse let lv = match poem.last_mut() { Some(lv) => lv, @@ -109,7 +119,7 @@ impl Appendable for Poem { self.push(v.clone()); } } - None => { + Some(_) | None => { // Push verse(s) self.push(verse.clone()); } @@ -125,7 +135,7 @@ impl Appendable for Poem { /// A [Poem] can parse poetry pub trait Readable { - fn read(poetry: String, env: &Environment) -> Result<Poem, Mishap>; + fn read(poetry: String, env: &mut Environment) -> Result<Poem, Mishap>; } impl Readable for Poem { @@ -135,7 +145,7 @@ impl Readable for Poem { /// machine-runnable [Poem]. If there is a parse error, [Poem::read] may /// return a [Mishap]. See [Poem::recite][super::recite] for how each /// [Verse] in a [Poem] is called. - fn read(poetry: String, env: &Environment) -> Result<Poem, Mishap> { + fn read(poetry: String, env: &mut Environment) -> Result<Poem, Mishap> { // Get all the characters in the input string as an iterator let mut chars = poetry.chars().into_iter(); |