Add a span component to edn::Value, r=ncalexan
Signed-off-by: Victor Porof <victor.porof@gmail.com>
This commit is contained in:
parent
d9b699b588
commit
896d7f8f88
8 changed files with 819 additions and 308 deletions
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
569
edn/src/types.rs
569
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<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);
|
||||
|
|
|
@ -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,14 +766,14 @@ 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![
|
||||
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),]))),
|
||||
(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),]))),
|
||||
|
@ -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![
|
||||
|
|
|
@ -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"),
|
||||
]),
|
||||
]);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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[..]);
|
||||
|
|
Loading…
Reference in a new issue