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:
JT
2023-03-27 11:31:57 +13:00
committed by GitHub
parent 332f1192a6
commit 7ec5f2f2eb
8 changed files with 355 additions and 9 deletions

View File

@ -1,6 +1,6 @@
use crate::{
ast::{Expr, MatchPattern, Pattern, RangeInclusion},
Unit, Value, VarId,
Span, Unit, Value, VarId,
};
pub trait Matcher {
@ -217,6 +217,54 @@ impl Matcher for Pattern {
_ => false,
}
}
Pattern::Or(patterns) => {
let mut result = false;
for pattern in patterns {
let mut local_matches = vec![];
if !result {
if pattern.match_value(value, &mut local_matches) {
// TODO: do we need to replace previous variables that defaulted to nothing?
matches.append(&mut local_matches);
result = true;
} else {
// Create variables that don't match and assign them to null
let vars = pattern.variables();
for var in &vars {
let mut found = false;
for match_ in matches.iter() {
if match_.0 == *var {
found = true;
}
}
if !found {
// FIXME: don't use Span::unknown()
matches.push((*var, Value::nothing(Span::unknown())))
}
}
}
} else {
// We already have a match, so ignore the remaining match variables
// And assign them to null
let vars = pattern.variables();
for var in &vars {
let mut found = false;
for match_ in matches.iter() {
if match_.0 == *var {
found = true;
}
}
if !found {
// FIXME: don't use Span::unknown()
matches.push((*var, Value::nothing(Span::unknown())))
}
}
}
}
result
}
}
}
}