From 345e9d23f8b8e6e7d5fb6eb3071b46c7f6fba420 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 27 Mar 2019 01:35:01 +0100 Subject: Integrate the special patterns from match_children!() into iter_patterns Considerably simplifies parser macros --- dhall_core/src/lib.rs | 1 + dhall_core/src/parser.rs | 168 +++++++++++------------------------------------ iter_patterns/src/lib.rs | 95 +++++++++++++++++++++++++-- 3 files changed, 129 insertions(+), 135 deletions(-) diff --git a/dhall_core/src/lib.rs b/dhall_core/src/lib.rs index 0874b09..2042b04 100644 --- a/dhall_core/src/lib.rs +++ b/dhall_core/src/lib.rs @@ -1,5 +1,6 @@ #![feature(trace_macros)] #![feature(slice_patterns)] +#![feature(bind_by_move_pattern_guards)] #![allow( clippy::many_single_char_names, clippy::should_implement_trait, diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs index 560d177..b7ac3f6 100644 --- a/dhall_core/src/parser.rs +++ b/dhall_core/src/parser.rs @@ -112,106 +112,6 @@ fn debug_pair(pair: Pair) -> String { s } -macro_rules! match_children { - (@make_child_match, - ($pair:expr, $($vars:tt)*), - ($($outer_acc:tt)*), - ($($acc:tt)*), - ($(,)* $ty:ident ($x:ident..) $($rest_of_match:tt)*) => $body:expr, - $($rest:tt)* - ) => { - match_children!(@make_child_match, - ($pair, $($vars)*), - ($($outer_acc)*), - ($($acc)*, xs..), - ($($rest_of_match)*) => { - let xs = xs.map(|x| match x { - ParsedValue::$ty(y) => Ok(y), - x => Err(format!("Unexpected child: {:?}", x)), - }).collect::, _>>()?; - let $x = xs.into_iter(); - $body - }, - $($rest)* - ) - }; - (@make_child_match, - ($($vars:tt)*), - ($($outer_acc:tt)*), - ($($acc:tt)*), - ($(,)* $ty:ident ($x:pat) $($rest_of_match:tt)*) => $body:expr, - $($rest:tt)* - ) => { - match_children!(@make_child_match, - ($($vars)*), - ($($outer_acc)*), - ($($acc)*, ParsedValue::$ty($x)), - ($($rest_of_match)*) => $body, - $($rest)* - ) - }; - (@make_child_match, - ($($vars:tt)*), - ($($outer_acc:tt)*), - (, $($acc:tt)*), - ($(,)*) => $body:expr, - $($rest:tt)* - ) => { - match_children!(@make_matches, - ($($vars)*), - ($($outer_acc)* [$($acc)*] => { $body },), - $($rest)* - ) - }; - (@make_child_match, - ($($vars:tt)*), - ($($outer_acc:tt)*), - (), - ($(,)*) => $body:expr, - $($rest:tt)* - ) => { - match_children!(@make_matches, - ($($vars)*), - ($($outer_acc)* [] => { $body },), - $($rest)* - ) - }; - - (@make_matches, - ($($vars:tt)*), - ($($acc:tt)*), - [$($args:tt)*] => $body:expr, - $($rest:tt)* - ) => { - match_children!(@make_child_match, - ($($vars)*), - ($($acc)*), - (), - ($($args)*) => $body, - $($rest)* - ) - }; - (@make_matches, ($pair:expr, $children:expr), ($($acc:tt)*) $(,)*) => { - { - #[allow(unreachable_code)] - iter_patterns::match_vec!($children; - $($acc)* - [x..] => Err( - format!("Unexpected children: {:?}", x.collect::>()) - )?, - ).ok_or_else(|| -> String { unreachable!() }) - } - }; - - (($($vars:tt)*); $( [$($args:tt)*] => $body:expr ),* $(,)*) => { - match_children!(@make_matches, - ($($vars)*), - (), - $( [$($args)*] => $body ),* , - ) - }; -} - macro_rules! make_parser { (@pattern, rule, $name:ident) => (Rule::$name); (@pattern, rule_group, $name:ident) => (_); @@ -248,10 +148,18 @@ macro_rules! make_parser { rule!( $name:ident<$o:ty> as $group:ident; - children!( $($args:tt)* ) + children!( $( [$($args:tt)*] => $body:expr ),* $(,)* ) ) ) => ({ - let res: $o = match_children!(($pair, $children); $($args)*)?; + #[allow(unused_imports)] + use ParsedValue::*; + #[allow(unreachable_code)] + let res: $o = iter_patterns::match_vec!($children; + $( [$($args)*] => $body, )* + [x..] => Err( + format!("Unexpected children: {:?}", x.collect::>()) + )?, + ).ok_or_else(|| -> String { unreachable!() })?; Ok(ParsedValue::$group(res)) }); (@body, $pair:expr, $children:expr, rule_group!( $name:ident<$o:ty> )) => ( @@ -369,7 +277,7 @@ make_parser! { )); rule!(double_quote_literal; children!( - [double_quote_chunk(chunks..)] => { + [double_quote_chunk(chunks)..] => { chunks.collect() } )); @@ -521,7 +429,7 @@ make_parser! { [quoted_path_component(s)] => s, )); rule!(path; children!( - [path_component(components..)] => { + [path_component(components)..] => { components.collect() } )); @@ -570,8 +478,8 @@ make_parser! { rule!(http; children!( [http_raw(url)] => url, - [http_raw(url), import_hashed(import_hashed)] => - URL { headers: Some(Box::new(import_hashed)), ..url }, + [http_raw(url), import_hashed(ih)] => + URL { headers: Some(Box::new(ih)), ..url }, )); rule!(env; children!( @@ -593,8 +501,8 @@ make_parser! { [http(url)] => { ImportLocation::Remote(url) }, - [local((prefix, path))] => { - ImportLocation::Local(prefix, path) + [local((prefix, p))] => { + ImportLocation::Local(prefix, p) }, )); @@ -608,8 +516,8 @@ make_parser! { rule!(import_hashed; children!( [import_type(location)] => ImportHashed { location, hash: None }, - [import_type(location), hash(hash)] => - ImportHashed { location, hash: Some(hash) }, + [import_type(location), hash(h)] => + ImportHashed { location, hash: Some(h) }, )); rule_group!(expression); @@ -644,7 +552,7 @@ make_parser! { )); rule!(let_expression as expression; children!( - [let_binding(bindings..), expression(final_expr)] => { + [let_binding(bindings).., expression(final_expr)] => { bindings.fold( final_expr, |acc, x| bx(Expr::Let(x.0, x.1, x.2, acc)) @@ -698,84 +606,84 @@ make_parser! { rule!(import_alt_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::ImportAlt; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(or_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::BoolOr; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(plus_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::NaturalPlus; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(text_append_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::TextAppend; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(list_append_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::ListAppend; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(and_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::BoolAnd; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(combine_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::Combine; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(prefer_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::Prefer; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(combine_types_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::CombineTypes; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(times_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::NaturalTimes; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(equal_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::BoolEQ; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, )); rule!(not_equal_expression as expression; children!( [expression(e)] => e, - [expression(first), expression(rest..)] => { + [expression(first), expression(rest)..] => { let o = BinOp::BoolNE; rest.fold(first, |acc, e| bx(Expr::BinOp(o, acc, e))) }, @@ -799,7 +707,7 @@ make_parser! { _ => bx(Expr::App(first, vec![second])), } }, - [expression(first), expression(second), expression(rest..)] => { + [expression(first), expression(second), expression(rest)..] => { match first.as_ref() { Expr::Builtin(Builtin::OptionalNone) => bx(Expr::App(bx(Expr::EmptyOptionalLit(second)), @@ -817,7 +725,7 @@ make_parser! { rule!(selector_expression as expression; children!( [expression(e)] => e, - [expression(first), selector(rest..)] => { + [expression(first), selector(rest)..] => { rest.fold(first, |acc, e| match e { Either::Left(l) => bx(Expr::Field(acc, l)), Either::Right(ls) => bx(Expr::Projection(acc, ls)), @@ -831,7 +739,7 @@ make_parser! { )); rule!(labels>; children!( - [label(ls..)] => ls.collect(), + [label(ls)..] => ls.collect(), )); rule!(literal_expression as expression; children!( @@ -900,7 +808,7 @@ make_parser! { rule!(non_empty_record_type <(ParsedExpr, BTreeMap)>; children!( - [expression(expr), record_type_entry(entries..)] => { + [expression(expr), record_type_entry(entries)..] => { (expr, entries.collect()) } )); @@ -911,7 +819,7 @@ make_parser! { rule!(non_empty_record_literal <(ParsedExpr, BTreeMap)>; children!( - [expression(expr), record_literal_entry(entries..)] => { + [expression(expr), record_literal_entry(entries)..] => { (expr, entries.collect()) } )); @@ -953,7 +861,7 @@ make_parser! { )); rule!(union_type_entries>; children!( - [union_type_entry(entries..)] => entries.collect() + [union_type_entry(entries)..] => entries.collect() )); rule!(union_type_entry<(Label, ParsedExpr)>; children!( @@ -961,7 +869,7 @@ make_parser! { )); rule!(non_empty_list_literal as expression; children!( - [expression(items..)] => bx(Expr::NEListLit(items.collect())) + [expression(items)..] => bx(Expr::NEListLit(items.collect())) )); rule!(final_expression as expression; children!( diff --git a/iter_patterns/src/lib.rs b/iter_patterns/src/lib.rs index 10679aa..a4be783 100644 --- a/iter_patterns/src/lib.rs +++ b/iter_patterns/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(slice_patterns)] +#![feature(slice_patterns, bind_by_move_pattern_guards)] /* Destructure an iterator using the syntax of slice_patterns. * Wraps the match body in `Some` if there was a match; returns @@ -35,6 +35,21 @@ macro_rules! destructure_iter { $($rest)* ) }; + // Special variable length pattern with a common unary variant + (@match_forwards, $iter:expr, ($body:expr), $variant:ident ($x:ident).., $($rest:tt)*) => { + $crate::destructure_iter!(@match_backwards, + $iter, + ({ + let $x = $iter + .map(|x| match x { + $variant(y) => y, + _ => unreachable!(), + }); + $body + }), + $($rest)* + ) + }; // Variable length pattern without a binder (@match_forwards, $iter:expr, ($body:expr), .., $($rest:tt)*) => { $crate::destructure_iter!(@match_backwards, @@ -117,6 +132,71 @@ macro_rules! destructure_iter { */ #[macro_export] macro_rules! match_vec { + // Variable length pattern + (@make_pat; ($($acc:tt)*), $x:ident.., $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, $x..), + $($rest)* + ) + }; + // Special variable length pattern with a common unary variant + (@make_pat; ($($acc:tt)*), $variant:ident ($x:ident).., $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, $x..), + $($rest)* + ) + }; + // Variable length pattern without a binder + (@make_pat; ($($acc:tt)*), .., $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, ..), + $($rest)* + ) + }; + // Single item pattern + (@make_pat; ($($acc:tt)*), $x:pat, $($rest:tt)*) => { + $crate::match_vec!(@make_pat; + ($($acc)*, $x), + $($rest)* + ) + }; + (@make_pat; (, $($acc:tt)*), $(,)*) => { + [$($acc)*] + }; + (@make_pat; ($($acc:tt)*), $(,)*) => { + [$($acc)*] + }; + + (@make_filter; $x:ident.., $($rest:tt)*) => { + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; $variant:ident ($x:ident).., $($rest:tt)*) => { + $x.iter() + .all(|x| match x { + $variant(_) => true, + _ => false, + }) + && + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; .., $($rest:tt)*) => { + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; $x:pat, $($rest:tt)*) => { + $crate::match_vec!(@make_filter; + $($rest)* + ) + }; + (@make_filter; $(,)*) => { + true + }; + ($arg:expr; $( [$($args:tt)*] => $body:expr ),* $(,)*) => { { let vec = $arg; @@ -126,7 +206,10 @@ macro_rules! match_vec { #[allow(unused_variables, unreachable_patterns)] match vec.as_slice() { $( - [$($args)*] => { + $crate::match_vec!(@make_pat; (), $($args)*,) + if + $crate::match_vec!(@make_filter; $($args)*,) + => { // Actually consume the values #[allow(unused_mut)] let mut iter = vec.into_iter(); @@ -179,7 +262,8 @@ fn test() { [Some(_x), None] => 2, [None, Some(y)] => 1, [None, _y..] => 3, - [_x.., Some(y), Some(z)] => y - z, + [_x.., Some(y), Some(z), None] => y - z, + [Some(ys)..] => ys.sum(), [] => 0, [..] => -1, ) @@ -189,10 +273,11 @@ fn test() { assert_eq!(test(vec![Some(0), None, None]), 4); assert_eq!(test(vec![Some(0), None]), 2); assert_eq!(test(vec![None, Some(0)]), 1); - assert_eq!(test(vec![Some(1), Some(2), Some(5), Some(14)]), -9); + assert_eq!(test(vec![Some(1), Some(2), Some(5), Some(14), None]), -9); + assert_eq!(test(vec![Some(1), Some(2), Some(3), Some(4)]), 10); assert_eq!(test(vec![None]), 3); assert_eq!(test(vec![]), 0); - assert_eq!(test(vec![Some(0)]), -1); + assert_eq!(test(vec![Some(0), None, Some(1)]), -1); // Test move out of pattern struct Foo; -- cgit v1.2.3