mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Merge pull request #3815 from roc-lang/glue-dict
Generate glue for `Dict` and `Set`
This commit is contained in:
commit
f2f4fb59b5
@ -41,10 +41,39 @@ insert = \@Set dict, key ->
|
||||
|> Dict.insert key {}
|
||||
|> @Set
|
||||
|
||||
# Inserting a duplicate key has no effect.
|
||||
expect
|
||||
actual =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "baz"
|
||||
|
||||
expected =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "baz"
|
||||
|
||||
expected == actual
|
||||
|
||||
len : Set k -> Nat
|
||||
len = \@Set dict ->
|
||||
Dict.len dict
|
||||
|
||||
# Inserting a duplicate key has no effect on length.
|
||||
expect
|
||||
actual =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "baz"
|
||||
|> Set.len
|
||||
|
||||
actual == 3
|
||||
|
||||
## Drops the given element from the set.
|
||||
remove : Set k, k -> Set k
|
||||
remove = \@Set dict, key ->
|
||||
|
@ -3018,7 +3018,7 @@ impl RecordFields {
|
||||
(it, ext)
|
||||
}
|
||||
|
||||
/// Get a sorted iterator over the fields of this record type
|
||||
/// get a sorted iterator over the fields of this record type
|
||||
///
|
||||
/// Implementation: When the record has an `ext` variable that is the empty record, then
|
||||
/// we read the (assumed sorted) fields directly from Subs. Otherwise we have to chase the
|
||||
|
@ -18,7 +18,7 @@ use roc_mono::layout::{
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::{
|
||||
subs::{Content, FlatType, GetSubsSlice, Subs, UnionLabels, UnionTags, Variable},
|
||||
types::RecordField,
|
||||
types::{AliasKind, RecordField},
|
||||
};
|
||||
use std::fmt::Display;
|
||||
|
||||
@ -934,7 +934,97 @@ fn add_builtin_type<'a>(
|
||||
|
||||
list_id
|
||||
}
|
||||
(layout, typ) => todo!("Handle builtin layout {:?} and type {:?}", layout, typ),
|
||||
(
|
||||
Builtin::List(elem_layout),
|
||||
Alias(Symbol::DICT_DICT, _alias_variables, alias_var, AliasKind::Opaque),
|
||||
) => {
|
||||
match (
|
||||
elem_layout,
|
||||
env.subs.get_content_without_compacting(*alias_var),
|
||||
) {
|
||||
(
|
||||
Layout::Struct { field_layouts, .. },
|
||||
Content::Structure(FlatType::Apply(Symbol::LIST_LIST, args_subs_slice)),
|
||||
) => {
|
||||
let (key_var, val_var) = {
|
||||
let args_tuple = env.subs.get_subs_slice(*args_subs_slice);
|
||||
|
||||
debug_assert_eq!(args_tuple.len(), 1);
|
||||
|
||||
match env.subs.get_content_without_compacting(args_tuple[0]) {
|
||||
Content::Structure(FlatType::TagUnion(union_tags, ext_var)) => {
|
||||
let (mut iter, _) = union_tags.sorted_iterator_and_ext(env.subs, *ext_var);
|
||||
let payloads = iter.next().unwrap().1;
|
||||
|
||||
debug_assert_eq!(iter.next(), None);
|
||||
|
||||
(payloads[0], payloads[1])
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
debug_assert_eq!(field_layouts.len(), 2);
|
||||
|
||||
let key_id = add_type_help(env, field_layouts[0], key_var, opt_name, types);
|
||||
let val_id = add_type_help(env, field_layouts[1], val_var, opt_name, types);
|
||||
let dict_id = types.add_anonymous(RocType::RocDict(key_id, val_id), layout);
|
||||
|
||||
types.depends(dict_id, key_id);
|
||||
types.depends(dict_id, val_id);
|
||||
|
||||
dict_id
|
||||
}
|
||||
(elem_layout, alias_content) => unreachable!(
|
||||
"Unrecognized List element for Dict. Layout was: {:?} and alias_content was: {:?}",
|
||||
elem_layout,
|
||||
alias_content
|
||||
),
|
||||
}
|
||||
}
|
||||
(
|
||||
Builtin::List(elem_layout),
|
||||
Alias(Symbol::SET_SET, _alias_vars, alias_var, AliasKind::Opaque),
|
||||
) => {
|
||||
match (
|
||||
elem_layout,
|
||||
env.subs.get_content_without_compacting(*alias_var),
|
||||
) {
|
||||
(
|
||||
Layout::Struct { field_layouts, .. },
|
||||
Alias(Symbol::DICT_DICT, alias_args, _alias_var, AliasKind::Opaque),
|
||||
) => {
|
||||
let dict_type_vars = env.subs.get_subs_slice(alias_args.type_variables());
|
||||
|
||||
debug_assert_eq!(dict_type_vars.len(), 2);
|
||||
|
||||
// Sets only use the key of the Dict they wrap, not the value
|
||||
let elem_var = dict_type_vars[0];
|
||||
|
||||
debug_assert_eq!(field_layouts.len(), 2);
|
||||
|
||||
let elem_id = add_type_help(env, field_layouts[0], elem_var, opt_name, types);
|
||||
let set_id = types.add_anonymous(RocType::RocSet(elem_id), layout);
|
||||
|
||||
types.depends(set_id, elem_id);
|
||||
|
||||
set_id
|
||||
}
|
||||
(elem_layout, alias_content) => unreachable!(
|
||||
"Unrecognized List element for Set. Layout was: {:?} and alias_content was: {:?}",
|
||||
elem_layout,
|
||||
alias_content
|
||||
),
|
||||
}
|
||||
}
|
||||
(Builtin::List(elem_layout), alias) => {
|
||||
unreachable!(
|
||||
"The type alias {:?} was not an Apply(Symbol::LIST_LIST) as expected, given that its builtin was Builtin::List({:?})",
|
||||
alias, elem_layout
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
10
crates/glue/tests/fixtures/dict/app.roc
vendored
Normal file
10
crates/glue/tests/fixtures/dict/app.roc
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
app "app"
|
||||
packages { pf: "platform.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main =
|
||||
Dict.empty
|
||||
|> Dict.insert "foo" "this will be overwritten"
|
||||
|> Dict.insert "baz" "blah"
|
||||
|> Dict.insert "foo" "bar"
|
9
crates/glue/tests/fixtures/dict/platform.roc
vendored
Normal file
9
crates/glue/tests/fixtures/dict/platform.roc
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
platform "test-platform"
|
||||
requires {} { main : _ }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Dict Str Str
|
||||
mainForHost = main
|
93
crates/glue/tests/fixtures/dict/src/lib.rs
vendored
Normal file
93
crates/glue/tests/fixtures/dict/src/lib.rs
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
mod test_glue;
|
||||
|
||||
use roc_std::{RocDict, RocStr};
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut RocDict<RocStr, RocStr>);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let dict = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<RocDict<RocStr, RocStr>> =
|
||||
core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
assert!(dict == dict); // PartialEq
|
||||
assert_eq!(dict.len(), 2); // len
|
||||
assert!(dict.clone() == dict.clone()); // Clone
|
||||
|
||||
assert!(dict.partial_cmp(&dict) == Some(Ordering::Equal)); // PartialOrd
|
||||
assert!(dict.cmp(&dict) == Ordering::Equal); // Ord
|
||||
|
||||
let mut set = HashSet::new();
|
||||
|
||||
set.insert(dict.clone()); // Eq, Hash
|
||||
set.insert(dict.clone());
|
||||
|
||||
assert_eq!(set.len(), 1);
|
||||
|
||||
println!("dict was: {:?}", dict); // Debug
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
// Externs required by roc_std and by the Roc app
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
11
crates/glue/tests/fixtures/set/app.roc
vendored
Normal file
11
crates/glue/tests/fixtures/set/app.roc
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
app "app"
|
||||
packages { pf: "platform.roc" }
|
||||
imports []
|
||||
provides [main] to pf
|
||||
|
||||
main =
|
||||
Set.empty
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "bar"
|
||||
|> Set.insert "foo"
|
||||
|> Set.insert "baz"
|
9
crates/glue/tests/fixtures/set/platform.roc
vendored
Normal file
9
crates/glue/tests/fixtures/set/platform.roc
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
platform "test-platform"
|
||||
requires {} { main : _ }
|
||||
exposes []
|
||||
packages {}
|
||||
imports []
|
||||
provides [mainForHost]
|
||||
|
||||
mainForHost : Set Str
|
||||
mainForHost = main
|
92
crates/glue/tests/fixtures/set/src/lib.rs
vendored
Normal file
92
crates/glue/tests/fixtures/set/src/lib.rs
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
mod test_glue;
|
||||
|
||||
use roc_std::{RocSet, RocStr};
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut RocSet<RocStr>);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_main() -> i32 {
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let set = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<RocSet<RocStr>> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
ret.assume_init()
|
||||
};
|
||||
|
||||
// Verify that it has all the expected traits.
|
||||
|
||||
assert!(set == set); // PartialEq
|
||||
assert_eq!(set.len(), 3); // len
|
||||
assert!(set.clone() == set.clone()); // Clone
|
||||
|
||||
assert!(set.partial_cmp(&set) == Some(Ordering::Equal)); // PartialOrd
|
||||
assert!(set.cmp(&set) == Ordering::Equal); // Ord
|
||||
|
||||
let mut hash_set = HashSet::new();
|
||||
|
||||
hash_set.insert(set.clone()); // Eq, Hash
|
||||
hash_set.insert(set.clone());
|
||||
|
||||
assert_eq!(hash_set.len(), 1);
|
||||
|
||||
println!("set was: {:?}", set); // Debug
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
// Externs required by roc_std and by the Roc app
|
||||
|
||||
use core::ffi::c_void;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void {
|
||||
return libc::malloc(size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_realloc(
|
||||
c_ptr: *mut c_void,
|
||||
new_size: usize,
|
||||
_old_size: usize,
|
||||
_alignment: u32,
|
||||
) -> *mut c_void {
|
||||
return libc::realloc(c_ptr, new_size);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) {
|
||||
return libc::free(c_ptr);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_panic(c_ptr: *mut c_void, tag_id: u32) {
|
||||
match tag_id {
|
||||
0 => {
|
||||
let slice = CStr::from_ptr(c_ptr as *const c_char);
|
||||
let string = slice.to_str().unwrap();
|
||||
eprintln!("Roc hit a panic: {}", string);
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memcpy(dst: *mut c_void, src: *mut c_void, n: usize) -> *mut c_void {
|
||||
libc::memcpy(dst, src, n)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void {
|
||||
libc::memset(dst, c, n)
|
||||
}
|
@ -70,6 +70,12 @@ mod glue_cli_run {
|
||||
fixtures! {
|
||||
basic_record:"basic-record" => "Record was: MyRcd { b: 42, a: 1995 }\n",
|
||||
nested_record:"nested-record" => "Record was: Outer { y: \"foo\", z: [1, 2], x: Inner { b: 24.0, a: 5 } }\n",
|
||||
dict:"dict" => indoc!(r#"
|
||||
dict was: RocDict {"foo": "bar", "baz": "blah"}
|
||||
"#),
|
||||
set:"set" => indoc!(r#"
|
||||
set was: RocSet {"foo", "bar", "baz"}
|
||||
"#),
|
||||
enumeration:"enumeration" => "tag_union was: MyEnum::Foo, Bar is: MyEnum::Bar, Baz is: MyEnum::Baz\n",
|
||||
union_with_padding:"union-with-padding" => indoc!(r#"
|
||||
tag_union was: NonRecursive::Foo("This is a test")
|
||||
|
@ -11,12 +11,16 @@ use core::ops::Drop;
|
||||
use core::str;
|
||||
|
||||
mod roc_box;
|
||||
mod roc_dict;
|
||||
mod roc_list;
|
||||
mod roc_set;
|
||||
mod roc_str;
|
||||
mod storage;
|
||||
|
||||
pub use roc_box::RocBox;
|
||||
pub use roc_dict::RocDict;
|
||||
pub use roc_list::RocList;
|
||||
pub use roc_set::RocSet;
|
||||
pub use roc_str::{InteriorNulError, RocStr};
|
||||
pub use storage::Storage;
|
||||
|
||||
|
77
crates/roc_std/src/roc_dict.rs
Normal file
77
crates/roc_std/src/roc_dict.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use crate::roc_list::{self, RocList};
|
||||
use core::{
|
||||
fmt::{self, Debug},
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct RocDict<K, V>(RocList<(K, V)>);
|
||||
|
||||
impl<K, V> RocDict<K, V> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self(RocList::with_capacity(capacity))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = &(K, V)> {
|
||||
self.into_iter()
|
||||
}
|
||||
|
||||
pub fn iter_keys(&self) -> impl Iterator<Item = &K> {
|
||||
self.0.iter().map(|(key, _)| key)
|
||||
}
|
||||
|
||||
pub fn iter_values(&self) -> impl Iterator<Item = &V> {
|
||||
self.0.iter().map(|(_, val)| val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Hash, V> RocDict<K, V> {
|
||||
#[allow(unused)]
|
||||
pub fn from_iter<I: Iterator<Item = (K, V)>>(src: I) -> Self {
|
||||
let mut ret = Self::with_capacity(src.size_hint().0);
|
||||
|
||||
for (key, val) in src {
|
||||
unsafe {
|
||||
ret.insert_unchecked(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
unsafe fn insert_unchecked(&mut self, _key: K, _val: V) {
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: Debug, V: Debug> Debug for RocDict<K, V> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("RocDict ")?;
|
||||
|
||||
f.debug_map()
|
||||
.entries(self.iter().map(|(k, v)| (k, v)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntoIterator for RocDict<K, V> {
|
||||
type Item = (K, V);
|
||||
type IntoIter = roc_list::IntoIter<(K, V)>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K, V> IntoIterator for &'a RocDict<K, V> {
|
||||
type Item = &'a (K, V);
|
||||
type IntoIter = core::slice::Iter<'a, (K, V)>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.as_slice().iter()
|
||||
}
|
||||
}
|
39
crates/roc_std/src/roc_set.rs
Normal file
39
crates/roc_std/src/roc_set.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use crate::roc_dict::RocDict;
|
||||
use core::{
|
||||
fmt::{self, Debug},
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct RocSet<T>(RocDict<T, ()>);
|
||||
|
||||
impl<T> RocSet<T> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self(RocDict::with_capacity(capacity))
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||
self.0.iter_keys()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash> RocSet<T> {
|
||||
#[allow(unused)]
|
||||
pub fn from_iter<I: Iterator<Item = T>>(src: I) -> Self {
|
||||
Self(RocDict::from_iter(src.map(|elem| (elem, ()))))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for RocSet<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("RocSet ")?;
|
||||
|
||||
f.debug_set().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user