diff options
Diffstat (limited to 'dhall_core/src/parser.rs')
-rw-r--r-- | dhall_core/src/parser.rs | 984 |
1 files changed, 0 insertions, 984 deletions
diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs deleted file mode 100644 index 12383d4..0000000 --- a/dhall_core/src/parser.rs +++ /dev/null @@ -1,984 +0,0 @@ -use itertools::Itertools; -use pest::iterators::Pair; -use pest::Parser; -pub use pest::Span; -use std::borrow::Cow; -use std::collections::BTreeMap; -use std::path::PathBuf; - -use dhall_generated_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. - -use crate::ExprF::*; - -type ParsedExpr<'a> = Expr<Span<'a>, Import>; -type ParsedSubExpr<'a> = SubExpr<Span<'a>, Import>; -type ParsedText<'a> = InterpolatedText<SubExpr<Span<'a>, Import>>; -type ParsedTextContents<'a> = - InterpolatedTextContents<SubExpr<Span<'a>, Import>>; - -pub type ParseError = pest::error::Error<Rule>; - -pub type ParseResult<T> = Result<T, ParseError>; - -fn rc(x: ParsedExpr<'_>) -> ParsedSubExpr<'_> { - crate::rc(x) -} - -fn spanned<'a>(_span: Span<'a>, x: ParsedExpr<'a>) -> ParsedExpr<'a> { - x - // This breaks equality testing; I need to fix that first - // Note(span, rc(x)) -} - -#[derive(Debug)] -enum Either<A, B> { - Left(A), - Right(B), -} - -impl crate::Builtin { - pub fn parse(s: &str) -> Option<Self> { - 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), - "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<Rule>, 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<Rule>) -> String { - use std::fmt::Write; - let mut s = String::new(); - fn aux(s: &mut String, indent: usize, prefix: String, pair: Pair<Rule>) { - 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! make_parser { - (@pattern, rule, $name:ident) => (Rule::$name); - (@pattern, token_rule, $name:ident) => (Rule::$name); - (@pattern, rule_group, $name:ident) => (_); - (@filter, rule) => (true); - (@filter, token_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)*] => $body:expr ),* $(,)* ) - ) - ) => ({ - #[allow(unused_imports)] - use ParsedValue::*; - #[allow(unreachable_code)] - let res: $o = improved_slice_patterns::match_vec!($children; - $( [$($args)*] => $body, )* - [x..] => Err( - format!("Unexpected children: {:?}", x.collect::<Vec<_>>()) - )?, - ).map_err(|_| -> String { unreachable!() })?; - Ok(ParsedValue::$group(res)) - }); - (@body, - $pair:expr, - $children:expr, - rule!( - $name:ident<$o:ty> - as $group:ident; - $span:ident; - $($args:tt)* - ) - ) => ({ - let $span = $pair.as_span(); - make_parser!(@body, - $pair, - $children, - rule!( - $name<$o> - as $group; - $($args)* - ) - ) - }); - (@body, - $pair:expr, - $children:expr, - token_rule!($name:ident<$o:ty>) - ) => ({ - Ok(ParsedValue::$name(())) - }); - (@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, clippy::large_enum_variant)] - #[derive(Debug)] - enum ParsedValue<'a> { - $( $name($o), )* - } - - fn parse_any<'a>(pair: Pair<'a, Rule>, children: Vec<ParsedValue<'a>>) - -> Result<ParsedValue<'a>, 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<ParsedValue<'a>> { - enum StackFrame<'a> { - Unprocessed(Pair<'a, Rule>), - Processed(Pair<'a, Rule>, usize), - } - use StackFrame::*; - let mut pairs_stack: Vec<StackFrame> = - vec![Unprocessed(initial_pair.clone())]; - let mut values_stack: Vec<ParsedValue> = 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 { - expression - | 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 - | first_application_expression - | selector_expression - | annotated_expression => true, - _ => false, - } -} - -make_parser! { - token_rule!(EOI<()>); - - rule!(simple_label<Label>; - captured_str!(s) => Label::from(s.trim().to_owned()) - ); - rule!(quoted_label<Label>; - captured_str!(s) => Label::from(s.trim().to_owned()) - ); - rule!(label<Label>; children!( - [simple_label(l)] => l, - [quoted_label(l)] => l, - )); - - rule!(double_quote_literal<ParsedText<'a>>; children!( - [double_quote_chunk(chunks)..] => { - chunks.collect() - } - )); - - rule!(double_quote_chunk<ParsedTextContents<'a>>; children!( - [interpolation(e)] => { - InterpolatedTextContents::Expr(rc(e)) - }, - [double_quote_escaped(s)] => { - InterpolatedTextContents::Text(s) - }, - [double_quote_char(s)] => { - InterpolatedTextContents::Text(s.to_owned()) - }, - )); - rule!(double_quote_escaped<String>; - captured_str!(s) => { - match s { - "\"" => "\"".to_owned(), - "$" => "$".to_owned(), - "\\" => "\\".to_owned(), - "/" => "/".to_owned(), - "b" => "\u{0008}".to_owned(), - "f" => "\u{000C}".to_owned(), - "n" => "\n".to_owned(), - "r" => "\r".to_owned(), - "t" => "\t".to_owned(), - _ => { - // "uXXXX" - use std::convert::TryFrom; - let c = u16::from_str_radix(&s[1..5], 16).unwrap(); - let c = char::try_from(u32::from(c)).unwrap(); - std::iter::once(c).collect() - } - } - } - ); - rule!(double_quote_char<&'a str>; - captured_str!(s) => s - ); - - rule!(single_quote_literal<ParsedText<'a>>; children!( - [single_quote_continue(lines)] => { - let space = InterpolatedTextContents::Text(" ".to_owned()); - let newline = InterpolatedTextContents::Text("\n".to_owned()); - let min_indent = lines - .iter() - .map(|l| { - l.iter().rev().take_while(|c| **c == space).count() - }) - .min() - .unwrap(); - - lines - .into_iter() - .rev() - .map(|mut l| { l.split_off(l.len() - min_indent); l }) - .intersperse(vec![newline]) - .flat_map(|x| x.into_iter().rev()) - .collect::<ParsedText>() - } - )); - rule!(single_quote_char<&'a str>; - captured_str!(s) => s - ); - rule!(escaped_quote_pair<&'a str>; - captured_str!(_) => "''" - ); - rule!(escaped_interpolation<&'a str>; - captured_str!(_) => "${" - ); - rule!(interpolation<ParsedExpr<'a>>; children!( - [expression(e)] => e - )); - - rule!(single_quote_continue<Vec<Vec<ParsedTextContents<'a>>>>; children!( - [interpolation(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Expr(rc(c)); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [escaped_quote_pair(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Text(c.to_owned()); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [escaped_interpolation(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Text(c.to_owned()); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [single_quote_char("\n"), single_quote_continue(lines)] => { - let mut lines = lines; - lines.push(vec![]); - lines - }, - [single_quote_char(c), single_quote_continue(lines)] => { - let c = InterpolatedTextContents::Text(c.to_owned()); - let mut lines = lines; - lines.last_mut().unwrap().push(c); - lines - }, - [] => { - vec![vec![]] - }, - )); - - rule!(builtin<ParsedExpr<'a>>; span; - captured_str!(s) => { - spanned(span, match crate::Builtin::parse(s) { - Some(b) => Builtin(b), - None => match s { - "True" => BoolLit(true), - "False" => BoolLit(false), - "Type" => Const(crate::Const::Type), - "Kind" => Const(crate::Const::Kind), - "Sort" => Const(crate::Const::Sort), - _ => Err( - format!("Unrecognized builtin: '{}'", s) - )?, - } - }) - } - ); - - token_rule!(NaN<()>); - token_rule!(minus_infinity_literal<()>); - token_rule!(plus_infinity_literal<()>); - - rule!(numeric_double_literal<core::Double>; - captured_str!(s) => { - let s = s.trim(); - match s.parse::<f64>() { - Ok(x) if x.is_infinite() => - Err(format!("Overflow while parsing double literal '{}'", s))?, - Ok(x) => NaiveDouble::from(x), - Err(e) => Err(format!("{}", e))?, - } - } - ); - - rule!(double_literal<core::Double>; children!( - [numeric_double_literal(n)] => n, - [minus_infinity_literal(n)] => std::f64::NEG_INFINITY.into(), - [plus_infinity_literal(n)] => std::f64::INFINITY.into(), - [NaN(n)] => std::f64::NAN.into(), - )); - - rule!(natural_literal<core::Natural>; - captured_str!(s) => { - s.trim() - .parse() - .map_err(|e| format!("{}", e))? - } - ); - - rule!(integer_literal<core::Integer>; - captured_str!(s) => { - s.trim() - .parse() - .map_err(|e| format!("{}", e))? - } - ); - - rule!(identifier<ParsedExpr<'a>> as expression; span; children!( - [variable(v)] => { - spanned(span, Var(v)) - }, - [builtin(e)] => e, - )); - - rule!(variable<V<Label>>; children!( - [label(l), natural_literal(idx)] => { - V(l, idx) - }, - [label(l)] => { - V(l, 0) - }, - )); - - rule!(unquoted_path_component<&'a str>; captured_str!(s) => s); - rule!(quoted_path_component<&'a str>; captured_str!(s) => s); - rule!(path_component<String>; children!( - [unquoted_path_component(s)] => { - percent_encoding::percent_decode(s.as_bytes()) - .decode_utf8_lossy() - .into_owned() - }, - [quoted_path_component(s)] => s.to_string(), - )); - rule!(path<PathBuf>; children!( - [path_component(components)..] => { - components.collect() - } - )); - - rule_group!(local<(FilePrefix, PathBuf)>); - - rule!(parent_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Parent, p) - )); - rule!(here_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Here, p) - )); - rule!(home_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Home, p) - )); - rule!(absolute_path<(FilePrefix, PathBuf)> as local; children!( - [path(p)] => (FilePrefix::Absolute, p) - )); - - rule!(scheme<Scheme>; captured_str!(s) => match s { - "http" => Scheme::HTTP, - "https" => Scheme::HTTPS, - _ => unreachable!(), - }); - - rule!(http_raw<URL>; children!( - [scheme(sch), authority(auth), path(p)] => URL { - scheme: sch, - authority: auth, - path: p, - query: None, - headers: None, - }, - [scheme(sch), authority(auth), path(p), query(q)] => URL { - scheme: sch, - authority: auth, - path: p, - query: Some(q), - headers: None, - }, - )); - - rule!(authority<String>; captured_str!(s) => s.to_owned()); - - rule!(query<String>; captured_str!(s) => s.to_owned()); - - rule!(http<URL>; children!( - [http_raw(url)] => url, - [http_raw(url), import_hashed(ih)] => - URL { headers: Some(Box::new(ih)), ..url }, - )); - - rule!(env<String>; children!( - [bash_environment_variable(s)] => s, - [posix_environment_variable(s)] => s, - )); - rule!(bash_environment_variable<String>; captured_str!(s) => s.to_owned()); - rule!(posix_environment_variable<String>; children!( - [posix_environment_variable_character(chars)..] => { - chars.collect() - }, - )); - rule!(posix_environment_variable_character<Cow<'a, str>>; - captured_str!(s) => { - match s { - "\\\"" => Cow::Owned("\"".to_owned()), - "\\\\" => Cow::Owned("\\".to_owned()), - "\\a" => Cow::Owned("\u{0007}".to_owned()), - "\\b" => Cow::Owned("\u{0008}".to_owned()), - "\\f" => Cow::Owned("\u{000C}".to_owned()), - "\\n" => Cow::Owned("\n".to_owned()), - "\\r" => Cow::Owned("\r".to_owned()), - "\\t" => Cow::Owned("\t".to_owned()), - "\\v" => Cow::Owned("\u{000B}".to_owned()), - _ => Cow::Borrowed(s) - } - } - ); - - token_rule!(missing<()>); - - rule!(import_type<ImportLocation>; children!( - [missing(_)] => { - ImportLocation::Missing - }, - [env(e)] => { - ImportLocation::Env(e) - }, - [http(url)] => { - ImportLocation::Remote(url) - }, - [local((prefix, p))] => { - ImportLocation::Local(prefix, p) - }, - )); - - rule!(hash<Hash>; captured_str!(s) => - Hash { - protocol: s.trim()[..6].to_owned(), - hash: s.trim()[7..].to_owned(), - } - ); - - rule!(import_hashed<ImportHashed>; children!( - [import_type(location)] => - ImportHashed { location, hash: None }, - [import_type(location), hash(h)] => - ImportHashed { location, hash: Some(h) }, - )); - - token_rule!(Text<()>); - - rule!(import<ParsedExpr<'a>> as expression; span; children!( - [import_hashed(location_hashed)] => { - spanned(span, Embed(Import { - mode: ImportMode::Code, - location_hashed - })) - }, - [import_hashed(location_hashed), Text(_)] => { - spanned(span, Embed(Import { - mode: ImportMode::RawText, - location_hashed - })) - }, - )); - - token_rule!(lambda<()>); - token_rule!(forall<()>); - token_rule!(arrow<()>); - token_rule!(merge<()>); - token_rule!(if_<()>); - token_rule!(in_<()>); - - rule!(expression<ParsedExpr<'a>> as expression; span; children!( - [lambda(()), label(l), expression(typ), - arrow(()), expression(body)] => { - spanned(span, Lam(l, rc(typ), rc(body))) - }, - [if_(()), expression(cond), expression(left), expression(right)] => { - spanned(span, BoolIf(rc(cond), rc(left), rc(right))) - }, - [let_binding(bindings).., in_(()), expression(final_expr)] => { - bindings.rev().fold( - final_expr, - |acc, x| Let(x.0, x.1, x.2, rc(acc)) - ) - }, - [forall(()), label(l), expression(typ), - arrow(()), expression(body)] => { - spanned(span, Pi(l, rc(typ), rc(body))) - }, - [expression(typ), arrow(()), expression(body)] => { - spanned(span, Pi("_".into(), rc(typ), rc(body))) - }, - [merge(()), expression(x), expression(y), expression(z)] => { - spanned(span, Merge(rc(x), rc(y), Some(rc(z)))) - }, - [expression(e)] => e, - )); - - rule!(let_binding<(Label, Option<ParsedSubExpr<'a>>, ParsedSubExpr<'a>)>; - children!( - [label(name), expression(annot), expression(expr)] => - (name, Some(rc(annot)), rc(expr)), - [label(name), expression(expr)] => - (name, None, rc(expr)), - )); - - token_rule!(List<()>); - token_rule!(Optional<()>); - - rule!(empty_collection<ParsedExpr<'a>> as expression; span; children!( - [List(_), expression(t)] => { - spanned(span, EmptyListLit(rc(t))) - }, - [Optional(_), expression(t)] => { - spanned(span, OldOptionalLit(None, rc(t))) - }, - )); - - rule!(non_empty_optional<ParsedExpr<'a>> as expression; span; children!( - [expression(x), Optional(_), expression(t)] => { - spanned(span, OldOptionalLit(Some(rc(x)), rc(t))) - } - )); - - rule!(import_alt_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::ImportAlt; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(or_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolOr; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(plus_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::NaturalPlus; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(text_append_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::TextAppend; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(list_append_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::ListAppend; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(and_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolAnd; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(combine_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::Combine; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(prefer_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::Prefer; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(combine_types_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::CombineTypes; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(times_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::NaturalTimes; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(equal_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolEQ; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - rule!(not_equal_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - let o = crate::BinOp::BoolNE; - rest.fold(first, |acc, e| BinOp(o, rc(acc), rc(e))) - }, - )); - - rule!(annotated_expression<ParsedExpr<'a>> as expression; span; children!( - [expression(e)] => e, - [expression(e), expression(annot)] => { - spanned(span, Annot(rc(e), rc(annot))) - }, - )); - - token_rule!(Some_<()>); - - rule!(application_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), expression(rest)..] => { - rest.fold(first, |acc, e| App(rc(acc), rc(e))) - }, - )); - - rule!(first_application_expression<ParsedExpr<'a>> as expression; span; - children!( - [expression(e)] => e, - [Some_(()), expression(e)] => { - spanned(span, SomeLit(rc(e))) - }, - [merge(()), expression(x), expression(y)] => { - spanned(span, Merge(rc(x), rc(y), None)) - }, - )); - - rule!(selector_expression<ParsedExpr<'a>> as expression; children!( - [expression(e)] => e, - [expression(first), selector(rest)..] => { - rest.fold(first, |acc, e| match e { - Either::Left(l) => Field(rc(acc), l), - Either::Right(ls) => Projection(rc(acc), ls), - }) - } - )); - - rule!(selector<Either<Label, Vec<Label>>>; children!( - [label(l)] => Either::Left(l), - [labels(ls)] => Either::Right(ls), - )); - - rule!(labels<Vec<Label>>; children!( - [label(ls)..] => ls.collect(), - )); - - rule!(primitive_expression<ParsedExpr<'a>> as expression; span; children!( - [double_literal(n)] => spanned(span, DoubleLit(n)), - [natural_literal(n)] => spanned(span, NaturalLit(n)), - [integer_literal(n)] => spanned(span, IntegerLit(n)), - [double_quote_literal(s)] => spanned(span, TextLit(s)), - [single_quote_literal(s)] => spanned(span, TextLit(s)), - [expression(e)] => e, - )); - - rule!(empty_record_literal<ParsedExpr<'a>> as expression; span; - captured_str!(_) => spanned(span, RecordLit(BTreeMap::new())) - ); - - rule!(empty_record_type<ParsedExpr<'a>> as expression; span; - captured_str!(_) => spanned(span, RecordType(BTreeMap::new())) - ); - - rule!(non_empty_record_type_or_literal<ParsedExpr<'a>> as expression; span; - children!( - [label(first_label), non_empty_record_type(rest)] => { - let (first_expr, mut map) = rest; - map.insert(first_label, rc(first_expr)); - spanned(span, RecordType(map)) - }, - [label(first_label), non_empty_record_literal(rest)] => { - let (first_expr, mut map) = rest; - map.insert(first_label, rc(first_expr)); - spanned(span, RecordLit(map)) - }, - )); - - rule!(non_empty_record_type - <(ParsedExpr<'a>, BTreeMap<Label, ParsedSubExpr<'a>>)>; children!( - [expression(expr), record_type_entry(entries)..] => { - (expr, entries.collect()) - } - )); - - rule!(record_type_entry<(Label, ParsedSubExpr<'a>)>; children!( - [label(name), expression(expr)] => (name, rc(expr)) - )); - - rule!(non_empty_record_literal - <(ParsedExpr<'a>, BTreeMap<Label, ParsedSubExpr<'a>>)>; children!( - [expression(expr), record_literal_entry(entries)..] => { - (expr, entries.collect()) - } - )); - - rule!(record_literal_entry<(Label, ParsedSubExpr<'a>)>; children!( - [label(name), expression(expr)] => (name, rc(expr)) - )); - - rule!(union_type_or_literal<ParsedExpr<'a>> as expression; span; children!( - [empty_union_type(_)] => { - spanned(span, UnionType(BTreeMap::new())) - }, - [non_empty_union_type_or_literal((Some((l, e)), entries))] => { - spanned(span, UnionLit(l, e, entries)) - }, - [non_empty_union_type_or_literal((None, entries))] => { - spanned(span, UnionType(entries)) - }, - )); - - token_rule!(empty_union_type<()>); - - rule!(non_empty_union_type_or_literal - <(Option<(Label, ParsedSubExpr<'a>)>, - BTreeMap<Label, Option<ParsedSubExpr<'a>>>)>; - children!( - [label(l), union_literal_variant_value((e, entries))] => { - (Some((l, e)), entries) - }, - [label(l), union_type_or_literal_variant_type((e, rest))] => { - let (x, mut entries) = rest; - entries.insert(l, e); - (x, entries) - }, - )); - - rule!(union_literal_variant_value - <(ParsedSubExpr<'a>, BTreeMap<Label, Option<ParsedSubExpr<'a>>>)>; - children!( - [expression(e), union_type_entry(entries)..] => { - (rc(e), entries.collect()) - }, - )); - - rule!(union_type_entry<(Label, Option<ParsedSubExpr<'a>>)>; children!( - [label(name), expression(expr)] => (name, Some(rc(expr))), - [label(name)] => (name, None), - )); - - // TODO: unary union variants - rule!(union_type_or_literal_variant_type - <(Option<ParsedSubExpr<'a>>, - (Option<(Label, ParsedSubExpr<'a>)>, - BTreeMap<Label, Option<ParsedSubExpr<'a>>>))>; - children!( - [expression(e), non_empty_union_type_or_literal(rest)] => { - (Some(rc(e)), rest) - }, - [expression(e)] => { - (Some(rc(e)), (None, BTreeMap::new())) - }, - [non_empty_union_type_or_literal(rest)] => { - (None, rest) - }, - [] => { - (None, (None, BTreeMap::new())) - }, - )); - - rule!(non_empty_list_literal<ParsedExpr<'a>> as expression; span; - children!( - [expression(items)..] => spanned( - span, - NEListLit(items.map(rc).collect()) - ) - )); - - rule!(final_expression<ParsedExpr<'a>> as expression; children!( - [expression(e), EOI(_eoi)] => e - )); -} - -pub fn parse_expr<'a>(s: &'a str) -> ParseResult<ParsedSubExpr<'a>> { - let mut pairs = DhallParser::parse(Rule::final_expression, s)?; - let expr = do_parse(pairs.next().unwrap())?; - assert_eq!(pairs.next(), None); - match expr { - ParsedValue::expression(e) => Ok(rc(e)), - _ => unreachable!(), - } - // Ok(rc(BoolLit(false))) -} - -#[test] -fn test_parse() { - // let expr = r#"{ x = "foo", y = 4 }.x"#; - // let expr = r#"(1 + 2) * 3"#; - let expr = r#"(1) + 3 * 5"#; - println!("{:?}", parse_expr(expr)); - match parse_expr(expr) { - Err(e) => { - println!("{:?}", e); - println!("{}", e); - } - ok => println!("{:?}", ok), - }; - // assert!(false); -} |