Move stdlib into its own crate

This commit is contained in:
Richard Feldman 2019-07-22 22:04:28 -04:00
parent 4502d2630f
commit c7a923d226
11 changed files with 334 additions and 81 deletions

View file

@ -4,8 +4,6 @@ pub mod parse_state;
pub mod eval; pub mod eval;
pub mod operator; pub mod operator;
pub mod region; pub mod region;
pub mod fast_fraction;
pub mod stdlib;
mod collections; mod collections;
// mod ena; // mod ena;

View file

@ -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<T> {
value: f64,
phantom: PhantomData<T>
}
/// 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<T> Approx<T> {
pub fn abs(num: &Self) -> Self {
Approx { value: num.value.abs(), phantom: PhantomData }
}
}

108
stdlib/Cargo.lock generated Normal file
View file

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

11
stdlib/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "roc-std"
version = "0.1.0"
authors = ["Richard Feldman <oss@rtfeldman.com>"]
[dependencies]
im-rc = "13.0.0"
fxhash = "0.2.1"
[dev-dependencies]
pretty_assertions = "0.5.1"

114
stdlib/src/approx.rs Normal file
View file

@ -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<T> {
value: f64,
phantom: PhantomData<T>
}
pub struct Valid;
pub type Approx = Approximation<Valid>;
impl Hash for Approx {
fn hash<H: Hasher>(&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<T> Approximation<T> {
pub fn abs(num: &Self) -> Self {
Approximation { value: num.value.abs(), phantom: PhantomData }
}
/// Returns whether the approximation is valid.
/// For an Approx<Valid>, 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<V>(self) -> Option<Approximation<Valid>> {
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<V>(self, fallback: V) -> Result<Approximation<Valid>, V> {
if self.is_valid() {
Ok(Approximation {value: self.value, phantom: PhantomData})
} else {
Err(fallback)
}
}
pub fn valid_or_else<F, V>(self, fallback_fn: F) -> Result<Approximation<Valid>, V>
where F: Fn() -> V
{
if self.is_valid() {
Ok(Approximation {value: self.value, phantom: PhantomData})
} else {
Err(fallback_fn())
}
}
}
impl<T> From<f64> for Approximation<T> {
fn from(num: f64) -> Self {
Approximation { value: num, phantom: PhantomData }
}
}
impl<T> From<f32> for Approximation<T> {
fn from(num: f32) -> Self {
Approximation { value: num as f64, phantom: PhantomData }
}
}
impl<T> Into<f64> for Approximation<T> {
fn into(self) -> f64 {
self.value
}
}
impl<T> Into<f32> for Approximation<T> {
fn into(self) -> f32 {
self.value as f32
}
}

2
stdlib/src/default.rs Normal file
View file

@ -0,0 +1,2 @@
/// A plain old bool.
pub type Bool = bool;

View file

@ -432,17 +432,17 @@ impl<T> Fraction<T> {
/// Otherwise, return None. /// Otherwise, return None.
pub fn into_valid<V>(self) -> Option<Fraction<Valid>> { pub fn into_valid<V>(self) -> Option<Fraction<Valid>> {
if self.is_valid() { if self.is_valid() {
Some(unsafe { std::mem::transmute::<Fraction<T>, Fraction<Valid>>(self) }) Some(Fraction {numerator: self.numerator, denominator: self.denominator, phantom: PhantomData})
} else { } else {
None None
} }
} }
/// If the fraction is valid, returns it with the type variable set accordingly. /// If the fraction is valid, return it with the type variable set accordingly.
/// Otherwise, returns the fallback value. /// Otherwise, return the fallback value.
pub fn valid_or<V>(self, fallback: V) -> Result<Fraction<Valid>, V> { pub fn valid_or<V>(self, fallback: V) -> Result<Fraction<Valid>, V> {
if self.is_valid() { if self.is_valid() {
Ok(unsafe { std::mem::transmute::<Fraction<T>, Fraction<Valid>>(self) }) Ok(Fraction {numerator: self.numerator, denominator: self.denominator, phantom: PhantomData})
} else { } else {
Err(fallback) Err(fallback)
} }
@ -452,7 +452,7 @@ impl<T> Fraction<T> {
where F: Fn() -> V where F: Fn() -> V
{ {
if self.is_valid() { if self.is_valid() {
Ok(unsafe { std::mem::transmute::<Fraction<T>, Fraction<Valid>>(self) }) Ok(Fraction {numerator: self.numerator, denominator: self.denominator, phantom: PhantomData})
} else { } else {
Err(fallback_fn()) Err(fallback_fn())
} }
@ -578,22 +578,14 @@ impl<T> Fraction<T> {
self_numerator.cmp(&other_numerator) self_numerator.cmp(&other_numerator)
} }
/// If the Frac is valid, transforms it with the given function. #[inline(always)]
/// If the Frac is invalid, returns it unchanged. pub fn numerator(&self) -> i64 {
pub fn map<F>(self, transform: F) -> Self self.numerator
where F: FnOnce(Fraction<Valid>) -> Fraction<Valid> }
{
if self.is_valid() { #[inline(always)]
unsafe { pub fn denominator(&self) -> i64 {
std::mem::transmute::<Fraction<Valid>, Fraction<T>>( self.denominator
transform(
std::mem::transmute::<Fraction<T>, Fraction<Valid>>(self)
)
)
}
} else {
self
}
} }
} }

20
stdlib/src/int.rs Normal file
View file

@ -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!");
}

9
stdlib/src/lib.rs Normal file
View file

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

15
stdlib/src/map.rs Normal file
View file

@ -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<K, V> {
store: HashMap<K, V, BuildHasherDefault<FxHasher>>;
order: Vector<K>
}
impl<K, V> Map<K, V> {
pub fn is_empty(self) -> bool {
self.store.is_empty()
}
}

42
stdlib/src/set.rs Normal file
View file

@ -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<K> {
store: HashSet<K>,
order: Vector<K>
}
impl<K> Set<K>
where K : std::hash::Hash
{
pub fn is_empty(self) -> bool {
self.store.is_empty()
}
pub fn insert<B>(self, elem: K) -> Set<B>
{
let mut new_set: Set<K> = self.clone();
new_set.store.insert(elem);
new_set.order.insert(elem);
new_set
}
pub fn map<F, B>(self, transform: F) -> Set<B>
where F: Fn(K) -> B,
B: Hash
{
let mut new_set: Set<B> = Set::new();
for elem in self.order.iter() {
if self.store.contains(elem) {
new_set.insert(transform(elem))
}
}
new_set
}
}