mirror of
https://github.com/nushell/nushell
synced 2024-12-26 04:53:09 +00:00
Simplify string interpolation (#3401)
* [DRAFT] simplify string interpolation * Fix test
This commit is contained in:
parent
25a8caa9b0
commit
311c0e3f50
6 changed files with 62 additions and 117 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3710,7 +3710,6 @@ dependencies = [
|
|||
name = "nu-table"
|
||||
version = "0.31.0"
|
||||
dependencies = [
|
||||
|
||||
"nu-ansi-term 0.31.0",
|
||||
"regex 1.5.3",
|
||||
"unicode-width",
|
||||
|
|
|
@ -52,7 +52,7 @@ impl WholeStreamCommand for Each {
|
|||
Example {
|
||||
description: "Number each item and echo a message",
|
||||
example:
|
||||
"echo ['bob' 'fred'] | each --numbered { echo `{{$it.index}} is {{$it.item}}` }",
|
||||
"echo ['bob' 'fred'] | each --numbered { echo $\"{$it.index} is {$it.item}\" }",
|
||||
result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
|
||||
},
|
||||
]
|
||||
|
|
|
@ -8,7 +8,7 @@ fn reduce_table_column() {
|
|||
echo "[{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}]"
|
||||
| from json
|
||||
| get total
|
||||
| reduce -f 20 { $it + ( math eval `{{$acc}}^1.05` )}
|
||||
| reduce -f 20 { $it + (math eval $"{$acc}^1.05")}
|
||||
| str from -d 1
|
||||
"#
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ fn reduce_table_column() {
|
|||
r#"
|
||||
echo "[{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}]"
|
||||
| from json
|
||||
| reduce -f 20 { $it.total + ( math eval `{{$acc}}^1.05` )}
|
||||
| reduce -f 20 { $it.total + (math eval $"{$acc}^1.05")}
|
||||
| str from -d 1
|
||||
"#
|
||||
)
|
||||
|
|
|
@ -467,7 +467,10 @@ fn parse_dollar_expr(
|
|||
scope: &dyn ParserScope,
|
||||
) -> (SpannedExpression, Option<ParseError>) {
|
||||
trace!("Parsing dollar expression: {:?}", lite_arg.item);
|
||||
if let (expr, None) = parse_range(lite_arg, scope) {
|
||||
if lite_arg.item.starts_with("$\"") && lite_arg.item.len() > 1 && lite_arg.item.ends_with('"') {
|
||||
// This is an interpolated string
|
||||
parse_interpolated_string(&lite_arg, scope)
|
||||
} else if let (expr, None) = parse_range(lite_arg, scope) {
|
||||
(expr, None)
|
||||
} else if let (expr, None) = parse_full_column_path(lite_arg, scope) {
|
||||
(expr, None)
|
||||
|
@ -493,79 +496,60 @@ fn format(input: &str, start: usize) -> (Vec<FormatCommand>, Option<ParseError>)
|
|||
loop {
|
||||
let mut before = String::new();
|
||||
|
||||
let mut found_start = false;
|
||||
while let Some(c) = loop_input.next() {
|
||||
loop {
|
||||
end += 1;
|
||||
if c == '{' {
|
||||
if let Some(x) = loop_input.peek() {
|
||||
if *x == '{' {
|
||||
found_start = true;
|
||||
end += 1;
|
||||
let _ = loop_input.next();
|
||||
break;
|
||||
}
|
||||
if let Some(c) = loop_input.next() {
|
||||
if c == '{' {
|
||||
break;
|
||||
}
|
||||
before.push(c);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
before.push(c);
|
||||
}
|
||||
|
||||
if !before.is_empty() {
|
||||
if found_start {
|
||||
output.push(FormatCommand::Text(
|
||||
before.to_string().spanned(Span::new(start, end - 2)),
|
||||
));
|
||||
} else {
|
||||
output.push(FormatCommand::Text(before.spanned(Span::new(start, end))));
|
||||
break;
|
||||
}
|
||||
output.push(FormatCommand::Text(
|
||||
before.to_string().spanned(Span::new(start, end - 1)),
|
||||
));
|
||||
}
|
||||
// Look for column as we're now at one
|
||||
let mut column = String::new();
|
||||
start = end;
|
||||
|
||||
let mut previous_c = ' ';
|
||||
let mut found_end = false;
|
||||
let mut open_count = 1;
|
||||
while let Some(c) = loop_input.next() {
|
||||
end += 1;
|
||||
if c == '}' && previous_c == '}' {
|
||||
let _ = column.pop();
|
||||
found_end = true;
|
||||
break;
|
||||
if c == '{' {
|
||||
open_count += 1;
|
||||
} else if c == '}' {
|
||||
open_count -= 1;
|
||||
if open_count == 0 {
|
||||
found_end = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
previous_c = c;
|
||||
column.push(c);
|
||||
}
|
||||
|
||||
if !column.is_empty() {
|
||||
if found_end {
|
||||
output.push(FormatCommand::Column(
|
||||
column.to_string().spanned(Span::new(start, end - 2)),
|
||||
));
|
||||
} else {
|
||||
output.push(FormatCommand::Column(
|
||||
column.to_string().spanned(Span::new(start, end)),
|
||||
));
|
||||
|
||||
if error.is_none() {
|
||||
error = Some(ParseError::argument_error(
|
||||
input.spanned(Span::new(original_start, end)),
|
||||
ArgumentError::MissingValueForName("unclosed {{ }}".to_string()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found_start && !found_end {
|
||||
error = Some(ParseError::argument_error(
|
||||
input.spanned(Span::new(original_start, end)),
|
||||
ArgumentError::MissingValueForName("unclosed {{ }}".to_string()),
|
||||
output.push(FormatCommand::Column(
|
||||
column.to_string().spanned(Span::new(start, end)),
|
||||
));
|
||||
}
|
||||
|
||||
if before.is_empty() && column.is_empty() {
|
||||
if column.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
if !found_end {
|
||||
error = Some(ParseError::argument_error(
|
||||
input.spanned(Span::new(original_start, end)),
|
||||
ArgumentError::MissingValueForName("unclosed { }".to_string()),
|
||||
));
|
||||
}
|
||||
|
||||
start = end;
|
||||
}
|
||||
|
||||
|
@ -578,10 +562,16 @@ fn parse_interpolated_string(
|
|||
scope: &dyn ParserScope,
|
||||
) -> (SpannedExpression, Option<ParseError>) {
|
||||
trace!("Parse_interpolated_string");
|
||||
let inner_string = trim_quotes(&lite_arg.item);
|
||||
let string_len = lite_arg.item.len();
|
||||
let inner_string = lite_arg
|
||||
.item
|
||||
.chars()
|
||||
.skip(2)
|
||||
.take(string_len - 3)
|
||||
.collect::<String>();
|
||||
let mut error = None;
|
||||
|
||||
let (format_result, err) = format(&inner_string, lite_arg.span.start() + 1);
|
||||
let (format_result, err) = format(&inner_string, lite_arg.span.start() + 2);
|
||||
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
|
@ -598,11 +588,18 @@ fn parse_interpolated_string(
|
|||
});
|
||||
}
|
||||
FormatCommand::Column(c) => {
|
||||
let (o, err) = parse_full_column_path(&c, scope);
|
||||
if error.is_none() {
|
||||
error = err;
|
||||
let result = parse(&c, c.span.start(), scope);
|
||||
match result {
|
||||
(classified_block, None) => {
|
||||
output.push(SpannedExpression {
|
||||
expr: Expression::Invocation(classified_block),
|
||||
span: c.span,
|
||||
});
|
||||
}
|
||||
(_, Some(err)) => {
|
||||
return (garbage(c.span), Some(err));
|
||||
}
|
||||
}
|
||||
output.push(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -649,12 +646,6 @@ fn parse_external_arg(
|
|||
parse_dollar_expr(&lite_arg, scope)
|
||||
} else if lite_arg.item.starts_with('(') {
|
||||
parse_invocation(&lite_arg, scope)
|
||||
} else if lite_arg.item.starts_with('`')
|
||||
&& lite_arg.item.len() > 1
|
||||
&& lite_arg.item.ends_with('`')
|
||||
{
|
||||
// This is an interpolated string
|
||||
parse_interpolated_string(&lite_arg, scope)
|
||||
} else {
|
||||
(
|
||||
SpannedExpression::new(Expression::string(lite_arg.item.clone()), lite_arg.span),
|
||||
|
@ -811,19 +802,11 @@ fn parse_arg(
|
|||
}
|
||||
}
|
||||
SyntaxShape::String => {
|
||||
if lite_arg.item.starts_with('`')
|
||||
&& lite_arg.item.len() > 1
|
||||
&& lite_arg.item.ends_with('`')
|
||||
{
|
||||
// This is an interpolated string
|
||||
parse_interpolated_string(&lite_arg, scope)
|
||||
} else {
|
||||
let trimmed = trim_quotes(&lite_arg.item);
|
||||
(
|
||||
SpannedExpression::new(Expression::string(trimmed), lite_arg.span),
|
||||
None,
|
||||
)
|
||||
}
|
||||
let trimmed = trim_quotes(&lite_arg.item);
|
||||
(
|
||||
SpannedExpression::new(Expression::string(trimmed), lite_arg.span),
|
||||
None,
|
||||
)
|
||||
}
|
||||
SyntaxShape::GlobPattern => {
|
||||
let trimmed = trim_quotes(&lite_arg.item);
|
||||
|
|
|
@ -19,7 +19,6 @@ pub(crate) fn trim_quotes(input: &str) -> String {
|
|||
match (chars.next(), chars.next_back()) {
|
||||
(Some('\''), Some('\'')) => chars.collect(),
|
||||
(Some('"'), Some('"')) => chars.collect(),
|
||||
(Some('`'), Some('`')) => chars.collect(),
|
||||
_ => input.to_string(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,55 +118,19 @@ fn string_interpolation_with_it() {
|
|||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo "foo" | each { echo `{{$it}}` }
|
||||
echo "foo" | each { echo $"{$it}" }
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_interpolation_with_column() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo [[name]; [bob]] | each { echo `{{name}} is cool` }
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "bob is cool");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_interpolation_with_column2() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo [[name]; [fred]] | each { echo `also {{name}} is cool` }
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "also fred is cool");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_interpolation_with_column3() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo [[name]; [sally]] | each { echo `also {{name}}` }
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "also sally");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_interpolation_with_it_column_path() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
echo [[name]; [sammie]] | each { echo `{{$it.name}}` }
|
||||
echo [[name]; [sammie]] | each { echo $"{$it.name}" }
|
||||
"#
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in a new issue