Skip to content

Commit

Permalink
Alternative implementation for Tilde expansion (#85)
Browse files Browse the repository at this point in the history
* Initial implementation for Tilde expansion
* Added support for single `~` and `a~b`
  • Loading branch information
prsabahrami authored Sep 8, 2024
1 parent a8cd958 commit b77493a
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 7 deletions.
37 changes: 32 additions & 5 deletions crates/deno_task_shell/src/grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,37 @@ COMMENT = _{ "#" ~ (!NEWLINE ~ ANY)* }
// Basic tokens
QUOTED_WORD = { DOUBLE_QUOTED | SINGLE_QUOTED }

UNQUOTED_PENDING_WORD = ${ !OPERATOR ~
(!(WHITESPACE | NEWLINE) ~ (
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) |
UNQUOTED_CHAR |
QUOTED_WORD
))+
}

TILDE_PREFIX = ${
"~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/") ~ (
(!("\"" | "'" | "$" | "\\" | "/") ~ ANY)
))*
}

ASSIGNMENT_TILDE_PREFIX = ${
"~" ~ (!(OPERATOR | WHITESPACE | NEWLINE | "/" | ":") ~
(!("\"" | "'" | "$" | "\\" | "/") ~ ANY)
)*
}

FILE_NAME_PENDING_WORD = ${ (!(WHITESPACE | OPERATOR | NEWLINE) ~ (UNQUOTED_ESCAPE_CHAR | ("$" ~ VARIABLE) | UNQUOTED_CHAR | QUOTED_WORD))+ }

Expand All @@ -41,7 +62,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
Expand All @@ -59,6 +85,7 @@ DLESSDASH = { "<<-" }
CLOBBER = { ">|" }
AMPERSAND = { "&" }
EXIT_STATUS = ${ "$?" }
TILDE = ${ "~" }


// Operators
Expand Down
64 changes: 62 additions & 2 deletions crates/deno_task_shell/src/parser.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2018-2024 the Deno authors. MIT license.

use anyhow::{anyhow, Result};
use anyhow::{anyhow, Context, Result};
use pest::iterators::Pair;
use pest::Parser;
use pest_derive::Parser;
Expand Down Expand Up @@ -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<String>,
}

impl TildePrefix {
pub fn only_tilde(self) -> bool {
self.user.is_none()
}

pub fn new(user: Option<String>) -> Self {
TildePrefix { user }
}
}

#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Word(Vec<WordPart>);
Expand Down Expand Up @@ -261,6 +278,8 @@ pub enum WordPart {
Command(SequentialList),
/// Quoted string (ex. `"hello"` or `'test'`)
Quoted(Vec<WordPart>),
/// Tilde prefix (ex. `~user/path` or `~/bin`)
Tilde(TildePrefix),
}

#[cfg_attr(feature = "serialization", derive(serde::Serialize))]
Expand Down Expand Up @@ -730,6 +749,10 @@ fn parse_word(pair: Pair<Rule>) -> Result<Word> {
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: {:?}",
Expand Down Expand Up @@ -795,6 +818,17 @@ fn parse_word(pair: Pair<Rule>) -> Result<Word> {
}
}

fn parse_tilde_prefix(pair: Pair<Rule>) -> Result<WordPart> {
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<Rule>) -> Result<WordPart> {
let mut parts = Vec::new();
let inner = pair.into_inner().next().unwrap();
Expand Down Expand Up @@ -863,7 +897,7 @@ fn parse_env_var(pair: Pair<Rule>) -> Result<EnvVar> {

// 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()
};
Expand All @@ -874,6 +908,32 @@ fn parse_env_var(pair: Pair<Rule>) -> Result<EnvVar> {
})
}

fn parse_assignment_value(pair: Pair<Rule>) -> Result<Word> {
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<Rule>) -> Result<Redirect> {
let mut inner = pair.into_inner();

Expand Down
21 changes: 21 additions & 0 deletions crates/deno_task_shell/src/shell/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -738,6 +741,12 @@ impl EvaluateWordTextError {
}
}

impl From<anyhow::Error> for EvaluateWordTextError {
fn from(err: anyhow::Error) -> Self {
Self::FailedToGetHomeDirectory(err)
}
}

fn evaluate_word_parts(
parts: Vec<WordPart>,
state: &ShellState,
Expand Down Expand Up @@ -896,6 +905,18 @@ 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();
current_text.push(TextPart::Text(home_str));
} else {
todo!("tilde expansion with user name is not supported");
}
continue;
}
};

// This text needs to be turned into a vector of strings.
Expand Down
7 changes: 7 additions & 0 deletions scripts/tilde_expansion.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ls ~/Desktop

echo ~$var

echo ~\/bin

echo ~parsabahraminejad/Desktop

0 comments on commit b77493a

Please sign in to comment.