diff --git a/edn/src/edn.rustpeg b/edn/src/edn.rustpeg index 3dc619e6..8d854dec 100644 --- a/edn/src/edn.rustpeg +++ b/edn/src/edn.rustpeg @@ -12,6 +12,7 @@ use std::collections::{BTreeSet, BTreeMap, LinkedList}; use std::iter::FromIterator; +use std::f64::{NAN, INFINITY, NEG_INFINITY}; use num::BigInt; use ordered_float::OrderedFloat; @@ -30,6 +31,14 @@ use types::Value; pub nil -> Value = "nil" { Value::Nil } +pub nan -> Value = + "#f NaN" { Value::Float(OrderedFloat(NAN)) } + +pub infinity -> Value = + "#f" s:$(sign) "Infinity" { + Value::Float(OrderedFloat(if s == "+" { INFINITY } else { NEG_INFINITY })) + } + pub boolean -> Value = "true" { Value::Boolean(true) } / "false" { Value::Boolean(false) } @@ -125,7 +134,7 @@ pub map -> Value = // It's important that float comes before integer or the parser assumes that // floats are integers and fails to parse pub value -> Value = - __ v:(nil / boolean / float / bigint / integer / text / keyword / symbol / list / vector / map / set) __ { + __ v:(nil / nan / infinity / boolean / float / bigint / integer / text / keyword / symbol / list / vector / map / set) __ { v } diff --git a/edn/src/types.rs b/edn/src/types.rs index 0042851c..9f08e32d 100644 --- a/edn/src/types.rs +++ b/edn/src/types.rs @@ -11,6 +11,7 @@ use std::collections::{BTreeSet, BTreeMap, LinkedList}; use std::cmp::{Ordering, Ord, PartialOrd}; use std::fmt::{Display, Formatter}; +use std::f64; use symbols; use num::BigInt; @@ -54,7 +55,19 @@ impl Display for Value { Boolean(v) => write!(f, "{}", v), Integer(v) => write!(f, "{}", v), BigInteger(ref v) => write!(f, "{}N", v), - Float(OrderedFloat(v)) => write!(f, "{}", v), + Float(ref v) => { + // TODO: make sure float syntax is correct. + 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), @@ -266,6 +279,7 @@ mod test { use std::collections::{BTreeSet, BTreeMap, LinkedList}; use std::cmp::{Ordering}; + use std::f64; use symbols; use num::BigInt; @@ -280,7 +294,7 @@ mod test { #[test] fn test_print_edn() { - assert_eq!("[ 1 2 ( 3.14 ) #{ 4N } { :foo/bar 42 } [ ] :five :six/seven eight nine/ten true false nil ]", + assert_eq!("[ 1 2 ( 3.14 ) #{ 4N } { :foo/bar 42 } [ ] :five :six/seven eight nine/ten true false nil #f NaN #f -Infinity #f +Infinity ]", Value::Vector(vec![ Value::Integer(1), Value::Integer(2), @@ -300,7 +314,10 @@ mod test { Value::NamespacedSymbol(symbols::NamespacedSymbol::new("nine", "ten")), Value::Boolean(true), Value::Boolean(false), - Value::Nil + Value::Nil, + Value::Float(OrderedFloat(f64::NAN)), + Value::Float(OrderedFloat(f64::NEG_INFINITY)), + Value::Float(OrderedFloat(f64::INFINITY)), ] ).to_string()); } @@ -323,4 +340,4 @@ mod test { assert_eq!(Value::Set(BTreeSet::new()).cmp(&Value::Set(BTreeSet::new())), Ordering::Equal); assert_eq!(Value::Map(BTreeMap::new()).cmp(&Value::Map(BTreeMap::new())), Ordering::Equal); } -} \ No newline at end of file +} diff --git a/edn/tests/tests.rs b/edn/tests/tests.rs index f34603c9..0a0513c5 100644 --- a/edn/tests/tests.rs +++ b/edn/tests/tests.rs @@ -14,6 +14,7 @@ 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; @@ -50,6 +51,21 @@ fn test_nil() { assert!(nil("true").is_err()); } +#[test] +fn test_nan() { + assert_eq!(nan("#f NaN").unwrap(), Float(OrderedFloat(f64::NAN))); + + assert!(nan("true").is_err()); +} + +#[test] +fn test_infinity() { + assert_eq!(infinity("#f-Infinity").unwrap(), Float(OrderedFloat(f64::NEG_INFINITY))); + assert_eq!(infinity("#f+Infinity").unwrap(), Float(OrderedFloat(f64::INFINITY))); + + assert!(infinity("true").is_err()); +} + #[test] fn test_boolean() { assert_eq!(boolean("true").unwrap(), Boolean(true));