From ffb5a4c26960960f526719d9b333df80119fef3c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 15 Aug 2019 17:59:01 +0200 Subject: Include precedence parsing in parser macros --- dhall_syntax/src/lib.rs | 1 + dhall_syntax/src/parser.rs | 320 ++++++++++++++++++++++++++------------------- 2 files changed, 188 insertions(+), 133 deletions(-) (limited to 'dhall_syntax') diff --git a/dhall_syntax/src/lib.rs b/dhall_syntax/src/lib.rs index 193c6ea..8406595 100644 --- a/dhall_syntax/src/lib.rs +++ b/dhall_syntax/src/lib.rs @@ -1,5 +1,6 @@ #![feature(trace_macros)] #![feature(slice_patterns)] +#![feature(try_blocks)] #![allow( clippy::many_single_char_names, clippy::should_implement_trait, diff --git a/dhall_syntax/src/parser.rs b/dhall_syntax/src/parser.rs index 52b4426..d467970 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -1,8 +1,10 @@ use itertools::Itertools; use pest::iterators::Pair; use pest::prec_climber as pcl; +use pest::prec_climber::PrecClimber; use pest::Parser; use std::borrow::Cow; +use std::collections::HashMap; use std::rc::Rc; use dhall_generated_parser::{DhallParser, Rule}; @@ -125,61 +127,117 @@ macro_rules! make_parser { (@filter, token_rule) => (true); (@filter, rule_group) => (false); - (@body, - ($($things:tt)*), - rule!( $name:ident<$o:ty>; $($args:tt)* ) - ) => ( - make_parser!(@body, - ($($things)*), - rule!( $name<$o> as $name; $($args)* ) + (@construct_climber, + ($map:expr), + rule!( + $name:ident<$o:ty> + as $group:ident; + prec_climb!( $climber:expr, $($_rest:tt)* ) ) - ); + ) => ({ + $map.insert(Rule::$name, $climber) + }); + (@construct_climber, ($($things:tt)*), $($args:tt)*) => (()); + (@body, - ($_input:expr, $pair:expr, $_children:expr), + ($climbers:expr, $input:expr, $pair:expr), rule!( $name:ident<$o:ty> as $group:ident; + $span:ident; captured_str!($x:pat) => $body:expr ) ) => ({ - let $x = $pair.as_str(); - let res: $o = $body; - Ok(ParsedValue::$group(res)) + let res: Result<_, String> = try { + let $span = Span::make($input, $pair.as_span()); + let $x = $pair.as_str(); + ParsedValue::$group($body) + }; + res.map_err(|msg| custom_parse_error(&$pair, msg)) }); (@body, - ($_input:expr, $_pair:expr, $children:expr), + ($climbers:expr, $input:expr, $pair:expr), rule!( $name:ident<$o:ty> as $group:ident; + $span:ident; children!( $( [$($args:tt)*] => $body:expr ),* $(,)* ) ) ) => ({ - #[allow(unused_imports)] - use ParsedValue::*; + let children: Vec<_> = $pair + .clone() + .into_inner() + .map(|p| parse_any($climbers, $input.clone(), p)) + .collect::>()?; + #[allow(unreachable_code)] - let res: $o = improved_slice_patterns::match_vec!($children; - $( [$($args)*] => $body, )* - [x..] => Err( - format!("Unexpected children: {:?}", x.collect::>()) - )?, - ).map_err(|_| -> String { unreachable!() })?; + let res: Result<_, String> = try { + #[allow(unused_imports)] + use ParsedValue::*; + let $span = Span::make($input, $pair.as_span()); + + improved_slice_patterns::match_vec!(children; + $( [$($args)*] => $body, )* + [x..] => Err( + format!("Unexpected children: {:?}", x.collect::>()) + )?, + ).map_err(|_| -> String { unreachable!() })? + }; + + let res = res.map_err(|msg| custom_parse_error(&$pair, msg))?; Ok(ParsedValue::$group(res)) }); (@body, - ($input:expr, $pair:expr, $children:expr), + ($climbers:expr, $input:expr, $pair:expr), + rule!( + $name:ident<$o:ty> + as $group:ident; + prec_climb!( $_climber:expr, $args:pat => $body:expr $(,)* ) + ) + ) => ({ + let climber = $climbers.get(&Rule::$name).unwrap(); + climber.climb( + $pair.clone().into_inner(), + |p| parse_any($climbers, $input.clone(), p), + |l, op, r| { + let (l, r) = (l?, r?); + let res: Result<_, String> = try { + #[allow(unused_imports)] + use ParsedValue::*; + match (l, op, r) { + $args => ParsedValue::$group($body), + children => Err( + format!("Unexpected children: {:?}", children) + )?, + } + }; + res.map_err(|msg| custom_parse_error(&$pair, msg)) + }, + ) + }); + (@body, + ($($things:tt)*), + rule!( $name:ident<$o:ty>; $($args:tt)* ) + ) => ( + make_parser!(@body, + ($($things)*), + rule!( $name<$o> as $name; $($args)* ) + ) + ); + (@body, + ($($things:tt)*), rule!( $name:ident<$o:ty> as $group:ident; - $span:ident; $($args:tt)* ) ) => ({ - let $span = Span::make($input, $pair.as_span()); make_parser!(@body, - ($input, $pair, $children), + ($($things)*), rule!( $name<$o> as $group; + _span; $($args)* ) ) @@ -201,20 +259,63 @@ macro_rules! make_parser { $( $name($o), )* } + fn construct_precclimbers() -> HashMap> { + let mut map = HashMap::new(); + $( + make_parser!(@construct_climber, (map), + $submac!( $name<$o> $($args)* )); + )* + map + } + + struct Parsers; + + impl Parsers { + $( + #[allow(non_snake_case, unused_variables)] + fn $name<'a>( + climbers: &HashMap>, + input: Rc, + pair: Pair<'a, Rule>, + ) -> ParseResult> { + make_parser!(@body, (climbers, input, pair), + $submac!( $name<$o> $($args)* )) + } + )* + } + fn parse_any<'a>( + climbers: &HashMap>, input: Rc, - pair: Pair<'a, Rule>, - children: Vec>, - ) -> Result, String> { + mut pair: Pair<'a, Rule>, + ) -> ParseResult> { + // Avoid parsing while the pair has exactly one child that can be returned as-is + loop { + if can_be_shortcutted(pair.as_rule()) { + let mut i = pair.clone().into_inner(); + let first = i.next(); + let second = i.next(); + match (first, second) { + // If pair has exactly one child, just go on parsing that child + (Some(p), None) => { + pair = p; + continue; + } + // Otherwise parse normally + _ => break, + } + } + break; + } + match pair.as_rule() { $( make_parser!(@pattern, $submac, $name) if make_parser!(@filter, $submac) - => make_parser!(@body, (input, pair, children), - $submac!( $name<$o> $($args)* )) + => Parsers::$name(climbers, input, pair) , )* - r => Err(format!("Unexpected {:?}", r)), + r => Err(custom_parse_error(&pair, format!("Unexpected {:?}", r))), } } ); @@ -222,109 +323,10 @@ macro_rules! make_parser { fn do_parse<'a>( input: Rc, - mut pair: Pair<'a, Rule>, + pair: Pair<'a, Rule>, ) -> ParseResult> { - // Avoid parsing while the pair has exactly one child that can be returned as-is - loop { - if can_be_shortcutted(pair.as_rule()) { - let mut i = pair.clone().into_inner(); - let first = i.next(); - let second = i.next(); - match (first, second) { - // If pair has exactly one child, just go on parsing that child - (Some(p), None) => { - pair = p; - continue; - } - // Otherwise parse normally - _ => break, - } - } - break; - } - - // Use precedence climbing to parse operator_expression - if pair.as_rule() == Rule::operator_expression { - let rule_to_binop = { - use crate::BinOp::*; - use Rule::*; - |r| { - Some(match r { - import_alt => ImportAlt, - bool_or => BoolOr, - natural_plus => NaturalPlus, - text_append => TextAppend, - list_append => ListAppend, - bool_and => BoolAnd, - combine => RecursiveRecordMerge, - prefer => RightBiasedRecordMerge, - combine_types => RecursiveRecordTypeMerge, - natural_times => NaturalTimes, - bool_eq => BoolEQ, - bool_ne => BoolNE, - equivalent => Equivalence, - _ => return None, - }) - } - }; - let operators = { - use Rule::*; - // In order of precedence - vec![ - import_alt, - bool_or, - natural_plus, - text_append, - list_append, - bool_and, - combine, - prefer, - combine_types, - natural_times, - bool_eq, - bool_ne, - equivalent, - ] - }; - let climber = pcl::PrecClimber::new( - operators - .into_iter() - .map(|op| pcl::Operator::new(op, pcl::Assoc::Left)) - .collect(), - ); - - climber.climb( - pair.clone().into_inner(), - |p| do_parse(input.clone(), p), - |l, p, r| { - let o = match rule_to_binop(p.as_rule()) { - Some(o) => o, - None => Err(custom_parse_error( - &pair, - format!("Rule {:?} isn't an operator", p.as_rule()), - ))?, - }; - use ParsedValue::expression; - match (l?, r?) { - (expression(l), expression(r)) => { - Ok(expression(unspanned(BinOp(o, l, r)))) - } - (l, r) => Err(custom_parse_error( - &pair, - format!("Unexpected children: {:?}", [l, r]), - ))?, - } - }, - ) - } else { - let children = pair - .clone() - .into_inner() - .map(|p| do_parse(input.clone(), p)) - .collect::>()?; - parse_any(input.clone(), pair.clone(), children) - .map_err(|msg| custom_parse_error(&pair, msg)) - } + let climbers = construct_precclimbers(); + parse_any(&climbers, input, pair) } // List of rules that can be shortcutted if they have a single child @@ -824,6 +826,58 @@ make_parser! { }, )); + rule!(operator_expression as expression; prec_climb!( + { + use Rule::*; + // In order of precedence + let operators = vec![ + import_alt, + bool_or, + natural_plus, + text_append, + list_append, + bool_and, + combine, + prefer, + combine_types, + natural_times, + bool_eq, + bool_ne, + equivalent, + ]; + PrecClimber::new( + operators + .into_iter() + .map(|op| pcl::Operator::new(op, pcl::Assoc::Left)) + .collect(), + ) + }, + (expression(l), op, expression(r)) => { + use crate::BinOp::*; + use Rule::*; + let op = match op.as_rule() { + import_alt => ImportAlt, + bool_or => BoolOr, + natural_plus => NaturalPlus, + text_append => TextAppend, + list_append => ListAppend, + bool_and => BoolAnd, + combine => RecursiveRecordMerge, + prefer => RightBiasedRecordMerge, + combine_types => RecursiveRecordTypeMerge, + natural_times => NaturalTimes, + bool_eq => BoolEQ, + bool_ne => BoolNE, + equivalent => Equivalence, + r => Err( + format!("Rule {:?} isn't an operator", r), + )?, + }; + + unspanned(BinOp(op, l, r)) + } + )); + token_rule!(Some_<()>); token_rule!(toMap<()>); -- cgit v1.2.3