This commit is contained in:
PoiScript 2019-01-04 16:53:20 +08:00
parent 2fef529f57
commit a85efe2056
22 changed files with 1776 additions and 7 deletions

14
src/elements/block.rs Normal file
View file

@ -0,0 +1,14 @@
pub enum BlockStart {
name: BlockName,
}
pub enum BlockName {
Center,
Comment,
Example,
Export,
Quote,
Src,
Verbose,
Special
}

79
src/elements/fn_def.rs Normal file
View file

@ -0,0 +1,79 @@
#[cfg_attr(test, derive(PartialEq, Debug))]
pub struct FnDef<'a> {
pub label: &'a str,
pub contents: &'a str,
}
#[inline]
fn valid_label(ch: u8) -> bool {
ch.is_ascii_alphanumeric() || ch == b'-' || ch == b'_'
}
impl<'a> FnDef<'a> {
pub fn parse(src: &'a str) -> Option<(FnDef<'a>, usize)> {
starts_with!(src, "[fn:");
let label = until_while!(src, 4, b']', valid_label);
if label == 4 {
return None;
}
let end = eol!(src);
Some((
FnDef {
label: &src[4..label],
contents: &src[label + 1..end],
},
end,
))
}
}
#[test]
fn parse() {
assert_eq!(
FnDef::parse("[fn:1] https://orgmode.org").unwrap(),
(
FnDef {
label: "1",
contents: " https://orgmode.org",
},
"[fn:1] https://orgmode.org".len()
)
);
assert_eq!(
FnDef::parse("[fn:word_1] https://orgmode.org").unwrap(),
(
FnDef {
label: "word_1",
contents: " https://orgmode.org",
},
"[fn:word_1] https://orgmode.org".len()
)
);
assert_eq!(
FnDef::parse("[fn:WORD-1] https://orgmode.org").unwrap(),
(
FnDef {
label: "WORD-1",
contents: " https://orgmode.org",
},
"[fn:WORD-1] https://orgmode.org".len()
)
);
assert_eq!(
FnDef::parse("[fn:WORD]").unwrap(),
(
FnDef {
label: "WORD",
contents: "",
},
"[fn:WORD]".len()
)
);
assert!(FnDef::parse("[fn:] https://orgmode.org").is_none());
assert!(FnDef::parse("[fn:wor d] https://orgmode.org").is_none());
assert!(FnDef::parse("[fn:WORD https://orgmode.org").is_none());
}

175
src/elements/keyword.rs Normal file
View file

@ -0,0 +1,175 @@
#[cfg_attr(test, derive(PartialEq, Debug))]
pub struct Keyword<'a> {
pub key: &'a str,
pub value: &'a str,
}
impl<'a> Keyword<'a> {
pub fn parse(src: &'a str) -> Option<(Keyword<'a>, usize)> {
starts_with!(src, "#+");
let key = until_while!(src, 2, b':', |c: u8| c.is_ascii_uppercase() || c == b'_');
let end = eol!(src);
if end == key + 1 {
Some((
Keyword {
key: &src[2..key],
value: "",
},
end,
))
} else {
let space = position!(src, key + 1, |c| !c.is_ascii_whitespace());
Some((
Keyword {
key: &src[2..key],
value: &src[space..end],
},
end,
))
}
}
}
#[cfg_attr(test, derive(PartialEq, Debug))]
pub struct AffKeyword<'a> {
pub key: AffKeywordKey<'a>,
pub option: Option<&'a str>,
pub value: &'a str,
}
#[cfg_attr(test, derive(PartialEq, Debug))]
pub enum AffKeywordKey<'a> {
Caption,
Header,
Name,
Plot,
Results,
AttrBackend(&'a str),
}
// impl<'a> AffKeyword<'a> {
// pub fn parse(src: &'a str) -> Option<AffKeyword<'a>> {
// if src.len() < 3 && !src.starts_with("#+") {
// return None;
// }
// let end = src.nextline();
// let colon = src[2..end].until(b':');
// let key_index = src[2..end]
// .as_bytes()
// .iter()
// .position(|&c| !(c.is_ascii_alphanumeric() || c == b'-' || c == b'_'));
// // .unwrap_or(2);
// // let key = match parse_key(&src[2..key_index]) {
// // }
// // if key.is_none() {
// // return None;
// // }
// if let Some(key_index) = key {
// // if src.as_bytes()[key_index] = b':'
// parse_key(&src[2..key_index])
// .filter(|_| src.as_bytes()[colon + 1] == b' ')
// .map(|key| {
// if src.as_bytes()[key_index + 1] == b'[' && src.as_bytes()[colon - 1] == b']' {
// AffKeyword {
// key,
// value: &s[colon + 2..end],
// option: Some(&s[key_index + 2..colon - 1]),
// }
// } else {
// AffKeyword {
// key,
// value: &s[colon + 2..end],
// option: None,
// }
// }
// })
// } else {
// None
// }
// }
// }
fn parse_key<'a>(key: &'a str) -> Option<AffKeywordKey<'a>> {
match key {
"CAPTION" => Some(AffKeywordKey::Caption),
"HEADER" => Some(AffKeywordKey::Header),
"NAME" => Some(AffKeywordKey::Name),
"PLOT" => Some(AffKeywordKey::Plot),
"RESULTS" => Some(AffKeywordKey::Results),
k => {
if k.starts_with("ATTR_")
&& k[5..]
.as_bytes()
.iter()
.all(|&c| c.is_ascii_alphanumeric() || c == b'-' || c == b'_')
{
Some(AffKeywordKey::AttrBackend(&k[5..]))
} else {
None
}
}
}
}
#[test]
fn parse() {
assert_eq!(
Keyword::parse("#+KEY:").unwrap(),
(
Keyword {
key: "KEY",
value: "",
},
"#+KEY:".len()
)
);
assert_eq!(
Keyword::parse("#+KEY: VALUE").unwrap(),
(
Keyword {
key: "KEY",
value: "VALUE",
},
"#+KEY: VALUE".len()
)
);
assert_eq!(
Keyword::parse("#+K_E_Y: VALUE").unwrap(),
(
Keyword {
key: "K_E_Y",
value: "VALUE",
},
"#+K_E_Y: VALUE".len()
)
);
assert_eq!(
Keyword::parse("#+KEY:VALUE").unwrap(),
(
Keyword {
key: "KEY",
value: "VALUE",
},
"#+KEY:VALUE".len()
)
);
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());
}
// #[test]
// fn parse_affiliated_keyword() {
// assert_eq!(AffKeyword::parse("#+KEY: VALUE"), None);
// assert_eq!(AffKeyword::parse("#+CAPTION: VALUE"), None);
// }

18
src/elements/mod.rs Normal file
View file

@ -0,0 +1,18 @@
pub mod fn_def;
pub mod keyword;
pub mod rule;
pub use self::fn_def::FnDef;
pub use self::keyword::Keyword;
pub use self::rule::Rule;
pub enum Element<'a> {
Paragraph(&'a str),
}
impl<'a> Element<'a> {
pub fn find_elem(src: &'a str) -> (Element<'a>, usize) {
// TODO
(Element::Paragraph(src), src.len())
}
}

32
src/elements/rule.rs Normal file
View file

@ -0,0 +1,32 @@
pub struct Rule;
impl Rule {
pub fn parse(src: &str) -> Option<usize> {
let end = eol!(src);
let leading = until_while!(src, 0, b'-', |c| c == b' ' || c == b'\t');
if src[leading..end].chars().all(|c| c == '-') && end - leading > 4 {
Some(end)
} else {
None
}
}
}
#[test]
fn parse() {
assert!(Rule::parse("-----").is_some());
assert!(Rule::parse("--------").is_some());
assert!(Rule::parse(" -----").is_some());
assert!(Rule::parse("\t\t-----").is_some());
assert!(Rule::parse("").is_none());
assert!(Rule::parse("----").is_none());
assert!(Rule::parse(" ----").is_none());
assert!(Rule::parse(" 0----").is_none());
assert!(Rule::parse("0 ----").is_none());
assert!(Rule::parse("0------").is_none());
assert!(Rule::parse("----0----").is_none());
assert!(Rule::parse("\t\t----").is_none());
assert!(Rule::parse("------0").is_none());
assert!(Rule::parse("----- 0").is_none());
}