mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
Move stdlib into its own crate
This commit is contained in:
parent
4502d2630f
commit
c7a923d226
11 changed files with 334 additions and 81 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
108
stdlib/Cargo.lock
generated
Normal 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
11
stdlib/Cargo.toml
Normal 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
114
stdlib/src/approx.rs
Normal 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
2
stdlib/src/default.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// A plain old bool.
|
||||
pub type Bool = bool;
|
|
@ -432,17 +432,17 @@ impl<T> Fraction<T> {
|
|||
/// Otherwise, return None.
|
||||
pub fn into_valid<V>(self) -> Option<Fraction<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 {
|
||||
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<V>(self, fallback: V) -> Result<Fraction<Valid>, V> {
|
||||
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 {
|
||||
Err(fallback)
|
||||
}
|
||||
|
@ -452,7 +452,7 @@ impl<T> Fraction<T> {
|
|||
where F: Fn() -> V
|
||||
{
|
||||
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 {
|
||||
Err(fallback_fn())
|
||||
}
|
||||
|
@ -578,22 +578,14 @@ impl<T> Fraction<T> {
|
|||
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<F>(self, transform: F) -> Self
|
||||
where F: FnOnce(Fraction<Valid>) -> Fraction<Valid>
|
||||
{
|
||||
if self.is_valid() {
|
||||
unsafe {
|
||||
std::mem::transmute::<Fraction<Valid>, Fraction<T>>(
|
||||
transform(
|
||||
std::mem::transmute::<Fraction<T>, Fraction<Valid>>(self)
|
||||
)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
self
|
||||
}
|
||||
#[inline(always)]
|
||||
pub fn numerator(&self) -> i64 {
|
||||
self.numerator
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn denominator(&self) -> i64 {
|
||||
self.denominator
|
||||
}
|
||||
}
|
||||
|
20
stdlib/src/int.rs
Normal file
20
stdlib/src/int.rs
Normal 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
9
stdlib/src/lib.rs
Normal 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
15
stdlib/src/map.rs
Normal 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
42
stdlib/src/set.rs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue