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