refactor(parser): clean up parse functions

This commit is contained in:
PoiScript 2019-03-19 13:53:47 +08:00
parent 7273a2e84d
commit 1f52e75d3d
13 changed files with 245 additions and 284 deletions

View file

@ -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))
}

View file

@ -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
})?;
Some((
if label == 4 {
None
} else {
Some(&src[4..label])
},
Some(&src[label + 1..def]),
def + 1,
))
})
.map(|i| (Some(&text[off + 1..off + i]), i + off + 1))?
} else {
Some((
if label == 4 {
None
} else {
Some(&src[4..label])
},
None,
label + 1,
))
}
(None, off + 1)
};
Some((label, def, off))
}
#[cfg(test)]

View file

@ -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)]

View file

@ -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);
}
}

View file

@ -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]
.iter()
.all(|&c| c != b'<' && c != b'>' && c != b'\n')
})?;
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
}

View file

@ -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| {
bytes[3..i]
.iter()
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
})?;
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'_')
})
.map(|i| (&text[3..i], i))?;
Some(if bytes[name] == b'}' {
expect!(src, name + 1, b'}')?;
expect!(src, name + 2, b'}')?;
(&src[3..name], None, name + 3)
let (args, off) = if bytes[off] == b'}' {
expect!(text, off + 1, b'}')?;
expect!(text, off + 2, b'}')?;
(None, off + 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,
)
})
Substring::new(")}}}")
.find(&text[off..])
.map(|i| (Some(&text[off + 1..off + i]), off + i + 4 /* )}}} */))?
};
Some((name, args, off))
}
#[cfg(test)]

View file

@ -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]
.iter()
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
})?;
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)]

View file

@ -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| {
i != 2
&& src.as_bytes()[2..i]
.iter()
.all(|&c| c.is_ascii_alphanumeric() || c == b'-')
})?;
let (name, off) = memchr(b':', text.as_bytes())
.filter(|&i| {
i != 2
&& 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)]

View file

@ -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]
.iter()
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
})?;
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)]