parser
This commit is contained in:
parent
2fef529f57
commit
a85efe2056
22 changed files with 1776 additions and 7 deletions
14
src/elements/block.rs
Normal file
14
src/elements/block.rs
Normal 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
79
src/elements/fn_def.rs
Normal 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
175
src/elements/keyword.rs
Normal 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
18
src/elements/mod.rs
Normal 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
32
src/elements/rule.rs
Normal 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());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue