use std::default::Default;
use card::{
ges::GesType,
keyword::Keyword,
line::{CondResult, Line as CardLine},
Card,
};
use lines::{KeywordLine, ParsedLine};
use skipresult::SkipResult;
macro_rules! next_or_return_previdx {
($self:ident, $previdx:expr) => {
match $self.next() {
None => {
return SkipResult {
skip_end: $previdx,
..Default::default()
}
}
Some(t) => t,
};
};
}
macro_rules! next_or_return_some_previdx {
($self:ident, $previdx:expr) => {
match $self.next() {
None => {
return Some(SkipResult {
skip_end: $previdx,
..Default::default()
})
}
Some(t) => t,
};
};
}
macro_rules! next_or_return_none {
($self:ident) => {
match $self.next() {
None => return None,
Some(t) => t,
};
};
}
macro_rules! advance {
($self:ident, $previdx:ident, $nextline:ident) => {
$previdx = Some($nextline.number);
$nextline = next_or_return_previdx!($self, $previdx);
};
}
macro_rules! advance_some {
($self:ident, $previdx:ident, $nextline:ident) => {
$previdx = Some($nextline.number);
$nextline = next_or_return_some_previdx!($self, $previdx);
};
}
pub trait CommentLess {
fn remove_comments(self) -> NoCommentIter<Self>
where
Self: Sized;
}
pub struct NoCommentIter<I> {
pub it: I,
}
impl<'a, I> Iterator for NoCommentIter<I>
where
I: Iterator<Item = ParsedLine<'a>>,
{
type Item = ParsedLine<'a>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(pl) = self.it.next() {
if pl.keyword != Some(&Keyword::Comment) {
return Some(pl);
}
}
None
}
}
impl<'a, I> CommentLess for I
where
I: Iterator<Item = ParsedLine<'a>>,
{
fn remove_comments(self) -> NoCommentIter<I> {
NoCommentIter { it: self }
}
}
impl<'a, I> NoCommentIter<I>
where
I: Iterator<Item = ParsedLine<'a>>,
{
pub fn skip_to_next_keyword<'b>(&'b mut self) -> Option<KeywordLine<'a>> {
let mut line = None;
while line.is_none() {
line = next_or_return_none!(self).try_into_keywordline();
}
line
}
pub fn skip_ges<'b>(
&'b mut self,
ges: GesType,
skipline: &ParsedLine<'a>,
) -> Option<SkipResult<'a>> {
let mut previdx: Option<usize> = None;
let mut nextline: ParsedLine<'a>;
let contained = ges.contains(skipline.text);
let ends = ges.ended_by(skipline.text);
if ends {
nextline = next_or_return_some_previdx!(self, previdx);
Some(SkipResult {
nextline: Some(nextline),
skip_end: Some(skipline.number),
})
} else if !ends && !contained {
None
} else {
nextline = next_or_return_some_previdx!(self, Some(skipline.number));
while ges.contains(nextline.text) {
advance_some!(self, previdx, nextline);
}
if ges.ended_by(nextline.text) {
advance_some!(self, previdx, nextline);
}
Some(SkipResult {
nextline: Some(nextline),
skip_end: previdx,
})
}
}
pub fn skip_fold<'b>(
&'b mut self,
skipline: KeywordLine<'a>,
) -> SkipResult<'a> {
let card: &Card = skipline.keyword.into();
if card.ownfold {
self.skip_card(skipline, card)
} else {
self.skip_card_gather(skipline, card)
}
}
pub fn skip_card<'b>(
&'b mut self,
skipline: KeywordLine<'a>,
card: &Card,
) -> SkipResult<'a> {
let mut conds: Vec<CondResult> = vec![]; let mut cardlines = card.lines.iter();
let cardline = cardlines.next().unwrap_or_else(|| unreachable!());
if let CardLine::Provides(_s, ref c) = cardline {
conds.push(c.evaluate(skipline.text));
}
let mut previdx: Option<usize> = None;
let mut nextline = next_or_return_previdx!(self, previdx);
for cardline in cardlines {
if nextline.keyword.is_some() {
break;
}
match *cardline {
CardLine::Provides(_s, ref c) => {
conds.push(c.evaluate(&nextline.text));
advance!(self, previdx, nextline);
}
CardLine::Ges(ref g) => {
if let Some(sr) = self.skip_ges(*g, &nextline) {
match sr.nextline {
None => return sr,
Some(pl) => {
previdx = sr.skip_end;
nextline = pl;
}
};
}
}
CardLine::Cells(_s) => {
advance!(self, previdx, nextline);
}
CardLine::Optional(_s, i) => {
if conds.get(i as usize) == Some(&CondResult::Bool(true)) {
advance!(self, previdx, nextline);
} else {
continue;
}
}
CardLine::Repeat(_s, i) => {
let num = match conds.get(i as usize) {
Some(CondResult::Number(Some(u))) if *u > 0 => u,
_ => continue,
};
for _ in 0..*num {
advance!(self, previdx, nextline);
if nextline.keyword.is_some() {
break;
}
}
}
CardLine::Block(_l, s) => loop {
while !nextline.text.as_ref().starts_with(s) {
advance!(self, previdx, nextline);
if nextline.keyword.is_some() {
break;
}
}
advance!(self, previdx, nextline);
},
CardLine::OptionalBlock(s1, s2) => {
if !nextline.text.as_ref().starts_with(s1) {
continue;
}
while !nextline.text.as_ref().starts_with(s2) {
advance!(self, previdx, nextline);
if nextline.keyword.is_some() {
break;
}
}
}
}
}
SkipResult {
nextline: Some(nextline),
skip_end: previdx.or_else(|| Some(skipline.number)),
}
}
pub fn skip_card_gather<'b>(
&'b mut self,
skipline: KeywordLine<'a>,
card: &Card,
) -> SkipResult<'a> {
let mut res = self.skip_card(skipline, card);
#[cfg_attr(rustfmt, rustfmt_skip)]
loop {
if let Some(ParsedLine{keyword: Some(k),number,text}) = res.nextline {
if *k == card.keyword {
res = self.skip_card(KeywordLine{keyword: k,number: number, text}, card);
continue;
}
}
return res;
}
}
}
#[cfg(test)]
mod tests {
use card::{
ges::GesType::GesNode,
keyword::Keyword::{self, *},
};
use carddata::*;
use lines::{KeywordLine, Lines, ParsedLine};
use nocommentiter::{CommentLess, NoCommentIter};
macro_rules! pline {
($number:expr, $text:expr, $keyword:expr) => {
ParsedLine {
number: $number,
text: $text.as_ref(),
keyword: $keyword,
}
};
}
macro_rules! kwline {
($number:expr, $text:expr, $keyword:expr) => {
KeywordLine {
number: $number,
text: $text,
keyword: $keyword,
}
};
}
macro_rules! make_lineiter {
($lines:ident, $keywords:ident, $li: ident, $str:expr) => {
$lines = Lines::from_slice($str.as_ref());
$keywords = $lines
.iter()
.map(Keyword::parse)
.collect::<Vec<Option<Keyword>>>();
$li = $keywords
.iter()
.zip($lines.iter())
.enumerate()
.map(ParsedLine::from)
.remove_comments()
};
}
macro_rules! make_test {
($name: ident, $strs: expr, $({$f:expr, $e:expr});+) => {
#[test]
fn $name() {
let lines;
let keywords;
let mut li;
make_lineiter!(lines, keywords, li, $strs);
$( assert_eq!($f(&mut li), $e) );+
}
};
}
fn next_kw<'a, I>(l: &mut NoCommentIter<I>) -> Option<KeywordLine<'a>>
where
I: Iterator<Item = ParsedLine<'a>>,
{
l.skip_to_next_keyword()
}
fn next_nocom<'a, I>(l: &mut NoCommentIter<I>) -> Option<ParsedLine<'a>>
where
I: Iterator<Item = ParsedLine<'a>>,
{
l.next()
}
const COMMENTS: &'static str = "#This\n$is\n#an\n#example\nof\nsome\
\nlines\n.";
make_test!(
works_with_slice,
COMMENTS,
{ next_nocom, Some(pline!(4, "of", None)) };
{ next_nocom, Some(pline!(5, "some", None))}
);
const KEYWORD_LINES: &'static str = "#Comment\n nokeyword\nNODE / \
\n#example\nNSMAS / \nsome\nlines\n.";
make_test!(
needs_no_keywords,
KEYWORD_LINES,
{|l: &mut NoCommentIter<_>| {
let _ = l.next();
let _ = l.next();
let _ = l.next();
let _ = l.next();
l.skip_to_next_keyword()
},
None
}
);
make_test!(
finds_real_keywords,
KEYWORD_LINES,
{ next_kw, Some(kwline!(2, b"NODE / ", &Node)) };
{ next_kw, Some(kwline!(4, b"NSMAS / ", &Nsmas)) };
{ next_kw, None };
{ next_nocom, None }
);
const GES1: &'static str = " PART 1234\
\n OGRP 'hausbau'\
\n DELGRP>NOD 'nix'\
\n END\
\nNODE / ";
make_test!(
can_skip_ges,
GES1,
{|l: &mut NoCommentIter<_>| {
let nextline = l.next().unwrap();
let tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline.unwrap(), pline!(4, b"NODE / ", Some(&Node)));
assert_eq!(tmp.skip_end, Some(3));
l.next()
}, None
}
);
const GES2: &'static str = " PART 1234\
\n OGRP 'hausbau'\
\n END\
\n DELGRP>NOD 'nix'\
\n MOD 10234\
\n NOD 1 23 093402 82\
\n END_MOD\
\n DELELE 12\
\n END";
const GES2_NEXT: &[u8] = b" DELGRP>NOD 'nix'";
make_test!(
can_skip_ges_repeatedly,
GES2,
{|l: &mut NoCommentIter<_>| {
let mut nextline = l.next().unwrap();
let mut tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline.unwrap(), pline!(3, GES2_NEXT, None));
assert_eq!(tmp.skip_end, Some(2));
nextline = l.next().unwrap();
tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline, None);
assert_eq!(tmp.skip_end, Some(8));
l.next()
}, None
}
);
const GES3: &'static str = " PART 1234\
\n OGRP 'hausbau'\
\nNODE / END\
\n DELGRP>NOD 'nix'\
\n MOD 10234\
\n NOD 1 23 093402 82\
\n END_MOD\
\nWhatever\
\n END";
const GES3_FIRST: &'static str = "NODE / END";
const GES3_SECOND: &'static str = "Whatever";
const GES3_LAST: &'static str = " END";
make_test!(
ends_ges_without_end,
GES3,
{|l: &mut NoCommentIter<_>| {
let mut nextline = l.next().unwrap();
let mut tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline.unwrap(), pline!(2, GES3_FIRST, Some(&Node)));
assert_eq!(tmp.skip_end, Some(1));
nextline = l.next().unwrap();
tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline.unwrap(), pline!(7, GES3_SECOND, None));
assert_eq!(tmp.skip_end, Some(6));
l.next()
}, Some(pline!(8, GES3_LAST, None))
}
);
const GES4: &'static str = "wupdiwup\nNODE / ";
const GES4_LAST: &'static str = "NODE / ";
make_test!(
can_skip_empty_ges,
GES4,
{|l: &mut NoCommentIter<_>| {
let nextline = l.next().unwrap();
let tmp = l.skip_ges(GesNode, &nextline);
assert!(tmp.is_none());
l.next().unwrap()
}, pline!(1, GES4_LAST, Some(&Node))
}
);
const GES5: &'static str = " PART 1234\
\n#Comment here\
\n OGRP 'hausbau'\
\n DELGRP>NOD 'nix'\
\n END\
\n$Another comment\
\nNODE / ";
const GES5_NEXTL: &'static str = "NODE / ";
make_test!(
ges_works_with_comments,
GES5,
{|l: &mut NoCommentIter<_>| {
let nextline = l.next().unwrap();
let tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline.unwrap(), pline!(6, GES5_NEXTL, Some(&Node)));
assert_eq!(tmp.skip_end, Some(4));
l.next()
}, None
}
);
const GES6: &'static str = " PART 1234\
\n#Comment here\
\n$Another comment\
\n#NODE / ";
make_test!(
ges_skips_comments_after_end,
GES6,
{|l: &mut NoCommentIter<_>| {
let nextline = l.next().unwrap();
let tmp = l.skip_ges(GesNode, &nextline).unwrap();
assert_eq!(tmp.nextline, None);
assert_eq!(tmp.skip_end, Some(0));
l.next()
}, None
}
);
const CARD_MASS_INCOMPLETE: &'static str =
"$ MASS Card\
\n$# IDNOD IFRA Blank DISr DISs DISt\
\nMASS / 0 0 \
\n$# TITLE\
\nNAME MASS / ->1 \
\n$# BLANK Mx My Mz\
\n$# BLANK Ix Iy Iz Blank\
\nNODE / \
\n ";
make_test!(
skip_incomplete_cards,
CARD_MASS_INCOMPLETE,
{|l: &mut NoCommentIter<_>| {
let firstline = l.next().unwrap();
let tmp = l.skip_card(firstline.try_into_keywordline().unwrap(), &MASS);
assert_eq!(
tmp.nextline.unwrap(),
pline!(7, &"NODE / ", Some(&Node))
);
tmp.skip_end
}, Some(4)
}
);
const LINES_GATHER: [&'static str; 20] = [
"NODE / 1 0. 0.5 0.",
"NODE / 1 0. 0.5 0.",
"NODE / 1 0. 0.5 0.",
"NODE / 1 0. 0.5 0.",
"#Comment here",
"SHELL / 3129 1 1 2967 2971 2970",
"invalid line here",
"SHELL / 3129 1 1 2967 2971 2970",
"SHELL / 3129 1 1 2967 2971 2970",
"#Comment",
"#Comment",
"SHELL / 3129 1 1 2967 2971 2970",
"SHELL / 3129 1 1 2967 2971 2970",
"$Comment",
"SHELL / 3129 1 1 2967 2971 2970",
"SHELL / 3129 1 1 2967 2971 2970",
"$Comment",
"#Comment",
"NODE / 1 0. 0.5 0.",
"NODE / 1 0. 0.5 0.",
];
#[test]
fn skips_gather_cards() {
let keywords: Vec<_> = LINES_GATHER
.iter()
.map(|l| Keyword::parse(l.as_ref()))
.collect();
let mut li = LINES_GATHER
.iter()
.zip(keywords.iter())
.enumerate()
.map(|(n, (t, k))| ParsedLine {
number: n,
text: t.as_ref(),
keyword: k.as_ref(),
})
.remove_comments();
let firstline = li.next().unwrap();
let mut tmp = li.skip_fold(firstline.try_into_keywordline().unwrap());
let mut tmp_nextline = tmp.nextline.unwrap();
assert_eq!(tmp_nextline, pline!(5, &LINES_GATHER[5], Some(&Shell)));
assert_eq!(tmp.skip_end, Some(3));
tmp = li.skip_fold(tmp_nextline.try_into_keywordline().unwrap());
tmp_nextline = tmp.nextline.unwrap();
assert_eq!(tmp_nextline, pline!(6, &LINES_GATHER[6], None));
assert_eq!(tmp.skip_end, Some(5));
let skipped = li.skip_to_next_keyword().unwrap();
tmp = li.skip_fold(skipped.into());
tmp_nextline = tmp.nextline.unwrap();
assert_eq!(tmp_nextline, pline!(18, &LINES_GATHER[18], Some(&Node)));
assert_eq!(tmp.skip_end, Some(15));
tmp = li.skip_fold(tmp_nextline.try_into_keywordline().unwrap());
assert_eq!(tmp.nextline, None);
assert_eq!(tmp.skip_end, None);
}
}