From 68c1120e7e52a462e2a5d6d867d0dfaf06b05614 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Mar 2019 19:18:46 +0100 Subject: Extract iter-matching code --- dhall/src/parser.rs | 142 +++++++++++++++++++++++++-------- dhall_parser/build.rs | 2 +- dhall_parser/src/dhall.pest.visibility | 1 - 3 files changed, 111 insertions(+), 34 deletions(-) diff --git a/dhall/src/parser.rs b/dhall/src/parser.rs index 8c6d520..053b993 100644 --- a/dhall/src/parser.rs +++ b/dhall/src/parser.rs @@ -25,15 +25,86 @@ pub fn custom_parse_error(pair: &Pair, msg: String) -> ParseError { } +/* Macro to pattern-match iterators. + * Panics if the sequence doesn't match; + * + * Example: + * ``` + * let vec = vec![1, 2, 3]; + * + * match_iter!(vec.into_iter(); (x, y?, z) => { + * // x: T + * // y: Option + * // z: T + * }) + * + * // or + * match_iter!(vec.into_iter(); (x, y, z*) => { + * // x, y: T + * // z: impl Iterator + * }) + * ``` + * + */ +macro_rules! match_iter { + // Everything else pattern + (@match 0, $iter:expr, $x:ident* $($rest:tt)*) => { + match_iter!(@match 2, $iter $($rest)*); + #[allow(unused_mut)] + let mut $x = $iter; + }; + // Alias to use in macros + (@match 0, $iter:expr, $x:ident?? $($rest:tt)*) => { + match_iter!(@match 2, $iter $($rest)*); + #[allow(unused_mut)] + let mut $x = $iter; + }; + // Optional pattern + (@match 0, $iter:expr, $x:ident? $($rest:tt)*) => { + match_iter!(@match 1, $iter $($rest)*); + let $x = $iter.next(); + $iter.next().ok_or(()).expect_err("Some values remain unused"); + }; + // Normal pattern + (@match 0, $iter:expr, $x:ident $($rest:tt)*) => { + let $x = $iter.next().unwrap(); + match_iter!(@match 0, $iter $($rest)*); + }; + // Normal pattern after a variable length one: declare reversed and take from the end + (@match $w:expr, $iter:expr, $x:ident $($rest:tt)*) => { + match_iter!(@match $w, $iter $($rest)*); + let $x = $iter.next_back().unwrap(); + }; + + // Check no elements remain + (@match 0, $iter:expr) => { + $iter.next().ok_or(()).expect_err("Some values remain unused"); + }; + (@match $_:expr, $iter:expr) => {}; + + // Entrypoint + ($iter:expr; ($($args:tt)*) => $body:expr) => { + { + #[allow(unused_mut)] + let mut iter = $iter; + match_iter!(@match 0, iter, $($args)*); + $body + } + }; +} + + macro_rules! make_parser { ($( named!( $name:ident<$o:ty>; $($args:tt)* ); )*) => ( + #[allow(dead_code)] #[allow(non_camel_case_types)] enum ParsedType { $( $name, )* } impl ParsedType { + #[allow(dead_code)] fn parse(self, pair: Pair) -> ParseResult { match self { $( ParsedType::$name => $name(pair), )* @@ -57,6 +128,11 @@ macro_rules! make_parser { )* } + fn do_the_parse(s: &str, r: Rule, ty: ParsedType) -> ParseResult { + let pairs = DhallParser::parse(r, s)?; + match_iter!(pairs; (e) => ty.parse(e)) + } + $( named!($name<$o>; $($args)*); )* @@ -66,50 +142,46 @@ macro_rules! make_parser { macro_rules! named { ($name:ident<$o:ty>; $submac:ident!( $($args:tt)* )) => ( #[allow(unused_variables)] - fn $name(pair: Pair) -> ParseResult { - let res = $submac!(pair; $($args)*); + fn $name<'a>(pair: Pair<'a, Rule>) -> ParseResult> { + let res: ParseResult<$o> = $submac!(pair; $($args)*); Ok(ParsedValue::$name(res?)) } ); } macro_rules! match_children { - // Normal pattern - (@match 0, $pairs:expr, $x:ident : $ty:ident $($rest:tt)*) => { - let $x = $ty($pairs.next().unwrap())?.$ty()?; - match_children!(@match 0, $pairs $($rest)*); + (@collect, $pairs:expr, ($($args:tt)*), $body:expr, ($($acc:tt)*), ($x:ident : $ty:ident, $($rest:tt)*)) => { + match_children!(@collect, $pairs, ($($args)*), $body, ($($acc)*, $x), ($($rest)*)) }; - // Normal pattern after a variable length one: declare reversed and take from the end - (@match $w:expr, $pairs:expr, $x:ident : $ty:ident $($rest:tt)*) => { - match_children!(@match $w, $pairs $($rest)*); - let $x = $ty($pairs.next_back().unwrap())?.$ty()?; + (@collect, $pairs:expr, ($($args:tt)*), $body:expr, ($($acc:tt)*), ($x:ident? : $ty:ident, $($rest:tt)*)) => { + match_children!(@collect, $pairs, ($($args)*), $body, ($($acc)*, $x?), ($($rest)*)) }; - // Optional pattern - (@match 0, $pairs:expr, $x:ident? : $ty:ident $($rest:tt)*) => { - match_children!(@match 1, $pairs $($rest)*); - let $x = $pairs.next().map($ty).map(|x| x?.$ty()).transpose()?; - $pairs.next().ok_or(()).expect_err("Some parsed values remain unused"); + (@collect, $pairs:expr, ($($args:tt)*), $body:expr, ($($acc:tt)*), ($x:ident* : $ty:ident, $($rest:tt)*)) => { + match_children!(@collect, $pairs, ($($args)*), $body, ($($acc)*, $x??), ($($rest)*)) }; - // Everything else pattern - (@match 0, $pairs:expr, $x:ident* : $ty:ident $($rest:tt)*) => { - match_children!(@match 2, $pairs $($rest)*); - #[allow(unused_mut)] - let mut $x = $pairs.map($ty).map(|x| x?.$ty()); + (@collect, $pairs:expr, ($($args:tt)*), $body:expr, (,$($acc:tt)*), ()) => { + match_iter!($pairs; ($($acc)*) => { + match_children!(@parse, $pairs, $($args)*); + Ok($body) + }) }; - // Check no elements remain - (@match 0, $pairs:expr) => { - $pairs.next().ok_or(()).expect_err("Some parsed values remain unused"); + (@parse, $pairs:expr, $x:ident : $ty:ident $($rest:tt)*) => { + let $x = $ty($x)?.$ty()?; + match_children!(@parse, $pairs $($rest)*); }; - (@match $_:expr, $pairs:expr) => {}; + (@parse, $pairs:expr, $x:ident? : $ty:ident $($rest:tt)*) => { + let $x = $x.map($ty).map(|x| x?.$ty()).transpose()?; + match_children!(@parse, $pairs $($rest)*); + }; + (@parse, $pairs:expr, $x:ident* : $ty:ident $($rest:tt)*) => { + #[allow(unused_mut)] + let mut $x = $x.map($ty).map(|x| x?.$ty()); + match_children!(@parse, $pairs $($rest)*); + }; + (@parse, $pairs:expr) => {}; // Entrypoints - (@pairs; $pairs:expr; ($($args:tt)*) => $body:expr) => { - { - match_children!(@match 0, $pairs, $($args)*); - Ok($body) - } - }; ($pair:expr; $($rest:tt)*) => { { #[allow(unused_mut)] @@ -117,6 +189,9 @@ macro_rules! match_children { match_children!(@pairs; pairs; $($rest)*) } }; + (@pairs; $pairs:expr; ($($args:tt)*) => $body:expr) => { + match_children!(@collect, $pairs, ($($args)*), $body, (), ($($args)*,)) + }; } macro_rules! with_captured_str { @@ -313,12 +388,15 @@ make_parser!{ }) ), )); + + named!(final_expression>; + match_children!((e: expression, _eoi: eoi) => e) + ); } pub fn parse_expr_pest(s: &str) -> ParseResult { - let mut pairs = DhallParser::parse(Rule::final_expression, s)?; - match_children!(@pairs; pairs; (e: expression, _eoi: eoi) => e) + do_the_parse(s, Rule::final_expression, ParsedType::final_expression)?.final_expression() } diff --git a/dhall_parser/build.rs b/dhall_parser/build.rs index 6001efe..8dd5875 100644 --- a/dhall_parser/build.rs +++ b/dhall_parser/build.rs @@ -28,7 +28,7 @@ fn main() -> std::io::Result<()> { let mut file = File::create(pest_path)?; writeln!(&mut file, "{}", abnf_to_pest(&data, &rule_settings)?)?; - writeln!(&mut file, "final_expression = _{{ SOI ~ complete_expression ~ EOI }}")?; + writeln!(&mut file, "final_expression = {{ SOI ~ complete_expression ~ EOI }}")?; Ok(()) } diff --git a/dhall_parser/src/dhall.pest.visibility b/dhall_parser/src/dhall.pest.visibility index e7aba3f..776af24 100644 --- a/dhall_parser/src/dhall.pest.visibility +++ b/dhall_parser/src/dhall.pest.visibility @@ -165,4 +165,3 @@ non_empty_union_type_or_literal non_empty_list_literal_raw # parenthesized_expression_raw # complete_expression -# final_expression -- cgit v1.2.3