From 2927b538c6294c4f4403909e84944513df4a5764 Mon Sep 17 00:00:00 2001 From: prsabahrami Date: Sun, 8 Sep 2024 11:44:31 -0400 Subject: [PATCH 1/6] Inital implementation for Tilde expansion --- crates/deno_task_shell/src/grammar.pest | 23 +++++++- crates/deno_task_shell/src/parser.rs | 63 ++++++++++++++++++++- crates/deno_task_shell/src/shell/execute.rs | 4 ++ scripts/tilde_expansion.sh | 7 +++ 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 scripts/tilde_expansion.sh diff --git a/crates/deno_task_shell/src/grammar.pest b/crates/deno_task_shell/src/grammar.pest index 0b270ee..a3d710b 100644 --- a/crates/deno_task_shell/src/grammar.pest +++ b/crates/deno_task_shell/src/grammar.pest @@ -7,17 +7,28 @@ COMMENT = _{ "#" ~ (!NEWLINE ~ ANY)* } // Basic tokens QUOTED_WORD = { DOUBLE_QUOTED | SINGLE_QUOTED } -UNQUOTED_PENDING_WORD = ${ !OPERATOR ~ - (!(WHITESPACE | NEWLINE) ~ ( +UNQUOTED_PENDING_WORD = ${ + (!(OPERATOR | WHITESPACE | NEWLINE) ~ ( EXIT_STATUS | UNQUOTED_ESCAPE_CHAR | SUB_COMMAND | ("$" ~ "{" ~ VARIABLE ~ "}" | "$" ~ VARIABLE) | + TILDE_PREFIX | UNQUOTED_CHAR | QUOTED_WORD ) )+ } +TILDE_PREFIX = ${ + "~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/") ~ ( + QUOTED_CHAR + ))* +} + +ASSIGNMENT_TILDE_PREFIX = ${ + "~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/" | ":") ~ ANY)* +} + FILE_NAME_PENDING_WORD = ${ (!(WHITESPACE | OPERATOR | NEWLINE) ~ (UNQUOTED_ESCAPE_CHAR | ("$" ~ VARIABLE) | UNQUOTED_CHAR | QUOTED_WORD))+ } QUOTED_PENDING_WORD = ${ ( @@ -41,7 +52,12 @@ DOUBLE_QUOTED = @{ "\"" ~ QUOTED_PENDING_WORD ~ "\"" } SINGLE_QUOTED = @{ "'" ~ (!"'" ~ ANY)* ~ "'" } NAME = ${ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } -ASSIGNMENT_WORD = { NAME ~ "=" ~ UNQUOTED_PENDING_WORD? } +ASSIGNMENT_WORD = { NAME ~ "=" ~ ASSIGNMENT_VALUE? } +ASSIGNMENT_VALUE = ${ + ASSIGNMENT_TILDE_PREFIX ~ + ((":" ~ ASSIGNMENT_TILDE_PREFIX) | (!":" ~ UNQUOTED_PENDING_WORD))* | + UNQUOTED_PENDING_WORD +} IO_NUMBER = @{ ASCII_DIGIT+ } // Special tokens @@ -59,6 +75,7 @@ DLESSDASH = { "<<-" } CLOBBER = { ">|" } AMPERSAND = { "&" } EXIT_STATUS = ${ "$?" } +TILDE = ${ "~" } // Operators diff --git a/crates/deno_task_shell/src/parser.rs b/crates/deno_task_shell/src/parser.rs index 61bd0a6..a2a9899 100644 --- a/crates/deno_task_shell/src/parser.rs +++ b/crates/deno_task_shell/src/parser.rs @@ -1,6 +1,6 @@ // Copyright 2018-2024 the Deno authors. MIT license. -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Result, Context}; use pest::iterators::Pair; use pest::Parser; use pest_derive::Parser; @@ -214,6 +214,23 @@ impl EnvVar { } } +#[cfg_attr(feature = "serialization", derive(serde::Serialize))] +#[cfg_attr(feature = "serialization", serde(rename_all = "camelCase"))] +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct TildePrefix { + pub user: Option, +} + +impl TildePrefix { + pub fn only_tilde(self) -> bool { + self.user.is_none() + } + + pub fn new(user: Option) -> Self { + TildePrefix { user } + } +} + #[cfg_attr(feature = "serialization", derive(serde::Serialize))] #[derive(Debug, PartialEq, Eq, Clone)] pub struct Word(Vec); @@ -261,6 +278,8 @@ pub enum WordPart { Command(SequentialList), /// Quoted string (ex. `"hello"` or `'test'`) Quoted(Vec), + /// Tilde prefix (ex. `~user/path`) + Tilde(TildePrefix), } #[cfg_attr(feature = "serialization", derive(serde::Serialize))] @@ -730,6 +749,10 @@ fn parse_word(pair: Pair) -> Result { let quoted = parse_quoted_word(part)?; parts.push(quoted); } + Rule::TILDE_PREFIX => { + let tilde_prefix = parse_tilde_prefix(part)?; + parts.push(tilde_prefix); + } _ => { return Err(anyhow::anyhow!( "Unexpected rule in UNQUOTED_PENDING_WORD: {:?}", @@ -795,6 +818,17 @@ fn parse_word(pair: Pair) -> Result { } } +fn parse_tilde_prefix(pair: Pair) -> Result { + let tilde_prefix_str = pair.as_str(); + let user = if tilde_prefix_str.len() > 1 { + Some(tilde_prefix_str[1..].to_string()) + } else { + None + }; + let tilde_prefix = TildePrefix::new(user); + Ok(WordPart::Tilde(tilde_prefix)) +} + fn parse_quoted_word(pair: Pair) -> Result { let mut parts = Vec::new(); let inner = pair.into_inner().next().unwrap(); @@ -863,7 +897,7 @@ fn parse_env_var(pair: Pair) -> Result { // Get the value of the environment variable let word_value = if let Some(value) = parts.next() { - parse_word(value)? + parse_assignment_value(value).context("Failed to parse assignment value")? } else { Word::new_empty() }; @@ -874,6 +908,31 @@ fn parse_env_var(pair: Pair) -> Result { }) } +fn parse_assignment_value(pair: Pair) -> Result { + let mut parts = Vec::new(); + + for part in pair.into_inner() { + match part.as_rule() { + Rule::ASSIGNMENT_TILDE_PREFIX => { + let tilde_prefix = parse_tilde_prefix(part).context("Failed to parse tilde prefix")?; + parts.push(tilde_prefix); + } + Rule::UNQUOTED_PENDING_WORD => { + let word_parts = parse_word(part)?; + parts.extend(word_parts.into_parts()); + } + _ => { + return Err(anyhow::anyhow!( + "Unexpected rule in assignment value: {:?}", + part.as_rule() + )) + } + } + } + + Ok(Word::new(parts)) +} + fn parse_io_redirect(pair: Pair) -> Result { let mut inner = pair.into_inner(); diff --git a/crates/deno_task_shell/src/shell/execute.rs b/crates/deno_task_shell/src/shell/execute.rs index cd988ef..45e148e 100644 --- a/crates/deno_task_shell/src/shell/execute.rs +++ b/crates/deno_task_shell/src/shell/execute.rs @@ -895,6 +895,10 @@ fn evaluate_word_parts( current_text.push(TextPart::Quoted(text)); continue; + }, + WordPart::Tilde(tilde_prefix) => { + current_text.push(TextPart::Text(tilde_prefix)); + continue; } }; diff --git a/scripts/tilde_expansion.sh b/scripts/tilde_expansion.sh new file mode 100644 index 0000000..155cb69 --- /dev/null +++ b/scripts/tilde_expansion.sh @@ -0,0 +1,7 @@ +ls ~/Desktop + +echo ~$var + +echo ~\/bin + +echo ~parsabahraminejad/Desktop From 9224afbc2d118e15ee5a9a81c8b9436d53651d55 Mon Sep 17 00:00:00 2001 From: prsabahrami Date: Sun, 8 Sep 2024 11:52:13 -0400 Subject: [PATCH 2/6] Added support for single ~ --- crates/deno_task_shell/src/shell/execute.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/deno_task_shell/src/shell/execute.rs b/crates/deno_task_shell/src/shell/execute.rs index 45e148e..9dbf952 100644 --- a/crates/deno_task_shell/src/shell/execute.rs +++ b/crates/deno_task_shell/src/shell/execute.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::path::Path; use std::rc::Rc; +use anyhow::Context; use futures::future; use futures::future::LocalBoxFuture; use futures::FutureExt; @@ -729,6 +730,8 @@ pub enum EvaluateWordTextError { }, #[error("glob: no matches found '{}'", pattern)] NoFilesMatched { pattern: String }, + #[error("Failed to get home directory")] + FailedToGetHomeDirectory(anyhow::Error), } impl EvaluateWordTextError { @@ -738,6 +741,12 @@ impl EvaluateWordTextError { } } +impl From for EvaluateWordTextError { + fn from(err: anyhow::Error) -> Self { + Self::FailedToGetHomeDirectory(err) + } +} + fn evaluate_word_parts( parts: Vec, state: &ShellState, @@ -897,7 +906,12 @@ fn evaluate_word_parts( continue; }, WordPart::Tilde(tilde_prefix) => { - current_text.push(TextPart::Text(tilde_prefix)); + if tilde_prefix.only_tilde() { + let home_str = dirs::home_dir().context("Failed to get home directory")?.display().to_string(); + current_text.push(TextPart::Text(home_str)); + } else { + todo!("tilde expansion with user name is not supported"); + } continue; } }; From 6fc25ab4ad92db23d8f96ac689f723d269e9d73d Mon Sep 17 00:00:00 2001 From: prsabahrami Date: Sun, 8 Sep 2024 11:52:41 -0400 Subject: [PATCH 3/6] run fmt --- crates/deno_task_shell/src/parser.rs | 43 +++++++++++---------- crates/deno_task_shell/src/shell/execute.rs | 7 +++- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/crates/deno_task_shell/src/parser.rs b/crates/deno_task_shell/src/parser.rs index a2a9899..3d1922b 100644 --- a/crates/deno_task_shell/src/parser.rs +++ b/crates/deno_task_shell/src/parser.rs @@ -1,6 +1,6 @@ // Copyright 2018-2024 the Deno authors. MIT license. -use anyhow::{anyhow, Result, Context}; +use anyhow::{anyhow, Context, Result}; use pest::iterators::Pair; use pest::Parser; use pest_derive::Parser; @@ -909,28 +909,29 @@ fn parse_env_var(pair: Pair) -> Result { } fn parse_assignment_value(pair: Pair) -> Result { - let mut parts = Vec::new(); - - for part in pair.into_inner() { - match part.as_rule() { - Rule::ASSIGNMENT_TILDE_PREFIX => { - let tilde_prefix = parse_tilde_prefix(part).context("Failed to parse tilde prefix")?; - parts.push(tilde_prefix); - } - Rule::UNQUOTED_PENDING_WORD => { - let word_parts = parse_word(part)?; - parts.extend(word_parts.into_parts()); - } - _ => { - return Err(anyhow::anyhow!( - "Unexpected rule in assignment value: {:?}", - part.as_rule() - )) - } - } + let mut parts = Vec::new(); + + for part in pair.into_inner() { + match part.as_rule() { + Rule::ASSIGNMENT_TILDE_PREFIX => { + let tilde_prefix = + parse_tilde_prefix(part).context("Failed to parse tilde prefix")?; + parts.push(tilde_prefix); + } + Rule::UNQUOTED_PENDING_WORD => { + let word_parts = parse_word(part)?; + parts.extend(word_parts.into_parts()); + } + _ => { + return Err(anyhow::anyhow!( + "Unexpected rule in assignment value: {:?}", + part.as_rule() + )) + } } + } - Ok(Word::new(parts)) + Ok(Word::new(parts)) } fn parse_io_redirect(pair: Pair) -> Result { diff --git a/crates/deno_task_shell/src/shell/execute.rs b/crates/deno_task_shell/src/shell/execute.rs index 9dbf952..62daa5d 100644 --- a/crates/deno_task_shell/src/shell/execute.rs +++ b/crates/deno_task_shell/src/shell/execute.rs @@ -904,10 +904,13 @@ fn evaluate_word_parts( current_text.push(TextPart::Quoted(text)); continue; - }, + } WordPart::Tilde(tilde_prefix) => { if tilde_prefix.only_tilde() { - let home_str = dirs::home_dir().context("Failed to get home directory")?.display().to_string(); + let home_str = dirs::home_dir() + .context("Failed to get home directory")? + .display() + .to_string(); current_text.push(TextPart::Text(home_str)); } else { todo!("tilde expansion with user name is not supported"); From c20d2e393039ac767c4e414ec52b0a7717a854ba Mon Sep 17 00:00:00 2001 From: prsabahrami Date: Sun, 8 Sep 2024 12:04:37 -0400 Subject: [PATCH 4/6] small fix --- crates/deno_task_shell/src/grammar.pest | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/deno_task_shell/src/grammar.pest b/crates/deno_task_shell/src/grammar.pest index a3d710b..ae4f95a 100644 --- a/crates/deno_task_shell/src/grammar.pest +++ b/crates/deno_task_shell/src/grammar.pest @@ -21,12 +21,14 @@ UNQUOTED_PENDING_WORD = ${ TILDE_PREFIX = ${ "~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/") ~ ( - QUOTED_CHAR + (!("\"" | "'" | "$" | "\\" | "/") ~ ANY) ))* } ASSIGNMENT_TILDE_PREFIX = ${ - "~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/" | ":") ~ ANY)* + "~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/" | ":") ~ + (!("\"" | "'" | "$" | "\\" | "/") ~ ANY) + )* } FILE_NAME_PENDING_WORD = ${ (!(WHITESPACE | OPERATOR | NEWLINE) ~ (UNQUOTED_ESCAPE_CHAR | ("$" ~ VARIABLE) | UNQUOTED_CHAR | QUOTED_WORD))+ } From 8c28dd9125c527dfca6807ee1b9524d4792dbff1 Mon Sep 17 00:00:00 2001 From: prsabahrami Date: Sun, 8 Sep 2024 12:10:02 -0400 Subject: [PATCH 5/6] Fixed parsing issue on echo a~b --- crates/deno_task_shell/src/grammar.pest | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/deno_task_shell/src/grammar.pest b/crates/deno_task_shell/src/grammar.pest index ae4f95a..8909557 100644 --- a/crates/deno_task_shell/src/grammar.pest +++ b/crates/deno_task_shell/src/grammar.pest @@ -8,16 +8,24 @@ COMMENT = _{ "#" ~ (!NEWLINE ~ ANY)* } QUOTED_WORD = { DOUBLE_QUOTED | SINGLE_QUOTED } UNQUOTED_PENDING_WORD = ${ + (TILDE_PREFIX ~ (!(OPERATOR | WHITESPACE | NEWLINE) ~ ( + EXIT_STATUS | + UNQUOTED_ESCAPE_CHAR | + SUB_COMMAND | + ("$" ~ "{" ~ VARIABLE ~ "}" | "$" ~ VARIABLE) | + UNQUOTED_CHAR | + QUOTED_WORD + ))*) + | (!(OPERATOR | WHITESPACE | NEWLINE) ~ ( EXIT_STATUS | UNQUOTED_ESCAPE_CHAR | SUB_COMMAND | ("$" ~ "{" ~ VARIABLE ~ "}" | "$" ~ VARIABLE) | - TILDE_PREFIX | UNQUOTED_CHAR | QUOTED_WORD - ) -)+ } + ))+ +} TILDE_PREFIX = ${ "~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/") ~ ( From 18c11a636c63017a1596e97df350045e3483326d Mon Sep 17 00:00:00 2001 From: Parsa Bahraminejad Date: Sun, 8 Sep 2024 12:26:53 -0400 Subject: [PATCH 6/6] Update parser.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Ondřej Čertík --- crates/deno_task_shell/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/deno_task_shell/src/parser.rs b/crates/deno_task_shell/src/parser.rs index 3d1922b..9032f53 100644 --- a/crates/deno_task_shell/src/parser.rs +++ b/crates/deno_task_shell/src/parser.rs @@ -278,7 +278,7 @@ pub enum WordPart { Command(SequentialList), /// Quoted string (ex. `"hello"` or `'test'`) Quoted(Vec), - /// Tilde prefix (ex. `~user/path`) + /// Tilde prefix (ex. `~user/path` or `~/bin`) Tilde(TildePrefix), }