mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 14:15:53 +02:00
Add or-patterns, fix var binding scope (#8633)
# Description Adds `|` patterns to `match`, allowing you to try multiple patterns for the same case. Example: ``` match {b: 1} { {a: $b} | {b: $b} => { print $b } } ``` Variables that don't bind are set to `$nothing` so that they can be later checked. This PR also: fixes #8631 Creates a set of integration tests for pattern matching also # User-Facing Changes Adds `|` to `match`. Fixes variable binding scope. # 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:
@ -559,6 +559,11 @@ pub fn flatten_pattern(match_pattern: &MatchPattern) -> Vec<(Span, FlatShape)> {
|
||||
Pattern::Variable(_) => {
|
||||
output.push((match_pattern.span, FlatShape::Variable));
|
||||
}
|
||||
Pattern::Or(patterns) => {
|
||||
for pattern in patterns {
|
||||
output.extend(flatten_pattern(pattern));
|
||||
}
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ pub fn parse_variable_pattern(
|
||||
let bytes = working_set.get_span_contents(span);
|
||||
|
||||
if is_variable(bytes) {
|
||||
if let Some(var_id) = working_set.find_variable(bytes) {
|
||||
if let Some(var_id) = working_set.find_variable_in_current_frame(bytes) {
|
||||
(
|
||||
MatchPattern {
|
||||
pattern: Pattern::Variable(var_id),
|
||||
|
@ -4526,7 +4526,7 @@ pub fn parse_match_block_expression(
|
||||
|
||||
let source = working_set.get_span_contents(inner_span);
|
||||
|
||||
let (output, err) = lex(source, start, &[b' ', b'\r', b'\n', b','], &[], false);
|
||||
let (output, err) = lex(source, start, &[b' ', b'\r', b'\n', b',', b'|'], &[], false);
|
||||
error = error.or(err);
|
||||
|
||||
let mut position = 0;
|
||||
@ -4539,7 +4539,7 @@ pub fn parse_match_block_expression(
|
||||
working_set.enter_scope();
|
||||
|
||||
// First parse the pattern
|
||||
let (pattern, err) = parse_pattern(working_set, output[position].span);
|
||||
let (mut pattern, err) = parse_pattern(working_set, output[position].span);
|
||||
error = error.or(err);
|
||||
|
||||
position += 1;
|
||||
@ -4555,19 +4555,75 @@ pub fn parse_match_block_expression(
|
||||
break;
|
||||
}
|
||||
|
||||
// Then the =>
|
||||
let thick_arrow = working_set.get_span_contents(output[position].span);
|
||||
if thick_arrow != b"=>" {
|
||||
// Multiple patterns connected by '|'
|
||||
let mut connector = working_set.get_span_contents(output[position].span);
|
||||
if connector == b"|" && position < output.len() {
|
||||
let mut or_pattern = vec![pattern];
|
||||
|
||||
while connector == b"|" && position < output.len() {
|
||||
connector = b"";
|
||||
|
||||
position += 1;
|
||||
|
||||
if position >= output.len() {
|
||||
error = error.or(Some(ParseError::Mismatch(
|
||||
"pattern".into(),
|
||||
"end of input".into(),
|
||||
Span::new(output[position - 1].span.end, output[position - 1].span.end),
|
||||
)));
|
||||
|
||||
working_set.exit_scope();
|
||||
break;
|
||||
}
|
||||
|
||||
let (pattern, err) = parse_pattern(working_set, output[position].span);
|
||||
error = error.or(err);
|
||||
or_pattern.push(pattern);
|
||||
|
||||
position += 1;
|
||||
if position >= output.len() {
|
||||
error = error.or(Some(ParseError::Mismatch(
|
||||
"=>".into(),
|
||||
"end of input".into(),
|
||||
Span::new(output[position - 1].span.end, output[position - 1].span.end),
|
||||
)));
|
||||
|
||||
working_set.exit_scope();
|
||||
break;
|
||||
} else {
|
||||
connector = working_set.get_span_contents(output[position].span);
|
||||
}
|
||||
}
|
||||
|
||||
let start = or_pattern
|
||||
.first()
|
||||
.expect("internal error: unexpected state of or-pattern")
|
||||
.span
|
||||
.start;
|
||||
let end = or_pattern
|
||||
.last()
|
||||
.expect("internal error: unexpected state of or-pattern")
|
||||
.span
|
||||
.end;
|
||||
|
||||
pattern = MatchPattern {
|
||||
pattern: Pattern::Or(or_pattern),
|
||||
span: Span::new(start, end),
|
||||
}
|
||||
}
|
||||
|
||||
// Then the `=>` arrow
|
||||
if connector != b"=>" {
|
||||
error = error.or(Some(ParseError::Mismatch(
|
||||
"=>".into(),
|
||||
"end of input".into(),
|
||||
Span::new(output[position - 1].span.end, output[position - 1].span.end),
|
||||
)));
|
||||
} else {
|
||||
position += 1;
|
||||
}
|
||||
|
||||
// Finally, the value/expression/block that we will run to produce the result
|
||||
position += 1;
|
||||
|
||||
if position >= output.len() {
|
||||
error = error.or(Some(ParseError::Mismatch(
|
||||
"match result".into(),
|
||||
@ -6094,6 +6150,11 @@ pub fn discover_captures_in_pattern(pattern: &MatchPattern, seen: &mut Vec<VarId
|
||||
discover_captures_in_pattern(&item.1, seen)
|
||||
}
|
||||
}
|
||||
Pattern::Or(patterns) => {
|
||||
for pattern in patterns {
|
||||
discover_captures_in_pattern(pattern, seen)
|
||||
}
|
||||
}
|
||||
Pattern::Value(_) | Pattern::IgnoreValue | Pattern::Garbage => {}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user