From 1baef509afe52ab285e73469fc597de8f4e166b6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 1 Sep 2019 18:38:39 +0200 Subject: Change parser macros to use a function-like syntax This makes the parser code look much less magical. --- Cargo.lock | 7 + dhall_proc_macros/src/lib.rs | 8 + dhall_proc_macros/src/parser.rs | 494 ++++++------ dhall_syntax/Cargo.toml | 1 + dhall_syntax/src/lib.rs | 1 + dhall_syntax/src/parser.rs | 1675 ++++++++++++++++++++++----------------- 6 files changed, 1188 insertions(+), 998 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 360168a..976b3c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pest 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -181,6 +182,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "maplit" version = "1.0.1" @@ -489,6 +495,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" diff --git a/dhall_proc_macros/src/lib.rs b/dhall_proc_macros/src/lib.rs index 37c9985..46d93e9 100644 --- a/dhall_proc_macros/src/lib.rs +++ b/dhall_proc_macros/src/lib.rs @@ -22,3 +22,11 @@ pub fn make_parser(input: TokenStream) -> TokenStream { Err(err) => err.to_compile_error(), }) } + +#[proc_macro] +pub fn parse_children(input: TokenStream) -> TokenStream { + TokenStream::from(match parser::parse_children(input) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error(), + }) +} diff --git a/dhall_proc_macros/src/parser.rs b/dhall_proc_macros/src/parser.rs index bb4e894..2618bec 100644 --- a/dhall_proc_macros/src/parser.rs +++ b/dhall_proc_macros/src/parser.rs @@ -2,7 +2,11 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::parse::{Parse, ParseStream, Result}; use syn::punctuated::Punctuated; -use syn::{bracketed, parenthesized, token, Expr, Ident, Pat, Token, Type}; +use syn::spanned::Spanned; +use syn::{ + bracketed, parenthesized, parse_quote, token, Error, Expr, Ident, ItemFn, + Pat, ReturnType, Token, Type, +}; mod rule_kw { syn::custom_keyword!(rule); @@ -16,73 +20,40 @@ struct Rules(Vec); #[derive(Debug, Clone)] struct Rule { - rule_token: rule_kw::rule, - bang_token: Token![!], - paren_token: token::Paren, name: Ident, - lt_token: token::Lt, output_type: Type, - gt_token: token::Gt, contents: RuleContents, - semi_token: Token![;], } #[derive(Debug, Clone)] enum RuleContents { - Empty, - CapturedString { - span: Option, - captured_str_token: rule_kw::captured_str, - bang_token: Token![!], - paren_token: token::Paren, - pattern: Pat, - fat_arrow_token: Token![=>], - body: Expr, - }, - Children { - span: Option, - children_token: rule_kw::children, - bang_token: Token![!], - paren_token: token::Paren, - branches: Punctuated, - }, PrecClimb { - span: Option, - prec_climb_token: rule_kw::prec_climb, - bang_token: Token![!], - paren_token: token::Paren, child_rule: Ident, - comma_token: Token![,], climber: Expr, - comma_token2: Token![,], - pattern: Pat, - fat_arrow_token: Token![=>], - body: Expr, + function: ItemFn, + }, + Function { + function: ItemFn, }, } #[derive(Debug, Clone)] struct ChildrenBranch { - bracket_token: token::Bracket, - pattern_unparsed: TokenStream, + pattern_span: Span, pattern: Punctuated, - fat_arrow_token: Token![=>], body: Expr, } #[derive(Debug, Clone)] enum ChildrenBranchPatternItem { - Single { - rule_name: Ident, - paren_token: token::Paren, - binder: Pat, - }, - Multiple { - rule_name: Ident, - paren_token: token::Paren, - binder: Ident, - slice_token: Token![..], - }, + Single { rule_name: Ident, binder: Pat }, + Multiple { rule_name: Ident, binder: Ident }, +} + +#[derive(Debug, Clone)] +struct ParseChildrenInput { + input_expr: Expr, + branches: Punctuated, } impl Parse for Rules { @@ -97,73 +68,50 @@ impl Parse for Rules { impl Parse for Rule { fn parse(input: ParseStream) -> Result { - let contents; - Ok(Rule { - rule_token: input.parse()?, - bang_token: input.parse()?, - paren_token: parenthesized!(contents in input), - name: contents.parse()?, - lt_token: contents.parse()?, - output_type: contents.parse()?, - gt_token: contents.parse()?, - contents: contents.parse()?, - semi_token: input.parse()?, - }) - } -} + let function: ItemFn = input.parse()?; + let (recognized_attrs, remaining_attrs) = function + .attrs + .iter() + .cloned() + .partition::, _>(|attr| attr.path.is_ident("prec_climb")); + let function = ItemFn { + attrs: remaining_attrs, + ..(function.clone()) + }; -impl Parse for RuleContents { - fn parse(input: ParseStream) -> Result { - if input.is_empty() { - return Ok(RuleContents::Empty); - } - let _: Token![;] = input.parse()?; - let span = if input.peek(Ident) && input.peek2(Token![;]) { - let span: Ident = input.parse()?; - let _: Token![;] = input.parse()?; - Some(span) - } else { - None + let name = function.sig.ident.clone(); + let output_type = match &function.sig.output { + ReturnType::Default => parse_quote!(()), + ReturnType::Type(_, t) => (**t).clone(), }; - let lookahead = input.lookahead1(); - if lookahead.peek(rule_kw::captured_str) { - let contents; - Ok(RuleContents::CapturedString { - span, - captured_str_token: input.parse()?, - bang_token: input.parse()?, - paren_token: parenthesized!(contents in input), - pattern: contents.parse()?, - fat_arrow_token: input.parse()?, - body: input.parse()?, - }) - } else if lookahead.peek(rule_kw::children) { - let contents; - Ok(RuleContents::Children { - span, - children_token: input.parse()?, - bang_token: input.parse()?, - paren_token: parenthesized!(contents in input), - branches: Punctuated::parse_terminated(&contents)?, - }) - } else if lookahead.peek(rule_kw::prec_climb) { - let contents; - Ok(RuleContents::PrecClimb { - span, - prec_climb_token: input.parse()?, - bang_token: input.parse()?, - paren_token: parenthesized!(contents in input), - child_rule: contents.parse()?, - comma_token: contents.parse()?, - climber: contents.parse()?, - comma_token2: contents.parse()?, - pattern: contents.parse()?, - fat_arrow_token: contents.parse()?, - body: contents.parse()?, + if recognized_attrs.is_empty() { + Ok(Rule { + name, + output_type, + contents: RuleContents::Function { function }, }) + } else if recognized_attrs.len() != 1 { + Err(input.error("expected a prec_climb attribute")) } else { - Err(lookahead.error()) + let attr = recognized_attrs.into_iter().next().unwrap(); + let (child_rule, climber) = + attr.parse_args_with(|input: ParseStream| { + let child_rule: Ident = input.parse()?; + let _: Token![,] = input.parse()?; + let climber: Expr = input.parse()?; + Ok((child_rule, climber)) + })?; + + Ok(Rule { + name, + output_type, + contents: RuleContents::PrecClimb { + child_rule, + climber, + function, + }, + }) } } } @@ -171,40 +119,52 @@ impl Parse for RuleContents { impl Parse for ChildrenBranch { fn parse(input: ParseStream) -> Result { let contents; + let _: token::Bracket = bracketed!(contents in input); + let pattern_unparsed: TokenStream = contents.fork().parse()?; + let pattern_span = pattern_unparsed.span(); + let pattern = Punctuated::parse_terminated(&contents)?; + let _: Token![=>] = input.parse()?; + let body = input.parse()?; + Ok(ChildrenBranch { - bracket_token: bracketed!(contents in input), - pattern_unparsed: contents.fork().parse()?, - pattern: Punctuated::parse_terminated(&contents)?, - fat_arrow_token: input.parse()?, - body: input.parse()?, + pattern_span, + pattern, + body, }) } } impl Parse for ChildrenBranchPatternItem { fn parse(input: ParseStream) -> Result { - let rule_name = input.parse()?; let contents; - let paren_token = parenthesized!(contents in input); + let rule_name = input.parse()?; + parenthesized!(contents in input); if input.peek(Token![..]) { - Ok(ChildrenBranchPatternItem::Multiple { - rule_name, - paren_token, - binder: contents.parse()?, - slice_token: input.parse()?, - }) + let binder = contents.parse()?; + let _: Token![..] = input.parse()?; + Ok(ChildrenBranchPatternItem::Multiple { rule_name, binder }) } else if input.is_empty() || input.peek(Token![,]) { - Ok(ChildrenBranchPatternItem::Single { - rule_name, - paren_token, - binder: contents.parse()?, - }) + let binder = contents.parse()?; + Ok(ChildrenBranchPatternItem::Single { rule_name, binder }) } else { Err(input.error("expected `..` or nothing")) } } } +impl Parse for ParseChildrenInput { + fn parse(input: ParseStream) -> Result { + let input_expr = input.parse()?; + let _: Token![;] = input.parse()?; + let branches = Punctuated::parse_terminated(input)?; + + Ok(ParseChildrenInput { + input_expr, + branches, + }) + } +} + fn make_construct_precclimbers(rules: &Rules) -> Result { let mut entries: Vec = Vec::new(); for rule in &rules.0 { @@ -233,11 +193,16 @@ fn make_entrypoints(rules: &Rules) -> Result { entries.push(quote!( #[allow(non_snake_case, dead_code)] fn #name<'a>( - input: Rc, + input_str: &str, pair: Pair<'a, Rule>, - ) -> ParseResult<#output_type> { + ) -> #output_type { let climbers = construct_precclimbers(); - Parsers::#name((&climbers, input), pair) + let input = ParseInput { + climbers: &climbers, + original_input_str: input_str.to_string().into(), + pair + }; + Parsers::#name(input) } )) } @@ -250,128 +215,37 @@ fn make_entrypoints(rules: &Rules) -> Result { )) } -fn make_parser_branch(branch: &ChildrenBranch) -> TokenStream { - let ChildrenBranch { - pattern, - body, - pattern_unparsed, - .. - } = branch; - let variable_pattern = Ident::new("variable_pattern", Span::call_site()); - let match_pat = pattern.iter().map(|item| match item { - ChildrenBranchPatternItem::Single { rule_name, .. } => { - quote!(Rule::#rule_name) - } - ChildrenBranchPatternItem::Multiple { .. } => { - quote!(#variable_pattern..) - } - }); - let match_filter = pattern.iter().map(|item| match item { - ChildrenBranchPatternItem::Single { .. } => quote!(true &&), - ChildrenBranchPatternItem::Multiple { rule_name, .. } => { - quote!(#variable_pattern.iter().all(|r| r == &Rule::#rule_name) &&) - } - }); - quote!( - [#(#match_pat),*] if #(#match_filter)* true => { - parse_children!((climbers, input.clone()), iter; - [#pattern_unparsed] => { - #[allow(unused_variables)] - let res: Result<_, String> = try { #body }; - res.map_err(|msg| - custom_parse_error(&pair, msg) - ) - } - ) - } - ) -} - -fn make_parser_expr(rule: &Rule) -> Result { - let name = &rule.name; - let expr = match &rule.contents { - RuleContents::Empty => quote!(Ok(())), - RuleContents::CapturedString { pattern, body, .. } => quote!( - let #pattern = pair.as_str(); - let res: Result<_, String> = try { #body }; - res.map_err(|msg| custom_parse_error(&pair, msg)) - ), - RuleContents::PrecClimb { - child_rule, - pattern, - body, - .. - } => quote!( - let climber = climbers.get(&Rule::#name).unwrap(); - climber.climb( - pair.clone().into_inner(), - |p| Parsers::#child_rule((climbers, input.clone()), p), - |l, op, r| { - let #pattern = (l?, op, r?); - let res: Result<_, String> = try { #body }; - res.map_err(|msg| custom_parse_error(&pair, msg)) - }, - ) - ), - RuleContents::Children { branches, .. } => { - let branches = branches.iter().map(make_parser_branch); - quote!( - let children_rules: Vec = pair - .clone() - .into_inner() - .map(|p| p.as_rule()) - .collect(); - - #[allow(unused_mut)] - let mut iter = pair.clone().into_inner(); - - #[allow(unreachable_code)] - match children_rules.as_slice() { - #(#branches,)* - [..] => Err(custom_parse_error( - &pair, - format!("Unexpected children: {:?}", children_rules) - )), - } - ) - } - }; - Ok(expr) -} - fn make_parsers(rules: &Rules) -> Result { - let mut entries: Vec = Vec::new(); - for rule in &rules.0 { - let span_def = match &rule.contents { - RuleContents::CapturedString { - span: Some(span), .. - } - | RuleContents::Children { - span: Some(span), .. - } - | RuleContents::PrecClimb { - span: Some(span), .. - } => Some(quote!( - let #span = Span::make(input.clone(), pair.as_span()); - )), - _ => None, - }; - + let entries = rules.0.iter().map(|rule| { let name = &rule.name; let output_type = &rule.output_type; - let expr = make_parser_expr(rule)?; - - entries.push(quote!( - #[allow(non_snake_case, dead_code)] - fn #name<'a>( - (climbers, input): (&HashMap>, Rc), - pair: Pair<'a, Rule>, - ) -> ParseResult<#output_type> { - #span_def - #expr - } - )) - } + match &rule.contents { + RuleContents::PrecClimb { + child_rule, + function, + .. + } => quote!( + #[allow(non_snake_case, dead_code)] + fn #name<'a, 'climbers>( + input: ParseInput<'a, 'climbers, Rule>, + ) -> #output_type { + #function + let climber = input.climbers.get(&Rule::#name).unwrap(); + climber.climb( + input.pair.clone().into_inner(), + |p| Parsers::#child_rule(input.with_pair(p)), + |l, op, r| { + #name(input.clone(), l?, op, r?) + }, + ) + } + ), + RuleContents::Function { function } => quote!( + #[allow(non_snake_case, dead_code)] + #function + ), + } + }); Ok(quote!( struct Parsers; @@ -384,7 +258,7 @@ fn make_parsers(rules: &Rules) -> Result { pub fn make_parser( input: proc_macro::TokenStream, ) -> Result { - let rules: Rules = syn::parse_macro_input::parse(input.clone())?; + let rules: Rules = syn::parse(input.clone())?; let construct_precclimbers = make_construct_precclimbers(&rules)?; let entrypoints = make_entrypoints(&rules)?; @@ -396,3 +270,123 @@ pub fn make_parser( #parsers )) } + +fn make_parser_branch(branch: &ChildrenBranch) -> Result { + use ChildrenBranchPatternItem::{Multiple, Single}; + + let body = &branch.body; + + // Convert the input pattern into a pattern-match on the Rules of the children. This uses + // slice_patterns. + // A single pattern just checks that the rule matches; a variable-length pattern binds the + // subslice and checks that they all match the chosen Rule in the `if`-condition. + let variable_pattern_ident = + Ident::new("variable_pattern", Span::call_site()); + let match_pat = branch.pattern.iter().map(|item| match item { + Single { rule_name, .. } => quote!(Rule::#rule_name), + Multiple { .. } => quote!(#variable_pattern_ident..), + }); + let match_filter = branch.pattern.iter().map(|item| match item { + Single { .. } => quote!(), + Multiple { rule_name, .. } => quote!( + #variable_pattern_ident.iter().all(|r| r == &Rule::#rule_name) && + ), + }); + + // Once we have found a branch that matches, we need to parse the children. + let mut singles_before_multiple = Vec::new(); + let mut multiple = None; + let mut singles_after_multiple = Vec::new(); + for item in &branch.pattern { + match item { + Single { + rule_name, binder, .. + } => { + if multiple.is_none() { + singles_before_multiple.push((rule_name, binder)) + } else { + singles_after_multiple.push((rule_name, binder)) + } + } + Multiple { + rule_name, binder, .. + } => { + if multiple.is_none() { + multiple = Some((rule_name, binder)) + } else { + return Err(Error::new( + branch.pattern_span.clone(), + "multiple variable-length patterns are not allowed", + )); + } + } + } + } + let mut parses = Vec::new(); + for (rule_name, binder) in singles_before_multiple.into_iter() { + parses.push(quote!( + let #binder = Parsers::#rule_name( + inputs.next().unwrap() + )?; + )) + } + // Note the `rev()`: we are taking inputs from the end of the iterator in reverse order, so that + // only the unmatched inputs are left for the variable-length pattern, if any. + for (rule_name, binder) in singles_after_multiple.into_iter().rev() { + parses.push(quote!( + let #binder = Parsers::#rule_name( + inputs.next_back().unwrap() + )?; + )) + } + if let Some((rule_name, binder)) = multiple { + parses.push(quote!( + let #binder = inputs + .map(|i| Parsers::#rule_name(i)) + .collect::, _>>()? + .into_iter(); + )) + } + + Ok(quote!( + [#(#match_pat),*] if #(#match_filter)* true => { + #(#parses)* + #body + } + )) +} + +pub fn parse_children( + input: proc_macro::TokenStream, +) -> Result { + let input: ParseChildrenInput = syn::parse(input)?; + + let input_expr = &input.input_expr; + let branches = input + .branches + .iter() + .map(make_parser_branch) + .collect::>>()?; + Ok(quote!({ + let children_rules: Vec = #input_expr.pair + .clone() + .into_inner() + .map(|p| p.as_rule()) + .collect(); + + #[allow(unused_mut)] + let mut inputs = #input_expr + .pair + .clone() + .into_inner() + .map(|p| #input_expr.with_pair(p)); + + #[allow(unreachable_code)] + match children_rules.as_slice() { + #(#branches,)* + [..] => return Err(#input_expr.error( + format!("Unexpected children: {:?}", children_rules) + )), + } + })) +} diff --git a/dhall_syntax/Cargo.toml b/dhall_syntax/Cargo.toml index 62ecced..eb492d0 100644 --- a/dhall_syntax/Cargo.toml +++ b/dhall_syntax/Cargo.toml @@ -15,5 +15,6 @@ pest = "2.1" either = "1.5.2" take_mut = "0.2.2" hex = "0.3.2" +lazy_static = "1.4.0" dhall_generated_parser = { path = "../dhall_generated_parser" } dhall_proc_macros = { path = "../dhall_proc_macros" } diff --git a/dhall_syntax/src/lib.rs b/dhall_syntax/src/lib.rs index e4a6077..290f53c 100644 --- a/dhall_syntax/src/lib.rs +++ b/dhall_syntax/src/lib.rs @@ -3,6 +3,7 @@ #![feature(try_blocks)] #![feature(never_type)] #![feature(bind_by_move_pattern_guards)] +#![feature(proc_macro_hygiene)] #![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 4fd6f57..fa2d7c5 100644 --- a/dhall_syntax/src/parser.rs +++ b/dhall_syntax/src/parser.rs @@ -8,6 +8,7 @@ use std::collections::HashMap; 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::*; @@ -27,55 +28,39 @@ pub type ParseError = pest::error::Error; pub type ParseResult = Result; -#[derive(Debug)] -enum Either { - Left(A), - Right(B), +#[derive(Debug, Clone)] +struct ParseInput<'input, 'climbers, Rule> +where + Rule: std::fmt::Debug + Copy + std::hash::Hash + Ord, +{ + pair: Pair<'input, Rule>, + climbers: &'climbers HashMap>, + original_input_str: Rc, } -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, +impl<'input, 'climbers> ParseInput<'input, 'climbers, Rule> { + fn error(&self, message: String) -> ParseError { + let message = format!( + "{} while matching on:\n{}", + message, + debug_pair(self.pair.clone()) + ); + let e = pest::error::ErrorVariant::CustomError { message }; + pest::error::Error::new_from_span(e, self.pair.as_span()) + } + fn with_pair(&self, new_pair: Pair<'input, Rule>) -> Self { + ParseInput { + pair: new_pair, + climbers: self.climbers, + original_input_str: self.original_input_str.clone(), } } -} - -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 as_span(&self) -> Span { + Span::make(self.original_input_str.clone(), self.pair.as_span()) + } + fn as_str(&self) -> &'input str { + self.pair.as_str() + } } fn debug_pair(pair: Pair) -> String { @@ -119,74 +104,48 @@ fn debug_pair(pair: Pair) -> String { s } -macro_rules! parse_children { - // Variable length pattern with a common unary variant - (@match_forwards, - $parse_args:expr, - $iter:expr, - ($body:expr), - $variant:ident ($x:ident).., - $($rest:tt)* - ) => { - parse_children!(@match_backwards, - $parse_args, $iter, - ({ - let $x = $iter - .map(|x| Parsers::$variant($parse_args, x)) - .collect::, _>>()? - .into_iter(); - $body - }), - $($rest)* - ) - }; - // Single item pattern - (@match_forwards, - $parse_args:expr, - $iter:expr, - ($body:expr), - $variant:ident ($x:pat), - $($rest:tt)* - ) => {{ - let p = $iter.next().unwrap(); - let $x = Parsers::$variant($parse_args, p)?; - parse_children!(@match_forwards, - $parse_args, $iter, - ($body), - $($rest)* - ) - }}; - // Single item pattern after a variable length one: declare reversed and take from the end - (@match_backwards, - $parse_args:expr, - $iter:expr, - ($body:expr), - $variant:ident ($x:pat), - $($rest:tt)* - ) => { - parse_children!(@match_backwards, $parse_args, $iter, ({ - let p = $iter.next_back().unwrap(); - let $x = Parsers::$variant($parse_args, p)?; - $body - }), $($rest)*) - }; - - // Check no elements remain - (@match_forwards, $parse_args:expr, $iter:expr, ($body:expr) $(,)*) => { - $body - }; - // After a variable length pattern, everything has already been consumed - (@match_backwards, $parse_args:expr, $iter:expr, ($body:expr) $(,)*) => { - $body - }; +#[derive(Debug)] +enum Either { + Left(A), + Right(B), +} - ($parse_args:expr, $iter:expr; [$($args:tt)*] => $body:expr) => { - parse_children!(@match_forwards, - $parse_args, $iter, - ($body), - $($args)*, - ) - }; +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, + } + } } // Trim the shared indent off of a vec of lines, as defined by the Dhall semantics of multiline @@ -230,674 +189,894 @@ fn trim_indent(lines: &mut Vec) { } } -dhall_proc_macros::make_parser! { - rule!(EOI<()>); - - rule!(simple_label