use itertools::Itertools; use pest::iterators::Pair; use pest::prec_climber as pcl; use pest::prec_climber::PrecClimber; use pest::Parser; use std::rc::Rc; use dhall_generated_parser::{DhallParser, Rule}; use dhall_proc_macros::{make_parser, parse_children}; use crate::map::{DupTreeMap, DupTreeSet}; use crate::ExprF::*; 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; pub mod pest_consume { use pest::error::{Error, ErrorVariant}; use pest::iterators::Pair; use pest::Span; /// Carries a pest Pair alongside custom user data. #[derive(Debug, Clone)] pub struct ParseInput<'input, 'data, Rule, Data> where Rule: pest::RuleType, { pair: Pair<'input, Rule>, user_data: &'data Data, } impl<'input, 'data, Rule, Data> ParseInput<'input, 'data, Rule, Data> where Rule: pest::RuleType, { pub fn new(pair: Pair<'input, Rule>, user_data: &'data Data) -> Self { ParseInput { pair, user_data } } /// Create an error that points to the span of the input. pub fn error(&self, message: String) -> Error { let message = format!( "{} while matching on:\n{}", message, debug_pair(self.pair.clone()) ); Error::new_from_span( ErrorVariant::CustomError { message }, self.as_span(), ) } /// Reconstruct the input with a new pair, passing the user data along. pub fn with_pair(&self, new_pair: Pair<'input, Rule>) -> Self { ParseInput { pair: new_pair, user_data: self.user_data, } } /// If the contained pair has exactly one child, return a new Self containing it. pub fn single_child(&self) -> Option { let mut children = self.pair.clone().into_inner(); if let Some(child) = children.next() { if children.next().is_none() { return Some(self.with_pair(child)); } } None } pub fn user_data(&self) -> &'data Data { self.user_data } pub fn as_pair(&self) -> &Pair<'input, Rule> { &self.pair } pub fn as_span(&self) -> Span<'input> { self.pair.as_span() } pub fn as_str(&self) -> &'input str { self.pair.as_str() } pub fn as_rule(&self) -> Rule { self.pair.as_rule() } } /// Used by the macros. pub trait PestConsumer { type Rule: pest::RuleType; fn rule_alias(rule: Self::Rule) -> String; fn allows_shortcut(rule: Self::Rule) -> bool; } /// Pretty-print a pair and its nested children. 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 } } type ParseInput<'input, 'data> = pest_consume::ParseInput<'input, 'data, Rule, Rc>; #[derive(Debug)] enum Either { Left(A), Right(B), } impl crate::Builtin { pub fn parse(s: &str) -> Option { use crate::Builtin::*; match s { "Bool" => Some(Bool), "Natural" => Some(Natural), "Integer" => Some(Integer), "Double" => Some(Double), "Text" => Some(Text), "List" => Some(List), "Optional" => Some(Optional), "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), "Natural/subtract" => Some(NaturalSubtract), "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, } } } fn input_to_span(input: ParseInput) -> Span { Span::make(input.user_data().clone(), input.as_pair().as_span()) } fn spanned(input: ParseInput, x: RawExpr) -> Expr { Expr::new(x, input_to_span(input)) } fn spanned_union(span1: Span, span2: Span, x: RawExpr) -> Expr { Expr::new(x, span1.union(&span2)) } // Trim the shared indent off of a vec of lines, as defined by the Dhall semantics of multiline // literals. fn trim_indent(lines: &mut Vec>) { let is_indent = |c: char| c == ' ' || c == '\t'; // There is at least one line so this is safe let last_line_head = lines.last().unwrap().head(); let indent_chars = last_line_head .char_indices() .take_while(|(_, c)| is_indent(*c)); let mut min_indent_idx = match indent_chars.last() { Some((i, _)) => i, // If there is no indent char, then no indent needs to be stripped None => return, }; for line in lines.iter() { // Ignore empty lines if line.is_empty() { continue; } // Take chars from line while they match the current minimum indent. let indent_chars = last_line_head[0..=min_indent_idx] .char_indices() .zip(line.head().chars()) .take_while(|((_, c1), c2)| c1 == c2); match indent_chars.last() { Some(((i, _), _)) => min_indent_idx = i, // If there is no indent char, then no indent needs to be stripped None => return, }; } // Remove the shared indent from non-empty lines for line in lines.iter_mut() { if !line.is_empty() { line.head_mut().replace_range(0..=min_indent_idx, ""); } } } lazy_static::lazy_static! { static ref PRECCLIMBER: PrecClimber = { 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(), ) }; } struct Parsers; #[make_parser(Rule)] impl Parsers { fn EOI(_input: ParseInput) -> ParseResult<()> { Ok(()) } #[alias(label)] fn simple_label(input: ParseInput) -> ParseResult