From 640a36906361ef5ef98fb66fd37246f084739d25 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2019 00:44:27 +0100 Subject: Handle simple parsing cases with new macros --- dhall/tests/common/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'dhall/tests/common') diff --git a/dhall/tests/common/mod.rs b/dhall/tests/common/mod.rs index 7ba64b0..2ee9724 100644 --- a/dhall/tests/common/mod.rs +++ b/dhall/tests/common/mod.rs @@ -32,7 +32,7 @@ macro_rules! make_spec_test { // The parser stack overflows even on small files // when compiled without optimizations thread::Builder::new() - .stack_size(4 * 1024 * 1024) + .stack_size(8 * 1024 * 1024) .spawn(move || { run_test($path, Feature::$type); }) -- cgit v1.3.1 From 5745299a328462f0a60e56a9d11cbc60a0e4380d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2019 20:24:58 +0100 Subject: Cleanup parser code --- dhall/tests/common/mod.rs | 2 +- dhall_core/src/lib.rs | 1 - dhall_core/src/parser.rs | 173 +++++++++++++--------------------------------- 3 files changed, 50 insertions(+), 126 deletions(-) (limited to 'dhall/tests/common') diff --git a/dhall/tests/common/mod.rs b/dhall/tests/common/mod.rs index 2ee9724..a635fb1 100644 --- a/dhall/tests/common/mod.rs +++ b/dhall/tests/common/mod.rs @@ -32,7 +32,7 @@ macro_rules! make_spec_test { // The parser stack overflows even on small files // when compiled without optimizations thread::Builder::new() - .stack_size(8 * 1024 * 1024) + .stack_size(16 * 1024 * 1024) .spawn(move || { run_test($path, Feature::$type); }) diff --git a/dhall_core/src/lib.rs b/dhall_core/src/lib.rs index 508ad3a..6215456 100644 --- a/dhall_core/src/lib.rs +++ b/dhall_core/src/lib.rs @@ -1,6 +1,5 @@ #![feature(trace_macros)] #![feature(slice_patterns)] -#![recursion_limit = "128"] #![allow( clippy::many_single_char_names, clippy::should_implement_trait, diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs index 30e3367..cbba8ca 100644 --- a/dhall_core/src/parser.rs +++ b/dhall_core/src/parser.rs @@ -107,7 +107,7 @@ macro_rules! match_pair { { let pair = $pair.clone(); let rule = pair.as_rule(); - let parsed: Vec<_> = pair.clone().into_inner().map(ParseWrapped::parse_any_fast).collect::>()?; + let parsed: Vec<_> = pair.clone().into_inner().map(parse_any).collect::>()?; #[allow(unreachable_code)] iter_patterns::match_vec!(parsed; $($acc)* @@ -119,48 +119,32 @@ macro_rules! match_pair { ($pair:expr; $( $submac:ident!($($args:tt)*) => $body:expr ),* $(,)*) => { match_pair!(@make_matches, $pair, (), $( $submac!($($args)*) => $body ),* ,) }; - ($pair:expr; $($args:tt)*) => { - { - let pair = $pair; - let result = match_iter_branching!(@noclone, match_pair; pair; $($args)*); - result.map_err(|e| match e { - IterMatchError::Other(e) => e, - _ => custom_parse_error(&pair, "No match found".to_owned()), - }) - } - }; } macro_rules! make_parser { - (@branch_rules, $pair:expr, ($($acc:tt)*), rule!( $name:ident<$o:ty>; $($args:tt)* ); $($rest:tt)*) => ( - make_parser!(@branch_rules, $pair, ($($acc)* Rule::$name => { - ParseWrapped::$name($pair) - },), $($rest)*) - ); - (@branch_rules, $pair:expr, ($($acc:tt)*), rule_in_group!( $name:ident<$o:ty>; $group:ident; $($args:tt)* ); $($rest:tt)*) => ( - make_parser!(@branch_rules, $pair, ($($acc)* Rule::$name => { - ParseWrapped::$name($pair).map(|x| x.$name()).map(ParsedValue::$group) - },), $($rest)*) - ); - (@branch_rules, $pair:expr, ($($acc:tt)*), binop!( $name:ident<$o:ty>; $op:ident ); $($rest:tt)*) => ( - make_parser!(@branch_rules, $pair, ($($acc)* Rule::$name => { - parse_binop($pair, BinOp::$op).map(ParsedValue::expression) - },), $($rest)*) + // Filter out definitions that should not be matched on + (@filter, rule) => (true); + (@filter, rule_in_group) => (true); + (@filter, binop) => (true); + (@filter, rule_group) => (false); + + (@body, $pair:expr, rule!( $name:ident<$o:ty>; $($args:tt)* )) => ( { + let res: $o = match_pair!($pair; $($args)*)?; + Ok(ParsedValue::$name(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, binop!( $name:ident<$o:ty>; $op:ident )) => ( { + parse_binop($pair, BinOp::$op) + }); + (@body, $pair:expr, rule_group!( $name:ident<$o:ty> )) => ( + unreachable!() ); - // (@branch_rules, $pair:expr, ($($acc:tt)*), rule_group!( $name:ident<$o:ty>; $($ty:ident),* ); $($rest:tt)*) => ( - // make_parser!(@branch_rules, $pair, ($($acc)* $( Rule::$ty => ParseUnwrapped::$ty($pair).map(ParsedValue::$name),)* ), $($rest)*) - // ); - (@branch_rules, $pair:expr, ($($acc:tt)*), $submac:ident!( $name:ident<$o:ty>; $($args:tt)* ); $($rest:tt)*) => ( - make_parser!(@branch_rules, $pair, ($($acc)*), $($rest)*) - ); - (@branch_rules, $pair:expr, ($($acc:tt)*),) => ( - #[allow(unreachable_patterns)] - match $pair.as_rule() { - $($acc)* - r => Err(custom_parse_error(&$pair, format!("parse_any_fast: Unexpected {:?}", r))), - } - ); - ($( $submac:ident!( $name:ident<$o:ty>; $($args:tt)* ); )*) => ( + + + ($( $submac:ident!( $name:ident<$o:ty> $($args:tt)* ); )*) => ( // #[allow(non_camel_case_types, dead_code)] // enum ParsedType { // $( $name, )* @@ -182,8 +166,6 @@ macro_rules! make_parser { // // } // // } // } - struct ParseWrapped; - struct ParseUnwrapped; #[allow(non_camel_case_types, dead_code)] #[derive(Debug)] @@ -203,71 +185,22 @@ macro_rules! make_parser { )* } - impl ParseWrapped { - #[allow(non_snake_case, dead_code)] - fn parse_any_fast(pair: Pair) -> ParseResult { - make_parser!(@branch_rules, pair, (), $( $submac!( $name<$o>; $($args)* ); )*) + #[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))), } } - - impl ParseUnwrapped { - #[allow(unused_variables, non_snake_case, dead_code, clippy::all)] - fn expression<'a>(pair: Pair<'a, Rule>) -> ParseResult { - ParseWrapped::expression(pair).map(|x| x.expression()) - } - } - impl ParseWrapped { - #[allow(unused_variables, non_snake_case, dead_code, clippy::all)] - fn expression<'a>(pair: Pair<'a, Rule>) -> ParseResult> { - ParseWrapped::parse_any_fast(pair) - } - } - - // fn do_the_parse(s: &str, r: Rule, ty: ParsedType) -> ParseResult { - // let pairs = DhallParser::parse(r, s)?; - // match_iter!(pairs; (e) => ty.parse(e)) - // } - - $( - $submac!($name<$o>; $($args)*); - )* ); } -macro_rules! make_pest_parse_function { - ($name:ident<$o:ty>; $submac:ident!( $($args:tt)* )) => ( - impl ParseUnwrapped { - #[allow(unused_variables, non_snake_case, dead_code, clippy::all)] - fn $name<'a>(pair: Pair<'a, Rule>) -> ParseResult<$o> { - ParseWrapped::$name(pair).map(|x| x.$name()) - } - } - impl ParseWrapped { - #[allow(unused_variables, non_snake_case, dead_code, clippy::all)] - fn $name<'a>(pair: Pair<'a, Rule>) -> ParseResult> { - let res: ParseResult<$o> = $submac!(pair; $($args)*); - Ok(ParsedValue::$name(res?)) - } - } - ); -} - -macro_rules! rule { - ($name:ident<$o:ty>; $($args:tt)*) => ( - make_pest_parse_function!($name<$o>; match_pair!( $($args)* )); - ); -} - -macro_rules! rule_in_group { - ($name:ident<$o:ty>; $group:ident; $($args:tt)*) => ( - make_pest_parse_function!($name<$o>; match_pair!( $($args)* )); - ); -} - -macro_rules! rule_group { - ($name:ident<$o:ty>; $($ty:ident),*) => (); -} - // List of rules that can be shortcutted as implemented in parse_binop fn can_be_shortcutted(rule: Rule) -> bool { use Rule::*; @@ -291,21 +224,25 @@ fn can_be_shortcutted(rule: Rule) -> bool { } } -fn parse_binop(pair: Pair, o: BinOp) -> ParseResult { +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(ParseUnwrapped::expression).collect::>()?; + 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 = ParseUnwrapped::expression(first)?; - Ok(rest.into_iter().fold(first, |acc, e| bx(Expr::BinOp(o, acc, e)))) + let first = parse_any(first)?.expression(); + Ok(ParsedValue::expression( + 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. - // println!("short-cutting {}", debug_pair(pair.clone())); pair = first; while can_be_shortcutted(pair.as_rule()) { let mut pairs = pair.clone().into_inner(); @@ -316,22 +253,10 @@ fn parse_binop(pair: Pair, o: BinOp) -> ParseResult { } pair = first; } - // println!("short-cutted {}", debug_pair(pair.clone())); - // println!(); - Ok(ParseUnwrapped::expression(pair)?) + parse_any(pair) } } -macro_rules! binop { - ($rule:ident<$ty:ty>; $op:ident) => { - rule!($rule<$ty>; - raw_pair!(pair) => { - parse_binop(pair, BinOp::$op)? - } - ); - }; -} - make_parser! { rule!(EOI<()>; children!() => ()); @@ -450,7 +375,7 @@ rule!(path; captured_str!(s) => (".".to_owned() + s).into() ); -rule_group!(local_raw<(FilePrefix, PathBuf)>;); +rule_group!(local_raw<(FilePrefix, PathBuf)>); rule_in_group!(parent_path<(FilePrefix, PathBuf)>; local_raw; children!(p: path) => (FilePrefix::Parent, p) @@ -492,7 +417,7 @@ rule!(import_hashed_raw<(ImportLocation, Option<()>)>; } ); -rule_group!(expression;); +rule_group!(expression); rule_in_group!(import_raw; expression; // TODO: handle "as Text" @@ -746,11 +671,11 @@ pub fn parse_expr(s: &str) -> ParseResult { let pairs = DhallParser::parse(Rule::final_expression, s)?; // Match the only item in the pairs iterator // println!("{}", debug_pair(pairs.clone().next().unwrap())); - let expr = iter_patterns::destructure_iter!(pairs; [p] => ParseUnwrapped::expression(p)) + let expr = iter_patterns::destructure_iter!(pairs; [p] => parse_any(p)) .unwrap()?; // expr.expression() - // Ok(expr.expression()) - Ok(expr) + Ok(expr.expression()) + // Ok(expr) // Ok(bx(Expr::BoolLit(false))) } -- cgit v1.3.1 From d6cbee16586be9013bebfa8dc9e7aa0a31c8c55f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 20 Mar 2019 22:46:07 +0100 Subject: Make parser implementation non-recursive --- dhall/tests/common/mod.rs | 17 +--- dhall_core/src/parser.rs | 231 +++++++++++++++++++++++++--------------------- 2 files changed, 129 insertions(+), 119 deletions(-) (limited to 'dhall/tests/common') diff --git a/dhall/tests/common/mod.rs b/dhall/tests/common/mod.rs index a635fb1..75aee38 100644 --- a/dhall/tests/common/mod.rs +++ b/dhall/tests/common/mod.rs @@ -24,22 +24,7 @@ macro_rules! make_spec_test { #[allow(non_snake_case)] fn $name() { use crate::common::*; - - if cfg!(feature = "nothreads") { - run_test($path, Feature::$type); - } else { - use std::thread; - // The parser stack overflows even on small files - // when compiled without optimizations - thread::Builder::new() - .stack_size(16 * 1024 * 1024) - .spawn(move || { - run_test($path, Feature::$type); - }) - .unwrap() - .join() - .unwrap(); - } + run_test($path, Feature::$type); } }; } diff --git a/dhall_core/src/parser.rs b/dhall_core/src/parser.rs index 8b14da0..34809b5 100644 --- a/dhall_core/src/parser.rs +++ b/dhall_core/src/parser.rs @@ -72,40 +72,39 @@ fn debug_pair(pair: Pair) -> String { } macro_rules! match_pair { - (@make_child_match, $pair:expr, ($($outer_acc:tt)*), ($($acc:tt)*), ($(,)* $ty:ident ($x:ident..) $($rest_of_match:tt)*) => $body:expr, $($rest:tt)*) => { - match_pair!(@make_child_match, $pair, ($($outer_acc)*), ($($acc)*, x..), ($($rest_of_match)*) => { + (@make_child_match, ($($vars:tt)*), ($($outer_acc:tt)*), ($($acc:tt)*), ($(,)* $ty:ident ($x:ident..) $($rest_of_match:tt)*) => $body:expr, $($rest:tt)*) => { + match_pair!(@make_child_match, ($($vars)*), ($($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)*), ($(,)* $ty:ident ($x:pat) $($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, ($($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, $pair:expr, ($($outer_acc:tt)*), (, $($acc:tt)*), ($(,)*) => $body:expr, $($rest:tt)*) => { - match_pair!(@make_matches, $pair, ([$($acc)*] => { $body }, $($outer_acc)*), $($rest)*) + (@make_child_match, ($($vars:tt)*), ($($outer_acc:tt)*), (, $($acc:tt)*), ($(,)*) => $body:expr, $($rest:tt)*) => { + match_pair!(@make_matches, ($($vars)*), ([$($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_child_match, ($($vars:tt)*), ($($outer_acc:tt)*), (), ($(,)*) => $body:expr, $($rest:tt)*) => { + match_pair!(@make_matches, ($($vars)*), ([] => { $body }, $($outer_acc)*), $($rest)*) }; - (@make_matches, $pair:expr, ($($acc:tt)*), [$($args:tt)*] => $body:expr, $($rest:tt)*) => { - match_pair!(@make_child_match, $pair, ($($acc)*), (), ($($args)*) => $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, ($($acc:tt)*) $(,)*) => { + (@make_matches, ($pair:expr, $parsed:expr), ($($acc:tt)*) $(,)*) => { { - let pair = $pair.clone(); + let pair = $pair; let rule = pair.as_rule(); - let parsed: Vec<_> = pair.clone().into_inner().map(parse_any).collect::>()?; #[allow(unreachable_code)] - iter_patterns::match_vec!(parsed; + 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; $( [$($args:tt)*] => $body:expr ),* $(,)*) => { - match_pair!(@make_matches, $pair, (), $( [$($args)*] => $body ),* ,) + (($($vars:tt)*); $( [$($args:tt)*] => $body:expr ),* $(,)*) => { + match_pair!(@make_matches, ($($vars)*), (), $( [$($args)*] => $body ),* ,) }; } @@ -115,24 +114,24 @@ macro_rules! make_parser { (@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, $parsed:expr, rule!( $name:ident<$o:ty>; $($args:tt)* )) => ( + make_parser!(@body, $pair, $parsed, 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 )) => ( { + (@body, $pair:expr, $parsed: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 )) => ( { + (@body, $pair:expr, $parsed: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; children!( $($args:tt)* ) )) => ( { - let res: $o = match_pair!($pair; $($args)*)?; + (@body, $pair:expr, $parsed:expr, rule_in_group!( $name:ident<$o:ty>; $group:ident; children!( $($args:tt)* ) )) => ( { + let res: $o = match_pair!(($pair, $parsed); $($args)*)?; Ok(ParsedValue::$group(res)) }); - (@body, $pair:expr, rule_group!( $name:ident<$o:ty> )) => ( + (@body, $pair:expr, $parsed:expr, rule_group!( $name:ident<$o:ty> )) => ( unreachable!() ); @@ -178,23 +177,45 @@ macro_rules! make_parser { )* } - #[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))), + // Non-recursive implementation to avoid stack overflows + fn parse_any<'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(pair) => { + let mut pairs: Vec<_> = pair.clone().into_inner().map(StackFrame::Unprocessed).collect(); + let n_children = pairs.len(); + pairs_stack.push(Processed(pair, n_children)); + pairs_stack.append(&mut pairs); + } + Processed(pair, n) => { + let mut parsed: Vec<_> = values_stack.split_off(values_stack.len() - n); + parsed.reverse(); + let val = match pair.as_rule() { + $( + Rule::$name if make_parser!(@filter, $submac) + => + make_parser!(@body, pair, parsed, $submac!( $name<$o> $($args)* )) + , + )* + r => Err(custom_parse_error(&pair, format!("parse_any: Unexpected {:?}", r))), + }?; + values_stack.push(val); + } + } + } + Ok(values_stack.pop().unwrap()) } ); } -// List of rules that can be shortcutted as implemented in parse_binop +// List of rules that can be shortcutted if they have a single child fn can_be_shortcutted(rule: Rule) -> bool { use Rule::*; match rule { @@ -217,38 +238,6 @@ fn can_be_shortcutted(rule: Rule) -> bool { } } -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!(_) => ()); @@ -477,42 +466,78 @@ rule_in_group!(non_empty_optional; expression; children!( } )); -rule_in_group!(import_alt_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::ImportAlt)? -); -rule_in_group!(or_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::BoolOr)? -); -rule_in_group!(plus_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::NaturalPlus)? -); -rule_in_group!(text_append_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::TextAppend)? -); -rule_in_group!(list_append_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::ListAppend)? -); -rule_in_group!(and_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::BoolAnd)? -); -rule_in_group!(combine_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::Combine)? -); -rule_in_group!(prefer_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::Prefer)? -); -rule_in_group!(combine_types_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::CombineTypes)? -); -rule_in_group!(times_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::NaturalTimes)? -); -rule_in_group!(equal_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::BoolEQ)? -); -rule_in_group!(not_equal_expression; expression; - raw_pair!(p) => parse_binop(p, BinOp::BoolNE)? -); +rule_in_group!(import_alt_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::ImportAlt, acc, e))) + }, +)); +rule_in_group!(or_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::BoolOr, acc, e))) + }, +)); +rule_in_group!(plus_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::NaturalPlus, acc, e))) + }, +)); +rule_in_group!(text_append_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::TextAppend, acc, e))) + }, +)); +rule_in_group!(list_append_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::ListAppend, acc, e))) + }, +)); +rule_in_group!(and_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::BoolAnd, acc, e))) + }, +)); +rule_in_group!(combine_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::Combine, acc, e))) + }, +)); +rule_in_group!(prefer_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::Prefer, acc, e))) + }, +)); +rule_in_group!(combine_types_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::CombineTypes, acc, e))) + }, +)); +rule_in_group!(times_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::NaturalTimes, acc, e))) + }, +)); +rule_in_group!(equal_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::BoolEQ, acc, e))) + }, +)); +rule_in_group!(not_equal_expression; expression; children!( + [expression(e)] => e, + [expression(first), expression(rest..)] => { + rest.fold(first, |acc, e| bx(Expr::BinOp(BinOp::BoolNE, acc, e))) + }, +)); rule_in_group!(annotated_expression; expression; children!( [expression(e), expression(annot)] => { -- cgit v1.3.1