From 15a5c76a8990636209fc3d9145bdd09f6c10a9f3 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 14:18:49 -0500 Subject: [PATCH 1/7] chore: compare benchmarks b/w nom, pest against serde --- .github/workflows/ci.yml | 2 + Cargo.lock | 361 +++++++++++++++++++++++++++++++++++- Cargo.toml | 11 +- benches/bench_nom.rs | 284 ++++++++++++++++++++++++++++ benches/bench_pest.rs | 28 +++ benches/bench_serde_json.rs | 16 ++ benches/json.pest | 34 ++++ benches/json_benches.rs | 19 ++ src/lib.rs | 1 + src/main.rs | 4 +- tailcall-jq/Cargo.toml | 3 + tailcall-jq/src/jq/jq.rs | 1 + tailcall-jq/src/jq/mod.rs | 1 + tailcall-jq/src/lib.rs | 1 + tests/ci.rs | 1 + 15 files changed, 759 insertions(+), 8 deletions(-) create mode 100644 benches/bench_nom.rs create mode 100644 benches/bench_pest.rs create mode 100644 benches/bench_serde_json.rs create mode 100644 benches/json.pest create mode 100644 benches/json_benches.rs create mode 100644 tailcall-jq/src/jq/jq.rs create mode 100644 tailcall-jq/src/jq/mod.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f9ad84..27bd60e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,8 @@ jobs: components: clippy, rustfmt - name: Cargo Test run: cargo test --all-features --workspace + - name: Cargo Bench + run: cargo bench - name: Cargo Fmt run: cargo +nightly fmt --check - name: Cargo Clippy diff --git a/Cargo.lock b/Cargo.lock index 66b6745..c0f00d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -21,6 +21,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + [[package]] name = "anyhow" version = "1.0.93" @@ -168,6 +180,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytes" version = "1.8.0" @@ -177,12 +195,70 @@ dependencies = [ "serde", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" + [[package]] name = "cpufeatures" version = "0.2.15" @@ -192,6 +268,73 @@ dependencies = [ "libc", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -285,6 +428,12 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -425,6 +574,16 @@ dependencies = [ "strum_macros", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "handlebars" version = "5.1.2" @@ -451,6 +610,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "http" version = "1.1.0" @@ -485,6 +650,26 @@ dependencies = [ "serde", ] +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -495,9 +680,21 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" name = "jq" version = "0.1.0" dependencies = [ + "criterion", "gh-workflow", - "pretty_assertions", - "tailcall-jq", + "nom", + "pest", + "pest_derive", + "serde_json", +] + +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", ] [[package]] @@ -599,6 +796,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + [[package]] name = "percent-encoding" version = "2.3.1" @@ -662,6 +865,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -723,6 +954,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.11.1" @@ -777,6 +1028,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "serde" version = "1.0.215" @@ -935,6 +1195,7 @@ dependencies = [ "async-graphql-value", "indexmap", "nom", + "pretty_assertions", "serde_json", "serde_json_borrow", ] @@ -972,6 +1233,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "toml_datetime" version = "0.6.8" @@ -1019,6 +1290,90 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "web-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index b43a5aa..a7416dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,16 @@ nom = "8.0.0-alpha2" anyhow = "1.0.93" [dev-dependencies] -pretty_assertions = "1.4.1" gh-workflow = "0.4.1" -tailcall-jq = { path = "tailcall-jq" } +pest = "2.7.14" +pest_derive = {version = "2.7.14"} +criterion = "0.5.1" +serde_json = "1.0.133" +nom = "8.0.0-alpha2" [workspace] members = ["tailcall-jq"] + +[[bench]] +name = "json_benches" +harness = false diff --git a/benches/bench_nom.rs b/benches/bench_nom.rs new file mode 100644 index 0000000..dafa81a --- /dev/null +++ b/benches/bench_nom.rs @@ -0,0 +1,284 @@ +use nom::{ + branch::alt, + bytes::{tag, take}, + character::{anychar, char, multispace0, none_of}, + combinator::{map, map_opt, map_res, value, verify}, + error::{Error, FromExternalError, ParseError}, + multi::{fold, separated_list0}, + number::double, + sequence::{delimited, preceded, separated_pair}, + Complete, Emit, Mode, OutputM, Parser, +}; + +use criterion::Criterion; +use std::{collections::HashMap, marker::PhantomData, num::ParseIntError}; + +#[derive(Debug, PartialEq, Clone)] +pub enum JsonValue { + Null, + Bool(bool), + Str(String), + Num(f64), + Array(Vec), + Object(HashMap), +} + +fn boolean<'a, E: ParseError<&'a str>>() -> impl Parser<&'a str, Output = bool, Error = E> { + alt((value(false, tag("false")), value(true, tag("true")))) +} + +fn u16_hex<'a, E: ParseError<&'a str> + FromExternalError<&'a str, ParseIntError>>( +) -> impl Parser<&'a str, Output = u16, Error = E> { + map_res(take(4usize), |s| u16::from_str_radix(s, 16)) +} + +fn unicode_escape<'a, E: ParseError<&'a str> + FromExternalError<&'a str, ParseIntError>>( +) -> impl Parser<&'a str, Output = char, Error = E> { + map_opt( + alt(( + // Not a surrogate + map( + verify(u16_hex(), |cp| !(0xD800..0xE000).contains(cp)), + |cp| cp as u32, + ), + // See https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF for details + map( + verify( + separated_pair(u16_hex(), tag("\\u"), u16_hex()), + |(high, low)| (0xD800..0xDC00).contains(high) && (0xDC00..0xE000).contains(low), + ), + |(high, low)| { + let high_ten = (high as u32) - 0xD800; + let low_ten = (low as u32) - 0xDC00; + (high_ten << 10) + low_ten + 0x10000 + }, + ), + )), + // Could probably be replaced with .unwrap() or _unchecked due to the verify checks + std::char::from_u32, + ) +} + +fn character< + 'a, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +>() -> impl Parser<&'a str, Output = char, Error = E> { + Character { e: PhantomData } + /*let (input, c) = none_of("\"")(input)?; + if c == '\\' { + alt(( + map_res(anychar, |c| { + Ok(match c { + '"' | '\\' | '/' => c, + 'b' => '\x08', + 'f' => '\x0C', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return Err(()), + }) + }), + preceded(char('u'), unicode_escape()), + )) + .parse(input) + } else { + Ok((input, c)) + }*/ +} + +struct Character { + e: PhantomData, +} + +impl<'a, E> Parser<&'a str> for Character +where + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +{ + type Output = char; + + type Error = E; + + fn process( + &mut self, + input: &'a str, + ) -> nom::PResult { + let (input, c): (&str, char) = + none_of("\"").process::>(input)?; + if c == '\\' { + alt(( + map_res(anychar, |c| { + Ok(match c { + '"' | '\\' | '/' => c, + 'b' => '\x08', + 'f' => '\x0C', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + _ => return Err(()), + }) + }), + preceded(char('u'), unicode_escape()), + )) + .process::(input) + } else { + Ok((input, OM::Output::bind(|| c))) + } + } +} + +fn string< + 'a, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +>() -> impl Parser<&'a str, Output = String, Error = E> { + delimited( + char('"'), + fold(0.., character(), String::new, |mut string, c| { + string.push(c); + string + }), + char('"'), + ) +} + +fn ws< + 'a, + O, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, + F: Parser<&'a str, Output = O, Error = E>, +>( + f: F, +) -> impl Parser<&'a str, Output = O, Error = E> { + delimited(multispace0(), f, multispace0()) +} + +fn array< + 'a, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +>() -> impl Parser<&'a str, Output = Vec, Error = E> { + delimited( + char('['), + ws(separated_list0(ws(char(',')), json_value())), + char(']'), + ) +} + +fn object< + 'a, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +>() -> impl Parser<&'a str, Output = HashMap, Error = E> { + map( + delimited( + char('{'), + ws(separated_list0( + ws(char(',')), + separated_pair(string(), ws(char(':')), json_value()), + )), + char('}'), + ), + |key_values| key_values.into_iter().collect(), + ) +} + +/* +fn json_value<'a, E: ParseError<&'a str> + FromExternalError<&'a str, ParseIntError> + FromExternalError<&'a str, ()>, +>() -> Box> { + use JsonValue::*; + + Box::new(alt(( + value(Null, tag("null")), + map(boolean(), Bool), + map(string(), Str), + map(double, Num), + map(array(), Array), + map(object(), Object), + ))) +} +*/ + +fn json_value< + 'a, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +>() -> JsonParser { + JsonParser { e: PhantomData } +} + +struct JsonParser { + e: PhantomData, +} + +// the main Parser implementation is done explicitely on a real type, +// because haaving json_value return `impl Parser` would result in +// "recursive opaque type" errors +impl<'a, E> Parser<&'a str> for JsonParser +where + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +{ + type Output = JsonValue; + type Error = E; + + fn process( + &mut self, + input: &'a str, + ) -> nom::PResult { + use JsonValue::*; + + alt(( + value(Null, tag("null")), + map(boolean(), Bool), + map(string(), Str), + map(double(), Num), + map(array(), Array), + map(object(), Object), + )) + .process::(input) + } +} + +fn json< + 'a, + E: ParseError<&'a str> + + FromExternalError<&'a str, ParseIntError> + + FromExternalError<&'a str, ()>, +>() -> impl Parser<&'a str, Output = JsonValue, Error = E> { + ws(json_value()) +} + +pub fn bench_nom(c: &mut Criterion) { + let input = r#" + { + "name": "John", + "age": 30, + "is_student": false, + "scores": [85.5, 90, 78], + "address": null + } + "#; + + json::>() + .process::>(input) + .unwrap(); + + c.bench_function("bench_nom", |b| { + b.iter(|| { + json::>() + .process::>(input) + .unwrap() + }); + }); +} diff --git a/benches/bench_pest.rs b/benches/bench_pest.rs new file mode 100644 index 0000000..475463b --- /dev/null +++ b/benches/bench_pest.rs @@ -0,0 +1,28 @@ +use criterion::Criterion; +use pest::Parser; +use pest_derive::Parser; + +#[derive(Parser)] +#[grammar = "benches/json.pest"] // relative path to the grammar file +struct JSONParser; + +pub fn bench_pest(c: &mut Criterion) { + let input = r#" + { + "name": "John", + "age": 30, + "is_student": false, + "scores": [85.5, 90, 78], + "address": null + } + "#; + + c.bench_function("bench_pest", |b| { + b.iter(|| { + JSONParser::parse(Rule::json, input) + .unwrap() + .next() + .unwrap() + }) + }); +} diff --git a/benches/bench_serde_json.rs b/benches/bench_serde_json.rs new file mode 100644 index 0000000..2430e80 --- /dev/null +++ b/benches/bench_serde_json.rs @@ -0,0 +1,16 @@ +use criterion::Criterion; + +pub fn bench_serde_json(c: &mut Criterion) { + let input = r#" + { + "name": "John", + "age": 30, + "is_student": false, + "scores": [85.5, 90, 78], + "address": null + } + "#; + c.bench_function("serde_json", move |b| { + b.iter(|| serde_json::from_str::(input).unwrap()) + }); +} diff --git a/benches/json.pest b/benches/json.pest new file mode 100644 index 0000000..3cc728c --- /dev/null +++ b/benches/json.pest @@ -0,0 +1,34 @@ +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } +object = { + "{" ~ "}" | + "{" ~ pair ~ ("," ~ pair)* ~ "}" +} +pair = { string ~ ":" ~ value } + +array = { + "[" ~ "]" | + "[" ~ value ~ ("," ~ value)* ~ "]" +} +value = _{ object | array | string | number | boolean | null } + +boolean = { "true" | "false" } + +null = { "null" } + +string = ${ "\"" ~ inner ~ "\"" } +inner = @{ char* } +char = { + !("\"" | "\\") ~ ANY + | "\\" ~ ("\"" | "\\" | "/" | "b" | "f" | "n" | "r" | "t") + | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) +} + +number = @{ + "-"? + ~ ("0" | ASCII_NONZERO_DIGIT ~ ASCII_DIGIT*) + ~ ("." ~ ASCII_DIGIT*)? + ~ (^"e" ~ ("+" | "-")? ~ ASCII_DIGIT+)? +} + +json = _{ SOI ~ (object | array) ~ EOI } + diff --git a/benches/json_benches.rs b/benches/json_benches.rs new file mode 100644 index 0000000..32f9473 --- /dev/null +++ b/benches/json_benches.rs @@ -0,0 +1,19 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +mod bench_pest; +mod bench_serde_json; + +mod bench_nom; + +fn all_benchmarks(c: &mut Criterion) { + bench_pest::bench_pest(c); + bench_serde_json::bench_serde_json(c); + bench_nom::bench_nom(c); +} + +criterion_group! { + name = benches; + config = Criterion::default(); + targets = all_benchmarks +} +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index e69de29..8b13789 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -0,0 +1 @@ + diff --git a/src/main.rs b/src/main.rs index 8a55e80..f328e4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1 @@ -fn main() { - -} \ No newline at end of file +fn main() {} diff --git a/tailcall-jq/Cargo.toml b/tailcall-jq/Cargo.toml index 698f0f2..9c238aa 100644 --- a/tailcall-jq/Cargo.toml +++ b/tailcall-jq/Cargo.toml @@ -11,3 +11,6 @@ indexmap = { workspace = true } serde_json_borrow = { workspace = true } nom = { workspace = true } anyhow = { workspace = true } + +[dev-dependencies] +pretty_assertions = "1.4.1" diff --git a/tailcall-jq/src/jq/jq.rs b/tailcall-jq/src/jq/jq.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tailcall-jq/src/jq/jq.rs @@ -0,0 +1 @@ + diff --git a/tailcall-jq/src/jq/mod.rs b/tailcall-jq/src/jq/mod.rs new file mode 100644 index 0000000..5a64fcf --- /dev/null +++ b/tailcall-jq/src/jq/mod.rs @@ -0,0 +1 @@ +mod jq; diff --git a/tailcall-jq/src/lib.rs b/tailcall-jq/src/lib.rs index 98a4ef2..093ade2 100644 --- a/tailcall-jq/src/lib.rs +++ b/tailcall-jq/src/lib.rs @@ -1,3 +1,4 @@ #![allow(clippy::module_inception)] +pub mod jq; pub mod jsonlike; pub mod mustache; diff --git a/tests/ci.rs b/tests/ci.rs index 9e6c877..6094568 100644 --- a/tests/ci.rs +++ b/tests/ci.rs @@ -19,6 +19,7 @@ fn generate() { .args("--all-features --workspace") .name("Cargo Test"), ) + .add_step(Cargo::new("bench").name("Cargo Bench")) .add_step( Cargo::new("fmt") .nightly() From fbbd24e26c46c96a99d1a872f9e003f388601727 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 15:00:25 -0500 Subject: [PATCH 2/7] add `pretty_assertions` --- Cargo.lock | 1 + Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index c0f00d5..4fc26cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,6 +685,7 @@ dependencies = [ "nom", "pest", "pest_derive", + "pretty_assertions", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index a7416dd..9065ae6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ pest_derive = {version = "2.7.14"} criterion = "0.5.1" serde_json = "1.0.133" nom = "8.0.0-alpha2" +pretty_assertions = "1.4.1" [workspace] members = ["tailcall-jq"] From 7107070d9f9aceb439bde0b34525b3befdf2e123 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 15:14:03 -0500 Subject: [PATCH 3/7] drop `pretty_assertions` from root proj --- Cargo.lock | 1 - Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4fc26cb..c0f00d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,7 +685,6 @@ dependencies = [ "nom", "pest", "pest_derive", - "pretty_assertions", "serde_json", ] diff --git a/Cargo.toml b/Cargo.toml index 9065ae6..a7416dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ pest_derive = {version = "2.7.14"} criterion = "0.5.1" serde_json = "1.0.133" nom = "8.0.0-alpha2" -pretty_assertions = "1.4.1" [workspace] members = ["tailcall-jq"] From f3ac5a36727107aee70b57a61abda44eda864e0f Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 15:20:31 -0500 Subject: [PATCH 4/7] use nightly for test --- .github/workflows/ci.yml | 2 +- tests/ci.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27bd60e..664d755 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: toolchain: stable, nightly components: clippy, rustfmt - name: Cargo Test - run: cargo test --all-features --workspace + run: cargo +nightly test --all-features --workspace - name: Cargo Bench run: cargo bench - name: Cargo Fmt diff --git a/tests/ci.rs b/tests/ci.rs index 6094568..e41fe77 100644 --- a/tests/ci.rs +++ b/tests/ci.rs @@ -16,6 +16,7 @@ fn generate() { ) .add_step( Cargo::new("test") + .nightly() .args("--all-features --workspace") .name("Cargo Test"), ) From 7fb80c0fecae7ad92123aa83a7e22a3b1d66103c Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 15:21:57 -0500 Subject: [PATCH 5/7] drop unused deps --- .github/workflows/ci.yml | 2 +- Cargo.lock | 23 ----------------------- tailcall-jq/Cargo.toml | 3 --- tailcall-jq/src/jsonlike/json_like.rs | 1 - tailcall-jq/src/mustache/parse.rs | 2 -- tests/ci.rs | 1 - 6 files changed, 1 insertion(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 664d755..27bd60e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: toolchain: stable, nightly components: clippy, rustfmt - name: Cargo Test - run: cargo +nightly test --all-features --workspace + run: cargo test --all-features --workspace - name: Cargo Bench run: cargo bench - name: Cargo Fmt diff --git a/Cargo.lock b/Cargo.lock index c0f00d5..9dd57ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,12 +412,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "diff" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" - [[package]] name = "digest" version = "0.10.7" @@ -893,16 +887,6 @@ dependencies = [ "plotters-backend", ] -[[package]] -name = "pretty_assertions" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" -dependencies = [ - "diff", - "yansi", -] - [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1195,7 +1179,6 @@ dependencies = [ "async-graphql-value", "indexmap", "nom", - "pretty_assertions", "serde_json", "serde_json_borrow", ] @@ -1464,9 +1447,3 @@ checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] - -[[package]] -name = "yansi" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/tailcall-jq/Cargo.toml b/tailcall-jq/Cargo.toml index 9c238aa..698f0f2 100644 --- a/tailcall-jq/Cargo.toml +++ b/tailcall-jq/Cargo.toml @@ -11,6 +11,3 @@ indexmap = { workspace = true } serde_json_borrow = { workspace = true } nom = { workspace = true } anyhow = { workspace = true } - -[dev-dependencies] -pretty_assertions = "1.4.1" diff --git a/tailcall-jq/src/jsonlike/json_like.rs b/tailcall-jq/src/jsonlike/json_like.rs index ebccd30..b5d48e4 100644 --- a/tailcall-jq/src/jsonlike/json_like.rs +++ b/tailcall-jq/src/jsonlike/json_like.rs @@ -44,7 +44,6 @@ pub trait JsonObjectLike<'obj>: Sized { #[cfg(test)] mod tests { use crate::jsonlike::group_by_key; - use pretty_assertions::assert_eq; use serde_json::json; use super::super::gather_path_matches; diff --git a/tailcall-jq/src/mustache/parse.rs b/tailcall-jq/src/mustache/parse.rs index 9031572..b699809 100644 --- a/tailcall-jq/src/mustache/parse.rs +++ b/tailcall-jq/src/mustache/parse.rs @@ -85,8 +85,6 @@ fn parse_mustache(input: &str) -> IResult<&str, Mustache> { #[cfg(test)] mod tests { - use pretty_assertions::assert_eq; - use crate::mustache::mustache::{Mustache, Segment}; #[test] diff --git a/tests/ci.rs b/tests/ci.rs index e41fe77..6094568 100644 --- a/tests/ci.rs +++ b/tests/ci.rs @@ -16,7 +16,6 @@ fn generate() { ) .add_step( Cargo::new("test") - .nightly() .args("--all-features --workspace") .name("Cargo Test"), ) From d6418c69234b22f6e59d791f00b183dc35280f82 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 15:29:57 -0500 Subject: [PATCH 6/7] try: fix ci to use code in the PR --- .github/workflows/ci.yml | 2 +- tests/ci.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 27bd60e..f78dc28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ name: Build and Test env: RUSTFLAGS: -Dwarnings on: - pull_request_target: + pull_request: types: - opened - synchronize diff --git a/tests/ci.rs b/tests/ci.rs index 6094568..abcc146 100644 --- a/tests/ci.rs +++ b/tests/ci.rs @@ -35,8 +35,8 @@ fn generate() { let event = Event::default() .push(Push::default().add_branch("main")) - .pull_request_target( - PullRequestTarget::default() + .pull_request( + PullRequest::default() .add_type(PullRequestType::Opened) .add_type(PullRequestType::Synchronize) .add_type(PullRequestType::Reopened) From a619445fc3aaf0548d441c4181f7c51da32af8d6 Mon Sep 17 00:00:00 2001 From: Sandipsinh Rathod Date: Tue, 19 Nov 2024 15:31:45 -0500 Subject: [PATCH 7/7] add `pretty_assertions` back --- Cargo.lock | 23 +++++++++++++++++++++++ tailcall-jq/Cargo.toml | 3 +++ tailcall-jq/src/jsonlike/json_like.rs | 1 + tailcall-jq/src/mustache/parse.rs | 2 ++ 4 files changed, 29 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9dd57ef..c0f00d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,6 +412,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "digest" version = "0.10.7" @@ -887,6 +893,16 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -1179,6 +1195,7 @@ dependencies = [ "async-graphql-value", "indexmap", "nom", + "pretty_assertions", "serde_json", "serde_json_borrow", ] @@ -1447,3 +1464,9 @@ checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/tailcall-jq/Cargo.toml b/tailcall-jq/Cargo.toml index 698f0f2..9c238aa 100644 --- a/tailcall-jq/Cargo.toml +++ b/tailcall-jq/Cargo.toml @@ -11,3 +11,6 @@ indexmap = { workspace = true } serde_json_borrow = { workspace = true } nom = { workspace = true } anyhow = { workspace = true } + +[dev-dependencies] +pretty_assertions = "1.4.1" diff --git a/tailcall-jq/src/jsonlike/json_like.rs b/tailcall-jq/src/jsonlike/json_like.rs index b5d48e4..ebccd30 100644 --- a/tailcall-jq/src/jsonlike/json_like.rs +++ b/tailcall-jq/src/jsonlike/json_like.rs @@ -44,6 +44,7 @@ pub trait JsonObjectLike<'obj>: Sized { #[cfg(test)] mod tests { use crate::jsonlike::group_by_key; + use pretty_assertions::assert_eq; use serde_json::json; use super::super::gather_path_matches; diff --git a/tailcall-jq/src/mustache/parse.rs b/tailcall-jq/src/mustache/parse.rs index b699809..9031572 100644 --- a/tailcall-jq/src/mustache/parse.rs +++ b/tailcall-jq/src/mustache/parse.rs @@ -85,6 +85,8 @@ fn parse_mustache(input: &str) -> IResult<&str, Mustache> { #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; + use crate::mustache::mustache::{Mustache, Segment}; #[test]