43 Commits

Author SHA1 Message Date
Tom Alexander
503db94b2c Publish version 0.1.12.
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-18 19:01:43 -04:00
Tom Alexander
a4381e5e39 Merge branch 'keyword_constants' 2023-10-18 18:48:52 -04:00
Tom Alexander
e11de60def Clippy fixes.
All checks were successful
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-18 18:39:04 -04:00
Tom Alexander
b2479e9de8 Remove Debug from the context variables.
Now that entities are stored in the settings struct, these variables are massive which makes them balloon trace sizes while being mostly unreadable. This removes Debug from them to serve as a static-analysis check that context is ALWAYS ignored in tracing calls.
2023-10-18 18:36:25 -04:00
Tom Alexander
49d1cef7ae Remove context from functions that no longer need it. 2023-10-18 18:28:24 -04:00
Tom Alexander
ba72cc1b29 The variables for keywords are actually constants.
These settings do not need to exist in GlobalSettings because they are actually constants in upstream Org-Mode.
2023-10-18 18:22:01 -04:00
Tom Alexander
c58b0e7c35 Add a script to dump an AST using docker.
All checks were successful
rust-test Build rust-test has succeeded
clippy Build clippy has succeeded
rustfmt Build rustfmt has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
2023-10-18 15:39:52 -04:00
Tom Alexander
f19d262825 Merge branch 'bullshitium'
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
2023-10-18 13:04:16 -04:00
Tom Alexander
68f3f2e159 Clippy fixes.
All checks were successful
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-18 12:42:09 -04:00
Tom Alexander
269e23c1b1 No more expect-fail tests! 2023-10-18 12:41:12 -04:00
Tom Alexander
e111b8b9b8 Performance optimization. 2023-10-18 12:39:08 -04:00
Tom Alexander
353ff07420 Handle bullshitium for broken dynamic blocks. 2023-10-18 12:32:48 -04:00
Tom Alexander
94dec31130 Consuming trailing whitespace for 🔚 bullshitium. 2023-10-18 12:17:57 -04:00
Tom Alexander
cf5d3ed745 Add tests for the 🔚 bullshitium. 2023-10-18 11:59:55 -04:00
Tom Alexander
b0b287cd47 Handle bullshitium for 🔚. 2023-10-18 11:57:39 -04:00
Tom Alexander
bcdf1f5e9d Merge branch 'entity_special_case'
All checks were successful
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has succeeded
rust-test Build rust-test has succeeded
2023-10-18 08:53:57 -04:00
Tom Alexander
17d8e76e05 Do not match POST for entities that end with a space.
This is a special case for en-spaces.
2023-10-18 08:52:18 -04:00
Tom Alexander
8db9038c53 Merge branch 'list_perf_improvement'
Some checks failed
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has failed
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
2023-10-18 08:40:19 -04:00
Tom Alexander
a276ba70e0 Fix empty content items with final item whitespace cut-off before headlines.
Some checks failed
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-17 15:56:02 -04:00
Tom Alexander
b7442c1e92 Do not match headlines as plain list items. 2023-10-17 15:35:43 -04:00
Tom Alexander
364ba79517 It actually worked on trailing whitespace ownership test case 2. 2023-10-17 15:22:31 -04:00
Tom Alexander
47408763e5 A first stab at a final item whitespace cut-off exit matcher. 2023-10-17 15:08:36 -04:00
Tom Alexander
bd187ebfe7 Remove re-parsing of the final list child. 2023-10-17 14:17:47 -04:00
Tom Alexander
59cb3c2bbf Remove unnecessary closures in plain lists. 2023-10-17 13:59:33 -04:00
Tom Alexander
44f7412a5c Merge branch 'perf_improvement'
Some checks failed
rustfmt Build rustfmt has succeeded
clippy Build clippy has succeeded
rust-build Build rust-build has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-test Build rust-test has succeeded
2023-10-17 13:54:44 -04:00
Tom Alexander
01464057ad Remove unused event types. 2023-10-17 13:43:33 -04:00
Tom Alexander
0208020e3e Also print byte offset.
Some checks failed
clippy Build clippy has failed
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-10-17 13:35:40 -04:00
Tom Alexander
a2f53361eb Record element start events and report them when the event_count feature is enabled. 2023-10-17 13:32:01 -04:00
Tom Alexander
17db05c2c7 Unify more error handling. 2023-10-17 12:42:34 -04:00
Tom Alexander
6139ea328d Unify some more error handling. 2023-10-17 12:22:52 -04:00
Tom Alexander
d20b4a410b Remove pointless map_err calls. 2023-10-17 11:56:36 -04:00
Tom Alexander
05c64f53b1 Remove boxed error from CustomError. 2023-10-17 11:40:11 -04:00
Tom Alexander
f65d0bb82d Remove redundant call to space0. 2023-10-17 11:33:26 -04:00
Tom Alexander
50d2831081 Cleanup. 2023-10-17 11:30:23 -04:00
Tom Alexander
bc9bd4f97b Eliminate some closures. 2023-10-17 11:10:18 -04:00
Tom Alexander
369d3e8c50 Add a full-document parse benchmark. 2023-10-17 10:57:04 -04:00
Tom Alexander
7d73eb6bd4 Merge branch 'error_rework'
Some checks failed
clippy Build clippy has succeeded
rust-foreign-document-test Build rust-foreign-document-test has failed
rust-build Build rust-build has failed
rustfmt Build rustfmt has failed
rust-test Build rust-test has succeeded
2023-10-17 10:40:46 -04:00
Tom Alexander
f59f153ee7 Clean up. 2023-10-17 10:39:21 -04:00
Tom Alexander
20c4a0f8f7 Continue removing MyError. 2023-10-17 10:35:33 -04:00
Tom Alexander
e776a051ad Continue removing MyError. 2023-10-17 10:13:00 -04:00
Tom Alexander
77e6c22ad8 Continue removing MyError. 2023-10-17 10:09:37 -04:00
Tom Alexander
c9d7251e3b Begin removing the MyError type. 2023-10-17 09:45:18 -04:00
Tom Alexander
8417b5fc9d Add an owned string entry for CustomError. 2023-10-17 09:27:15 -04:00
56 changed files with 742 additions and 468 deletions

View File

@@ -2,7 +2,7 @@
[package] [package]
name = "organic" name = "organic"
version = "0.1.11" version = "0.1.12"
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"
@@ -59,6 +59,7 @@ default = []
compare = ["tokio/process", "tokio/macros"] compare = ["tokio/process", "tokio/macros"]
foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"] foreign_document_test = ["compare", "dep:futures", "tokio/sync", "dep:walkdir", "tokio/process"]
tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"] tracing = ["dep:opentelemetry", "dep:opentelemetry-otlp", "dep:opentelemetry-semantic-conventions", "dep:tokio", "dep:tracing", "dep:tracing-opentelemetry", "dep:tracing-subscriber"]
event_count = []
# Optimized build for any sort of release. # Optimized build for any sort of release.
[profile.release-lto] [profile.release-lto]

View File

@@ -66,10 +66,6 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
} }
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
fn is_expect_fail(name: &str) -> Option<&str> { fn is_expect_fail(_name: &str) -> Option<&str> {
match name { None
"greater_element_drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."),
"element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."),
_ => None,
}
} }

View File

@@ -0,0 +1,3 @@
foo
:end:
bar

View File

@@ -0,0 +1,2 @@
foo
:end:

58
scripts/dump_ast.bash Executable file
View File

@@ -0,0 +1,58 @@
#!/usr/bin/env bash
#
# Dump the AST of an org-mode document from emacs
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
REALPATH=$(command -v uu-realpath || command -v realpath)
MAKE=$(command -v gmake || command -v make)
############## Setup #########################
function die {
local status_code="$1"
shift
(>&2 echo "${@}")
exit "$status_code"
}
function log {
(>&2 echo "${@}")
}
############## Program #########################
function main {
if [ $# -eq 0 ]; then
dump_ast_stdin "${@}"
else
dump_ast_file "${@}"
fi
}
function dump_ast_stdin {
# Until we can find a good way to encode stdin as an elisp string in bash, I cannot operate on stdin.
die 1 "This script only works on files."
}
function dump_ast_file {
local target_file mounted_file elisp_script
target_file=$($REALPATH "$1")
mounted_file="/input${target_file}"
elisp_script=$(cat <<EOF
(progn
(erase-buffer)
(require 'org)
(defun org-table-align () t)
(find-file-read-only "${mounted_file}")
(org-mode)
(message "%s" (pp-to-string (org-element-parse-buffer)))
)
EOF
)
exec docker run --init --rm -i --mount type=tmpfs,destination=/tmp -v "/:/input:ro" --entrypoint "" organic-test emacs -q --no-site-file --no-splash --batch --eval "$elisp_script"
}
main "${@}"

View File

@@ -14,7 +14,7 @@ function main {
additional_flags+=(--profile "$PROFILE") additional_flags+=(--profile "$PROFILE")
fi fi
(cd "$DIR/../" && cargo build --no-default-features "${additional_flags[@]}") (cd "$DIR/../" && cargo build --no-default-features "${additional_flags[@]}")
perf record --freq=2000 --call-graph dwarf --output="$DIR/../perf.data" "$DIR/../target/${PROFILE}/parse" "${@}" perf record --freq=70000 --call-graph dwarf --output="$DIR/../perf.data" "$DIR/../target/${PROFILE}/parse" "${@}"
# Convert to a format firefox will read # Convert to a format firefox will read
# flags to consider --show-info # flags to consider --show-info

View File

@@ -1,15 +1,27 @@
use super::global_settings::EntityDefinition; use super::global_settings::EntityDefinition;
pub(crate) const DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS: [&str; 1] = ["CAPTION"]; /// Keywords that contain the standard set of objects (excluding footnote references).
///
/// Corresponds to org-element-parsed-keywords elisp variable.
pub(crate) const ORG_ELEMENT_PARSED_KEYWORDS: [&str; 1] = ["CAPTION"];
pub(crate) const DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"]; /// Keywords that can have a secondary value in square brackets.
///
/// Corresponds to org-element-dual-keywords elisp variable.
pub(crate) const ORG_ELEMENT_DUAL_KEYWORDS: [&str; 2] = ["CAPTION", "RESULTS"];
pub(crate) const DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [ /// Keywords that can be affiliated with an element.
///
/// Corresponds to org-element-affiliated-keywords elisp variable.
pub(crate) const ORG_ELEMENT_AFFILIATED_KEYWORDS: [&str; 13] = [
"CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT", "CAPTION", "DATA", "HEADER", "HEADERS", "LABEL", "NAME", "PLOT", "RESNAME", "RESULT",
"RESULTS", "SOURCE", "SRCNAME", "TBLNAME", "RESULTS", "SOURCE", "SRCNAME", "TBLNAME",
]; ];
pub(crate) const DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &str); 8] = [ /// Mapping of keyword names.
///
/// Corresponds to org-element-keyword-translation-alist elisp variable.
pub(crate) const ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST: [(&str, &str); 8] = [
("DATA", "NAME"), ("DATA", "NAME"),
("LABEL", "NAME"), ("LABEL", "NAME"),
("RESNAME", "NAME"), ("RESNAME", "NAME"),

View File

@@ -9,11 +9,9 @@ use super::list::List;
use super::DynContextMatcher; use super::DynContextMatcher;
use super::RefContext; use super::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::OrgSource; use crate::parser::OrgSource;
#[derive(Debug)]
pub(crate) enum ContextElement<'r, 's> { pub(crate) enum ContextElement<'r, 's> {
/// Stores a parser that indicates that children should exit upon matching an exit matcher. /// Stores a parser that indicates that children should exit upon matching an exit matcher.
ExitMatcherNode(ExitMatcherNode<'r>), ExitMatcherNode(ExitMatcherNode<'r>),
@@ -35,15 +33,6 @@ pub(crate) struct ExitMatcherNode<'r> {
pub(crate) class: ExitClass, pub(crate) class: ExitClass,
} }
impl<'r> std::fmt::Debug for ExitMatcherNode<'r> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut formatter = f.debug_struct("ExitMatcherNode");
formatter.field("class", &self.class.to_string());
formatter.finish()
}
}
#[derive(Debug)]
pub(crate) struct Context<'g, 'r, 's> { pub(crate) struct Context<'g, 'r, 's> {
global_settings: &'g GlobalSettings<'g, 's>, global_settings: &'g GlobalSettings<'g, 's>,
tree: List<'r, &'r ContextElement<'r, 's>>, tree: List<'r, &'r ContextElement<'r, 's>>,
@@ -108,7 +97,7 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
pub(crate) fn check_exit_matcher( pub(crate) fn check_exit_matcher(
&'r self, &'r self,
i: OrgSource<'s>, i: OrgSource<'s>,
) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError<OrgSource<'s>>> { ) -> IResult<OrgSource<'s>, OrgSource<'s>, CustomError> {
let mut current_class_filter = ExitClass::Gamma; let mut current_class_filter = ExitClass::Gamma;
for current_node in self.iter_context() { for current_node in self.iter_context() {
let context_element = current_node.get_data(); let context_element = current_node.get_data();
@@ -123,7 +112,7 @@ impl<'g, 'r, 's> Context<'g, 'r, 's> {
} }
} }
// TODO: Make this a specific error instead of just a generic MyError // TODO: Make this a specific error instead of just a generic MyError
return Err(nom::Err::Error(CustomError::MyError(MyError("NoExit")))); return Err(nom::Err::Error(CustomError::Static("NoExit")));
} }
/// Indicates if elements should consume the whitespace after them. /// Indicates if elements should consume the whitespace after them.

View File

@@ -1,13 +1,7 @@
#[derive(Debug, Copy, Clone)] #[derive(Copy, Clone)]
pub(crate) enum ExitClass { pub(crate) enum ExitClass {
Document = 1, Document = 1,
Alpha = 2, Alpha = 2,
Beta = 3, Beta = 3,
Gamma = 4, Gamma = 4,
} }
impl std::fmt::Display for ExitClass {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}

View File

@@ -1,17 +1,16 @@
use std::fmt::Debug;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(any(feature = "compare", feature = "foreign_document_test"))] #[cfg(any(feature = "compare", feature = "foreign_document_test"))]
pub trait FileAccessInterface: Sync + Debug { pub trait FileAccessInterface: Sync {
fn read_file(&self, path: &str) -> Result<String, std::io::Error>; fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
} }
#[cfg(not(any(feature = "compare", feature = "foreign_document_test")))] #[cfg(not(any(feature = "compare", feature = "foreign_document_test")))]
pub trait FileAccessInterface: Debug { pub trait FileAccessInterface {
fn read_file(&self, path: &str) -> Result<String, std::io::Error>; fn read_file(&self, path: &str) -> Result<String, std::io::Error>;
} }
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct LocalFileAccessInterface { pub struct LocalFileAccessInterface {
pub working_directory: Option<PathBuf>, pub working_directory: Option<PathBuf>,
} }

View File

@@ -5,16 +5,12 @@ use super::constants::DEFAULT_ORG_ENTITIES;
use super::constants::DEFAULT_ORG_LINK_PARAMETERS; use super::constants::DEFAULT_ORG_LINK_PARAMETERS;
use super::FileAccessInterface; use super::FileAccessInterface;
use super::LocalFileAccessInterface; use super::LocalFileAccessInterface;
use crate::context::constants::DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS;
use crate::context::constants::DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS;
use crate::context::constants::DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST;
use crate::context::constants::DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS;
use crate::types::IndentationLevel; use crate::types::IndentationLevel;
use crate::types::Object; use crate::types::Object;
// TODO: Ultimately, I think we'll need most of this: https://orgmode.org/manual/In_002dbuffer-Settings.html // TODO: Ultimately, I think we'll need most of this: https://orgmode.org/manual/In_002dbuffer-Settings.html
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct GlobalSettings<'g, 's> { pub struct GlobalSettings<'g, 's> {
pub radio_targets: Vec<&'g Vec<Object<'s>>>, pub radio_targets: Vec<&'g Vec<Object<'s>>>,
pub file_access: &'g dyn FileAccessInterface, pub file_access: &'g dyn FileAccessInterface,
@@ -58,26 +54,6 @@ pub struct GlobalSettings<'g, 's> {
/// ///
/// Corresponds to org-entities elisp variable. /// Corresponds to org-entities elisp variable.
pub entities: &'g [EntityDefinition<'s>], pub entities: &'g [EntityDefinition<'s>],
/// Keywords that contain the standard set of objects (excluding footnote references).
///
/// Corresponds to org-element-parsed-keywords elisp variable.
pub element_parsed_keywords: &'g [&'s str],
/// Keywords that can have a secondary value in square brackets.
///
/// Corresponds to org-element-dual-keywords elisp variable.
pub element_dual_keywords: &'g [&'s str],
/// Keywords that can be affiliated with an element.
///
/// Corresponds to org-element-affiliated-keywords elisp variable.
pub element_affiliated_keywords: &'g [&'s str],
/// Mapping of keyword names.
///
/// Corresponds to org-element-keyword-translation-alist elisp variable.
pub element_keyword_translation_alist: &'g [(&'s str, &'s str)],
} }
pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8; pub const DEFAULT_TAB_WIDTH: IndentationLevel = 8;
@@ -112,10 +88,6 @@ impl<'g, 's> GlobalSettings<'g, 's> {
link_parameters: &DEFAULT_ORG_LINK_PARAMETERS, link_parameters: &DEFAULT_ORG_LINK_PARAMETERS,
link_templates: BTreeMap::new(), link_templates: BTreeMap::new(),
entities: &DEFAULT_ORG_ENTITIES, entities: &DEFAULT_ORG_ENTITIES,
element_parsed_keywords: &DEFAULT_ORG_ELEMENT_PARSED_KEYWORDS,
element_dual_keywords: &DEFAULT_ORG_ELEMENT_DUAL_KEYWORDS,
element_affiliated_keywords: &DEFAULT_ORG_ELEMENT_AFFILIATED_KEYWORDS,
element_keyword_translation_alist: &DEFAULT_ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST,
} }
} }
} }
@@ -126,7 +98,7 @@ impl<'g, 's> Default for GlobalSettings<'g, 's> {
} }
} }
#[derive(Debug, Clone, PartialEq, Default)] #[derive(Clone, PartialEq, Default)]
pub enum HeadlineLevelFilter { pub enum HeadlineLevelFilter {
Odd, Odd,

View File

@@ -1,7 +1,7 @@
use crate::error::Res; use crate::error::Res;
use crate::parser::OrgSource; use crate::parser::OrgSource;
mod constants; pub(crate) mod constants;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod context; mod context;
mod exiting; mod exiting;

View File

@@ -2,22 +2,20 @@ use nom::error::ErrorKind;
use nom::error::ParseError; use nom::error::ParseError;
use nom::IResult; use nom::IResult;
pub(crate) type Res<T, U> = IResult<T, U, CustomError<T>>; pub(crate) type Res<T, U> = IResult<T, U, CustomError>;
#[derive(Debug)] #[derive(Debug)]
pub enum CustomError<I> { pub enum CustomError {
MyError(MyError<&'static str>), #[allow(dead_code)]
Nom(I, ErrorKind), Text(String),
Static(&'static str),
IO(std::io::Error), IO(std::io::Error),
BoxedError(Box<dyn std::error::Error>), Parser(ErrorKind),
} }
#[derive(Debug)] impl<I: std::fmt::Debug> ParseError<I> for CustomError {
pub struct MyError<I>(pub(crate) I); fn from_error_kind(_input: I, kind: ErrorKind) -> Self {
CustomError::Parser(kind)
impl<I> ParseError<I> for CustomError<I> {
fn from_error_kind(input: I, kind: ErrorKind) -> Self {
CustomError::Nom(input, kind)
} }
fn append(_input: I, _kind: ErrorKind, /*mut*/ other: Self) -> Self { fn append(_input: I, _kind: ErrorKind, /*mut*/ other: Self) -> Self {
@@ -26,20 +24,20 @@ impl<I> ParseError<I> for CustomError<I> {
} }
} }
impl<I> From<std::io::Error> for CustomError<I> { impl From<std::io::Error> for CustomError {
fn from(value: std::io::Error) -> Self { fn from(value: std::io::Error) -> Self {
CustomError::IO(value) CustomError::IO(value)
} }
} }
impl<I> From<&'static str> for CustomError<I> { impl From<&'static str> for CustomError {
fn from(value: &'static str) -> Self { fn from(value: &'static str) -> Self {
CustomError::MyError(MyError(value)) CustomError::Static(value)
} }
} }
impl<I> From<Box<dyn std::error::Error>> for CustomError<I> { impl From<String> for CustomError {
fn from(value: Box<dyn std::error::Error>) -> Self { fn from(value: String) -> Self {
CustomError::BoxedError(value) CustomError::Text(value)
} }
} }

View File

@@ -1,5 +1,4 @@
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod error; mod error;
pub(crate) use error::CustomError; pub(crate) use error::CustomError;
pub(crate) use error::MyError;
pub(crate) use error::Res; pub(crate) use error::Res;

View File

@@ -0,0 +1,43 @@
use std::collections::HashMap;
use std::sync::Mutex;
use super::EventType;
use crate::parser::OrgSource;
#[derive(Debug, Eq, Hash, PartialEq)]
struct EventKey {
event_type: EventType,
byte_offset: usize,
}
pub(crate) type EventCount = usize;
static GLOBAL_DATA: Mutex<Option<HashMap<EventKey, EventCount>>> = Mutex::new(None);
pub(crate) fn record_event(event_type: EventType, input: OrgSource<'_>) {
let mut db = GLOBAL_DATA.lock().unwrap();
let db = db.get_or_insert_with(HashMap::new);
let key = EventKey {
event_type,
byte_offset: input.get_byte_offset(),
};
*db.entry(key).or_insert(0) += 1;
}
pub fn report(original_document: &str) {
let mut db = GLOBAL_DATA.lock().unwrap();
let db = db.get_or_insert_with(HashMap::new);
let mut results: Vec<_> = db.iter().map(|(k, v)| (k, v)).collect();
results.sort_by_key(|(_k, v)| *v);
// This would put the most common at the top, but that is a pain when there is already a lot of output from the parser.
// results.sort_by(|(_ak, av), (_bk, bv)| bv.cmp(av));
for (key, count) in results {
println!(
"{:?} {} character offset: {} byte offset: {}",
key.event_type,
count,
original_document[..key.byte_offset].chars().count() + 1,
key.byte_offset
)
}
}

View File

@@ -0,0 +1,4 @@
#[derive(Debug, Eq, Hash, PartialEq)]
pub(crate) enum EventType {
ElementStart,
}

6
src/event_count/mod.rs Normal file
View File

@@ -0,0 +1,6 @@
mod database;
mod event_type;
pub(crate) use database::record_event;
pub use database::report;
pub(crate) use event_type::EventType;

View File

@@ -13,6 +13,8 @@ pub mod compare;
mod context; mod context;
mod error; mod error;
#[cfg(feature = "event_count")]
pub mod event_count;
mod iter; mod iter;
pub mod parser; pub mod parser;
pub mod types; pub mod types;

View File

@@ -54,8 +54,11 @@ fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
} }
fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> { fn run_anonymous_parse<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
let rust_parsed = parse(org_contents.as_ref())?; let org_contents = org_contents.as_ref();
let rust_parsed = parse(org_contents)?;
println!("{:#?}", rust_parsed); println!("{:#?}", rust_parsed);
#[cfg(feature = "event_count")]
organic::event_count::report(org_contents);
Ok(()) Ok(())
} }
@@ -75,5 +78,7 @@ fn run_parse_on_file<P: AsRef<Path>>(org_path: P) -> Result<(), Box<dyn std::err
}; };
let rust_parsed = parse_with_settings(org_contents, &global_settings)?; let rust_parsed = parse_with_settings(org_contents, &global_settings)?;
println!("{:#?}", rust_parsed); println!("{:#?}", rust_parsed);
#[cfg(feature = "event_count")]
organic::event_count::report(org_contents);
Ok(()) Ok(())
} }

View File

@@ -14,17 +14,46 @@ use nom::multi::many0;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::keyword::affiliated_keyword;
use super::object_parser::standard_set_object; use super::object_parser::standard_set_object;
use super::util::confine_context; use super::util::confine_context;
use super::OrgSource;
use crate::context::bind_context; use crate::context::bind_context;
use crate::context::constants::ORG_ELEMENT_DUAL_KEYWORDS;
use crate::context::constants::ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST;
use crate::context::constants::ORG_ELEMENT_PARSED_KEYWORDS;
use crate::context::Context; use crate::context::Context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::GlobalSettings; use crate::context::GlobalSettings;
use crate::context::List; use crate::context::List;
use crate::error::Res;
use crate::types::AffiliatedKeywordValue; use crate::types::AffiliatedKeywordValue;
use crate::types::AffiliatedKeywords; use crate::types::AffiliatedKeywords;
use crate::types::Keyword; use crate::types::Keyword;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn affiliated_keywords<'s>(
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Keyword<'s>>> {
let mut ret = Vec::new();
let mut remaining = input;
loop {
let result = affiliated_keyword(remaining);
match result {
Ok((remain, kw)) => {
remaining = remain;
ret.push(kw);
}
Err(_) => {
break;
}
}
}
Ok((remaining, ret))
}
pub(crate) fn parse_affiliated_keywords<'g, 's, AK>( pub(crate) fn parse_affiliated_keywords<'g, 's, AK>(
global_settings: &'g GlobalSettings<'g, 's>, global_settings: &'g GlobalSettings<'g, 's>,
input: AK, input: AK,
@@ -34,8 +63,8 @@ where
{ {
let mut ret = BTreeMap::new(); let mut ret = BTreeMap::new();
for kw in input { for kw in input {
let translated_name = translate_name(global_settings, kw.key); let translated_name = translate_name(kw.key);
let keyword_type = identify_keyword_type(global_settings, translated_name.as_str()); let keyword_type = identify_keyword_type(translated_name.as_str());
match keyword_type { match keyword_type {
AffiliatedKeywordType::SingleString => { AffiliatedKeywordType::SingleString => {
ret.insert( ret.insert(
@@ -120,12 +149,12 @@ where
AffiliatedKeywords { keywords: ret } AffiliatedKeywords { keywords: ret }
} }
fn translate_name<'g, 's>(global_settings: &'g GlobalSettings<'g, 's>, name: &'s str) -> String { fn translate_name(name: &str) -> String {
let name_until_optval = name let name_until_optval = name
.split_once('[') .split_once('[')
.map(|(before, _after)| before) .map(|(before, _after)| before)
.unwrap_or(name); .unwrap_or(name);
for (src, dst) in global_settings.element_keyword_translation_alist { for (src, dst) in ORG_ELEMENT_KEYWORD_TRANSLATION_ALIST {
if name_until_optval.eq_ignore_ascii_case(src) { if name_until_optval.eq_ignore_ascii_case(src) {
return dst.to_lowercase(); return dst.to_lowercase();
} }
@@ -140,20 +169,15 @@ enum AffiliatedKeywordType {
ObjectTree, ObjectTree,
} }
fn identify_keyword_type<'g, 's>( fn identify_keyword_type(name: &str) -> AffiliatedKeywordType {
global_settings: &'g GlobalSettings<'g, 's>,
name: &'s str,
) -> AffiliatedKeywordType {
let is_multiple = ["CAPTION", "HEADER"] let is_multiple = ["CAPTION", "HEADER"]
.into_iter() .into_iter()
.any(|candidate| name.eq_ignore_ascii_case(candidate)) .any(|candidate| name.eq_ignore_ascii_case(candidate))
|| name.to_lowercase().starts_with("attr_"); || name.to_lowercase().starts_with("attr_");
let is_parsed = global_settings let is_parsed = ORG_ELEMENT_PARSED_KEYWORDS
.element_parsed_keywords
.iter() .iter()
.any(|candidate| name.eq_ignore_ascii_case(candidate)); .any(|candidate| name.eq_ignore_ascii_case(candidate));
let can_have_optval = global_settings let can_have_optval = ORG_ELEMENT_DUAL_KEYWORDS
.element_dual_keywords
.iter() .iter()
.any(|candidate| name.eq_ignore_ascii_case(candidate)); .any(|candidate| name.eq_ignore_ascii_case(candidate));
match (is_multiple, is_parsed, can_have_optval) { match (is_multiple, is_parsed, can_have_optval) {

View File

@@ -21,7 +21,6 @@ use super::OrgSource;
use crate::context::Matcher; use crate::context::Matcher;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::org_line_ending; use crate::parser::util::org_line_ending;
@@ -217,9 +216,7 @@ fn impl_balanced_bracket<
} }
if fail_parser(remaining).is_ok() { if fail_parser(remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("Fail parser matched.")));
"Fail parser matched.",
))));
} }
let (remain, _) = anychar(remaining)?; let (remain, _) = anychar(remaining)?;

165
src/parser/bullshitium.rs Normal file
View File

@@ -0,0 +1,165 @@
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::character::complete::anychar;
use nom::character::complete::space0;
use nom::multi::many_till;
use nom::sequence::tuple;
use super::paragraph::paragraph;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use super::util::org_line_ending;
use super::util::start_of_line;
use super::OrgSource;
use crate::context::bind_context;
use crate::context::RefContext;
use crate::error::CustomError;
use crate::error::Res;
use crate::parser::macros::element;
use crate::types::AffiliatedKeywords;
use crate::types::Object;
use crate::types::Paragraph;
use crate::types::PlainText;
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn bullshitium<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Paragraph<'s>> {
alt((
bind_context!(broken_end, context),
bind_context!(broken_dynamic_block, context),
))(input)
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn detect_bullshitium<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, ()> {
element!(detect_broken_end, context, input);
element!(detect_broken_dynamic_block, context, input);
Err(nom::Err::Error(CustomError::Static("No bullshitium.")))
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn broken_end<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Paragraph<'s>> {
start_of_line(input)?;
let (remaining, _) = space0(input)?;
let (remaining, _) = tag_no_case(":end:")(remaining)?;
let (lead_in_remaining, _) = tuple((space0, org_line_ending))(remaining)?;
if let Ok((remaining, mut paragraph)) =
paragraph(std::iter::empty(), lead_in_remaining, context, input)
{
match paragraph.children.first_mut() {
Some(Object::PlainText(plain_text)) => {
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
}
Some(obj) => {
panic!("Unhandled first object type inside bullshitium {:?}", obj);
}
None => {
unreachable!("Paragraph must have children.");
}
};
Ok((remaining, paragraph))
} else {
let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
Ok((
remaining,
Paragraph {
source: input.get_until(remaining).into(),
affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText {
source: input.get_until(lead_in_remaining).into(),
})],
},
))
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
pub(crate) fn detect_broken_end<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, ()> {
start_of_line(input)?;
let (remaining, _) = space0(input)?;
let (remaining, _) = tag_no_case(":end:")(remaining)?;
let (_remaining, _) = tuple((space0, org_line_ending))(remaining)?;
Ok((input, ()))
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn broken_dynamic_block<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Paragraph<'s>> {
start_of_line(input)?;
let (remaining, _) = space0(input)?;
let (remaining, _) = tag_no_case("#+BEGIN:")(remaining)?;
let (lead_in_remaining, _) = many_till(anychar, org_line_ending)(remaining)?;
if let Ok((remaining, mut paragraph)) =
paragraph(std::iter::empty(), lead_in_remaining, context, input)
{
match paragraph.children.first_mut() {
Some(Object::PlainText(plain_text)) => {
plain_text.source = input.get_until_end_of_str(plain_text.source).into();
}
Some(obj) => {
panic!("Unhandled first object type inside bullshitium {:?}", obj);
}
None => {
unreachable!("Paragraph must have children.");
}
};
Ok((remaining, paragraph))
} else {
let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, lead_in_remaining)?;
Ok((
remaining,
Paragraph {
source: input.get_until(remaining).into(),
affiliated_keywords: AffiliatedKeywords::default(),
children: vec![Object::PlainText(PlainText {
source: input.get_until(lead_in_remaining).into(),
})],
},
))
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context))
)]
pub(crate) fn detect_broken_dynamic_block<'b, 'g, 'r, 's>(
_context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, ()> {
start_of_line(input)?;
let (remaining, _) = space0(input)?;
let (_remaining, _) = tag_no_case("#+BEGIN:")(remaining)?;
Ok((input, ()))
}

View File

@@ -137,7 +137,7 @@ fn _global_prefix_end<'b, 'g, 'r, 's>(
unreachable!("Exceeded citation global prefix bracket depth.") unreachable!("Exceeded citation global prefix bracket depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); let close_bracket = tag::<_, _, CustomError>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -191,7 +191,7 @@ fn _global_suffix_end<'b, 'g, 'r, 's>(
unreachable!("Exceeded citation global suffix bracket depth.") unreachable!("Exceeded citation global suffix bracket depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); let close_bracket = tag::<_, _, CustomError>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }

View File

@@ -20,7 +20,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::object_parser::minimal_set_object; use crate::parser::object_parser::minimal_set_object;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
@@ -151,7 +150,7 @@ fn _key_prefix_end<'b, 'g, 'r, 's>(
unreachable!("Exceeded citation key prefix bracket depth.") unreachable!("Exceeded citation key prefix bracket depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); let close_bracket = tag::<_, _, CustomError>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -181,7 +180,7 @@ fn _key_suffix_end<'b, 'g, 'r, 's>(
unreachable!("Exceeded citation key suffix bracket depth.") unreachable!("Exceeded citation key suffix bracket depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input); let close_bracket = tag::<_, _, CustomError>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -199,9 +198,7 @@ where
let pre_bracket_depth = input.get_bracket_depth(); let pre_bracket_depth = input.get_bracket_depth();
let (remaining, output) = inner(input)?; let (remaining, output) = inner(input)?;
if remaining.get_bracket_depth() - pre_bracket_depth != 0 { if remaining.get_bracket_depth() - pre_bracket_depth != 0 {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("UnbalancedBrackets")));
"UnbalancedBrackets",
))));
} }
Ok((remaining, output)) Ok((remaining, output))
} }

View File

@@ -19,7 +19,6 @@ use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::immediate_in_section; use crate::parser::util::immediate_in_section;
@@ -35,9 +34,9 @@ pub(crate) fn comment<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Comment<'s>> { ) -> Res<OrgSource<'s>, Comment<'s>> {
if immediate_in_section(context, "comment") { if immediate_in_section(context, "comment") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
)))); )));
} }
let parser_context = ContextElement::Context("comment"); let parser_context = ContextElement::Context("comment");
let parser_context = context.with_additional_node(&parser_context); let parser_context = context.with_additional_node(&parser_context);

View File

@@ -19,9 +19,7 @@ use crate::context::GlobalSettings;
use crate::context::List; use crate::context::List;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::org_source::convert_error;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
use crate::types::AstNode; use crate::types::AstNode;
use crate::types::Document; use crate::types::Document;
@@ -103,7 +101,7 @@ pub fn parse_file_with_settings<'g, 's, P: AsRef<Path>>(
/// This will not prevent additional settings from being learned during parsing, for example when encountering a "#+TODO". /// This will not prevent additional settings from being learned during parsing, for example when encountering a "#+TODO".
#[allow(dead_code)] #[allow(dead_code)]
fn document<'s>(context: RefContext<'_, '_, '_, 's>, input: &'s str) -> Res<&'s str, Document<'s>> { fn document<'s>(context: RefContext<'_, '_, '_, 's>, input: &'s str) -> Res<&'s str, Document<'s>> {
let (remaining, doc) = document_org_source(context, input.into()).map_err(convert_error)?; let (remaining, doc) = document_org_source(context, input.into())?;
Ok((Into::<&str>::into(remaining), doc)) Ok((Into::<&str>::into(remaining), doc))
} }
@@ -127,27 +125,16 @@ fn document_org_source<'b, 'g, 'r, 's>(
.get_global_settings() .get_global_settings()
.file_access .file_access
.read_file(setup_file) .read_file(setup_file)
.map_err(|err| nom::Err::<CustomError<OrgSource<'_>>>::Failure(err.into())) .map_err(|err| nom::Err::<CustomError>::Failure(err.into()))
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
for setup_file in setup_files.iter().map(String::as_str) { for setup_file in setup_files.iter().map(String::as_str) {
let (_, setup_file_settings) = let (_, setup_file_settings) = scan_for_in_buffer_settings(setup_file.into())?;
scan_for_in_buffer_settings(setup_file.into()).map_err(|err| {
eprintln!("{}", err);
nom::Err::Error(CustomError::MyError(MyError(
"TODO: make this take an owned string so I can dump err.to_string() into it.",
)))
})?;
final_settings.extend(setup_file_settings); final_settings.extend(setup_file_settings);
} }
final_settings.extend(document_settings); final_settings.extend(document_settings);
let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings()) let new_settings = apply_in_buffer_settings(final_settings, context.get_global_settings())
.map_err(|err| { .map_err(nom::Err::Error)?;
eprintln!("{}", err);
nom::Err::Error(CustomError::MyError(MyError(
"TODO: make this take an owned string so I can dump err.to_string() into it.",
)))
})?;
let new_context = context.with_global_settings(&new_settings); let new_context = context.with_global_settings(&new_settings);
let context = &new_context; let context = &new_context;
@@ -172,15 +159,13 @@ fn document_org_source<'b, 'g, 'r, 's>(
let parser_context = context.with_global_settings(&new_global_settings); let parser_context = context.with_global_settings(&new_global_settings);
let (remaining, mut document) = _document(&parser_context, input) let (remaining, mut document) = _document(&parser_context, input)
.map(|(rem, out)| (Into::<&str>::into(rem), out))?; .map(|(rem, out)| (Into::<&str>::into(rem), out))?;
apply_post_parse_in_buffer_settings(&mut document) apply_post_parse_in_buffer_settings(&mut document);
.map_err(|err| nom::Err::<CustomError<OrgSource<'_>>>::Failure(err.into()))?;
return Ok((remaining.into(), document)); return Ok((remaining.into(), document));
} }
} }
// Find final in-buffer settings that do not impact parsing // Find final in-buffer settings that do not impact parsing
apply_post_parse_in_buffer_settings(&mut document) apply_post_parse_in_buffer_settings(&mut document);
.map_err(|err| nom::Err::<CustomError<OrgSource<'_>>>::Failure(err.into()))?;
Ok((remaining.into(), document)) Ok((remaining.into(), document))
} }
@@ -210,3 +195,17 @@ fn _document<'b, 'g, 'r, 's>(
}, },
)) ))
} }
#[cfg(test)]
mod tests {
use test::Bencher;
use super::*;
#[bench]
fn bench_full_document(b: &mut Bencher) {
let input = include_str!("../../org_mode_samples/element_container_priority/README.org");
b.iter(|| assert!(parse(input).is_ok()));
}
}

View File

@@ -19,7 +19,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
@@ -48,9 +47,9 @@ where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
if immediate_in_section(context, "drawer") { if immediate_in_section(context, "drawer") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
)))); )));
} }
start_of_line(remaining)?; start_of_line(remaining)?;
let (remaining, _leading_whitespace) = space0(remaining)?; let (remaining, _leading_whitespace) = space0(remaining)?;

View File

@@ -26,7 +26,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
@@ -54,9 +53,9 @@ where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
if immediate_in_section(context, "dynamic block") { if immediate_in_section(context, "dynamic block") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
)))); )));
} }
start_of_line(remaining)?; start_of_line(remaining)?;

View File

@@ -1,5 +1,3 @@
use nom::multi::many0;
use super::babel_call::babel_call; use super::babel_call::babel_call;
use super::clock::clock; use super::clock::clock;
use super::comment::comment; use super::comment::comment;
@@ -14,7 +12,6 @@ use super::footnote_definition::detect_footnote_definition;
use super::footnote_definition::footnote_definition; use super::footnote_definition::footnote_definition;
use super::greater_block::greater_block; use super::greater_block::greater_block;
use super::horizontal_rule::horizontal_rule; use super::horizontal_rule::horizontal_rule;
use super::keyword::affiliated_keyword;
use super::keyword::keyword; use super::keyword::keyword;
use super::latex_environment::latex_environment; use super::latex_environment::latex_environment;
use super::lesser_block::comment_block; use super::lesser_block::comment_block;
@@ -27,11 +24,16 @@ use super::paragraph::paragraph;
use super::plain_list::detect_plain_list; use super::plain_list::detect_plain_list;
use super::plain_list::plain_list; use super::plain_list::plain_list;
use super::table::detect_table; use super::table::detect_table;
use crate::context::parser_with_context;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
#[cfg(feature = "event_count")]
use crate::event_count::record_event;
#[cfg(feature = "event_count")]
use crate::event_count::EventType;
use crate::parser::affiliated_keyword::affiliated_keywords;
use crate::parser::bullshitium::bullshitium;
use crate::parser::bullshitium::detect_bullshitium;
use crate::parser::macros::ak_element; use crate::parser::macros::ak_element;
use crate::parser::macros::element; use crate::parser::macros::element;
use crate::parser::table::org_mode_table; use crate::parser::table::org_mode_table;
@@ -55,8 +57,9 @@ fn _element<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
can_be_paragraph: bool, can_be_paragraph: bool,
) -> Res<OrgSource<'s>, Element<'s>> { ) -> Res<OrgSource<'s>, Element<'s>> {
let (post_affiliated_keywords_input, affiliated_keywords) = #[cfg(feature = "event_count")]
many0(parser_with_context!(affiliated_keyword)(context))(input)?; record_event(EventType::ElementStart, input);
let (post_affiliated_keywords_input, affiliated_keywords) = affiliated_keywords(input)?;
let mut affiliated_keywords = affiliated_keywords.into_iter(); let mut affiliated_keywords = affiliated_keywords.into_iter();
@@ -240,6 +243,9 @@ fn _element<'b, 'g, 'r, 's>(
); );
if can_be_paragraph { if can_be_paragraph {
// Fake paragraphs
element!(bullshitium, context, input, Element::Paragraph);
// Paragraph without affiliated keyword // Paragraph without affiliated keyword
ak_element!( ak_element!(
paragraph, paragraph,
@@ -251,9 +257,7 @@ fn _element<'b, 'g, 'r, 's>(
); );
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("No element.")))
"No element.",
))))
} }
pub(crate) const fn detect_element( pub(crate) const fn detect_element(
@@ -272,8 +276,7 @@ fn _detect_element<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
can_be_paragraph: bool, can_be_paragraph: bool,
) -> Res<OrgSource<'s>, ()> { ) -> Res<OrgSource<'s>, ()> {
let (post_affiliated_keywords_input, affiliated_keywords) = let (post_affiliated_keywords_input, affiliated_keywords) = affiliated_keywords(input)?;
many0(parser_with_context!(affiliated_keyword)(context))(input)?;
let mut affiliated_keywords = affiliated_keywords.into_iter(); let mut affiliated_keywords = affiliated_keywords.into_iter();
@@ -319,11 +322,14 @@ fn _detect_element<'b, 'g, 'r, 's>(
input input
); );
// Fake paragraphs
if !can_be_paragraph {
element!(detect_bullshitium, context, input);
}
if _element(context, input, can_be_paragraph).is_ok() { if _element(context, input, can_be_paragraph).is_ok() {
return Ok((input, ())); return Ok((input, ()));
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("No element detected.")))
"No element detected.",
))))
} }

View File

@@ -1,11 +1,11 @@
use nom::branch::alt; use nom::branch::alt;
use nom::bytes::complete::tag; use nom::bytes::complete::tag;
use nom::character::complete::satisfy; use nom::character::complete::satisfy;
use nom::combinator::cond;
use nom::combinator::eof; use nom::combinator::eof;
use nom::combinator::map; use nom::combinator::map;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::combinator::verify;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource; use super::org_source::OrgSource;
@@ -13,7 +13,6 @@ use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use crate::context::EntityDefinition; use crate::context::EntityDefinition;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::types::Entity; use crate::types::Entity;
@@ -58,18 +57,21 @@ fn name<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, (&'g EntityDefinition<'s>, OrgSource<'s>, bool)> { ) -> Res<OrgSource<'s>, (&'g EntityDefinition<'s>, OrgSource<'s>, bool)> {
for entity in context.get_global_settings().entities { for entity in context.get_global_settings().entities {
let result = tuple(( let result = tuple((
tag::<_, _, CustomError<_>>(entity.name), tag::<_, _, CustomError>(entity.name),
cond(
!entity.name.ends_with(' '),
alt(( alt((
verify(map(tag("{}"), |_| true), |_| !entity.name.ends_with(' ')), map(tag("{}"), |_| true),
map(peek(recognize(entity_end)), |_| false), map(peek(recognize(entity_end)), |_| false),
)), )),
),
))(input); ))(input);
if let Ok((remaining, (ent, use_brackets))) = result { if let Ok((remaining, (ent, use_brackets))) = result {
return Ok((remaining, (entity, ent, use_brackets))); return Ok((remaining, (entity, ent, use_brackets.unwrap_or(false))));
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError("NoEntity")))) Err(nom::Err::Error(CustomError::Static("NoEntity")))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -22,7 +22,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
@@ -48,9 +47,9 @@ where
AK: IntoIterator<Item = Keyword<'s>>, AK: IntoIterator<Item = Keyword<'s>>,
{ {
if immediate_in_section(context, "footnote definition") { if immediate_in_section(context, "footnote definition") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
)))); )));
} }
start_of_line(remaining)?; start_of_line(remaining)?;
// Cannot be indented. // Cannot be indented.

View File

@@ -20,7 +20,6 @@ use crate::context::ExitMatcherNode;
use crate::context::List; use crate::context::List;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::footnote_definition::label; use crate::parser::footnote_definition::label;
use crate::parser::object_parser::standard_set_object; use crate::parser::object_parser::standard_set_object;
@@ -176,9 +175,9 @@ fn _footnote_definition_end<'b, 'g, 'r, 's>(
let current_depth = input.get_bracket_depth() - starting_bracket_depth; let current_depth = input.get_bracket_depth() - starting_bracket_depth;
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep // Its impossible for the next character to end the footnote reference definition if we're any amount of brackets deep
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"NoFootnoteReferenceDefinitionEnd", "NoFootnoteReferenceDefinitionEnd",
)))); )));
} }
if current_depth < 0 { if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing bracket should end the footnote definition. // This shouldn't be possible because if depth is 0 then a closing bracket should end the footnote definition.

View File

@@ -28,7 +28,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
@@ -232,9 +231,9 @@ fn greater_block_body<'c, 'b, 'g, 'r, 's>(
context_name: &'c str, context_name: &'c str,
) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> { ) -> Res<OrgSource<'s>, (&'s str, Vec<Element<'s>>)> {
if in_section(context, context_name) { if in_section(context, context_name) {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
)))); )));
} }
let exit_with_name = greater_block_end(name); let exit_with_name = greater_block_end(name);
let (remaining, _nl) = tuple((space0, line_ending))(input)?; let (remaining, _nl) = tuple((space0, line_ending))(input)?;

View File

@@ -18,18 +18,18 @@ use nom::sequence::tuple;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::section::section; use super::section::section;
use super::util::exit_matcher_parser;
use super::util::get_consumed; use super::util::get_consumed;
use super::util::org_line_ending; use super::util::org_line_ending;
use super::util::org_space; use super::util::org_space;
use super::util::org_space_or_line_ending; use super::util::org_space_or_line_ending;
use super::util::start_of_line; use super::util::start_of_line;
use crate::context::parser_with_context; use crate::context::bind_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ExitClass; use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::object_parser::standard_set_object; use crate::parser::object_parser::standard_set_object;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
@@ -62,10 +62,10 @@ fn _heading<'b, 'g, 'r, 's>(
let mut scheduled = None; let mut scheduled = None;
let mut deadline = None; let mut deadline = None;
let mut closed = None; let mut closed = None;
not(|i| context.check_exit_matcher(i))(input)?; not(bind_context!(exit_matcher_parser, context))(input)?;
let (remaining, pre_headline) = headline(context, input, parent_star_count)?; let (remaining, pre_headline) = headline(context, input, parent_star_count)?;
let section_matcher = parser_with_context!(section)(context); let section_matcher = bind_context!(section, context);
let heading_matcher = parser_with_context!(heading(pre_headline.star_count))(context); let heading_matcher = bind_context!(heading(pre_headline.star_count), context);
let (remaining, maybe_section) = let (remaining, maybe_section) =
opt(map(section_matcher, DocumentElement::Section))(remaining)?; opt(map(section_matcher, DocumentElement::Section))(remaining)?;
let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?; let (remaining, _ws) = opt(tuple((start_of_line, many0(blank_line))))(remaining)?;
@@ -155,7 +155,7 @@ fn headline<'b, 'g, 'r, 's>(
let (remaining, (_, (headline_level, star_count, _), _)) = tuple(( let (remaining, (_, (headline_level, star_count, _), _)) = tuple((
start_of_line, start_of_line,
verify( verify(
parser_with_context!(headline_level)(&parser_context), bind_context!(headline_level, &parser_context),
|(_, count, _)| *count > parent_star_count, |(_, count, _)| *count > parent_star_count,
), ),
peek(org_space), peek(org_space),
@@ -163,7 +163,7 @@ fn headline<'b, 'g, 'r, 's>(
let (remaining, maybe_todo_keyword) = opt(tuple(( let (remaining, maybe_todo_keyword) = opt(tuple((
space1, space1,
parser_with_context!(heading_keyword)(&parser_context), bind_context!(heading_keyword, &parser_context),
peek(org_space_or_line_ending), peek(org_space_or_line_ending),
)))(remaining)?; )))(remaining)?;
@@ -177,9 +177,7 @@ fn headline<'b, 'g, 'r, 's>(
let (remaining, maybe_title) = opt(tuple(( let (remaining, maybe_title) = opt(tuple((
space1, space1,
consumed(many1(parser_with_context!(standard_set_object)( consumed(many1(bind_context!(standard_set_object, &parser_context))),
&parser_context,
))),
)))(remaining)?; )))(remaining)?;
let (remaining, maybe_tags) = opt(tuple((space0, tags)))(remaining)?; let (remaining, maybe_tags) = opt(tuple((space0, tags)))(remaining)?;
@@ -260,7 +258,7 @@ fn heading_keyword<'b, 'g, 'r, 's>(
.iter() .iter()
.map(String::as_str) .map(String::as_str)
{ {
let result = tag::<_, _, CustomError<_>>(todo_keyword)(input); let result = tag::<_, _, CustomError>(todo_keyword)(input);
if let Ok((remaining, ent)) = result { if let Ok((remaining, ent)) = result {
return Ok((remaining, (TodoKeywordType::Todo, ent))); return Ok((remaining, (TodoKeywordType::Todo, ent)));
} }
@@ -270,14 +268,12 @@ fn heading_keyword<'b, 'g, 'r, 's>(
.iter() .iter()
.map(String::as_str) .map(String::as_str)
{ {
let result = tag::<_, _, CustomError<_>>(todo_keyword)(input); let result = tag::<_, _, CustomError>(todo_keyword)(input);
if let Ok((remaining, ent)) = result { if let Ok((remaining, ent)) = result {
return Ok((remaining, (TodoKeywordType::Done, ent))); return Ok((remaining, (TodoKeywordType::Done, ent)));
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("NoTodoKeyword")))
"NoTodoKeyword",
))))
} }
} }
@@ -288,9 +284,9 @@ fn priority_cookie(input: OrgSource<'_>) -> Res<OrgSource<'_>, PriorityCookie> {
tag("]"), tag("]"),
))(input)?; ))(input)?;
let cookie = PriorityCookie::try_from(priority_character).map_err(|_| { let cookie = PriorityCookie::try_from(priority_character).map_err(|_| {
nom::Err::Error(CustomError::MyError(MyError( nom::Err::Error(CustomError::Static(
"Failed to cast priority cookie to number.", "Failed to cast priority cookie to number.",
))) ))
})?; })?;
Ok((remaining, cookie)) Ok((remaining, cookie))
} }

View File

@@ -35,7 +35,7 @@ pub(crate) fn scan_for_in_buffer_settings<'s>(
let mut remaining = input; let mut remaining = input;
loop { loop {
// Skip text until possible in_buffer_setting // Skip text until possible in_buffer_setting
let start_of_pound = take_until::<_, _, CustomError<_>>("#+")(remaining); let start_of_pound = take_until::<_, _, CustomError>("#+")(remaining);
let start_of_pound = if let Ok((start_of_pound, _)) = start_of_pound { let start_of_pound = if let Ok((start_of_pound, _)) = start_of_pound {
start_of_pound start_of_pound
} else { } else {
@@ -47,7 +47,7 @@ pub(crate) fn scan_for_in_buffer_settings<'s>(
let (remain, maybe_kw) = match filtered_keyword(in_buffer_settings_key)(start_of_line) { let (remain, maybe_kw) = match filtered_keyword(in_buffer_settings_key)(start_of_line) {
Ok((remain, kw)) => (remain, Some(kw)), Ok((remain, kw)) => (remain, Some(kw)),
Err(_) => { Err(_) => {
let end_of_line = take_until::<_, _, CustomError<_>>("\n")(start_of_pound); let end_of_line = take_until::<_, _, CustomError>("\n")(start_of_pound);
if let Ok((end_of_line, _)) = end_of_line { if let Ok((end_of_line, _)) = end_of_line {
(end_of_line, None) (end_of_line, None)
} else { } else {
@@ -84,11 +84,14 @@ fn in_buffer_settings_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSou
))(input) ))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(level = "debug"))] #[cfg_attr(
feature = "tracing",
tracing::instrument(level = "debug", skip(original_settings))
)]
pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>( pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
keywords: Vec<Keyword<'sf>>, keywords: Vec<Keyword<'sf>>,
original_settings: &'g GlobalSettings<'g, 's>, original_settings: &'g GlobalSettings<'g, 's>,
) -> Result<GlobalSettings<'g, 's>, String> { ) -> Result<GlobalSettings<'g, 's>, CustomError> {
let mut new_settings = original_settings.clone(); let mut new_settings = original_settings.clone();
// Todo Keywords // Todo Keywords
@@ -98,7 +101,11 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
|| kw.key.eq_ignore_ascii_case("typ_todo") || kw.key.eq_ignore_ascii_case("typ_todo")
}) { }) {
let (_, (in_progress_words, complete_words)) = let (_, (in_progress_words, complete_words)) =
todo_keywords(kw.value).map_err(|err| err.to_string())?; todo_keywords(kw.value).map_err(|err| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;
new_settings new_settings
.in_progress_todo_keywords .in_progress_todo_keywords
.extend(in_progress_words.into_iter().map(str::to_string)); .extend(in_progress_words.into_iter().map(str::to_string));
@@ -112,9 +119,14 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
.iter() .iter()
.filter(|kw| kw.key.eq_ignore_ascii_case("startup")) .filter(|kw| kw.key.eq_ignore_ascii_case("startup"))
{ {
let (_remaining, settings) = let (_remaining, settings) = separated_list0(space1::<&str, CustomError>, is_not(" \t"))(
separated_list0(space1::<&str, nom::error::Error<_>>, is_not(" \t"))(kw.value) kw.value,
.map_err(|err: nom::Err<_>| err.to_string())?; )
.map_err(|err: nom::Err<_>| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;
if settings.contains(&"odd") { if settings.contains(&"odd") {
new_settings.odd_levels_only = HeadlineLevelFilter::Odd; new_settings.odd_levels_only = HeadlineLevelFilter::Odd;
} }
@@ -128,7 +140,11 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
.iter() .iter()
.filter(|kw| kw.key.eq_ignore_ascii_case("link")) .filter(|kw| kw.key.eq_ignore_ascii_case("link"))
{ {
let (_, (link_key, link_value)) = link_template(kw.value).map_err(|e| e.to_string())?; let (_, (link_key, link_value)) = link_template(kw.value).map_err(|err| match err {
nom::Err::Incomplete(_) => CustomError::Text(err.to_string()),
nom::Err::Error(e) => e,
nom::Err::Failure(e) => e,
})?;
new_settings new_settings
.link_templates .link_templates
.insert(link_key.to_owned(), link_value.to_owned()); .insert(link_key.to_owned(), link_value.to_owned());
@@ -139,9 +155,7 @@ pub(crate) fn apply_in_buffer_settings<'g, 's, 'sf>(
/// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing. /// Apply in-buffer settings that do not impact parsing and therefore can be applied after parsing.
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>( pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(document: &mut Document<'s>) {
document: &mut Document<'s>,
) -> Result<(), &'static str> {
document.category = Into::<AstNode>::into(&*document) document.category = Into::<AstNode>::into(&*document)
.into_iter() .into_iter()
.filter_map(|ast_node| { .filter_map(|ast_node| {
@@ -154,7 +168,6 @@ pub(crate) fn apply_post_parse_in_buffer_settings<'g, 's, 'sf>(
}) })
.last() .last()
.map(|kw| kw.value.to_owned()); .map(|kw| kw.value.to_owned());
Ok(())
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]

View File

@@ -19,7 +19,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
@@ -131,9 +130,7 @@ fn _header_end<'b, 'g, 'r, 's>(
let current_depth = input.get_bracket_depth() - starting_bracket_depth; let current_depth = input.get_bracket_depth() - starting_bracket_depth;
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the header if we're any amount of bracket deep // Its impossible for the next character to end the header if we're any amount of bracket deep
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("NoHeaderEnd")));
"NoHeaderEnd",
))));
} }
if current_depth < 0 { if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing bracket should end the header. // This shouldn't be possible because if depth is 0 then a closing bracket should end the header.
@@ -183,9 +180,7 @@ fn _argument_end<'b, 'g, 'r, 's>(
let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth; let current_depth = input.get_parenthesis_depth() - starting_parenthesis_depth;
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the argument if we're any amount of parenthesis deep // Its impossible for the next character to end the argument if we're any amount of parenthesis deep
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("NoArgumentEnd")));
"NoArgumentEnd",
))));
} }
if current_depth < 0 { if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing parenthesis should end the argument. // This shouldn't be possible because if depth is 0 then a closing parenthesis should end the argument.

View File

@@ -21,7 +21,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
@@ -125,9 +124,7 @@ fn _header_end<'b, 'g, 'r, 's>(
let current_depth = input.get_bracket_depth() - starting_bracket_depth; let current_depth = input.get_bracket_depth() - starting_bracket_depth;
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the header if we're any amount of bracket deep // Its impossible for the next character to end the header if we're any amount of bracket deep
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("NoHeaderEnd")));
"NoHeaderEnd",
))));
} }
if current_depth < 0 { if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing bracket should end the header. // This shouldn't be possible because if depth is 0 then a closing bracket should end the header.
@@ -187,7 +184,7 @@ fn _body_end<'b, 'g, 'r, 's>(
let current_depth = input.get_brace_depth() - starting_brace_depth; let current_depth = input.get_brace_depth() - starting_brace_depth;
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the body if we're any amount of brace deep // Its impossible for the next character to end the body if we're any amount of brace deep
return Err(nom::Err::Error(CustomError::MyError(MyError("NoBodyEnd")))); return Err(nom::Err::Error(CustomError::Static("NoBodyEnd")));
} }
if current_depth < 0 { if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing brace should end the body. // This shouldn't be possible because if depth is 0 then a closing brace should end the body.

View File

@@ -4,11 +4,9 @@ use nom::bytes::complete::tag;
use nom::bytes::complete::tag_no_case; use nom::bytes::complete::tag_no_case;
use nom::bytes::complete::take_while1; use nom::bytes::complete::take_while1;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::character::complete::line_ending;
use nom::character::complete::one_of; use nom::character::complete::one_of;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::combinator::consumed; use nom::combinator::consumed;
use nom::combinator::eof;
use nom::combinator::map; use nom::combinator::map;
use nom::combinator::not; use nom::combinator::not;
use nom::combinator::peek; use nom::combinator::peek;
@@ -22,10 +20,11 @@ use super::org_source::BracketDepth;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use super::util::get_consumed; use super::util::get_consumed;
use super::util::maybe_consume_trailing_whitespace_if_not_exiting; use super::util::maybe_consume_trailing_whitespace_if_not_exiting;
use crate::context::parser_with_context; use super::util::org_line_ending;
use crate::context::constants::ORG_ELEMENT_AFFILIATED_KEYWORDS;
use crate::context::constants::ORG_ELEMENT_DUAL_KEYWORDS;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::macros::element; use crate::parser::macros::element;
use crate::parser::util::start_of_line; use crate::parser::util::start_of_line;
@@ -50,11 +49,8 @@ fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s
// TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references. // TODO: When key is a member of org-element-parsed-keywords, value can contain the standard set objects, excluding footnote references.
let (remaining, (consumed_input, (_, _, parsed_key, _))) = let (remaining, (consumed_input, (_, _, parsed_key, _))) =
consumed(tuple((space0, tag("#+"), key_parser, tag(":"))))(input)?; consumed(tuple((space0, tag("#+"), key_parser, tag(":"))))(input)?;
if let Ok((remaining, _)) = tuple(( let (remaining, _ws) = space0(remaining)?;
space0::<OrgSource<'_>, CustomError<OrgSource<'_>>>, if let Ok((remaining, _)) = org_line_ending(remaining) {
alt((line_ending, eof)),
))(remaining)
{
return Ok(( return Ok((
remaining, remaining,
Keyword { Keyword {
@@ -65,12 +61,9 @@ fn _filtered_keyword<'s, F: Fn(OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s
}, },
)); ));
} }
let (remaining, _ws) = space0(remaining)?; let (remaining, parsed_value) =
let (remaining, parsed_value) = recognize(many_till( recognize(many_till(anychar, peek(tuple((space0, org_line_ending)))))(remaining)?;
anychar, let (remaining, _ws) = tuple((space0, org_line_ending))(remaining)?;
peek(tuple((space0, alt((line_ending, eof))))),
))(remaining)?;
let (remaining, _ws) = tuple((space0, alt((line_ending, eof))))(remaining)?;
Ok(( Ok((
remaining, remaining,
Keyword { Keyword {
@@ -106,11 +99,8 @@ where
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub(crate) fn affiliated_keyword<'b, 'g, 'r, 's>( pub(crate) fn affiliated_keyword<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, Keyword<'s>> {
context: RefContext<'b, 'g, 'r, 's>, filtered_keyword(affiliated_key)(input)
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Keyword<'s>> {
filtered_keyword(parser_with_context!(affiliated_key)(context))(input)
} }
#[cfg_attr( #[cfg_attr(
@@ -145,29 +135,18 @@ fn regular_keyword_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn affiliated_key<'b, 'g, 'r, 's>( fn affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
context: RefContext<'b, 'g, 'r, 's>, element!(dual_affiliated_key, input);
input: OrgSource<'s>, element!(plain_affiliated_key, input);
) -> Res<OrgSource<'s>, OrgSource<'s>> {
element!(dual_affiliated_key, context, input);
element!(plain_affiliated_key, context, input);
element!(export_keyword, input); element!(export_keyword, input);
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("No affiliated key.")))
"No affiliated key.",
))))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn plain_affiliated_key<'b, 'g, 'r, 's>( fn plain_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
context: RefContext<'b, 'g, 'r, 's>, for keyword in ORG_ELEMENT_AFFILIATED_KEYWORDS {
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
for keyword in context.get_global_settings().element_affiliated_keywords {
let result = map( let result = map(
tuple(( tuple((tag_no_case::<_, _, CustomError>(keyword), peek(tag(":")))),
tag_no_case::<_, _, CustomError<_>>(*keyword),
peek(tag(":")),
)),
|(key, _)| key, |(key, _)| key,
)(input); )(input);
if let Ok((remaining, ent)) = result { if let Ok((remaining, ent)) = result {
@@ -175,19 +154,14 @@ fn plain_affiliated_key<'b, 'g, 'r, 's>(
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("NoKeywordKey")))
"NoKeywordKey",
))))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn dual_affiliated_key<'b, 'g, 'r, 's>( fn dual_affiliated_key<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
context: RefContext<'b, 'g, 'r, 's>, for keyword in ORG_ELEMENT_DUAL_KEYWORDS {
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
for keyword in context.get_global_settings().element_dual_keywords {
let result = recognize(tuple(( let result = recognize(tuple((
tag_no_case::<_, _, CustomError<_>>(*keyword), tag_no_case::<_, _, CustomError>(keyword),
tag("["), tag("["),
optval, optval,
tag("]"), tag("]"),
@@ -198,9 +172,7 @@ fn dual_affiliated_key<'b, 'g, 'r, 's>(
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("NoKeywordKey")))
"NoKeywordKey",
))))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
@@ -228,7 +200,7 @@ fn _optval_end<'s>(
unreachable!("Exceeded optval bracket depth.") unreachable!("Exceeded optval bracket depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<_, _, CustomError<_>>("]")(input); let close_bracket = tag::<_, _, CustomError>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -249,19 +221,12 @@ mod tests {
use test::Bencher; use test::Bencher;
use super::*; use super::*;
use crate::context::Context;
use crate::context::ContextElement;
use crate::context::GlobalSettings;
use crate::context::List;
use crate::parser::OrgSource; use crate::parser::OrgSource;
#[bench] #[bench]
fn bench_affiliated_keyword(b: &mut Bencher) { fn bench_affiliated_keyword(b: &mut Bencher) {
let input = OrgSource::new("#+CAPTION[*foo*]: bar *baz*"); let input = OrgSource::new("#+CAPTION[*foo*]: bar *baz*");
let global_settings = GlobalSettings::default();
let initial_context = ContextElement::document_context();
let initial_context = Context::new(&global_settings, List::new(&initial_context));
b.iter(|| assert!(affiliated_keyword(&initial_context, input).is_ok())); b.iter(|| assert!(affiliated_keyword(input).is_ok()));
} }
} }

View File

@@ -17,7 +17,6 @@ use super::util::maybe_consume_object_trailing_whitespace_if_not_exiting;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
@@ -209,9 +208,9 @@ fn pre<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, ()> { ) -> Res<OrgSource<'s>, ()> {
let preceding_character = input.get_preceding_character(); let preceding_character = input.get_preceding_character();
if let Some('$') = preceding_character { if let Some('$') = preceding_character {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid pre character for dollar char fragment.", "Not a valid pre character for dollar char fragment.",
)))); )));
} }
Ok((input, ())) Ok((input, ()))
} }
@@ -283,9 +282,9 @@ fn close_border<'b, 'g, 'r, 's>(
match preceding_character { match preceding_character {
Some(c) if !c.is_whitespace() && !".,;$".contains(c) => Ok((input, ())), Some(c) if !c.is_whitespace() && !".,;$".contains(c) => Ok((input, ())),
_ => { _ => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid pre character for dollar char fragment.", "Not a valid pre character for dollar char fragment.",
)))); )));
} }
} }
} }

View File

@@ -27,7 +27,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::object_parser::standard_set_object; use crate::parser::object_parser::standard_set_object;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
@@ -604,7 +603,7 @@ fn _example_src_switches<'s>(
} }
} }
if !matched_a_word { if !matched_a_word {
return Err(nom::Err::Error(CustomError::MyError(MyError("No words.")))); return Err(nom::Err::Error(CustomError::Static("No words.")));
} }
let remaining = last_match_remaining; let remaining = last_match_remaining;

View File

@@ -7,7 +7,6 @@ use nom::multi::many0;
use super::org_source::OrgSource; use super::org_source::OrgSource;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::types::LineBreak; use crate::types::LineBreak;
@@ -45,9 +44,9 @@ fn pre<'b, 'g, 'r, 's>(
match preceding_character { match preceding_character {
// If None, we are at the start of the file // If None, we are at the start of the file
None | Some('\\') => { None | Some('\\') => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid pre character for line break.", "Not a valid pre character for line break.",
)))); )));
} }
_ => {} _ => {}
}; };
@@ -55,9 +54,9 @@ fn pre<'b, 'g, 'r, 's>(
let current_line = input.text_since_line_break(); let current_line = input.text_since_line_break();
let is_non_empty_line = current_line.chars().any(|c| !c.is_whitespace()); let is_non_empty_line = current_line.chars().any(|c| !c.is_whitespace());
if !is_non_empty_line { if !is_non_empty_line {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid pre line for line break.", "Not a valid pre line for line break.",
)))); )));
} }
Ok((input, ())) Ok((input, ()))

View File

@@ -1,6 +1,7 @@
mod affiliated_keyword; mod affiliated_keyword;
mod angle_link; mod angle_link;
mod babel_call; mod babel_call;
mod bullshitium;
mod citation; mod citation;
mod citation_reference; mod citation_reference;
mod clock; mod clock;

View File

@@ -4,7 +4,6 @@ use super::regular_link::regular_link;
use super::subscript_and_superscript::detect_subscript_or_superscript; use super::subscript_and_superscript::detect_subscript_or_superscript;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::angle_link::angle_link; use crate::parser::angle_link::angle_link;
use crate::parser::citation::citation; use crate::parser::citation::citation;
@@ -43,7 +42,7 @@ pub(crate) fn standard_set_object<'b, 'g, 'r, 's>(
input, input,
Object::PlainText Object::PlainText
); );
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -61,7 +60,7 @@ pub(crate) fn minimal_set_object<'b, 'g, 'r, 's>(
input, input,
Object::PlainText Object::PlainText
); );
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -103,7 +102,7 @@ fn standard_set_object_sans_plain_text<'b, 'g, 'r, 's>(
element!(angle_link, context, input, Object::AngleLink); element!(angle_link, context, input, Object::AngleLink);
element!(org_macro, context, input, Object::OrgMacro); element!(org_macro, context, input, Object::OrgMacro);
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -119,7 +118,7 @@ fn minimal_set_object_sans_plain_text<'b, 'g, 'r, 's>(
element!(entity, context, input, Object::Entity); element!(entity, context, input, Object::Entity);
element!(latex_fragment, context, input, Object::LatexFragment); element!(latex_fragment, context, input, Object::LatexFragment);
element!(text_markup, context, input); element!(text_markup, context, input);
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -137,9 +136,7 @@ pub(crate) fn detect_standard_set_object_sans_plain_text<'b, 'g, 'r, 's>(
return Ok((input, ())); return Ok((input, ()));
} }
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("No object detected.")));
"No object detected.",
))));
} }
#[cfg_attr( #[cfg_attr(
@@ -157,9 +154,7 @@ fn detect_minimal_set_object_sans_plain_text<'b, 'g, 'r, 's>(
return Ok((input, ())); return Ok((input, ()));
} }
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("No object detected.")));
"No object detected.",
))));
} }
#[cfg_attr( #[cfg_attr(
@@ -182,7 +177,7 @@ pub(crate) fn regular_link_description_set_object<'b, 'g, 'r, 's>(
input, input,
Object::PlainText Object::PlainText
); );
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -205,7 +200,7 @@ fn regular_link_description_set_object_sans_plain_text<'b, 'g, 'r, 's>(
element!(inline_babel_call, context, input, Object::InlineBabelCall); element!(inline_babel_call, context, input, Object::InlineBabelCall);
element!(org_macro, context, input, Object::OrgMacro); element!(org_macro, context, input, Object::OrgMacro);
element!(minimal_set_object_sans_plain_text, context, input); element!(minimal_set_object_sans_plain_text, context, input);
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -223,9 +218,7 @@ fn detect_regular_link_description_set_object_sans_plain_text<'b, 'g, 'r, 's>(
return Ok((input, ())); return Ok((input, ()));
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("No object detected.")))
"No object detected.",
))))
} }
#[cfg_attr( #[cfg_attr(
@@ -243,7 +236,7 @@ pub(crate) fn table_cell_set_object<'b, 'g, 'r, 's>(
input, input,
Object::PlainText Object::PlainText
); );
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -271,7 +264,7 @@ fn table_cell_set_object_sans_plain_text<'b, 'g, 'r, 's>(
element!(target, context, input, Object::Target); element!(target, context, input, Object::Target);
element!(timestamp, context, input, Object::Timestamp); element!(timestamp, context, input, Object::Timestamp);
element!(minimal_set_object_sans_plain_text, context, input); element!(minimal_set_object_sans_plain_text, context, input);
Err(nom::Err::Error(CustomError::MyError(MyError("No object.")))) Err(nom::Err::Error(CustomError::Static("No object.")))
} }
#[cfg_attr( #[cfg_attr(
@@ -289,7 +282,5 @@ fn detect_table_cell_set_object_sans_plain_text<'b, 'g, 'r, 's>(
return Ok((input, ())); return Ok((input, ()));
} }
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("No object detected.")));
"No object detected.",
))));
} }

View File

@@ -96,7 +96,7 @@ fn org_macro_arg<'b, 'g, 'r, 's>(
loop { loop {
not(parser_with_context!(exit_matcher_parser)(context))(remaining)?; not(parser_with_context!(exit_matcher_parser)(context))(remaining)?;
not(peek(tag("}}}")))(remaining)?; not(peek(tag("}}}")))(remaining)?;
if peek(tuple((space0::<OrgSource<'_>, CustomError<_>>, tag(")"))))(remaining).is_ok() { if peek(tuple((space0::<_, CustomError>, tag(")"))))(remaining).is_ok() {
break; break;
} }
@@ -108,7 +108,7 @@ fn org_macro_arg<'b, 'g, 'r, 's>(
} }
if next_char == '\\' { if next_char == '\\' {
escaping = true; escaping = true;
if peek(tag::<_, _, CustomError<_>>(")"))(new_remaining).is_ok() { if peek(tag::<_, _, CustomError>(")"))(new_remaining).is_ok() {
// Special case for backslash at the end of a macro // Special case for backslash at the end of a macro
remaining = new_remaining; remaining = new_remaining;
break; break;

View File

@@ -10,9 +10,6 @@ use nom::InputTakeAtPosition;
use nom::Offset; use nom::Offset;
use nom::Slice; use nom::Slice;
use crate::error::CustomError;
use crate::error::MyError;
pub(crate) type BracketDepth = i16; pub(crate) type BracketDepth = i16;
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
@@ -85,6 +82,15 @@ impl<'s> OrgSource<'s> {
self.slice(..(other.end - self.start)) self.slice(..(other.end - self.start))
} }
pub(crate) fn get_until_end_of_str(&self, other: &'s str) -> OrgSource<'s> {
let full_source_start = self.full_source.as_ptr() as usize;
let other_start = other.as_ptr() as usize - full_source_start;
let other_end = other_start + other.len();
debug_assert!(other_start >= self.start);
debug_assert!(other_end <= self.end);
self.slice(..(other_end - self.start))
}
pub(crate) fn get_start_of_line(&self) -> OrgSource<'s> { pub(crate) fn get_start_of_line(&self) -> OrgSource<'s> {
let skipped_text = self.text_since_line_break(); let skipped_text = self.text_since_line_break();
let mut bracket_depth = self.bracket_depth; let mut bracket_depth = self.bracket_depth;
@@ -385,33 +391,6 @@ impl<'n, 's> FindSubstring<&'n str> for OrgSource<'s> {
} }
} }
pub(crate) fn convert_error<'a, I: Into<CustomError<&'a str>>>(
err: nom::Err<I>,
) -> nom::Err<CustomError<&'a str>> {
match err {
nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed),
nom::Err::Error(err) => nom::Err::Error(err.into()),
nom::Err::Failure(err) => nom::Err::Failure(err.into()),
}
}
impl<'s> From<CustomError<OrgSource<'s>>> for CustomError<&'s str> {
fn from(value: CustomError<OrgSource<'s>>) -> Self {
match value {
CustomError::MyError(err) => CustomError::MyError(err),
CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind),
CustomError::IO(err) => CustomError::IO(err),
CustomError::BoxedError(err) => CustomError::BoxedError(err),
}
}
}
impl<'s> From<MyError<OrgSource<'s>>> for MyError<&'s str> {
fn from(value: MyError<OrgSource<'s>>) -> Self {
MyError(value.0.into())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@@ -36,7 +36,6 @@ use crate::context::ExitMatcherNode;
use crate::context::Matcher; use crate::context::Matcher;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
@@ -95,9 +94,9 @@ fn pre<'b, 'g, 'r, 's>(
Some(x) if !WORD_CONSTITUENT_CHARACTERS.contains(x) => {} Some(x) if !WORD_CONSTITUENT_CHARACTERS.contains(x) => {}
Some(_) => { Some(_) => {
// Not at start of line, cannot be a heading // Not at start of line, cannot be a heading
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid pre character for plain link.", "Not a valid pre character for plain link.",
)))); )));
} }
}; };
Ok((input, ())) Ok((input, ()))
@@ -262,15 +261,13 @@ pub(crate) fn protocol<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
for link_parameter in context.get_global_settings().link_parameters { for link_parameter in context.get_global_settings().link_parameters {
let result = tag_no_case::<_, _, CustomError<_>>(*link_parameter)(input); let result = tag_no_case::<_, _, CustomError>(*link_parameter)(input);
if let Ok((remaining, ent)) = result { if let Ok((remaining, ent)) = result {
return Ok((remaining, ent)); return Ok((remaining, ent));
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("NoLinkProtocol")))
"NoLinkProtocol",
))))
} }
#[cfg_attr( #[cfg_attr(
@@ -327,8 +324,7 @@ fn impl_path_plain_end<'b, 'g, 'r, 's>(
} }
if current_depth == 0 { if current_depth == 0 {
let close_parenthesis = let close_parenthesis = tag::<_, _, CustomError>(")")(remaining);
tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(remaining);
if close_parenthesis.is_ok() { if close_parenthesis.is_ok() {
return close_parenthesis; return close_parenthesis;
} }
@@ -342,9 +338,7 @@ fn impl_path_plain_end<'b, 'g, 'r, 's>(
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("No path plain end")))
"No path plain end",
))))
} }
#[cfg_attr( #[cfg_attr(
@@ -420,18 +414,18 @@ fn _path_plain_parenthesis_end<'s>(
unreachable!("Exceeded plain link parenthesis depth.") unreachable!("Exceeded plain link parenthesis depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input); let close_parenthesis = tag::<_, _, CustomError>(")")(input);
if close_parenthesis.is_ok() { if close_parenthesis.is_ok() {
return close_parenthesis; return close_parenthesis;
} }
} }
if current_depth == 1 { if current_depth == 1 {
let open_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("(")(input); let open_parenthesis = tag::<_, _, CustomError>("(")(input);
if open_parenthesis.is_ok() { if open_parenthesis.is_ok() {
return open_parenthesis; return open_parenthesis;
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static(
"No closing parenthesis", "No closing parenthesis",
)))) )))
} }

View File

@@ -3,6 +3,7 @@ use nom::bytes::complete::tag;
use nom::character::complete::anychar; use nom::character::complete::anychar;
use nom::character::complete::digit1; use nom::character::complete::digit1;
use nom::character::complete::line_ending; use nom::character::complete::line_ending;
use nom::character::complete::multispace1;
use nom::character::complete::one_of; use nom::character::complete::one_of;
use nom::character::complete::space0; use nom::character::complete::space0;
use nom::character::complete::space1; use nom::character::complete::space1;
@@ -17,6 +18,7 @@ use nom::multi::many0;
use nom::multi::many1; use nom::multi::many1;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use nom::InputTake;
use super::affiliated_keyword::parse_affiliated_keywords; use super::affiliated_keyword::parse_affiliated_keywords;
use super::element_parser::element; use super::element_parser::element;
@@ -25,6 +27,7 @@ use super::org_source::OrgSource;
use super::util::include_input; use super::util::include_input;
use super::util::indentation_level; use super::util::indentation_level;
use super::util::non_whitespace_character; use super::util::non_whitespace_character;
use crate::context::bind_context;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::ContextMatcher; use crate::context::ContextMatcher;
@@ -32,7 +35,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::blank_line; use crate::parser::util::blank_line;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
@@ -78,9 +80,47 @@ where
{ {
return Ok((input, ())); return Ok((input, ()));
} }
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static("No element detected.")));
"No element detected.", }
))));
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn detect_not_plain_list_item_indent<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, (u16, OrgSource<'s>)> {
if let Ok((_remaining, (_, indent, _))) = tuple((
start_of_line,
parser_with_context!(indentation_level)(context),
not(tuple((
parser_with_context!(bullet)(context),
alt((space1, line_ending, eof)),
))),
))(input)
{
return Ok((input, indent));
}
// Headlines are not plain list items.
if let Ok((_remaining, (_, indent, _))) = verify(
tuple((
start_of_line,
parser_with_context!(indentation_level)(context),
tuple((
parser_with_context!(bullet)(context),
alt((space1, line_ending, eof)),
)),
)),
|(_, (depth, _), ((_, bullet), _))| {
*depth == 0 && Into::<&str>::into(bullet).starts_with('*')
},
)(input)
{
return Ok((input, indent));
}
return Err(nom::Err::Error(CustomError::Static("No element detected.")));
} }
#[cfg_attr( #[cfg_attr(
@@ -123,7 +163,7 @@ where
// While #3 is the most slow, it also seems to cleanest and involves the least manual mutation of already-parsed objects so I am going with #3 for now, but we should revisit #1 or #2 when the parser is more developed. // While #3 is the most slow, it also seems to cleanest and involves the least manual mutation of already-parsed objects so I am going with #3 for now, but we should revisit #1 or #2 when the parser is more developed.
loop { loop {
let list_item = parser_with_context!(plain_list_item)(&parser_context)(remaining); let list_item = plain_list_item(&parser_context, remaining);
match (&first_item_list_type, &list_item) { match (&first_item_list_type, &list_item) {
(None, Ok((_remain, (list_type, _item)))) => { (None, Ok((_remain, (list_type, _item)))) => {
let _ = first_item_list_type.insert(*list_type); let _ = first_item_list_type.insert(*list_type);
@@ -143,25 +183,17 @@ where
} }
}; };
let maybe_exit = parser_with_context!(exit_matcher_parser)(&parser_context)(remaining); let maybe_exit = exit_matcher_parser(&parser_context, remaining);
if maybe_exit.is_ok() { if maybe_exit.is_ok() {
break; break;
} }
} }
let (final_child_start, _final_item_first_parse) = match children.pop() { if children.is_empty() {
Some(final_child) => final_child, return Err(nom::Err::Error(CustomError::Static(
None => {
return Err(nom::Err::Error(CustomError::MyError(MyError(
"Plain lists require at least one element.", "Plain lists require at least one element.",
)))); )));
} }
};
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
let final_item_context = parser_context.with_additional_node(&final_item_context);
let (remaining, (_, reparsed_final_item)) =
parser_with_context!(plain_list_item)(&final_item_context)(final_child_start)?;
children.push((final_child_start, reparsed_final_item));
let (remaining, _trailing_ws) = let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
@@ -190,10 +222,10 @@ fn plain_list_item<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, (PlainListType, PlainListItem<'s>)> { ) -> Res<OrgSource<'s>, (PlainListType, PlainListItem<'s>)> {
start_of_line(input)?; start_of_line(input)?;
let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?; let (remaining, (indent_level, _leading_whitespace)) = indentation_level(context, input)?;
let (remaining, (bullet_type, bull)) = verify( let (remaining, (bullet_type, bull)) =
parser_with_context!(bullet)(context), verify(bind_context!(bullet, context), |(_bullet_type, bull)| {
|(_bullet_type, bull)| !Into::<&str>::into(bull).starts_with('*') || indent_level > 0, !Into::<&str>::into(bull).starts_with('*') || indent_level > 0
)(remaining)?; })(remaining)?;
let (remaining, maybe_counter_set) = let (remaining, maybe_counter_set) =
opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?; opt(tuple((space1, tag("[@"), counter_set_value, tag("]"))))(remaining)?;
@@ -202,7 +234,7 @@ fn plain_list_item<'b, 'g, 'r, 's>(
let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?; let (remaining, maybe_checkbox) = opt(tuple((space1, item_checkbox)))(remaining)?;
let (remaining, maybe_tag) = if let BulletType::Unordered = bullet_type { let (remaining, maybe_tag) = if let BulletType::Unordered = bullet_type {
opt(tuple((space1, parser_with_context!(item_tag)(context))))(remaining)? opt(tuple((space1, bind_context!(item_tag, context))))(remaining)?
} else { } else {
(remaining, None) (remaining, None)
}; };
@@ -214,6 +246,12 @@ fn plain_list_item<'b, 'g, 'r, 's>(
}; };
let exit_matcher = plain_list_item_end(indent_level); let exit_matcher = plain_list_item_end(indent_level);
let final_item_whitespace_cutoff = final_item_whitespace_cutoff(indent_level);
let final_whitespace_context = ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta,
exit_matcher: &final_item_whitespace_cutoff,
});
let final_whitespace_context = context.with_additional_node(&final_whitespace_context);
let contexts = [ let contexts = [
ContextElement::ConsumeTrailingWhitespace(true), ContextElement::ConsumeTrailingWhitespace(true),
ContextElement::ExitMatcherNode(ExitMatcherNode { ContextElement::ExitMatcherNode(ExitMatcherNode {
@@ -221,17 +259,21 @@ fn plain_list_item<'b, 'g, 'r, 's>(
exit_matcher: &exit_matcher, exit_matcher: &exit_matcher,
}), }),
]; ];
let parser_context = context.with_additional_node(&contexts[0]); let parser_context = final_whitespace_context.with_additional_node(&contexts[0]);
let parser_context = parser_context.with_additional_node(&contexts[1]); let parser_context = parser_context.with_additional_node(&contexts[1]);
let maybe_contentless_item: Res<OrgSource<'_>, ()> = peek(parser_with_context!( let maybe_contentless_item: Res<OrgSource<'_>, ()> =
detect_contentless_item_contents detect_contentless_item_contents(&parser_context, remaining);
)(&parser_context))(remaining);
if let Ok((_rem, _ws)) = maybe_contentless_item { if let Ok((_rem, _ws)) = maybe_contentless_item {
let (remaining, _trailing_ws) = if context.should_consume_trailing_whitespace() { let (remaining, _trailing_ws) = if tuple((
recognize(alt((recognize(many1(blank_line)), eof)))(remaining)? blank_line,
} else { bind_context!(final_item_whitespace_cutoff, context),
))(remaining)
.is_ok()
{
recognize(alt((blank_line, eof)))(remaining)? recognize(alt((blank_line, eof)))(remaining)?
} else {
recognize(alt((recognize(many1(blank_line)), eof)))(remaining)?
}; };
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
return Ok(( return Ok((
@@ -259,26 +301,14 @@ fn plain_list_item<'b, 'g, 'r, 's>(
.filter(|b| *b == b'\n') .filter(|b| *b == b'\n')
.count(); .count();
let (mut remaining, (mut children, _exit_contents)) = many_till( let (remaining, (children, _exit_contents)) = many_till(
include_input(parser_with_context!(element(true))(&parser_context)), include_input(bind_context!(element(true), &parser_context)),
parser_with_context!(exit_matcher_parser)(&parser_context), bind_context!(exit_matcher_parser, &parser_context),
)(remaining)?; )(remaining)?;
if !children.is_empty() && !context.should_consume_trailing_whitespace() { // We have to use the parser_context here to include the whitespace cut-off
let final_item_context = ContextElement::ConsumeTrailingWhitespace(false);
let final_item_context = parser_context.with_additional_node(&final_item_context);
let (final_child_start, _original_final_child) = children
.pop()
.expect("if-statement already checked that children was non-empty.");
let (remain, reparsed_final_element) = include_input(parser_with_context!(element(true))(
&final_item_context,
))(final_child_start)?;
remaining = remain;
children.push(reparsed_final_element);
}
let (remaining, _trailing_ws) = let (remaining, _trailing_ws) =
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(&final_whitespace_context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
return Ok(( return Ok((
@@ -325,7 +355,7 @@ fn bullet<'b, 'g, 'r, 's>(
map(tag("+"), |bull| (BulletType::Unordered, bull)), map(tag("+"), |bull| (BulletType::Unordered, bull)),
map( map(
recognize(tuple(( recognize(tuple((
parser_with_context!(counter)(context), bind_context!(counter, context),
alt((tag("."), tag(")"))), alt((tag("."), tag(")"))),
))), ))),
|bull| (BulletType::Ordered, bull), |bull| (BulletType::Ordered, bull),
@@ -380,6 +410,52 @@ fn counter_set_value<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, PlainListIt
))(input) ))(input)
} }
const fn final_item_whitespace_cutoff(indent_level: IndentationLevel) -> impl ContextMatcher {
move |context, input: OrgSource<'_>| {
impl_final_item_whitespace_cutoff(context, input, indent_level)
}
}
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
fn impl_final_item_whitespace_cutoff<'b, 'g, 'r, 's>(
context: RefContext<'b, 'g, 'r, 's>,
input: OrgSource<'s>,
indent_level: IndentationLevel,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
// element!(plain_list_end, context, input);
if let Ok((_remaining, _)) = verify(
tuple((
opt(blank_line),
bind_context!(indentation_level, context),
not(multispace1),
)),
|(_, (depth, _stars), _not_whitespace)| *depth < indent_level,
)(input)
{
return Ok((input, input.take(0)));
}
if let Ok((_remaining, _)) = tuple((
opt(blank_line),
verify(
bind_context!(detect_not_plain_list_item_indent, context),
|(depth, _)| *depth == indent_level,
),
))(input)
{
return Ok((input, input.take(0)));
}
Err(nom::Err::Error(CustomError::Static(
"No whitespace cut-off.",
)))
}
#[cfg_attr( #[cfg_attr(
feature = "tracing", feature = "tracing",
tracing::instrument(ret, level = "debug", skip(_context)) tracing::instrument(ret, level = "debug", skip(_context))
@@ -415,7 +491,7 @@ fn _plain_list_item_end<'b, 'g, 'r, 's>(
start_of_line(input)?; start_of_line(input)?;
recognize(tuple(( recognize(tuple((
opt(blank_line), opt(blank_line),
parser_with_context!(line_indented_lte_matcher)(context), bind_context!(line_indented_lte_matcher, context),
)))(input) )))(input)
} }
@@ -434,7 +510,7 @@ fn _line_indented_lte<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let matched = recognize(verify( let matched = recognize(verify(
tuple(( tuple((
parser_with_context!(indentation_level)(context), bind_context!(indentation_level, context),
non_whitespace_character, non_whitespace_character,
)), )),
// It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09) // It is fine that we get the indent level using the number of bytes rather than the number of characters because nom's space0 only matches space and tab (0x20 and 0x09)
@@ -460,8 +536,8 @@ fn item_tag<'b, 'g, 'r, 's>(
let (remaining, (children, _exit_contents)) = verify( let (remaining, (children, _exit_contents)) = verify(
many_till( many_till(
// TODO: Should this be using a different set like the minimal set? // TODO: Should this be using a different set like the minimal set?
parser_with_context!(standard_set_object)(&parser_context), bind_context!(standard_set_object, &parser_context),
parser_with_context!(exit_matcher_parser)(&parser_context), bind_context!(exit_matcher_parser, &parser_context),
), ),
|(children, _exit_contents)| !children.is_empty(), |(children, _exit_contents)| !children.is_empty(),
)(input)?; )(input)?;
@@ -511,7 +587,7 @@ fn item_tag_post_gap<'b, 'g, 'r, 's>(
alt(( alt((
peek(recognize(not(blank_line))), peek(recognize(not(blank_line))),
peek(recognize(tuple((many0(blank_line), eof)))), peek(recognize(tuple((many0(blank_line), eof)))),
parser_with_context!(exit_matcher_parser)(context), bind_context!(exit_matcher_parser, context),
)), )),
), ),
))), ))),
@@ -541,7 +617,7 @@ fn detect_contentless_item_contents<'b, 'g, 'r, 's>(
) -> Res<OrgSource<'s>, ()> { ) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = recognize(many_till( let (remaining, _) = recognize(many_till(
blank_line, blank_line,
parser_with_context!(exit_matcher_parser)(context), bind_context!(exit_matcher_parser, context),
))(input)?; ))(input)?;
Ok((remaining, ())) Ok((remaining, ()))
} }

View File

@@ -20,7 +20,6 @@ use super::util::org_space_or_line_ending;
use crate::context::parser_with_context; use crate::context::parser_with_context;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::types::Object; use crate::types::Object;
use crate::types::PlainText; use crate::types::PlainText;
@@ -92,7 +91,7 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
break; break;
} }
let is_not_whitespace = is_not::<&str, &str, CustomError<_>>(" \t\r\n")(goal); let is_not_whitespace = is_not::<_, _, CustomError>(" \t\r\n")(goal);
if let Ok((new_goal, payload)) = is_not_whitespace { if let Ok((new_goal, payload)) = is_not_whitespace {
let (new_remaining, _) = tuple(( let (new_remaining, _) = tuple((
tag_no_case(payload), tag_no_case(payload),
@@ -108,7 +107,7 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
} }
let is_whitespace = recognize(many1(alt(( let is_whitespace = recognize(many1(alt((
recognize(one_of::<&str, &str, CustomError<_>>(" \t")), recognize(one_of::<_, _, CustomError>(" \t")),
line_ending, line_ending,
))))(goal); ))))(goal);
if let Ok((new_goal, _)) = is_whitespace { if let Ok((new_goal, _)) = is_whitespace {
@@ -118,9 +117,9 @@ impl<'x> RematchObject<'x> for PlainText<'x> {
continue; continue;
} }
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Target does not match.", "Target does not match.",
)))); )));
} }
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);

View File

@@ -21,7 +21,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::parser::util::immediate_in_section; use crate::parser::util::immediate_in_section;
@@ -39,9 +38,9 @@ pub(crate) fn property_drawer<'b, 'g, 'r, 's>(
input: OrgSource<'s>, input: OrgSource<'s>,
) -> Res<OrgSource<'s>, PropertyDrawer<'s>> { ) -> Res<OrgSource<'s>, PropertyDrawer<'s>> {
if immediate_in_section(context, "property-drawer") { if immediate_in_section(context, "property-drawer") {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Cannot nest objects of the same element", "Cannot nest objects of the same element",
)))); )));
} }
let ( let (
remaining, remaining,

View File

@@ -21,7 +21,6 @@ use crate::context::ExitMatcherNode;
use crate::context::List; use crate::context::List;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::types::Object; use crate::types::Object;
@@ -52,9 +51,7 @@ pub(crate) fn radio_link<'b, 'g, 'r, 's>(
)); ));
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("NoRadioLink")))
"NoRadioLink",
))))
} }
#[cfg_attr( #[cfg_attr(
@@ -98,9 +95,9 @@ pub(crate) fn rematch_target<'x, 'b, 'g, 'r, 's>(
new_matches.push(new_match); new_matches.push(new_match);
} }
_ => { _ => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"OnlyMinimalSetObjectsAllowed", "OnlyMinimalSetObjectsAllowed",
)))); )));
} }
}; };
} }

View File

@@ -43,7 +43,6 @@ use crate::context::ExitMatcherNode;
use crate::context::List; use crate::context::List;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::types::LinkType; use crate::types::LinkType;
use crate::types::Object; use crate::types::Object;
@@ -163,11 +162,7 @@ fn parse_path_reg<'b, 'g, 'r, 's>(
parser_with_context!(protocol_path_reg)(context), parser_with_context!(protocol_path_reg)(context),
fuzzy_path_reg, fuzzy_path_reg,
))(replaced_input) ))(replaced_input)
.map_err(|_| { .map_err(|_| nom::Err::Error(CustomError::Static("No pathreg match after replacement.")))?;
nom::Err::Error(CustomError::MyError(MyError(
"No pathreg match after replacement.",
)))
})?;
let remaining = input.take(input.len()); let remaining = input.take(input.len());
let link_type = match link.link_type { let link_type = match link.link_type {
LinkType::Protocol(protocol) => LinkType::Protocol(protocol.into_owned().into()), LinkType::Protocol(protocol) => LinkType::Protocol(protocol.into_owned().into()),
@@ -483,7 +478,5 @@ fn impl_path_reg_end<'b, 'g, 'r, 's>(
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("No path reg end")))
"No path reg end",
))))
} }

View File

@@ -26,7 +26,6 @@ use crate::context::ExitMatcherNode;
use crate::context::Matcher; use crate::context::Matcher;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::types::Object; use crate::types::Object;
@@ -39,7 +38,7 @@ pub(crate) fn detect_subscript_or_superscript<'s>(input: OrgSource<'s>) -> Res<O
// This does not have to detect all valid subscript/superscript but all that it detects must be valid. // This does not have to detect all valid subscript/superscript but all that it detects must be valid.
let (remaining, _) = one_of("_^")(input)?; let (remaining, _) = one_of("_^")(input)?;
pre(input)?; pre(input)?;
if tag::<_, _, CustomError<_>>("*")(remaining).is_ok() { if tag::<_, _, CustomError>("*")(remaining).is_ok() {
return Ok((input, ())); return Ok((input, ()));
} }
let (remaining, _) = opt(one_of("+-"))(remaining)?; let (remaining, _) = opt(one_of("+-"))(remaining)?;
@@ -232,9 +231,9 @@ fn _script_with_braces_end<'b, 'g, 'r, 's>(
let current_depth = input.get_brace_depth() - starting_brace_depth; let current_depth = input.get_brace_depth() - starting_brace_depth;
if current_depth > 0 { if current_depth > 0 {
// Its impossible for the next character to end the subscript or superscript if we're any amount of braces deep // Its impossible for the next character to end the subscript or superscript if we're any amount of braces deep
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid end for subscript or superscript.", "Not a valid end for subscript or superscript.",
)))); )));
} }
if current_depth < 0 { if current_depth < 0 {
// This shouldn't be possible because if depth is 0 then a closing brace should end the subscript or superscript. // This shouldn't be possible because if depth is 0 then a closing brace should end the subscript or superscript.
@@ -282,12 +281,12 @@ fn _script_with_parenthesis_end<'s>(
unreachable!("Exceeded citation key suffix bracket depth.") unreachable!("Exceeded citation key suffix bracket depth.")
} }
if current_depth == 0 { if current_depth == 0 {
let close_parenthesis = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>(")")(input); let close_parenthesis = tag::<_, _, CustomError>(")")(input);
if close_parenthesis.is_ok() { if close_parenthesis.is_ok() {
return close_parenthesis; return close_parenthesis;
} }
} }
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static(
"No script parenthesis end.", "No script parenthesis end.",
)))) )))
} }

View File

@@ -13,7 +13,6 @@ use crate::context::ExitClass;
use crate::context::ExitMatcherNode; use crate::context::ExitMatcherNode;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
use crate::types::Target; use crate::types::Target;
@@ -42,9 +41,9 @@ pub(crate) fn target<'b, 'g, 'r, 's>(
.get_preceding_character() .get_preceding_character()
.expect("We cannot be at the start of the file because we are inside a target."); .expect("We cannot be at the start of the file because we are inside a target.");
if preceding_character.is_whitespace() { if preceding_character.is_whitespace() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Targets cannot end with whitespace.", "Targets cannot end with whitespace.",
)))); )));
} }
let (remaining, _) = tag(">>")(remaining)?; let (remaining, _) = tag(">>")(remaining)?;
let (remaining, _trailing_whitespace) = let (remaining, _trailing_whitespace) =

View File

@@ -34,7 +34,6 @@ use crate::context::ExitMatcherNode;
use crate::context::List; use crate::context::List;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::radio_link::rematch_target; use crate::parser::radio_link::rematch_target;
use crate::parser::util::exit_matcher_parser; use crate::parser::util::exit_matcher_parser;
@@ -234,9 +233,9 @@ fn _text_markup_object<'b, 'g, 'r, 's, 'c>(
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _enter = span.enter(); let _enter = span.enter();
if exit_matcher_parser(context, remaining).is_ok() { if exit_matcher_parser(context, remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Parent exit matcher is triggering.", "Parent exit matcher is triggering.",
)))); )));
} }
} }
@@ -290,9 +289,9 @@ fn _text_markup_string<'b, 'g, 'r, 's, 'c>(
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _enter = span.enter(); let _enter = span.enter();
if exit_matcher_parser(context, remaining).is_ok() { if exit_matcher_parser(context, remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Parent exit matcher is triggering.", "Parent exit matcher is triggering.",
)))); )));
} }
} }
@@ -321,9 +320,9 @@ fn pre<'b, 'g, 'r, 's>(
// If None, we are at the start of the file which is technically the beginning of a line. // If None, we are at the start of the file which is technically the beginning of a line.
Some('-') | Some('(') | Some('{') | Some('\'') | Some('"') => {} Some('-') | Some('(') | Some('{') | Some('\'') | Some('"') => {}
Some(_) => { Some(_) => {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Not a valid pre character for text markup.", "Not a valid pre character for text markup.",
)))); )));
} }
None => unreachable!(), // None is for start of file, which should already be handled by the start_of_line matcher above. None => unreachable!(), // None is for start of file, which should already be handled by the start_of_line matcher above.
}; };
@@ -360,9 +359,9 @@ fn _text_markup_end<'b, 'g, 'r, 's, 'c>(
contents_start_offset: usize, contents_start_offset: usize,
) -> Res<OrgSource<'s>, OrgSource<'s>> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
if input.get_byte_offset() == contents_start_offset { if input.get_byte_offset() == contents_start_offset {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Text markup cannot be empty", "Text markup cannot be empty",
)))); )));
} }
not(preceded_by_whitespace(false))(input)?; not(preceded_by_whitespace(false))(input)?;
let (remaining, _marker) = terminated( let (remaining, _marker) = terminated(
@@ -495,9 +494,9 @@ fn _rematch_text_markup_object<'b, 'g, 'r, 's, 'x>(
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
let _enter = span.enter(); let _enter = span.enter();
if exit_matcher_parser(context, remaining).is_ok() { if exit_matcher_parser(context, remaining).is_ok() {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Parent exit matcher is triggering.", "Parent exit matcher is triggering.",
)))); )));
} }
} }

View File

@@ -20,7 +20,6 @@ use crate::context::parser_with_context;
use crate::context::ContextElement; use crate::context::ContextElement;
use crate::context::RefContext; use crate::context::RefContext;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::types::IndentationLevel; use crate::types::IndentationLevel;
@@ -129,9 +128,7 @@ pub(crate) fn start_of_line<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, ()>
if input.is_at_start_of_line() { if input.is_at_start_of_line() {
Ok((input, ())) Ok((input, ()))
} else { } else {
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("Not at start of line")))
"Not at start of line",
))))
} }
} }
@@ -152,9 +149,9 @@ fn _preceded_by_whitespace<'s>(
.map(|c| c.is_whitespace() || c == '\u{200B}') // 200B = Zero-width space .map(|c| c.is_whitespace() || c == '\u{200B}') // 200B = Zero-width space
.unwrap_or(allow_start_of_file) .unwrap_or(allow_start_of_file)
{ {
return Err(nom::Err::Error(CustomError::MyError(MyError( return Err(nom::Err::Error(CustomError::Static(
"Must be preceded by a whitespace character.", "Must be preceded by a whitespace character.",
)))); )));
} }
Ok((input, ())) Ok((input, ()))
} }
@@ -195,9 +192,7 @@ pub(crate) fn text_until_exit<'b, 'g, 'r, 's>(
#[allow(dead_code)] #[allow(dead_code)]
fn not_yet_implemented() -> Res<OrgSource<'static>, ()> { fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
Err(nom::Err::Error(CustomError::MyError(MyError( Err(nom::Err::Error(CustomError::Static("Not implemented yet.")))
"Not implemented yet.",
))))
} }
#[allow(dead_code)] #[allow(dead_code)]
@@ -205,9 +200,7 @@ fn not_yet_implemented() -> Res<OrgSource<'static>, ()> {
/// Text from the current point until the next line break or end of file /// Text from the current point until the next line break or end of file
/// ///
/// Useful for debugging. /// Useful for debugging.
fn text_until_eol<'r, 's>( fn text_until_eol<'r, 's>(input: OrgSource<'s>) -> Result<&'s str, nom::Err<CustomError>> {
input: OrgSource<'s>,
) -> Result<&'s str, nom::Err<CustomError<OrgSource<'s>>>> {
let line = recognize(many_till(anychar, alt((line_ending, eof))))(input) let line = recognize(many_till(anychar, alt((line_ending, eof))))(input)
.map(|(_remaining, line)| Into::<&str>::into(line))?; .map(|(_remaining, line)| Into::<&str>::into(line))?;
Ok(line.trim()) Ok(line.trim())
@@ -250,6 +243,10 @@ pub(crate) fn org_line_ending(input: OrgSource<'_>) -> Res<OrgSource<'_>, OrgSou
} }
/// Match the whitespace at the beginning of a line and give it an indentation level. /// Match the whitespace at the beginning of a line and give it an indentation level.
#[cfg_attr(
feature = "tracing",
tracing::instrument(ret, level = "debug", skip(context))
)]
pub(crate) fn indentation_level<'s>( pub(crate) fn indentation_level<'s>(
context: RefContext<'_, '_, '_, 's>, context: RefContext<'_, '_, '_, 's>,
input: OrgSource<'s>, input: OrgSource<'s>,

View File

@@ -78,7 +78,7 @@ enum RemoveLineBreakState {
/// Removes all whitespace from a string if any line breaks are present. /// Removes all whitespace from a string if any line breaks are present.
/// ///
/// Example: "foo bar" => "foo bar" but "foo \n bar" => "foobar". /// Example: "foo bar" => "foo bar" but "foo \n bar" => "foo bar".
pub(crate) fn coalesce_whitespace_if_line_break(input: &str) -> Cow<'_, str> { pub(crate) fn coalesce_whitespace_if_line_break(input: &str) -> Cow<'_, str> {
let mut state = CoalesceWhitespaceIfLineBreakState::Normal; let mut state = CoalesceWhitespaceIfLineBreakState::Normal;
for (offset, c) in input.char_indices() { for (offset, c) in input.char_indices() {
@@ -515,4 +515,25 @@ mod tests {
assert!(matches!(output, Cow::Borrowed(_))); assert!(matches!(output, Cow::Borrowed(_)));
Ok(()) Ok(())
} }
#[test]
fn test_coalesce_whitespace_if_line_break() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(coalesce_whitespace_if_line_break("foo bar"), "foo bar");
assert_eq!(coalesce_whitespace_if_line_break("foo \n bar"), "foo bar");
Ok(())
}
#[test]
fn test_remove_whitespace_if_line_break() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(remove_whitespace_if_line_break("foo bar"), "foo bar");
assert_eq!(remove_whitespace_if_line_break("foo \n bar"), "foobar");
Ok(())
}
#[test]
fn test_remove_line_break() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(remove_line_break("foo bar"), "foo bar");
assert_eq!(remove_line_break("foo \n bar"), "foo bar");
Ok(())
}
} }