diff --git a/README.md b/README.md index 1e269b36..c9b964fb 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,8 @@ So what are they? Our EDN parser. It uses `rust-peg` to parse [EDN](https://github.com/edn-format/edn), which is Clojure/Datomic's richer alternative to JSON. `edn`'s dependencies are all either for representing rich values (`chrono`, `uuid`, `ordered-float`) or for parsing (`serde`, `peg`). +In addition, this crate turns a stream of EDN values into a representation suitable to be transacted. + #### `mentat_core` This is the lowest-level Mentat crate. It collects together the following things: @@ -185,12 +187,6 @@ Similarly, this crate defines an abstract representation of a SQL query as under Mentat has two main inputs: reads (queries) and writes (transacts). Just as `mentat_query` defines the types produced by the query parser, `mentat_tx` defines the types produced by the tx parser. -### Transact processing - -#### `mentat_tx_parser` - -This is a `combine` parser that turns a stream of EDN values into a representation suitable to be transacted. - ### Query processing #### `mentat_query_parser` diff --git a/db/Cargo.toml b/db/Cargo.toml index 0dd92f43..438f558a 100644 --- a/db/Cargo.toml +++ b/db/Cargo.toml @@ -29,9 +29,6 @@ path = "../sql" [dependencies.mentat_tx] path = "../tx" -[dependencies.mentat_tx_parser] -path = "../tx-parser" - # Should be dev-dependencies. [dependencies.tabwriter] version = "1.0.3" diff --git a/db/src/bootstrap.rs b/db/src/bootstrap.rs index 1d8f4418..758c2127 100644 --- a/db/src/bootstrap.rs +++ b/db/src/bootstrap.rs @@ -17,7 +17,6 @@ use edn::symbols; use entids; use db::TypedSQLValue; use mentat_tx::entities::Entity; -use mentat_tx_parser; use mentat_core::{ IdentMap, Schema, @@ -300,6 +299,6 @@ pub(crate) fn bootstrap_entities() -> Vec { // Failure here is a coding error (since the inputs are fixed), not a runtime error. // TODO: represent these bootstrap data errors rather than just panicing. - let bootstrap_entities: Vec = mentat_tx_parser::Tx::parse(&bootstrap_assertions.with_spans()).unwrap(); + let bootstrap_entities: Vec = edn::parse::entities(&bootstrap_assertions.to_string()).unwrap(); return bootstrap_entities; } diff --git a/db/src/db.rs b/db/src/db.rs index 0be561d0..58729ccb 100644 --- a/db/src/db.rs +++ b/db/src/db.rs @@ -1159,7 +1159,6 @@ mod tests { Schema, attribute, }; - use mentat_tx_parser; use rusqlite; use std::collections::{ BTreeMap, @@ -1217,8 +1216,7 @@ mod tests { fn transact(&mut self, transaction: I) -> Result where I: Borrow { // Failure to parse the transaction is a coding error, so we unwrap. - let assertions = edn::parse::value(transaction.borrow()).expect(format!("to be able to parse {} into EDN", transaction.borrow()).as_str()); - let entities: Vec<_> = mentat_tx_parser::Tx::parse(&assertions).expect(format!("to be able to parse {} into entities", assertions).as_str()); + let entities = edn::parse::entities(transaction.borrow()).expect(format!("to be able to parse {} into entities", transaction.borrow()).as_str()); let details = { // The block scopes the borrow of self.sqlite. diff --git a/db/src/errors.rs b/db/src/errors.rs index a90982d0..4d7b20bf 100644 --- a/db/src/errors.rs +++ b/db/src/errors.rs @@ -21,7 +21,6 @@ use rusqlite; use mentat_tx::entities::{ TempId, }; -use mentat_tx_parser; use mentat_core::{ KnownEntid, }; @@ -60,6 +59,30 @@ impl ::std::fmt::Display for SchemaConstraintViolation { } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum InputError { + /// Map notation included a bad `:db/id` value. + BadDbId, + + /// A value place cannot be interpreted as an entity place (for example, in nested map + /// notation). + BadEntityPlace, +} + +impl ::std::fmt::Display for InputError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + use self::InputError::*; + match self { + &BadDbId => { + writeln!(f, ":db/id in map notation must either not be present or be an entid, an ident, or a tempid") + }, + &BadEntityPlace => { + writeln!(f, "cannot convert value place into entity place") + }, + } + } +} + error_chain! { types { Error, ErrorKind, ResultExt, Result; @@ -69,10 +92,6 @@ error_chain! { Rusqlite(rusqlite::Error); } - links { - TxParseError(mentat_tx_parser::Error, mentat_tx_parser::ErrorKind); - } - errors { /// We're just not done yet. Message that the feature is recognized but not yet /// implemented. @@ -152,5 +171,12 @@ error_chain! { description("schema constraint violation") display("schema constraint violation: {}", violation) } + + /// The transaction was malformed in some way (that was not recognized at parse time; for + /// example, in a way that is schema-dependent). + InputError(error: InputError) { + description("transaction input error") + display("transaction input error: {}", error) + } } } diff --git a/db/src/internal_types.rs b/db/src/internal_types.rs index beb17196..6766d831 100644 --- a/db/src/internal_types.rs +++ b/db/src/internal_types.rs @@ -86,12 +86,12 @@ impl TransactableValue for ValueAndSpan { (Some(&PlainSymbol(edn::PlainSymbol(ref s))), Some(a), Some(v), None) if s == "lookup-ref" => { match a.clone().into_entity_place()? { EntidOrLookupRefOrTempId::Entid(a) => Ok(EntidOrLookupRefOrTempId::LookupRef(entities::LookupRef { a, v: v.clone().without_spans() })), - EntidOrLookupRefOrTempId::TempId(_) => bail!(""), - EntidOrLookupRefOrTempId::TxFunction(_) => bail!(""), - EntidOrLookupRefOrTempId::LookupRef(_) => bail!(""), + EntidOrLookupRefOrTempId::TempId(_) | + EntidOrLookupRefOrTempId::TxFunction(_) | + EntidOrLookupRefOrTempId::LookupRef(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)), } }, - _ => bail!(ErrorKind::NotYetImplemented("cannot convert value place into entity place".into())) + _ => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)), } }, Nil | @@ -105,7 +105,7 @@ impl TransactableValue for ValueAndSpan { Keyword(_) | Vector(_) | Set(_) | - Map(_) => bail!(ErrorKind::NotYetImplemented("cannot convert value place into entity place".into())) + Map(_) => bail!(ErrorKind::InputError(errors::InputError::BadEntityPlace)), } } diff --git a/db/src/lib.rs b/db/src/lib.rs index b87619e4..9652a342 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -26,7 +26,6 @@ extern crate time; #[macro_use] extern crate mentat_core; extern crate mentat_sql; extern crate mentat_tx; -extern crate mentat_tx_parser; use std::iter::repeat; diff --git a/db/src/tx.rs b/db/src/tx.rs index 15a001f4..e48f132b 100644 --- a/db/src/tx.rs +++ b/db/src/tx.rs @@ -174,7 +174,7 @@ pub fn remove_db_id(map: &mut entmod::MapNotation) -> Result { - bail!(ErrorKind::NotYetImplemented("db id error".into())) + bail!(ErrorKind::InputError(errors::InputError::BadDbId)) }, } } else { diff --git a/edn/src/edn.rustpeg b/edn/src/edn.rustpeg index 729d8176..4d1d5d82 100644 --- a/edn/src/edn.rustpeg +++ b/edn/src/edn.rustpeg @@ -23,6 +23,8 @@ use num::BigInt; use ordered_float::OrderedFloat; use uuid::Uuid; +use entities::*; +use symbols::*; use types::{SpannedValue, Span, ValueAndSpan}; // Goal: Be able to parse https://github.com/edn-format/edn @@ -51,21 +53,25 @@ validbase = [3][0-6] / [12][0-9] / [2-9] hex = [0-9a-fA-F] sign = [+-] -pub bigint -> SpannedValue = b:$( sign? digit+ ) "N" - { SpannedValue::BigInteger(b.parse::().unwrap()) } -pub octalinteger -> SpannedValue = "0" i:$( octaldigit+ ) - { SpannedValue::Integer(i64::from_str_radix(i, 8).unwrap()) } -pub hexinteger -> SpannedValue = "0x" i:$( hex+ ) - { SpannedValue::Integer(i64::from_str_radix(i, 16).unwrap()) } +pub raw_bigint -> BigInt = b:$( sign? digit+ ) "N" + { b.parse::().unwrap() } +pub raw_octalinteger -> i64 = "0" i:$( octaldigit+ ) + { i64::from_str_radix(i, 8).unwrap() } +pub raw_hexinteger -> i64 = "0x" i:$( hex+ ) + { i64::from_str_radix(i, 16).unwrap() } +pub raw_basedinteger -> i64 = b:$( validbase ) "r" i:$( alphanumeric+ ) + { i64::from_str_radix(i, b.parse::().unwrap()).unwrap() } +pub raw_integer -> i64 = i:$( sign? digit+ ) !("." / ([eE])) + { i.parse::().unwrap() } +pub raw_float -> OrderedFloat = f:$(sign? digit+ ("." digit+)? ([eE] sign? digit+)?) + { OrderedFloat(f.parse::().unwrap()) } -pub basedinteger -> SpannedValue = b:$( validbase ) "r" i:$( alphanumeric+ ) - { SpannedValue::Integer(i64::from_str_radix(i, b.parse::().unwrap()).unwrap()) } - -pub integer -> SpannedValue = i:$( sign? digit+ ) !("." / ([eE])) - { SpannedValue::Integer(i.parse::().unwrap()) } - -pub float -> SpannedValue = f:$(sign? digit+ ("." digit+)? ([eE] sign? digit+)?) - { SpannedValue::Float(OrderedFloat(f.parse::().unwrap())) } +pub bigint -> SpannedValue = v:raw_bigint { SpannedValue::BigInteger(v) } +pub octalinteger -> SpannedValue = v:raw_octalinteger { SpannedValue::Integer(v) } +pub hexinteger -> SpannedValue = v:raw_hexinteger { SpannedValue::Integer(v) } +pub basedinteger -> SpannedValue = v:raw_basedinteger { SpannedValue::Integer(v) } +pub integer -> SpannedValue = v:raw_integer { SpannedValue::Integer(v) } +pub float -> SpannedValue = v:raw_float { SpannedValue::Float(v) } number -> SpannedValue = ( bigint / basedinteger / hexinteger / octalinteger / integer / float ) @@ -81,8 +87,11 @@ string_normal_chars -> &'input str = $([^"\\]+) // output = [quote, "foo", backslash, "bar", quote] // result = r#""foo\\bar""# // For the typical case, string_normal_chars will match multiple, leading to a single-element vec. -pub text -> SpannedValue = "\"" t:((string_special_char / string_normal_chars)*) "\"" - { SpannedValue::Text(t.join(&"").to_string()) } +pub raw_text -> String = "\"" t:((string_special_char / string_normal_chars)*) "\"" + { t.join(&"").to_string() } + +pub text -> SpannedValue + = v:raw_text { SpannedValue::Text(v) } // RFC 3339 timestamps. #inst "1985-04-12T23:20:50.52Z" // We accept an arbitrary depth of decimals. @@ -180,9 +189,69 @@ pub value -> ValueAndSpan = } } +atom -> ValueAndSpan + = v:value {? if v.is_atom() { Ok(v) } else { Err("expected atom") } } + // Clojure (and thus EDN) regards commas as whitespace, and thus the two-element vectors [1 2] and // [1,,,,2] are equivalent, as are the maps {:a 1, :b 2} and {:a 1 :b 2}. -whitespace = [ \r\n\t,] -comment = ";" [^\r\n]* [\r\n]? +whitespace = #quiet<[ \r\n\t,]> +comment = #quiet<";" [^\r\n]* [\r\n]?> __ = (whitespace / comment)* + +pub op -> OpType + = ":db/add" { OpType::Add } + / ":db/retract" { OpType::Retract } + +raw_keyword -> NamespacedKeyword + = keyword_prefix ns:$(symbol_namespace) namespace_separator n:$(symbol_name) { NamespacedKeyword::new(ns, n) } + +raw_forward_keyword -> NamespacedKeyword + = v:raw_keyword {? if v.is_forward() { Ok(v) } else { Err("expected :forward/keyword") } } + +raw_backward_keyword -> NamespacedKeyword + = v:raw_keyword {? if v.is_backward() { Ok(v) } else { Err("expected :backward/_keyword") } } + +entid -> Entid + = v:( raw_basedinteger / raw_hexinteger / raw_octalinteger / raw_integer ) { Entid::Entid(v) } + / v:raw_keyword { Entid::Ident(v) } + +forward_entid -> Entid + = v:( raw_basedinteger / raw_hexinteger / raw_octalinteger / raw_integer ) { Entid::Entid(v) } + / v:raw_forward_keyword { Entid::Ident(v) } + +backward_entid -> Entid + = v:raw_backward_keyword { Entid::Ident(v.to_reversed()) } + +lookup_ref -> LookupRef + = "(" __ "lookup-ref" __ a:(entid) __ v:(value) __ ")" { LookupRef { a, v: v.without_spans() } } + +tx_function -> TxFunction + = "(" __ n:$(symbol_name) __ ")" { TxFunction { op: PlainSymbol::new(n) } } + +entity_place -> EntidOrLookupRefOrTempId + = v:raw_text { EntidOrLookupRefOrTempId::TempId(TempId::External(v)) } + / v:entid { EntidOrLookupRefOrTempId::Entid(v) } + / v:lookup_ref { EntidOrLookupRefOrTempId::LookupRef(v) } + / v:tx_function { EntidOrLookupRefOrTempId::TxFunction(v) } + +value_place_pair -> (Entid, AtomOrLookupRefOrVectorOrMapNotation) + = k:(entid) __ v:(value_place) { (k, v) } + +map_notation -> MapNotation + = "{" __ kvs:(value_place_pair*) __ "}" { kvs.into_iter().collect() } + +value_place -> AtomOrLookupRefOrVectorOrMapNotation + = __ v:lookup_ref __ { AtomOrLookupRefOrVectorOrMapNotation::LookupRef(v) } + / __ v:tx_function __ { AtomOrLookupRefOrVectorOrMapNotation::TxFunction(v) } + / __ "[" __ vs:(value_place*) __ "]" __ { AtomOrLookupRefOrVectorOrMapNotation::Vector(vs) } + / __ v:map_notation __ { AtomOrLookupRefOrVectorOrMapNotation::MapNotation(v) } + / __ v:atom __ { AtomOrLookupRefOrVectorOrMapNotation::Atom(v) } + +pub entity -> Entity + = __ "[" __ op:(op) __ e:(entity_place) __ a:(forward_entid) __ v:(value_place) __ "]" __ { Entity::AddOrRetract { op, e: e, a, v: v } } + / __ "[" __ op:(op) __ e:(value_place) __ a:(backward_entid) __ v:(entity_place) __ "]" __ { Entity::AddOrRetract { op, e: v, a, v: e } } + / __ map:map_notation __ { Entity::MapNotation(map) } + +pub entities -> Vec + = __ "[" __ es:(entity*) __ "]" __ { es } diff --git a/tx/src/entities.rs b/edn/src/entities.rs similarity index 94% rename from tx/src/entities.rs rename to edn/src/entities.rs index 091ad1f0..4eba1912 100644 --- a/tx/src/entities.rs +++ b/edn/src/entities.rs @@ -10,14 +10,19 @@ //! This module defines core types that support the transaction processor. -extern crate edn; - use std::collections::BTreeMap; use std::fmt; -use self::edn::symbols::NamespacedKeyword; +use symbols::{ + NamespacedKeyword, + PlainSymbol, +}; +use types::{ + Value, + ValueAndSpan, +}; -/// A tempid, either an external tempid given in a transaction (usually as an `edn::Value::Text`), +/// A tempid, either an external tempid given in a transaction (usually as an `Value::Text`), /// or an internal tempid allocated by Mentat itself. #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub enum TempId { @@ -63,7 +68,7 @@ pub struct LookupRef { pub a: Entid, // In theory we could allow nested lookup-refs. In practice this would require us to process // lookup-refs in multiple phases, like how we resolve tempids, which isn't worth the effort. - pub v: edn::Value, // An atom. + pub v: Value, // An atom. } /// A "transaction function" that exposes some value determined by the current transaction. The @@ -80,14 +85,14 @@ pub struct LookupRef { /// generalization. #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub struct TxFunction { - pub op: edn::PlainSymbol, + pub op: PlainSymbol, } pub type MapNotation = BTreeMap; #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub enum AtomOrLookupRefOrVectorOrMapNotation { - Atom(edn::ValueAndSpan), + Atom(ValueAndSpan), LookupRef(LookupRef), TxFunction(TxFunction), Vector(Vec), diff --git a/edn/src/lib.rs b/edn/src/lib.rs index 53f461d7..d1e382e2 100644 --- a/edn/src/lib.rs +++ b/edn/src/lib.rs @@ -22,6 +22,7 @@ extern crate serde; #[macro_use] extern crate serde_derive; +pub mod entities; pub mod symbols; pub mod types; pub mod pretty_print; diff --git a/edn/src/types.rs b/edn/src/types.rs index 10419bf6..549dfb71 100644 --- a/edn/src/types.rs +++ b/edn/src/types.rs @@ -110,6 +110,10 @@ impl ValueAndSpan { } } + pub fn is_atom(&self) -> bool { + self.inner.is_atom() + } + pub fn as_atom(&self) -> Option<&ValueAndSpan> { if self.inner.is_atom() { Some(self) diff --git a/src/conn.rs b/src/conn.rs index f27d43f7..1a965f7d 100644 --- a/src/conn.rs +++ b/src/conn.rs @@ -10,6 +10,10 @@ #![allow(dead_code)] +use std::borrow::{ + Borrow, +}; + use std::collections::{ BTreeMap, }; @@ -85,8 +89,6 @@ use mentat_tx::entities::{ OpType, }; -use mentat_tx_parser; - use mentat_tolstoy::Syncer; use uuid::Uuid; @@ -494,9 +496,8 @@ impl<'a, 'c> InProgress<'a, 'c> { Ok(report) } - pub fn transact(&mut self, transaction: &str) -> Result { - let assertion_vector = edn::parse::value(transaction)?; - let entities = mentat_tx_parser::Tx::parse(&assertion_vector)?; + pub fn transact(&mut self, transaction: B) -> Result where B: Borrow { + let entities = edn::parse::entities(transaction.borrow())?; self.transact_entities(entities) } @@ -912,15 +913,14 @@ impl Conn { /// Transact entities against the Mentat store, using the given connection and the current /// metadata. - pub fn transact(&mut self, + pub fn transact(&mut self, sqlite: &mut rusqlite::Connection, - transaction: &str) -> Result { + transaction: B) -> Result where B: Borrow { // Parse outside the SQL transaction. This is a tradeoff: we are limiting the scope of the // transaction, and indeed we don't even create a SQL transaction if the provided input is // invalid, but it means SQLite errors won't be found until the parse is complete, and if // there's a race for the database (don't do that!) we are less likely to win it. - let assertion_vector = edn::parse::value(transaction)?; - let entities = mentat_tx_parser::Tx::parse(&assertion_vector)?; + let entities = edn::parse::entities(transaction.borrow())?; let mut in_progress = self.begin_transaction(sqlite)?; let report = in_progress.transact_entities(entities)?; @@ -1248,7 +1248,7 @@ mod tests { // Bad transaction data: missing leading :db/add. let report = conn.transact(&mut sqlite, "[[\"t\" :db/ident :b/keyword]]"); match report.unwrap_err() { - Error(ErrorKind::TxParseError(::mentat_tx_parser::errors::ErrorKind::ParseError(_)), _) => { }, + Error(ErrorKind::EdnParseError(_), _) => { }, x => panic!("expected EDN parse error, got {:?}", x), } diff --git a/src/errors.rs b/src/errors.rs index 42ea0c1c..679388fd 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -30,7 +30,6 @@ use mentat_query_pull; use mentat_query_translator; use mentat_sql; use mentat_tolstoy; -use mentat_tx_parser; error_chain! { types { @@ -52,7 +51,6 @@ error_chain! { PullError(mentat_query_pull::errors::Error, mentat_query_pull::errors::ErrorKind); TranslatorError(mentat_query_translator::Error, mentat_query_translator::ErrorKind); SqlError(mentat_sql::Error, mentat_sql::ErrorKind); - TxParseError(mentat_tx_parser::Error, mentat_tx_parser::ErrorKind); SyncError(mentat_tolstoy::Error, mentat_tolstoy::ErrorKind); } diff --git a/src/lib.rs b/src/lib.rs index 7bc81013..79029994 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,7 +32,6 @@ extern crate mentat_query_translator; extern crate mentat_sql; extern crate mentat_tolstoy; extern crate mentat_tx; -extern crate mentat_tx_parser; pub use mentat_core::{ Attribute, diff --git a/tests/query.rs b/tests/query.rs index 1ad0b02d..8a981bc8 100644 --- a/tests/query.rs +++ b/tests/query.rs @@ -742,7 +742,7 @@ fn test_type_reqs() { {:db/ident :test/long2 :db/valueType :db.type/long :db/cardinality :db.cardinality/one} ]"#).unwrap(); - conn.transact(&mut c, &format!("[[:db/add {} :test/long2 5]]", entid)).unwrap(); + conn.transact(&mut c, format!("[[:db/add {} :test/long2 5]]", entid)).unwrap(); let longs_query = r#"[:find [?v ...] :order (asc ?v) :in ?e diff --git a/tools/cli/src/mentat_cli/repl.rs b/tools/cli/src/mentat_cli/repl.rs index 119c7ebc..db88881a 100644 --- a/tools/cli/src/mentat_cli/repl.rs +++ b/tools/cli/src/mentat_cli/repl.rs @@ -511,7 +511,7 @@ impl Repl { fn transact(&mut self, transaction: String) -> ::mentat::errors::Result { let mut tx = self.store.begin_transaction()?; - let report = tx.transact(&transaction)?; + let report = tx.transact(transaction)?; tx.commit()?; Ok(report) } diff --git a/tx-parser/benches.txt b/tx-parser/benches.txt new file mode 100644 index 00000000..ee8eab4f --- /dev/null +++ b/tx-parser/benches.txt @@ -0,0 +1,81 @@ +Before slices, release: + +⋊> ~/M/mentat on ⨯ cargo bench --package mentat_tx_parser + Finished release [optimized] target(s) in 0.0 secs + Running target/release/deps/bench-0defa345d586a763 + +running 2 tests +test bench_parse1 ... bench: 7,745 ns/iter (+/- 2,515) +test bench_parse2 ... bench: 494,244 ns/iter (+/- 159,379) + +test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured + + Running target/release/deps/mentat_tx_parser-c3f614f12d05a17a + +running 5 tests +test tests::test_add ... ignored +test tests::test_lookup_ref ... ignored +test tests::test_map_notation ... ignored +test tests::test_nested_vector ... ignored +test tests::test_retract ... ignored + +test result: ok. 0 passed; 0 failed; 5 ignored; 0 measured + +⋊> ~/M/mentat on ⨯ cargo bench --package mentat_tx_parser + Finished release [optimized] target(s) in 0.0 secs + Running target/release/deps/bench-0defa345d586a763 + +running 2 tests +test bench_parse1 ... bench: 7,793 ns/iter (+/- 1,258) +test bench_parse2 ... bench: 532,144 ns/iter (+/- 110,614) + +test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured + +After slices, release: + +⋊> ~/M/mentat on parse-faster ⨯ cargo bench --package mentat_tx_parser 16:10:57 + Compiling mentat_tx_parser v0.0.1 (file:///Users/nalexander/Mozilla/mentat/tx-parser) + Finished release [optimized + debuginfo] target(s) in 2.25 secs + Running target/release/deps/bench-0defa345d586a763 + +running 3 tests +test bench_parse1 ... bench: 1,413 ns/iter (+/- 92) +test bench_parse2 ... bench: 26,190 ns/iter (+/- 4,167) +test bench_parse3 ... bench: 51,823 ns/iter (+/- 7,000) + +test result: ok. 0 passed; 0 failed; 0 ignored; 3 measured + + Running target/release/deps/mentat_tx_parser-c3f614f12d05a17a + +running 1 test +test tests::test_add ... ignored + +test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured + +⋊> ~/M/mentat on parse-faster ⨯ cargo bench --package mentat_tx_parser 16:16:35 + Finished release [optimized + debuginfo] target(s) in 0.0 secs + Running target/release/deps/bench-0defa345d586a763 + +running 3 tests +test bench_parse1 ... bench: 1,410 ns/iter (+/- 164) +test bench_parse2 ... bench: 26,195 ns/iter (+/- 1,851) +test bench_parse3 ... bench: 51,680 ns/iter (+/- 12,190) + +test result: ok. 0 passed; 0 failed; 0 ignored; 3 measured + + Running target/release/deps/mentat_tx_parser-c3f614f12d05a17a + +running 1 test +test tests::test_add ... ignored + +test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured + +test bench_parse1 ... bench: 7,690 ns/iter (+/- 3,035) +test bench_parse2 ... bench: 548,920 ns/iter (+/- 282,846) +test bench_parse3 ... bench: 1,757,897 ns/iter (+/- 301,719) +test bench_parse4 ... bench: 6,957,627 ns/iter (+/- 1,162,660) + +test bench_parse1 ... bench: 1,038 ns/iter (+/- 213) +test bench_parse2 ... bench: 18,647 ns/iter (+/- 3,971) +test bench_parse3 ... bench: 36,715 ns/iter (+/- 6,508) +test bench_parse4 ... bench: 79,502 ns/iter (+/- 19,525) diff --git a/tx-parser/benches/bench.rs b/tx-parser/benches/bench.rs index 9445b028..91605f83 100644 --- a/tx-parser/benches/bench.rs +++ b/tx-parser/benches/bench.rs @@ -14,7 +14,7 @@ use mentat_tx_parser::Tx; fn bench_parse1(b: &mut Bencher) { let input = r#"[[:db/add 1 :test/val "a"]]"#; let parsed_edn = edn::parse::value(input).expect("to parse test input"); - b.iter(|| Tx::parse(parsed_edn.clone())); + b.iter(|| Tx::parse(&parsed_edn)); } #[bench] @@ -46,6 +46,42 @@ fn bench_parse2(b: &mut Bencher) { [:db/add 24 :test/val "x"] [:db/add 25 :test/val "y"] [:db/add 26 :test/val "z"]]"#; - let parsed_edn = edn::parse::value(input).expect("to parse test input"); - b.iter(|| Tx::parse(parsed_edn.clone())); + b.iter(|| { + let parsed_edn = edn::parse::value(input).expect("to parse test input"); + Tx::parse(&parsed_edn).expect("to parse tx"); + }); +} + +#[bench] +fn bench_parse3(b: &mut Bencher) { + let input = r#" + [[:db/add 1 :test/val "a"] + [:db/add 2 :test/val "b"] + [:db/add 3 :test/val "c"] + [:db/add 4 :test/val "d"] + [:db/add 5 :test/val "e"] + [:db/add 6 :test/val "f"] + [:db/add 7 :test/val "g"] + [:db/add 8 :test/val "h"] + [:db/add 9 :test/val "i"] + [:db/add 10 :test/val "j"] + [:db/add 11 :test/val "k"] + [:db/add 12 :test/val "l"] + [:db/add 13 :test/val "m"] + [:db/add 14 :test/val "n"] + [:db/add 15 :test/val "o"] + [:db/add 16 :test/val "p"] + [:db/add 17 :test/val "q"] + [:db/add 18 :test/val "r"] + [:db/add 19 :test/val "s"] + [:db/add 20 :test/val "t"] + [:db/add 21 :test/val "u"] + [:db/add 22 :test/val "v"] + [:db/add 23 :test/val "w"] + [:db/add 24 :test/val "x"] + [:db/add 25 :test/val "y"] + [:db/add 26 :test/val "z"]]"#; + b.iter(|| { + edn::parse::entities(input).expect("to parse test input"); + }); } diff --git a/tx-parser/benches2.txt b/tx-parser/benches2.txt new file mode 100644 index 00000000..94a9c1f3 --- /dev/null +++ b/tx-parser/benches2.txt @@ -0,0 +1,17 @@ + +running 5 tests +test tests::test_add ... ignored +test tests::test_lookup_ref ... ignored +test tests::test_map_notation ... ignored +test tests::test_nested_vector ... ignored +test tests::test_retract ... ignored + +test result: ok. 0 passed; 0 failed; 5 ignored; 0 measured; 0 filtered out + + +running 3 tests +test bench_parse1 ... bench: 2,799 ns/iter (+/- 801) +test bench_parse2 ... bench: 191,856 ns/iter (+/- 19,331) +test bench_parse3 ... bench: 53,925 ns/iter (+/- 10,299) + +test result: ok. 0 passed; 0 failed; 0 ignored; 3 measured; 0 filtered out diff --git a/tx/src/lib.rs b/tx/src/lib.rs index 78eac748..ad503e05 100644 --- a/tx/src/lib.rs +++ b/tx/src/lib.rs @@ -8,4 +8,6 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. -pub mod entities; +#[allow(unused_imports)] +#[macro_use] extern crate edn; +pub use edn::entities;