parser: Add cell path literal syntax (#8493)

# Description

This adds a new cell path literal syntax for use in any value position,
not just in a context where we expect a cell path.

This can be used to assign to a variable and then later use that
variable as a cell path.

Example:
```
> let cell_path = $.a.b
> {a: {b: 3}} | get $cell_path
3
```
# User-Facing Changes

This adds the syntax `$.a.b` to universally mean the cell path `a.b`,
even in a context that doesn't expect a cell path.

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
JT 2023-03-17 15:19:41 +13:00 committed by GitHub
parent 0ac3f7a1c8
commit 1552eb921a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 23 deletions

View File

@ -1688,6 +1688,12 @@ pub(crate) fn parse_dollar_expr(
if contents.starts_with(b"$\"") || contents.starts_with(b"$'") {
parse_string_interpolation(working_set, span, expand_aliases_denylist)
} else if contents.starts_with(b"$.") {
parse_simple_cell_path(
working_set,
Span::new(span.start + 2, span.end),
expand_aliases_denylist,
)
} else if let (expr, None) = parse_range(working_set, span, expand_aliases_denylist) {
(expr, None)
} else {
@ -2129,6 +2135,33 @@ pub fn parse_cell_path(
(tail, error)
}
pub fn parse_simple_cell_path(
working_set: &mut StateWorkingSet,
span: Span,
expand_aliases_denylist: &[usize],
) -> (Expression, Option<ParseError>) {
let source = working_set.get_span_contents(span);
let mut error = None;
let (tokens, err) = lex(source, span.start, &[b'\n', b'\r'], &[b'.', b'?'], true);
error = error.or(err);
let tokens = tokens.into_iter().peekable();
let (cell_path, err) = parse_cell_path(working_set, tokens, false, expand_aliases_denylist);
error = error.or(err);
(
Expression {
expr: Expr::CellPath(CellPath { members: cell_path }),
span,
ty: Type::CellPath,
custom_completion: None,
},
error,
)
}
pub fn parse_full_cell_path(
working_set: &mut StateWorkingSet,
implicit_head: Option<VarId>,
@ -4646,29 +4679,7 @@ pub fn parse_value(
)
}
}
SyntaxShape::CellPath => {
let source = working_set.get_span_contents(span);
let mut error = None;
let (tokens, err) = lex(source, span.start, &[b'\n', b'\r'], &[b'.', b'?'], true);
error = error.or(err);
let tokens = tokens.into_iter().peekable();
let (cell_path, err) =
parse_cell_path(working_set, tokens, false, expand_aliases_denylist);
error = error.or(err);
(
Expression {
expr: Expr::CellPath(CellPath { members: cell_path }),
span,
ty: Type::CellPath,
custom_completion: None,
},
error,
)
}
SyntaxShape::CellPath => parse_simple_cell_path(working_set, span, expand_aliases_denylist),
SyntaxShape::Boolean => {
// Redundant, though we catch bad boolean parses here
if bytes == b"true" || bytes == b"false" {

View File

@ -133,3 +133,8 @@ fn list_row_optional_access_succeeds() -> TestResult {
fn do_not_delve_too_deep_in_nested_lists() -> TestResult {
fail_test("[[{foo: bar}]].foo", "cannot find column")
}
#[test]
fn cell_path_literals() -> TestResult {
run_test("let cell_path = $.a.b; {a: {b: 3}} | get $cell_path", "3")
}