summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRory Dudley2024-04-04 22:12:14 -0600
committerRory Dudley2024-04-04 22:12:14 -0600
commit1415c8f9b89699000ef8d864ff8f0e1bebca4a5f (patch)
tree64093c0eded6a6f28105dbd743729b4075d32889
parentbadbba41476cd6fd424042c48681acde82227ba6 (diff)
downloaddwarvish-1415c8f9b89699000ef8d864ff8f0e1bebca4a5f.tar.gz
Handle aliases in read()
Instead of handling aliases in the recite() function, which requires two loops to handle properly with the current implementation, offload checking for aliases to the read() function.
-rw-r--r--src/compose.rs2
-rw-r--r--src/main.rs2
-rw-r--r--src/poem.rs74
-rw-r--r--src/poem/anthology/alias.rs2
-rw-r--r--src/poem/anthology/source.rs2
-rw-r--r--src/poem/read.rs34
-rw-r--r--src/poem/read/parse.rs32
-rw-r--r--src/poem/recite.rs25
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();