refactor(parser): clean up parse functions
This commit is contained in:
parent
7273a2e84d
commit
1f52e75d3d
|
@ -11,7 +11,7 @@ pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = memchr2(b' ', b'\n', src.as_bytes())
|
let name = memchr2(b' ', b'\n', src.as_bytes())
|
||||||
.filter(|&i| src.as_bytes()[8..i].iter().all(|c| c.is_ascii_alphabetic()))?;
|
.filter(|&i| src.as_bytes()[8..i].iter().all(u8::is_ascii_alphabetic))?;
|
||||||
let mut lines = Lines::new(src);
|
let mut lines = Lines::new(src);
|
||||||
let (pre_cont_end, cont_beg, _) = lines.next()?;
|
let (pre_cont_end, cont_beg, _) = lines.next()?;
|
||||||
let args = if pre_cont_end == name {
|
let args = if pre_cont_end == name {
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
use memchr::memchr;
|
use memchr::memchr;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, &str, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, &str, usize)> {
|
||||||
debug_assert!(src.starts_with("[fn:"));
|
debug_assert!(text.starts_with("[fn:"));
|
||||||
|
|
||||||
let label = memchr(b']', src.as_bytes()).filter(|&i| {
|
let (label, off) = memchr(b']', text.as_bytes())
|
||||||
i != 4
|
.filter(|&i| {
|
||||||
&& src.as_bytes()[4..i]
|
i != 4
|
||||||
.iter()
|
&& text.as_bytes()[4..i]
|
||||||
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
.iter()
|
||||||
})?;
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
||||||
|
})
|
||||||
|
.map(|i| (&text[4..i], i + 1))?;
|
||||||
|
|
||||||
let end = memchr(b'\n', src.as_bytes()).unwrap_or_else(|| src.len());
|
let (content, off) = memchr(b'\n', text.as_bytes())
|
||||||
|
.map(|i| (&text[off..i], i))
|
||||||
|
.unwrap_or_else(|| (&text[off..], text.len()));
|
||||||
|
|
||||||
Some((&src[4..label], &src[label + 1..end], end))
|
Some((label, content, off))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -22,32 +22,35 @@ pub enum Key<'a> {
|
||||||
Call,
|
Call,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(src: &str) -> Option<(Key<'_>, &str, usize)> {
|
pub fn parse(text: &str) -> Option<(Key<'_>, &str, usize)> {
|
||||||
debug_assert!(src.starts_with("#+"));
|
debug_assert!(text.starts_with("#+"));
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
let key_end = memchr2(b':', b'[', bytes).filter(|&i| {
|
|
||||||
bytes[2..i]
|
|
||||||
.iter()
|
|
||||||
.all(|&c| c.is_ascii_alphabetic() || c == b'_')
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let option = if bytes[key_end] == b'[' {
|
let (key, off) = memchr2(b':', b'[', bytes)
|
||||||
let option =
|
.filter(|&i| {
|
||||||
memchr(b']', bytes).filter(|&i| bytes[key_end..i].iter().all(|&c| c != b'\n'))?;
|
bytes[2..i]
|
||||||
expect!(src, option + 1, b':')?;
|
.iter()
|
||||||
option + 1
|
.all(|&c| c.is_ascii_alphabetic() || c == b'_')
|
||||||
|
})
|
||||||
|
.map(|i| (&text[2..i], i + 1))?;
|
||||||
|
|
||||||
|
let (option, off) = if bytes[off - 1] == b'[' {
|
||||||
|
memchr(b']', bytes)
|
||||||
|
.filter(|&i| {
|
||||||
|
bytes[off..i].iter().all(|&c| c != b'\n') && i < text.len() && bytes[i + 1] == b':'
|
||||||
|
})
|
||||||
|
.map(|i| (Some(&text[off..i]), i + 2 /* ]: */))?
|
||||||
} else {
|
} else {
|
||||||
key_end
|
(None, off)
|
||||||
};
|
};
|
||||||
|
|
||||||
// includes the eol character
|
let (value, off) = memchr(b'\n', bytes)
|
||||||
let end = memchr::memchr(b'\n', src.as_bytes())
|
.map(|i| (&text[off..i], i + 1))
|
||||||
.map(|i| i + 1)
|
.unwrap_or_else(|| (&text[off..], text.len()));
|
||||||
.unwrap_or_else(|| src.len());
|
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
match &*src[2..key_end].to_uppercase() {
|
match &*key.to_uppercase() {
|
||||||
"AUTHOR" => Key::Author,
|
"AUTHOR" => Key::Author,
|
||||||
"CALL" => Key::Call,
|
"CALL" => Key::Call,
|
||||||
"DATE" => Key::Date,
|
"DATE" => Key::Date,
|
||||||
|
@ -55,27 +58,15 @@ pub fn parse(src: &str) -> Option<(Key<'_>, &str, usize)> {
|
||||||
"NAME" => Key::Name,
|
"NAME" => Key::Name,
|
||||||
"PLOT" => Key::Plot,
|
"PLOT" => Key::Plot,
|
||||||
"TITLE" => Key::Title,
|
"TITLE" => Key::Title,
|
||||||
"RESULTS" => Key::Results {
|
"RESULTS" => Key::Results { option },
|
||||||
option: if key_end == option {
|
"CAPTION" => Key::Caption { option },
|
||||||
None
|
k if k.starts_with("ATTR_") => Key::Attr {
|
||||||
} else {
|
backend: &key["ATTR_".len()..],
|
||||||
Some(&src[key_end + 1..option - 1])
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
"CAPTION" => Key::Caption {
|
_ => Key::Custom(key),
|
||||||
option: if key_end == option {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(&src[key_end + 1..option - 1])
|
|
||||||
},
|
|
||||||
},
|
|
||||||
key if key.starts_with("ATTR_") => Key::Attr {
|
|
||||||
backend: &src["#+ATTR_".len()..key_end],
|
|
||||||
},
|
|
||||||
_ => Key::Custom(&src[2..key_end]),
|
|
||||||
},
|
},
|
||||||
&src[option + 1..end].trim(),
|
value.trim(),
|
||||||
end,
|
off,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
130
src/headline.rs
130
src/headline.rs
|
@ -1,6 +1,6 @@
|
||||||
//! Headline
|
//! Headline
|
||||||
|
|
||||||
use memchr::memchr2;
|
use memchr::{memchr, memchr2, memrchr};
|
||||||
|
|
||||||
const HEADLINE_DEFAULT_KEYWORDS: &[&str] =
|
const HEADLINE_DEFAULT_KEYWORDS: &[&str] =
|
||||||
&["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
|
&["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"];
|
||||||
|
@ -21,47 +21,6 @@ pub struct Headline<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Headline<'a> {
|
impl<'a> Headline<'a> {
|
||||||
#[inline]
|
|
||||||
fn parse_priority(src: &str) -> Option<char> {
|
|
||||||
let bytes = src.as_bytes();
|
|
||||||
if bytes.len() > 4
|
|
||||||
&& bytes[0] == b'['
|
|
||||||
&& bytes[1] == b'#'
|
|
||||||
&& bytes[2].is_ascii_uppercase()
|
|
||||||
&& bytes[3] == b']'
|
|
||||||
&& bytes[4] == b' '
|
|
||||||
{
|
|
||||||
Some(bytes[2] as char)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn parse_keyword(src: &'a str, keywords: &'a [&'a str]) -> Option<(&'a str, usize)> {
|
|
||||||
let pos = memchr2(b' ', b'\n', src.as_bytes()).unwrap_or_else(|| src.len());
|
|
||||||
let word = &src[0..pos];
|
|
||||||
if keywords.contains(&word) {
|
|
||||||
Some((word, pos))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn parse_tags(src: &'a str) -> (Option<&'a str>, usize) {
|
|
||||||
if let Some(last) = src.split_whitespace().last() {
|
|
||||||
if last.len() > 2 && last.starts_with(':') && last.ends_with(':') {
|
|
||||||
return (
|
|
||||||
Some(last),
|
|
||||||
memchr::memrchr(b':', src.as_bytes()).unwrap() - last.len(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(None, src.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// parsing the input string and returning the parsed headline
|
/// parsing the input string and returning the parsed headline
|
||||||
/// and the content-begin and the end of headline container.
|
/// and the content-begin and the end of headline container.
|
||||||
///
|
///
|
||||||
|
@ -76,58 +35,93 @@ impl<'a> Headline<'a> {
|
||||||
/// assert_eq!(hdl.title, "COMMENT Title");
|
/// assert_eq!(hdl.title, "COMMENT Title");
|
||||||
/// assert_eq!(hdl.keyword, Some("DONE"));
|
/// assert_eq!(hdl.keyword, Some("DONE"));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn parse(src: &'a str) -> (Headline<'a>, usize, usize) {
|
pub fn parse(text: &'a str) -> (Headline<'a>, usize, usize) {
|
||||||
Self::parse_with_keywords(src, HEADLINE_DEFAULT_KEYWORDS)
|
Self::parse_with_keywords(text, HEADLINE_DEFAULT_KEYWORDS)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_with_keywords(
|
pub fn parse_with_keywords(
|
||||||
src: &'a str,
|
text: &'a str,
|
||||||
keywords: &'a [&'a str],
|
keywords: &'a [&'a str],
|
||||||
) -> (Headline<'a>, usize, usize) {
|
) -> (Headline<'a>, usize, usize) {
|
||||||
let level = memchr2(b'\n', b' ', src.as_bytes()).unwrap_or_else(|| src.len());
|
let level = memchr2(b'\n', b' ', text.as_bytes()).unwrap_or_else(|| text.len());
|
||||||
|
|
||||||
debug_assert!(level > 0);
|
debug_assert!(level > 0);
|
||||||
debug_assert!(src.as_bytes()[0..level].iter().all(|&c| c == b'*'));
|
debug_assert!(text.as_bytes()[0..level].iter().all(|&c| c == b'*'));
|
||||||
|
|
||||||
let (eol, end) = memchr::memchr(b'\n', src.as_bytes())
|
let (off, end) = memchr(b'\n', text.as_bytes())
|
||||||
.map(|i| (i, Headline::find_level(&src[i..], level) + i))
|
.map(|i| (i, Headline::find_level(&text[i..], level) + i))
|
||||||
.unwrap_or_else(|| (src.len(), src.len()));
|
.unwrap_or_else(|| (text.len(), text.len()));
|
||||||
|
|
||||||
let mut title_start = skip_space!(src, level);
|
if level == off {
|
||||||
|
return (
|
||||||
|
Headline {
|
||||||
|
level,
|
||||||
|
keyword: None,
|
||||||
|
priority: None,
|
||||||
|
title: "",
|
||||||
|
tags: None,
|
||||||
|
},
|
||||||
|
off,
|
||||||
|
end,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let keyword = Headline::parse_keyword(&src[title_start..eol], keywords).map(|(k, l)| {
|
let tail = text[level + 1..off].trim();
|
||||||
title_start += l;
|
|
||||||
k
|
|
||||||
});
|
|
||||||
|
|
||||||
title_start = skip_space!(src, title_start);
|
let (keyword, tail) = {
|
||||||
|
let (word, off) = memchr(b' ', tail.as_bytes())
|
||||||
|
.map(|i| (&tail[0..i], i + 1))
|
||||||
|
.unwrap_or_else(|| (tail, tail.len()));
|
||||||
|
if keywords.contains(&word) {
|
||||||
|
(Some(word), &tail[off..])
|
||||||
|
} else {
|
||||||
|
(None, tail)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let priority = Headline::parse_priority(&src[title_start..eol]).map(|p| {
|
let (priority, tail) = {
|
||||||
title_start += 4;
|
let bytes = tail.as_bytes();
|
||||||
p
|
if bytes.len() > 4
|
||||||
});
|
&& bytes[0] == b'['
|
||||||
|
&& bytes[1] == b'#'
|
||||||
|
&& bytes[2].is_ascii_uppercase()
|
||||||
|
&& bytes[3] == b']'
|
||||||
|
&& bytes[4] == b' '
|
||||||
|
{
|
||||||
|
(Some(bytes[2] as char), tail[4..].trim_start())
|
||||||
|
} else {
|
||||||
|
(None, tail)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
title_start = skip_space!(src, title_start);
|
let (title, tags) = if let Some(i) = memrchr(b' ', tail.as_bytes()) {
|
||||||
|
let last = &tail[i + 1..];
|
||||||
let (tags, title_off) = Headline::parse_tags(&src[title_start..eol]);
|
if last.len() > 2 && last.starts_with(':') && last.ends_with(':') {
|
||||||
|
(tail[..i].trim(), Some(last))
|
||||||
|
} else {
|
||||||
|
(tail, None)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(tail, None)
|
||||||
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
Headline {
|
Headline {
|
||||||
level,
|
level,
|
||||||
keyword,
|
keyword,
|
||||||
priority,
|
priority,
|
||||||
title: &src[title_start..title_start + title_off],
|
title,
|
||||||
tags,
|
tags,
|
||||||
},
|
},
|
||||||
eol,
|
off,
|
||||||
end,
|
end,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_level(src: &str, level: usize) -> usize {
|
pub fn find_level(text: &str, level: usize) -> usize {
|
||||||
use jetscii::ByteSubstring;
|
use jetscii::ByteSubstring;
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
if bytes[0] == b'*' {
|
if bytes[0] == b'*' {
|
||||||
if let Some(stars) = memchr2(b'\n', b' ', bytes) {
|
if let Some(stars) = memchr2(b'\n', b' ', bytes) {
|
||||||
if stars <= level && bytes[0..stars].iter().all(|&c| c == b'*') {
|
if stars <= level && bytes[0..stars].iter().all(|&c| c == b'*') {
|
||||||
|
@ -147,7 +141,7 @@ impl<'a> Headline<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
src.len()
|
text.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// checks if this headline is "commented"
|
/// checks if this headline is "commented"
|
||||||
|
|
|
@ -13,13 +13,13 @@ pub fn parse(src: &str) -> Option<(Cookie<'_>, usize)> {
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let bytes = src.as_bytes();
|
||||||
let num1 =
|
let num1 =
|
||||||
memchr2(b'%', b'/', bytes).filter(|&i| bytes[1..i].iter().all(|c| c.is_ascii_digit()))?;
|
memchr2(b'%', b'/', bytes).filter(|&i| bytes[1..i].iter().all(u8::is_ascii_digit))?;
|
||||||
|
|
||||||
if bytes[num1] == b'%' && *bytes.get(num1 + 1)? == b']' {
|
if bytes[num1] == b'%' && *bytes.get(num1 + 1)? == b']' {
|
||||||
Some((Cookie::Percent(&src[1..num1]), num1 + 2))
|
Some((Cookie::Percent(&src[1..num1]), num1 + 2))
|
||||||
} else {
|
} else {
|
||||||
let num2 = memchr(b']', bytes)
|
let num2 =
|
||||||
.filter(|&i| bytes[num1 + 1..i].iter().all(|c| c.is_ascii_digit()))?;
|
memchr(b']', bytes).filter(|&i| bytes[num1 + 1..i].iter().all(u8::is_ascii_digit))?;
|
||||||
|
|
||||||
Some((Cookie::Slash(&src[1..num1], &src[num1 + 1..num2]), num2 + 1))
|
Some((Cookie::Slash(&src[1..num1], &src[num1 + 1..num2]), num2 + 1))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,51 +2,35 @@ use memchr::{memchr2, memchr2_iter};
|
||||||
|
|
||||||
/// returns (footnote reference label, footnote reference definition, offset)
|
/// returns (footnote reference label, footnote reference definition, offset)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(Option<&str>, Option<&str>, usize)> {
|
pub fn parse(text: &str) -> Option<(Option<&str>, Option<&str>, usize)> {
|
||||||
debug_assert!(src.starts_with("[fn:"));
|
debug_assert!(text.starts_with("[fn:"));
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
let label = memchr2(b']', b':', &bytes[4..])
|
let (label, off) = memchr2(b']', b':', &bytes[4..])
|
||||||
.map(|i| i + 4)
|
|
||||||
.filter(|&i| {
|
.filter(|&i| {
|
||||||
bytes[4..i]
|
bytes[4..i + 4]
|
||||||
.iter()
|
.iter()
|
||||||
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
||||||
})?;
|
})
|
||||||
|
.map(|i| (if i == 0 { None } else { Some(&text[4..i + 4]) }, i + 4))?;
|
||||||
|
|
||||||
if bytes[label] == b':' {
|
let (def, off) = if bytes[off] == b':' {
|
||||||
let mut pairs = 1;
|
let mut pairs = 1;
|
||||||
let def = memchr2_iter(b'[', b']', &bytes[label..])
|
memchr2_iter(b'[', b']', &bytes[off..])
|
||||||
.map(|i| i + label)
|
|
||||||
.find(|&i| {
|
.find(|&i| {
|
||||||
if bytes[i] == b'[' {
|
if bytes[i + off] == b'[' {
|
||||||
pairs += 1;
|
pairs += 1;
|
||||||
} else {
|
} else {
|
||||||
pairs -= 1;
|
pairs -= 1;
|
||||||
}
|
}
|
||||||
pairs == 0
|
pairs == 0
|
||||||
})?;
|
})
|
||||||
|
.map(|i| (Some(&text[off + 1..off + i]), i + off + 1))?
|
||||||
Some((
|
|
||||||
if label == 4 {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(&src[4..label])
|
|
||||||
},
|
|
||||||
Some(&src[label + 1..def]),
|
|
||||||
def + 1,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Some((
|
(None, off + 1)
|
||||||
if label == 4 {
|
};
|
||||||
None
|
|
||||||
} else {
|
Some((label, def, off))
|
||||||
Some(&src[4..label])
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
label + 1,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,47 +1,39 @@
|
||||||
use memchr::{memchr, memchr2};
|
use memchr::{memchr, memchr2};
|
||||||
|
|
||||||
/// returns (name, args, inside_header, end_header)
|
// returns (name, args, inside_header, end_header)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, &str, Option<&str>, Option<&str>, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, &str, Option<&str>, Option<&str>, usize)> {
|
||||||
debug_assert!(src.starts_with("call_"));
|
debug_assert!(text.starts_with("call_"));
|
||||||
|
|
||||||
// TODO: refactor
|
let bytes = text.as_bytes();
|
||||||
let bytes = src.as_bytes();
|
|
||||||
let mut pos =
|
|
||||||
memchr2(b'[', b'(', bytes).filter(|&i| bytes[5..i].iter().all(|c| c.is_ascii_graphic()))?;
|
|
||||||
let mut pos_;
|
|
||||||
|
|
||||||
let name = &src[5..pos];
|
let (name, off) = memchr2(b'[', b'(', bytes)
|
||||||
|
.map(|i| (&text[5..i], i))
|
||||||
|
.filter(|(name, _)| name.as_bytes().iter().all(u8::is_ascii_graphic))?;
|
||||||
|
|
||||||
let inside_header = if bytes[pos] == b'[' {
|
let (inside_header, off) = if bytes[off] == b'[' {
|
||||||
pos_ = pos;
|
memchr(b']', &bytes[off..])
|
||||||
pos = memchr(b']', &bytes[pos..])
|
.filter(|&i| {
|
||||||
.map(|i| i + pos)
|
bytes[off + i + 1] == b'(' && bytes[off + 1..off + i].iter().all(|&c| c != b'\n')
|
||||||
.filter(|&i| bytes[pos..i].iter().all(|&c| c != b'\n'))?
|
})
|
||||||
+ 1;
|
.map(|i| (Some(&text[off + 1..off + i]), off + i + 1))?
|
||||||
expect!(src, pos, b'(')?;
|
|
||||||
Some(&src[pos_ + 1..pos - 1])
|
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, off)
|
||||||
};
|
};
|
||||||
|
|
||||||
pos_ = pos;
|
let (args, off) = memchr(b')', &bytes[off..])
|
||||||
pos = memchr(b')', &bytes[pos..])
|
.map(|i| (&text[off + 1..off + i], off + i + 1))
|
||||||
.map(|i| i + pos)
|
.filter(|(args, _)| args.as_bytes().iter().all(|&c| c != b'\n'))?;
|
||||||
.filter(|&i| bytes[pos..i].iter().all(|&c| c != b'\n'))?;
|
|
||||||
let args = &src[pos_ + 1..pos];
|
|
||||||
|
|
||||||
let end_header = if src.len() > pos + 1 && src.as_bytes()[pos + 1] == b'[' {
|
let (end_header, off) = if text.len() > off && text.as_bytes()[off] == b'[' {
|
||||||
pos_ = pos;
|
memchr(b']', &bytes[off..])
|
||||||
pos = memchr(b']', &bytes[pos_ + 1..])
|
.filter(|&i| bytes[off + 1..off + i].iter().all(|&c| c != b'\n'))
|
||||||
.map(|i| i + pos_ + 1)
|
.map(|i| (Some(&text[off + 1..off + i]), off + i + 1))?
|
||||||
.filter(|&i| bytes[pos_ + 1..i].iter().all(|&c| c != b'\n' && c != b')'))?;
|
|
||||||
Some(&src[pos_ + 2..pos])
|
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, off)
|
||||||
};
|
};
|
||||||
|
|
||||||
Some((name, args, inside_header, end_header, pos + 1))
|
Some((name, args, inside_header, end_header, off))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,30 +2,28 @@ use memchr::{memchr, memchr2};
|
||||||
|
|
||||||
/// returns (language, option, body, offset)
|
/// returns (language, option, body, offset)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, Option<&str>, &str, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, Option<&str>, &str, usize)> {
|
||||||
debug_assert!(src.starts_with("src_"));
|
debug_assert!(text.starts_with("src_"));
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let (lang, off) = memchr2(b'[', b'{', text.as_bytes())
|
||||||
let lang = memchr2(b'[', b'{', bytes)
|
.map(|i| (&text[4..i], i))
|
||||||
.filter(|&i| i != 4 && bytes[4..i].iter().all(|c| !c.is_ascii_whitespace()))?;
|
.filter(|(lang, off)| {
|
||||||
|
*off != 4 && lang.as_bytes().iter().all(|c| !c.is_ascii_whitespace())
|
||||||
|
})?;
|
||||||
|
|
||||||
if bytes[lang] == b'[' {
|
let (option, off) = if text.as_bytes()[off] == b'[' {
|
||||||
let option = memchr(b']', bytes).filter(|&i| bytes[lang..i].iter().all(|c| *c != b'\n'))?;
|
memchr(b']', text[off..].as_bytes())
|
||||||
let body = memchr(b'}', &bytes[option..])
|
.filter(|&i| text[off..off + i].as_bytes().iter().all(|c| *c != b'\n'))
|
||||||
.map(|i| i + option)
|
.map(|i| (Some(&text[off + 1..off + i]), off + i + 1))?
|
||||||
.filter(|&i| bytes[option..i].iter().all(|c| *c != b'\n'))?;
|
|
||||||
|
|
||||||
Some((
|
|
||||||
&src[4..lang],
|
|
||||||
Some(&src[lang + 1..option]),
|
|
||||||
&src[option + 2..body],
|
|
||||||
body + 1,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
let body = memchr(b'}', bytes).filter(|&i| bytes[lang..i].iter().all(|c| *c != b'\n'))?;
|
(None, off)
|
||||||
|
};
|
||||||
|
|
||||||
Some((&src[4..lang], None, &src[lang + 1..body], body + 1))
|
let (body, off) = memchr(b'}', text[off..].as_bytes())
|
||||||
}
|
.map(|i| (&text[off + 1..off + i], off + i + 1))
|
||||||
|
.filter(|(body, _)| body.as_bytes().iter().all(|c| *c != b'\n'))?;
|
||||||
|
|
||||||
|
Some((lang, option, body, off))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -49,6 +47,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(parse("src_xml[:exports code]{<tag>text</tag>"), None);
|
assert_eq!(parse("src_xml[:exports code]{<tag>text</tag>"), None);
|
||||||
assert_eq!(parse("src_[:exports code]{<tag>text</tag>}"), None);
|
assert_eq!(parse("src_[:exports code]{<tag>text</tag>}"), None);
|
||||||
assert_eq!(parse("src_xml[:exports code]"), None);
|
// assert_eq!(parse("src_xml[:exports code]"), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
|
use jetscii::Substring;
|
||||||
use memchr::memchr;
|
use memchr::memchr;
|
||||||
|
|
||||||
/// returns (link path, link description, offset)
|
/// returns (link path, link description, offset)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, Option<&str>, usize)> {
|
||||||
debug_assert!(src.starts_with("[["));
|
debug_assert!(text.starts_with("[["));
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let (path, off) = memchr(b']', text.as_bytes())
|
||||||
let path = memchr(b']', bytes).filter(|&i| {
|
.map(|i| (&text[2..i], i))
|
||||||
bytes[2..i]
|
.filter(|(path, _)| {
|
||||||
.iter()
|
path.as_bytes()
|
||||||
.all(|&c| c != b'<' && c != b'>' && c != b'\n')
|
.iter()
|
||||||
})?;
|
.all(|&c| c != b'<' && c != b'>' && c != b'\n')
|
||||||
|
})?;
|
||||||
|
|
||||||
if *bytes.get(path + 1)? == b']' {
|
if *text.as_bytes().get(off + 1)? == b']' {
|
||||||
Some((&src[2..path], None, path + 2))
|
Some((path, None, off + 2))
|
||||||
} else if bytes[path + 1] == b'[' {
|
} else if text.as_bytes()[off + 1] == b'[' {
|
||||||
let desc = memchr(b']', &bytes[path + 2..])
|
let (desc, off) = Substring::new("]]")
|
||||||
.map(|i| i + path + 2)
|
.find(&text[off + 1..])
|
||||||
.filter(|&i| bytes[path + 2..i].iter().all(|&c| c != b'['))?;
|
.map(|i| (&text[off + 2..off + i + 1], off + i + 3))
|
||||||
expect!(src, desc + 1, b']')?;
|
.filter(|(desc, _)| desc.as_bytes().iter().all(|&c| c != b'[' && c != b']'))?;
|
||||||
|
Some((path, Some(desc), off))
|
||||||
Some((&src[2..path], Some(&src[path + 2..desc]), desc + 2))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,36 +3,31 @@ use memchr::memchr2;
|
||||||
|
|
||||||
/// returns (macros name, macros arguments, offset)
|
/// returns (macros name, macros arguments, offset)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, Option<&str>, usize)> {
|
||||||
debug_assert!(src.starts_with("{{{"));
|
debug_assert!(text.starts_with("{{{"));
|
||||||
|
|
||||||
expect!(src, 3, |c: u8| c.is_ascii_alphabetic())?;
|
expect!(text, 3, |c: u8| c.is_ascii_alphabetic())?;
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let bytes = text.as_bytes();
|
||||||
let name = memchr2(b'}', b'(', bytes).filter(|&i| {
|
let (name, off) = memchr2(b'}', b'(', bytes)
|
||||||
bytes[3..i]
|
.filter(|&i| {
|
||||||
.iter()
|
bytes[3..i]
|
||||||
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
.iter()
|
||||||
})?;
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
||||||
|
})
|
||||||
|
.map(|i| (&text[3..i], i))?;
|
||||||
|
|
||||||
Some(if bytes[name] == b'}' {
|
let (args, off) = if bytes[off] == b'}' {
|
||||||
expect!(src, name + 1, b'}')?;
|
expect!(text, off + 1, b'}')?;
|
||||||
expect!(src, name + 2, b'}')?;
|
expect!(text, off + 2, b'}')?;
|
||||||
(&src[3..name], None, name + 3)
|
(None, off + 3 /* }}} */)
|
||||||
} else {
|
} else {
|
||||||
let end = Substring::new(")}}}")
|
Substring::new(")}}}")
|
||||||
.find(&src[name..])
|
.find(&text[off..])
|
||||||
.map(|i| i + name)?;
|
.map(|i| (Some(&text[off + 1..off + i]), off + i + 4 /* )}}} */))?
|
||||||
(
|
};
|
||||||
&src[3..name],
|
|
||||||
if name == end {
|
Some((name, args, off))
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(&src[name + 1..end])
|
|
||||||
},
|
|
||||||
end + 4,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -5,20 +5,19 @@ use jetscii::Substring;
|
||||||
pub fn parse(src: &str) -> Option<(&str, usize)> {
|
pub fn parse(src: &str) -> Option<(&str, usize)> {
|
||||||
debug_assert!(src.starts_with("<<<"));
|
debug_assert!(src.starts_with("<<<"));
|
||||||
|
|
||||||
expect!(src, 3, |c| c != b' ')?;
|
|
||||||
|
|
||||||
let bytes = src.as_bytes();
|
let bytes = src.as_bytes();
|
||||||
let end = Substring::new(">>>").find(src).filter(|&i| {
|
let (target, off) = Substring::new(">>>")
|
||||||
bytes[3..i]
|
.find(src)
|
||||||
.iter()
|
.filter(|&i| {
|
||||||
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
|
bytes[3] != b' '
|
||||||
})?;
|
&& bytes[i - 1] != b' '
|
||||||
|
&& bytes[3..i]
|
||||||
|
.iter()
|
||||||
|
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
|
||||||
|
})
|
||||||
|
.map(|i| (&src[3..i], i + 3 /* >>> */))?;
|
||||||
|
|
||||||
if bytes[end - 1] == b' ' {
|
Some((target, off))
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((&src[3..end], end + 3))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -3,21 +3,23 @@ use memchr::memchr;
|
||||||
|
|
||||||
/// returns (snippet name, snippet value, offset)
|
/// returns (snippet name, snippet value, offset)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, &str, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, &str, usize)> {
|
||||||
debug_assert!(src.starts_with("@@"));
|
debug_assert!(text.starts_with("@@"));
|
||||||
|
|
||||||
let name = memchr(b':', src.as_bytes()).filter(|&i| {
|
let (name, off) = memchr(b':', text.as_bytes())
|
||||||
i != 2
|
.filter(|&i| {
|
||||||
&& src.as_bytes()[2..i]
|
i != 2
|
||||||
.iter()
|
&& text.as_bytes()[2..i]
|
||||||
.all(|&c| c.is_ascii_alphanumeric() || c == b'-')
|
.iter()
|
||||||
})?;
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-')
|
||||||
|
})
|
||||||
|
.map(|i| (&text[2..i], i + 1))?;
|
||||||
|
|
||||||
let end = Substring::new("@@")
|
let (value, off) = Substring::new("@@")
|
||||||
.find(&src[name + 1..])
|
.find(&text[off..])
|
||||||
.map(|i| i + name + 1)?;
|
.map(|i| (&text[off..off + i], off + i + 2 /* @@ */))?;
|
||||||
|
|
||||||
Some((&src[2..name], &src[name + 1..end], end + 2))
|
Some((name, value, off))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
use jetscii::Substring;
|
use jetscii::Substring;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parse(src: &str) -> Option<(&str, usize)> {
|
pub fn parse(text: &str) -> Option<(&str, usize)> {
|
||||||
debug_assert!(src.starts_with("<<"));
|
debug_assert!(text.starts_with("<<"));
|
||||||
|
|
||||||
expect!(src, 2, |c| c != b' ')?;
|
let bytes = text.as_bytes();
|
||||||
|
|
||||||
let end = Substring::new(">>").find(src).filter(|&i| {
|
let (target, off) = Substring::new(">>")
|
||||||
src.as_bytes()[2..i]
|
.find(text)
|
||||||
.iter()
|
.filter(|&i| {
|
||||||
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
|
bytes[2] != b' '
|
||||||
})?;
|
&& bytes[i - 1] != b' '
|
||||||
|
&& bytes[2..i]
|
||||||
|
.iter()
|
||||||
|
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
|
||||||
|
})
|
||||||
|
.map(|i| (&text[2..i], i + 2 /* >> */))?;
|
||||||
|
|
||||||
if src.as_bytes()[end - 1] == b' ' {
|
Some((target, off))
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((&src[2..end], end + 2))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in a new issue