use pest::iterators::Pair; use pest::Parser; use std::collections::BTreeMap; use std::path::PathBuf; use std::rc::Rc; use dhall_parser::{DhallParser, Rule}; use crate::core; use crate::core::*; // This file consumes the parse tree generated by pest and turns it into // our own AST. All those custom macros should eventually moved into // their own crate because they are quite general and useful. For now they // are here and hopefully you can figure out how they work. pub type ParsedExpr = Expr; pub type ParsedText = InterpolatedText; pub type ParsedTextContents<'a> = InterpolatedTextContents<'a, X, Import>; pub type RcExpr = Rc; pub type ParseError = pest::error::Error; pub type ParseResult = Result; pub fn custom_parse_error(pair: &Pair, msg: String) -> ParseError { let msg = format!("{} while matching on:\n{}", msg, debug_pair(pair.clone())); let e = pest::error::ErrorVariant::CustomError { message: msg }; pest::error::Error::new_from_span(e, pair.as_span()) } fn debug_pair(pair: Pair) -> String { use std::fmt::Write; let mut s = String::new(); fn aux(s: &mut String, indent: usize, prefix: String, pair: Pair) { let indent_str = "| ".repeat(indent); let rule = pair.as_rule(); let contents = pair.as_str(); let mut inner = pair.into_inner(); let mut first = true; while let Some(p) = inner.next() { if first { first = false; let last = inner.peek().is_none(); if last && p.as_str() == contents { let prefix = format!("{}{:?} > ", prefix, rule); aux(s, indent, prefix, p); continue; } else { writeln!( s, r#"{}{}{:?}: "{}""#, indent_str, prefix, rule, contents ) .unwrap(); } } aux(s, indent + 1, "".into(), p); } if first { writeln!( s, r#"{}{}{:?}: "{}""#, indent_str, prefix, rule, contents ) .unwrap(); } } aux(&mut s, 0, "".into(), pair); s } macro_rules! match_pair { (@make_child_match, $pair:expr, ($($outer_acc:tt)*), ($($acc:tt)*), ($(,)* $x:ident : $ty:ident $($rest_of_match:tt)*) => $body:expr, $($rest:tt)*) => { match_pair!(@make_child_match, $pair, ($($outer_acc)*), ($($acc)*, ParsedValue::$ty($x)), ($($rest_of_match)*) => $body, $($rest)*) }; (@make_child_match, $pair:expr, ($($outer_acc:tt)*), ($($acc:tt)*), ($(,)* $x:ident.. : $ty:ident $($rest_of_match:tt)*) => $body:expr, $($rest:tt)*) => { match_pair!(@make_child_match, $pair, ($($outer_acc)*), ($($acc)*, x..), ($($rest_of_match)*) => { let $x = x.map(|x| x.$ty()); $body }, $($rest)*) }; (@make_child_match, $pair:expr, ($($outer_acc:tt)*), (, $($acc:tt)*), ($(,)*) => $body:expr, $($rest:tt)*) => { match_pair!(@make_matches, $pair, ([$($acc)*] => { $body }, $($outer_acc)*), $($rest)*) }; (@make_child_match, $pair:expr, ($($outer_acc:tt)*), (), ($(,)*) => $body:expr, $($rest:tt)*) => { match_pair!(@make_matches, $pair, ([] => { $body }, $($outer_acc)*), $($rest)*) }; (@make_matches, $pair:expr, ($($acc:tt)*), children!($($args:tt)*) => $body:expr, $($rest:tt)*) => { match_pair!(@make_child_match, $pair, ($($acc)*), (), ($($args)*) => $body, $($rest)*) }; (@make_matches, $pair:expr, ($($acc:tt)*) $(,)*) => { { let pair = $pair.clone(); let rule = pair.as_rule(); let parsed: Vec<_> = pair.clone().into_inner().map(parse_any).collect::>()?; #[allow(unreachable_code)] iter_patterns::match_vec!(parsed; $($acc)* [x..] => panic!("Unexpected children while parsing rule '{:?}': {:?}", rule, x.collect::>()), ).ok_or_else(|| custom_parse_error(&pair, "No match found".to_owned())) } }; ($pair:expr; $( $submac:ident!($($args:tt)*) => $body:expr ),* $(,)*) => { match_pair!(@make_matches, $pair, (), $( $submac!($($args)*) => $body ),* ,) }; } macro_rules! make_parser { // Filter out definitions that should not be matched on (i.e. rule_group) (@filter, rule) => (true); (@filter, rule_in_group) => (true); (@filter, rule_group) => (false); (@body, $pair:expr, rule!( $name:ident<$o:ty>; $($args:tt)* )) => ( make_parser!(@body, $pair, rule_in_group!( $name<$o>; $name; $($args)* )) ); (@body, $pair:expr, rule_in_group!( $name:ident<$o:ty>; $group:ident; raw_pair!($x:pat) => $body:expr )) => ( { let $x = $pair.clone(); let res: $o = $body; Ok(ParsedValue::$group(res)) }); (@body, $pair:expr, rule_in_group!( $name:ident<$o:ty>; $group:ident; captured_str!($x:ident) => $body:expr )) => ( { let $x = $pair.as_str(); let res: $o = $body; Ok(ParsedValue::$group(res)) }); (@body, $pair:expr, rule_in_group!( $name:ident<$o:ty>; $group:ident; $($args:tt)* )) => ( { let res: $o = match_pair!($pair; $($args)*)?; Ok(ParsedValue::$group(res)) }); (@body, $pair:expr, rule_group!( $name:ident<$o:ty> )) => ( unreachable!() ); ($( $submac:ident!( $name:ident<$o:ty> $($args:tt)* ); )*) => ( // #[allow(non_camel_case_types, dead_code)] // enum ParsedType { // $( $name, )* // } // impl ParsedType { // #[allow(dead_code)] // fn parse(self, pair: Pair) -> ParseResult { // match self { // $( ParsedType::$name => { // let ret = $name(pair)?; // Ok(ParsedValue::$name(ret)) // }, )* // } // } // // fn parse(self, pair: Pair) -> ParseResult { // // match self { // // $( ParsedType::$name => $name(pair), )* // // } // // } // } #[allow(non_camel_case_types, dead_code)] #[derive(Debug)] enum ParsedValue<'a> { $( $name($o), )* } impl<'a> ParsedValue<'a> { $( #[allow(non_snake_case, dead_code)] fn $name(self) -> $o { match self { ParsedValue::$name(x) => x, _ => unreachable!(), } } )* } #[allow(non_snake_case, dead_code)] fn parse_any<'a>(pair: Pair<'a, Rule>) -> ParseResult> { #[allow(unreachable_patterns)] match pair.as_rule() { $( Rule::$name if make_parser!(@filter, $submac) => make_parser!(@body, pair, $submac!( $name<$o> $($args)* )) , )* r => Err(custom_parse_error(&pair, format!("parse_any: Unexpected {:?}", r))), } } ); } // List of rules that can be shortcutted as implemented in parse_binop fn can_be_shortcutted(rule: Rule) -> bool { use Rule::*; match rule { import_alt_expression | or_expression | plus_expression | text_append_expression | list_append_expression | and_expression | combine_expression | prefer_expression | combine_types_expression | times_expression | equal_expression | not_equal_expression | application_expression | selector_expression_raw | annotated_expression => true, _ => false, } } fn parse_binop(pair: Pair, o: BinOp) -> ParseResult { // This all could be a trivial fold, but to avoid stack explosion // we try to cut down on the recursion level here, by consuming // chains of blah_expression > ... > blih_expression in one go. let mut pair = pair; let mut pairs = pair.into_inner(); let first = pairs.next().unwrap(); let rest: Vec<_> = pairs .map(|p| parse_any(p).map(|x| x.expression())) .collect::>()?; if !rest.is_empty() { // If there is more than one subexpression, handle it normally let first = parse_any(first)?.expression(); Ok(rest .into_iter() .fold(first, |acc, e| bx(Expr::BinOp(o, acc, e)))) } else { // Otherwise, consume short-cuttable rules as long as they contain only one subexpression. pair = first; while can_be_shortcutted(pair.as_rule()) { let mut pairs = pair.clone().into_inner(); let first = pairs.next().unwrap(); let rest: Vec<_> = pairs.collect(); if !rest.is_empty() { break; } pair = first; } Ok(parse_any(pair)?.expression()) } } make_parser! { rule!(EOI<()>; raw_pair!(_) => ()); rule!(label_raw