Simplify string interpolation (#3401)

* [DRAFT] simplify string interpolation

* Fix test
This commit is contained in:
JT 2021-05-12 13:53:57 +12:00 committed by GitHub
parent 25a8caa9b0
commit 311c0e3f50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 62 additions and 117 deletions

1
Cargo.lock generated
View File

@ -3710,7 +3710,6 @@ dependencies = [
name = "nu-table" name = "nu-table"
version = "0.31.0" version = "0.31.0"
dependencies = [ dependencies = [
"nu-ansi-term 0.31.0", "nu-ansi-term 0.31.0",
"regex 1.5.3", "regex 1.5.3",
"unicode-width", "unicode-width",

View File

@ -52,7 +52,7 @@ impl WholeStreamCommand for Each {
Example { Example {
description: "Number each item and echo a message", description: "Number each item and echo a message",
example: 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")]), result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
}, },
] ]

View File

@ -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}]" echo "[{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}]"
| from json | from json
| get total | get total
| reduce -f 20 { $it + ( math eval `{{$acc}}^1.05` )} | reduce -f 20 { $it + (math eval $"{$acc}^1.05")}
| str from -d 1 | str from -d 1
"# "#
) )
@ -21,7 +21,7 @@ fn reduce_table_column() {
r#" r#"
echo "[{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}]" echo "[{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}]"
| from json | 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 | str from -d 1
"# "#
) )

View File

@ -467,7 +467,10 @@ fn parse_dollar_expr(
scope: &dyn ParserScope, scope: &dyn ParserScope,
) -> (SpannedExpression, Option<ParseError>) { ) -> (SpannedExpression, Option<ParseError>) {
trace!("Parsing dollar expression: {:?}", lite_arg.item); 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) (expr, None)
} else if let (expr, None) = parse_full_column_path(lite_arg, scope) { } else if let (expr, None) = parse_full_column_path(lite_arg, scope) {
(expr, None) (expr, None)
@ -493,79 +496,60 @@ fn format(input: &str, start: usize) -> (Vec<FormatCommand>, Option<ParseError>)
loop { loop {
let mut before = String::new(); let mut before = String::new();
let mut found_start = false; loop {
while let Some(c) = loop_input.next() {
end += 1; end += 1;
if c == '{' { if let Some(c) = loop_input.next() {
if let Some(x) = loop_input.peek() { if c == '{' {
if *x == '{' { break;
found_start = true;
end += 1;
let _ = loop_input.next();
break;
}
} }
before.push(c);
} else {
break;
} }
before.push(c);
} }
if !before.is_empty() { if !before.is_empty() {
if found_start { output.push(FormatCommand::Text(
output.push(FormatCommand::Text( before.to_string().spanned(Span::new(start, end - 1)),
before.to_string().spanned(Span::new(start, end - 2)), ));
));
} else {
output.push(FormatCommand::Text(before.spanned(Span::new(start, end))));
break;
}
} }
// Look for column as we're now at one // Look for column as we're now at one
let mut column = String::new(); let mut column = String::new();
start = end; start = end;
let mut previous_c = ' ';
let mut found_end = false; let mut found_end = false;
let mut open_count = 1;
while let Some(c) = loop_input.next() { while let Some(c) = loop_input.next() {
end += 1; end += 1;
if c == '}' && previous_c == '}' { if c == '{' {
let _ = column.pop(); open_count += 1;
found_end = true; } else if c == '}' {
break; open_count -= 1;
if open_count == 0 {
found_end = true;
break;
}
} }
previous_c = c;
column.push(c); column.push(c);
} }
if !column.is_empty() { if !column.is_empty() {
if found_end { output.push(FormatCommand::Column(
output.push(FormatCommand::Column( column.to_string().spanned(Span::new(start, end)),
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()),
)); ));
} }
if before.is_empty() && column.is_empty() { if column.is_empty() {
break; break;
} }
if !found_end {
error = Some(ParseError::argument_error(
input.spanned(Span::new(original_start, end)),
ArgumentError::MissingValueForName("unclosed { }".to_string()),
));
}
start = end; start = end;
} }
@ -578,10 +562,16 @@ fn parse_interpolated_string(
scope: &dyn ParserScope, scope: &dyn ParserScope,
) -> (SpannedExpression, Option<ParseError>) { ) -> (SpannedExpression, Option<ParseError>) {
trace!("Parse_interpolated_string"); 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 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() { if error.is_none() {
error = err; error = err;
@ -598,11 +588,18 @@ fn parse_interpolated_string(
}); });
} }
FormatCommand::Column(c) => { FormatCommand::Column(c) => {
let (o, err) = parse_full_column_path(&c, scope); let result = parse(&c, c.span.start(), scope);
if error.is_none() { match result {
error = err; (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) parse_dollar_expr(&lite_arg, scope)
} else if lite_arg.item.starts_with('(') { } else if lite_arg.item.starts_with('(') {
parse_invocation(&lite_arg, scope) 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 { } else {
( (
SpannedExpression::new(Expression::string(lite_arg.item.clone()), lite_arg.span), SpannedExpression::new(Expression::string(lite_arg.item.clone()), lite_arg.span),
@ -811,19 +802,11 @@ fn parse_arg(
} }
} }
SyntaxShape::String => { SyntaxShape::String => {
if lite_arg.item.starts_with('`') let trimmed = trim_quotes(&lite_arg.item);
&& lite_arg.item.len() > 1 (
&& lite_arg.item.ends_with('`') SpannedExpression::new(Expression::string(trimmed), lite_arg.span),
{ None,
// 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,
)
}
} }
SyntaxShape::GlobPattern => { SyntaxShape::GlobPattern => {
let trimmed = trim_quotes(&lite_arg.item); let trimmed = trim_quotes(&lite_arg.item);

View File

@ -19,7 +19,6 @@ pub(crate) fn trim_quotes(input: &str) -> String {
match (chars.next(), chars.next_back()) { match (chars.next(), chars.next_back()) {
(Some('\''), Some('\'')) => chars.collect(), (Some('\''), Some('\'')) => chars.collect(),
(Some('"'), Some('"')) => chars.collect(), (Some('"'), Some('"')) => chars.collect(),
(Some('`'), Some('`')) => chars.collect(),
_ => input.to_string(), _ => input.to_string(),
} }
} }

View File

@ -118,55 +118,19 @@ fn string_interpolation_with_it() {
let actual = nu!( let actual = nu!(
cwd: ".", cwd: ".",
r#" r#"
echo "foo" | each { echo `{{$it}}` } echo "foo" | each { echo $"{$it}" }
"# "#
); );
assert_eq!(actual.out, "foo"); 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] #[test]
fn string_interpolation_with_it_column_path() { fn string_interpolation_with_it_column_path() {
let actual = nu!( let actual = nu!(
cwd: ".", cwd: ".",
r#" r#"
echo [[name]; [sammie]] | each { echo `{{$it.name}}` } echo [[name]; [sammie]] | each { echo $"{$it.name}" }
"# "#
); );