refactor(parser): wrap some objects and elements with struct

This commit is contained in:
PoiScript 2019-04-24 17:42:21 +08:00
parent 69534576f1
commit 56e289fb48
22 changed files with 1055 additions and 875 deletions

View file

@ -1,60 +1,80 @@
use jetscii::Substring;
use memchr::memchr2;
/// returns (macros name, macros arguments, offset)
#[inline]
pub fn parse(text: &str) -> Option<(&str, Option<&str>, usize)> {
debug_assert!(text.starts_with("{{{"));
#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)]
pub struct Macros<'a> {
pub name: &'a str,
pub arguments: Option<&'a str>,
}
let bytes = text.as_bytes();
if text.len() <= 3 || !bytes[3].is_ascii_alphabetic() {
return None;
}
impl<'a> Macros<'a> {
#[inline]
pub fn parse(text: &str) -> Option<(Macros<'_>, usize)> {
debug_assert!(text.starts_with("{{{"));
let (name, off) = memchr2(b'}', b'(', bytes)
.filter(|&i| {
bytes[3..i]
.iter()
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
})
.map(|i| (&text[3..i], i))?;
let (args, off) = if bytes[off] == b'}' {
if text.len() <= off + 2 || bytes[off + 1] != b'}' || bytes[off + 2] != b'}' {
let bytes = text.as_bytes();
if text.len() <= 3 || !bytes[3].is_ascii_alphabetic() {
return None;
}
(None, off + 3 /* }}} */)
} else {
Substring::new(")}}}")
.find(&text[off..])
.map(|i| (Some(&text[off + 1..off + i]), off + i + 4 /* )}}} */))?
};
Some((name, args, off))
}
let (name, off) = memchr2(b'}', b'(', bytes)
.filter(|&i| {
bytes[3..i]
.iter()
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
})
.map(|i| (&text[3..i], i))?;
#[cfg(test)]
mod tests {
#[test]
fn parse() {
use super::parse;
let (arguments, off) = if bytes[off] == b'}' {
if text.len() <= off + 2 || bytes[off + 1] != b'}' || bytes[off + 2] != b'}' {
return None;
}
(None, off + "}}}".len())
} else {
Substring::new(")}}}")
.find(&text[off..])
.map(|i| (Some(&text[off + 1..off + i]), off + i + ")}}}".len()))?
};
assert_eq!(
parse("{{{poem(red,blue)}}}"),
Some(("poem", Some("red,blue"), "{{{poem(red,blue)}}}".len()))
);
assert_eq!(
parse("{{{poem())}}}"),
Some(("poem", Some(")"), "{{{poem())}}}".len()))
);
assert_eq!(
parse("{{{author}}}"),
Some(("author", None, "{{{author}}}".len()))
);
assert_eq!(parse("{{{0uthor}}}"), None);
assert_eq!(parse("{{{author}}"), None);
assert_eq!(parse("{{{poem(}}}"), None);
assert_eq!(parse("{{{poem)}}}"), None);
Some((Macros { name, arguments }, off))
}
}
#[test]
fn parse() {
assert_eq!(
Macros::parse("{{{poem(red,blue)}}}"),
Some((
Macros {
name: "poem",
arguments: Some("red,blue")
},
"{{{poem(red,blue)}}}".len()
))
);
assert_eq!(
Macros::parse("{{{poem())}}}"),
Some((
Macros {
name: "poem",
arguments: Some(")")
},
"{{{poem())}}}".len()
))
);
assert_eq!(
Macros::parse("{{{author}}}"),
Some((
Macros {
name: "author",
arguments: None
},
"{{{author}}}".len()
))
);
assert_eq!(Macros::parse("{{{0uthor}}}"), None);
assert_eq!(Macros::parse("{{{author}}"), None);
assert_eq!(Macros::parse("{{{poem(}}}"), None);
assert_eq!(Macros::parse("{{{poem)}}}"), None);
}