use itertools::Itertools; use pest::iterators::Pair; use pest::Parser; use std::collections::BTreeMap; use std::path::PathBuf; use dhall_parser::{DhallParser, Rule}; use crate::*; // 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. type ParsedText = InterpolatedText; type ParsedTextContents = InterpolatedTextContents; pub type ParseError = pest::error::Error; pub type ParseResult = Result; #[derive(Debug)] enum Either { Left(A), Right(B), } impl Builtin { pub fn parse(s: &str) -> Option { use self::Builtin::*; match s { "Bool" => Some(Bool), "Natural" => Some(Natural), "Integer" => Some(Integer), "Double" => Some(Double), "Text" => Some(Text), "List" => Some(List), "Optional" => Some(Optional), "Some" => Some(OptionalSome), "None" => Some(OptionalNone), "Natural/build" => Some(NaturalBuild), "Natural/fold" => Some(NaturalFold), "Natural/isZero" => Some(NaturalIsZero), "Natural/even" => Some(NaturalEven), "Natural/odd" => Some(NaturalOdd), "Natural/toInteger" => Some(NaturalToInteger), "Natural/show" => Some(NaturalShow), "Integer/toDouble" => Some(IntegerToDouble), "Integer/show" => Some(IntegerShow), "Double/show" => Some(DoubleShow), "List/build" => Some(ListBuild), "List/fold" => Some(ListFold), "List/length" => Some(ListLength), "List/head" => Some(ListHead), "List/last" => Some(ListLast), "List/indexed" => Some(ListIndexed), "List/reverse" => Some(ListReverse), "Optional/fold" => Some(OptionalFold), "Optional/build" => Some(OptionalBuild), "Text/show" => Some(TextShow), _ => None, } } } 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, $($vars:tt)*), ($($outer_acc:tt)*), ($($acc:tt)*), ($(,)* $ty:ident ($x:ident..) $($rest_of_match:tt)*) => $body:expr, $($rest:tt)*) => { match_pair!(@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_pair!(@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_pair!(@make_matches, ($($vars)*), ($($outer_acc)* [$($acc)*] => { $body },), $($rest)*) }; (@make_child_match, ($($vars:tt)*), ($($outer_acc:tt)*), (), ($(,)*) => $body:expr, $($rest:tt)*) => { match_pair!(@make_matches, ($($vars)*), ($($outer_acc)* [] => { $body },), $($rest)*) }; (@make_matches, ($($vars:tt)*), ($($acc:tt)*), [$($args:tt)*] => $body:expr, $($rest:tt)*) => { match_pair!(@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_pair!(@make_matches, ($($vars)*), (), $( [$($args)*] => $body ),* ,) }; } macro_rules! make_parser { (@pattern, rule, $name:ident) => (Rule::$name); (@pattern, rule_group, $name:ident) => (_); (@filter, rule) => (true); (@filter, rule_group) => (false); (@body, $pair:expr, $children:expr, rule!( $name:ident<$o:ty>; $($args:tt)* )) => ( make_parser!(@body, $pair, $children, rule!( $name<$o> as $name; $($args)* )) ); (@body, $pair:expr, $children:expr, rule!( $name:ident<$o:ty> as $group:ident; captured_str!($x:pat) => $body:expr )) => ( { let $x = $pair.as_str(); let res: $o = $body; Ok(ParsedValue::$group(res)) }); (@body, $pair:expr, $children:expr, rule!( $name:ident<$o:ty> as $group:ident; children!( $($args:tt)* ) )) => ( { let res: $o = match_pair!(($pair, $children); $($args)*)?; Ok(ParsedValue::$group(res)) }); (@body, $pair:expr, $children:expr, rule_group!( $name:ident<$o:ty> )) => ( unreachable!() ); ($( $submac:ident!( $name:ident<$o:ty> $($args:tt)* ); )*) => ( #[allow(non_camel_case_types, dead_code)] #[derive(Debug)] enum ParsedValue<'a> { $( $name($o), )* } fn parse_any<'a>(pair: Pair<'a, Rule>, children: Vec>) -> Result, String> { match pair.as_rule() { $( make_parser!(@pattern, $submac, $name) if make_parser!(@filter, $submac) => make_parser!(@body, pair, children, $submac!( $name<$o> $($args)* )) , )* r => Err(format!("Unexpected {:?}", r)), } } // Non-recursive implementation to avoid stack overflows fn do_parse<'a>(initial_pair: Pair<'a, Rule>) -> ParseResult> { enum StackFrame<'a> { Unprocessed(Pair<'a, Rule>), Processed(Pair<'a, Rule>, usize), } use StackFrame::*; let mut pairs_stack: Vec = vec![Unprocessed(initial_pair.clone())]; let mut values_stack: Vec = vec![]; while let Some(p) = pairs_stack.pop() { match p { Unprocessed(mut pair) => { loop { let mut pairs: Vec<_> = pair.clone().into_inner().collect(); let n_children = pairs.len(); if n_children == 1 && can_be_shortcutted(pair.as_rule()) { pair = pairs.pop().unwrap(); continue } else { pairs_stack.push(Processed(pair, n_children)); pairs_stack.extend(pairs.into_iter().map(StackFrame::Unprocessed)); break } } } Processed(pair, n) => { let mut children: Vec<_> = values_stack.split_off(values_stack.len() - n); children.reverse(); let val = match parse_any(pair.clone(), children) { Ok(v) => v, Err(msg) => Err(custom_parse_error(&pair, msg))?, }; values_stack.push(val); } } } Ok(values_stack.pop().unwrap()) } ); } // List of rules that can be shortcutted if they have a single child 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 | annotated_expression => true, _ => false, } } make_parser! { rule!(EOI<()>; captured_str!(_) => ()); rule!(simple_label