refactor: cleanup utils macros
This commit is contained in:
parent
346ebc83d7
commit
0b355b498c
28
README.md
28
README.md
|
@ -43,6 +43,34 @@ _Section 2_
|
||||||
|
|
||||||
Alternatively, you can use the built-in render.
|
Alternatively, you can use the built-in render.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use orgize::{HtmlHandler, Render};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let contents = r#"* Title 1
|
||||||
|
*Section 1*
|
||||||
|
** Title 2
|
||||||
|
_Section 2_
|
||||||
|
* Title 3
|
||||||
|
/Section 3/
|
||||||
|
* Title 4
|
||||||
|
=Section 4="#;
|
||||||
|
|
||||||
|
let cursor = Cursor::new(Vec::new());
|
||||||
|
let mut render = Render::new(HtmlHandler, cursor, &contents);
|
||||||
|
|
||||||
|
render
|
||||||
|
.render()
|
||||||
|
.expect("something went wrong rendering the file");
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
String::from_utf8(render.into_wirter().into_inner()).expect("invalid utf-8")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT
|
MIT
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use lines::Lines;
|
use lines::Lines;
|
||||||
|
use memchr::memchr2;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -7,12 +8,14 @@ pub struct Block;
|
||||||
impl Block {
|
impl Block {
|
||||||
// return (name, args, contents-begin, contents-end, end)
|
// return (name, args, contents-begin, contents-end, end)
|
||||||
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
||||||
if src.len() < 17 || !src[0..8].eq_ignore_ascii_case("#+BEGIN_") {
|
debug_assert!(src.starts_with("#+"));
|
||||||
|
|
||||||
|
if !src[2..8].eq_ignore_ascii_case("BEGIN_") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = until_while!(src, 8, |c| c == b' ' || c == b'\n', |c: u8| c
|
let name = memchr2(b' ', b'\n', src.as_bytes())
|
||||||
.is_ascii_alphabetic())?;
|
.filter(|&i| src.as_bytes()[8..i].iter().all(|c| c.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,3 +1,6 @@
|
||||||
|
use lines::Lines;
|
||||||
|
use memchr::memchr2;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DynBlock;
|
pub struct DynBlock;
|
||||||
|
@ -5,17 +8,26 @@ pub struct DynBlock;
|
||||||
impl DynBlock {
|
impl DynBlock {
|
||||||
// return (name, parameters, contents-begin, contents-end, end)
|
// return (name, parameters, contents-begin, contents-end, end)
|
||||||
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
pub fn parse(src: &str) -> Option<(&str, Option<&str>, usize, usize, usize)> {
|
||||||
if src.len() < 17 || !src[0..9].eq_ignore_ascii_case("#+BEGIN: ") {
|
debug_assert!(src.starts_with("#+"));
|
||||||
|
|
||||||
|
if !src[2..9].eq_ignore_ascii_case("BEGIN: ") {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let bytes = src.as_bytes();
|
||||||
let args = eol!(src);
|
let args = eol!(src);
|
||||||
let name = until_while!(src, 9, |c| c == b' ' || c == b'\n', |c: u8| c
|
let name = memchr2(b' ', b'\n', &bytes[9..])
|
||||||
.is_ascii_alphabetic())?;
|
.map(|i| i + 9)
|
||||||
|
.filter(|&i| {
|
||||||
|
src.as_bytes()[9..i]
|
||||||
|
.iter()
|
||||||
|
.all(|&c| c.is_ascii_alphabetic())
|
||||||
|
})?;
|
||||||
|
let mut lines = Lines::new(src);
|
||||||
|
let (mut pre_cont_end, _, _) = lines.next()?;
|
||||||
|
|
||||||
let mut pos = 0;
|
while let Some((cont_end, end, line)) = lines.next() {
|
||||||
for line_end in lines!(src) {
|
if line.trim().eq_ignore_ascii_case("#+END:") {
|
||||||
if src[pos..line_end].trim().eq_ignore_ascii_case("#+END:") {
|
|
||||||
return Some((
|
return Some((
|
||||||
&src[8..name].trim(),
|
&src[8..name].trim(),
|
||||||
if name == args {
|
if name == args {
|
||||||
|
@ -24,11 +36,11 @@ impl DynBlock {
|
||||||
Some(&src[name..args].trim())
|
Some(&src[name..args].trim())
|
||||||
},
|
},
|
||||||
args,
|
args,
|
||||||
pos,
|
pre_cont_end,
|
||||||
line_end,
|
end,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
pos = line_end;
|
pre_cont_end = cont_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
|
@ -45,6 +57,6 @@ CONTENTS
|
||||||
#+END:
|
#+END:
|
||||||
"
|
"
|
||||||
),
|
),
|
||||||
Some(("clocktable", Some(":scope file"), 31, 41, 48))
|
Some(("clocktable", Some(":scope file"), 31, 40, 48))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
|
use memchr::memchr;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FnDef;
|
pub struct FnDef;
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn valid_label(ch: u8) -> bool {
|
|
||||||
ch.is_ascii_alphanumeric() || ch == b'-' || ch == b'_'
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FnDef {
|
impl FnDef {
|
||||||
pub fn parse(src: &str) -> Option<(&str, &str, usize)> {
|
pub fn parse(src: &str) -> Option<(&str, &str, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("[fn:"));
|
||||||
starts_with!(src, "[fn:");
|
|
||||||
}
|
|
||||||
|
|
||||||
let label = until_while!(src, 4, b']', valid_label)?;
|
let label = memchr(b']', src.as_bytes()).filter(|&i| {
|
||||||
|
i != 4
|
||||||
if label == 4 {
|
&& src.as_bytes()[4..i]
|
||||||
return None;
|
.iter()
|
||||||
}
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
|
||||||
|
})?;
|
||||||
|
|
||||||
let end = eol!(src);
|
let end = eol!(src);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::{memchr, memchr2};
|
||||||
|
|
||||||
pub struct Keyword;
|
pub struct Keyword;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
|
@ -25,16 +27,18 @@ pub enum Key<'a> {
|
||||||
impl Keyword {
|
impl Keyword {
|
||||||
// return (key, value, offset)
|
// return (key, value, offset)
|
||||||
pub fn parse(src: &str) -> Option<(Key<'_>, &str, usize)> {
|
pub fn parse(src: &str) -> Option<(Key<'_>, &str, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("#+"));
|
||||||
starts_with!(src, "#+");
|
|
||||||
}
|
|
||||||
|
|
||||||
let key_end = until_while!(src, 2, |c| c == b':' || c == b'[', |c: u8| c
|
let bytes = src.as_bytes();
|
||||||
.is_ascii_alphabetic()
|
let key_end = memchr2(b':', b'[', bytes).filter(|&i| {
|
||||||
|| c == b'_')?;
|
bytes[2..i]
|
||||||
|
.iter()
|
||||||
|
.all(|&c| c.is_ascii_alphabetic() || c == b'_')
|
||||||
|
})?;
|
||||||
|
|
||||||
let option = if src.as_bytes()[key_end] == b'[' {
|
let option = if bytes[key_end] == b'[' {
|
||||||
let option = until_while!(src, key_end, b']', |c: u8| c != b'\n')?;
|
let option =
|
||||||
|
memchr(b']', bytes).filter(|&i| bytes[key_end..i].iter().all(|&c| c != b'\n'))?;
|
||||||
expect!(src, option + 1, b':')?;
|
expect!(src, option + 1, b':')?;
|
||||||
option + 1
|
option + 1
|
||||||
} else {
|
} else {
|
||||||
|
@ -100,8 +104,6 @@ fn parse() {
|
||||||
);
|
);
|
||||||
assert!(Keyword::parse("#+KE Y: VALUE").is_none());
|
assert!(Keyword::parse("#+KE Y: VALUE").is_none());
|
||||||
assert!(Keyword::parse("#+ KEY: VALUE").is_none());
|
assert!(Keyword::parse("#+ KEY: VALUE").is_none());
|
||||||
assert!(Keyword::parse("# +KEY: VALUE").is_none());
|
|
||||||
assert!(Keyword::parse(" #+KEY: VALUE").is_none());
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Keyword::parse("#+RESULTS:"),
|
Keyword::parse("#+RESULTS:"),
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl List {
|
||||||
let i = bytes
|
let i = bytes
|
||||||
.iter()
|
.iter()
|
||||||
.position(|&c| !c.is_ascii_digit())
|
.position(|&c| !c.is_ascii_digit())
|
||||||
.unwrap_or_else(|| src.len());
|
.unwrap_or_else(|| src.len() - 1);
|
||||||
let c = bytes[i];
|
let c = bytes[i];
|
||||||
if !(c == b'.' || c == b')') {
|
if !(c == b'.' || c == b')') {
|
||||||
return (false, false);
|
return (false, false);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::{memchr, memchr2};
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cookie<'a> {
|
pub struct Cookie<'a> {
|
||||||
|
@ -6,22 +8,25 @@ pub struct Cookie<'a> {
|
||||||
|
|
||||||
impl<'a> Cookie<'a> {
|
impl<'a> Cookie<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(Cookie<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(Cookie<'a>, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("["));
|
||||||
starts_with!(src, '[');
|
|
||||||
}
|
|
||||||
|
|
||||||
let num1 = until_while!(src, 1, |c| c == b'%' || c == b'/', |c: u8| c
|
let num1 = memchr2(b'%', b'/', src.as_bytes())
|
||||||
.is_ascii_digit())?;
|
.filter(|&i| src.as_bytes()[1..i].iter().all(|c| c.is_ascii_digit()))?;
|
||||||
|
|
||||||
if src.as_bytes()[num1] == b'%' && *src.as_bytes().get(num1 + 1)? == b']' {
|
if src.as_bytes()[num1] == b'%' && *src.as_bytes().get(num1 + 1)? == b']' {
|
||||||
Some((
|
Some((
|
||||||
Cookie {
|
Cookie {
|
||||||
value: &src[0..num1 + 2],
|
value: &src[0..=num1 + 1],
|
||||||
},
|
},
|
||||||
num1 + 2,
|
num1 + 2,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let num2 = until_while!(src, num1 + 1, b']', |c: u8| c.is_ascii_digit())?;
|
let num2 = memchr(b']', src.as_bytes()).filter(|&i| {
|
||||||
|
src.as_bytes()[num1 + 1..i]
|
||||||
|
.iter()
|
||||||
|
.all(|c| c.is_ascii_digit())
|
||||||
|
})?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
Cookie {
|
Cookie {
|
||||||
value: &src[0..=num2],
|
value: &src[0..=num2],
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::memchr;
|
||||||
|
|
||||||
pub struct Emphasis;
|
pub struct Emphasis;
|
||||||
|
|
||||||
impl Emphasis {
|
impl Emphasis {
|
||||||
|
@ -5,13 +7,10 @@ impl Emphasis {
|
||||||
pub fn parse(src: &str, marker: u8) -> Option<usize> {
|
pub fn parse(src: &str, marker: u8) -> Option<usize> {
|
||||||
expect!(src, 1, |c: u8| !c.is_ascii_whitespace())?;
|
expect!(src, 1, |c: u8| !c.is_ascii_whitespace())?;
|
||||||
|
|
||||||
let mut lines = 0;
|
let bytes = src.as_bytes();
|
||||||
let end = until_while!(src, 1, marker, |c| {
|
let end = memchr(marker, &bytes[1..])
|
||||||
if c == b'\n' {
|
.map(|i| i + 1)
|
||||||
lines += 1;
|
.filter(|&i| bytes[1..i].iter().filter(|&&c| c == b'\n').count() < 2)?;
|
||||||
}
|
|
||||||
lines < 2
|
|
||||||
})?;
|
|
||||||
|
|
||||||
expect!(src, end - 1, |c: u8| !c.is_ascii_whitespace())?;
|
expect!(src, end - 1, |c: u8| !c.is_ascii_whitespace())?;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::{memchr2, memchr2_iter};
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FnRef<'a> {
|
pub struct FnRef<'a> {
|
||||||
|
@ -5,26 +7,32 @@ pub struct FnRef<'a> {
|
||||||
definition: Option<&'a str>,
|
definition: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid_label(ch: u8) -> bool {
|
fn valid_label(ch: &u8) -> bool {
|
||||||
ch.is_ascii_alphanumeric() || ch == b'-' || ch == b'_'
|
ch.is_ascii_alphanumeric() || *ch == b'-' || *ch == b'_'
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FnRef<'a> {
|
impl<'a> FnRef<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(FnRef<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(FnRef<'a>, usize)> {
|
||||||
starts_with!(src, "[fn:");
|
debug_assert!(src.starts_with("[fn:"));
|
||||||
|
|
||||||
let label = until_while!(src, 4, |c| c == b']' || c == b':', valid_label)?;
|
let bytes = src.as_bytes();
|
||||||
|
let label = memchr2(b']', b':', &bytes[4..])
|
||||||
|
.map(|i| i + 4)
|
||||||
|
.filter(|&i| bytes[4..i].iter().all(valid_label))?;
|
||||||
|
|
||||||
if src.as_bytes()[label] == b':' {
|
if bytes[label] == b':' {
|
||||||
let mut pairs = 1;
|
let mut pairs = 1;
|
||||||
let def = until!(src[label..], |c| {
|
let def = memchr2_iter(b'[', b']', &bytes[label..])
|
||||||
if c == b'[' {
|
.map(|i| i + label)
|
||||||
|
.filter(|&i| {
|
||||||
|
if bytes[i] == b'[' {
|
||||||
pairs += 1;
|
pairs += 1;
|
||||||
} else if c == b']' {
|
} else {
|
||||||
pairs -= 1;
|
pairs -= 1;
|
||||||
}
|
}
|
||||||
c == b']' && pairs == 0
|
pairs == 0
|
||||||
})? + label;
|
})
|
||||||
|
.next()?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
FnRef {
|
FnRef {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::{memchr, memchr2};
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InlineCall<'a> {
|
pub struct InlineCall<'a> {
|
||||||
|
@ -11,17 +13,21 @@ pub struct InlineCall<'a> {
|
||||||
|
|
||||||
impl<'a> InlineCall<'a> {
|
impl<'a> InlineCall<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(InlineCall, usize)> {
|
pub fn parse(src: &'a str) -> Option<(InlineCall, usize)> {
|
||||||
starts_with!(src, "call_");
|
debug_assert!(src.starts_with("call_"));
|
||||||
|
|
||||||
let mut pos = until_while!(src, 5, |c| c == b'[' || c == b'(', |c: u8| c
|
let bytes = src.as_bytes();
|
||||||
.is_ascii_graphic())?;
|
let mut pos = memchr2(b'[', b'(', bytes)
|
||||||
|
.filter(|&i| bytes[5..i].iter().all(|c| c.is_ascii_graphic()))?;
|
||||||
let mut pos_;
|
let mut pos_;
|
||||||
|
|
||||||
let name = &src[5..pos];
|
let name = &src[5..pos];
|
||||||
|
|
||||||
let inside_header = if src.as_bytes()[pos] == b'[' {
|
let inside_header = if bytes[pos] == b'[' {
|
||||||
pos_ = pos;
|
pos_ = pos;
|
||||||
pos = until_while!(src, pos, b']', |c: u8| c != b'\n')? + 1;
|
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'(')?;
|
expect!(src, pos, b'(')?;
|
||||||
Some(&src[pos_ + 1..pos - 1])
|
Some(&src[pos_ + 1..pos - 1])
|
||||||
} else {
|
} else {
|
||||||
|
@ -29,13 +35,16 @@ impl<'a> InlineCall<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
pos_ = pos;
|
pos_ = pos;
|
||||||
pos = until_while!(src, pos, b')', |c| c != b'\n')?;
|
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 = &src[pos_ + 1..pos];
|
||||||
|
|
||||||
let end_header = if src.len() > pos + 1 && src.as_bytes()[pos + 1] == b'[' {
|
let end_header = if src.len() > pos + 1 && src.as_bytes()[pos + 1] == b'[' {
|
||||||
pos_ = pos;
|
pos_ = pos;
|
||||||
pos = until_while!(src, pos_ + 1, |c| c == b']', |c: u8| c != b'\n'
|
pos = memchr(b']', &bytes[pos_ + 1..])
|
||||||
&& c != b')')?;
|
.map(|i| i + pos_ + 1)
|
||||||
|
.filter(|&i| bytes[pos_ + 1..i].iter().all(|&c| c != b'\n' && c != b')'))?;
|
||||||
Some(&src[pos_ + 2..pos])
|
Some(&src[pos_ + 2..pos])
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::{memchr, memchr2};
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InlineSrc<'a> {
|
pub struct InlineSrc<'a> {
|
||||||
|
@ -8,18 +10,18 @@ pub struct InlineSrc<'a> {
|
||||||
|
|
||||||
impl<'a> InlineSrc<'a> {
|
impl<'a> InlineSrc<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(InlineSrc, usize)> {
|
pub fn parse(src: &'a str) -> Option<(InlineSrc, usize)> {
|
||||||
starts_with!(src, "src_");
|
debug_assert!(src.starts_with("src_"));
|
||||||
|
|
||||||
let lang = until_while!(src, 4, |c| c == b'[' || c == b'{', |c: u8| !c
|
let bytes = src.as_bytes();
|
||||||
.is_ascii_whitespace())?;
|
let lang = memchr2(b'[', b'{', bytes)
|
||||||
|
.filter(|&i| i != 4 && bytes[4..i].iter().all(|c| !c.is_ascii_whitespace()))?;
|
||||||
|
|
||||||
if lang == 4 {
|
if bytes[lang] == b'[' {
|
||||||
return None;
|
let option =
|
||||||
}
|
memchr(b']', bytes).filter(|&i| bytes[lang..i].iter().all(|c| *c != b'\n'))?;
|
||||||
|
let body = memchr(b'}', &bytes[option..])
|
||||||
if src.as_bytes()[lang] == b'[' {
|
.map(|i| i + option)
|
||||||
let option = until_while!(src, lang, b']', |c| c != b'\n')?;
|
.filter(|&i| bytes[option..i].iter().all(|c| *c != b'\n'))?;
|
||||||
let body = until_while!(src, option, b'}', |c| c != b'\n')?;
|
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
InlineSrc {
|
InlineSrc {
|
||||||
|
@ -30,7 +32,8 @@ impl<'a> InlineSrc<'a> {
|
||||||
body + 1,
|
body + 1,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
let body = until_while!(src, lang, b'}', |c| c != b'\n')?;
|
let body =
|
||||||
|
memchr(b'}', bytes).filter(|&i| bytes[lang..i].iter().all(|c| *c != b'\n'))?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
InlineSrc {
|
InlineSrc {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use memchr::memchr;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Link<'a> {
|
pub struct Link<'a> {
|
||||||
|
@ -7,13 +9,16 @@ pub struct Link<'a> {
|
||||||
|
|
||||||
impl<'a> Link<'a> {
|
impl<'a> Link<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(Link<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(Link<'a>, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("[["));
|
||||||
starts_with!(src, "[[");
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = until_while!(src, 2, b']', |c| c != b'<' && c != b'>' && c != b'\n')?;
|
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')
|
||||||
|
})?;
|
||||||
|
|
||||||
if cond_eq!(src, path + 1, b']') {
|
if *bytes.get(path + 1)? == b']' {
|
||||||
Some((
|
Some((
|
||||||
Link {
|
Link {
|
||||||
path: &src[2..path],
|
path: &src[2..path],
|
||||||
|
@ -21,8 +26,10 @@ impl<'a> Link<'a> {
|
||||||
},
|
},
|
||||||
path + 2,
|
path + 2,
|
||||||
))
|
))
|
||||||
} else if src.as_bytes()[path + 1] == b'[' {
|
} else if bytes[path + 1] == b'[' {
|
||||||
let desc = until_while!(src, path + 2, b']', |c| c != 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']')?;
|
expect!(src, desc + 1, b']')?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
|
@ -61,5 +68,4 @@ fn parse() {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert!(Link::parse("[[#id][desc]").is_none());
|
assert!(Link::parse("[[#id][desc]").is_none());
|
||||||
assert!(Link::parse("[#id][desc]]").is_none());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use jetscii::Substring;
|
use jetscii::Substring;
|
||||||
|
use memchr::memchr2;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -7,54 +8,83 @@ pub struct Macros<'a> {
|
||||||
pub args: Option<&'a str>,
|
pub args: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn valid_name(ch: u8) -> bool {
|
|
||||||
ch.is_ascii_alphanumeric() || ch == b'-' || ch == b'_'
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Macros<'a> {
|
impl<'a> Macros<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(Macros<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(Macros<'a>, usize)> {
|
||||||
starts_with!(src, "{{{");
|
debug_assert!(src.starts_with("{{{"));
|
||||||
|
|
||||||
expect!(src, 3, |c: u8| c.is_ascii_alphabetic())?;
|
expect!(src, 3, |c: u8| c.is_ascii_alphabetic())?;
|
||||||
|
|
||||||
let name = until_while!(src, 3, |c| c == b'}' || c == b'(', valid_name)?;
|
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'_')
|
||||||
|
})?;
|
||||||
|
|
||||||
if src.as_bytes()[name] == b'}' {
|
Some(if bytes[name] == b'}' {
|
||||||
expect!(src, name + 1, b'}')?;
|
expect!(src, name + 1, b'}')?;
|
||||||
expect!(src, name + 2, b'}')?;
|
expect!(src, name + 2, b'}')?;
|
||||||
Some((
|
(
|
||||||
Macros {
|
Macros {
|
||||||
name: &src[3..name],
|
name: &src[3..name],
|
||||||
args: None,
|
args: None,
|
||||||
},
|
},
|
||||||
name + 3,
|
name + 3,
|
||||||
))
|
)
|
||||||
} else {
|
} else {
|
||||||
let end = Substring::new("}}}").find(&src[name..]).map(|i| i + name)?;
|
let end = Substring::new(")}}}")
|
||||||
expect!(src, end - 1, b')')?;
|
.find(&src[name..])
|
||||||
Some((
|
.map(|i| i + name)?;
|
||||||
|
(
|
||||||
Macros {
|
Macros {
|
||||||
name: &src[3..name],
|
name: &src[3..name],
|
||||||
args: if name == end {
|
args: if name == end {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(&src[name + 1..end - 1])
|
Some(&src[name + 1..end])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
end + 3,
|
end + 4,
|
||||||
))
|
)
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse() {
|
fn parse() {
|
||||||
parse_succ!(Macros, "{{{poem(red,blue)}}}", name: "poem", args: Some("red,blue"));
|
assert_eq!(
|
||||||
parse_succ!(Macros, "{{{poem())}}}", name: "poem", args: Some(")"));
|
Macros::parse("{{{poem(red,blue)}}}"),
|
||||||
parse_succ!(Macros, "{{{author}}}", name: "author", args: None);
|
Some((
|
||||||
parse_fail!(Macros, "{{author}}}");
|
Macros {
|
||||||
parse_fail!(Macros, "{{{0uthor}}}");
|
name: "poem",
|
||||||
parse_fail!(Macros, "{{{author}}");
|
args: Some("red,blue")
|
||||||
parse_fail!(Macros, "{{{poem(}}}");
|
},
|
||||||
parse_fail!(Macros, "{{{poem)}}}");
|
"{{{poem(red,blue)}}}".len()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Macros::parse("{{{poem())}}}"),
|
||||||
|
Some((
|
||||||
|
Macros {
|
||||||
|
name: "poem",
|
||||||
|
args: Some(")")
|
||||||
|
},
|
||||||
|
"{{{poem())}}}".len()
|
||||||
|
))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Macros::parse("{{{author}}}"),
|
||||||
|
Some((
|
||||||
|
Macros {
|
||||||
|
name: "author",
|
||||||
|
args: 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
mod cookie;
|
mod cookie;
|
||||||
mod emphasis;
|
mod emphasis;
|
||||||
mod entity;
|
|
||||||
mod fn_ref;
|
mod fn_ref;
|
||||||
mod fragment;
|
|
||||||
mod inline_call;
|
mod inline_call;
|
||||||
mod inline_src;
|
mod inline_src;
|
||||||
mod link;
|
mod link;
|
||||||
|
@ -67,51 +65,47 @@ impl<'a> Object<'a> {
|
||||||
|
|
||||||
let mut pre = pos;
|
let mut pre = pos;
|
||||||
|
|
||||||
match (bytes[pos], bytes[pos + 1], bytes[pos + 2]) {
|
match bytes[pos] {
|
||||||
(b'@', b'@', _) => {
|
b'@' if bytes[pos + 1] == b'@' => {
|
||||||
if let Some((snippet, off)) = Snippet::parse(&src[pos..]) {
|
if let Some((snippet, off)) = Snippet::parse(&src[pos..]) {
|
||||||
brk!(Object::Snippet(snippet), off, pos);
|
brk!(Object::Snippet(snippet), off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(b'{', b'{', b'{') => {
|
b'{' if bytes[pos + 1] == b'{' && bytes[pos + 2] == b'{' => {
|
||||||
if let Some((macros, off)) = Macros::parse(&src[pos..]) {
|
if let Some((macros, off)) = Macros::parse(&src[pos..]) {
|
||||||
brk!(Object::Macros(macros), off, pos);
|
brk!(Object::Macros(macros), off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(b'<', b'<', b'<') => {
|
b'<' if bytes[pos + 1] == b'<' => {
|
||||||
|
if bytes[pos + 2] == b'<' {
|
||||||
if let Some((target, off)) = RadioTarget::parse(&src[pos..]) {
|
if let Some((target, off)) = RadioTarget::parse(&src[pos..]) {
|
||||||
brk!(Object::RadioTarget(target), off, pos);
|
brk!(Object::RadioTarget(target), off, pos);
|
||||||
}
|
}
|
||||||
}
|
} else if bytes[pos + 2] != b'\n' {
|
||||||
(b'<', b'<', third) => {
|
|
||||||
if third != b'\n' {
|
|
||||||
if let Some((target, off)) = Target::parse(&src[pos..]) {
|
if let Some((target, off)) = Target::parse(&src[pos..]) {
|
||||||
brk!(Object::Target(target), off, pos);
|
brk!(Object::Target(target), off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(b'[', b'f', b'n') => {
|
b'[' => {
|
||||||
|
if bytes[pos + 1..].starts_with(b"fn:") {
|
||||||
if let Some((fn_ref, off)) = FnRef::parse(&src[pos..]) {
|
if let Some((fn_ref, off)) = FnRef::parse(&src[pos..]) {
|
||||||
brk!(Object::FnRef(fn_ref), off, pos);
|
brk!(Object::FnRef(fn_ref), off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(b'[', b'[', _) => {
|
|
||||||
|
if bytes[pos + 1] == b'[' {
|
||||||
if let Some((link, off)) = Link::parse(&src[pos..]) {
|
if let Some((link, off)) = Link::parse(&src[pos..]) {
|
||||||
brk!(Object::Link(link), off, pos);
|
brk!(Object::Link(link), off, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(b'[', _, _) => {
|
|
||||||
if let Some((cookie, off)) = Cookie::parse(&src[pos..]) {
|
if let Some((cookie, off)) = Cookie::parse(&src[pos..]) {
|
||||||
brk!(Object::Cookie(cookie), off, pos);
|
brk!(Object::Cookie(cookie), off, pos);
|
||||||
}
|
}
|
||||||
// TODO: Timestamp
|
// TODO: Timestamp
|
||||||
}
|
}
|
||||||
(b'{', _, _)
|
b'{' | b' ' | b'"' | b',' | b'(' | b'\n' => pre += 1,
|
||||||
| (b' ', _, _)
|
|
||||||
| (b'"', _, _)
|
|
||||||
| (b',', _, _)
|
|
||||||
| (b'(', _, _)
|
|
||||||
| (b'\n', _, _) => pre += 1,
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,12 +140,12 @@ impl<'a> Object<'a> {
|
||||||
brk!(Object::Code(&src[pre + 1..pre + end]), end + 1, pre);
|
brk!(Object::Code(&src[pre + 1..pre + end]), end + 1, pre);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b'c' => {
|
b'c' if src[pre..].starts_with("call_") => {
|
||||||
if let Some((call, off)) = InlineCall::parse(&src[pre..]) {
|
if let Some((call, off)) = InlineCall::parse(&src[pre..]) {
|
||||||
brk!(Object::InlineCall(call), off, pre);
|
brk!(Object::InlineCall(call), off, pre);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b's' => {
|
b's' if src[pre..].starts_with("src_") => {
|
||||||
if let Some((src, off)) = InlineSrc::parse(&src[pre..]) {
|
if let Some((src, off)) = InlineSrc::parse(&src[pre..]) {
|
||||||
brk!(Object::InlineSrc(src), off, pre);
|
brk!(Object::InlineSrc(src), off, pre);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use jetscii::Substring;
|
use jetscii::Substring;
|
||||||
|
use memchr::memchr;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -9,15 +10,14 @@ pub struct Snippet<'a> {
|
||||||
|
|
||||||
impl<'a> Snippet<'a> {
|
impl<'a> Snippet<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(Snippet<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(Snippet<'a>, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("@@"));
|
||||||
starts_with!(src, "@@");
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = until_while!(src, 2, b':', |c: u8| c.is_ascii_alphanumeric() || c == b'-')?;
|
let name = memchr(b':', src.as_bytes()).filter(|&i| {
|
||||||
|
i != 2
|
||||||
if name == 2 {
|
&& src.as_bytes()[2..i]
|
||||||
return None;
|
.iter()
|
||||||
}
|
.all(|&c| c.is_ascii_alphanumeric() || c == b'-')
|
||||||
|
})?;
|
||||||
|
|
||||||
let end = Substring::new("@@")
|
let end = Substring::new("@@")
|
||||||
.find(&src[name + 1..])
|
.find(&src[name + 1..])
|
||||||
|
@ -66,7 +66,6 @@ fn parse() {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
assert!(Snippet::parse("@@html:<b>@").is_none());
|
assert!(Snippet::parse("@@html:<b>@").is_none());
|
||||||
assert!(Snippet::parse("@html:<b>@@").is_none());
|
|
||||||
assert!(Snippet::parse("@@html<b>@@").is_none());
|
assert!(Snippet::parse("@@html<b>@@").is_none());
|
||||||
assert!(Snippet::parse("@@:<b>@@").is_none());
|
assert!(Snippet::parse("@@:<b>@@").is_none());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use jetscii::Substring;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(PartialEq))]
|
#[cfg_attr(test, derive(PartialEq))]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
// TODO: text-markup, entities, latex-fragments, subscript and superscript
|
// TODO: text-markup, entities, latex-fragments, subscript and superscript
|
||||||
|
@ -5,17 +7,17 @@ pub struct RadioTarget<'a>(&'a str);
|
||||||
|
|
||||||
impl<'a> RadioTarget<'a> {
|
impl<'a> RadioTarget<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(RadioTarget<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(RadioTarget<'a>, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("<<<"));
|
||||||
starts_with!(src, "<<<");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect!(src, 3, |c| c != b' ')?;
|
expect!(src, 3, |c| c != b' ')?;
|
||||||
|
|
||||||
let end = until_while!(src, 3, b'>', |c| c != b'<' && c != b'\n')?;
|
let end = Substring::new(">>>").find(src).filter(|&i| {
|
||||||
|
src.as_bytes()[3..i]
|
||||||
|
.iter()
|
||||||
|
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
|
||||||
|
})?;
|
||||||
|
|
||||||
expect!(src, end - 1, |c| c != b' ')?;
|
expect!(src, end - 1, |c| c != b' ')?;
|
||||||
expect!(src, end + 1, b'>')?;
|
|
||||||
expect!(src, end + 2, b'>')?;
|
|
||||||
|
|
||||||
Some((RadioTarget(&src[3..end]), end + 3))
|
Some((RadioTarget(&src[3..end]), end + 3))
|
||||||
}
|
}
|
||||||
|
@ -27,16 +29,17 @@ pub struct Target<'a>(&'a str);
|
||||||
|
|
||||||
impl<'a> Target<'a> {
|
impl<'a> Target<'a> {
|
||||||
pub fn parse(src: &'a str) -> Option<(Target<'a>, usize)> {
|
pub fn parse(src: &'a str) -> Option<(Target<'a>, usize)> {
|
||||||
if cfg!(test) {
|
debug_assert!(src.starts_with("<<"));
|
||||||
starts_with!(src, "<<");
|
|
||||||
}
|
|
||||||
|
|
||||||
expect!(src, 2, |c| c != b' ')?;
|
expect!(src, 2, |c| c != b' ')?;
|
||||||
|
|
||||||
let end = until_while!(src, 2, b'>', |c| c != b'<' && c != b'\n')?;
|
let end = Substring::new(">>").find(src).filter(|&i| {
|
||||||
|
src.as_bytes()[2..i]
|
||||||
|
.iter()
|
||||||
|
.all(|&c| c != b'<' && c != b'\n' && c != b'>')
|
||||||
|
})?;
|
||||||
|
|
||||||
expect!(src, end - 1, |c| c != b' ')?;
|
expect!(src, end - 1, |c| c != b' ')?;
|
||||||
expect!(src, end + 1, b'>')?;
|
|
||||||
|
|
||||||
Some((Target(&src[2..end]), end + 2))
|
Some((Target(&src[2..end]), end + 2))
|
||||||
}
|
}
|
||||||
|
@ -52,13 +55,12 @@ fn parse() {
|
||||||
RadioTarget::parse("<<<tar get>>>").unwrap(),
|
RadioTarget::parse("<<<tar get>>>").unwrap(),
|
||||||
(RadioTarget("tar get"), "<<<tar get>>>".len())
|
(RadioTarget("tar get"), "<<<tar get>>>".len())
|
||||||
);
|
);
|
||||||
parse_fail!(RadioTarget, "<<<target >>>");
|
assert_eq!(RadioTarget::parse("<<<target >>>"), None);
|
||||||
parse_fail!(RadioTarget, "<<< target>>>");
|
assert_eq!(RadioTarget::parse("<<< target>>>"), None);
|
||||||
parse_fail!(RadioTarget, "<<<ta<get>>>");
|
assert_eq!(RadioTarget::parse("<<<ta<get>>>"), None);
|
||||||
parse_fail!(RadioTarget, "<<<ta>get>>>");
|
assert_eq!(RadioTarget::parse("<<<ta>get>>>"), None);
|
||||||
parse_fail!(RadioTarget, "<<<ta\nget>>>");
|
assert_eq!(RadioTarget::parse("<<<ta\nget>>>"), None);
|
||||||
parse_fail!(RadioTarget, "<<target>>>");
|
assert_eq!(RadioTarget::parse("<<<target>>"), None);
|
||||||
parse_fail!(RadioTarget, "<<<target>>");
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Target::parse("<<target>>").unwrap(),
|
Target::parse("<<target>>").unwrap(),
|
||||||
|
@ -68,11 +70,10 @@ fn parse() {
|
||||||
Target::parse("<<tar get>>").unwrap(),
|
Target::parse("<<tar get>>").unwrap(),
|
||||||
(Target("tar get"), "<<tar get>>".len())
|
(Target("tar get"), "<<tar get>>".len())
|
||||||
);
|
);
|
||||||
parse_fail!(Target, "<<target >>");
|
assert_eq!(Target::parse("<<target >>"), None);
|
||||||
parse_fail!(Target, "<< target>>");
|
assert_eq!(Target::parse("<< target>>"), None);
|
||||||
parse_fail!(Target, "<<ta<get>>");
|
assert_eq!(Target::parse("<<ta<get>>"), None);
|
||||||
parse_fail!(Target, "<<ta>get>>");
|
assert_eq!(Target::parse("<<ta>get>>"), None);
|
||||||
parse_fail!(Target, "<<ta\nget>>");
|
assert_eq!(Target::parse("<<ta\nget>>"), None);
|
||||||
parse_fail!(Target, "<target>>");
|
assert_eq!(Target::parse("<<target>"), None);
|
||||||
parse_fail!(Target, "<<target>");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -414,6 +414,8 @@ impl<'a> Iterator for Parser<'a> {
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|x| match x {
|
.map(|x| match x {
|
||||||
Container::Headline { beg, end } => {
|
Container::Headline { beg, end } => {
|
||||||
|
debug_assert!(self.off >= beg);
|
||||||
|
debug_assert!(self.off <= end);
|
||||||
if self.off >= end {
|
if self.off >= end {
|
||||||
self.end()
|
self.end()
|
||||||
} else if self.off == beg {
|
} else if self.off == beg {
|
||||||
|
|
129
src/utils.rs
129
src/utils.rs
|
@ -22,99 +22,21 @@ macro_rules! eol {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! until {
|
|
||||||
($src:expr, $until:tt) => {{
|
|
||||||
let mut pos = 0;
|
|
||||||
loop {
|
|
||||||
if pos >= $src.len() {
|
|
||||||
break None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if $until == $src.as_bytes()[pos] {
|
|
||||||
break Some(pos);
|
|
||||||
} else {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
($src:expr, $until:expr) => {{
|
|
||||||
let mut pos = 0;
|
|
||||||
loop {
|
|
||||||
if pos >= $src.len() {
|
|
||||||
break None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if $until($src.as_bytes()[pos]) {
|
|
||||||
break Some(pos);
|
|
||||||
} else {
|
|
||||||
pos += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! until_while {
|
|
||||||
($src:expr, $start:expr, $until:tt, $while:expr) => {{
|
|
||||||
let mut pos = $start;
|
|
||||||
loop {
|
|
||||||
if pos >= $src.len() {
|
|
||||||
break None;
|
|
||||||
} else if $until == $src.as_bytes()[pos] {
|
|
||||||
break Some(pos);
|
|
||||||
} else if $while($src.as_bytes()[pos]) {
|
|
||||||
pos += 1;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
break None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
($src:expr, $start:expr, $until:expr, $while:expr) => {{
|
|
||||||
let mut pos = $start;
|
|
||||||
loop {
|
|
||||||
if pos >= $src.len() {
|
|
||||||
break None;
|
|
||||||
} else if $until($src.as_bytes()[pos]) {
|
|
||||||
break Some(pos);
|
|
||||||
} else if $while($src.as_bytes()[pos]) {
|
|
||||||
pos += 1;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
break None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! cond_eq {
|
|
||||||
($s:ident, $i:expr, $p:expr) => {
|
|
||||||
if $i >= $s.len() {
|
|
||||||
return None;
|
|
||||||
} else {
|
|
||||||
$s.as_bytes()[$i] == $p
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! starts_with {
|
|
||||||
($s:ident, $p:expr) => {
|
|
||||||
if !$s.starts_with($p) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! skip_space {
|
macro_rules! skip_space {
|
||||||
($src:ident) => {
|
($src:ident) => {
|
||||||
until!($src, |c| c != b' ' && c != b'\t').unwrap_or(0)
|
$src.as_bytes()
|
||||||
|
.iter()
|
||||||
|
.position(|c| c != b' ' && c != b'\t')
|
||||||
|
.unwrap_or(0)
|
||||||
};
|
};
|
||||||
($src:ident, $from:expr) => {
|
($src:ident, $from:expr) => {
|
||||||
until!($src[$from..], |c| c != b' ' && c != b'\t').unwrap_or(0) + $from
|
$src[$from..]
|
||||||
|
.as_bytes()
|
||||||
|
.iter()
|
||||||
|
.position(|&c| c != b' ' && c != b'\t')
|
||||||
|
.map(|i| i + $from)
|
||||||
|
.unwrap_or(0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,34 +53,3 @@ macro_rules! skip_empty_line {
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! parse_fail {
|
|
||||||
($ty:ident, $src:expr) => {
|
|
||||||
assert_eq!($ty::parse($src), None);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! parse_succ {
|
|
||||||
($ty:ident, $src:expr, $($field:ident : $value:expr),* ) => {
|
|
||||||
assert_eq!(
|
|
||||||
$ty::parse($src),
|
|
||||||
Some((
|
|
||||||
$ty {
|
|
||||||
$( $field : $value ),*
|
|
||||||
},
|
|
||||||
$src.len()
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! lines {
|
|
||||||
($src:ident) => {
|
|
||||||
memchr::memchr_iter(b'\n', $src.as_bytes())
|
|
||||||
.map(|i| i + 1)
|
|
||||||
.chain(std::iter::once($src.len()))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue