diff options
-rw-r--r-- | src/compose.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/poem.rs | 74 | ||||
-rw-r--r-- | src/poem/anthology/alias.rs | 2 | ||||
-rw-r--r-- | src/poem/anthology/source.rs | 2 | ||||
-rw-r--r-- | src/poem/read.rs | 34 | ||||
-rw-r--r-- | src/poem/read/parse.rs | 32 | ||||
-rw-r--r-- | src/poem/recite.rs | 25 |
8 files changed, 98 insertions, 75 deletions
diff --git a/src/compose.rs b/src/compose.rs index c90f470..f59b719 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) { + let poem = match Poem::read(poetry, &Environment::new()) { Ok(poem) => poem, Err(e) => { eprintln!( diff --git a/src/main.rs b/src/main.rs index e0d0ed1..3e0a2e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -65,7 +65,7 @@ fn repl(away: &mut Arc<Mutex<bool>>, env: &mut Environment) { *away.lock().unwrap() = true; // Parse the poem - let poem = Poem::read(poetry); + let poem = Poem::read(poetry, env); let poem = match poem { Ok(poem) => poem, Err(e) => { diff --git a/src/poem.rs b/src/poem.rs index 0eda6b7..a063a14 100644 --- a/src/poem.rs +++ b/src/poem.rs @@ -15,10 +15,11 @@ mod tests { use super::elements::rune::Rune; use super::read::Readable; use super::*; + use crate::compose::Environment; #[test] fn it_parses_a_verse_with_no_meter() { - let poem = Poem::read("cargo build --release".to_string()); + let poem = Poem::read("cargo build --release".to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "cargo"); @@ -26,7 +27,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_couplet_meter() { - let poem = Poem::read("ls -la | lolcat".to_string()); + let poem = Poem::read("ls -la | lolcat".to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "ls"); @@ -35,7 +36,7 @@ mod tests { #[test] fn it_parses_a_verse_with_the_quiet_meter() { - let poem = Poem::read("sleep 20 &".to_string()); + let poem = Poem::read("sleep 20 &".to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "sleep"); @@ -44,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()); + let poem = Poem::read("sleep 2 && ls -la".to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "sleep"); @@ -53,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()); + let poem = Poem::read("sleep 2; ls -la".to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); assert_eq!(poem.first().unwrap().verb(), "sleep"); @@ -62,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()); + let poem = Poem::read("lolcat < src/main.rs".to_string(), &Environment::new()); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); let verse = verses.next().unwrap(); @@ -78,7 +79,10 @@ mod tests { #[test] fn it_parses_a_verse_with_the_write_rune() { - let poem = Poem::read("cat src/main.rs > /dev/null".to_string()); + let poem = Poem::read( + "cat src/main.rs > /dev/null".to_string(), + &Environment::new(), + ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); let verse = verses.next().unwrap(); @@ -94,7 +98,10 @@ mod tests { #[test] fn it_parses_a_verse_with_the_addendum_rune() { - let poem = Poem::read("cat src/main.rs >> /dev/null".to_string()); + let poem = Poem::read( + "cat src/main.rs >> /dev/null".to_string(), + &Environment::new(), + ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); let verse = verses.next().unwrap(); @@ -110,37 +117,40 @@ 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()); + let poem = Poem::read("lolcat <".to_string(), &Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("lolcat <;".to_string()); + let poem = Poem::read("lolcat <;".to_string(), &Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("lolcat < && ls -la".to_string()); + let poem = Poem::read("lolcat < && ls -la".to_string(), &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()); + let poem = Poem::read("cat src/main.rs >".to_string(), &Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat src/main.rs >;".to_string()); + let poem = Poem::read("cat src/main.rs >;".to_string(), &Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat > && ls -la".to_string()); + let poem = Poem::read("cat > && ls -la".to_string(), &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()); + let poem = Poem::read("cat src/main.rs >>".to_string(), &Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat src/main.rs >>;".to_string()); + let poem = Poem::read("cat src/main.rs >>;".to_string(), &Environment::new()); assert!(poem.is_err()); - let poem = Poem::read("cat >> && ls -la".to_string()); + let poem = Poem::read("cat >> && ls -la".to_string(), &Environment::new()); assert!(poem.is_err()); } #[test] 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()); + let poem = Poem::read( + "ls -la | lolcat && echo hello | lolcat && sleep 2 &".to_string(), + &Environment::new(), + ); assert!(poem.is_ok()); let mut verses = poem.unwrap().into_iter(); @@ -170,25 +180,25 @@ mod tests { #[test] fn it_parses_the_continue_meter_without_a_stanza() { - let poem = Poem::read(";;;;;;;".to_string()); + let poem = Poem::read(";;;;;;;".to_string(), &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()); + let poem = Poem::read("|".to_string(), &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()); + let poem = Poem::read("&".to_string(), &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()); + let poem = Poem::read("&&".to_string(), &Environment::new()); assert!(poem.is_err()); } @@ -199,7 +209,7 @@ mod tests { sleep 2 "; - let poem = Poem::read(file.to_string()); + let poem = Poem::read(file.to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); @@ -239,7 +249,7 @@ mod tests { wc -l src/**/*.rs | lolcat; ls -la | grep git "; - let poem = Poem::read(file.to_string()); + let poem = Poem::read(file.to_string(), &Environment::new()); assert!(poem.is_ok()); let poem = poem.unwrap(); @@ -249,36 +259,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).is_err(), true); + assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); let poetry = "cat file.txt&&|".to_string(); - assert_eq!(Poem::read(poetry).is_err(), true); + assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); let poetry = "cat <".to_string(); - assert_eq!(Poem::read(poetry).is_err(), true); + assert_eq!(Poem::read(poetry, &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).is_err(), true); + assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); let poetry = "echo \"hello".to_string(); - assert_eq!(Poem::read(poetry).is_err(), true); + assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); let poetry = "`true".to_string(); - assert_eq!(Poem::read(poetry).is_err(), true); + assert_eq!(Poem::read(poetry, &Environment::new()).is_err(), true); } #[test] fn it_interprets_tilda_as_home() { let poetry = "cd ~".to_string(); - let poem = Poem::read(poetry).unwrap(); + let poem = Poem::read(poetry, &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).unwrap(); + let poem = Poem::read(poetry, &Environment::new()).unwrap(); assert_eq!(poem[0].verb(), "cd"); assert_eq!( poem[0].clause(), diff --git a/src/poem/anthology/alias.rs b/src/poem/anthology/alias.rs index 0183ec2..4c9b7b6 100644 --- a/src/poem/anthology/alias.rs +++ b/src/poem/anthology/alias.rs @@ -39,7 +39,7 @@ pub fn incant(verse: &Verse, out: &mut String, aliases: &mut HashMap<String, Str } if verse.couplet { - *out = lines.join("\n"); + *out = format!("{}\n", lines.join("\n")); } else { println!("{}", lines.join("\n")); } diff --git a/src/poem/anthology/source.rs b/src/poem/anthology/source.rs index 77d1330..182fef4 100644 --- a/src/poem/anthology/source.rs +++ b/src/poem/anthology/source.rs @@ -34,7 +34,7 @@ pub fn incant(verse: &Verse, out: &mut String, env: &mut Environment) -> i32 { } }; - let poem = match Poem::read(poetry) { + let poem = match Poem::read(poetry, env) { Ok(poem) => poem, Err(e) => { eprintln!("dwvsh: {}", e.to_string().to_lowercase()); diff --git a/src/poem/read.rs b/src/poem/read.rs index d88cf0d..0af7fec 100644 --- a/src/poem/read.rs +++ b/src/poem/read.rs @@ -4,7 +4,8 @@ use super::{ }; use core::fmt; mod parse; -use crate::{next, poem, remark, string}; +use crate::compose::Environment; +use crate::{append, next, poem, remark, string}; #[derive(Debug, PartialEq, Eq)] pub enum Mishap { @@ -51,20 +52,25 @@ impl Appendable for Poem { /// Push a [Verse] to the [Poem] after checking that the [Verse] is not /// empty. Also sets the meter of the [Verse]. fn add(&mut self, verse: &mut Self::Type, last: Rune, meter: Rune) { - if !verse.is_empty() { - verse.meter = meter; - if last == Rune::Couplet || meter == Rune::Couplet { - verse.couplet = true; - } - self.push(verse.clone()); - verse.clear(); + if verse.is_empty() { + return; + } + + // Check the meter + verse.meter = meter; + if last == Rune::Couplet || meter == Rune::Couplet { + verse.couplet = true; } + + // Push verse(s) and clear the current verse stack + self.push(verse.clone()); + verse.clear(); } } /// A [Poem] can parse poetry pub trait Readable { - fn read(poetry: String) -> Result<Poem, Mishap>; + fn read(poetry: String, env: &Environment) -> Result<Poem, Mishap>; } impl Readable for Poem { @@ -74,7 +80,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) -> Result<Poem, Mishap> { + fn read(poetry: String, env: &Environment) -> Result<Poem, Mishap> { // Get all the characters in the input string as an iterator let mut chars = poetry.chars().into_iter(); @@ -118,7 +124,8 @@ impl Readable for Poem { } // Push the verse and break - poem.add(&mut verse, last, Rune::None); + // poem.add(&mut verse, last, Rune::None); + append!(poem, last, Rune::None, verse, env); break; } }; @@ -207,7 +214,7 @@ impl Readable for Poem { // Indicates a sub-poem Rune::Poem => { - poem!(chars, j, i, c, verse, word); + poem!(chars, j, i, c, verse, word, env); } // Indicates a file operation (<, >, or >>) @@ -221,7 +228,8 @@ impl Readable for Poem { // These meters indicate the end of a verse Rune::Couplet | Rune::Quiet | Rune::And | Rune::Continue => { verse.add(&mut word); - poem.add(&mut verse, last, rune); + // poem.add(&mut verse, last, rune); + append!(poem, last, rune, verse, env); } // Interpret ~ as $HOME diff --git a/src/poem/read/parse.rs b/src/poem/read/parse.rs index 0836eea..70367c4 100644 --- a/src/poem/read/parse.rs +++ b/src/poem/read/parse.rs @@ -63,7 +63,7 @@ macro_rules! remark { /// Same as the [string!] macro, but don't `continue` #[macro_export] macro_rules! poem { - ($chars:expr, $j:expr, $i:expr, $c:expr, $verse:expr, $word:expr) => { + ($chars:expr, $j:expr, $i:expr, $c:expr, $verse:expr, $word:expr, $env:expr) => { let token = $c; let mut poetry = Word::new(); loop { @@ -76,7 +76,7 @@ macro_rules! poem { } } } - let sp = Poem::read(poetry.iter().collect()); + let sp = Poem::read(poetry.iter().collect(), $env); let sp = match sp { Ok(sp) => sp, Err(e) => return Err(e), @@ -85,3 +85,31 @@ macro_rules! poem { $word.push('\x0b'); }; } + +/// Append a verse to the poem +/// +/// Append a verse to poem, first checking for aliases in the environment, and +/// processing the alias first, if necessary. +#[macro_export] +macro_rules! append { + ($poem:expr, $last:expr, $meter:expr, $verse:expr, $env:expr) => { + if !$verse.is_empty() { + match $env.aliases.get(&$verse.verb()) { + Some(alias) => { + let alias = alias.to_string(); + let mut poem = Poem::read(alias, $env)?; + let len = poem.len(); + for (i, verse) in poem.iter_mut().enumerate() { + if $verse.clause().is_some() && i + 1 == len { + verse.stanza.append(&mut $verse.clause().unwrap()); + } + $poem.push(verse.clone()); + } + } + None => { + $poem.add(&mut $verse, $last, $meter); + } + } + } + }; +} diff --git a/src/poem/recite.rs b/src/poem/recite.rs index 1f24d14..4038e37 100644 --- a/src/poem/recite.rs +++ b/src/poem/recite.rs @@ -4,7 +4,6 @@ use crate::compose::Environment; use crate::path; use crate::poem::anthology; use crate::poem::elements::rune::Rune; -use crate::poem::read::Readable; use std::env; use std::{ io, @@ -26,30 +25,8 @@ impl Reciteable for Poem { // Keep track of pids for background processes let mut pids: Arc<Mutex<Vec<i32>>> = Arc::new(Mutex::new(Vec::new())); - // Check for aliases - let mut vv = Poem::new(); - for verse in self.iter() { - let alias = match env.aliases.get(&verse.verb()) { - Some(alias) => alias, - None => { - vv.push(verse.clone()); - continue; - } - } - .to_string(); - - let mut poem = Poem::read(alias).unwrap(); - let len = poem.len(); - for (i, new_verse) in poem.iter_mut().enumerate() { - if verse.clause().is_some() && i + 1 == len { - new_verse.stanza.append(&mut verse.clause().unwrap()); - } - vv.push(new_verse.clone()); - } - } - // Loop through each verse in the poem - for verse in vv.iter() { + for verse in self.iter() { // Verse may need to be mutable let mut verse = verse.clone(); |