feat(parser): table blank lines

This commit is contained in:
PoiScript 2019-10-30 14:43:55 +08:00
parent ead6ea6289
commit b446471535
10 changed files with 151 additions and 114 deletions

View file

@ -1,8 +1,11 @@
use std::borrow::Cow;
use nom::{
bytes::complete::tag_no_case, character::complete::alpha1, error::ParseError,
sequence::preceded, IResult,
bytes::complete::tag_no_case,
character::complete::{alpha1, space0},
error::ParseError,
sequence::preceded,
IResult,
};
use crate::parsers::{blank_lines, line, take_lines_while};
@ -226,6 +229,7 @@ pub fn parse_block_element(input: &str) -> Option<(&str, (&str, Option<&str>, &s
fn parse_block_element_internal<'a, E: ParseError<&'a str>>(
input: &'a str,
) -> IResult<&str, (&str, Option<&str>, &str, usize), E> {
let (input, _) = space0(input)?;
let (input, name) = preceded(tag_no_case("#+BEGIN_"), alpha1)(input)?;
let (input, args) = line(input)?;
let end_line = format!("#+END_{}", name);

View file

@ -139,6 +139,7 @@ impl Clock<'_> {
}
fn parse_clock<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Clock, E> {
let (input, _) = space0(input)?;
let (input, _) = tag("CLOCK:")(input)?;
let (input, _) = space0(input)?;
let (input, timestamp) = parse_inactive(input)?;

View file

@ -14,7 +14,10 @@ pub struct Comment<'a> {
impl Comment<'_> {
pub(crate) fn parse(input: &str) -> Option<(&str, Comment<'_>)> {
let (input, value) = take_lines_while(|line| line == "#" || line.starts_with("# "))(input);
let (input, value) = take_lines_while(|line| {
let line = line.trim_start();
line == "#" || line.starts_with("# ")
})(input);
let (input, blank) = blank_lines(input);
if value.is_empty() {

View file

@ -2,6 +2,7 @@ use std::borrow::Cow;
use nom::{
bytes::complete::{tag, take_while1},
character::complete::space0,
error::ParseError,
sequence::delimited,
IResult,
@ -56,6 +57,7 @@ pub fn parse_drawer<'a, E: ParseError<&'a str>>(
pub fn parse_drawer_without_blank<'a, E: ParseError<&'a str>>(
input: &'a str,
) -> IResult<&str, (Drawer, &str), E> {
let (input, _) = space0(input)?;
let (input, name) = delimited(
tag(":"),
take_while1(|c: char| c.is_ascii_alphabetic() || c == '-' || c == '_'),

View file

@ -2,7 +2,7 @@ use std::borrow::Cow;
use nom::{
bytes::complete::tag_no_case,
character::complete::{alpha1, space1},
character::complete::{alpha1, space0, space1},
error::ParseError,
IResult,
};
@ -46,6 +46,7 @@ impl DynBlock<'_> {
fn parse_dyn_block<'a, E: ParseError<&'a str>>(
input: &'a str,
) -> IResult<&str, (DynBlock, &str), E> {
let (input, _) = space0(input)?;
let (input, _) = tag_no_case("#+BEGIN:")(input)?;
let (input, _) = space1(input)?;
let (input, name) = alpha1(input)?;

View file

@ -15,7 +15,10 @@ pub struct FixedWidth<'a> {
impl FixedWidth<'_> {
pub(crate) fn parse(input: &str) -> Option<(&str, FixedWidth<'_>)> {
let (input, value) = take_lines_while(|line| line == ":" || line.starts_with(": "))(input);
let (input, value) = take_lines_while(|line| {
let line = line.trim_start();
line == ":" || line.starts_with(": ")
})(input);
let (input, blank) = blank_lines(input);
if value.is_empty() {

View file

@ -2,6 +2,7 @@ use std::borrow::Cow;
use nom::{
bytes::complete::{tag, take_till},
character::complete::space0,
combinator::opt,
error::ParseError,
sequence::delimited,
@ -67,6 +68,7 @@ pub fn parse_keyword(input: &str) -> Option<(&str, (&str, Option<&str>, &str, us
fn parse_keyword_internal<'a, E: ParseError<&'a str>>(
input: &'a str,
) -> IResult<&str, (&str, Option<&str>, &str, usize), E> {
let (input, _) = space0(input)?;
let (input, _) = tag("#+")(input)?;
let (input, key) = take_till(|c: char| c.is_ascii_whitespace() || c == ':' || c == '[')(input)?;
let (input, optional) = opt(delimited(

View file

@ -1,4 +1,6 @@
use nom::{bytes::complete::take_while_m_n, error::ParseError, IResult};
use nom::{
bytes::complete::take_while_m_n, character::complete::space0, error::ParseError, IResult,
};
use crate::parsers::{blank_lines, eol};
@ -18,6 +20,7 @@ impl Rule {
}
fn parse_rule<'a, E: ParseError<&'a str>>(input: &'a str) -> IResult<&str, Rule, E> {
let (input, _) = space0(input)?;
let (input, _) = take_while_m_n(5, usize::max_value(), |c| c == '-')(input)?;
let (input, _) = eol(input)?;
let (input, blank) = blank_lines(input);

View file

@ -1,12 +1,8 @@
use std::borrow::Cow;
use nom::{
combinator::{peek, verify},
error::ParseError,
IResult,
};
use memchr::memchr;
use crate::parsers::{line, take_lines_while};
use crate::parsers::{blank_lines, take_lines_while};
/// Table Elemenet
#[derive(Debug)]
@ -16,20 +12,64 @@ use crate::parsers::{line, take_lines_while};
pub enum Table<'a> {
/// "org" type table
#[cfg_attr(feature = "ser", serde(rename = "org"))]
Org { tblfm: Option<Cow<'a, str>> },
Org {
#[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
tblfm: Option<Cow<'a, str>>,
/// Numbers of blank lines between last table's line and next non-blank
/// line or buffer's end
post_blank: usize,
},
/// "table.el" type table
#[cfg_attr(feature = "ser", serde(rename = "table.el"))]
TableEl { value: Cow<'a, str> },
TableEl {
value: Cow<'a, str>,
/// Numbers of blank lines between last table's line and next non-blank
/// line or buffer's end
post_blank: usize,
},
}
impl Table<'_> {
pub fn parse_table_el(input: &str) -> Option<(&str, Table<'_>)> {
let first_line = memchr(b'\n', input.as_bytes())
.map(|i| input[0..i].trim())
.unwrap_or_else(|| input.trim());
// first line must be the "+-" string and followed by plus or minus signs
if !first_line.starts_with("+-")
|| first_line
.as_bytes()
.iter()
.any(|&c| c != b'+' && c != b'-')
{
return None;
}
let (input, content) = take_lines_while(|line| {
let line = line.trim_start();
line.starts_with('|') || line.starts_with('+')
})(input);
let (input, blank) = blank_lines(input);
Some((
input,
Table::TableEl {
value: content.into(),
post_blank: blank,
},
))
}
pub fn into_owned(self) -> Table<'static> {
match self {
Table::Org { tblfm } => Table::Org {
Table::Org { tblfm, post_blank } => Table::Org {
tblfm: tblfm.map(Into::into).map(Cow::Owned),
post_blank: post_blank,
},
Table::TableEl { value } => Table::TableEl {
Table::TableEl { value, post_blank } => Table::TableEl {
value: value.into_owned().into(),
post_blank: post_blank,
},
}
}
@ -46,57 +86,28 @@ pub enum TableRow {
Rule,
}
impl TableRow {
pub(crate) fn parse(input: &str) -> Option<TableRow> {
if input.starts_with("|-") {
Some(TableRow::Rule)
} else if input.starts_with('|') {
Some(TableRow::Standard)
} else {
None
}
}
}
pub fn parse_table_el(input: &str) -> Option<(&str, &str)> {
parse_table_el_internal::<()>(input).ok()
}
fn parse_table_el_internal<'a, E: ParseError<&'a str>>(
input: &'a str,
) -> IResult<&'a str, &'a str, E> {
let (input, _) = peek(verify(line, |s: &str| {
let s = s.trim();
s.starts_with("+-") && s.as_bytes().iter().all(|&c| c == b'+' || c == b'-')
}))(input)?;
let (input, content) =
take_lines_while(|line| line.starts_with('|') || line.starts_with('+'))(input);
Ok((input, content))
}
#[test]
fn parse_table_el_() {
use nom::error::VerboseError;
assert_eq!(
parse_table_el_internal::<VerboseError<&str>>(
r#"+---+
| |
+---+
Table::parse_table_el(
r#" +---+
| |
+---+
"#
),
Ok((
r#"
"#,
r#"+---+
| |
+---+
Some((
"",
Table::TableEl {
value: r#" +---+
| |
+---+
"#
.into(),
post_blank: 1
}
))
);
assert!(parse_table_el_internal::<VerboseError<&str>>("").is_err());
assert!(parse_table_el_internal::<VerboseError<&str>>("+----|---").is_err());
assert!(Table::parse_table_el("").is_none());
assert!(Table::parse_table_el("+----|---").is_none());
}