forked from extern/nushell
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:
@ -10,12 +10,48 @@ pub struct MatchPattern {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl MatchPattern {
|
||||
pub fn variables(&self) -> Vec<VarId> {
|
||||
self.pattern.variables()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Pattern {
|
||||
Record(Vec<(String, MatchPattern)>),
|
||||
List(Vec<MatchPattern>),
|
||||
Value(Expression),
|
||||
Variable(VarId),
|
||||
Or(Vec<MatchPattern>),
|
||||
IgnoreValue, // the _ pattern
|
||||
Garbage,
|
||||
}
|
||||
|
||||
impl Pattern {
|
||||
pub fn variables(&self) -> Vec<VarId> {
|
||||
let mut output = vec![];
|
||||
match self {
|
||||
Pattern::Record(items) => {
|
||||
for item in items {
|
||||
output.append(&mut item.1.variables());
|
||||
}
|
||||
}
|
||||
Pattern::List(items) => {
|
||||
for item in items {
|
||||
output.append(&mut item.variables());
|
||||
}
|
||||
}
|
||||
Pattern::Value(_) => {}
|
||||
Pattern::Variable(var_id) => output.push(*var_id),
|
||||
Pattern::Or(patterns) => {
|
||||
for pattern in patterns {
|
||||
output.append(&mut pattern.variables());
|
||||
}
|
||||
}
|
||||
Pattern::IgnoreValue => {}
|
||||
Pattern::Garbage => {}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
|
@ -1803,6 +1803,24 @@ impl<'a> StateWorkingSet<'a> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn find_variable_in_current_frame(&self, name: &[u8]) -> Option<VarId> {
|
||||
let mut removed_overlays = vec![];
|
||||
|
||||
for scope_frame in self.delta.scope.iter().rev().take(1) {
|
||||
for overlay_frame in scope_frame
|
||||
.active_overlays(&mut removed_overlays)
|
||||
.iter()
|
||||
.rev()
|
||||
{
|
||||
if let Some(var_id) = overlay_frame.vars.get(name) {
|
||||
return Some(*var_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn add_variable(
|
||||
&mut self,
|
||||
mut name: Vec<u8>,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user