Skip to content

Commit

Permalink
Add edn::read example showing how to handle reading quoted s-expressi…
Browse files Browse the repository at this point in the history
…ons.

Run clippy on examples in CI.
  • Loading branch information
Grinkers committed Sep 18, 2024
1 parent 75bed24 commit f0d902e
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 7 deletions.
5 changes: 3 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
run: |
cargo test --all-features --no-fail-fast --target ${{ matrix.target }}
cargo test --features std --no-default-features --no-fail-fast --target ${{ matrix.target }}
cargo run --example get-nth --target ${{ matrix.target }}
cargo test --examples --target ${{ matrix.target }}
build_linux:
name: Build Linux
Expand Down Expand Up @@ -114,7 +114,7 @@ jobs:
run: |
cargo test --all-features --no-fail-fast --target ${{ matrix.target }}
cargo test --features std --no-default-features --no-fail-fast --target ${{ matrix.target }}
cargo run --example get-nth --target ${{ matrix.target }}
cargo test --examples --target ${{ matrix.target }}
build_embedded:
name: Build Embedded
Expand Down Expand Up @@ -158,3 +158,4 @@ jobs:
toolchain: stable minus 2 releases
- run: cargo clippy --all-features -- --deny warnings
- run: cargo clippy --no-default-features -- --deny warnings
- run: cargo clippy --examples -- --deny warnings -A clippy::unwrap-used
6 changes: 4 additions & 2 deletions bb.edn
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{:tasks
{:init (do (def code-cov-env
{"CARGO_INCREMENTAL" "0"
"RUSTFLAGS" "-Cinstrument-coverage -Copt-level=0 -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off"
"RUSTFLAGS" "-Cinstrument-coverage -Copt-level=0 -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off"
"LLVM_PROFILE_FILE" "target/coverage/cargo-test-%p-%m.profraw"}))
:requires ([babashka.fs :as fs])
clean {:doc "Removes target folder"
:task (fs/delete-tree "target")}
test_lib_features (shell "cargo test --all-features --no-fail-fast")
test_lib_no_default_features (shell "cargo test --features std --no-default-features --no-fail-fast")
test-examples (do (shell "cargo run --example get-nth"))
test-examples (do (shell "cargo test --examples"))
cargo-test {:doc "Runs all cargo tests"
:depends [test_lib_features test_lib_no_default_features test-examples]}
cargo-fmt {:doc "Checks cargo fmt"
Expand All @@ -17,6 +17,8 @@
:task (shell "cargo clippy --all-features -- --deny warnings")}
cargo-clippy-no-defaults {:doc "Cargo clippy with no default features"
:task (shell "cargo clippy --no-default-features -- --deny warnings")}
cargo-clippy-examples {:doc "Cargo clippy on examples"
:task (shell "cargo --examples -- --deny warnings -A clippy::unwrap-used")}
clippy {:doc "Runs all variations of cargo clippy"
:depends [cargo-clippy-all-features cargo-clippy-no-defaults]}
cov-all-features {:doc "Coverage, all features"
Expand Down
8 changes: 7 additions & 1 deletion examples/get-nth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,11 @@ fn maybe_forty_two<'a>(edn: &'a Edn<'a>) -> Option<&Edn<'a>> {

fn main() {
let e = edn::read_string("{:foo {猫 {{:foo :bar} [1 2 42 3]}}}").unwrap();
println!("{:?}", maybe_forty_two(&e));
let edn = maybe_forty_two(&e).unwrap();
assert_eq!(edn, &Edn::Int(42));
}

#[test]
fn run() {
main();
}
62 changes: 62 additions & 0 deletions examples/read_quotes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use clojure_reader::edn::{self, Edn};

// Recursively traverse the Edn struct.
// To keep this example short, this only handles lists and literals.
fn wrap_quote(edn: Edn<'_>) -> Edn<'_> {
match edn {
Edn::Symbol(s) => s.strip_prefix('\'').map_or(Edn::Symbol(s), |stripped| {
Edn::List(vec![Edn::Symbol("quote"), Edn::Symbol(stripped)])
}),
Edn::List(mut edn) => {
edn.reverse();
let mut list = vec![];

while let Some(e) = edn.pop() {
if e == Edn::Symbol("'") {
list.push(Edn::List(vec![Edn::Symbol("quote"), wrap_quote(edn.pop().unwrap())]));
} else {
list.push(wrap_quote(e));
}
}

return Edn::List(list);
}
_ => edn,
}
}

// Use `read` to handle the leading ' symbol.
fn quotify(s: &str) -> Edn<'_> {
let (edn, rest) = edn::read(s).unwrap();

let edn = if edn == Edn::Symbol("'") {
Edn::List(vec![Edn::Symbol("quote"), edn::read_string(rest).unwrap()])
} else {
edn
};

let edn = wrap_quote(edn);
edn
}

fn main() {
let quoted = quotify("'(foo (bar '(a 'b)))");
assert_eq!(format!("{quoted}"), "(quote (foo (bar (quote (a (quote b))))))");

let quoted = quotify("(foo '(a))");
assert_eq!(format!("{quoted}"), "(foo (quote (a)))");

let quoted = quotify("'(foo the 'bar)");
assert_eq!(format!("{quoted}"), "(quote (foo the (quote bar)))");

let quoted = quotify("(foo the 'bar)");
assert_eq!(format!("{quoted}"), "(foo the (quote bar))");

let quoted = quotify("(foo the bar)");
assert_eq!(format!("{quoted}"), "(foo the bar)");
}

#[test]
fn run() {
main();
}
5 changes: 3 additions & 2 deletions src/edn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ pub fn read_string(edn: &str) -> Result<Edn<'_>, error::Error> {
///
/// # Errors
///
/// Default behavior of Clojure's `read` is to throw an error on EOF, unlike `read_string`.
/// https://clojure.github.io/tools.reader/#clojure.tools.reader.edn/read
///
/// See [`crate::error::Error`].
pub fn read(edn: &str) -> Result<(Edn<'_>, &str), error::Error> {
let r = parse::parse(edn)?;
// Default behavior of Clojure's `read` is to throw an error on EOF, unlike `read_string`
// https://clojure.github.io/tools.reader/#clojure.tools.reader.edn/read
if r.0 == Edn::Nil && r.1.is_empty() {
return Err(error::Error {
code: error::Code::UnexpectedEOF,
Expand Down

0 comments on commit f0d902e

Please sign in to comment.