Move 'where' to parser keywords; Add 'filter' command (#7365)

# Description

This PR moves the `where` command to a parser keyword. While it still
uses the shape-directed parsing dictated by the signature, we're free to
change the parsing code now to a custom one once we remove the syntax
shapes.

As a side effect, the `where -b` flag was removed and its functionality
has moved to the new `filter` command.

Just FYI, other commands that take row conditions:
- `take until`
- `take while`
- `skip until`
- `skip while`
- `any`
- `all`

We can either move these to the parser as well or make them accept a
closure instead of row condition.

# User-Facing Changes

New `filter` command which replaces `where -b` functionality.

# 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

# 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:
Jakub Žádník
2022-12-10 19:23:24 +02:00
committed by GitHub
parent 7e2781a2af
commit 6b4282eadf
8 changed files with 499 additions and 245 deletions

View File

@ -1,3 +1,4 @@
use log::trace;
use nu_path::canonicalize_with;
use nu_protocol::{
ast::{
@ -3130,6 +3131,97 @@ pub fn parse_source(
)
}
pub fn parse_where_expr(
working_set: &mut StateWorkingSet,
spans: &[Span],
expand_aliases_denylist: &[usize],
) -> (Expression, Option<ParseError>) {
trace!("parsing: where");
if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"where" {
return (
garbage(span(spans)),
Some(ParseError::UnknownState(
"internal error: Wrong call name for 'where' command".into(),
span(spans),
)),
);
}
if spans.len() < 2 {
return (
garbage(span(spans)),
Some(ParseError::MissingPositional(
"row condition".into(),
span(spans),
"where <row_condition>".into(),
)),
);
}
let call = match working_set.find_decl(b"where", &Type::Any) {
Some(decl_id) => {
let ParsedInternalCall {
call,
error: mut err,
output,
} = parse_internal_call(
working_set,
spans[0],
&spans[1..],
decl_id,
expand_aliases_denylist,
);
let decl = working_set.get_decl(decl_id);
let call_span = span(spans);
err = check_call(call_span, &decl.signature(), &call).or(err);
if err.is_some() || call.has_flag("help") {
return (
Expression {
expr: Expr::Call(call),
span: call_span,
ty: output,
custom_completion: None,
},
err,
);
}
call
}
None => {
return (
garbage(span(spans)),
Some(ParseError::UnknownState(
"internal error: 'where' declaration not found".into(),
span(spans),
)),
)
}
};
(
Expression {
expr: Expr::Call(call),
span: span(spans),
ty: Type::Any,
custom_completion: None,
},
None,
)
}
pub fn parse_where(
working_set: &mut StateWorkingSet,
spans: &[Span],
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) {
let (expression, err) = parse_where_expr(working_set, spans, expand_aliases_denylist);
(Pipeline::from_vec(vec![expression]), err)
}
#[cfg(feature = "plugin")]
pub fn parse_register(
working_set: &mut StateWorkingSet,

View File

@ -17,7 +17,8 @@ use nu_protocol::{
use crate::parse_keywords::{
parse_alias, parse_def, parse_def_predecl, parse_export_in_block, parse_extern, parse_for,
parse_hide, parse_let, parse_module, parse_overlay, parse_source, parse_use,
parse_hide, parse_let, parse_module, parse_overlay, parse_source, parse_use, parse_where,
parse_where_expr,
};
use itertools::Itertools;
@ -1892,6 +1893,7 @@ pub fn parse_full_cell_path(
span: Span,
expand_aliases_denylist: &[usize],
) -> (Expression, Option<ParseError>) {
trace!("parsing: full cell path");
let full_cell_span = span;
let source = working_set.get_span_contents(span);
let mut error = None;
@ -1996,10 +1998,11 @@ pub fn parse_full_cell_path(
(out, true)
} else if let Some(var_id) = implicit_head {
trace!("parsing: implicit head of full cell path");
(
Expression {
expr: Expr::Var(var_id),
span: Span::new(0, 0),
span: head.span,
ty: Type::Any,
custom_completion: None,
},
@ -3035,6 +3038,7 @@ pub fn expand_to_cell_path(
var_id: VarId,
expand_aliases_denylist: &[usize],
) {
trace!("parsing: expanding to cell path");
if let Expression {
expr: Expr::String(_),
span,
@ -4589,6 +4593,8 @@ pub fn parse_math_expression(
lhs_row_var_id: Option<VarId>,
expand_aliases_denylist: &[usize],
) -> (Expression, Option<ParseError>) {
trace!("parsing: math expression");
// As the expr_stack grows, we increase the required precedence to grow larger
// If, at any time, the operator we're looking at is the same or lower precedence
// of what is in the expression stack, we collapse the expression stack.
@ -4765,6 +4771,8 @@ pub fn parse_expression(
expand_aliases_denylist: &[usize],
is_subexpression: bool,
) -> (Expression, Option<ParseError>) {
trace!("parsing: expression");
let mut pos = 0;
let mut shorthand = vec![];
@ -5014,6 +5022,7 @@ pub fn parse_expression(
spans[0],
)),
),
b"where" => parse_where_expr(working_set, &spans[pos..], expand_aliases_denylist),
#[cfg(feature = "plugin")]
b"register" => (
parse_call(
@ -5149,6 +5158,7 @@ pub fn parse_builtin_commands(
}
b"export" => parse_export_in_block(working_set, lite_command, expand_aliases_denylist),
b"hide" => parse_hide(working_set, &lite_command.parts, expand_aliases_denylist),
b"where" => parse_where(working_set, &lite_command.parts, expand_aliases_denylist),
#[cfg(feature = "plugin")]
b"register" => parse_register(working_set, &lite_command.parts, expand_aliases_denylist),
_ => {
@ -5294,6 +5304,7 @@ pub fn parse_block(
.iter()
.map(|command| match command {
LiteElement::Command(span, command) => {
trace!("parsing: pipeline element: command");
let (expr, err) = parse_expression(
working_set,
&command.parts,
@ -5309,6 +5320,7 @@ pub fn parse_block(
PipelineElement::Expression(*span, expr)
}
LiteElement::Redirection(span, redirection, command) => {
trace!("parsing: pipeline element: redirection");
let (expr, err) = parse_string(
working_set,
command.parts[0],
@ -6185,8 +6197,6 @@ pub fn parse(
scoped: bool,
expand_aliases_denylist: &[usize],
) -> (Block, Option<ParseError>) {
trace!("starting top-level parse");
let mut error = None;
let span_offset = working_set.next_span_start();