Implement a basic edn matcher, r=ncalexan (#271) (#338)

Signed-off-by: Victor Porof <victor.porof@gmail.com>
This commit is contained in:
Victor Porof 2017-02-23 09:11:34 +01:00 committed by GitHub
parent bf707acbc3
commit 7fc2a22d68
2 changed files with 582 additions and 0 deletions

View file

@ -17,6 +17,7 @@ pub mod symbols;
pub mod types; pub mod types;
pub mod pretty_print; pub mod pretty_print;
pub mod utils; pub mod utils;
pub mod matcher;
pub mod parse { pub mod parse {
include!(concat!(env!("OUT_DIR"), "/edn.rs")); include!(concat!(env!("OUT_DIR"), "/edn.rs"));

581
edn/src/matcher.rs Normal file
View file

@ -0,0 +1,581 @@
// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
use std::collections::HashMap;
use std::cell::RefCell;
use itertools::diff_with;
use symbols;
use types::Value;
/// A trait defining pattern matching rules for any given pattern of type `T`.
trait PatternMatchingRules<'a, T> {
/// Return true if the given pattern matches an arbitrary value.
fn matches_any(pattern: &T) -> bool;
/// Return the placeholder name if the given pattern matches a placeholder.
fn matches_placeholder(pattern: &'a T) -> Option<(&'a String)>;
}
/// A default type implementing `PatternMatchingRules` specialized on
/// EDN values using plain symbols as patterns. These patterns are:
/// * `_` matches arbitrary sub-EDN;
/// * `?name` matches sub-EDN, which must be identical each place `?name` appears;
struct DefaultPatternMatchingRules;
impl<'a> PatternMatchingRules<'a, Value> for DefaultPatternMatchingRules {
fn matches_any(pattern: &Value) -> bool {
match *pattern {
Value::PlainSymbol(symbols::PlainSymbol(ref s)) => s.starts_with('_'),
_ => false
}
}
fn matches_placeholder(pattern: &'a Value) -> Option<(&'a String)> {
match *pattern {
Value::PlainSymbol(symbols::PlainSymbol(ref s)) => if s.starts_with('?') { Some(s) } else { None },
_ => None
}
}
}
/// Pattern matcher for EDN values utilizing specified pattern matching rules.
/// For example, using this with `DefaultPatternMatchingRules`:
/// * `[_]` matches an arbitrary one-element vector;
/// * `[_ _]` matches an arbitrary two-element vector;
/// * `[?x ?x]` matches `[1 1]` and `[#{} #{}]` but not `[1 2]` or `[[] #{}]`;
struct Matcher<'a> {
placeholders: RefCell<HashMap<&'a String, &'a Value>>
}
impl<'a> Matcher<'a> {
/// Creates a Matcher instance.
fn new() -> Matcher<'a> {
Matcher {
placeholders: RefCell::default()
}
}
/// Performs pattern matching between two EDN `Value` instances (`value`
/// and `pattern`) utilizing a specified pattern matching ruleset `T`.
/// Returns true if matching succeeds.
pub fn match_with_rules<T>(value: &'a Value, pattern: &'a Value) -> bool
where T: PatternMatchingRules<'a, Value> {
let matcher = Matcher::new();
matcher.match_internal::<T>(value, pattern)
}
/// Recursively traverses two EDN `Value` instances (`value` and `pattern`)
/// performing pattern matching. Note that the internal `placeholders` cache
/// might not be empty on invocation.
fn match_internal<T>(&self, value: &'a Value, pattern: &'a Value) -> bool
where T: PatternMatchingRules<'a, Value> {
use Value::*;
if T::matches_any(pattern) {
true
} else if let Some(symbol) = T::matches_placeholder(pattern) {
let mut placeholders = self.placeholders.borrow_mut();
value == *placeholders.entry(symbol).or_insert(value)
} else {
match (value, pattern) {
(&Vector(ref v), &Vector(ref p)) =>
diff_with(v, p, |a, b| self.match_internal::<T>(a, b)).is_none(),
(&List(ref v), &List(ref p)) =>
diff_with(v, p, |a, b| self.match_internal::<T>(a, b)).is_none(),
(&Set(ref v), &Set(ref p)) =>
v.len() == p.len() &&
v.iter().all(|a| p.iter().any(|b| self.match_internal::<T>(a, b))) &&
p.iter().all(|b| v.iter().any(|a| self.match_internal::<T>(a, b))),
(&Map(ref v), &Map(ref p)) =>
v.len() == p.len() &&
v.iter().all(|a| p.iter().any(|b| self.match_internal::<T>(a.0, b.0) && self.match_internal::<T>(a.1, b.1))) &&
p.iter().all(|b| v.iter().any(|a| self.match_internal::<T>(a.0, b.0) && self.match_internal::<T>(a.1, b.1))),
_ => value == pattern
}
}
}
}
impl Value {
/// Performs default pattern matching between this value and some `pattern`.
/// Returns true if matching succeeds.
pub fn matches(&self, pattern: &Value) -> bool {
Matcher::match_with_rules::<DefaultPatternMatchingRules>(self, pattern)
}
}
#[cfg(test)]
mod test {
use parse;
macro_rules! assert_match {
( $pattern:tt, $value:tt, $expected:expr ) => {
let pattern = parse::value($pattern).unwrap().without_spans();
let value = parse::value($value).unwrap().without_spans();
assert_eq!(value.matches(&pattern), $expected);
};
( $pattern:tt =~ $value:tt ) => {
assert_match!($pattern, $value, true);
};
( $pattern:tt !~ $value:tt ) => {
assert_match!($pattern, $value, false);
}
}
#[test]
fn test_match_primitives() {
assert_match!("nil" =~ "nil");
assert_match!("true" =~ "true");
assert_match!("true" !~ "false");
assert_match!("1" =~ "1");
assert_match!("1" !~ "2");
assert_match!("1N" =~ "1N");
assert_match!("1N" !~ "2N");
assert_match!("1.0" =~ "1.0");
assert_match!("1.0" !~ "2.0");
assert_match!("\"a\"" =~ "\"a\"");
assert_match!("\"a\"" !~ "\"b\"");
assert_match!("foo" =~ "foo");
assert_match!("foo" !~ "bar");
assert_match!("foo/bar" !~ "foo");
}
#[test]
fn test_match_collections_sorted() {
assert_match!("[nil, true, 1, \"foo\", bar, :baz]" =~ "[nil, true, 1, \"foo\", bar, :baz]");
assert_match!("(nil, true, 1, \"foo\", bar, :baz)" =~ "(nil, true, 1, \"foo\", bar, :baz)");
assert_match!("#{nil, true, 1, \"foo\", bar, :baz}" =~ "#{nil, true, 1, \"foo\", bar, :baz}");
assert_match!("{nil true, 1 \"foo\", bar :baz}" =~ "{nil true, 1 \"foo\", bar :baz}");
}
#[test]
fn test_match_collections_unsorted() {
assert_match!("[nil, true, 1, \"foo\", bar, :baz]" !~ "[1, \"foo\", nil, true, bar, :baz]");
assert_match!("(nil, true, 1, \"foo\", bar, :baz)" !~ "(1, \"foo\", nil, true, bar, :baz)");
assert_match!("#{nil, true, 1, \"foo\", bar, :baz}" =~ "#{1, \"foo\", nil, true, bar, :baz}");
assert_match!("{nil true, 1 \"foo\", bar :baz}" =~ "{1 \"foo\", nil true, bar :baz}");
}
#[test]
fn test_match_maps_switched_key_values() {
assert_match!("{1 2, 3 4}" =~ "{1 2, 3 4}");
assert_match!("{2 1, 3 4}" !~ "{1 2, 3 4}");
assert_match!("{2 1, 4 3}" !~ "{1 2, 3 4}");
assert_match!("{1 2, 4 3}" !~ "{1 2, 3 4}");
}
#[test]
fn test_match_maps_ordered_collection_keys_and_values() {
assert_match!("{[1, 2] (3, 4)}" =~ "{[1, 2] (3, 4)}");
assert_match!("{[2, 1] (3, 4)}" !~ "{[1, 2] (3, 4)}");
assert_match!("{[2, 1] (4, 3)}" !~ "{[1, 2] (3, 4)}");
assert_match!("{[1, 2] (4, 3)}" !~ "{[1, 2] (3, 4)}");
assert_match!("{(3, 4) [1, 2]}" !~ "{[1, 2] (3, 4)}");
assert_match!("{(3, 4) [2, 1]}" !~ "{[1, 2] (3, 4)}");
assert_match!("{(4, 3) [2, 1]}" !~ "{[1, 2] (3, 4)}");
assert_match!("{(4, 3) [1, 2]}" !~ "{[1, 2] (3, 4)}");
}
#[test]
fn test_match_maps_unordered_collection_keys_and_values() {
assert_match!("{#{1, 2} #{3, 4}}" =~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{2, 1} #{3, 4}}" =~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{2, 1} #{4, 3}}" =~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{1, 2} #{4, 3}}" =~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{3, 4} #{1, 2}}" !~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{3, 4} #{2, 1}}" !~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{4, 3} #{2, 1}}" !~ "{#{1, 2} #{3, 4}}");
assert_match!("{#{4, 3} #{1, 2}}" !~ "{#{1, 2} #{3, 4}}");
}
#[test]
fn test_match_any_simple() {
assert_match!("_" =~ "nil");
assert_match!("_" =~ "true");
assert_match!("_" =~ "1");
assert_match!("_" =~ "1N");
assert_match!("_" =~ "1.0");
assert_match!("_" =~ "\"a\"");
assert_match!("_" =~ "_");
assert_match!("_" =~ "symbol");
assert_match!("_" =~ "ns/symbol");
assert_match!("_" =~ ":keyword");
assert_match!("_" =~ ":ns/keyword");
assert_match!("_" =~ "[nil, true, 1, \"foo\", bar, :baz]");
assert_match!("_" =~ "(nil, true, 1, \"foo\", bar, :baz)");
assert_match!("_" =~ "#{nil, true, 1, \"foo\", bar, :baz}");
assert_match!("_" =~ "{nil true, 1 \"foo\", bar :baz}");
}
#[test]
fn test_match_any_in_same_collection_type_simple() {
assert_match!("[_]" =~ "[1]");
assert_match!("(_)" =~ "(2)");
assert_match!("#{_}" =~ "#{3}");
assert_match!("{_ _}" =~ "{4 5}");
}
#[test]
fn test_match_any_in_different_collection_type_simple() {
assert_match!("[_]" !~ "(1)");
assert_match!("(_)" !~ "#{2}");
assert_match!("#{_}" !~ "[3]");
assert_match!("{_ _}" !~ "[4 5]");
assert_match!("{_ _}" !~ "(6 7)");
assert_match!("{_ _}" !~ "#{8 9}");
}
#[test]
fn test_match_any_in_vector_with_multiple_values() {
assert_match!("[_ 2]" =~ "[1 2]");
assert_match!("[1 _]" =~ "[1 2]");
assert_match!("[1 _ 3 4]" =~ "[1 2 3 4]");
assert_match!("[1 [2 [3 _]] 5 [_ 7]]" =~ "[1 [2 [3 4]] 5 [6 7]]");
assert_match!("[_]" =~ "[[foo bar]]");
assert_match!("[_]" =~ "[(foo bar)]");
assert_match!("[_]" =~ "[#{foo bar}]");
assert_match!("[_]" =~ "[{foo bar}]");
assert_match!("[_ 2]" !~ "[2 1]");
assert_match!("[1 _]" !~ "[2 1]");
assert_match!("[1 _ 3]" !~ "[2 1 3]");
assert_match!("[_ 2]" !~ "[3 4]");
assert_match!("[1 _]" !~ "[3 4]");
}
#[test]
fn test_match_multiple_any_in_vector_with_multiple_values() {
assert_match!("[1 _ _]" =~ "[1 2 3]");
assert_match!("[2 _ _]" !~ "[1 2 3]");
assert_match!("[3 _ _]" !~ "[1 2 3]");
assert_match!("[_ 1 _]" !~ "[1 2 3]");
assert_match!("[_ 2 _]" =~ "[1 2 3]");
assert_match!("[_ 3 _]" !~ "[1 2 3]");
assert_match!("[_ _ 1]" !~ "[1 2 3]");
assert_match!("[_ _ 2]" !~ "[1 2 3]");
assert_match!("[_ _ 3]" =~ "[1 2 3]");
assert_match!("[1 _ _]" !~ "[2 1 3]");
assert_match!("[2 _ _]" =~ "[2 1 3]");
assert_match!("[3 _ _]" !~ "[2 1 3]");
assert_match!("[_ 1 _]" =~ "[2 1 3]");
assert_match!("[_ 2 _]" !~ "[2 1 3]");
assert_match!("[_ 3 _]" !~ "[2 1 3]");
assert_match!("[_ _ 1]" !~ "[2 1 3]");
assert_match!("[_ _ 2]" !~ "[2 1 3]");
assert_match!("[_ _ 3]" =~ "[2 1 3]");
}
#[test]
fn test_match_any_in_list_with_multiple_values() {
assert_match!("(_ 2)" =~ "(1 2)");
assert_match!("(1 _)" =~ "(1 2)");
assert_match!("(1 _ 3 4)" =~ "(1 2 3 4)");
assert_match!("(1 (2 (3 _)) 5 (_ 7))" =~ "(1 (2 (3 4)) 5 (6 7))");
assert_match!("(_)" =~ "([foo bar])");
assert_match!("(_)" =~ "((foo bar))");
assert_match!("(_)" =~ "(#{foo bar})");
assert_match!("(_)" =~ "({foo bar})");
assert_match!("(_ 2)" !~ "(2 1)");
assert_match!("(1 _)" !~ "(2 1)");
assert_match!("(1 _ 3)" !~ "(2 1 3)");
assert_match!("(_ 2)" !~ "(3 4)");
assert_match!("(1 _)" !~ "(3 4)");
}
#[test]
fn test_match_multiple_any_in_list_with_multiple_values() {
assert_match!("(1 _ _)" =~ "(1 2 3)");
assert_match!("(2 _ _)" !~ "(1 2 3)");
assert_match!("(3 _ _)" !~ "(1 2 3)");
assert_match!("(_ 1 _)" !~ "(1 2 3)");
assert_match!("(_ 2 _)" =~ "(1 2 3)");
assert_match!("(_ 3 _)" !~ "(1 2 3)");
assert_match!("(_ _ 1)" !~ "(1 2 3)");
assert_match!("(_ _ 2)" !~ "(1 2 3)");
assert_match!("(_ _ 3)" =~ "(1 2 3)");
assert_match!("(1 _ _)" !~ "(2 1 3)");
assert_match!("(2 _ _)" =~ "(2 1 3)");
assert_match!("(3 _ _)" !~ "(2 1 3)");
assert_match!("(_ 1 _)" =~ "(2 1 3)");
assert_match!("(_ 2 _)" !~ "(2 1 3)");
assert_match!("(_ 3 _)" !~ "(2 1 3)");
assert_match!("(_ _ 1)" !~ "(2 1 3)");
assert_match!("(_ _ 2)" !~ "(2 1 3)");
assert_match!("(_ _ 3)" =~ "(2 1 3)");
}
#[test]
fn test_match_any_in_set_with_multiple_values() {
assert_match!("#{_ 2}" =~ "#{1 2}");
assert_match!("#{1 _}" =~ "#{1 2}");
assert_match!("#{1 _ 3 4}" =~ "#{1 2 3 4}");
assert_match!("#{1 #{2 #{3 _}} 5 #{_ 7}}" =~ "#{1 #{2 #{3 4}} 5 #{6 7}}");
assert_match!("#{_}" =~ "#{[foo bar]}");
assert_match!("#{_}" =~ "#{(foo bar)}");
assert_match!("#{_}" =~ "#{#{foo bar}}");
assert_match!("#{_}" =~ "#{{foo bar}}");
assert_match!("#{_ 2}" =~ "#{2 1}");
assert_match!("#{1 _}" =~ "#{2 1}");
assert_match!("#{1 _ 3}" =~ "#{2 1 3}");
assert_match!("#{_ 2}" !~ "#{3 4}");
assert_match!("#{1 _}" !~ "#{3 4}");
}
#[test]
fn test_match_multiple_any_in_set_with_multiple_values() {
// These are false because _ is a symbol and sets guarantee
// uniqueness of children. So pattern matching will fail because
// the pattern is a set of length 2, while the matched edn is a set
// of length 3. If _ were unique, all of these assertions would
// be true. Need to better handle pattern rules.
assert_match!("#{1 _ _}" !~ "#{1 2 3}");
assert_match!("#{2 _ _}" !~ "#{1 2 3}");
assert_match!("#{3 _ _}" !~ "#{1 2 3}");
assert_match!("#{_ 1 _}" !~ "#{1 2 3}");
assert_match!("#{_ 2 _}" !~ "#{1 2 3}");
assert_match!("#{_ 3 _}" !~ "#{1 2 3}");
assert_match!("#{_ _ 1}" !~ "#{1 2 3}");
assert_match!("#{_ _ 2}" !~ "#{1 2 3}");
assert_match!("#{_ _ 3}" !~ "#{1 2 3}");
assert_match!("#{1 _ _}" !~ "#{2 1 3}");
assert_match!("#{2 _ _}" !~ "#{2 1 3}");
assert_match!("#{3 _ _}" !~ "#{2 1 3}");
assert_match!("#{_ 1 _}" !~ "#{2 1 3}");
assert_match!("#{_ 2 _}" !~ "#{2 1 3}");
assert_match!("#{_ 3 _}" !~ "#{2 1 3}");
assert_match!("#{_ _ 1}" !~ "#{2 1 3}");
assert_match!("#{_ _ 2}" !~ "#{2 1 3}");
assert_match!("#{_ _ 3}" !~ "#{2 1 3}");
}
#[test]
fn test_match_any_in_map_with_multiple_values() {
assert_match!("{_ 2}" =~ "{1 2}");
assert_match!("{1 _}" =~ "{1 2}");
assert_match!("{1 _, 3 4}" =~ "{1 2, 3 4}");
assert_match!("{1 {2 {3 _}}, 5 {_ 7}}" =~ "{1 {2 {3 4}}, 5 {6 7}}");
assert_match!("{_ _}" =~ "{[foo bar] [baz boz]}");
assert_match!("{_ _}" =~ "{(foo bar) (baz boz)}");
assert_match!("{_ _}" =~ "{#{foo bar} #{baz boz}}");
assert_match!("{_ _}" =~ "{{foo bar} {baz boz}}");
assert_match!("{_ 2, 3 4}" =~ "{3 4, 1 2}");
assert_match!("{1 _, 3 4}" =~ "{3 4, 1 2}");
assert_match!("{_ _, 3 4}" =~ "{3 4, 1 2}");
assert_match!("{1 2, _ 4}" =~ "{3 4, 1 2}");
assert_match!("{1 2, 3 _}" =~ "{3 4, 1 2}");
assert_match!("{1 2, _ _}" =~ "{3 4, 1 2}");
assert_match!("{1 2, _ 4, 5 6}" =~ "{3 4, 1 2, 5 6}");
assert_match!("{1 2, 3 _, 5 6}" =~ "{3 4, 1 2, 5 6}");
assert_match!("{1 2, _ _, 5 6}" =~ "{3 4, 1 2, 5 6}");
assert_match!("{_ 2}" !~ "{3 4}");
assert_match!("{1 _}" !~ "{3 4}");
}
#[test]
fn test_match_multiple_any_in_map_with_multiple_values() {
// These are false because _ is a symbol and maps guarantee
// uniqueness of keys. So pattern matching will fail because
// the pattern is a map of length 2, while the matched edn is a map
// of length 3. If _ were unique, all of these assertions would
// be true. Need to better handle pattern rules.
assert_match!("{1 2, _ 4, _ 6}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{3 4, _ 6, _ 2}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{5 6, _ 2, _ 4}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{1 2, _ _, _ _}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{3 4, _ _, _ _}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{5 6, _ _, _ _}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{_ _, 1 2, _ _}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{_ _, 3 4, _ _}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{_ _, 5 6, _ _}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{_ _, _ _, 1 2}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{_ _, _ _, 3 4}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{_ _, _ _, 5 6}" !~ "{1 2, 3 4, 5 6}");
assert_match!("{1 2, _ _, _ _}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{3 4, _ _, _ _}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{5 6, _ _, _ _}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{_ _, 1 2, _ _}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{_ _, 3 4, _ _}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{_ _, 5 6, _ _}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{_ _, _ _, 1 2}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{_ _, _ _, 3 4}" !~ "{3 4, 1 2, 5 6}");
assert_match!("{_ _, _ _, 5 6}" !~ "{3 4, 1 2, 5 6}");
}
#[test]
fn test_match_placeholder_simple() {
assert_match!("?x" =~ "nil");
assert_match!("?x" =~ "true");
assert_match!("?x" =~ "1");
assert_match!("?x" =~ "1N");
assert_match!("?x" =~ "1.0");
assert_match!("?x" =~ "\"a\"");
assert_match!("?x" =~ "_");
assert_match!("?x" =~ "symbol");
assert_match!("?x" =~ "ns/symbol");
assert_match!("?x" =~ ":keyword");
assert_match!("?x" =~ ":ns/keyword");
assert_match!("?x" =~ "[nil, true, 1, \"foo\", bar, :baz]");
assert_match!("?x" =~ "(nil, true, 1, \"foo\", bar, :baz)");
assert_match!("?x" =~ "#{nil, true, 1, \"foo\", bar, :baz}");
assert_match!("?x" =~ "{nil true, 1 \"foo\", bar :baz}");
}
#[test]
fn test_match_placeholder_in_same_collection_type_simple() {
assert_match!("[?x]" =~ "[1]");
assert_match!("(?x)" =~ "(2)");
assert_match!("#{?x}" =~ "#{3}");
assert_match!("{?x ?x}" =~ "{4 4}");
assert_match!("{?x ?x}" !~ "{4 5}");
assert_match!("{?x ?y}" =~ "{4 4}");
assert_match!("{?x ?y}" =~ "{4 5}");
}
#[test]
fn test_match_placeholder_in_different_collection_type_simple() {
assert_match!("[?x]" !~ "(1)");
assert_match!("(?x)" !~ "#{2}");
assert_match!("#{?x}" !~ "[3]");
assert_match!("{?x ?x}" !~ "[4 5]");
assert_match!("{?x ?x}" !~ "(6 7)");
assert_match!("{?x ?x}" !~ "#{8 9}");
}
#[test]
fn test_match_placeholder_in_vector_with_multiple_values() {
assert_match!("[?x ?y]" =~ "[1 2]");
assert_match!("[?x ?y]" =~ "[1 1]");
assert_match!("[?x ?x]" !~ "[1 2]");
assert_match!("[?x ?x]" =~ "[1 1]");
assert_match!("[1 ?x 3 ?y]" =~ "[1 2 3 4]");
assert_match!("[1 ?x 3 ?y]" =~ "[1 2 3 2]");
assert_match!("[1 ?x 3 ?x]" !~ "[1 2 3 4]");
assert_match!("[1 ?x 3 ?x]" =~ "[1 2 3 2]");
assert_match!("[1 [2 [3 ?x]] 5 [?y 7]]" =~ "[1 [2 [3 4]] 5 [6 7]]");
assert_match!("[1 [2 [3 ?x]] 5 [?y 7]]" =~ "[1 [2 [3 4]] 5 [4 7]]");
assert_match!("[1 [2 [3 ?x]] 5 [?x 7]]" !~ "[1 [2 [3 4]] 5 [6 7]]");
assert_match!("[1 [2 [3 ?x]] 5 [?y 7]]" =~ "[1 [2 [3 4]] 5 [4 7]]");
assert_match!("[?x ?y ?x ?y]" =~ "[1 2 1 2]");
assert_match!("[?x ?y ?x ?y]" !~ "[1 2 2 1]");
assert_match!("[[?x ?y] [?x ?y]]" =~ "[[1 2] [1 2]]");
assert_match!("[[?x ?y] [?x ?y]]" !~ "[[1 2] [2 1]]");
}
#[test]
fn test_match_placeholder_in_list_with_multiple_values() {
assert_match!("(?x ?y)" =~ "(1 2)");
assert_match!("(?x ?y)" =~ "(1 1)");
assert_match!("(?x ?x)" !~ "(1 2)");
assert_match!("(?x ?x)" =~ "(1 1)");
assert_match!("(1 ?x 3 ?y)" =~ "(1 2 3 4)");
assert_match!("(1 ?x 3 ?y)" =~ "(1 2 3 2)");
assert_match!("(1 ?x 3 ?x)" !~ "(1 2 3 4)");
assert_match!("(1 ?x 3 ?x)" =~ "(1 2 3 2)");
assert_match!("(1 (2 (3 ?x)) 5 (?y 7))" =~ "(1 (2 (3 4)) 5 (6 7))");
assert_match!("(1 (2 (3 ?x)) 5 (?y 7))" =~ "(1 (2 (3 4)) 5 (4 7))");
assert_match!("(1 (2 (3 ?x)) 5 (?x 7))" !~ "(1 (2 (3 4)) 5 (6 7))");
assert_match!("(1 (2 (3 ?x)) 5 (?y 7))" =~ "(1 (2 (3 4)) 5 (4 7))");
assert_match!("(?x ?y ?x ?y)" =~ "(1 2 1 2)");
assert_match!("(?x ?y ?x ?y)" !~ "(1 2 2 1)");
assert_match!("((?x ?y) (?x ?y))" =~ "((1 2) (1 2))");
assert_match!("((?x ?y) (?x ?y))" !~ "((1 2) (2 1))");
}
#[test]
fn test_match_placeholder_in_set_with_multiple_values() {
assert_match!("#{?x ?y}" =~ "#{1 2}");
assert_match!("#{?x ?y}" !~ "#{1 1}");
assert_match!("#{?x ?x}" !~ "#{1 2}");
assert_match!("#{?x ?x}" =~ "#{1 1}");
assert_match!("#{1 ?x 3 ?y}" =~ "#{1 2 3 4}");
assert_match!("#{1 ?x 3 ?y}" !~ "#{1 2 3 2}");
assert_match!("#{1 ?x 3 ?x}" !~ "#{1 2 3 4}");
assert_match!("#{1 ?x 3 ?x}" =~ "#{1 2 3 2}");
assert_match!("#{1 #{2 #{3 ?x}} 5 #{?y 7}}" =~ "#{1 #{2 #{3 4}} 5 #{6 7}}");
assert_match!("#{1 #{2 #{3 ?x}} 5 #{?y 7}}" =~ "#{1 #{2 #{3 4}} 5 #{4 7}}");
assert_match!("#{1 #{2 #{3 ?x}} 5 #{?x 7}}" !~ "#{1 #{2 #{3 4}} 5 #{6 7}}");
assert_match!("#{1 #{2 #{3 ?x}} 5 #{?y 7}}" =~ "#{1 #{2 #{3 4}} 5 #{4 7}}");
assert_match!("#{?x ?y ?x ?y}" =~ "#{1 2 1 2}");
assert_match!("#{?x ?y ?x ?y}" =~ "#{1 2 2 1}");
assert_match!("#{#{?x ?y} #{?x ?y}}" =~ "#{#{1 2} #{1 2}}");
assert_match!("#{#{?x ?y} #{?x ?y}}" =~ "#{#{1 2} #{2 1}}");
}
#[test]
fn test_match_placeholder_in_map_with_multiple_values() {
assert_match!("{?x ?y}" =~ "{1 2}");
assert_match!("{?x ?y}" =~ "{1 1}");
assert_match!("{?x ?x}" !~ "{1 2}");
assert_match!("{?x ?x}" =~ "{1 1}");
assert_match!("{1 ?x, 3 ?y}" =~ "{1 2, 3 4}");
assert_match!("{1 ?x, 3 ?y}" =~ "{1 2, 3 2}");
assert_match!("{1 ?x, 3 ?x}" !~ "{1 2, 3 4}");
assert_match!("{1 ?x, 3 ?x}" =~ "{1 2, 3 2}");
assert_match!("{1 {2 {3 ?x}}, 5 {?y 7}}" =~ "{1 {2 {3 4}}, 5 {6 7}}");
assert_match!("{1 {2 {3 ?x}}, 5 {?y 7}}" =~ "{1 {2 {3 4}}, 5 {4 7}}");
assert_match!("{1 {2 {3 ?x}}, 5 {?x 7}}" !~ "{1 {2 {3 4}}, 5 {6 7}}");
assert_match!("{1 {2 {3 ?x}}, 5 {?y 7}}" =~ "{1 {2 {3 4}}, 5 {4 7}}");
assert_match!("{?x ?y, ?x ?y}" =~ "{1 2, 1 2}");
assert_match!("{?x ?y, ?x ?y}" !~ "{1 2, 2 1}");
assert_match!("{{?x ?y}, {?x ?y}}" =~ "{{1 2}, {1 2}}");
assert_match!("{{?x ?y}, {?x ?y}}" !~ "{{1 2}, {2 1}}");
}
#[test]
fn test_match_placeholder_in_different_value_types() {
assert_match!("{1 {2 [3 ?x]}, 5 (?y 7)}" =~ "{1 {2 [3 4]}, 5 (6 7)}");
assert_match!("{1 {2 [3 ?x]}, 5 (?y 7)}" =~ "{1 {2 [3 4]}, 5 (4 7)}");
assert_match!("{1 {2 [3 ?x]}, 5 (?x 7)}" !~ "{1 {2 [3 4]}, 5 (6 7)}");
assert_match!("{1 {2 [3 ?x]}, 5 (?y 7)}" =~ "{1 {2 [3 4]}, 5 (4 7)}");
assert_match!("{?x {?x [?x ?x]}, ?x (?x ?x)}" !~ "{1 {2 [3 4]}, 5 (6 7)}");
assert_match!("{?x {?x [?x ?x]}, ?x (?x ?x)}" =~ "{1 {1 [1 1]}, 1 (1 1)}");
assert_match!("[#{?x ?y} ?x]" =~ "[#{1 2} 1]");
assert_match!("[#{?x ?y} ?y]" =~ "[#{1 2} 2]");
}
}