diff --git a/src/lib.rs b/src/lib.rs index c94aaf905f..1cc81e9d47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,8 +4,6 @@ pub mod parse_state; pub mod eval; pub mod operator; pub mod region; -pub mod fast_fraction; -pub mod stdlib; mod collections; // mod ena; diff --git a/src/stdlib.rs b/src/stdlib.rs deleted file mode 100644 index 3c7a1b1994..0000000000 --- a/src/stdlib.rs +++ /dev/null @@ -1,58 +0,0 @@ - -use std::marker::PhantomData; - -/// Approx is stored as an f64 under the hood. -/// -/// However, part of Roc's design is that Roc users never encounter Infinity, -/// -Infinity, NaN, or -0. To Roc application authors, the only difference between -/// Approx and Frac is that Approx supports a few more operations (sqrt, -/// trigonometry, etc) and is potentially imprecise. -/// -/// To achieve this, Roc maps all invalid Float values (NaN, Infinity, -Infinity) to -/// Err values. This means that any value which could contain one of these bit patterns -/// is typed as Result { ok: Approx, err: InvalidApprox }, including any Approx values -/// passed into Roc. -/// -/// Roc code does not have the expressive power to create NaN, Infinity, or -Infinity, -/// so the Approx type inside Roc represents an f64 that is guaratneed not to be NaN, -/// Infinity, or -Infinity. -/// -/// Additionally, the implementation detail of 0 and -0 being different f64 values does -/// not reach Roc code because there is no way to convert an Approx directly to a String. -/// Instead, Approx must go through conversion to either a Frac or an Int, neither of -/// which is capable of representing -0. In f64 operations, 0 and -0 are considered -/// equivalent, so the distinction does not matter there either. -pub struct Approx { - value: f64, - phantom: PhantomData -} - -/// A plain old i64. -pub struct Int(i64); - -/// A plain old bool. -pub type Bool = bool; - -fn underflow_panic() -> ! { - panic!("Underflow!"); -} - -impl Int { - pub fn abs(&self) -> Self { - let Int(int_self) = self; - - let (output, underflowed) = int_self.overflowing_abs(); - - if underflowed { - underflow_panic(); - } - - Int(output) - } -} - -impl Approx { - pub fn abs(num: &Self) -> Self { - Approx { value: num.value.abs(), phantom: PhantomData } - } -} \ No newline at end of file diff --git a/stdlib/Cargo.lock b/stdlib/Cargo.lock new file mode 100644 index 0000000000..41aa83c0fa --- /dev/null +++ b/stdlib/Cargo.lock @@ -0,0 +1,108 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "im-rc" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "sized-chunks 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pretty_assertions" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "roc-std" +version = "0.1.0" +dependencies = [ + "im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "sized-chunks" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "typenum" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" +"checksum im-rc 13.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0197597d095c0d11107975d3175173f810ee572c2501ff4de64f4f3f119806" +"checksum pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a029430f0d744bc3d15dd474d591bed2402b645d024583082b9f63bb936dac6" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum sized-chunks 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2a2eb3fe454976eefb479f78f9b394d34d661b647c6326a3a6e66f68bb12c26" +"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/stdlib/Cargo.toml b/stdlib/Cargo.toml new file mode 100644 index 0000000000..1e3a0c0f5a --- /dev/null +++ b/stdlib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "roc-std" +version = "0.1.0" +authors = ["Richard Feldman "] + +[dependencies] +im-rc = "13.0.0" +fxhash = "0.2.1" + +[dev-dependencies] +pretty_assertions = "0.5.1" diff --git a/stdlib/src/approx.rs b/stdlib/src/approx.rs new file mode 100644 index 0000000000..cd004aab06 --- /dev/null +++ b/stdlib/src/approx.rs @@ -0,0 +1,114 @@ +use std::marker::PhantomData; +use std::hash::{Hash, Hasher}; + +/// Approx is stored as an f64 under the hood. +/// +/// However, part of Roc's design is that Roc users never encounter Infinity, +/// -Infinity, NaN, or -0. To Roc application authors, the only difference between +/// Approx and Frac is that Approx supports a few more operations (sqrt, +/// trigonometry, etc) and is potentially imprecise. +/// +/// To achieve this, Roc maps all invalid Float values (NaN, Infinity, -Infinity) to +/// Err values. This means that any value which could contain one of these bit patterns +/// is typed as Result { ok: Approx, err: InvalidApprox }, including any Approx values +/// passed into Roc. +/// +/// Roc code does not have the expressive power to create NaN, Infinity, or -Infinity, +/// so the Approx type inside Roc represents an f64 that is guaratneed not to be NaN, +/// Infinity, or -Infinity. +/// +/// Additionally, the implementation detail of 0 and -0 being different f64 values does +/// not reach Roc code because there is no way to convert an Approx directly to a String. +/// Instead, Approx must go through conversion to either a Frac or an Int, neither of +/// which is capable of representing -0. In f64 operations, 0 and -0 are considered +/// equivalent, so the distinction does not matter there either. +pub struct Approximation { + value: f64, + phantom: PhantomData +} + +pub struct Valid; + +pub type Approx = Approximation; + +impl Hash for Approx { + fn hash(&self, state: &mut H) { + panic!("TODO: implement using integer_decode"); + // let (man, exp, sign) = f.integer_decode(); + + // if man == 0 { + // // Consolidate the representation of zero, whether signed or not + // // The IEEE standard considers positive and negative zero to be equal + // 0 + // } else { + // (man ^ ((exp as u64) << 48) ^ sign as u64) + // }.hash(state) + } +} + +impl Approximation { + pub fn abs(num: &Self) -> Self { + Approximation { value: num.value.abs(), phantom: PhantomData } + } + + /// Returns whether the approximation is valid. + /// For an Approx, this will always return true. + #[inline(always)] + pub fn is_valid(&self) -> bool { + self.value.is_finite() + } + + /// If the approximation is valid, return it wrapped in Some. + /// Otherwise, return None. + pub fn into_valid(self) -> Option> { + if self.is_valid() { + Some(Approximation {value: self.value, phantom: PhantomData}) + } else { + None + } + } + + /// If the approximation is valid, return it with the type variable set accordingly. + /// Otherwise, return the fallback value. + pub fn valid_or(self, fallback: V) -> Result, V> { + if self.is_valid() { + Ok(Approximation {value: self.value, phantom: PhantomData}) + } else { + Err(fallback) + } + } + + pub fn valid_or_else(self, fallback_fn: F) -> Result, V> + where F: Fn() -> V + { + if self.is_valid() { + Ok(Approximation {value: self.value, phantom: PhantomData}) + } else { + Err(fallback_fn()) + } + } +} + +impl From for Approximation { + fn from(num: f64) -> Self { + Approximation { value: num, phantom: PhantomData } + } +} + +impl From for Approximation { + fn from(num: f32) -> Self { + Approximation { value: num as f64, phantom: PhantomData } + } +} + +impl Into for Approximation { + fn into(self) -> f64 { + self.value + } +} + +impl Into for Approximation { + fn into(self) -> f32 { + self.value as f32 + } +} \ No newline at end of file diff --git a/stdlib/src/default.rs b/stdlib/src/default.rs new file mode 100644 index 0000000000..ce8e20835a --- /dev/null +++ b/stdlib/src/default.rs @@ -0,0 +1,2 @@ +/// A plain old bool. +pub type Bool = bool; \ No newline at end of file diff --git a/src/fast_fraction.rs b/stdlib/src/frac.rs similarity index 97% rename from src/fast_fraction.rs rename to stdlib/src/frac.rs index a3e3648e57..1e2d18300a 100644 --- a/src/fast_fraction.rs +++ b/stdlib/src/frac.rs @@ -432,17 +432,17 @@ impl Fraction { /// Otherwise, return None. pub fn into_valid(self) -> Option> { if self.is_valid() { - Some(unsafe { std::mem::transmute::, Fraction>(self) }) + Some(Fraction {numerator: self.numerator, denominator: self.denominator, phantom: PhantomData}) } else { None } } - /// If the fraction is valid, returns it with the type variable set accordingly. - /// Otherwise, returns the fallback value. + /// If the fraction is valid, return it with the type variable set accordingly. + /// Otherwise, return the fallback value. pub fn valid_or(self, fallback: V) -> Result, V> { if self.is_valid() { - Ok(unsafe { std::mem::transmute::, Fraction>(self) }) + Ok(Fraction {numerator: self.numerator, denominator: self.denominator, phantom: PhantomData}) } else { Err(fallback) } @@ -452,7 +452,7 @@ impl Fraction { where F: Fn() -> V { if self.is_valid() { - Ok(unsafe { std::mem::transmute::, Fraction>(self) }) + Ok(Fraction {numerator: self.numerator, denominator: self.denominator, phantom: PhantomData}) } else { Err(fallback_fn()) } @@ -578,22 +578,14 @@ impl Fraction { self_numerator.cmp(&other_numerator) } - /// If the Frac is valid, transforms it with the given function. - /// If the Frac is invalid, returns it unchanged. - pub fn map(self, transform: F) -> Self - where F: FnOnce(Fraction) -> Fraction - { - if self.is_valid() { - unsafe { - std::mem::transmute::, Fraction>( - transform( - std::mem::transmute::, Fraction>(self) - ) - ) - } - } else { - self - } + #[inline(always)] + pub fn numerator(&self) -> i64 { + self.numerator + } + + #[inline(always)] + pub fn denominator(&self) -> i64 { + self.denominator } } diff --git a/stdlib/src/int.rs b/stdlib/src/int.rs new file mode 100644 index 0000000000..7796984608 --- /dev/null +++ b/stdlib/src/int.rs @@ -0,0 +1,20 @@ +/// An i64 that always panics on overflow. +pub struct Int(i64); + +impl Int { + pub fn abs(&self) -> Self { + let Int(int_self) = self; + + let (output, underflowed) = int_self.overflowing_abs(); + + if underflowed { + underflow_panic(); + } + + Int(output) + } +} + +fn underflow_panic() -> ! { + panic!("Underflow!"); +} \ No newline at end of file diff --git a/stdlib/src/lib.rs b/stdlib/src/lib.rs new file mode 100644 index 0000000000..92f0da0b10 --- /dev/null +++ b/stdlib/src/lib.rs @@ -0,0 +1,9 @@ +pub mod frac; +pub mod approx; +pub mod map; +pub mod set; +pub mod int; +pub mod default; + +extern crate im_rc; +extern crate fxhash; \ No newline at end of file diff --git a/stdlib/src/map.rs b/stdlib/src/map.rs new file mode 100644 index 0000000000..15387c66ae --- /dev/null +++ b/stdlib/src/map.rs @@ -0,0 +1,15 @@ +use im_rc::hashmap::HashMap; +use im_rc::vector::Vector; +use fxhash::FxHasher; + +/// A persistent HashMap which records insertion order and iterates in that order. +pub struct Map { + store: HashMap>; + order: Vector +} + +impl Map { + pub fn is_empty(self) -> bool { + self.store.is_empty() + } +} \ No newline at end of file diff --git a/stdlib/src/set.rs b/stdlib/src/set.rs new file mode 100644 index 0000000000..f688d3f747 --- /dev/null +++ b/stdlib/src/set.rs @@ -0,0 +1,42 @@ +use im_rc::hashset::HashSet; +use std::hash::{Hash}; +use im_rc::vector::Vector; + +/// A persistent Set which records insertion order and iterates in that order. +pub struct Set { + store: HashSet, + order: Vector +} + +impl Set +where K : std::hash::Hash +{ + pub fn is_empty(self) -> bool { + self.store.is_empty() + } + + pub fn insert(self, elem: K) -> Set + { + let mut new_set: Set = self.clone(); + + new_set.store.insert(elem); + new_set.order.insert(elem); + + new_set + } + + pub fn map(self, transform: F) -> Set + where F: Fn(K) -> B, + B: Hash + { + let mut new_set: Set = Set::new(); + + for elem in self.order.iter() { + if self.store.contains(elem) { + new_set.insert(transform(elem)) + } + } + + new_set + } +} \ No newline at end of file