48 Commits

Author SHA1 Message Date
Tom Alexander
678106bb65 Use tekton matrix for specifying all combinations of features for the build CI job.
All checks were successful
rust-test Build rust-test has succeeded
2023-08-25 03:56:08 -04:00
Tom Alexander
19432d91ab Get the emacs and org-mode versions when launching the compare script.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-25 03:11:19 -04:00
Tom Alexander
16a107eebb Update org-mode version. 2023-08-25 02:56:28 -04:00
Tom Alexander
77348b560c Parameterize the emacs and org-mode versions in the dockerfiles.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
2023-08-25 02:03:35 -04:00
Tom Alexander
fc79507ef3 Merge branch 'plain_list_perf_investigation'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-25 01:10:04 -04:00
Tom Alexander
9c1e6ccc97 Add a detect_element function.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
This is an optimization. When you have something like plain text which ends when it hits the next element, we only need to parse enough to detect that an element is about to occur. For elements like plain lists, this is as simple as parsing a line starting with optional whitespace and then a bullet, which avoids parsing the entire plain list tree. The benefit is most noticeable in deeply nested plain lists.
2023-08-25 01:07:53 -04:00
Tom Alexander
0dbc8f0925 Remove redundant exit matcher checks.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-25 00:01:56 -04:00
Tom Alexander
02fe10fba3 Move objects to a lower exit class.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
Paragraph's exit matcher which detects elements was causing the plain list parser to exit after the first item was parsed which was causing significant amounts of re-parsing.
2023-08-24 23:34:23 -04:00
Tom Alexander
33d7ae03d1 Add a TODO.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-24 21:35:34 -04:00
Tom Alexander
03faa7257f Move the indent level for plain list's exit matcher to const fn instead of grabbing from the context.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
This made a slight improvement to performance.
2023-08-24 20:50:24 -04:00
Tom Alexander
ae3510abd5 Do not cast lesser block name to lowercase at runtime.
This reduced the runtime of my problematic test case from 6.9 seconds to 6 seconds.
2023-08-24 20:10:43 -04:00
fluxcdbot
ad3f47864a CI: autofix rust code.
Some checks failed
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has failed
2023-08-24 23:43:41 +00:00
Tom Alexander
533ef2a9a8 Merge branch 'wrapped_input'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-24 19:40:55 -04:00
Tom Alexander
cf37bc4111 Remove unnecessary context from some util functions.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-24 19:29:00 -04:00
Tom Alexander
e5224cda63 Removing dead code.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-24 18:40:25 -04:00
Tom Alexander
64e3481660 Update get_consumed to use the new wrapped input type. 2023-08-24 18:33:40 -04:00
Tom Alexander
32071ce74d Fix handling of start of line in OrgSource.
All checks were successful
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-24 18:08:16 -04:00
Tom Alexander
e84e2b5147 Update tests to compile again. 2023-08-24 17:15:24 -04:00
Tom Alexander
3348807a05 Eliminate the document root context element. 2023-08-24 17:01:12 -04:00
Tom Alexander
720afa5d32 Update getting the previous character and previous line.
This can be done a lot more efficiently now that we are keeping track of this information in the wrapped input type instead of having to fetch to the original document out of the context tree.
2023-08-24 16:56:07 -04:00
Tom Alexander
dab598e5e7 Convert all functions to using the wrapped input type.
Some checks failed
rust-test Build rust-test has failed
rust-build Build rust-build has failed
2023-08-24 16:06:29 -04:00
Tom Alexander
b7a5dd48ea Impl missing traits. 2023-08-22 23:32:27 -04:00
Tom Alexander
c475dce6da Fix lifetime issue. 2023-08-22 23:14:23 -04:00
Tom Alexander
6d1675fa00 Lifetime issue. 2023-08-22 22:57:44 -04:00
Tom Alexander
cda49c628c Move the wrapped input into the parser. 2023-08-22 22:33:50 -04:00
Tom Alexander
65b87bd65d Merge remote-tracking branch 'input/main' into wrapped_input 2023-08-22 22:26:55 -04:00
Tom Alexander
5a7f34b63e Prepare for merging into Organic. 2023-08-22 22:24:35 -04:00
Tom Alexander
edff1e089d Implement text since line break. 2023-08-22 22:18:44 -04:00
Tom Alexander
bc29f1dfc0 Add slicing tests. 2023-08-22 21:38:50 -04:00
Tom Alexander
e4656cddf6 Implement slice, take, and compare. 2023-08-22 21:25:13 -04:00
Tom Alexander
1e3dadd458 Wrap the input. 2023-08-22 17:24:26 -04:00
Tom Alexander
2ec055af5a Very simple setup. 2023-08-22 17:22:13 -04:00
Tom Alexander
6823db5c60 Initial commit. 2023-08-22 17:11:45 -04:00
Tom Alexander
21e1ceb8e0 Merge branch 'add_performance_check_scripts'
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-22 14:21:44 -04:00
Tom Alexander
655af88cdf Add scripts for running perf and callgrind. 2023-08-22 14:21:27 -04:00
Tom Alexander
8561fdc1bd Make the autogen prefix fully integrated into the test name.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-21 00:14:10 -04:00
Tom Alexander
f2089257b0 Re-enable disabled test.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
The latest code in org-mode has been fixed.
2023-08-21 00:08:26 -04:00
Tom Alexander
09821c8898 Prefix the automatically generated tests.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-20 23:53:11 -04:00
Tom Alexander
69ecfd2646 Move all the specific-token tests into subfolders.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-20 23:38:47 -04:00
Tom Alexander
8162f03051 Put all trailing whitespace ownership test cases into the automated tests.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
Notes for this investigation moved to cba1d1e988/notes/plain_list_ownership_notes.org .

Mailing list thread on the investigation: https://list.orgmode.org/9372527e-3852-419e-936a-7b4dd38cc847@app.fastmail.com/ .
2023-08-20 16:03:31 -04:00
Tom Alexander
d8c3285e3c Add --init flag to docker run.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-build Build rust-build has succeeded
rust-test Build rust-test has succeeded
I noticed in a separate project that ctrl+c was not being honored under --init was passed, so I'm adding it in here.
2023-08-19 02:51:00 -04:00
Tom Alexander
5db6cd617e Improve test cases for plain list ownership. 2023-08-19 02:30:31 -04:00
Tom Alexander
4cd3697fb0 Update org-mode version in dockerfile. 2023-08-18 23:20:29 -04:00
Tom Alexander
2cd6f736c2 Fix building without compare feature.
All checks were successful
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
rust-build Build rust-build has succeeded
2023-08-17 00:13:25 -04:00
fluxcdbot
5686256039 CI: autofix rust code.
Some checks failed
rust-build Build rust-build has failed
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
2023-08-17 04:05:48 +00:00
Tom Alexander
7cf1b2d2b8 Disable the failing plain list whitespace ownership test.
Some checks failed
rust-build Build rust-build has failed
rustfmt Build rustfmt has succeeded
rust-test Build rust-test has succeeded
2023-08-17 00:03:05 -04:00
Tom Alexander
b848d7be73 Merge branch 'no_files' 2023-08-16 23:57:58 -04:00
Tom Alexander
74f4aa8d33 Remove dependency on files for running compare.
The tests still use files since they get the test name from a file but compare does the same action via stdin so it can operator on any org source.
2023-08-16 23:56:05 -04:00
134 changed files with 2098 additions and 1242 deletions

View File

@@ -1,3 +1,4 @@
**/.git **/.git
target target
Cargo.lock Cargo.lock
notes/

View File

@@ -14,10 +14,6 @@ spec:
- name: path-to-dockerfile - name: path-to-dockerfile
description: The path to the Dockerfile description: The path to the Dockerfile
type: string type: string
- name: command
type: array
description: Command to run.
default: []
tasks: tasks:
- name: report-pending - name: report-pending
taskRef: taskRef:
@@ -81,9 +77,19 @@ spec:
workspace: docker-credentials workspace: docker-credentials
runAfter: runAfter:
- fetch-repository - fetch-repository
- name: run-image-none - name: build-organic
taskRef: taskRef:
name: run-docker-image name: run-docker-image
matrix:
params:
- name: feature-compare
value:
- "true"
- "false"
- name: feature-tracing
value:
- "true"
- "false"
workspaces: workspaces:
- name: source - name: source
workspace: git-source workspace: git-source
@@ -93,77 +99,21 @@ spec:
- build-image - build-image
params: params:
- name: command - name: command
value: ["$(params.command[*])"] value: ["/bin/sh", "-c"]
- name: args - name: args
value: ["--no-default-features"] value:
- name: docker-image - |
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)" set -euo pipefail
- name: run-image-tracing IFS=$$'\n\t'
taskRef: features=()
name: run-docker-image if [ $(params.feature-compare) = "true" ]; then features+=(compare); fi
workspaces: if [ $(params.feature-tracing) = "true" ]; then features+=(tracing); fi
- name: source if [ $${#features[@]} -eq 0 ]; then
workspace: git-source exec cargo build --no-default-features
- name: cargo-cache else
workspace: cargo-cache featurelist=$$(IFS="," ; echo "$${features[*]}")
runAfter: exec cargo build --no-default-features --features "$$featurelist"
- run-image-none fi
params:
- name: command
value: ["$(params.command[*])"]
- name: args
value: ["--no-default-features", "--features", "tracing"]
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-compare
taskRef:
name: run-docker-image
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- run-image-tracing
params:
- name: command
value: ["$(params.command[*])"]
- name: args
value: ["--no-default-features", "--features", "compare"]
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-default
taskRef:
name: run-docker-image
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- run-image-compare
params:
- name: command
value: ["$(params.command[*])"]
- name: args
value: []
- name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
- name: run-image-all
taskRef:
name: run-docker-image
workspaces:
- name: source
workspace: git-source
- name: cargo-cache
workspace: cargo-cache
runAfter:
- run-image-default
params:
- name: command
value: ["$(params.command[*])"]
- name: args
value: ["--no-default-features", "--features", "tracing,compare"]
- name: docker-image - name: docker-image
value: "$(params.image-name):$(tasks.fetch-repository.results.commit)" value: "$(params.image-name):$(tasks.fetch-repository.results.commit)"
finally: finally:
@@ -256,5 +206,3 @@ spec:
value: docker/organic_build/ value: docker/organic_build/
- name: path-to-dockerfile - name: path-to-dockerfile
value: docker/organic_build/Dockerfile value: docker/organic_build/Dockerfile
- name: command
value: [cargo, build]

View File

@@ -40,10 +40,16 @@ tracing-subscriber = { version = "0.3.17", optional = true, features = ["env-fil
walkdir = "2.3.3" walkdir = "2.3.3"
[features] [features]
default = ["compare", "tracing"] default = ["compare"]
compare = [] compare = []
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"]
[profile.release] [profile.release-lto]
inherits = "release"
lto = true lto = true
strip = "symbols" strip = "symbols"
[profile.perf]
inherits = "release"
lto = true
debug = true

View File

@@ -40,7 +40,7 @@ test:
.PHONY: dockertest .PHONY: dockertest
dockertest: dockertest:
> $(MAKE) -C docker/organic_test > $(MAKE) -C docker/organic_test
> docker run --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test cargo test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS) > docker run --init --rm -i -t -v "$$(readlink -f ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test cargo test --no-fail-fast --lib --test test_loader -- --test-threads $(TESTJOBS)
.PHONY: dockerclean .PHONY: dockerclean
dockerclean: dockerclean:

View File

@@ -41,6 +41,7 @@ fn write_test(test_file: &mut File, test: &walkdir::DirEntry) {
.strip_suffix(".org") .strip_suffix(".org")
.expect("Should have .org extension") .expect("Should have .org extension")
.replace("/", "_"); .replace("/", "_");
let test_name = format!("autogen_{}", test_name);
if let Some(_reason) = is_expect_fail(test_name.as_str()) { if let Some(_reason) = is_expect_fail(test_name.as_str()) {
write!(test_file, "#[ignore]\n").unwrap(); write!(test_file, "#[ignore]\n").unwrap();
@@ -71,10 +72,9 @@ use organic::parser::sexp::sexp_with_padding;
fn is_expect_fail(name: &str) -> Option<&str> { fn is_expect_fail(name: &str) -> Option<&str> {
match name { match name {
"drawer_drawer_with_headline_inside" => Some("Apparently lines with :end: become their own paragraph. This odd behavior needs to be investigated more."), "autogen_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."), "autogen_element_container_priority_footnote_definition_dynamic_block" => Some("Apparently broken begin lines become their own paragraph."),
"paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."), "autogen_lesser_element_paragraphs_paragraph_with_backslash_line_breaks" => Some("The text we're getting out of the parse tree is already processed to remove line breaks, so our comparison needs to take that into account."),
"export_snippet_paragraph_break_precedence" => Some("The latest code for org-mode is matching the export snippet without the closing @@."), // https://list.orgmode.org/orgmode/fb61ea28-f004-4c25-adf7-69fc55683ed4@app.fastmail.com/T/#u
_ => None, _ => None,
} }
} }

View File

@@ -6,7 +6,7 @@ all: build push
.PHONY: build .PHONY: build
build: build:
docker build -t $(IMAGE_NAME) -f Dockerfile ../ docker build -t $(IMAGE_NAME) -f Dockerfile ../../
.PHONY: push .PHONY: push
push: push:

View File

@@ -6,7 +6,7 @@ all: build push
.PHONY: build .PHONY: build
build: build:
docker build -t $(IMAGE_NAME) -f Dockerfile ../ docker build -t $(IMAGE_NAME) -f Dockerfile ../../
.PHONY: push .PHONY: push
push: push:

View File

@@ -1,11 +1,10 @@
FROM alpine:3.17 AS build FROM alpine:3.17 AS build
RUN apk add --no-cache build-base musl-dev git autoconf make texinfo gnutls-dev ncurses-dev gawk RUN apk add --no-cache build-base musl-dev git autoconf make texinfo gnutls-dev ncurses-dev gawk
FROM build AS build-emacs FROM build AS build-emacs
ARG EMACS_VERSION=emacs-29.1
RUN git clone --depth 1 --branch emacs-29.1 https://git.savannah.gnu.org/git/emacs.git /root/emacs RUN git clone --depth 1 --branch $EMACS_VERSION https://git.savannah.gnu.org/git/emacs.git /root/emacs
WORKDIR /root/emacs WORKDIR /root/emacs
RUN mkdir /root/dist RUN mkdir /root/dist
RUN ./autogen.sh RUN ./autogen.sh
@@ -15,9 +14,12 @@ RUN make DESTDIR="/root/dist" install
FROM build AS build-org-mode FROM build AS build-org-mode
ARG ORG_VERSION=7bdec435ff5d86220d13c431e799c5ed44a57da1
COPY --from=build-emacs /root/dist/ / COPY --from=build-emacs /root/dist/ /
RUN mkdir /root/dist RUN mkdir /root/dist
RUN mkdir /root/org-mode && git -C /root/org-mode init && git -C /root/org-mode remote add origin https://git.savannah.gnu.org/git/emacs/org-mode.git && git -C /root/org-mode fetch origin b89bc55867d7cb809c379d371d12d409db785154 && git -C /root/org-mode checkout FETCH_HEAD # Savannah does not allow fetching specific revisions, so we're going to have to put unnecessary load on their server by cloning main and then checking out the revision we want.
RUN git clone https://git.savannah.gnu.org/git/emacs/org-mode.git /root/org-mode && git -C /root/org-mode checkout $ORG_VERSION
# RUN mkdir /root/org-mode && git -C /root/org-mode init --initial-branch=main && git -C /root/org-mode remote add origin https://git.savannah.gnu.org/git/emacs/org-mode.git && git -C /root/org-mode fetch origin $ORG_VERSION && git -C /root/org-mode checkout FETCH_HEAD
WORKDIR /root/org-mode WORKDIR /root/org-mode
RUN make compile RUN make compile
RUN make DESTDIR="/root/dist" install RUN make DESTDIR="/root/dist" install

View File

@@ -6,7 +6,7 @@ all: build push
.PHONY: build .PHONY: build
build: build:
docker build -t $(IMAGE_NAME) -f Dockerfile ../ docker build -t $(IMAGE_NAME) -f Dockerfile ../../
.PHONY: push .PHONY: push
push: push:

0
foo Normal file
View File

View File

@@ -1,128 +0,0 @@
* Test 1
** Source
#+begin_src org
1. foo
1. bar
2. baz
2. lorem
ipsum
#+end_src
** Ownership
This table is just showing ownership for the plain list items, not the containing plain list nor the elements inside each item.
| Plain List *Item* | Owns trailing blank lines |
|------------------------+---------------------------|
| foo (includes bar baz) | Yes |
| bar | Yes |
| baz | Yes |
| lorem | No |
** Analysis
This seems to imply that plain list items own their trailing blank lines except for the final plain list item in the top-most plain list which does not own its trailing blank lines.
* Test 2
** Source
#+begin_src org
1. foo
bar
1. baz
lorem
ipsum
dolar
#+end_src
** Ownership
This table is just showing ownership for the plain list items, not the containing plain list nor the elements inside each item.
| Plain List *Item* | Owns trailing blank lines |
|--------------------------+---------------------------|
| foo -> ipsum (inclusive) | No |
| baz lorem | No |
** Analysis
This shows that the final plain list item in a nested plain list (baz lorem) does not own its trailing blank lines which conflicts with "baz" from Test 1.
* Test 3
** Source
#+begin_src org
1. foo
1. bar
baz
2. lorem
ipsum
#+end_src
** Ownership
| Plain List *Item* | Owns trailing blank lines |
|------------------------+---------------------------|
| foo (includes bar baz) | Yes |
| bar baz | Yes |
| lorem | No |
** Analysis
This was to test if having an extra paragraph in the final list item in the nested list changes the behavior. The behavior is consistent with Test 1, so the extra paragraph is not the cause of the discrepancy.
* Test 4
** Source
#+begin_src org
1. foo
1. bar
2. baz
candy
2. lorem
ipsum
#+end_src
** Ownership
| Plain List *Item* | Owns trailing blank lines |
|----------------------------------+---------------------------|
| foo (includes bar baz and candy) | Yes |
| bar | Yes |
| baz | No |
| lorem | No |
** Analysis
This was to test if putting a non-plain-list element at the end of foo changes the ownership of blank lines. baz changed to no longer owning its trailing whitespace.
This seems to imply that list items own their trailing whitespace except for the final item unless that list item is at the end of a list item.
* Test 5
** Source
#+begin_src org
1. foo
1. bar
2. baz
candy
2. lorem
1. cat
2. dog
ipsum
#+end_src
** Ownership
| Plain List *Item* | Owns trailing blank lines |
|----------------------------------+---------------------------|
| foo (includes bar baz and candy) | Yes |
| bar | Yes |
| baz | No |
| lorem (includes cat and dog) | No |
| cat | Yes |
| dog | No |
** Analysis
This breaks the theory that the final list item nested at the end of a list item gets to own its trailing blank lines since dog does not own its blank lines despite Test 1's baz owning its blank lines.
New Theory: final list items only own their blank lines if they are nested at the end of a non-final list item.

View File

@@ -0,0 +1,11 @@
1. foo
1. bar
2. baz
cat
2. lorem
ipsum

View File

@@ -0,0 +1,10 @@
1. cat
1. foo
1. bar
2. baz
2. lorem
ipsum

View File

@@ -0,0 +1,12 @@
1. cat
1. foo
1. bar
2. baz
2. lorem
2. dog
ipsum

13
scripts/callgrind.bash Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR/../"
RUSTFLAGS="-C opt-level=0" cargo build --no-default-features
valgrind --tool=callgrind --callgrind-out-file=callgrind.out target/debug/compare
echo "You probably want to run:"
echo "callgrind_annotate --auto=yes callgrind.out"

18
scripts/perf.bash Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
: ${PROFILE:="perf"}
cd "$DIR/../"
cargo build --profile "$PROFILE" --no-default-features
perf record --freq=2000 --call-graph dwarf --output=perf.data target/${PROFILE}/compare
# Convert to a format firefox will read
# flags to consider --show-info
perf script -F +pid --input perf.data > perf.firefox
echo "You probably want to go to https://profiler.firefox.com/"
echo "Either that or run hotspot"

View File

@@ -13,9 +13,8 @@ REALPATH=$(command -v uu-realpath || command -v realpath)
MAKE=$(command -v gmake || command -v make) MAKE=$(command -v gmake || command -v make)
function main { function main {
local org_file="$($REALPATH "$1")"
build_container build_container
launch_container "$org_file" launch_container
} }
function build_container { function build_container {
@@ -23,22 +22,13 @@ function build_container {
} }
function launch_container { function launch_container {
local org_file="$1"
local additional_flags=() local additional_flags=()
local additional_args=() local additional_args=()
local init_script=$(cat <<EOF
set -euo pipefail
IFS=\$'\n\t'
cargo run -- /input.org
EOF
)
if [ "$SHELL" != "YES" ]; then if [ "$SHELL" != "YES" ]; then
additional_args+=(sh -c "$init_script") additional_args+=(cargo run)
else else
additional_flags+=(-i -t) additional_flags+=(-t)
fi fi
if [ "$TRACE" = "YES" ]; then if [ "$TRACE" = "YES" ]; then
@@ -50,7 +40,7 @@ EOF
additional_flags+=(--env RUST_BACKTRACE=full) additional_flags+=(--env RUST_BACKTRACE=full)
fi fi
docker run "${additional_flags[@]}" --rm -v "${org_file}:/input.org:ro" -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test "${additional_args[@]}" docker run "${additional_flags[@]}" --init --rm -i -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test "${additional_args[@]}"
} }
main "${@}" main "${@}"

View File

@@ -50,7 +50,7 @@ cargo test --no-fail-fast --lib --test test_loader "$test" -- --show-output
EOF EOF
) )
docker run --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test sh -c "$init_script" docker run --init --rm -v "$($REALPATH ./):/source:ro" --mount source=cargo-cache,target=/usr/local/cargo/registry --mount source=rust-cache,target=/target --env CARGO_TARGET_DIR=/target -w /source organic-test sh -c "$init_script"
} }

View File

@@ -3,3 +3,5 @@ mod parse;
mod util; mod util;
pub use diff::compare_document; pub use diff::compare_document;
pub use parse::emacs_parse_org_document; pub use parse::emacs_parse_org_document;
pub use parse::get_emacs_version;
pub use parse::get_org_mode_version;

View File

@@ -1,22 +1,25 @@
use std::path::Path;
use std::process::Command; use std::process::Command;
pub fn emacs_parse_org_document<'a, C>(file_path: C) -> Result<String, Box<dyn std::error::Error>> pub fn emacs_parse_org_document<C>(file_contents: C) -> Result<String, Box<dyn std::error::Error>>
where where
C: AsRef<Path>, C: AsRef<str>,
{ {
let elisp_script = r#"(progn let escaped_file_contents = escape_elisp_string(file_contents);
let elisp_script = format!(
r#"(progn
(erase-buffer)
(insert "{escaped_file_contents}")
(org-mode) (org-mode)
(message "%s" (pp-to-string (org-element-parse-buffer))) (message "%s" (pp-to-string (org-element-parse-buffer)))
)"#; )"#,
escaped_file_contents = escaped_file_contents
);
let mut cmd = Command::new("emacs"); let mut cmd = Command::new("emacs");
let proc = cmd let proc = cmd
.arg("-q") .arg("-q")
.arg("--no-site-file") .arg("--no-site-file")
.arg("--no-splash") .arg("--no-splash")
.arg("--batch") .arg("--batch")
.arg("--insert")
.arg(file_path.as_ref().as_os_str())
.arg("--eval") .arg("--eval")
.arg(elisp_script); .arg(elisp_script);
let out = proc.output()?; let out = proc.output()?;
@@ -24,3 +27,62 @@ where
let org_sexp = out.stderr; let org_sexp = out.stderr;
Ok(String::from_utf8(org_sexp)?) Ok(String::from_utf8(org_sexp)?)
} }
fn escape_elisp_string<C>(file_contents: C) -> String
where
C: AsRef<str>,
{
let source = file_contents.as_ref();
let source_len = source.len();
// We allocate a string 10% larger than the source to account for escape characters. Without this, we would have more allocations during processing.
let mut output = String::with_capacity(source_len + (source_len / 10));
for c in source.chars() {
match c {
'"' | '\\' => {
output.push('\\');
output.push(c);
}
_ => {
output.push(c);
}
}
}
output
}
pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
let elisp_script = r#"(progn
(message "%s" (version))
)"#;
let mut cmd = Command::new("emacs");
let proc = cmd
.arg("-q")
.arg("--no-site-file")
.arg("--no-splash")
.arg("--batch")
.arg("--eval")
.arg(elisp_script);
let out = proc.output()?;
out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?)
}
pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
let elisp_script = r#"(progn
(org-mode)
(message "%s" (org-version nil t nil))
)"#;
let mut cmd = Command::new("emacs");
let proc = cmd
.arg("-q")
.arg("--no-site-file")
.arg("--no-splash")
.arg("--batch")
.arg("--eval")
.arg(elisp_script);
let out = proc.output()?;
out.status.exit_ok()?;
Ok(String::from_utf8(out.stderr)?)
}

View File

@@ -42,6 +42,39 @@ pub fn assert_bounds<'s, S: Source<'s>>(
emacs: &'s Token<'s>, emacs: &'s Token<'s>,
rust: &'s S, rust: &'s S,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let standard_properties = get_standard_properties(emacs)?;
let (begin, end) = (
standard_properties
.begin
.ok_or("Token should have a begin.")?,
standard_properties
.end
.ok_or("Token should have a begin.")?,
);
let (rust_begin, rust_end) = get_offsets(source, rust);
if (rust_begin + 1) != begin || (rust_end + 1) != end {
Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
}
Ok(())
}
struct StandardProperties {
begin: Option<usize>,
#[allow(dead_code)]
post_affiliated: Option<usize>,
#[allow(dead_code)]
contents_begin: Option<usize>,
#[allow(dead_code)]
contents_end: Option<usize>,
end: Option<usize>,
#[allow(dead_code)]
post_blank: Option<usize>,
}
fn get_standard_properties<'s>(
emacs: &'s Token<'s>,
) -> Result<StandardProperties, Box<dyn std::error::Error>> {
let children = emacs.as_list()?; let children = emacs.as_list()?;
let attributes_child = children let attributes_child = children
.iter() .iter()
@@ -49,34 +82,60 @@ pub fn assert_bounds<'s, S: Source<'s>>(
.ok_or("Should have an attributes child.")?; .ok_or("Should have an attributes child.")?;
let attributes_map = attributes_child.as_map()?; let attributes_map = attributes_child.as_map()?;
let standard_properties = attributes_map.get(":standard-properties"); let standard_properties = attributes_map.get(":standard-properties");
let (begin, end) = if standard_properties.is_some() { Ok(if standard_properties.is_some() {
let std_props = standard_properties let mut std_props = standard_properties
.expect("if statement proves its Some") .expect("if statement proves its Some")
.as_vector()?; .as_vector()?
let begin = std_props .into_iter();
.get(0) let begin = maybe_token_to_usize(std_props.next())?;
.ok_or("Missing first element in standard properties")? let post_affiliated = maybe_token_to_usize(std_props.next())?;
.as_atom()?; let contents_begin = maybe_token_to_usize(std_props.next())?;
let end = std_props let contents_end = maybe_token_to_usize(std_props.next())?;
.get(1) let end = maybe_token_to_usize(std_props.next())?;
.ok_or("Missing first element in standard properties")? let post_blank = maybe_token_to_usize(std_props.next())?;
.as_atom()?; StandardProperties {
(begin, end) begin,
} else { post_affiliated,
let begin = attributes_map contents_begin,
.get(":begin") contents_end,
.ok_or("Missing :begin attribute.")? end,
.as_atom()?; post_blank,
let end = attributes_map
.get(":end")
.ok_or("Missing :end attribute.")?
.as_atom()?;
(begin, end)
};
let (rust_begin, rust_end) = get_offsets(source, rust);
if (rust_begin + 1).to_string() != begin || (rust_end + 1).to_string() != end {
Err(format!("Rust bounds ({rust_begin}, {rust_end}) do not match emacs bounds ({emacs_begin}, {emacs_end})", rust_begin = rust_begin + 1, rust_end = rust_end + 1, emacs_begin=begin, emacs_end=end))?;
} }
} else {
Ok(()) let begin = maybe_token_to_usize(attributes_map.get(":begin").map(|token| *token))?;
let end = maybe_token_to_usize(attributes_map.get(":end").map(|token| *token))?;
let contents_begin =
maybe_token_to_usize(attributes_map.get(":contents-begin").map(|token| *token))?;
let contents_end =
maybe_token_to_usize(attributes_map.get(":contents-end").map(|token| *token))?;
let post_blank =
maybe_token_to_usize(attributes_map.get(":post-blank").map(|token| *token))?;
let post_affiliated =
maybe_token_to_usize(attributes_map.get(":post-affiliated").map(|token| *token))?;
StandardProperties {
begin,
post_affiliated,
contents_begin,
contents_end,
end,
post_blank,
}
})
}
fn maybe_token_to_usize(
token: Option<&Token<'_>>,
) -> Result<Option<usize>, Box<dyn std::error::Error>> {
Ok(token
.map(|token| token.as_atom())
.map_or(Ok(None), |r| r.map(Some))?
.map(|val| {
if val == "nil" {
None
} else {
Some(val.parse::<usize>())
}
})
.flatten() // Outer option is whether or not the param exists, inner option is whether or not it is nil
.map_or(Ok(None), |r| r.map(Some))?)
} }

View File

@@ -4,6 +4,7 @@ use nom::IResult;
pub type Res<T, U> = IResult<T, U, CustomError<T>>; pub type Res<T, U> = IResult<T, U, CustomError<T>>;
// TODO: MyError probably shouldn't be based on the same type as the input type since it's used exclusively with static strings right now.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum CustomError<I> { pub enum CustomError<I> {
MyError(MyError<I>), MyError(MyError<I>),

View File

@@ -7,6 +7,10 @@ mod compare;
pub use compare::compare_document; pub use compare::compare_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
pub use compare::emacs_parse_org_document; pub use compare::emacs_parse_org_document;
#[cfg(feature = "compare")]
pub use compare::get_emacs_version;
#[cfg(feature = "compare")]
pub use compare::get_org_mode_version;
mod error; mod error;
pub mod parser; pub mod parser;

View File

@@ -1,13 +1,16 @@
#![feature(round_char_boundary)] #![feature(round_char_boundary)]
use std::path::Path; use std::io::Read;
#[cfg(feature = "compare")]
use ::organic::parser::document; use ::organic::parser::document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
use organic::compare_document; use organic::compare_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
use organic::emacs_parse_org_document; use organic::emacs_parse_org_document;
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
use organic::get_emacs_version;
#[cfg(feature = "compare")]
use organic::get_org_mode_version;
#[cfg(feature = "compare")]
use organic::parser::sexp::sexp_with_padding; use organic::parser::sexp::sexp_with_padding;
#[cfg(feature = "tracing")] #[cfg(feature = "tracing")]
@@ -36,22 +39,30 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn main_body() -> Result<(), Box<dyn std::error::Error>> { fn main_body() -> Result<(), Box<dyn std::error::Error>> {
run_compare( let org_contents = read_stdin_to_string()?;
std::env::args() run_compare(org_contents)
.nth(1) }
.ok_or("Pass a single file into this script.")?,
) fn read_stdin_to_string() -> Result<String, Box<dyn std::error::Error>> {
let mut stdin_contents = String::new();
std::io::stdin()
.lock()
.read_to_string(&mut stdin_contents)?;
Ok(stdin_contents)
} }
#[cfg(feature = "compare")] #[cfg(feature = "compare")]
fn run_compare<P: AsRef<Path>>(todo_org_path: P) -> Result<(), Box<dyn std::error::Error>> { fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
let org_contents = std::fs::read_to_string(todo_org_path.as_ref())?; let emacs_version = get_emacs_version()?;
let (remaining, rust_parsed) = document(org_contents.as_str()).expect("Org Parse failure"); let org_mode_version = get_org_mode_version()?;
let org_sexp = emacs_parse_org_document(todo_org_path.as_ref())?; eprintln!("Using emacs version: {}", emacs_version.trim());
eprintln!("Using org-mode version: {}", org_mode_version.trim());
let (remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
let org_sexp = emacs_parse_org_document(org_contents.as_ref())?;
let (_remaining, parsed_sexp) = let (_remaining, parsed_sexp) =
sexp_with_padding(org_sexp.as_str()).expect("Sexp Parse failure"); sexp_with_padding(org_sexp.as_str()).map_err(|e| e.to_string())?;
println!("{}\n\n\n", org_contents.as_str()); println!("{}\n\n\n", org_contents.as_ref());
println!("{}", org_sexp); println!("{}", org_sexp);
println!("{:#?}", rust_parsed); println!("{:#?}", rust_parsed);
@@ -70,7 +81,11 @@ fn run_compare<P: AsRef<Path>>(todo_org_path: P) -> Result<(), Box<dyn std::erro
} }
#[cfg(not(feature = "compare"))] #[cfg(not(feature = "compare"))]
fn run_compare<P: AsRef<Path>>(_todo_org_path: P) -> Result<(), Box<dyn std::error::Error>> { fn run_compare<P: AsRef<str>>(org_contents: P) -> Result<(), Box<dyn std::error::Error>> {
println!("This program was built with compare disabled. Doing nothing."); eprintln!(
"This program was built with compare disabled. Only parsing with organic, not comparing."
);
let (remaining, rust_parsed) = document(org_contents.as_ref()).map_err(|e| e.to_string())?;
println!("{:#?}", rust_parsed);
Ok(()) Ok(())
} }

View File

@@ -4,6 +4,7 @@ use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@@ -16,7 +17,10 @@ use crate::parser::util::get_consumed;
use crate::parser::AngleLink; use crate::parser::AngleLink;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn angle_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, AngleLink<'s>> { pub fn angle_link<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, AngleLink<'s>> {
let (remaining, _) = tag("<")(input)?; let (remaining, _) = tag("<")(input)?;
let (remaining, proto) = protocol(context, remaining)?; let (remaining, proto) = protocol(context, remaining)?;
let (remaining, _separator) = tag(":")(remaining)?; let (remaining, _separator) = tag(":")(remaining)?;
@@ -26,18 +30,21 @@ pub fn angle_link<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
Ok(( Ok((
remaining, remaining,
AngleLink { AngleLink {
source, source: source.into(),
link_type: proto, link_type: proto.into(),
path, path: path.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_angle<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn path_angle<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Gamma,
exit_matcher: &path_angle_end, exit_matcher: &path_angle_end,
})); }));
@@ -48,6 +55,9 @@ fn path_angle<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn path_angle_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn path_angle_end<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag(">")(input) tag(">")(input)
} }

View File

@@ -11,6 +11,7 @@ use nom::multi::many_till;
use nom::multi::separated_list1; use nom::multi::separated_list1;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
@@ -29,7 +30,10 @@ use crate::parser::util::get_consumed;
use crate::parser::Object; use crate::parser::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn citation<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Citation<'s>> { pub fn citation<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Citation<'s>> {
// TODO: Despite being a standard object, citations cannot exist inside the global prefix/suffix for other citations because citations must contain something that matches @key which is forbidden inside the global prefix/suffix. This TODO is to evaluate if its worth putting in an explicit check for this (which can be easily accomplished by checking the output of `get_bracket_depth()`). I suspect its not worth it because I expect, outside of intentionally crafted inputs, this parser will exit immediately inside a citation since it is unlikely to find the "[cite" substring inside a citation global prefix/suffix. // TODO: Despite being a standard object, citations cannot exist inside the global prefix/suffix for other citations because citations must contain something that matches @key which is forbidden inside the global prefix/suffix. This TODO is to evaluate if its worth putting in an explicit check for this (which can be easily accomplished by checking the output of `get_bracket_depth()`). I suspect its not worth it because I expect, outside of intentionally crafted inputs, this parser will exit immediately inside a citation since it is unlikely to find the "[cite" substring inside a citation global prefix/suffix.
let (remaining, _) = tag_no_case("[cite")(input)?; let (remaining, _) = tag_no_case("[cite")(input)?;
let (remaining, _) = opt(citestyle)(remaining)?; let (remaining, _) = opt(citestyle)(remaining)?;
@@ -44,11 +48,16 @@ pub fn citation<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str
let (remaining, _) = tag("]")(remaining)?; let (remaining, _) = tag("]")(remaining)?;
let (remaining, _) = space0(remaining)?; let (remaining, _) = space0(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Citation { source })) Ok((
remaining,
Citation {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn citestyle<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> { fn citestyle<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, _) = tuple((tag("/"), style))(input)?; let (remaining, _) = tuple((tag("/"), style))(input)?;
let (remaining, _) = opt(tuple((tag("/"), variant)))(remaining)?; let (remaining, _) = opt(tuple((tag("/"), variant)))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
@@ -56,14 +65,14 @@ fn citestyle<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> {
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn style<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> { fn style<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(many1(verify(anychar, |c| { recognize(many1(verify(anychar, |c| {
c.is_alphanumeric() || "_-".contains(*c) c.is_alphanumeric() || "_-".contains(*c)
})))(input) })))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn variant<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> { fn variant<'r, 's>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(many1(verify(anychar, |c| { recognize(many1(verify(anychar, |c| {
c.is_alphanumeric() || "_-/".contains(*c) c.is_alphanumeric() || "_-/".contains(*c)
})))(input) })))(input)
@@ -72,8 +81,8 @@ fn variant<'r, 's>(input: &'s str) -> Res<&'s str, &'s str> {
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_prefix<'r, 's>( fn global_prefix<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@@ -81,7 +90,7 @@ fn global_prefix<'r, 's>(
depth: 0, depth: 0,
})) }))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Gamma,
exit_matcher: &global_prefix_end, exit_matcher: &global_prefix_end,
})); }));
let (remaining, (children, _exit_contents)) = verify( let (remaining, (children, _exit_contents)) = verify(
@@ -96,12 +105,15 @@ fn global_prefix<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn global_prefix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation."); .expect("This function should only be called from inside a citation.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@@ -116,7 +128,7 @@ fn global_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -130,8 +142,8 @@ fn global_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_suffix<'r, 's>( fn global_suffix<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, Vec<Object<'s>>> { ) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@@ -139,7 +151,7 @@ fn global_suffix<'r, 's>(
depth: 0, depth: 0,
})) }))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Gamma,
exit_matcher: &global_suffix_end, exit_matcher: &global_suffix_end,
})); }));
let (remaining, (children, _exit_contents)) = verify( let (remaining, (children, _exit_contents)) = verify(
@@ -153,12 +165,15 @@ fn global_suffix<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn global_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn global_suffix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation."); .expect("This function should only be called from inside a citation.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@@ -173,7 +188,7 @@ fn global_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -188,24 +203,21 @@ fn global_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::element_parser::element; use crate::parser::element_parser::element;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::source::Source; use crate::parser::source::Source;
#[test] #[test]
fn citation_simple() { fn citation_simple() {
let input = "[cite:@foo]"; let input = OrgSource::new("[cite:@foo]");
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let paragraph_matcher = parser_with_context!(element(true))(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let paragraph_matcher = parser_with_context!(element(true))(&document_context);
let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph"); let (remaining, first_paragraph) = paragraph_matcher(input).expect("Parse first paragraph");
let first_paragraph = match first_paragraph { let first_paragraph = match first_paragraph {
crate::parser::Element::Paragraph(paragraph) => paragraph, crate::parser::Element::Paragraph(paragraph) => paragraph,
_ => panic!("Should be a paragraph!"), _ => panic!("Should be a paragraph!"),
}; };
assert_eq!(remaining, ""); assert_eq!(Into::<&str>::into(remaining), "");
assert_eq!(first_paragraph.get_source(), "[cite:@foo]"); assert_eq!(first_paragraph.get_source(), "[cite:@foo]");
assert_eq!(first_paragraph.children.len(), 1); assert_eq!(first_paragraph.children.len(), 1);
assert_eq!( assert_eq!(

View File

@@ -10,6 +10,7 @@ use nom::multi::many_till;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::Res; use crate::error::Res;
@@ -28,21 +29,26 @@ use crate::parser::Object;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn citation_reference<'r, 's>( pub fn citation_reference<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, CitationReference<'s>> { ) -> Res<OrgSource<'s>, CitationReference<'s>> {
let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?; let (remaining, _prefix) = opt(parser_with_context!(key_prefix)(context))(input)?;
let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?; let (remaining, _key) = parser_with_context!(citation_reference_key)(context)(remaining)?;
let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?; let (remaining, _suffix) = opt(parser_with_context!(key_suffix)(context))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, CitationReference { source })) Ok((
remaining,
CitationReference {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn citation_reference_key<'r, 's>( pub fn citation_reference_key<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, source) = recognize(tuple(( let (remaining, source) = recognize(tuple((
tag("@"), tag("@"),
many1(verify( many1(verify(
@@ -59,7 +65,10 @@ pub fn citation_reference_key<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_prefix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec<Object<'s>>> { fn key_prefix<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@@ -67,7 +76,7 @@ fn key_prefix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
depth: 0, depth: 0,
})) }))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Gamma,
exit_matcher: &key_prefix_end, exit_matcher: &key_prefix_end,
})); }));
let (remaining, (children, _exit_contents)) = verify( let (remaining, (children, _exit_contents)) = verify(
@@ -81,7 +90,10 @@ fn key_prefix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_suffix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Vec<Object<'s>>> { fn key_suffix<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Vec<Object<'s>>> {
// TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient. // TODO: I could insert CitationBracket entries in the context after each matched object to reduce the scanning done for counting brackets which should be more efficient.
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::CitationBracket(CitationBracket { .with_additional_node(ContextElement::CitationBracket(CitationBracket {
@@ -89,7 +101,7 @@ fn key_suffix<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
depth: 0, depth: 0,
})) }))
.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { .with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Gamma,
exit_matcher: &key_suffix_end, exit_matcher: &key_suffix_end,
})); }));
let (remaining, (children, _exit_contents)) = verify( let (remaining, (children, _exit_contents)) = verify(
@@ -114,12 +126,15 @@ pub fn get_bracket_depth<'r, 's>(context: Context<'r, 's>) -> Option<&'r Citatio
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn key_prefix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation reference."); .expect("This function should only be called from inside a citation reference.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@@ -134,7 +149,7 @@ fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }
@@ -146,12 +161,15 @@ fn key_prefix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn key_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn key_suffix_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let context_depth = get_bracket_depth(context) let context_depth = get_bracket_depth(context)
.expect("This function should only be called from inside a citation reference."); .expect("This function should only be called from inside a citation reference.");
let text_since_context_entry = get_consumed(context_depth.position, input); let text_since_context_entry = get_consumed(context_depth.position, input);
let mut current_depth = context_depth.depth; let mut current_depth = context_depth.depth;
for c in text_since_context_entry.chars() { for c in Into::<&str>::into(text_since_context_entry).chars() {
match c { match c {
'[' => { '[' => {
current_depth += 1; current_depth += 1;
@@ -166,7 +184,7 @@ fn key_suffix_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
} }
} }
if current_depth == 0 { if current_depth == 0 {
let close_bracket = tag::<&str, &str, CustomError<&str>>("]")(input); let close_bracket = tag::<&str, OrgSource<'_>, CustomError<OrgSource<'_>>>("]")(input);
if close_bracket.is_ok() { if close_bracket.is_ok() {
return close_bracket; return close_bracket;
} }

View File

@@ -11,6 +11,7 @@ use nom::combinator::recognize;
use nom::combinator::verify; use nom::combinator::verify;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
@@ -19,8 +20,11 @@ use crate::parser::util::start_of_line;
use crate::parser::Clock; use crate::parser::Clock;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn clock<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Clock<'s>> { pub fn clock<'r, 's>(
start_of_line(context, input)?; context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Clock<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _clock) = tag_no_case("clock:")(remaining)?; let (remaining, _clock) = tag_no_case("clock:")(remaining)?;
let (remaining, _gap_whitespace) = space1(remaining)?; let (remaining, _gap_whitespace) = space1(remaining)?;
@@ -31,14 +35,19 @@ pub fn clock<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, C
))(remaining)?; ))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Clock { source })) Ok((
remaining,
Clock {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_timestamp_range_duration<'r, 's>( fn inactive_timestamp_range_duration<'r, 's>(
context: Context<'r, 's>, _context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, &'s str> { ) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple(( recognize(tuple((
tag("["), tag("["),
is_not("\r\n]"), is_not("\r\n]"),
@@ -50,14 +59,17 @@ fn inactive_timestamp_range_duration<'r, 's>(
space1, space1,
digit1, digit1,
tag(":"), tag(":"),
verify(digit1, |mm: &str| mm.len() == 2), verify(digit1, |mm: &OrgSource<'_>| mm.len() == 2),
space0, space0,
alt((line_ending, eof)), alt((line_ending, eof)),
)))(input) )))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn inactive_timestamp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn inactive_timestamp<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
recognize(tuple(( recognize(tuple((
tag("["), tag("["),
is_not("\r\n]"), is_not("\r\n]"),

View File

@@ -11,6 +11,7 @@ use nom::multi::many0;
use nom::sequence::preceded; use nom::sequence::preceded;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::util::get_consumed; use super::util::get_consumed;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
@@ -24,10 +25,13 @@ use crate::parser::util::start_of_line;
use crate::parser::Comment; use crate::parser::Comment;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn comment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Comment<'s>> { pub fn comment<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'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::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
let parser_context = context.with_additional_node(ContextElement::Context("comment")); let parser_context = context.with_additional_node(ContextElement::Context("comment"));
@@ -38,12 +42,20 @@ pub fn comment<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?; many0(preceded(not(exit_matcher), comment_line_matcher))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Comment { source })) Ok((
remaining,
Comment {
source: source.into(),
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn comment_line<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn comment_line<'r, 's>(
start_of_line(context, input)?; _context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
let (remaining, _indent) = space0(input)?; let (remaining, _indent) = space0(input)?;
let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple(( let (remaining, (_hash, _leading_whitespace_and_content, _line_ending)) = tuple((
tag("#"), tag("#"),
@@ -57,22 +69,21 @@ fn comment_line<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::parser::parser_context::ContextElement;
use crate::parser::parser_context::ContextTree; use crate::parser::parser_context::ContextTree;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
#[test] #[test]
fn require_space_after_hash() { fn require_space_after_hash() {
let input = "# Comment line let input = OrgSource::new(
"# Comment line
#not a comment #not a comment
# Comment again"; # Comment again",
);
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let comment_matcher = parser_with_context!(comment)(&initial_context);
initial_context.with_additional_node(ContextElement::DocumentRoot(input));
let comment_matcher = parser_with_context!(comment)(&document_context);
let (remaining, first_comment) = comment_matcher(input).expect("Parse first comment"); let (remaining, first_comment) = comment_matcher(input).expect("Parse first comment");
assert_eq!( assert_eq!(
remaining, Into::<&str>::into(remaining),
r#"#not a comment r#"#not a comment
# Comment again"# # Comment again"#
); );

View File

@@ -6,6 +6,7 @@ use nom::combinator::eof;
use nom::combinator::recognize; use nom::combinator::recognize;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::sexp::sexp; use super::sexp::sexp;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
@@ -14,8 +15,11 @@ use crate::parser::util::start_of_line;
use crate::parser::DiarySexp; use crate::parser::DiarySexp;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn diary_sexp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, DiarySexp<'s>> { pub fn diary_sexp<'r, 's>(
start_of_line(context, input)?; _context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, DiarySexp<'s>> {
start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, _clock) = tag("%%")(remaining)?; let (remaining, _clock) = tag("%%")(remaining)?;
let (remaining, _gap_whitespace) = space0(remaining)?; let (remaining, _gap_whitespace) = space0(remaining)?;
@@ -24,5 +28,10 @@ pub fn diary_sexp<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?; recognize(tuple((space0, alt((line_ending, eof)))))(remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, DiarySexp { source })) Ok((
remaining,
DiarySexp {
source: source.into(),
},
))
} }

View File

@@ -16,6 +16,8 @@ use nom::sequence::tuple;
use super::element::Element; use super::element::Element;
use super::object::Object; use super::object::Object;
use super::org_source::convert_error;
use super::org_source::OrgSource;
use super::parser_with_context::parser_with_context; use super::parser_with_context::parser_with_context;
use super::source::Source; use super::source::Source;
use super::token::AllTokensIterator; use super::token::AllTokensIterator;
@@ -95,9 +97,10 @@ impl<'s> Source<'s> for Heading<'s> {
#[allow(dead_code)] #[allow(dead_code)]
pub fn document(input: &str) -> Res<&str, Document> { pub fn document(input: &str) -> Res<&str, Document> {
let initial_context: ContextTree<'_, '_> = ContextTree::new(); let initial_context: ContextTree<'_, '_> = ContextTree::new();
let document_context = let wrapped_input = OrgSource::new(input);
initial_context.with_additional_node(ContextElement::DocumentRoot(input)); let (remaining, document) = _document(&initial_context, wrapped_input)
let (remaining, document) = _document(&document_context, input)?; .map(|(rem, out)| (Into::<&str>::into(rem), out))
.map_err(convert_error)?;
{ {
// If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets. // If there are radio targets in this document then we need to parse the entire document again with the knowledge of the radio targets.
let all_radio_targets: Vec<&Vec<Object<'_>>> = document let all_radio_targets: Vec<&Vec<Object<'_>>> = document
@@ -113,17 +116,22 @@ pub fn document(input: &str) -> Res<&str, Document> {
.map(|rt| &rt.children) .map(|rt| &rt.children)
.collect(); .collect();
if !all_radio_targets.is_empty() { if !all_radio_targets.is_empty() {
let document_context = document_context let initial_context = initial_context
.with_additional_node(ContextElement::RadioTarget(all_radio_targets)); .with_additional_node(ContextElement::RadioTarget(all_radio_targets));
let (remaining, document) = _document(&document_context, input)?; let (remaining, document) = _document(&initial_context, wrapped_input)
return Ok((remaining, document)); .map(|(rem, out)| (Into::<&str>::into(rem), out))
.map_err(convert_error)?;
return Ok((remaining.into(), document));
} }
} }
Ok((remaining, document)) Ok((remaining.into(), document))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Document<'s>> { fn _document<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Document<'s>> {
let zeroth_section_matcher = parser_with_context!(zeroth_section)(context); let zeroth_section_matcher = parser_with_context!(zeroth_section)(context);
let heading_matcher = parser_with_context!(heading)(context); let heading_matcher = parser_with_context!(heading)(context);
let (remaining, _blank_lines) = many0(blank_line)(input)?; let (remaining, _blank_lines) = many0(blank_line)(input)?;
@@ -133,7 +141,7 @@ pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
Ok(( Ok((
remaining, remaining,
Document { Document {
source, source: source.into(),
zeroth_section, zeroth_section,
children, children,
}, },
@@ -141,7 +149,10 @@ pub fn _document<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Section<'s>> { fn zeroth_section<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Section<'s>> {
// TODO: The zeroth section is specialized so it probably needs its own parser // TODO: The zeroth section is specialized so it probably needs its own parser
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
@@ -182,11 +193,20 @@ fn zeroth_section<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s s
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Section { source, children })) Ok((
remaining,
Section {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn section<'r, 's>(context: Context<'r, 's>, mut input: &'s str) -> Res<&'s str, Section<'s>> { fn section<'r, 's>(
context: Context<'r, 's>,
mut input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Section<'s>> {
// TODO: The zeroth section is specialized so it probably needs its own parser // TODO: The zeroth section is specialized so it probably needs its own parser
let parser_context = context let parser_context = context
.with_additional_node(ContextElement::ConsumeTrailingWhitespace(true)) .with_additional_node(ContextElement::ConsumeTrailingWhitespace(true))
@@ -223,17 +243,29 @@ fn section<'r, 's>(context: Context<'r, 's>, mut input: &'s str) -> Res<&'s str,
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
Ok((remaining, Section { source, children })) Ok((
remaining,
Section {
source: source.into(),
children,
},
))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn section_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn section_end<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let headline_matcher = parser_with_context!(headline)(context); let headline_matcher = parser_with_context!(headline)(context);
recognize(headline_matcher)(input) recognize(headline_matcher)(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Heading<'s>> { fn heading<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Heading<'s>> {
not(|i| context.check_exit_matcher(i))(input)?; not(|i| context.check_exit_matcher(i))(input)?;
let (remaining, (star_count, _ws, title)) = headline(context, input)?; let (remaining, (star_count, _ws, title)) = headline(context, input)?;
let section_matcher = parser_with_context!(section)(context); let section_matcher = parser_with_context!(section)(context);
@@ -249,7 +281,7 @@ fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Hea
Ok(( Ok((
remaining, remaining,
Heading { Heading {
source, source: source.into(),
stars: star_count, stars: star_count,
title, title,
children, children,
@@ -260,18 +292,17 @@ fn heading<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Hea
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn headline<'r, 's>( fn headline<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, (usize, &'s str, Vec<Object<'s>>)> { ) -> Res<OrgSource<'s>, (usize, OrgSource<'s>, Vec<Object<'s>>)> {
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Document, class: ExitClass::Document,
exit_matcher: &headline_end, exit_matcher: &headline_end,
})); }));
let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context); let standard_set_object_matcher = parser_with_context!(standard_set_object)(&parser_context);
let start_of_line_matcher = parser_with_context!(start_of_line)(&parser_context);
let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple(( let (remaining, (_sol, star_count, ws, title, _line_ending)) = tuple((
start_of_line_matcher, start_of_line,
many1_count(tag("*")), many1_count(tag("*")),
space1, space1,
many1(standard_set_object_matcher), many1(standard_set_object_matcher),
@@ -281,7 +312,10 @@ fn headline<'r, 's>(
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn headline_end<'r, 's>(_context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn headline_end<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
line_ending(input) line_ending(input)
} }

View File

@@ -10,6 +10,7 @@ use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@@ -31,13 +32,16 @@ use crate::parser::Element;
use crate::parser::Paragraph; use crate::parser::Paragraph;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Drawer<'s>> { pub fn drawer<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Drawer<'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::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple(( let (remaining, (_open_colon, drawer_name, _close_colon, _new_line)) = tuple((
tag(":"), tag(":"),
@@ -63,9 +67,9 @@ pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
))(remaining) ))(remaining)
{ {
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => { Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line)); let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain); let source = get_consumed(remaining, remain);
element.set_source(source); element.set_source(source.into());
(remain, vec![element]) (remain, vec![element])
} }
Err(_) => { Err(_) => {
@@ -81,21 +85,24 @@ pub fn drawer<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
Ok(( Ok((
remaining, remaining,
Drawer { Drawer {
source, source: source.into(),
name: drawer_name, name: drawer_name.into(),
children, children,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input) take_while(|c| WORD_CONSTITUENT_CHARACTERS.contains(c) || "-_".contains(c))(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn drawer_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn drawer_end<'r, 's>(
start_of_line(context, input)?; _context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
recognize(tuple(( recognize(tuple((
space0, space0,
tag_no_case(":end:"), tag_no_case(":end:"),

View File

@@ -11,6 +11,7 @@ use nom::combinator::recognize;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::CustomError; use crate::error::CustomError;
use crate::error::MyError; use crate::error::MyError;
@@ -33,15 +34,15 @@ use crate::parser::Element;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn dynamic_block<'r, 's>( pub fn dynamic_block<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, DynamicBlock<'s>> { ) -> Res<OrgSource<'s>, DynamicBlock<'s>> {
// TODO: Do I need to differentiate between different dynamic block types. // TODO: Do I need to differentiate between different dynamic block types.
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::MyError(MyError(
"Cannot nest objects of the same element", "Cannot nest objects of the same element".into(),
)))); ))));
} }
start_of_line(context, input)?; start_of_line(input)?;
let (remaining, _leading_whitespace) = space0(input)?; let (remaining, _leading_whitespace) = space0(input)?;
let (remaining, (_begin, name, parameters, _ws)) = tuple(( let (remaining, (_begin, name, parameters, _ws)) = tuple((
recognize(tuple((tag_no_case("#+begin:"), space1))), recognize(tuple((tag_no_case("#+begin:"), space1))),
@@ -69,9 +70,9 @@ pub fn dynamic_block<'r, 's>(
))(remaining) ))(remaining)
{ {
Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => { Ok((remain, (_not_immediate_exit, first_line, (_trailing_whitespace, _exit_contents)))) => {
let mut element = Element::Paragraph(Paragraph::of_text(first_line)); let mut element = Element::Paragraph(Paragraph::of_text(first_line.into()));
let source = get_consumed(remaining, remain); let source = get_consumed(remaining, remain);
element.set_source(source); element.set_source(source.into());
(remain, vec![element]) (remain, vec![element])
} }
Err(_) => { Err(_) => {
@@ -86,27 +87,30 @@ pub fn dynamic_block<'r, 's>(
Ok(( Ok((
remaining, remaining,
DynamicBlock { DynamicBlock {
source, source: source.into(),
name, name: name.into(),
parameters, parameters: parameters.map(|val| val.into()),
children, children,
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn name<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not(" \t\r\n")(input) is_not(" \t\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn parameters<'s>(input: &'s str) -> Res<&'s str, &'s str> { fn parameters<'s>(input: OrgSource<'s>) -> Res<OrgSource<'s>, OrgSource<'s>> {
is_not("\r\n")(input) is_not("\r\n")(input)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn dynamic_block_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn dynamic_block_end<'r, 's>(
start_of_line(context, input)?; _context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
start_of_line(input)?;
let (remaining, source) = recognize(tuple(( let (remaining, source) = recognize(tuple((
space0, space0,
tag_no_case("#+end:"), tag_no_case("#+end:"),

View File

@@ -19,28 +19,32 @@ use super::lesser_block::example_block;
use super::lesser_block::export_block; use super::lesser_block::export_block;
use super::lesser_block::src_block; use super::lesser_block::src_block;
use super::lesser_block::verse_block; use super::lesser_block::verse_block;
use super::org_source::OrgSource;
use super::paragraph::paragraph; use super::paragraph::paragraph;
use super::plain_list::detect_plain_list;
use super::plain_list::plain_list; use super::plain_list::plain_list;
use super::source::SetSource; use super::source::SetSource;
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 super::Context; use super::Context;
use crate::error::CustomError;
use crate::error::MyError;
use crate::error::Res; use crate::error::Res;
use crate::parser::parser_with_context::parser_with_context; use crate::parser::parser_with_context::parser_with_context;
use crate::parser::table::org_mode_table; use crate::parser::table::org_mode_table;
pub fn element( pub const fn element(
can_be_paragraph: bool, can_be_paragraph: bool,
) -> impl for<'r, 's> Fn(Context<'r, 's>, &'s str) -> Res<&'s str, Element<'s>> { ) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, Element<'s>> {
move |context: Context, input: &str| _element(context, input, can_be_paragraph) move |context: Context, input: OrgSource<'_>| _element(context, input, can_be_paragraph)
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _element<'r, 's>( fn _element<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
can_be_paragraph: bool, can_be_paragraph: bool,
) -> Res<&'s str, Element<'s>> { ) -> Res<OrgSource<'s>, Element<'s>> {
let plain_list_matcher = parser_with_context!(plain_list)(context); let plain_list_matcher = parser_with_context!(plain_list)(context);
let greater_block_matcher = parser_with_context!(greater_block)(context); let greater_block_matcher = parser_with_context!(greater_block)(context);
let dynamic_block_matcher = parser_with_context!(dynamic_block)(context); let dynamic_block_matcher = parser_with_context!(dynamic_block)(context);
@@ -103,7 +107,30 @@ fn _element<'r, 's>(
maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?; maybe_consume_trailing_whitespace_if_not_exiting(context, remaining)?;
let source = get_consumed(input, remaining); let source = get_consumed(input, remaining);
element.set_source(source); element.set_source(source.into());
Ok((remaining, element)) Ok((remaining, element))
} }
pub const fn detect_element(
can_be_paragraph: bool,
) -> impl for<'r, 's> Fn(Context<'r, 's>, OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
move |context: Context, input: OrgSource<'_>| _detect_element(context, input, can_be_paragraph)
}
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn _detect_element<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
can_be_paragraph: bool,
) -> Res<OrgSource<'s>, ()> {
if detect_plain_list(context, input).is_ok() {
return Ok((input, ()));
}
if _element(context, input, can_be_paragraph).is_ok() {
return Ok((input, ()));
}
return Err(nom::Err::Error(CustomError::MyError(MyError(
"No element detected.".into(),
))));
}

View File

@@ -7,6 +7,7 @@ use nom::combinator::eof;
use nom::combinator::peek; use nom::combinator::peek;
use nom::combinator::recognize; use nom::combinator::recognize;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::object::Entity; use crate::parser::object::Entity;
@@ -14,7 +15,10 @@ use crate::parser::parser_with_context::parser_with_context;
use crate::parser::util::get_consumed; use crate::parser::util::get_consumed;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn entity<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, Entity<'s>> { pub fn entity<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, Entity<'s>> {
let (remaining, _) = tag("\\")(input)?; let (remaining, _) = tag("\\")(input)?;
let (remaining, entity_name) = name(context, remaining)?; let (remaining, entity_name) = name(context, remaining)?;
let (remaining, _) = alt(( let (remaining, _) = alt((
@@ -27,14 +31,17 @@ pub fn entity<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str,
Ok(( Ok((
remaining, remaining,
Entity { Entity {
source, source: source.into(),
entity_name, entity_name: entity_name.into(),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn name<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
// TODO: This should be defined by org-entities and optionally org-entities-user // TODO: This should be defined by org-entities and optionally org-entities-user
// TODO: Add the rest of the entities, this is a very incomplete list // TODO: Add the rest of the entities, this is a very incomplete list
@@ -43,7 +50,7 @@ fn name<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s st
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn entity_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, ()> { fn entity_end<'r, 's>(_context: Context<'r, 's>, input: OrgSource<'s>) -> Res<OrgSource<'s>, ()> {
let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?; let (remaining, _) = alt((eof, recognize(satisfy(|c| !c.is_alphabetic()))))(input)?;
Ok((remaining, ())) Ok((remaining, ()))

View File

@@ -8,6 +8,9 @@ pub enum ExitClass {
/// Elements who cede priority to alpha elements when matching. /// Elements who cede priority to alpha elements when matching.
Beta = 300, Beta = 300,
/// Elements who cede priority to alpha and beta elements when matching.
Gamma = 4000,
} }
impl std::fmt::Display for ExitClass { impl std::fmt::Display for ExitClass {

View File

@@ -7,6 +7,7 @@ use nom::multi::many1;
use nom::multi::many_till; use nom::multi::many_till;
use nom::sequence::tuple; use nom::sequence::tuple;
use super::org_source::OrgSource;
use super::Context; use super::Context;
use crate::error::Res; use crate::error::Res;
use crate::parser::exiting::ExitClass; use crate::parser::exiting::ExitClass;
@@ -20,13 +21,13 @@ use crate::parser::ExportSnippet;
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
pub fn export_snippet<'r, 's>( pub fn export_snippet<'r, 's>(
context: Context<'r, 's>, context: Context<'r, 's>,
input: &'s str, input: OrgSource<'s>,
) -> Res<&'s str, ExportSnippet<'s>> { ) -> Res<OrgSource<'s>, ExportSnippet<'s>> {
let (remaining, _) = tag("@@")(input)?; let (remaining, _) = tag("@@")(input)?;
let (remaining, backend_name) = backend(context, remaining)?; let (remaining, backend_name) = backend(context, remaining)?;
let parser_context = let parser_context =
context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode { context.with_additional_node(ContextElement::ExitMatcherNode(ExitMatcherNode {
class: ExitClass::Beta, class: ExitClass::Gamma,
exit_matcher: &export_snippet_end, exit_matcher: &export_snippet_end,
})); }));
let (remaining, backend_contents) = opt(tuple(( let (remaining, backend_contents) = opt(tuple((
@@ -38,15 +39,18 @@ pub fn export_snippet<'r, 's>(
Ok(( Ok((
remaining, remaining,
ExportSnippet { ExportSnippet {
source, source: source.into(),
backend: backend_name, backend: backend_name.into(),
contents: backend_contents.map(|(_colon, backend_contents)| backend_contents), contents: backend_contents.map(|(_colon, backend_contents)| backend_contents.into()),
}, },
)) ))
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn backend<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn backend<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, backend_name) = let (remaining, backend_name) =
recognize(many1(verify(anychar, |c| c.is_alphanumeric() || *c == '-')))(input)?; recognize(many1(verify(anychar, |c| c.is_alphanumeric() || *c == '-')))(input)?;
@@ -54,7 +58,10 @@ fn backend<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn contents<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn contents<'r, 's>(
context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
let (remaining, source) = recognize(verify( let (remaining, source) = recognize(verify(
many_till(anychar, parser_with_context!(exit_matcher_parser)(context)), many_till(anychar, parser_with_context!(exit_matcher_parser)(context)),
|(children, _exit_contents)| !children.is_empty(), |(children, _exit_contents)| !children.is_empty(),
@@ -63,6 +70,9 @@ fn contents<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'
} }
#[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))]
fn export_snippet_end<'r, 's>(context: Context<'r, 's>, input: &'s str) -> Res<&'s str, &'s str> { fn export_snippet_end<'r, 's>(
_context: Context<'r, 's>,
input: OrgSource<'s>,
) -> Res<OrgSource<'s>, OrgSource<'s>> {
tag("@@")(input) tag("@@")(input)
} }

Some files were not shown because too many files have changed in this diff Show More