From 896d7f8f88bc65a5c922dad967768c9d9cf2af76 Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Tue, 14 Feb 2017 16:43:32 +0100 Subject: [PATCH] Add a span component to edn::Value, r=ncalexan Signed-off-by: Victor Porof --- db/src/bootstrap.rs | 3 + db/src/db.rs | 6 +- edn/src/edn.rustpeg | 183 ++++++++---- edn/src/types.rs | 571 ++++++++++++++++++++++++-------------- edn/tests/tests.rs | 340 +++++++++++++++++++++-- query-parser/src/find.rs | 20 +- query-parser/src/lib.rs | 2 + tx-parser/tests/parser.rs | 2 +- 8 files changed, 819 insertions(+), 308 deletions(-) diff --git a/db/src/bootstrap.rs b/db/src/bootstrap.rs index e7efd07b..d6a23a64 100644 --- a/db/src/bootstrap.rs +++ b/db/src/bootstrap.rs @@ -127,6 +127,7 @@ lazy_static! { :db/noHistory {:db/valueType :db.type/boolean :db/cardinality :db.cardinality/one}}"#; edn::parse::value(s) + .map(|v| v.without_spans()) .map_err(|_| ErrorKind::BadBootstrapDefinition("Unable to parse V1_SYMBOLIC_SCHEMA".into())) .unwrap() }; @@ -144,8 +145,10 @@ lazy_static! { :db/unique :db.unique/value :db/cardinality :db.cardinality/many}}"#; let right = edn::parse::value(s) + .map(|v| v.without_spans()) .map_err(|_| ErrorKind::BadBootstrapDefinition("Unable to parse V2_SYMBOLIC_SCHEMA".into())) .unwrap(); + edn::utils::merge(&V1_SYMBOLIC_SCHEMA, &right) .ok_or(ErrorKind::BadBootstrapDefinition("Unable to parse V2_SYMBOLIC_SCHEMA".into())) .unwrap() diff --git a/db/src/db.rs b/db/src/db.rs index 9aa6b105..1067e629 100644 --- a/db/src/db.rs +++ b/db/src/db.rs @@ -954,7 +954,7 @@ mod tests { assert_eq!(transactions.0[0].0.len(), 89); // TODO: extract a test macro simplifying this boilerplate yet further. - let value = edn::parse::value(include_str!("../../tx/fixtures/test_add.edn")).unwrap(); + let value = edn::parse::value(include_str!("../../tx/fixtures/test_add.edn")).unwrap().without_spans(); let transactions = value.as_vector().unwrap(); assert_transactions(&conn, &mut db, transactions); @@ -974,7 +974,7 @@ mod tests { assert_eq!(transactions.0.len(), 1); assert_eq!(transactions.0[0].0.len(), 89); - let value = edn::parse::value(include_str!("../../tx/fixtures/test_retract.edn")).unwrap(); + let value = edn::parse::value(include_str!("../../tx/fixtures/test_retract.edn")).unwrap().without_spans(); let transactions = value.as_vector().unwrap(); assert_transactions(&conn, &mut db, transactions); @@ -994,7 +994,7 @@ mod tests { assert_eq!(transactions.0.len(), 1); assert_eq!(transactions.0[0].0.len(), 89); - let value = edn::parse::value(include_str!("../../tx/fixtures/test_upsert_vector.edn")).unwrap(); + let value = edn::parse::value(include_str!("../../tx/fixtures/test_upsert_vector.edn")).unwrap().without_spans(); let transactions = value.as_vector().unwrap(); assert_transactions(&conn, &mut db, transactions); diff --git a/edn/src/edn.rustpeg b/edn/src/edn.rustpeg index 7363f05a..880bb850 100644 --- a/edn/src/edn.rustpeg +++ b/edn/src/edn.rustpeg @@ -16,8 +16,8 @@ use std::f64::{NAN, INFINITY, NEG_INFINITY}; use num::BigInt; use ordered_float::OrderedFloat; -use types; -use types::Value; + +use types::{SpannedValue, Span, ValueAndSpan}; // Goal: Be able to parse https://github.com/edn-format/edn // Also extensible to help parse http://docs.datomic.com/query.html @@ -28,20 +28,43 @@ use types::Value; // TODO: Support tagged elements // TODO: Support discard -pub nil -> Value = - "nil" { Value::Nil } - -pub nan -> Value = - "#f" whitespace+ "NaN" { Value::Float(OrderedFloat(NAN)) } - -pub infinity -> Value = - "#f" whitespace+ s:$(sign) "Infinity" { - Value::Float(OrderedFloat(if s == "+" { INFINITY } else { NEG_INFINITY })) +pub nil -> ValueAndSpan = + start:#position "nil" end:#position { + ValueAndSpan { + inner: SpannedValue::Nil, + span: Span(start, end) + } } -pub boolean -> Value = - "true" { Value::Boolean(true) } / - "false" { Value::Boolean(false) } +pub nan -> ValueAndSpan = + start:#position "#f" whitespace+ "NaN" end:#position { + ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(NAN)), + span: Span(start, end) + } + } + +pub infinity -> ValueAndSpan = + start:#position "#f" whitespace+ s:$(sign) "Infinity" end:#position { + ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(if s == "+" { INFINITY } else { NEG_INFINITY })), + span: Span(start, end) + } + } + +pub boolean -> ValueAndSpan = + start:#position "true" end:#position { + ValueAndSpan { + inner: SpannedValue::Boolean(true), + span: Span(start, end) + } + } / + start:#position "false" end:#position { + ValueAndSpan { + inner: SpannedValue::Boolean(false), + span: Span(start, end) + } + } digit = [0-9] alphanumeric = [0-9a-zA-Z] @@ -50,30 +73,45 @@ validbase = [3][0-6] / [12][0-9] / [2-9] hex = [0-9a-fA-F] sign = "-" / "+" -pub bigint -> Value = - b:$( sign? digit+ ) "N" { - Value::BigInteger(b.parse::().unwrap()) +pub bigint -> ValueAndSpan = + start:#position b:$( sign? digit+ ) "N" end:#position { + ValueAndSpan { + inner: SpannedValue::BigInteger(b.parse::().unwrap()), + span: Span(start, end) + } } -pub octalinteger -> Value = - "0" i:$( octaldigit+ ) { - Value::Integer(i64::from_str_radix(i, 8).unwrap()) +pub octalinteger -> ValueAndSpan = + start:#position "0" i:$( octaldigit+ ) end:#position { + ValueAndSpan { + inner: SpannedValue::Integer(i64::from_str_radix(i, 8).unwrap()), + span: Span(start, end) + } } -pub hexinteger -> Value = - "0x" i:$( hex+ ) { - Value::Integer(i64::from_str_radix(i, 16).unwrap()) +pub hexinteger -> ValueAndSpan = + start:#position "0x" i:$( hex+ ) end:#position { + ValueAndSpan { + inner: SpannedValue::Integer(i64::from_str_radix(i, 16).unwrap()), + span: Span(start, end) + } } -pub basedinteger -> Value = +pub basedinteger -> ValueAndSpan = // Only allow values 2-36 - b:$( validbase ) "r" i:$( alphanumeric+ ) { - Value::Integer(i64::from_str_radix(i, b.parse::().unwrap()).unwrap()) + start:#position b:$( validbase ) "r" i:$( alphanumeric+ ) end:#position { + ValueAndSpan { + inner: SpannedValue::Integer(i64::from_str_radix(i, b.parse::().unwrap()).unwrap()), + span: Span(start, end) + } } -pub integer -> Value = - i:$( sign? digit+ ) { - Value::Integer(i.parse::().unwrap()) +pub integer -> ValueAndSpan = + start:#position i:$( sign? digit+ ) end:#position { + ValueAndSpan { + inner: SpannedValue::Integer(i.parse::().unwrap()), + span: Span(start, end) + } } frac = sign? digit+ "." digit+ @@ -82,9 +120,12 @@ frac_exp = sign? digit+ "." digit+ ("e" / "E") sign? digit+ // The order here is important - frac_exp must come before (exp / frac) or the // parser assumes exp or frac when the float is really a frac_exp and fails -pub float -> Value = - f:$( frac_exp / exp / frac ) { - Value::Float(OrderedFloat(f.parse::().unwrap())) +pub float -> ValueAndSpan = + start:#position f:$( frac_exp / exp / frac ) end:#position { + ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(f.parse::().unwrap())), + span: Span(start, end) + } } // TODO: \newline, \return, \space and \tab @@ -93,9 +134,12 @@ quote = "\\\"" tab = "\\tab" char = [^"] / special_char -pub text -> Value = - "\"" t:$( char* ) "\"" { - Value::Text(t.to_string()) +pub text -> ValueAndSpan = + start:#position "\"" t:$( char* ) "\"" end:#position { + ValueAndSpan { + inner: SpannedValue::Text(t.to_string()), + span: Span(start, end) + } } namespace_divider = "." @@ -112,48 +156,69 @@ symbol_name = ( symbol_char_initial+ / "." ) ( symbol_char_subsequent* / "." ) keyword_prefix = ":" -pub symbol -> Value = - ns:( sns:$(symbol_namespace) namespace_separator { - sns - })? n:$(symbol_name) { - types::to_symbol(ns, n) +pub symbol -> ValueAndSpan = + start:#position + ns:( sns:$(symbol_namespace) namespace_separator { sns })? + n:$(symbol_name) + end:#position { + ValueAndSpan { + inner: SpannedValue::from_symbol(ns, n), + span: Span(start, end) + } } -pub keyword -> Value = - keyword_prefix ns:( sns:$(symbol_namespace) namespace_separator { - sns - })? n:$(symbol_name) { - types::to_keyword(ns, n) +pub keyword -> ValueAndSpan = + start:#position + keyword_prefix + ns:( sns:$(symbol_namespace) namespace_separator { sns })? + n:$(symbol_name) + end:#position { + ValueAndSpan { + inner: SpannedValue::from_keyword(ns, n), + span: Span(start, end) + } } -pub list -> Value = - "(" __ v:(value)* __ ")" { - Value::List(LinkedList::from_iter(v)) +pub list -> ValueAndSpan = + start:#position "(" __ v:(value)* __ ")" end:#position { + ValueAndSpan { + inner: SpannedValue::List(LinkedList::from_iter(v)), + span: Span(start, end) + } } -pub vector -> Value = - "[" __ v:(value)* __ "]" { - Value::Vector(v) +pub vector -> ValueAndSpan = + start:#position "[" __ v:(value)* __ "]" end:#position { + ValueAndSpan { + inner: SpannedValue::Vector(v), + span: Span(start, end) + } } -pub set -> Value = - "#{" __ v:(value)* __ "}" { - Value::Set(BTreeSet::from_iter(v)) +pub set -> ValueAndSpan = + start:#position "#{" __ v:(value)* __ "}" end:#position { + ValueAndSpan { + inner: SpannedValue::Set(BTreeSet::from_iter(v)), + span: Span(start, end) + } } -pair -> (Value, Value) = +pair -> (ValueAndSpan, ValueAndSpan) = k:(value) v:(value) { (k, v) } -pub map -> Value = - "{" __ v:(pair)* __ "}" { - Value::Map(BTreeMap::from_iter(v)) +pub map -> ValueAndSpan = + start:#position "{" __ v:(pair)* __ "}" end:#position { + ValueAndSpan { + inner: SpannedValue::Map(BTreeMap::from_iter(v)), + span: Span(start, end) + } } // It's important that float comes before integer or the parser assumes that // floats are integers and fails to parse -pub value -> Value = +pub value -> ValueAndSpan = __ v:(nil / nan / infinity / boolean / float / octalinteger / hexinteger / basedinteger / bigint / integer / text / keyword / symbol / list / vector / map / set) __ { v } diff --git a/edn/src/types.rs b/edn/src/types.rs index 7ef5a4ac..b2a5d5e5 100644 --- a/edn/src/types.rs +++ b/edn/src/types.rs @@ -24,7 +24,6 @@ pub enum Value { Boolean(bool), Integer(i64), BigInteger(BigInt), - // https://users.rust-lang.org/t/hashmap-key-cant-be-float-number-type-why/7892 Float(OrderedFloat), Text(String), PlainSymbol(symbols::PlainSymbol), @@ -44,68 +43,82 @@ pub enum Value { Map(BTreeMap), } -use self::Value::*; +/// SpannedValue is the parallel to Value but used in ValueAndSpan. +/// Container types have ValueAndSpan children. +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub enum SpannedValue { + Nil, + Boolean(bool), + Integer(i64), + BigInteger(BigInt), + Float(OrderedFloat), + Text(String), + PlainSymbol(symbols::PlainSymbol), + NamespacedSymbol(symbols::NamespacedSymbol), + Keyword(symbols::Keyword), + NamespacedKeyword(symbols::NamespacedKeyword), + Vector(Vec), + List(LinkedList), + Set(BTreeSet), + Map(BTreeMap), +} -impl Display for Value { - // TODO: Make sure float syntax is correct, handle NaN and escaping. - // See https://github.com/mozilla/mentat/issues/232 - fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result { - match *self { - Nil => write!(f, "nil"), - Boolean(v) => write!(f, "{}", v), - Integer(v) => write!(f, "{}", v), - BigInteger(ref v) => write!(f, "{}N", v), - // TODO: make sure float syntax is correct. - Float(ref v) => { - if *v == OrderedFloat(f64::INFINITY) { - write!(f, "#f +Infinity") - } else if *v == OrderedFloat(f64::NEG_INFINITY) { - write!(f, "#f -Infinity") - } else if *v == OrderedFloat(f64::NAN) { - write!(f, "#f NaN") - } else { - write!(f, "{}", v) - } - } - // TODO: EDN escaping. - Text(ref v) => write!(f, "{}", v), - PlainSymbol(ref v) => v.fmt(f), - NamespacedSymbol(ref v) => v.fmt(f), - Keyword(ref v) => v.fmt(f), - NamespacedKeyword(ref v) => v.fmt(f), - Vector(ref v) => { - write!(f, "[")?; - for x in v { - write!(f, " {}", x)?; - } - write!(f, " ]") - } - List(ref v) => { - write!(f, "(")?; - for x in v { - write!(f, " {}", x)?; - } - write!(f, " )") - } - Set(ref v) => { - write!(f, "#{{")?; - for x in v { - write!(f, " {}", x)?; - } - write!(f, " }}") - } - Map(ref v) => { - write!(f, "{{")?; - for (key, val) in v { - write!(f, " {} {}", key, val)?; - } - write!(f, " }}") - } +/// Span represents the current offset (start, end) into the input string. +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct Span(pub usize, pub usize); + +/// A wrapper type around SpannedValue and Span, representing some EDN Value +/// and the parsing offset (start, end) in the original EDN string. +#[derive(PartialEq, Eq, Hash, Clone, Debug)] +pub struct ValueAndSpan { + pub inner: SpannedValue, + pub span: Span, +} + +impl From for Value { + fn from(src: SpannedValue) -> Value { + match src { + SpannedValue::Nil => Value::Nil, + SpannedValue::Boolean(v) => Value::Boolean(v), + SpannedValue::Integer(v) => Value::Integer(v), + SpannedValue::BigInteger(v) => Value::BigInteger(v), + SpannedValue::Float(v) => Value::Float(v), + SpannedValue::Text(v) => Value::Text(v), + SpannedValue::PlainSymbol(v) => Value::PlainSymbol(v), + SpannedValue::NamespacedSymbol(v) => Value::NamespacedSymbol(v), + SpannedValue::Keyword(v) => Value::Keyword(v), + SpannedValue::NamespacedKeyword(v) => Value::NamespacedKeyword(v), + SpannedValue::Vector(v) => Value::Vector(v.into_iter().map(|x| x.without_spans()).collect()), + SpannedValue::List(v) => Value::List(v.into_iter().map(|x| x.without_spans()).collect()), + SpannedValue::Set(v) => Value::Set(v.into_iter().map(|x| x.without_spans()).collect()), + SpannedValue::Map(v) => Value::Map(v.into_iter().map(|(x, y)| (x.without_spans(), y.without_spans())).collect()), } } } -/// Creates `is_$TYPE` helper functions for Value, like +/// Creates `from_$TYPE` helper functions for Value and SpannedValue, +/// like `from_float()` or `from_ordered_float()`. +macro_rules! def_from { + ($name: ident, $out: ty, $kind: path, $t: ty, $( $transform: expr ),* ) => { + pub fn $name(src: $t) -> $out { + $( let src = $transform(src); )* + $kind(src) + } + } +} + +/// Creates `from_$TYPE` helper functions for Value or SpannedValue, +/// like `from_bigint()` where the conversion is optional. +macro_rules! def_from_option { + ($name: ident, $out: ty, $kind: path, $t: ty, $( $transform: expr ),* ) => { + pub fn $name<'a>(src: $t) -> Option<$out> { + $( let src = $transform(src); )* + src.map(|v| $kind(v)) + } + } +} + +/// Creates `is_$TYPE` helper functions for Value or SpannedValue, like /// `is_big_integer()` or `is_text()`. macro_rules! def_is { ($name: ident, $pat: pat) => { @@ -115,9 +128,9 @@ macro_rules! def_is { } } -/// Creates `as_$TYPE` helper functions for Value, like `as_integer()`, -/// which returns the underlying value representing this Value wrapped -/// in an Option, like ``. +/// Creates `as_$TYPE` helper functions for Value or SpannedValue, like +/// `as_integer()`, which returns the underlying value representing the +/// original variable wrapped in an Option, like `Option`. macro_rules! def_as { ($name: ident, $kind: path, $t: ty, $( $transform: expr ),* ) => { pub fn $name(&self) -> Option<$t> { @@ -126,9 +139,9 @@ macro_rules! def_as { } } -/// Creates `as_$TYPE` helper functions for Value, like `as_big_integer()`, -/// which returns a reference to the underlying value representing this Value -/// wrapped in an Option, like ``. +/// Creates `as_$TYPE` helper functions for Value or SpannedValue, like +/// `as_big_integer()`, which returns a reference to the underlying value +/// representing the original variable wrapped in an Option, like `Option<&BigInt>`. macro_rules! def_as_ref { ($name: ident, $kind: path, $t: ty) => { pub fn $name(&self) -> Option<&$t> { @@ -137,9 +150,9 @@ macro_rules! def_as_ref { } } -/// Creates `into_$TYPE` helper functions for Value, like `into_big_integer()`, -/// which consumes it, returning underlying value representing this Value -/// wrapped in an Option, like ``. +/// Creates `into_$TYPE` helper functions for Value or SpannedValue, like +/// `into_big_integer()`, which consumes it returning underlying value +/// representing the original variable wrapped in an Option, like `Option`. macro_rules! def_into { ($name: ident, $kind: path, $t: ty, $( $transform: expr ),* ) => { pub fn $name(self) -> Option<$t> { @@ -148,125 +161,6 @@ macro_rules! def_into { } } -impl Value { - def_is!(is_nil, Nil); - def_is!(is_boolean, Boolean(_)); - def_is!(is_integer, Integer(_)); - def_is!(is_big_integer, BigInteger(_)); - def_is!(is_float, Float(_)); - def_is!(is_text, Text(_)); - def_is!(is_symbol, PlainSymbol(_)); - def_is!(is_namespaced_symbol, NamespacedSymbol(_)); - def_is!(is_keyword, Keyword(_)); - def_is!(is_namespaced_keyword, NamespacedKeyword(_)); - def_is!(is_vector, Vector(_)); - def_is!(is_list, List(_)); - def_is!(is_set, Set(_)); - def_is!(is_map, Map(_)); - - /// `as_nil` does not use the macro as it does not have an underlying - /// value, and returns `Option<()>`. - pub fn as_nil(&self) -> Option<()> { - match *self { Nil => Some(()), _ => None } - } - - def_as!(as_boolean, Boolean, bool,); - def_as!(as_integer, Integer, i64,); - def_as!(as_float, Float, f64, |v: OrderedFloat| v.into_inner()); - - def_as_ref!(as_big_integer, BigInteger, BigInt); - def_as_ref!(as_ordered_float, Float, OrderedFloat); - def_as_ref!(as_text, Text, String); - def_as_ref!(as_symbol, PlainSymbol, symbols::PlainSymbol); - def_as_ref!(as_namespaced_symbol, NamespacedSymbol, symbols::NamespacedSymbol); - def_as_ref!(as_keyword, Keyword, symbols::Keyword); - def_as_ref!(as_namespaced_keyword, NamespacedKeyword, symbols::NamespacedKeyword); - def_as_ref!(as_vector, Vector, Vec); - def_as_ref!(as_list, List, LinkedList); - def_as_ref!(as_set, Set, BTreeSet); - def_as_ref!(as_map, Map, BTreeMap); - - def_into!(into_boolean, Boolean, bool,); - def_into!(into_integer, Integer, i64,); - def_into!(into_big_integer, BigInteger, BigInt,); - def_into!(into_ordered_float, Float, OrderedFloat,); - def_into!(into_float, Float, f64, |v: OrderedFloat| v.into_inner()); - def_into!(into_text, Text, String,); - def_into!(into_symbol, PlainSymbol, symbols::PlainSymbol,); - def_into!(into_namespaced_symbol, NamespacedSymbol, symbols::NamespacedSymbol,); - def_into!(into_keyword, Keyword, symbols::Keyword,); - def_into!(into_namespaced_keyword, NamespacedKeyword, symbols::NamespacedKeyword,); - def_into!(into_vector, Vector, Vec,); - def_into!(into_list, List, LinkedList,); - def_into!(into_set, Set, BTreeSet,); - def_into!(into_map, Map, BTreeMap,); - - pub fn from_bigint(src: &str) -> Option { - src.parse::().map(Value::BigInteger).ok() - } - - pub fn from_symbol<'a, T: Into>>(namespace: T, name: &str) -> Value { - to_symbol(namespace, name) - } - - pub fn from_keyword<'a, T: Into>>(namespace: T, name: &str) -> Value { - to_keyword(namespace, name) - } -} - -impl From for Value { - fn from(src: f64) -> Value { - Value::Float(OrderedFloat::from(src)) - } -} - -impl PartialOrd for Value { - fn partial_cmp(&self, other: &Value) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Value { - fn cmp(&self, other: &Value) -> Ordering { - match (self, other) { - (&Nil, &Nil) => Ordering::Equal, - (&Boolean(a), &Boolean(b)) => b.cmp(&a), - (&Integer(a), &Integer(b)) => b.cmp(&a), - (&BigInteger(ref a), &BigInteger(ref b)) => b.cmp(a), - (&Float(ref a), &Float(ref b)) => b.cmp(a), - (&Text(ref a), &Text(ref b)) => b.cmp(a), - (&PlainSymbol(ref a), &PlainSymbol(ref b)) => b.cmp(a), - (&NamespacedSymbol(ref a), &NamespacedSymbol(ref b)) => b.cmp(a), - (&Keyword(ref a), &Keyword(ref b)) => b.cmp(a), - (&NamespacedKeyword(ref a), &NamespacedKeyword(ref b)) => b.cmp(a), - (&Vector(ref a), &Vector(ref b)) => b.cmp(a), - (&List(ref a), &List(ref b)) => b.cmp(a), - (&Set(ref a), &Set(ref b)) => b.cmp(a), - (&Map(ref a), &Map(ref b)) => b.cmp(a), - _ => to_ord(self).cmp(&to_ord(other)) - } - } -} - -fn to_ord(value: &Value) -> i32 { - match *value { - Nil => 0, - Boolean(_) => 1, - Integer(_) => 2, - BigInteger(_) => 3, - Float(_) => 4, - Text(_) => 5, - PlainSymbol(_) => 6, - NamespacedSymbol(_) => 7, - Keyword(_) => 8, - NamespacedKeyword(_) => 9, - Vector(_) => 10, - List(_) => 11, - Set(_) => 12, - Map(_) => 13, - } -} - /// Converts `name` into a plain or namespaced value symbol, depending on /// whether or not `namespace` is given. /// @@ -276,16 +170,24 @@ fn to_ord(value: &Value) -> i32 { /// # use edn::types::to_symbol; /// # use edn::types::Value; /// # use edn::symbols; -/// let value = to_symbol("foo", "bar"); +/// let value = to_symbol!("foo", "bar", Value); /// assert_eq!(value, Value::NamespacedSymbol(symbols::NamespacedSymbol::new("foo", "bar"))); /// -/// let value = to_symbol(None, "baz"); +/// let value = to_symbol!(None, "baz", Value); /// assert_eq!(value, Value::PlainSymbol(symbols::PlainSymbol::new("baz"))); +/// +/// let value = to_symbol!("foo", "bar", SpannedValue); +/// assert_eq!(value.into(), to_symbol!("foo", "bar", Value)); +/// +/// let value = to_symbol!(None, "baz", SpannedValue); +/// assert_eq!(value.into(), to_symbol!(None, "baz", Value)); /// ``` -pub fn to_symbol<'a, T: Into>>(namespace: T, name: &str) -> Value { - namespace.into().map_or_else( - || Value::PlainSymbol(symbols::PlainSymbol::new(name)), - |ns| Value::NamespacedSymbol(symbols::NamespacedSymbol::new(ns, name))) +macro_rules! to_symbol { + ( $namespace:expr, $name:expr, $t:tt ) => { + $namespace.into().map_or_else( + || $t::PlainSymbol(symbols::PlainSymbol::new($name)), + |ns| $t::NamespacedSymbol(symbols::NamespacedSymbol::new(ns, $name))) + } } /// Converts `name` into a plain or namespaced value keyword, depending on @@ -297,16 +199,265 @@ pub fn to_symbol<'a, T: Into>>(namespace: T, name: &str) -> Valu /// # use edn::types::to_keyword; /// # use edn::types::Value; /// # use edn::symbols; -/// let value = to_keyword("foo", "bar"); +/// let value = to_keyword!("foo", "bar", Value); /// assert_eq!(value, Value::NamespacedKeyword(symbols::NamespacedKeyword::new("foo", "bar"))); /// -/// let value = to_keyword(None, "baz"); +/// let value = to_keyword!(None, "baz", Value); /// assert_eq!(value, Value::Keyword(symbols::Keyword::new("baz"))); +/// +/// let value = to_keyword!("foo", "bar", SpannedValue); +/// assert_eq!(value.into(), to_keyword!("foo", "bar", Value)); +/// +/// let value = to_keyword!(None, "baz", SpannedValue); +/// assert_eq!(value.into(), to_keyword!(None, "baz", Value)); /// ``` -pub fn to_keyword<'a, T: Into>>(namespace: T, name: &str) -> Value { - namespace.into().map_or_else( - || Value::Keyword(symbols::Keyword::new(name)), - |ns| Value::NamespacedKeyword(symbols::NamespacedKeyword::new(ns, name))) +macro_rules! to_keyword { + ( $namespace:expr, $name:expr, $t:tt ) => { + $namespace.into().map_or_else( + || $t::Keyword(symbols::Keyword::new($name)), + |ns| $t::NamespacedKeyword(symbols::NamespacedKeyword::new(ns, $name))) + } +} + +/// Implements multiple is*, as*, into* and from* methods common to +/// both Value and SpannedValue. +macro_rules! def_common_value_methods { + ( $t:tt, $tchild:tt ) => { + def_is!(is_nil, $t::Nil); + def_is!(is_boolean, $t::Boolean(_)); + def_is!(is_integer, $t::Integer(_)); + def_is!(is_big_integer, $t::BigInteger(_)); + def_is!(is_float, $t::Float(_)); + def_is!(is_text, $t::Text(_)); + def_is!(is_symbol, $t::PlainSymbol(_)); + def_is!(is_namespaced_symbol, $t::NamespacedSymbol(_)); + def_is!(is_keyword, $t::Keyword(_)); + def_is!(is_namespaced_keyword, $t::NamespacedKeyword(_)); + def_is!(is_vector, $t::Vector(_)); + def_is!(is_list, $t::List(_)); + def_is!(is_set, $t::Set(_)); + def_is!(is_map, $t::Map(_)); + + /// `as_nil` does not use the macro as it does not have an underlying + /// value, and returns `Option<()>`. + pub fn as_nil(&self) -> Option<()> { + match *self { $t::Nil => Some(()), _ => None } + } + + def_as!(as_boolean, $t::Boolean, bool,); + def_as!(as_integer, $t::Integer, i64,); + def_as!(as_float, $t::Float, f64, |v: OrderedFloat| v.into_inner()); + + def_as_ref!(as_big_integer, $t::BigInteger, BigInt); + def_as_ref!(as_ordered_float, $t::Float, OrderedFloat); + def_as_ref!(as_text, $t::Text, String); + def_as_ref!(as_symbol, $t::PlainSymbol, symbols::PlainSymbol); + def_as_ref!(as_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol); + def_as_ref!(as_keyword, $t::Keyword, symbols::Keyword); + def_as_ref!(as_namespaced_keyword, $t::NamespacedKeyword, symbols::NamespacedKeyword); + def_as_ref!(as_vector, $t::Vector, Vec<$tchild>); + def_as_ref!(as_list, $t::List, LinkedList<$tchild>); + def_as_ref!(as_set, $t::Set, BTreeSet<$tchild>); + def_as_ref!(as_map, $t::Map, BTreeMap<$tchild, $tchild>); + + def_into!(into_boolean, $t::Boolean, bool,); + def_into!(into_integer, $t::Integer, i64,); + def_into!(into_big_integer, $t::BigInteger, BigInt,); + def_into!(into_ordered_float, $t::Float, OrderedFloat,); + def_into!(into_float, $t::Float, f64, |v: OrderedFloat| v.into_inner()); + def_into!(into_text, $t::Text, String,); + def_into!(into_symbol, $t::PlainSymbol, symbols::PlainSymbol,); + def_into!(into_namespaced_symbol, $t::NamespacedSymbol, symbols::NamespacedSymbol,); + def_into!(into_keyword, $t::Keyword, symbols::Keyword,); + def_into!(into_namespaced_keyword, $t::NamespacedKeyword, symbols::NamespacedKeyword,); + def_into!(into_vector, $t::Vector, Vec<$tchild>,); + def_into!(into_list, $t::List, LinkedList<$tchild>,); + def_into!(into_set, $t::Set, BTreeSet<$tchild>,); + def_into!(into_map, $t::Map, BTreeMap<$tchild, $tchild>,); + + def_from_option!(from_bigint, $t, $t::BigInteger, &'a str, |src: &'a str| src.parse::().ok()); + def_from!(from_float, $t, $t::Float, f64, |src: f64| OrderedFloat::from(src)); + def_from!(from_ordered_float, $t, $t::Float, OrderedFloat,); + + pub fn from_symbol<'a, T: Into>>(namespace: T, name: &str) -> $t { + to_symbol!(namespace, name, $t) + } + + pub fn from_keyword<'a, T: Into>>(namespace: T, name: &str) -> $t { + to_keyword!(namespace, name, $t) + } + + fn precedence(&self) -> i32 { + match *self { + $t::Nil => 0, + $t::Boolean(_) => 1, + $t::Integer(_) => 2, + $t::BigInteger(_) => 3, + $t::Float(_) => 4, + $t::Text(_) => 5, + $t::PlainSymbol(_) => 6, + $t::NamespacedSymbol(_) => 7, + $t::Keyword(_) => 8, + $t::NamespacedKeyword(_) => 9, + $t::Vector(_) => 10, + $t::List(_) => 11, + $t::Set(_) => 12, + $t::Map(_) => 13, + } + } + } +} + +/// Compares Value or SpannedValue instances and returns Ordering. +/// Used in `Ord` implementations. +macro_rules! def_common_value_ord { + ( $t:tt, $value:expr, $other:expr ) => { + match ($value, $other) { + (&$t::Nil, &$t::Nil) => Ordering::Equal, + (&$t::Boolean(a), &$t::Boolean(b)) => b.cmp(&a), + (&$t::Integer(a), &$t::Integer(b)) => b.cmp(&a), + (&$t::BigInteger(ref a), &$t::BigInteger(ref b)) => b.cmp(a), + (&$t::Float(ref a), &$t::Float(ref b)) => b.cmp(a), + (&$t::Text(ref a), &$t::Text(ref b)) => b.cmp(a), + (&$t::PlainSymbol(ref a), &$t::PlainSymbol(ref b)) => b.cmp(a), + (&$t::NamespacedSymbol(ref a), &$t::NamespacedSymbol(ref b)) => b.cmp(a), + (&$t::Keyword(ref a), &$t::Keyword(ref b)) => b.cmp(a), + (&$t::NamespacedKeyword(ref a), &$t::NamespacedKeyword(ref b)) => b.cmp(a), + (&$t::Vector(ref a), &$t::Vector(ref b)) => b.cmp(a), + (&$t::List(ref a), &$t::List(ref b)) => b.cmp(a), + (&$t::Set(ref a), &$t::Set(ref b)) => b.cmp(a), + (&$t::Map(ref a), &$t::Map(ref b)) => b.cmp(a), + _ => $value.precedence().cmp(&$other.precedence()) + } + } +} + +/// Converts a Value or SpannedValue to string, given a formatter. +// TODO: Make sure float syntax is correct, handle NaN and escaping. +// See https://github.com/mozilla/mentat/issues/232 +macro_rules! def_common_value_display { + ( $t:tt, $value:expr, $f:expr ) => { + match *$value { + $t::Nil => write!($f, "nil"), + $t::Boolean(v) => write!($f, "{}", v), + $t::Integer(v) => write!($f, "{}", v), + $t::BigInteger(ref v) => write!($f, "{}N", v), + // TODO: make sure float syntax is correct. + $t::Float(ref v) => { + if *v == OrderedFloat(f64::INFINITY) { + write!($f, "#f +Infinity") + } else if *v == OrderedFloat(f64::NEG_INFINITY) { + write!($f, "#f -Infinity") + } else if *v == OrderedFloat(f64::NAN) { + write!($f, "#f NaN") + } else { + write!($f, "{}", v) + } + } + // TODO: EDN escaping. + $t::Text(ref v) => write!($f, "{}", v), + $t::PlainSymbol(ref v) => v.fmt($f), + $t::NamespacedSymbol(ref v) => v.fmt($f), + $t::Keyword(ref v) => v.fmt($f), + $t::NamespacedKeyword(ref v) => v.fmt($f), + $t::Vector(ref v) => { + write!($f, "[")?; + for x in v { + write!($f, " {}", x)?; + } + write!($f, " ]") + } + $t::List(ref v) => { + write!($f, "(")?; + for x in v { + write!($f, " {}", x)?; + } + write!($f, " )") + } + $t::Set(ref v) => { + write!($f, "#{{")?; + for x in v { + write!($f, " {}", x)?; + } + write!($f, " }}") + } + $t::Map(ref v) => { + write!($f, "{{")?; + for (key, val) in v { + write!($f, " {} {}", key, val)?; + } + write!($f, " }}") + } + } + } +} + +impl Value { + def_common_value_methods!(Value, Value); +} + +impl SpannedValue { + def_common_value_methods!(SpannedValue, ValueAndSpan); +} + +impl ValueAndSpan { + pub fn without_spans(self) -> Value { + self.inner.into() + } +} + +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Value) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialOrd for SpannedValue { + fn partial_cmp(&self, other: &SpannedValue) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialOrd for ValueAndSpan { + fn partial_cmp(&self, other: &ValueAndSpan) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Value { + fn cmp(&self, other: &Value) -> Ordering { + def_common_value_ord!(Value, self, other) + } +} + +impl Ord for SpannedValue { + fn cmp(&self, other: &SpannedValue) -> Ordering { + def_common_value_ord!(SpannedValue, self, other) + } +} + +impl Ord for ValueAndSpan { + fn cmp(&self, other: &ValueAndSpan) -> Ordering { + self.inner.cmp(&other.inner) + } +} + +impl Display for Value { + fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result { + def_common_value_display!(Value, self, f) + } +} + +impl Display for SpannedValue { + fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result { + def_common_value_display!(SpannedValue, self, f) + } +} + +impl Display for ValueAndSpan { + fn fmt(&self, f: &mut Formatter) -> ::std::fmt::Result { + self.inner.fmt(f) + } } #[cfg(test)] @@ -321,14 +472,15 @@ mod test { use std::iter::FromIterator; use std::f64; - use symbols; use parse; + use num::BigInt; use ordered_float::OrderedFloat; #[test] fn test_value_from() { - assert_eq!(Value::from(42f64), Value::Float(OrderedFloat::from(42f64))); + assert_eq!(Value::from_float(42f64), Value::Float(OrderedFloat::from(42f64))); + assert_eq!(Value::from_ordered_float(OrderedFloat::from(42f64)), Value::Float(OrderedFloat::from(42f64))); assert_eq!(Value::from_bigint("42").unwrap(), Value::BigInteger(BigInt::from(42))); } @@ -339,7 +491,7 @@ mod test { Value::Integer(1), Value::Integer(2), Value::List(LinkedList::from_iter(vec![ - Value::Float(OrderedFloat(3.14)) + Value::from_float(3.14) ])), Value::Set(BTreeSet::from_iter(vec![ Value::from_bigint("4").unwrap() @@ -349,20 +501,21 @@ mod test { (Value::from_keyword("baz", "boz"), Value::Integer(43)) ])), Value::Vector(vec![]), - Value::Keyword(symbols::Keyword::new("five")), - Value::NamespacedKeyword(symbols::NamespacedKeyword::new("six", "seven")), - Value::PlainSymbol(symbols::PlainSymbol::new("eight")), - Value::NamespacedSymbol(symbols::NamespacedSymbol::new("nine", "ten")), + Value::from_keyword(None, "five"), + Value::from_keyword("six", "seven"), + Value::from_symbol(None, "eight"), + Value::from_symbol("nine", "ten"), Value::Boolean(true), Value::Boolean(false), Value::Nil, - Value::Float(OrderedFloat(f64::NAN)), - Value::Float(OrderedFloat(f64::NEG_INFINITY)), - Value::Float(OrderedFloat(f64::INFINITY)), + Value::from_float(f64::NAN), + Value::from_float(f64::NEG_INFINITY), + Value::from_float(f64::INFINITY), ]); assert_eq!(string, data.to_string()); assert_eq!(string, parse::value(&data.to_string()).unwrap().to_string()); + assert_eq!(string, parse::value(&data.to_string()).unwrap().without_spans().to_string()); } #[test] @@ -372,7 +525,7 @@ mod test { assert_eq!(Value::Boolean(false).cmp(&Value::Boolean(true)), Ordering::Greater); assert_eq!(Value::Integer(1).cmp(&Value::Integer(2)), Ordering::Greater); assert_eq!(Value::from_bigint("1").cmp(&Value::from_bigint("2")), Ordering::Greater); - assert_eq!(Value::from(1f64).cmp(&Value::from(2f64)), Ordering::Greater); + assert_eq!(Value::from_float(1f64).cmp(&Value::from_float(2f64)), Ordering::Greater); assert_eq!(Value::Text("1".to_string()).cmp(&Value::Text("2".to_string())), Ordering::Greater); assert_eq!(Value::from_symbol("a", "b").cmp(&Value::from_symbol("c", "d")), Ordering::Greater); assert_eq!(Value::from_symbol(None, "a").cmp(&Value::from_symbol(None, "b")), Ordering::Greater); diff --git a/edn/tests/tests.rs b/edn/tests/tests.rs index 3c5f3693..a3b91194 100644 --- a/edn/tests/tests.rs +++ b/edn/tests/tests.rs @@ -15,44 +15,88 @@ extern crate ordered_float; use std::collections::{BTreeSet, BTreeMap, LinkedList}; use std::iter::FromIterator; use std::f64; + use num::bigint::ToBigInt; use num::traits::{Zero, One}; use ordered_float::OrderedFloat; + +use edn::parse::{self, ParseError}; +use edn::types::{Value, SpannedValue, Span, ValueAndSpan}; use edn::symbols; -use edn::types::Value; -use edn::types::Value::*; -use edn::parse::*; use edn::utils; // Helper for making wrapped keywords with a namespace. fn k_ns(ns: &str, name: &str) -> Value { - NamespacedKeyword(symbols::NamespacedKeyword::new(ns, name)) + Value::NamespacedKeyword(symbols::NamespacedKeyword::new(ns, name)) } // Helper for making wrapped keywords without a namespace. fn k_plain(name: &str) -> Value { - Keyword(symbols::Keyword::new(name)) + Value::Keyword(symbols::Keyword::new(name)) } // Helper for making wrapped symbols with a namespace fn s_ns(ns: &str, name: &str) -> Value { - NamespacedSymbol(symbols::NamespacedSymbol::new(ns, name)) + Value::NamespacedSymbol(symbols::NamespacedSymbol::new(ns, name)) } // Helper for making wrapped symbols without a namespace fn s_plain(name: &str) -> Value { - PlainSymbol(symbols::PlainSymbol::new(name)) + Value::PlainSymbol(symbols::PlainSymbol::new(name)) } +// Helpers for parsing strings and converting them into edn::Value. +macro_rules! fn_parse_into_value { + ($name: ident) => { + fn $name<'a, T>(src: T) -> Result where T: Into<&'a str> { + parse::$name(src.into()).map(|x| x.inner.into()) + } + } +} + +// These look exactly like their `parse::foo` counterparts, but +// automatically convert the returned result into Value. Use `parse:foo` +// if you want the original ValueAndSpan instance. +fn_parse_into_value!(nil); +fn_parse_into_value!(nan); +fn_parse_into_value!(infinity); +fn_parse_into_value!(boolean); +fn_parse_into_value!(bigint); +fn_parse_into_value!(octalinteger); +fn_parse_into_value!(hexinteger); +fn_parse_into_value!(basedinteger); +fn_parse_into_value!(integer); +fn_parse_into_value!(float); +fn_parse_into_value!(text); +fn_parse_into_value!(symbol); +fn_parse_into_value!(keyword); +fn_parse_into_value!(list); +fn_parse_into_value!(vector); +fn_parse_into_value!(set); +fn_parse_into_value!(map); +fn_parse_into_value!(value); + #[test] fn test_nil() { + use self::Value::*; + assert_eq!(nil("nil").unwrap(), Nil); assert!(nil("true").is_err()); } +#[test] +fn test_span_nil() { + assert_eq!(parse::nil("nil").unwrap(), ValueAndSpan { + inner: SpannedValue::Nil, + span: Span(0, 3) + }); +} + #[test] fn test_nan() { + use self::Value::*; + assert!(nan("#fNaN").is_err()); assert!(nan("#f;x\nNaN").is_err()); @@ -63,8 +107,18 @@ fn test_nan() { assert!(nan("true").is_err()); } +#[test] +fn test_span_nan() { + assert_eq!(parse::nan("#f NaN").unwrap(), ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(f64::NAN)), + span: Span(0, 6) + }); +} + #[test] fn test_infinity() { + use self::Value::*; + assert!(infinity("#f-Infinity").is_err()); assert!(infinity("#f+Infinity").is_err()); @@ -83,16 +137,45 @@ fn test_infinity() { assert!(infinity("true").is_err()); } +#[test] +fn test_span_infinity() { + assert_eq!(parse::infinity("#f -Infinity").unwrap(), ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(f64::NEG_INFINITY)), + span: Span(0, 12) + }); + assert_eq!(parse::infinity("#f +Infinity").unwrap(), ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(f64::INFINITY)), + span: Span(0, 12) + }); +} + #[test] fn test_boolean() { + use self::Value::*; + assert_eq!(boolean("true").unwrap(), Boolean(true)); assert_eq!(boolean("false").unwrap(), Boolean(false)); assert!(boolean("nil").is_err()); } +#[test] +fn test_span_boolean() { + assert_eq!(parse::boolean("true").unwrap(), ValueAndSpan { + inner: SpannedValue::Boolean(true), + span: Span(0, 4) + }); + + assert_eq!(parse::boolean("false").unwrap(), ValueAndSpan { + inner: SpannedValue::Boolean(false), + span: Span(0, 5) + }); +} + #[test] fn test_integer() { + use self::Value::*; + assert_eq!(integer("0").unwrap(), Integer(0i64)); assert_eq!(integer("1").unwrap(), Integer(1i64)); assert_eq!(integer("999").unwrap(), Integer(999i64)); @@ -103,33 +186,66 @@ fn test_integer() { #[test] fn test_hexinteger() { + use self::Value::*; + assert_eq!(hexinteger("0xabc111").unwrap(), Integer(11256081)); assert_eq!(hexinteger("0xABCDEF").unwrap(), Integer(11259375)); assert_eq!(hexinteger("0xabcdef").unwrap(), Integer(11259375)); + assert!(hexinteger("1").is_err()); assert!(hexinteger("nil").is_err()); assert!(hexinteger("0xZZZ").is_err()); } #[test] fn test_basedinteger() { + use self::Value::*; + assert_eq!(basedinteger("2r111").unwrap(), Integer(7)); assert_eq!(basedinteger("36r1z").unwrap(), Integer(71)); assert_eq!(basedinteger("36r1Z").unwrap(), Integer(71)); assert_eq!(basedinteger("12r11").unwrap(), Integer(13)); assert_eq!(basedinteger("24r10").unwrap(), Integer(24)); + assert!(basedinteger("1").is_err()); assert!(basedinteger("nil").is_err()); } #[test] fn test_octalinteger() { + use self::Value::*; + assert_eq!(octalinteger("011").unwrap(), Integer(9)); assert_eq!(octalinteger("00107").unwrap(), Integer(71)); + + assert!(octalinteger("1").is_err()); + assert!(octalinteger("nil").is_err()); +} + +#[test] +fn test_span_integer() { + assert_eq!(parse::integer("42").unwrap(), ValueAndSpan { + inner: SpannedValue::Integer(42), + span: Span(0, 2) + }); + assert_eq!(parse::hexinteger("0xabc111").unwrap(), ValueAndSpan { + inner: SpannedValue::Integer(11256081), + span: Span(0, 8) + }); + assert_eq!(parse::basedinteger("2r111").unwrap(), ValueAndSpan { + inner: SpannedValue::Integer(7), + span: Span(0, 5) + }); + assert_eq!(parse::octalinteger("011").unwrap(), ValueAndSpan { + inner: SpannedValue::Integer(9), + span: Span(0, 3) + }); } #[test] fn test_bigint() { + use self::Value::*; + let max_i64 = i64::max_value().to_bigint().unwrap(); let bigger = &max_i64 * &max_i64; @@ -141,19 +257,43 @@ fn test_bigint() { assert!(bigint("nil").is_err()); } +#[test] +fn test_span_bigint() { + let max_i64 = i64::max_value().to_bigint().unwrap(); + let bigger = &max_i64 * &max_i64; + + assert_eq!(parse::bigint("85070591730234615847396907784232501249N").unwrap(), ValueAndSpan { + inner: SpannedValue::BigInteger(bigger), + span: Span(0, 39) + }); +} + #[test] fn test_float() { + use self::Value::*; + assert_eq!(float("111.222").unwrap(), Float(OrderedFloat(111.222f64))); assert_eq!(float("3e4").unwrap(), Float(OrderedFloat(3e4f64))); assert_eq!(float("-55e-66").unwrap(), Float(OrderedFloat(-55e-66f64))); assert_eq!(float("77.88e99").unwrap(), Float(OrderedFloat(77.88e99f64))); assert_eq!(float("-9.9E-9").unwrap(), Float(OrderedFloat(-9.9E-9f64))); + assert!(float("42").is_err()); assert!(float("nil").is_err()); } +#[test] +fn test_span_float() { + assert_eq!(parse::float("42.0").unwrap(), ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(42f64)), + span: Span(0, 4) + }); +} + #[test] fn test_text() { + use self::Value::*; + assert_eq!(text("\"hello world\"").unwrap(), Text("hello world".to_string())); assert_eq!(text("\"\"").unwrap(), Text("".to_string())); @@ -161,6 +301,14 @@ fn test_text() { assert!(text("nil").is_err()); } +#[test] +fn test_span_text() { + assert_eq!(parse::text("\"hello world\"").unwrap(), ValueAndSpan { + inner: SpannedValue::Text("hello world".to_string()), + span: Span(0, 13) + }); +} + #[test] fn test_symbol() { assert_eq!(symbol("$").unwrap(), s_plain("$")); @@ -179,6 +327,18 @@ fn test_symbol() { assert_eq!(symbol("foo_bar").unwrap(), s_plain("foo_bar")); } +#[test] +fn test_span_symbol() { + assert_eq!(parse::symbol("hello").unwrap(), ValueAndSpan { + inner: SpannedValue::from_symbol(None, "hello"), + span: Span(0, 5) + }); + assert_eq!(parse::symbol("hello/world").unwrap(), ValueAndSpan { + inner: SpannedValue::from_symbol("hello", "world"), + span: Span(0, 11) + }); +} + #[test] fn test_keyword() { assert_eq!(keyword(":hello/world").unwrap(), k_ns("hello", "world")); @@ -197,29 +357,135 @@ fn test_keyword() { assert!(keyword(":foo/").is_err()); } +#[test] +fn test_span_keyword() { + assert_eq!(parse::keyword(":hello").unwrap(), ValueAndSpan { + inner: SpannedValue::from_keyword(None, "hello"), + span: Span(0, 6) + }); + assert_eq!(parse::keyword(":hello/world").unwrap(), ValueAndSpan { + inner: SpannedValue::from_keyword("hello", "world"), + span: Span(0, 12) + }); +} + #[test] fn test_value() { + use self::Value::*; + let max_i64 = i64::max_value().to_bigint().unwrap(); let bigger = &max_i64 * &max_i64; assert_eq!(value("nil").unwrap(), Nil); assert_eq!(value("true").unwrap(), Boolean(true)); assert_eq!(value("1").unwrap(), Integer(1i64)); + assert_eq!(value("0xabc111").unwrap(), Integer(11256081)); + assert_eq!(value("2r111").unwrap(), Integer(7)); + assert_eq!(value("011").unwrap(), Integer(9)); + assert_eq!(value("85070591730234615847396907784232501249N").unwrap(), BigInteger(bigger)); + assert_eq!(value("111.222").unwrap(), Float(OrderedFloat(111.222f64))); assert_eq!(value("\"hello world\"").unwrap(), Text("hello world".to_string())); assert_eq!(value("$").unwrap(), s_plain("$")); assert_eq!(value(".").unwrap(), s_plain(".")); assert_eq!(value("$symbol").unwrap(), s_plain("$symbol")); assert_eq!(value(":hello").unwrap(), k_plain("hello")); assert_eq!(value("[1]").unwrap(), Vector(vec![Integer(1)])); - assert_eq!(value("111.222").unwrap(), Float(OrderedFloat(111.222f64))); - assert_eq!(value("85070591730234615847396907784232501249N").unwrap(), BigInteger(bigger)); - assert_eq!(value("0xabc111").unwrap(), Integer(11256081)); - assert_eq!(value("2r111").unwrap(), Integer(7)); - assert_eq!(value("011").unwrap(), Integer(9)); + assert_eq!(value("(1)").unwrap(), List(LinkedList::from_iter(vec![Integer(1)]))); + assert_eq!(value("#{1}").unwrap(), Set(BTreeSet::from_iter(vec![Integer(1)]))); + assert_eq!(value("{1 2}").unwrap(), Map(BTreeMap::from_iter(vec![(Integer(1), Integer(2))]))); +} + +#[test] +fn test_span_value() { + let max_i64 = i64::max_value().to_bigint().unwrap(); + let bigger = &max_i64 * &max_i64; + + assert_eq!(parse::value("nil").unwrap(), ValueAndSpan { + inner: SpannedValue::Nil, + span: Span(0,3) + }); + assert_eq!(parse::value("true").unwrap(), ValueAndSpan { + inner: SpannedValue::Boolean(true), + span: Span(0,4) + }); + assert_eq!(parse::value("1").unwrap(), ValueAndSpan { + inner: SpannedValue::Integer(1i64), + span: Span(0,1) + }); + assert_eq!(parse::value("85070591730234615847396907784232501249N").unwrap(), ValueAndSpan { + inner: SpannedValue::BigInteger(bigger), + span: Span(0,39) + }); + assert_eq!(parse::value("111.222").unwrap(), ValueAndSpan { + inner: SpannedValue::Float(OrderedFloat(111.222f64)), + span: Span(0,7) + }); + assert_eq!(parse::value("\"hello world\"").unwrap(), ValueAndSpan { + inner: SpannedValue::Text("hello world".to_string()), + span: Span(0,13) + }); + assert_eq!(parse::value("$").unwrap(), ValueAndSpan { + inner: SpannedValue::from_symbol(None, "$"), + span: Span(0,1) + }); + assert_eq!(parse::value(".").unwrap(), ValueAndSpan { + inner: SpannedValue::from_symbol(None, "."), + span: Span(0,1) + }); + assert_eq!(parse::value("$symbol").unwrap(), ValueAndSpan { + inner: SpannedValue::from_symbol(None, "$symbol"), + span: Span(0,7) + }); + assert_eq!(parse::value(":hello").unwrap(), ValueAndSpan { + inner: SpannedValue::from_keyword(None, "hello"), + span: Span(0,6) + }); + assert_eq!(parse::value("[1]").unwrap(), ValueAndSpan { + inner: SpannedValue::Vector(vec![ + ValueAndSpan { + inner: SpannedValue::Integer(1), + span: Span(1,2) + } + ]), + span: Span(0,3) + }); + assert_eq!(parse::value("(1)").unwrap(), ValueAndSpan { + inner: SpannedValue::List(LinkedList::from_iter(vec![ + ValueAndSpan { + inner: SpannedValue::Integer(1), + span: Span(1,2) + } + ])), + span: Span(0,3) + }); + assert_eq!(parse::value("#{1}").unwrap(), ValueAndSpan { + inner: SpannedValue::Set(BTreeSet::from_iter(vec![ + ValueAndSpan { + inner: SpannedValue::Integer(1), + span: Span(2,3) + } + ])), + span: Span(0,4) + }); + assert_eq!(parse::value("{1 2}").unwrap(), ValueAndSpan { + inner: SpannedValue::Map(BTreeMap::from_iter(vec![ + (ValueAndSpan { + inner: SpannedValue::Integer(1), + span: Span(1,2) + }, + ValueAndSpan { + inner: SpannedValue::Integer(2), + span: Span(3,4) + }) + ])), + span: Span(0,5) + }); } #[test] fn test_vector() { + use self::Value::*; + let max_i64 = i64::max_value().to_bigint().unwrap(); let bigger = &max_i64 * &max_i64; @@ -295,6 +561,8 @@ fn test_vector() { #[test] fn test_list() { + use self::Value::*; + let test = "()"; let value = List(LinkedList::from_iter(vec![ ])); @@ -366,6 +634,8 @@ fn test_list() { #[test] fn test_set() { + use self::Value::*; + let test = "#{}"; let value = Set(BTreeSet::from_iter(vec![ ])); @@ -441,6 +711,8 @@ fn test_set() { #[test] fn test_map() { + use self::Value::*; + let test = "{}"; let value = Map(BTreeMap::from_iter(vec![ ])); @@ -494,18 +766,18 @@ fn test_map() { assert_eq!(map(test).unwrap(), value); let test = "{:a 1, $b {:b/a nil, :b/b #{nil 5}}, c [1 2], d (3 4)}"; - let value = Map( - BTreeMap::from_iter( - vec![ - (Keyword(symbols::Keyword::new("a")), Integer(1)), - (s_plain("$b"), Map(BTreeMap::from_iter(vec![ - (k_ns("b", "a"), Nil), - (k_ns("b", "b"), - Set(BTreeSet::from_iter(vec![Nil, Integer(5),]))), + let value = Map(BTreeMap::from_iter(vec![ + (Keyword(symbols::Keyword::new("a")), Integer(1)), + (s_plain("$b"), Map(BTreeMap::from_iter(vec![ + (k_ns("b", "a"), Nil), + (k_ns("b", "b"), Set(BTreeSet::from_iter(vec![ + Nil, + Integer(5) ]))), - (s_plain("c"), Vector(vec![Integer(1), Integer(2),])), - (s_plain("d"), List(LinkedList::from_iter(vec![Integer(3), Integer(4),]))), - ])); + ]))), + (s_plain("c"), Vector(vec![Integer(1), Integer(2),])), + (s_plain("d"), List(LinkedList::from_iter(vec![Integer(3), Integer(4),]))), + ])); assert_eq!(map(test).unwrap(), value); assert!(map("#{").is_err()); @@ -520,6 +792,8 @@ fn test_map() { /// Secondly, see note in `test_query_starred_pages` on the use of ' #[test] fn test_query_active_sessions() { + use self::Value::*; + let test = "[ :find ?id ?reason ?ts :in $ @@ -565,6 +839,8 @@ fn test_query_active_sessions() { #[test] fn test_query_ended_sessions() { + use self::Value::*; + let test = "[ :find ?id ?endReason ?ts :in $ @@ -598,6 +874,8 @@ fn test_query_ended_sessions() { #[test] fn test_query_starred_pages() { + use self::Value::*; + // TODO: The original query had added "'" like `:find '[?url` and `since '[$ ?since] '[$]` let test = "[ :find [?url ?title ?starredOn] @@ -632,6 +910,8 @@ fn test_query_starred_pages() { #[test] fn test_query_saved_pages() { + use self::Value::*; + let test = "[ :find ?page ?url ?title ?excerpt :in $ @@ -693,6 +973,8 @@ fn test_query_saved_pages() { #[test] fn test_query_pages_matching_string_1() { + use self::Value::*; + /* // Original query :find '[?url ?title] @@ -769,6 +1051,8 @@ fn test_query_pages_matching_string_1() { #[test] fn test_query_pages_matching_string_2() { + use self::Value::*; + /* // Original query :find '[?url ?title ?excerpt] @@ -861,6 +1145,8 @@ fn test_query_pages_matching_string_2() { #[test] fn test_query_visited() { + use self::Value::*; + /* // Original query :find '[?url ?title (max ?time)] @@ -903,6 +1189,8 @@ fn test_query_visited() { #[test] fn test_query_find_title() { + use self::Value::*; + /* // Original query :find ?title . @@ -1075,7 +1363,7 @@ fn test_is_and_as_type_helper_functions() { s_ns("$ns", "$symbol"), k_plain("hello"), k_ns("hello", "world"), - Value::Vector(vec![Integer(1)]), + Value::Vector(vec![Value::Integer(1)]), Value::List(LinkedList::from_iter(vec![])), Value::Set(BTreeSet::from_iter(vec![])), Value::Map(BTreeMap::from_iter(vec![ @@ -1125,7 +1413,7 @@ fn test_is_and_as_type_helper_functions() { def_test_as_type!(value, as_namespaced_symbol, i == 7, symbols::NamespacedSymbol::new("$ns", "$symbol")); def_test_as_type!(value, as_keyword, i == 8, symbols::Keyword::new("hello")); def_test_as_type!(value, as_namespaced_keyword, i == 9, symbols::NamespacedKeyword::new("hello", "world")); - def_test_as_type!(value, as_vector, i == 10, vec![Integer(1)]); + def_test_as_type!(value, as_vector, i == 10, vec![Value::Integer(1)]); def_test_as_type!(value, as_list, i == 11, LinkedList::from_iter(vec![])); def_test_as_type!(value, as_set, i == 12, BTreeSet::from_iter(vec![])); def_test_as_type!(value, as_map, i == 13, BTreeMap::from_iter(vec![ @@ -1143,7 +1431,7 @@ fn test_is_and_as_type_helper_functions() { def_test_into_type!(value, into_namespaced_symbol, i == 7, symbols::NamespacedSymbol::new("$ns", "$symbol")); def_test_into_type!(value, into_keyword, i == 8, symbols::Keyword::new("hello")); def_test_into_type!(value, into_namespaced_keyword, i == 9, symbols::NamespacedKeyword::new("hello", "world")); - def_test_into_type!(value, into_vector, i == 10, vec![Integer(1)]); + def_test_into_type!(value, into_vector, i == 10, vec![Value::Integer(1)]); def_test_into_type!(value, into_list, i == 11, LinkedList::from_iter(vec![])); def_test_into_type!(value, into_set, i == 12, BTreeSet::from_iter(vec![])); def_test_into_type!(value, into_map, i == 13, BTreeMap::from_iter(vec![ diff --git a/query-parser/src/find.rs b/query-parser/src/find.rs index 5a35f7f9..0a626a5d 100644 --- a/query-parser/src/find.rs +++ b/query-parser/src/find.rs @@ -166,7 +166,7 @@ impl From for QueryParseError { pub fn parse_find_string(string: &str) -> QueryParseResult { let expr = edn::parse::value(string)?; - parse_find(expr) + parse_find(expr.without_spans()) } pub fn parse_find(expr: edn::Value) -> QueryParseResult { @@ -187,7 +187,7 @@ mod test_parse { extern crate edn; use self::edn::{NamespacedKeyword, PlainSymbol}; - use self::edn::types::{to_keyword, to_symbol}; + use self::edn::types::Value; use super::mentat_query::{ Element, FindSpec, @@ -203,18 +203,18 @@ mod test_parse { // TODO: when #224 lands, fix to_keyword to be variadic. #[test] fn test_parse_find() { - let truncated_input = edn::Value::Vector(vec![to_keyword(None, "find")]); + let truncated_input = edn::Value::Vector(vec![Value::from_keyword(None, "find")]); assert!(parse_find(truncated_input).is_err()); let input = edn::Value::Vector(vec![ - to_keyword(None, "find"), - to_symbol(None, "?x"), - to_symbol(None, "?y"), - to_keyword(None, "where"), + Value::from_keyword(None, "find"), + Value::from_symbol(None, "?x"), + Value::from_symbol(None, "?y"), + Value::from_keyword(None, "where"), edn::Value::Vector(vec![ - to_symbol(None, "?x"), - to_keyword("foo", "bar"), - to_symbol(None, "?y"), + Value::from_symbol(None, "?x"), + Value::from_keyword("foo", "bar"), + Value::from_symbol(None, "?y"), ]), ]); diff --git a/query-parser/src/lib.rs b/query-parser/src/lib.rs index ed0ee048..5805592d 100644 --- a/query-parser/src/lib.rs +++ b/query-parser/src/lib.rs @@ -8,6 +8,8 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +#![allow(unused_imports)] + #[macro_use] extern crate matches; diff --git a/tx-parser/tests/parser.rs b/tx-parser/tests/parser.rs index f0a7add4..255243fc 100644 --- a/tx-parser/tests/parser.rs +++ b/tx-parser/tests/parser.rs @@ -26,7 +26,7 @@ fn test_entities() { [:db/add "tempid" :test/a "v"] [:db/retract 102 :test/b "w"]]"#; - let edn = parse::value(input).unwrap(); + let edn = parse::value(input).unwrap().without_spans(); let input = [edn]; let result = Tx::parse(&input[..]);