use crate::iter::ConcatIterator; use crate::parser::{comment, tabspaces}; use combine::parser::char::{alpha_num, char, digit, newline, spaces}; use combine::parser::combinator::attempt; use combine::sep_end_by; use combine::stream::Stream; use combine::token; use combine::{between, choice, eof, many, many1, sep_by1}; #[allow(non_camel_case_types)] type number = u64; /// node range expression /// /// [00X-00Y] from x to y (including also x and y). /// /// Currently it does not support skip like: [X-Y/Z] #[derive(Default, Debug, PartialEq, Clone)] struct NodeaggSubRange { start: number, stop: number, // TODO: skip is not supported yet. // TODO: impl PartialEq } impl NodeaggSubRange { fn iter(&self) -> impl Iterator { self.start..=self.stop } } #[derive(Default, Debug, PartialEq, Clone)] struct NodeaggSubNumber(number); impl NodeaggSubNumber { fn iter(&self) -> impl Iterator { std::iter::once(self.0) } } #[derive(Debug, PartialEq, Clone)] enum NodeaggSubArrayElementKind { Range(NodeaggSubRange), Number(NodeaggSubNumber), } #[derive(Debug, PartialEq, Clone)] struct NodeaggSubArrayElement { width: usize, inner: NodeaggSubArrayElementKind, } impl NodeaggSubArrayElement { fn iter(&self) -> Box + '_> { match &self.inner { NodeaggSubArrayElementKind::Range(v) => Box::new(v.iter()), NodeaggSubArrayElementKind::Number(v) => Box::new(v.iter()), } } } #[derive(Default, Debug, PartialEq, Clone)] struct NodeaggSubArray { values: Vec, } impl Extend for NodeaggSubArray { fn extend>(&mut self, iter: T) { self.values.extend(iter); } } impl NodeaggSubArray { fn iter(&self) -> Box + '_> { // TODO: reduce copy // TODO: should use itertools::concat? let width = self.values.iter().map(|x| x.width).max().unwrap(); Box::new( (ConcatIterator { itrs: self.values.iter().map(|e| e.iter()).collect::>(), }) .map(move |x| format!("{0:0>1$}", x, width)), ) } } #[derive(Debug, PartialEq, Clone)] enum NodeaggSubExpr { NodeaggSub(String), Array(NodeaggSubArray), } #[derive(Default, Debug, Clone, PartialEq)] struct NodeaggBaseExpr(Vec); impl Extend for NodeaggBaseExpr { fn extend>(&mut self, iter: T) { self.0.extend(iter); } } impl NodeaggSubExpr { pub fn iter(&self) -> Box + '_> { match self { NodeaggSubExpr::NodeaggSub(s) => Box::new(std::iter::once(s.clone())), NodeaggSubExpr::Array(a) => a.iter(), } } } impl NodeaggBaseExpr { pub fn iter(&self) -> Box + '_> { use itertools::Itertools; if self.0.len() == 0 { Box::new(std::iter::empty::()) } else { // TODO: reduce clone Box::new( self.0 .iter() .clone() .map(|e| e.iter().map(|s| s.to_string()).collect::>()) .multi_cartesian_product() .map(|v| { v.iter() .map(|s| s.to_string()) .collect::>() .join("") }), ) } } } #[derive(Default, Debug, Clone)] pub(crate) struct NodeaggExpr(Vec); impl Extend for NodeaggExpr { fn extend>(&mut self, iter: T) { self.0.extend(iter); } } impl NodeaggExpr { pub(crate) fn iter(&self) -> itertools::Unique> { // TODO: reduce copy // TODO: should use itertools::concat? use itertools::Itertools; (ConcatIterator { itrs: self.0.iter().map(|e| e.iter()).collect::>(), }) .unique() } } parser! { fn nodeagg_sub_expr[Input]()(Input) -> NodeaggSubExpr where [Input: Stream] { use std::cmp::max; //let skip_spaces = || spaces().silent(); // TODO: shold relaxing characters and shold not use parser::char::alpha_num? let word = many1(alpha_num().or(char('.')).or(char('-'))); let number = many1(digit()).map(|t: String| NodeaggSubArrayElement { width: t.len(), inner: NodeaggSubArrayElementKind::Number ( NodeaggSubNumber(t.parse::().unwrap()) )} ); let range = (many1(digit()), char('-'), many1(digit())).map(|t: (String, _, String)| NodeaggSubArrayElement { width: max(t.0.len(), t.2.len()), inner: NodeaggSubArrayElementKind::Range ( NodeaggSubRange { start: t.0.parse::().unwrap(), stop: t.2.parse::().unwrap(), })} ); let comma_list = sep_by1::( attempt(range).or(number) , char(',')); let array = between(char('['), char(']'), comma_list); choice(( word.map(NodeaggSubExpr::NodeaggSub), array.map(NodeaggSubExpr::Array), )) } } parser! { fn nodeagg_base_expr[Input]()(Input) -> NodeaggBaseExpr where [Input: Stream] { many(nodeagg_sub_expr()).skip(tabspaces()) } } parser! { pub(crate) fn nodeagg_expr[Input]()(Input) -> NodeaggExpr where [Input: Stream] { //spaces().with(sep_end_by(nodeagg_base_expr().skip(comment()), token(',').or(newline()).skip(spaces()).skip(comment()))).skip(spaces().skip(comment()).with(eof())) spaces().with(sep_end_by(nodeagg_base_expr().skip(comment()), (token(',').or(newline())).skip(tabspaces()).skip(comment()) )).skip(tabspaces().skip(comment()).with(eof())) }} #[cfg(test)] mod tests { use super::*; use combine::EasyParser; use combine::Parser; // for test use impl From> for NodeaggBaseExpr { fn from(item: Vec) -> Self { NodeaggBaseExpr(item) } } // for test use impl From for Vec { fn from(item: NodeaggExpr) -> Self { item.0 } } impl From> for NodeaggExpr { fn from(item: Vec) -> Self { NodeaggExpr(item) } } impl PartialEq for NodeaggExpr { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } #[test] fn _nodeagg_sub_expr() { assert!(nodeagg_sub_expr().parse("").is_err()); assert_eq!( nodeagg_sub_expr().parse("node"), Ok((NodeaggSubExpr::NodeaggSub("node".to_string()), "")) ); assert_eq!( nodeagg_sub_expr().parse("123"), Ok((NodeaggSubExpr::NodeaggSub("123".to_string()), "")) ); assert!(nodeagg_sub_expr().parse("[]").is_err()); assert!(nodeagg_sub_expr().parse("[1]").is_ok()); assert!(nodeagg_sub_expr().parse("[01,02]").is_ok()); assert!(nodeagg_sub_expr().parse("[01-02]").is_ok()); assert_eq!( nodeagg_sub_expr() .parse("[1,2,3-4]") .unwrap() .0 .iter() .collect::() .len(), 4 ); } #[test] fn _nodeagg_base_expr() { assert_eq!(nodeagg_base_expr().parse(""), Ok((vec![].into(), ""))); assert_eq!( nodeagg_base_expr().parse("123"), Ok(( vec![NodeaggSubExpr::NodeaggSub("123".to_string())].into(), "" )) ); assert_eq!( nodeagg_base_expr().parse("node "), Ok(( vec![NodeaggSubExpr::NodeaggSub("node".to_string())].into(), "" )) ); assert!(nodeagg_base_expr().parse("[]").is_err()); assert!(nodeagg_base_expr().parse("[1]").is_ok()); assert!(nodeagg_base_expr().parse("node1,node2").is_ok()); assert!(nodeagg_base_expr().parse("[1,2]").is_ok()); assert!(nodeagg_base_expr().parse("[1-2]").is_ok()); assert!(nodeagg_base_expr().parse("anode[01-02]").is_ok()); } #[test] fn _nodeagg_expr() { assert_eq!( nodeagg_expr().easy_parse("node1,node2"), Ok(( vec![ nodeagg_base_expr().parse("node1").unwrap().0, nodeagg_base_expr().parse("node2").unwrap().0 ] .into(), "" )) ); assert_eq!( nodeagg_expr() .parse("anode[01-02],bnode2") .unwrap() .0 .iter() .collect::>(), vec!["anode01", "anode02", "bnode2"] ); assert!(nodeagg_expr().parse("node1, node2").is_ok()); assert!(nodeagg_expr().parse(" node1,node2").is_ok()); assert!(nodeagg_expr().parse("node1,node2 ").is_ok()); assert!(nodeagg_expr().parse("node1\nnode2").is_ok()); assert!(nodeagg_expr().parse("node1 a").is_err()); } }