mirror of
https://github.com/ankitects/anki.git
synced 2026-01-13 22:13:58 -05:00
update card_rendering/parser.rs
This commit is contained in:
parent
ef0686b679
commit
001aea4eca
1 changed files with 37 additions and 25 deletions
|
|
@ -21,7 +21,8 @@ use nom::sequence::pair;
|
||||||
use nom::sequence::preceded;
|
use nom::sequence::preceded;
|
||||||
use nom::sequence::separated_pair;
|
use nom::sequence::separated_pair;
|
||||||
use nom::sequence::terminated;
|
use nom::sequence::terminated;
|
||||||
use nom::sequence::tuple;
|
use nom::Input;
|
||||||
|
use nom::Parser;
|
||||||
|
|
||||||
use super::CardNodes;
|
use super::CardNodes;
|
||||||
use super::Directive;
|
use super::Directive;
|
||||||
|
|
@ -86,9 +87,12 @@ impl<'a> Directive<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Consume 0 or more of anything in " \t\r\n" after `parser`.
|
/// Consume 0 or more of anything in " \t\r\n" after `parser`.
|
||||||
fn trailing_whitespace0<'parser, 's, P, O>(parser: P) -> impl FnMut(&'s str) -> IResult<'s, O>
|
fn trailing_whitespace0<I, O, E, P>(parser: P) -> impl Parser<I, Output = O, Error = E>
|
||||||
where
|
where
|
||||||
P: FnMut(&'s str) -> IResult<'s, O> + 'parser,
|
I: Input,
|
||||||
|
<I as Input>::Item: nom::AsChar,
|
||||||
|
E: nom::error::ParseError<I>,
|
||||||
|
P: Parser<I, Output = O, Error = E>,
|
||||||
{
|
{
|
||||||
terminated(parser, multispace0)
|
terminated(parser, multispace0)
|
||||||
}
|
}
|
||||||
|
|
@ -97,11 +101,11 @@ where
|
||||||
fn is_not0<'parser, 'arr: 'parser, 's: 'parser>(
|
fn is_not0<'parser, 'arr: 'parser, 's: 'parser>(
|
||||||
arr: &'arr str,
|
arr: &'arr str,
|
||||||
) -> impl FnMut(&'s str) -> IResult<'s, &'s str> + 'parser {
|
) -> impl FnMut(&'s str) -> IResult<'s, &'s str> + 'parser {
|
||||||
alt((is_not(arr), success("")))
|
move |s| alt((is_not(arr), success(""))).parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node(s: &str) -> IResult<Node> {
|
fn node(s: &str) -> IResult<Node> {
|
||||||
alt((sound_node, tag_node, text_node))(s)
|
alt((sound_node, tag_node, text_node)).parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sound tag `[sound:resource]`, where `resource` is pointing to a sound or
|
/// A sound tag `[sound:resource]`, where `resource` is pointing to a sound or
|
||||||
|
|
@ -110,11 +114,11 @@ fn sound_node(s: &str) -> IResult<Node> {
|
||||||
map(
|
map(
|
||||||
delimited(tag("[sound:"), is_not("]"), tag("]")),
|
delimited(tag("[sound:"), is_not("]"), tag("]")),
|
||||||
Node::SoundOrVideo,
|
Node::SoundOrVideo,
|
||||||
)(s)
|
)
|
||||||
|
.parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_till_potential_tag_start(s: &str) -> IResult<&str> {
|
fn take_till_potential_tag_start(s: &str) -> IResult<&str> {
|
||||||
use nom::InputTake;
|
|
||||||
// first char could be '[', but wasn't part of a node, so skip (eof ends parse)
|
// first char could be '[', but wasn't part of a node, so skip (eof ends parse)
|
||||||
let (after, offset) = anychar(s).map(|(s, c)| (s, c.len_utf8()))?;
|
let (after, offset) = anychar(s).map(|(s, c)| (s, c.len_utf8()))?;
|
||||||
Ok(match after.find('[') {
|
Ok(match after.find('[') {
|
||||||
|
|
@ -127,7 +131,7 @@ fn take_till_potential_tag_start(s: &str) -> IResult<&str> {
|
||||||
fn tag_node(s: &str) -> IResult<Node> {
|
fn tag_node(s: &str) -> IResult<Node> {
|
||||||
/// Match the start of an opening tag and return its name.
|
/// Match the start of an opening tag and return its name.
|
||||||
fn name(s: &str) -> IResult<&str> {
|
fn name(s: &str) -> IResult<&str> {
|
||||||
preceded(tag("[anki:"), is_not("] \t\r\n"))(s)
|
preceded(tag("[anki:"), is_not("] \t\r\n")).parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a parser to match an opening `name` tag and return its options.
|
/// Return a parser to match an opening `name` tag and return its options.
|
||||||
|
|
@ -138,31 +142,35 @@ fn tag_node(s: &str) -> IResult<Node> {
|
||||||
/// empty.
|
/// empty.
|
||||||
fn options(s: &str) -> IResult<Vec<(&str, &str)>> {
|
fn options(s: &str) -> IResult<Vec<(&str, &str)>> {
|
||||||
fn key(s: &str) -> IResult<&str> {
|
fn key(s: &str) -> IResult<&str> {
|
||||||
is_not("] \t\r\n=")(s)
|
is_not("] \t\r\n=").parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn val(s: &str) -> IResult<&str> {
|
fn val(s: &str) -> IResult<&str> {
|
||||||
alt((
|
alt((
|
||||||
delimited(tag("\""), is_not0("\""), tag("\"")),
|
delimited(tag("\""), is_not0("\""), tag("\"")),
|
||||||
is_not0("] \t\r\n\""),
|
is_not0("] \t\r\n\""),
|
||||||
))(s)
|
))
|
||||||
|
.parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
many0(trailing_whitespace0(separated_pair(key, tag("="), val)))(s)
|
many0(trailing_whitespace0(separated_pair(key, tag("="), val))).parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
delimited(
|
move |s| {
|
||||||
pair(tag("[anki:"), trailing_whitespace0(tag(name))),
|
delimited(
|
||||||
options,
|
pair(tag("[anki:"), trailing_whitespace0(tag(name))),
|
||||||
tag("]"),
|
options,
|
||||||
)
|
tag("]"),
|
||||||
|
)
|
||||||
|
.parse(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a parser to match a closing `name` tag.
|
/// Return a parser to match a closing `name` tag.
|
||||||
fn closing_parser<'parser, 'name: 'parser, 's: 'parser>(
|
fn closing_parser<'parser, 'name: 'parser, 's: 'parser>(
|
||||||
name: &'name str,
|
name: &'name str,
|
||||||
) -> impl FnMut(&'s str) -> IResult<'s, ()> + 'parser {
|
) -> impl FnMut(&'s str) -> IResult<'s, ()> + 'parser {
|
||||||
value((), tuple((tag("[/anki:"), tag(name), tag("]"))))
|
move |s| value((), (tag("[/anki:"), tag(name), tag("]"))).parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a parser to match and return anything until a closing `name` tag
|
/// Return a parser to match and return anything until a closing `name` tag
|
||||||
|
|
@ -170,12 +178,15 @@ fn tag_node(s: &str) -> IResult<Node> {
|
||||||
fn content_parser<'parser, 'name: 'parser, 's: 'parser>(
|
fn content_parser<'parser, 'name: 'parser, 's: 'parser>(
|
||||||
name: &'name str,
|
name: &'name str,
|
||||||
) -> impl FnMut(&'s str) -> IResult<'s, &'s str> + 'parser {
|
) -> impl FnMut(&'s str) -> IResult<'s, &'s str> + 'parser {
|
||||||
recognize(fold_many0(
|
move |s| {
|
||||||
pair(not(closing_parser(name)), take_till_potential_tag_start),
|
recognize(fold_many0(
|
||||||
// we don't need to accumulate anything
|
pair(not(closing_parser(name)), take_till_potential_tag_start),
|
||||||
|| (),
|
// we don't need to accumulate anything
|
||||||
|_, _| (),
|
|| (),
|
||||||
))
|
|_, _| (),
|
||||||
|
))
|
||||||
|
.parse(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (_, tag_name) = name(s)?;
|
let (_, tag_name) = name(s)?;
|
||||||
|
|
@ -185,11 +196,12 @@ fn tag_node(s: &str) -> IResult<Node> {
|
||||||
closing_parser(tag_name),
|
closing_parser(tag_name),
|
||||||
),
|
),
|
||||||
|(options, content)| Node::Directive(Directive::new(tag_name, options, content)),
|
|(options, content)| Node::Directive(Directive::new(tag_name, options, content)),
|
||||||
)(s)
|
)
|
||||||
|
.parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn text_node(s: &str) -> IResult<Node> {
|
fn text_node(s: &str) -> IResult<Node> {
|
||||||
map(take_till_potential_tag_start, Node::Text)(s)
|
map(take_till_potential_tag_start, Node::Text).parse(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue