14 Commits

Author SHA1 Message Date
Tom Alexander
e673aa862e Publish version 0.1.9.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-03 00:18:00 -04:00
Tom Alexander
3b6659c5fd Merge branch 'table_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-03 00:14:21 -04:00
Tom Alexander
68a3f8b87e Fix table rule row detection. 2023-10-03 00:13:15 -04:00
Tom Alexander
b1244de1dc Compare row type. 2023-10-03 00:03:58 -04:00
Tom Alexander
e5a402ee1b Compare type and value.
All checks were successful
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
Since we only support org-mode tables, type is always org. Value seems to always be nil, not sure why.
2023-10-02 23:57:17 -04:00
Tom Alexander
d4a2ad4a7f Merge branch 'node_property_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 23:47:11 -04:00
Tom Alexander
3d1b2713ed Compare key and value. 2023-10-02 23:45:31 -04:00
Tom Alexander
60bec4695b Merge branch 'drawer_properties' 2023-10-02 23:38:34 -04:00
Tom Alexander
d992947ff1 Compare name. 2023-10-02 23:34:06 -04:00
Tom Alexander
76fb24d1d1 Merge branch 'comment_properties'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-02 23:30:29 -04:00
Tom Alexander
b56318fbe4 Add TODO comment. 2023-10-02 23:29:58 -04:00
Tom Alexander
8169499de3 Compare value. 2023-10-02 23:28:32 -04:00
Tom Alexander
29d9e76545 Merge branch 'footnote_definition_properties' 2023-10-02 22:50:26 -04:00
Tom Alexander
4d356b855e Compare label. 2023-10-02 22:48:54 -04:00
9 changed files with 169 additions and 36 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "organic" name = "organic"
version = "0.1.8" version = "0.1.9"
authors = ["Tom Alexander <tom@fizz.buzz>"] authors = ["Tom Alexander <tom@fizz.buzz>"]
description = "An org-mode parser." description = "An org-mode parser."
edition = "2021" edition = "2021"

View File

@@ -1,4 +1,5 @@
# Comment # Comment
#
# indented line # indented line
# At the top of the file # At the top of the file

View File

@@ -86,6 +86,7 @@ use crate::types::Superscript;
use crate::types::Table; use crate::types::Table;
use crate::types::TableCell; use crate::types::TableCell;
use crate::types::TableRow; use crate::types::TableRow;
use crate::types::TableRowType;
use crate::types::Target; use crate::types::Target;
use crate::types::Time; use crate::types::Time;
use crate::types::TimeUnit; use crate::types::TimeUnit;
@@ -1172,9 +1173,20 @@ fn compare_footnote_definition<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let mut child_status = Vec::new(); let mut child_status = Vec::new();
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut message = None;
// TODO: Compare :label :pre-blank // TODO: Compare :pre-blank
// Compare label
let label = get_property_quoted_string(emacs, ":label")?
.ok_or("Footnote definitions should have a name.")?;
if label != rust.label {
this_status = DiffStatus::Bad;
message = Some(format!(
"Label mismatch (emacs != rust) {:?} != {:?}",
label, rust.label
));
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1197,9 +1209,20 @@ fn compare_comment<'b, 's>(
rust: &'b Comment<'s>, rust: &'b Comment<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let child_status = Vec::new(); let child_status = Vec::new();
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut message = None;
// TODO: Compare :value
// Compare value
let value =
get_property_quoted_string(emacs, ":value")?.ok_or("Comments should have a value.")?;
let rust_value = rust.get_value();
if value != rust_value {
this_status = DiffStatus::Bad;
message = Some(format!(
"Value mismatch (emacs != rust) {:?} != {:?}",
value, rust_value
));
}
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
@@ -1219,9 +1242,19 @@ fn compare_drawer<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let mut child_status = Vec::new(); let mut child_status = Vec::new();
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut message = None;
// TODO: Compare :drawer-name
// Compare drawer-name
let name =
get_property_quoted_string(emacs, ":drawer-name")?.ok_or("Drawers should have a name.")?;
if name != rust.name {
this_status = DiffStatus::Bad;
message = Some(format!(
"Name mismatch (emacs != rust) {:?} != {:?}",
name, rust.name
));
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1269,10 +1302,33 @@ fn compare_node_property<'b, 's>(
rust: &'b NodeProperty<'s>, rust: &'b NodeProperty<'s>,
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let child_status = Vec::new(); let child_status = Vec::new();
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut message = None;
// TODO: Compare :key :value // Compare key
let key =
get_property_quoted_string(emacs, ":key")?.ok_or("Node properties should have a key.")?;
if key != rust.name {
this_status = DiffStatus::Bad;
message = Some(format!(
"Key mismatch (emacs != rust) {:?} != {:?}",
key, rust.name
));
}
// Compare value
let value = get_property_quoted_string(emacs, ":value")?;
match (value.as_ref(), rust.value) {
(None, None) => {}
(Some(emacs_value), Some(rust_value)) if emacs_value == rust_value => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Value mismatch (emacs != rust) {:?} != {:?}",
value, rust.value
));
}
}
Ok(DiffResult { Ok(DiffResult {
status: this_status, status: this_status,
@@ -1333,7 +1389,23 @@ fn compare_table<'b, 's>(
} }
} }
// TODO: Compare :type :value // Compare type
let table_type = get_property_unquoted_atom(emacs, ":type")?.expect("Table should have a type");
if table_type != "org" {
this_status = DiffStatus::Bad;
message = Some(format!(
"Table type mismatch (emacs != rust) {:?} != {:?}",
table_type, "org"
));
}
// Compare value
let value = get_property(emacs, ":value")?;
if value.is_some() {
// I don't know what :value is for, but it seems to always be nil. This is here to alert me to value being non-nil so I can investigate.
this_status = DiffStatus::Bad;
message = Some(format!("Non-nil value {:?}", value))
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);
@@ -1357,12 +1429,23 @@ fn compare_table_row<'b, 's>(
) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> { ) -> Result<DiffEntry<'b, 's>, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let mut child_status = Vec::new(); let mut child_status = Vec::new();
let this_status = DiffStatus::Good; let mut this_status = DiffStatus::Good;
let message = None; let mut message = None;
// TODO: Compare :type // Compare type
// let row_type = get_property_unquoted_atom(emacs, ":type")?;
// :type is an unquoted atom of either standard or rule let rust_row_type = rust.get_type();
match (row_type, &rust_row_type) {
(Some("standard"), TableRowType::Standard) => {}
(Some("rule"), TableRowType::Rule) => {}
_ => {
this_status = DiffStatus::Bad;
message = Some(format!(
"Type mismatch (emacs != rust) {:?} != {:?}",
row_type, rust_row_type
));
}
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) { for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?); child_status.push(compare_ast_node(source, emacs_child, rust_child.into())?);

View File

@@ -1,18 +1,19 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::anychar;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt; use nom::combinator::recognize;
use nom::multi::many0; use nom::multi::many0;
use nom::multi::many_till;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::util::get_consumed; use super::util::get_consumed;
use super::util::org_line_ending;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::RefContext; use crate::context::RefContext;
@@ -38,15 +39,29 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
let parser_context = context.with_additional_node(&parser_context); let parser_context = context.with_additional_node(&parser_context);
let comment_line_matcher = parser_with_context!(comment_line)(&parser_context); let comment_line_matcher = parser_with_context!(comment_line)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context); let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, _first_line) = comment_line_matcher(input)?; let (remaining, first_line) = comment_line_matcher(input)?;
let (remaining, _remaining_lines) = let (remaining, mut remaining_lines) =
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?; many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
let mut value = Vec::with_capacity(remaining_lines.len() + 1);
let last_line = remaining_lines.pop();
if let Some(last_line) = last_line {
value.push(Into::<&str>::into(first_line));
value.extend(remaining_lines.into_iter().map(Into::<&str>::into));
let last_line = Into::<&str>::into(last_line);
// Trim the line ending from the final line.
value.push(&last_line[..(last_line.len() - 1)])
} else {
// Trim the line ending from the only line.
let only_line = Into::<&str>::into(first_line);
value.push(&only_line[..(only_line.len() - 1)])
}
Ok(( Ok((
remaining, remaining,
Comment { Comment {
source: source.into(), source: source.into(),
value,
}, },
)) ))
} }
@@ -57,14 +72,13 @@ fn comment_line<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _indent) = space0(input)?; let (remaining, _) = tuple((space0, tag("#")))(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple(( if let Ok((remaining, line_break)) = org_line_ending(remaining) {
tag("#"), return Ok((remaining, line_break));
opt(tuple((space1, is_not("\r\n")))), }
alt((line_ending, eof)), let (remaining, _) = tag(" ")(remaining)?;
))(remaining)?; let (remaining, value) = recognize(many_till(anychar, org_line_ending))(remaining)?;
let source = get_consumed(input, remaining); Ok((remaining, value))
Ok((remaining, source))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -101,7 +101,7 @@ fn node_property<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>, context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, NodeProperty<'s>> { ) -> Res<OrgSource<'s>, NodeProperty<'s>> {
let (remaining, (_start_of_line, _leading_whitespace, _open_colon, _name, _close_colon)) = let (remaining, (_start_of_line, _leading_whitespace, _open_colon, name, _close_colon)) =
tuple(( tuple((
start_of_line, start_of_line,
space0, space0,
@@ -120,6 +120,7 @@ fn node_property<'b, 'g, 'r, 's>(
remaining, remaining,
NodeProperty { NodeProperty {
source: source.into(), source: source.into(),
name: Into::<&str>::into(name),
value: None, value: None,
}, },
)) ))
@@ -132,6 +133,7 @@ fn node_property<'b, 'g, 'r, 's>(
remaining, remaining,
NodeProperty { NodeProperty {
source: source.into(), source: source.into(),
name: Into::<&str>::into(name),
value: Some(value.into()), value: Some(value.into()),
}, },
)) ))

View File

@@ -3,8 +3,8 @@ use nom::bytes::complete::is_not;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::combinator::eof;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::opt;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
@@ -17,6 +17,7 @@ use super::keyword::table_formula_keyword;
use super::object_parser::table_cell_set_object; use super::object_parser::table_cell_set_object;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::util::exit_matcher_parser; use super::util::exit_matcher_parser;
use super::util::org_line_ending;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ExitClass; use crate::context::ExitClass;
@@ -105,7 +106,7 @@ fn org_mode_table_row_rule<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, TableRow<'s>> { ) -> Res<OrgSource<'s>, TableRow<'s>> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, _) = tuple((space0, tag("|-"), is_not("\r\n"), line_ending))(input)?; let (remaining, _) = tuple((space0, tag("|-"), opt(is_not("\r\n")), org_line_ending))(input)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,
@@ -125,7 +126,7 @@ fn org_mode_table_row_regular<'b, 'g, 'r, 's>(
let (remaining, _) = tuple((space0, tag("|")))(input)?; let (remaining, _) = tuple((space0, tag("|")))(input)?;
let (remaining, children) = let (remaining, children) =
many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?; many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?;
let (remaining, _tail) = recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?; let (remaining, _tail) = recognize(tuple((space0, org_line_ending)))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok(( Ok((
remaining, remaining,

View File

@@ -94,6 +94,7 @@ pub struct PropertyDrawer<'s> {
#[derive(Debug)] #[derive(Debug)]
pub struct NodeProperty<'s> { pub struct NodeProperty<'s> {
pub source: &'s str, pub source: &'s str,
pub name: &'s str,
pub value: Option<&'s str>, pub value: Option<&'s str>,
} }
@@ -110,6 +111,12 @@ pub struct TableRow<'s> {
pub children: Vec<TableCell<'s>>, pub children: Vec<TableCell<'s>>,
} }
#[derive(Debug)]
pub enum TableRowType {
Standard,
Rule,
}
impl<'s> StandardProperties<'s> for PlainList<'s> { impl<'s> StandardProperties<'s> for PlainList<'s> {
fn get_source<'b>(&'b self) -> &'s str { fn get_source<'b>(&'b self) -> &'s str {
self.source self.source
@@ -193,3 +200,13 @@ impl<'s> PlainListItem<'s> {
.map(|(checkbox_type, _)| checkbox_type) .map(|(checkbox_type, _)| checkbox_type)
} }
} }
impl<'s> TableRow<'s> {
pub fn get_type(&self) -> TableRowType {
if self.children.is_empty() {
TableRowType::Rule
} else {
TableRowType::Standard
}
}
}

View File

@@ -12,6 +12,7 @@ pub struct Paragraph<'s> {
#[derive(Debug)] #[derive(Debug)]
pub struct Comment<'s> { pub struct Comment<'s> {
pub source: &'s str, pub source: &'s str,
pub value: Vec<&'s str>,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -209,3 +210,16 @@ impl<'s> StandardProperties<'s> for LatexEnvironment<'s> {
self.source self.source
} }
} }
impl<'s> Comment<'s> {
pub fn get_value(&self) -> String {
// TODO: maybe we should handle parsing here instead of storing the parsing result in the AST since I imagine getting the value of comments won't be a common operation.
let final_size = self.value.iter().map(|line| line.len()).sum();
let mut ret = String::with_capacity(final_size);
for line in &self.value {
ret.push_str(line);
}
ret
}
}

View File

@@ -35,6 +35,7 @@ pub use greater_element::QuoteBlock;
pub use greater_element::SpecialBlock; pub use greater_element::SpecialBlock;
pub use greater_element::Table; pub use greater_element::Table;
pub use greater_element::TableRow; pub use greater_element::TableRow;
pub use greater_element::TableRowType;
pub use lesser_element::BabelCall; pub use lesser_element::BabelCall;
pub use lesser_element::Clock; pub use lesser_element::Clock;
pub use lesser_element::Comment; pub use lesser_element::Comment;