Add a span component to edn::Value, r=ncalexan

Signed-off-by: Victor Porof <victor.porof@gmail.com>
This commit is contained in:
Victor Porof 2017-02-14 16:43:32 +01:00
parent d9b699b588
commit 896d7f8f88
8 changed files with 819 additions and 308 deletions

View file

@ -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()

View file

@ -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);

View file

@ -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::<BigInt>().unwrap())
pub bigint -> ValueAndSpan =
start:#position b:$( sign? digit+ ) "N" end:#position {
ValueAndSpan {
inner: SpannedValue::BigInteger(b.parse::<BigInt>().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::<u32>().unwrap()).unwrap())
start:#position b:$( validbase ) "r" i:$( alphanumeric+ ) end:#position {
ValueAndSpan {
inner: SpannedValue::Integer(i64::from_str_radix(i, b.parse::<u32>().unwrap()).unwrap()),
span: Span(start, end)
}
}
pub integer -> Value =
i:$( sign? digit+ ) {
Value::Integer(i.parse::<i64>().unwrap())
pub integer -> ValueAndSpan =
start:#position i:$( sign? digit+ ) end:#position {
ValueAndSpan {
inner: SpannedValue::Integer(i.parse::<i64>().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::<f64>().unwrap()))
pub float -> ValueAndSpan =
start:#position f:$( frac_exp / exp / frac ) end:#position {
ValueAndSpan {
inner: SpannedValue::Float(OrderedFloat(f.parse::<f64>().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
}

View file

@ -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<f64>),
Text(String),
PlainSymbol(symbols::PlainSymbol),
@ -44,68 +43,82 @@ pub enum Value {
Map(BTreeMap<Value, Value>),
}
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<f64>),
Text(String),
PlainSymbol(symbols::PlainSymbol),
NamespacedSymbol(symbols::NamespacedSymbol),
Keyword(symbols::Keyword),
NamespacedKeyword(symbols::NamespacedKeyword),
Vector(Vec<ValueAndSpan>),
List(LinkedList<ValueAndSpan>),
Set(BTreeSet<ValueAndSpan>),
Map(BTreeMap<ValueAndSpan, ValueAndSpan>),
}
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<SpannedValue> 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 `<Option<i64>`.
/// 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<i64>`.
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 `<Option<&BigInt>`.
/// 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 `<Option<BigInt>`.
/// 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<BigInt>`.
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<f64>| v.into_inner());
def_as_ref!(as_big_integer, BigInteger, BigInt);
def_as_ref!(as_ordered_float, Float, OrderedFloat<f64>);
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<Value>);
def_as_ref!(as_list, List, LinkedList<Value>);
def_as_ref!(as_set, Set, BTreeSet<Value>);
def_as_ref!(as_map, Map, BTreeMap<Value, Value>);
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<f64>,);
def_into!(into_float, Float, f64, |v: OrderedFloat<f64>| 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<Value>,);
def_into!(into_list, List, LinkedList<Value>,);
def_into!(into_set, Set, BTreeSet<Value>,);
def_into!(into_map, Map, BTreeMap<Value, Value>,);
pub fn from_bigint(src: &str) -> Option<Value> {
src.parse::<BigInt>().map(Value::BigInteger).ok()
}
pub fn from_symbol<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
to_symbol(namespace, name)
}
pub fn from_keyword<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> Value {
to_keyword(namespace, name)
}
}
impl From<f64> for Value {
fn from(src: f64) -> Value {
Value::Float(OrderedFloat::from(src))
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Value) -> Option<Ordering> {
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<Option<&'a str>>>(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<Option<&'a str>>>(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<Option<&'a str>>>(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<f64>| v.into_inner());
def_as_ref!(as_big_integer, $t::BigInteger, BigInt);
def_as_ref!(as_ordered_float, $t::Float, OrderedFloat<f64>);
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<f64>,);
def_into!(into_float, $t::Float, f64, |v: OrderedFloat<f64>| 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::<BigInt>().ok());
def_from!(from_float, $t, $t::Float, f64, |src: f64| OrderedFloat::from(src));
def_from!(from_ordered_float, $t, $t::Float, OrderedFloat<f64>,);
pub fn from_symbol<'a, T: Into<Option<&'a str>>>(namespace: T, name: &str) -> $t {
to_symbol!(namespace, name, $t)
}
pub fn from_keyword<'a, T: Into<Option<&'a str>>>(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<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd for SpannedValue {
fn partial_cmp(&self, other: &SpannedValue) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialOrd for ValueAndSpan {
fn partial_cmp(&self, other: &ValueAndSpan) -> Option<Ordering> {
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);

View file

@ -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<Value, ParseError> 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![

View file

@ -166,7 +166,7 @@ impl From<edn::parse::ParseError> 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"),
]),
]);

View file

@ -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;

View file

@ -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[..]);