Merge branch 'table'

This commit is contained in:
Tom Alexander 2023-04-21 15:58:20 -04:00
commit bcf5f5a9c8
Signed by: talexander
GPG Key ID: D3A179C9A53C0EDE
17 changed files with 338 additions and 46 deletions

View File

@ -6,9 +6,14 @@ MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
TESTJOBS := 4
OS:=$(shell uname -s)
RELEASEFLAGS :=
ifeq ($(OS),Linux)
TESTJOBS:=$(shell nproc)
RELEASEFLAGS=--target x86_64-unknown-linux-musl
endif
ifeq ($(OS),FreeBSD)
TESTJOBS:=$(shell sysctl -n hw.ncpu)
endif
ifeq ($(origin .RECIPEPREFIX), undefined)
@ -22,7 +27,7 @@ build:
.PHONY: release
release:
> cargo build --release --target x86_64-unknown-linux-musl
> cargo build --release $(RELEASEFLAGS)
.PHONY: clean
clean:

View File

@ -76,8 +76,7 @@ fn is_expect_fail(name: &str) -> Option<&str> {
"element_container_priority_footnote_definition_dynamic_block" => Some("Keyword needs to be implemented."),
"element_container_priority_greater_block_dynamic_block" => Some("Keyword needs to be implemented."),
"element_container_priority_section_dynamic_block" => Some("Keyword needs to be implemented."),
"exit_matcher_investigation_table_list" => Some("Table needs to be implemented."),
"element_container_priority_readme" => Some("Table needs to be implemented."),
"element_container_priority_readme" => Some("A lot needs to be implemented."),
_ => None,
}
}

View File

@ -0,0 +1,3 @@
| foo | bar |
|-----+-------|
| baz | lorem |

View File

@ -21,5 +21,5 @@ function get_test_names {
}
get_test_names "$@" | while read test; do
(cd "$DIR/../" && cargo test --no-fail-fast --test test_loader "$test")
(cd "$DIR/../" && cargo test --no-fail-fast --test test_loader "$test" -- --show-output)
done

View File

@ -14,6 +14,9 @@ use crate::parser::PlainList;
use crate::parser::PlainListItem;
use crate::parser::PropertyDrawer;
use crate::parser::Section;
use crate::parser::Table;
use crate::parser::TableCell;
use crate::parser::TableRow;
use crate::DynamicBlock;
#[derive(Debug)]
@ -198,6 +201,7 @@ fn compare_element<'s>(
Element::Comment(obj) => compare_comment(source, emacs, obj),
Element::Drawer(obj) => compare_drawer(source, emacs, obj),
Element::PropertyDrawer(obj) => compare_property_drawer(source, emacs, obj),
Element::Table(obj) => compare_table(source, emacs, obj),
}
}
@ -459,3 +463,88 @@ fn compare_property_drawer<'s>(
children: child_status,
})
}
fn compare_table<'s>(
source: &'s str,
emacs: &'s Token<'s>,
rust: &'s Table<'s>,
) -> Result<DiffResult, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let mut this_status = DiffStatus::Good;
let emacs_name = "table";
if assert_name(emacs, emacs_name).is_err() {
this_status = DiffStatus::Bad;
}
if assert_bounds(source, emacs, rust).is_err() {
this_status = DiffStatus::Bad;
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_table_row(source, emacs_child, rust_child)?);
}
Ok(DiffResult {
status: this_status,
name: emacs_name.to_owned(),
message: None,
children: child_status,
})
}
fn compare_table_row<'s>(
source: &'s str,
emacs: &'s Token<'s>,
rust: &'s TableRow<'s>,
) -> Result<DiffResult, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let mut this_status = DiffStatus::Good;
let emacs_name = "table-row";
if assert_name(emacs, emacs_name).is_err() {
this_status = DiffStatus::Bad;
}
if assert_bounds(source, emacs, rust).is_err() {
this_status = DiffStatus::Bad;
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {
child_status.push(compare_table_cell(source, emacs_child, rust_child)?);
}
Ok(DiffResult {
status: this_status,
name: emacs_name.to_owned(),
message: None,
children: child_status,
})
}
fn compare_table_cell<'s>(
source: &'s str,
emacs: &'s Token<'s>,
rust: &'s TableCell<'s>,
) -> Result<DiffResult, Box<dyn std::error::Error>> {
let children = emacs.as_list()?;
let mut child_status = Vec::new();
let mut this_status = DiffStatus::Good;
let emacs_name = "table-cell";
if assert_name(emacs, emacs_name).is_err() {
this_status = DiffStatus::Bad;
}
if assert_bounds(source, emacs, rust).is_err() {
this_status = DiffStatus::Bad;
}
for (emacs_child, rust_child) in children.iter().skip(2).zip(rust.children.iter()) {}
Ok(DiffResult {
status: this_status,
name: emacs_name.to_owned(),
message: None,
children: child_status,
})
}

View File

@ -3,7 +3,7 @@ use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
pub fn init_telemetry() -> Result<(), Box<dyn std::error::Error>> {
let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("WARN"));
// let env_filter = EnvFilter::try_from_default_env().unwrap_or(EnvFilter::new("warn"));
// let stdout = tracing_subscriber::fmt::Layer::new()
// .pretty()
@ -20,7 +20,7 @@ pub fn init_telemetry() -> Result<(), Box<dyn std::error::Error>> {
let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer);
tracing_subscriber::registry()
.with(env_filter)
// .with(env_filter)
.with(opentelemetry)
// .with(stdout)
.try_init()?;

View File

@ -1,8 +1,8 @@
#![feature(round_char_boundary)]
#![feature(exit_status_error)]
mod parser;
mod compare;
pub use parser::*;
mod parser;
pub use compare::compare_document;
pub use compare::emacs_parse_org_document;
pub use compare::sexp;
pub use compare::compare_document;
pub use parser::*;

View File

@ -1,9 +1,8 @@
#![feature(round_char_boundary)]
use crate::init_tracing::init_telemetry;
use crate::init_tracing::shutdown_telemetry;
use crate::parser::document;
use ::organic::document;
mod init_tracing;
mod parser;
const TEST_DOC: &'static str = include_str!("../toy_language.txt");

View File

@ -49,8 +49,11 @@ pub fn comment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
fn comment_line<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
start_of_line(context, input)?;
let (remaining, _indent) = space0(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) =
tuple((tag("#"), opt(tuple((space1, is_not("\r\n")))), alt((line_ending, eof))))(remaining)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple((
tag("#"),
opt(tuple((space1, is_not("\r\n")))),
alt((line_ending, eof)),
))(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, source))
}
@ -71,12 +74,13 @@ mod tests {
let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context =
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let comment_matcher =
parser_with_context!(comment)(&document_context);
let (remaining, first_comment) =
comment_matcher(input).expect("Parse first comment");
assert_eq!(remaining, r#"#not a comment
# Comment again"#);
let comment_matcher = parser_with_context!(comment)(&document_context);
let (remaining, first_comment) = comment_matcher(input).expect("Parse first comment");
assert_eq!(
remaining,
r#"#not a comment
# Comment again"#
);
assert_eq!(
first_comment.source,
"# Comment line

View File

@ -9,8 +9,11 @@ use super::greater_element::FootnoteDefinition;
use super::greater_element::GreaterBlock;
use super::greater_element::PlainList;
use super::greater_element::PropertyDrawer;
use super::greater_element::Table;
use super::greater_element::TableRow;
use super::lesser_element::Comment;
use super::lesser_element::Paragraph;
use super::lesser_element::TableCell;
use super::paragraph::paragraph;
use super::plain_list::plain_list;
use super::source::Source;
@ -18,6 +21,8 @@ use super::Context;
use super::Drawer;
use super::PlainListItem;
use crate::parser::parser_with_context::parser_with_context;
use crate::parser::table;
use crate::parser::table::org_mode_table;
use nom::branch::alt;
use nom::combinator::map;
@ -31,6 +36,7 @@ pub enum Element<'s> {
Comment(Comment<'s>),
Drawer(Drawer<'s>),
PropertyDrawer(PropertyDrawer<'s>),
Table(Table<'s>),
}
impl<'s> Source<'s> for Element<'s> {
@ -44,6 +50,7 @@ impl<'s> Source<'s> for Element<'s> {
Element::Comment(obj) => obj.source,
Element::Drawer(obj) => obj.source,
Element::PropertyDrawer(obj) => obj.source,
Element::Table(obj) => obj.source,
}
}
}
@ -102,6 +109,24 @@ impl<'s> Source<'s> for PropertyDrawer<'s> {
}
}
impl<'s> Source<'s> for Table<'s> {
fn get_source(&'s self) -> &'s str {
self.source
}
}
impl<'s> Source<'s> for TableRow<'s> {
fn get_source(&'s self) -> &'s str {
self.source
}
}
impl<'s> Source<'s> for TableCell<'s> {
fn get_source(&'s self) -> &'s str {
self.source
}
}
#[tracing::instrument(ret, level = "debug")]
pub fn element<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Element<'s>> {
let non_paragraph_matcher = parser_with_context!(non_paragraph_element)(context);
@ -123,6 +148,7 @@ pub fn non_paragraph_element<'r, 's>(
let footnote_definition_matcher = parser_with_context!(footnote_definition)(context);
let comment_matcher = parser_with_context!(comment)(context);
let drawer_matcher = parser_with_context!(drawer)(context);
let table_matcher = parser_with_context!(org_mode_table)(context);
alt((
map(plain_list_matcher, Element::PlainList),
map(greater_block_matcher, Element::GreaterBlock),
@ -130,5 +156,6 @@ pub fn non_paragraph_element<'r, 's>(
map(footnote_definition_matcher, Element::FootnoteDefinition),
map(comment_matcher, Element::Comment),
map(drawer_matcher, Element::Drawer),
map(table_matcher, Element::Table),
))(input)
}

View File

@ -1,4 +1,5 @@
use super::element::Element;
use super::lesser_element::TableCell;
#[derive(Debug)]
pub struct PlainList<'s> {
@ -55,3 +56,15 @@ pub struct NodeProperty<'s> {
pub source: &'s str,
pub value: Option<&'s str>,
}
#[derive(Debug)]
pub struct Table<'s> {
pub source: &'s str,
pub children: Vec<TableRow<'s>>,
}
#[derive(Debug)]
pub struct TableRow<'s> {
pub source: &'s str,
pub children: Vec<TableCell<'s>>,
}

View File

@ -1,4 +1,5 @@
use super::object::{Object, TextMarkup};
use super::object::Object;
use super::object::TextMarkup;
#[derive(Debug)]
pub struct Paragraph<'s> {
@ -11,6 +12,12 @@ pub struct Comment<'s> {
pub source: &'s str,
}
#[derive(Debug)]
pub struct TableCell<'s> {
pub source: &'s str,
pub children: Vec<Object<'s>>,
}
impl<'s> Paragraph<'s> {
pub fn of_text(input: &'s str) -> Self {
let mut objects = Vec::with_capacity(1);

View File

@ -18,6 +18,7 @@ mod plain_list;
mod plain_text;
mod property_drawer;
mod source;
mod table;
mod util;
pub use document::document;
pub use document::Document;
@ -32,7 +33,10 @@ pub use greater_element::GreaterBlock;
pub use greater_element::PlainList;
pub use greater_element::PlainListItem;
pub use greater_element::PropertyDrawer;
pub use greater_element::Table;
pub use greater_element::TableRow;
pub use lesser_element::Comment;
pub use lesser_element::Paragraph;
pub use lesser_element::TableCell;
pub use source::Source;
type Context<'r, 's> = &'r parser_context::ContextTree<'r, 's>;

View File

@ -48,6 +48,20 @@ pub fn standard_set_object<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, Object<'s>> {
// TODO: add entities, LaTeX fragments, export snippets, footnote references, citations (NOT citation references), inline babel calls, inline source blocks, line breaks, links, macros, targets and radio targets, statistics cookies, subscript and superscript, timestamps, and text markup.
not(|i| context.check_exit_matcher(i))(input)?;
let plain_text_matcher = parser_with_context!(plain_text)(context);
map(plain_text_matcher, Object::PlainText)(input)
}
#[tracing::instrument(ret, level = "debug")]
pub fn minimal_set_object<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, Object<'s>> {
// TODO: add text markup, entities, LaTeX fragments, superscripts and subscripts
not(|i| context.check_exit_matcher(i))(input)?;
let plain_text_matcher = parser_with_context!(plain_text)(context);

View File

@ -89,7 +89,6 @@ impl<'r, 's> ContextTree<'r, 's> {
return local_result;
}
}
}
_ => {}
};

151
src/parser/table.rs Normal file
View File

@ -0,0 +1,151 @@
use nom::branch::alt;
use nom::bytes::complete::is_not;
use nom::bytes::complete::tag;
use nom::character::complete::line_ending;
use nom::character::complete::space0;
use nom::combinator::eof;
use nom::combinator::not;
use nom::combinator::peek;
use nom::combinator::recognize;
use nom::combinator::verify;
use nom::multi::many1;
use nom::multi::many_till;
use nom::sequence::tuple;
use super::Context;
use crate::parser::error::Res;
use crate::parser::exiting::ExitClass;
use crate::parser::greater_element::TableRow;
use crate::parser::lesser_element::TableCell;
use crate::parser::object::minimal_set_object;
use crate::parser::object::Object;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ExitMatcherNode;
use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed;
use crate::parser::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::parser::util::start_of_line;
use crate::parser::Table;
/// Parse an org-mode-style table
///
/// This is not the table.el style.
#[tracing::instrument(ret, level = "debug")]
pub fn org_mode_table<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Table<'s>> {
start_of_line(context, input)?;
peek(tuple((space0, tag("|"))))(input)?;
let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
.with_additional_node(ContextElement::Context("table"))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Alpha,
exit_matcher: &table_end,
}));
let org_mode_table_row_matcher = parser_with_context!(org_mode_table_row)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, (children, _exit_contents)) =
many_till(org_mode_table_row_matcher, exit_matcher)(input)?;
// TODO: Consume trailing formulas
let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, Table { source, children }))
}
#[tracing::instrument(ret, level = "debug")]
fn table_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> {
start_of_line(context, input)?;
recognize(tuple((space0, not(tag("|")))))(input)
}
#[tracing::instrument(ret, level = "debug")]
pub fn org_mode_table_row<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, TableRow<'s>> {
alt((
parser_with_context!(org_mode_table_row_rule)(context),
parser_with_context!(org_mode_table_row_regular)(context),
))(input)
}
#[tracing::instrument(ret, level = "debug")]
pub fn org_mode_table_row_rule<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, TableRow<'s>> {
start_of_line(context, input)?;
let (remaining, _) = tuple((space0, tag("|-"), is_not("\r\n"), line_ending))(input)?;
let source = get_consumed(input, remaining);
Ok((
remaining,
TableRow {
source,
children: Vec::new(),
},
))
}
#[tracing::instrument(ret, level = "debug")]
pub fn org_mode_table_row_regular<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, TableRow<'s>> {
start_of_line(context, input)?;
let (remaining, _) = tuple((space0, tag("|")))(input)?;
let (remaining, children) =
many1(parser_with_context!(org_mode_table_cell)(context))(remaining)?;
let (remaining, _tail) = recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, TableRow { source, children }))
}
#[tracing::instrument(ret, level = "debug")]
pub fn org_mode_table_cell<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, TableCell<'s>> {
let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta,
exit_matcher: &org_mode_table_cell_end,
}));
let table_cell_set_object_matcher =
parser_with_context!(table_cell_set_object)(&parser_context);
let exit_matcher = parser_with_context!(exit_matcher_parser)(&parser_context);
let (remaining, (children, _exit_contents)) = verify(
many_till(table_cell_set_object_matcher, exit_matcher),
|(children, exit_contents)| !children.is_empty() || exit_contents.ends_with("|"),
)(input)?;
let (remaining, _tail) = org_mode_table_cell_end(&parser_context, remaining)?;
let source = get_consumed(input, remaining);
Ok((remaining, TableCell { source, children }))
}
#[tracing::instrument(ret, level = "debug")]
fn org_mode_table_cell_end<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, &'s str> {
recognize(tuple((space0, alt((tag("|"), peek(line_ending))))))(input)
}
#[tracing::instrument(ret, level = "debug")]
pub fn table_cell_set_object<'r, 's>(
context: Context<'r, 's>,
input: &'s str,
) -> Res<&'s str, Object<'s>> {
not(|i| context.check_exit_matcher(i))(input)?;
parser_with_context!(minimal_set_object)(context)(input)
// TODO: add citations, export snippets, footnote references, links, macros, radio targets, targets, and timestamps.
}

View File

@ -1,25 +1,3 @@
# Blank lines and comments can come before property drawers in the zeroth section
:PROPERTIES:
:FOO: bar
:END:
* Spaces turn property drawers into regular drawers
:PROPERTIES:
:FOO: bar
:END:
* Comments turn property drawers into regular drawers
# Comment
:PROPERTIES:
:FOO: bar
:END:
* Baseline
:PROPERTIES:
:FOO: bar
:END:
| foo | bar |
|-----+-------|
| baz | lorem |