forked from extern/nushell
Add rest and ignore-rest patterns (#8681)
# Description Adds two more patterns when working with lists: ``` [1, ..$remainder] ``` and ``` [1, ..] ``` The first one collects the remaining items and assigns them into the variable. The second one ignores any remaining values. # User-Facing Changes Adds more capability to list pattern matching. # 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 - `cargo run -- crates/nu-utils/standard_library/tests.nu` to run the tests for the standard library > **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:
@ -515,6 +515,9 @@ pub fn flatten_pattern(match_pattern: &MatchPattern) -> Vec<(Span, FlatShape)> {
|
||||
Pattern::IgnoreValue => {
|
||||
output.push((match_pattern.span, FlatShape::Nothing));
|
||||
}
|
||||
Pattern::IgnoreRest => {
|
||||
output.push((match_pattern.span, FlatShape::Nothing));
|
||||
}
|
||||
Pattern::List(items) => {
|
||||
if let Some(first) = items.first() {
|
||||
if let Some(last) = items.last() {
|
||||
@ -559,6 +562,9 @@ pub fn flatten_pattern(match_pattern: &MatchPattern) -> Vec<(Span, FlatShape)> {
|
||||
Pattern::Variable(_) => {
|
||||
output.push((match_pattern.span, FlatShape::Variable));
|
||||
}
|
||||
Pattern::Rest(_) => {
|
||||
output.push((match_pattern.span, FlatShape::Variable));
|
||||
}
|
||||
Pattern::Or(patterns) => {
|
||||
for pattern in patterns {
|
||||
output.extend(flatten_pattern(pattern));
|
||||
|
@ -1,7 +1,7 @@
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression, MatchPattern, Pattern},
|
||||
engine::StateWorkingSet,
|
||||
Span, SyntaxShape, Type,
|
||||
Span, SyntaxShape, Type, VarId,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@ -72,32 +72,34 @@ pub fn parse_pattern(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_variable_pattern(
|
||||
working_set: &mut StateWorkingSet,
|
||||
span: Span,
|
||||
) -> (MatchPattern, Option<ParseError>) {
|
||||
fn parse_variable_pattern_helper(working_set: &mut StateWorkingSet, span: Span) -> Option<VarId> {
|
||||
let bytes = working_set.get_span_contents(span);
|
||||
|
||||
if is_variable(bytes) {
|
||||
if let Some(var_id) = working_set.find_variable_in_current_frame(bytes) {
|
||||
(
|
||||
MatchPattern {
|
||||
pattern: Pattern::Variable(var_id),
|
||||
span,
|
||||
},
|
||||
None,
|
||||
)
|
||||
Some(var_id)
|
||||
} else {
|
||||
let var_id = working_set.add_variable(bytes.to_vec(), span, Type::Any, false);
|
||||
|
||||
(
|
||||
MatchPattern {
|
||||
pattern: Pattern::Variable(var_id),
|
||||
span,
|
||||
},
|
||||
None,
|
||||
)
|
||||
Some(var_id)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_variable_pattern(
|
||||
working_set: &mut StateWorkingSet,
|
||||
span: Span,
|
||||
) -> (MatchPattern, Option<ParseError>) {
|
||||
if let Some(var_id) = parse_variable_pattern_helper(working_set, span) {
|
||||
(
|
||||
MatchPattern {
|
||||
pattern: Pattern::Variable(var_id),
|
||||
span,
|
||||
},
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
garbage(span),
|
||||
@ -143,10 +145,39 @@ pub fn parse_list_pattern(
|
||||
|
||||
if let LiteElement::Command(_, command) = arg {
|
||||
while spans_idx < command.parts.len() {
|
||||
let (arg, err) = parse_pattern(working_set, command.parts[spans_idx]);
|
||||
error = error.or(err);
|
||||
let contents = working_set.get_span_contents(command.parts[spans_idx]);
|
||||
if contents == b".." {
|
||||
args.push(MatchPattern {
|
||||
pattern: Pattern::IgnoreRest,
|
||||
span: command.parts[spans_idx],
|
||||
});
|
||||
break;
|
||||
} else if contents.starts_with(b"..$") {
|
||||
if let Some(var_id) = parse_variable_pattern_helper(
|
||||
working_set,
|
||||
Span::new(
|
||||
command.parts[spans_idx].start + 2,
|
||||
command.parts[spans_idx].end,
|
||||
),
|
||||
) {
|
||||
args.push(MatchPattern {
|
||||
pattern: Pattern::Rest(var_id),
|
||||
span: command.parts[spans_idx],
|
||||
});
|
||||
break;
|
||||
} else {
|
||||
args.push(garbage(command.parts[spans_idx]));
|
||||
error = error.or(Some(ParseError::Expected(
|
||||
"valid variable name".into(),
|
||||
command.parts[spans_idx],
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
let (arg, err) = parse_pattern(working_set, command.parts[spans_idx]);
|
||||
error = error.or(err);
|
||||
|
||||
args.push(arg);
|
||||
args.push(arg);
|
||||
};
|
||||
|
||||
spans_idx += 1;
|
||||
}
|
||||
|
@ -6237,7 +6237,8 @@ pub fn discover_captures_in_pattern(pattern: &MatchPattern, seen: &mut Vec<VarId
|
||||
discover_captures_in_pattern(pattern, seen)
|
||||
}
|
||||
}
|
||||
Pattern::Value(_) | Pattern::IgnoreValue | Pattern::Garbage => {}
|
||||
Pattern::Rest(var_id) => seen.push(*var_id),
|
||||
Pattern::Value(_) | Pattern::IgnoreValue | Pattern::IgnoreRest | Pattern::Garbage => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user