mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge pull request #678 from rtfeldman/record-bool-tests
More REPL Improvements
This commit is contained in:
commit
d21ffb7eee
5 changed files with 472 additions and 212 deletions
|
@ -6,7 +6,7 @@ use roc_gen::{run_jit_function, run_jit_function_dynamic_type};
|
|||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_mono::layout::{Builtin, Layout};
|
||||
use roc_mono::layout::{union_sorted_tags_help, Builtin, Layout, UnionVariant};
|
||||
use roc_parse::ast::{AssignedField, Expr, StrLiteral};
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||
|
@ -64,6 +64,10 @@ fn jit_to_ast_help<'a>(
|
|||
env, num, content
|
||||
))
|
||||
}
|
||||
Layout::Builtin(Builtin::Int8) => {
|
||||
// NOTE: this is does not handle 8-bit numbers yet
|
||||
run_jit_function!(lib, main_fn_name, u8, |num| byte_to_ast(env, num, content))
|
||||
}
|
||||
Layout::Builtin(Builtin::Int64) => {
|
||||
run_jit_function!(lib, main_fn_name, i64, |num| num_to_ast(
|
||||
env,
|
||||
|
@ -89,19 +93,20 @@ fn jit_to_ast_help<'a>(
|
|||
Layout::Builtin(Builtin::List(_, elem_layout)) => run_jit_function!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
(*const libc::c_void, usize),
|
||||
|(ptr, len): (*const libc::c_void, usize)| {
|
||||
list_to_ast(env, ptr, len, elem_layout, content)
|
||||
}
|
||||
(*const u8, usize),
|
||||
|(ptr, len): (*const u8, usize)| { list_to_ast(env, ptr, len, elem_layout, content) }
|
||||
),
|
||||
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &'static str, |_| {
|
||||
Layout::Builtin(other) => {
|
||||
todo!("add support for rendering builtin {:?} to the REPL", other)
|
||||
}
|
||||
Layout::PhantomEmptyStruct => run_jit_function!(lib, main_fn_name, &u8, |_| {
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: &[],
|
||||
}
|
||||
}),
|
||||
Layout::Struct(field_layouts) => {
|
||||
let ptr_to_ast = |ptr: *const libc::c_void| match content {
|
||||
let ptr_to_ast = |ptr: *const u8| match content {
|
||||
Content::Structure(FlatType::Record(fields, _)) => {
|
||||
struct_to_ast(env, ptr, field_layouts, fields)
|
||||
}
|
||||
|
@ -113,13 +118,7 @@ fn jit_to_ast_help<'a>(
|
|||
|
||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
||||
|
||||
// We expect anything with payload vars
|
||||
// that is a single Tag TagUnion
|
||||
// has a Record content so the above case
|
||||
// should match instead
|
||||
debug_assert_eq!(payload_vars.len(), 0);
|
||||
|
||||
single_tag_union_to_ast(env, field_layouts, tag_name.clone(), payload_vars)
|
||||
single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars)
|
||||
}
|
||||
other => {
|
||||
unreachable!(
|
||||
|
@ -132,76 +131,93 @@ fn jit_to_ast_help<'a>(
|
|||
let fields = [Layout::Builtin(Builtin::Int64), layout.clone()];
|
||||
let layout = Layout::Struct(&fields);
|
||||
|
||||
match env.ptr_bytes {
|
||||
// 64-bit target (8-byte pointers, 16-byte structs)
|
||||
8 => match layout.stack_size(env.ptr_bytes) {
|
||||
8 => {
|
||||
// just one eightbyte, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
16 => {
|
||||
// two eightbytes, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 16], |bytes: [u8; 16]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 eightbytes
|
||||
// the return "value" is a pointer to the result
|
||||
let result_stack_size = layout.stack_size(env.ptr_bytes);
|
||||
|
||||
run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
result_stack_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const u8) }
|
||||
)
|
||||
}
|
||||
Layout::Union(union_layouts) => match content {
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
debug_assert_eq!(union_layouts.len(), tags.len());
|
||||
|
||||
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> =
|
||||
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
|
||||
let size = layout.stack_size(env.ptr_bytes);
|
||||
match union_variant {
|
||||
UnionVariant::Wrapped(tags_and_layouts) => {
|
||||
run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
size as usize,
|
||||
|ptr: *const u8| {
|
||||
// Because this is a `Wrapped`, the first 8 bytes encode the tag ID
|
||||
let tag_id = *(ptr as *const i64);
|
||||
|
||||
// use the tag ID as an index, to get its name and layout of any arguments
|
||||
let (tag_name, arg_layouts) = &tags_and_layouts[tag_id as usize];
|
||||
|
||||
let tag_expr = tag_name_to_expr(env, tag_name);
|
||||
let loc_tag_expr = &*env.arena.alloc(Located::at_zero(tag_expr));
|
||||
|
||||
let variables = &tags[tag_name];
|
||||
|
||||
// because the arg_layouts include the tag ID, it is one longer
|
||||
debug_assert_eq!(arg_layouts.len() - 1, variables.len());
|
||||
|
||||
// skip forward to the start of the first element, ignoring the tag id
|
||||
let ptr = ptr.offset(8);
|
||||
|
||||
let it = variables.iter().copied().zip(&arg_layouts[1..]);
|
||||
let output = sequence_of_expr(env, ptr, it);
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
// 32-bit target (4-byte pointers, 8-byte structs)
|
||||
4 => {
|
||||
// TODO what are valid return sizes here?
|
||||
// this is just extrapolated from the 64-bit case above
|
||||
// and not (yet) actually tested on a 32-bit system
|
||||
match layout.stack_size(env.ptr_bytes) {
|
||||
4 => {
|
||||
// just one fourbyte, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 4], |bytes: [u8; 4]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
8 => {
|
||||
// just one fourbyte, returned as-is
|
||||
run_jit_function!(lib, main_fn_name, [u8; 8], |bytes: [u8; 8]| {
|
||||
ptr_to_ast((&bytes).as_ptr() as *const libc::c_void)
|
||||
})
|
||||
}
|
||||
larger_size => {
|
||||
// anything more than 2 fourbytes
|
||||
// the return "value" is a pointer to the result
|
||||
run_jit_function_dynamic_type!(
|
||||
lib,
|
||||
main_fn_name,
|
||||
larger_size as usize,
|
||||
|bytes: *const u8| { ptr_to_ast(bytes as *const libc::c_void) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
panic!("Unsupported target: Roc cannot currently compile to systems where pointers are {} bytes in length.", other);
|
||||
_ => unreachable!("any other variant would have a different layout"),
|
||||
}
|
||||
}
|
||||
Content::Structure(FlatType::RecursiveTagUnion(_, _, _)) => {
|
||||
todo!("print recursive tag unions in the REPL")
|
||||
}
|
||||
other => unreachable!("Weird content for Union layout: {:?}", other),
|
||||
},
|
||||
Layout::RecursiveUnion(_) | Layout::RecursivePointer => {
|
||||
todo!("add support for rendering recursive tag unions in the REPL")
|
||||
}
|
||||
other => {
|
||||
todo!("TODO add support for rendering {:?} in the REPL", other);
|
||||
|
||||
Layout::FunctionPointer(_, _) | Layout::Closure(_, _, _) => {
|
||||
todo!("add support for rendering functions in the REPL")
|
||||
}
|
||||
Layout::Pointer(_) => todo!("add support for rendering pointers in the REPL"),
|
||||
}
|
||||
}
|
||||
|
||||
fn tag_name_to_expr<'a>(env: &Env<'a, '_>, tag_name: &TagName) -> Expr<'a> {
|
||||
match tag_name {
|
||||
TagName::Global(_) => Expr::GlobalTag(
|
||||
env.arena
|
||||
.alloc_str(&tag_name.as_string(env.interns, env.home)),
|
||||
),
|
||||
TagName::Private(_) => Expr::PrivateTag(
|
||||
env.arena
|
||||
.alloc_str(&tag_name.as_string(env.interns, env.home)),
|
||||
),
|
||||
TagName::Closure(_) => unreachable!("User cannot type this"),
|
||||
}
|
||||
}
|
||||
|
||||
fn ptr_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const libc::c_void,
|
||||
ptr: *const u8,
|
||||
layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
) -> Expr<'a> {
|
||||
|
@ -227,7 +243,7 @@ fn ptr_to_ast<'a>(
|
|||
Layout::Builtin(Builtin::List(_, elem_layout)) => {
|
||||
// Turn the (ptr, len) wrapper struct into actual ptr and len values.
|
||||
let len = unsafe { *(ptr.offset(env.ptr_bytes as isize) as *const usize) };
|
||||
let ptr = unsafe { *(ptr as *const *const libc::c_void) };
|
||||
let ptr = unsafe { *(ptr as *const *const u8) };
|
||||
|
||||
list_to_ast(env, ptr, len, elem_layout, content)
|
||||
}
|
||||
|
@ -241,6 +257,12 @@ fn ptr_to_ast<'a>(
|
|||
Content::Structure(FlatType::Record(fields, _)) => {
|
||||
struct_to_ast(env, ptr, field_layouts, fields)
|
||||
}
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
debug_assert_eq!(tags.len(), 1);
|
||||
|
||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
||||
single_tag_union_to_ast(env, ptr, field_layouts, tag_name.clone(), payload_vars)
|
||||
}
|
||||
other => {
|
||||
unreachable!(
|
||||
"Something had a Struct layout, but instead of a Record type, it had: {:?}",
|
||||
|
@ -259,7 +281,7 @@ fn ptr_to_ast<'a>(
|
|||
|
||||
fn list_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const libc::c_void,
|
||||
ptr: *const u8,
|
||||
len: usize,
|
||||
elem_layout: &Layout<'a>,
|
||||
content: &Content,
|
||||
|
@ -282,11 +304,11 @@ fn list_to_ast<'a>(
|
|||
|
||||
let arena = env.arena;
|
||||
let mut output = Vec::with_capacity_in(len, &arena);
|
||||
let elem_size = elem_layout.stack_size(env.ptr_bytes);
|
||||
let elem_size = elem_layout.stack_size(env.ptr_bytes) as usize;
|
||||
|
||||
for index in 0..(len as isize) {
|
||||
let offset_bytes: isize = index * elem_size as isize;
|
||||
let elem_ptr = unsafe { ptr.offset(offset_bytes) };
|
||||
for index in 0..len {
|
||||
let offset_bytes = index * elem_size;
|
||||
let elem_ptr = unsafe { ptr.add(offset_bytes) };
|
||||
let loc_expr = &*arena.alloc(Located {
|
||||
value: ptr_to_ast(env, elem_ptr, elem_layout, &elem_content),
|
||||
region: Region::zero(),
|
||||
|
@ -302,7 +324,8 @@ fn list_to_ast<'a>(
|
|||
|
||||
fn single_tag_union_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
field_layouts: &[Layout<'a>],
|
||||
ptr: *const u8,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
tag_name: TagName,
|
||||
payload_vars: &[Variable],
|
||||
) -> Expr<'a> {
|
||||
|
@ -310,28 +333,50 @@ fn single_tag_union_to_ast<'a>(
|
|||
|
||||
let arena = env.arena;
|
||||
|
||||
let tag_expr = match tag_name {
|
||||
TagName::Global(_) => {
|
||||
Expr::GlobalTag(arena.alloc_str(&tag_name.as_string(env.interns, env.home)))
|
||||
}
|
||||
TagName::Private(_) => {
|
||||
Expr::PrivateTag(arena.alloc_str(&tag_name.as_string(env.interns, env.home)))
|
||||
}
|
||||
TagName::Closure(_) => unreachable!("User cannot type this"),
|
||||
};
|
||||
let tag_expr = tag_name_to_expr(env, &tag_name);
|
||||
|
||||
let loc_tag_expr = &*arena.alloc(Located {
|
||||
value: tag_expr,
|
||||
region: Region::zero(),
|
||||
});
|
||||
let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr));
|
||||
|
||||
Expr::Apply(loc_tag_expr, &[], CalledVia::Space)
|
||||
let it = payload_vars.iter().copied().zip(field_layouts);
|
||||
let output = sequence_of_expr(env, ptr as *const u8, it).into_bump_slice();
|
||||
|
||||
Expr::Apply(loc_tag_expr, output, CalledVia::Space)
|
||||
}
|
||||
|
||||
fn sequence_of_expr<'a, I>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const u8,
|
||||
sequence: I,
|
||||
) -> Vec<'a, &'a Located<Expr<'a>>>
|
||||
where
|
||||
I: Iterator<Item = (Variable, &'a Layout<'a>)>,
|
||||
I: ExactSizeIterator<Item = (Variable, &'a Layout<'a>)>,
|
||||
{
|
||||
let arena = env.arena;
|
||||
let subs = env.subs;
|
||||
let mut output = Vec::with_capacity_in(sequence.len(), &arena);
|
||||
|
||||
// We'll advance this as we iterate through the fields
|
||||
let mut field_ptr = ptr as *const u8;
|
||||
|
||||
for (var, layout) in sequence {
|
||||
let content = subs.get_without_compacting(var).content;
|
||||
let expr = ptr_to_ast(env, field_ptr, layout, &content);
|
||||
let loc_expr = Located::at_zero(expr);
|
||||
|
||||
output.push(&*arena.alloc(loc_expr));
|
||||
|
||||
// Advance the field pointer to the next field.
|
||||
field_ptr = unsafe { field_ptr.offset(layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn struct_to_ast<'a>(
|
||||
env: &Env<'a, '_>,
|
||||
ptr: *const libc::c_void,
|
||||
field_layouts: &[Layout<'a>],
|
||||
ptr: *const u8,
|
||||
field_layouts: &'a [Layout<'a>],
|
||||
fields: &MutMap<Lowercase, RecordField<Variable>>,
|
||||
) -> Expr<'a> {
|
||||
let arena = env.arena;
|
||||
|
@ -339,7 +384,7 @@ fn struct_to_ast<'a>(
|
|||
let mut output = Vec::with_capacity_in(field_layouts.len(), &arena);
|
||||
|
||||
// The fields, sorted alphabetically
|
||||
let sorted_fields = {
|
||||
let mut sorted_fields = {
|
||||
let mut vec = fields
|
||||
.iter()
|
||||
.collect::<std::vec::Vec<(&Lowercase, &RecordField<Variable>)>>();
|
||||
|
@ -349,15 +394,14 @@ fn struct_to_ast<'a>(
|
|||
vec
|
||||
};
|
||||
|
||||
debug_assert_eq!(sorted_fields.len(), field_layouts.len());
|
||||
if sorted_fields.len() == 1 {
|
||||
// this is a 1-field wrapper record around another record or 1-tag tag union
|
||||
let (label, field) = sorted_fields.pop().unwrap();
|
||||
|
||||
// We'll advance this as we iterate through the fields
|
||||
let mut field_ptr = ptr;
|
||||
let inner_content = env.subs.get_without_compacting(field.into_inner()).content;
|
||||
|
||||
for ((label, field), field_layout) in sorted_fields.iter().zip(field_layouts.iter()) {
|
||||
let content = subs.get_without_compacting(*field.as_inner()).content;
|
||||
let loc_expr = &*arena.alloc(Located {
|
||||
value: ptr_to_ast(env, field_ptr, field_layout, &content),
|
||||
value: ptr_to_ast(env, ptr, &Layout::Struct(field_layouts), &inner_content),
|
||||
region: Region::zero(),
|
||||
});
|
||||
|
||||
|
@ -370,17 +414,47 @@ fn struct_to_ast<'a>(
|
|||
region: Region::zero(),
|
||||
};
|
||||
|
||||
output.push(loc_field);
|
||||
let output = env.arena.alloc([loc_field]);
|
||||
|
||||
// Advance the field pointer to the next field.
|
||||
field_ptr = unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: output,
|
||||
}
|
||||
} else {
|
||||
debug_assert_eq!(sorted_fields.len(), field_layouts.len());
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
// We'll advance this as we iterate through the fields
|
||||
let mut field_ptr = ptr;
|
||||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: output,
|
||||
for ((label, field), field_layout) in sorted_fields.iter().zip(field_layouts.iter()) {
|
||||
let content = subs.get_without_compacting(*field.as_inner()).content;
|
||||
let loc_expr = &*arena.alloc(Located {
|
||||
value: ptr_to_ast(env, field_ptr, field_layout, &content),
|
||||
region: Region::zero(),
|
||||
});
|
||||
|
||||
let field_name = Located {
|
||||
value: &*arena.alloc_str(label.as_str()),
|
||||
region: Region::zero(),
|
||||
};
|
||||
let loc_field = Located {
|
||||
value: AssignedField::RequiredValue(field_name, &[], loc_expr),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
output.push(loc_field);
|
||||
|
||||
// Advance the field pointer to the next field.
|
||||
field_ptr =
|
||||
unsafe { field_ptr.offset(field_layout.stack_size(env.ptr_bytes) as isize) };
|
||||
}
|
||||
|
||||
let output = output.into_bump_slice();
|
||||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -468,20 +542,17 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
|||
debug_assert!(payload_vars_1.is_empty());
|
||||
debug_assert!(payload_vars_2.is_empty());
|
||||
|
||||
let tag_name_as_str_1 = &tag_name_1.as_string(env.interns, env.home);
|
||||
let tag_name_as_str_2 = &tag_name_2.as_string(env.interns, env.home);
|
||||
|
||||
let tag_name = if value {
|
||||
tag_name_as_str_1.max(tag_name_as_str_2)
|
||||
max_by_key(tag_name_1, tag_name_2, |n| {
|
||||
n.as_string(env.interns, env.home)
|
||||
})
|
||||
} else {
|
||||
tag_name_as_str_1.min(tag_name_as_str_2)
|
||||
min_by_key(tag_name_1, tag_name_2, |n| {
|
||||
n.as_string(env.interns, env.home)
|
||||
})
|
||||
};
|
||||
|
||||
if tag_name.starts_with('@') {
|
||||
Expr::PrivateTag(arena.alloc_str(tag_name))
|
||||
} else {
|
||||
Expr::GlobalTag(arena.alloc_str(tag_name))
|
||||
}
|
||||
tag_name_to_expr(env, tag_name)
|
||||
}
|
||||
other => {
|
||||
unreachable!("Unexpected FlatType {:?} in bool_to_ast", other);
|
||||
|
@ -499,6 +570,117 @@ fn bool_to_ast<'a>(env: &Env<'a, '_>, value: bool, content: &Content) -> Expr<'a
|
|||
}
|
||||
}
|
||||
|
||||
fn byte_to_ast<'a>(env: &Env<'a, '_>, value: u8, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
||||
let arena = env.arena;
|
||||
|
||||
match content {
|
||||
Structure(flat_type) => {
|
||||
match flat_type {
|
||||
FlatType::Record(fields, _) => {
|
||||
debug_assert_eq!(fields.len(), 1);
|
||||
|
||||
let (label, field) = fields.iter().next().unwrap();
|
||||
let loc_label = Located {
|
||||
value: &*arena.alloc_str(label.as_str()),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let assigned_field = {
|
||||
// We may be multiple levels deep in nested tag unions
|
||||
// and/or records (e.g. { a: { b: { c: True } } }),
|
||||
// so we need to do this recursively on the field type.
|
||||
let field_var = *field.as_inner();
|
||||
let field_content = env.subs.get_without_compacting(field_var).content;
|
||||
let loc_expr = Located {
|
||||
value: byte_to_ast(env, value, &field_content),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
AssignedField::RequiredValue(loc_label, &[], arena.alloc(loc_expr))
|
||||
};
|
||||
|
||||
let loc_assigned_field = Located {
|
||||
value: assigned_field,
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
Expr::Record {
|
||||
update: None,
|
||||
fields: arena.alloc([loc_assigned_field]),
|
||||
}
|
||||
}
|
||||
FlatType::TagUnion(tags, _) if tags.len() == 1 => {
|
||||
let (tag_name, payload_vars) = tags.iter().next().unwrap();
|
||||
|
||||
let loc_tag_expr = {
|
||||
let tag_name = &tag_name.as_string(env.interns, env.home);
|
||||
let tag_expr = if tag_name.starts_with('@') {
|
||||
Expr::PrivateTag(arena.alloc_str(tag_name))
|
||||
} else {
|
||||
Expr::GlobalTag(arena.alloc_str(tag_name))
|
||||
};
|
||||
|
||||
&*arena.alloc(Located {
|
||||
value: tag_expr,
|
||||
region: Region::zero(),
|
||||
})
|
||||
};
|
||||
|
||||
let payload = {
|
||||
// Since this has the layout of a number, there should be
|
||||
// exactly one payload in this tag.
|
||||
debug_assert_eq!(payload_vars.len(), 1);
|
||||
|
||||
let var = *payload_vars.iter().next().unwrap();
|
||||
let content = env.subs.get_without_compacting(var).content;
|
||||
|
||||
let loc_payload = &*arena.alloc(Located {
|
||||
value: byte_to_ast(env, value, &content),
|
||||
region: Region::zero(),
|
||||
});
|
||||
|
||||
arena.alloc([loc_payload])
|
||||
};
|
||||
|
||||
Expr::Apply(loc_tag_expr, payload, CalledVia::Space)
|
||||
}
|
||||
FlatType::TagUnion(tags, _) => {
|
||||
// anything with fewer tags is not a byte
|
||||
debug_assert!(tags.len() > 2);
|
||||
|
||||
let tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)> =
|
||||
tags.iter().map(|(a, b)| (a.clone(), b.clone())).collect();
|
||||
|
||||
let union_variant = union_sorted_tags_help(env.arena, tags_vec, None, env.subs);
|
||||
|
||||
match union_variant {
|
||||
UnionVariant::ByteUnion(tagnames) => {
|
||||
let tag_name = &tagnames[value as usize];
|
||||
let tag_expr = tag_name_to_expr(env, tag_name);
|
||||
let loc_tag_expr = Located::at_zero(tag_expr);
|
||||
Expr::Apply(env.arena.alloc(loc_tag_expr), &[], CalledVia::Space)
|
||||
}
|
||||
_ => unreachable!("invalid union variant for a Byte!"),
|
||||
}
|
||||
}
|
||||
other => {
|
||||
unreachable!("Unexpected FlatType {:?} in bool_to_ast", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
Alias(_, _, var) => {
|
||||
let content = env.subs.get_without_compacting(*var).content;
|
||||
|
||||
byte_to_ast(env, value, &content)
|
||||
}
|
||||
other => {
|
||||
unreachable!("Unexpected FlatType {:?} in bool_to_ast", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn num_to_ast<'a>(env: &Env<'a, '_>, num_expr: Expr<'a>, content: &Content) -> Expr<'a> {
|
||||
use Content::*;
|
||||
|
||||
|
@ -648,3 +830,30 @@ fn str_slice_to_ast<'a>(_arena: &'a Bump, string: &'a str) -> Expr<'a> {
|
|||
Expr::Str(StrLiteral::PlainLine(string))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is currently nighly-only: use the implementation in std once it's stabilized
|
||||
pub fn max_by<T, F: FnOnce(&T, &T) -> std::cmp::Ordering>(v1: T, v2: T, compare: F) -> T {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match compare(&v1, &v2) {
|
||||
Ordering::Less | Ordering::Equal => v2,
|
||||
Ordering::Greater => v1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn min_by<T, F: FnOnce(&T, &T) -> std::cmp::Ordering>(v1: T, v2: T, compare: F) -> T {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
match compare(&v1, &v2) {
|
||||
Ordering::Less | Ordering::Equal => v1,
|
||||
Ordering::Greater => v2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
|
||||
max_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
|
||||
}
|
||||
|
||||
pub fn min_by_key<T, F: FnMut(&T) -> K, K: Ord>(v1: T, v2: T, mut f: F) -> T {
|
||||
min_by(v1, v2, |v1, v2| f(v1).cmp(&f(v2)))
|
||||
}
|
||||
|
|
|
@ -90,12 +90,15 @@ mod repl_eval {
|
|||
#[test]
|
||||
fn bool_in_record() {
|
||||
expect_success("{ x: 1 == 1 }", "{ x: True } : { x : Bool }");
|
||||
expect_success(
|
||||
"{ z: { y: { x: 1 == 1 } } }",
|
||||
"{ z: { y: { x: True } } } : { z : { y : { x : Bool } } }",
|
||||
);
|
||||
expect_success("{ x: 1 != 1 }", "{ x: False } : { x : Bool }");
|
||||
// TODO: see ptr_to_ast
|
||||
// expect_success(
|
||||
// "{ x: 1 == 1, y: 1 != 1 }",
|
||||
// "{ x: True, y: False } : { x : Bool, y : Bool }",
|
||||
// );
|
||||
expect_success(
|
||||
"{ x: 1 == 1, y: 1 != 1 }",
|
||||
"{ x: True, y: False } : { x : Bool, y : Bool }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -116,14 +119,46 @@ mod repl_eval {
|
|||
expect_success("False", "False : [ False ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn byte_tag_union() {
|
||||
expect_success(
|
||||
"if 1 == 1 then Red else if 1 == 1 then Green else Blue",
|
||||
"Red : [ Blue, Green, Red ]*",
|
||||
);
|
||||
|
||||
expect_success(
|
||||
"{ y: { x: if 1 == 1 then Red else if 1 == 1 then Green else Blue } }",
|
||||
"{ y: { x: Red } } : { y : { x : [ Blue, Green, Red ]* } }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_in_record() {
|
||||
expect_success(
|
||||
"{ x: Foo 1 2 3, y : 4 }",
|
||||
"{ x: Foo 1 2 3, y: 4 } : { x : [ Foo (Num *) (Num *) (Num *) ]*, y : Num * }",
|
||||
);
|
||||
expect_success(
|
||||
"{ x: Foo 1 2 3 }",
|
||||
"{ x: Foo 1 2 3 } : { x : [ Foo (Num *) (Num *) (Num *) ]* }",
|
||||
);
|
||||
expect_success("{ x: Unit }", "{ x: Unit } : { x : [ Unit ]* }");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_element_tag_union() {
|
||||
expect_success("True 1", "True 1 : [ True (Num *) ]*");
|
||||
expect_success("Foo 1 3.14", "Foo 1 3.14 : [ Foo (Num *) Float ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tag_with_arguments() {
|
||||
expect_success("True 1", "True 1 : [ True (Num *) ]*");
|
||||
// TODO handle more situations
|
||||
// expect_success(
|
||||
// "if 1 == 1 then True 1 else False 3.14",
|
||||
// "True 1 : [ True (Num *), False Float ]*",
|
||||
// )
|
||||
|
||||
expect_success(
|
||||
"if 1 == 1 then True 3 else False 3.14",
|
||||
"True 3 : [ False Float, True (Num *) ]*",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -273,32 +308,29 @@ mod repl_eval {
|
|||
);
|
||||
}
|
||||
|
||||
// TODO uncomment this once https://github.com/rtfeldman/roc/issues/295 is done
|
||||
// #[test]
|
||||
// fn basic_2_field_f64_record() {
|
||||
// expect_success(
|
||||
// "{ foo: 4.1, bar: 2.3 }",
|
||||
// "{ bar: 2.3, foo: 4.1 } : { bar : Float, foo : Float }",
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn basic_2_field_f64_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2.3 }",
|
||||
"{ bar: 2.3, foo: 4.1 } : { bar : Float, foo : Float }",
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn basic_2_field_mixed_record() {
|
||||
// expect_success(
|
||||
// "{ foo: 4.1, bar: 2 }",
|
||||
// "{ bar: 2, foo: 4.1 } : { bar : Num *, foo : Float }",
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn basic_2_field_mixed_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2 }",
|
||||
"{ bar: 2, foo: 4.1 } : { bar : Num *, foo : Float }",
|
||||
);
|
||||
}
|
||||
|
||||
// TODO uncomment this once https://github.com/rtfeldman/roc/issues/295 is done
|
||||
//
|
||||
// #[test]
|
||||
// fn basic_3_field_record() {
|
||||
// expect_success(
|
||||
// "{ foo: 4.1, bar: 2, baz: 0x5 }",
|
||||
// "{ foo: 4.1, bar: 2, baz: 0x5 } : { foo : Float, bar : Num *, baz : Int }",
|
||||
// );
|
||||
// }
|
||||
#[test]
|
||||
fn basic_3_field_record() {
|
||||
expect_success(
|
||||
"{ foo: 4.1, bar: 2, baz: 0x5 }",
|
||||
"{ bar: 2, baz: 5, foo: 4.1 } : { bar : Num *, baz : Int, foo : Float }",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_of_1_field_records() {
|
||||
|
|
|
@ -820,4 +820,28 @@ mod gen_records {
|
|||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn booleans_in_record() {
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 == 1, y: 1 == 1 }"),
|
||||
(true, true),
|
||||
(bool, bool)
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 != 1, y: 1 == 1 }"),
|
||||
(false, true),
|
||||
(bool, bool)
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 == 1, y: 1 != 1 }"),
|
||||
(true, false),
|
||||
(bool, bool)
|
||||
);
|
||||
assert_evals_to!(
|
||||
indoc!("{ x: 1 != 1, y: 1 != 1 }"),
|
||||
(false, false),
|
||||
(bool, bool)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -936,7 +936,7 @@ fn get_recursion_var(subs: &Subs, var: Variable) -> Option<Variable> {
|
|||
}
|
||||
}
|
||||
|
||||
fn union_sorted_tags_help<'a>(
|
||||
pub fn union_sorted_tags_help<'a>(
|
||||
arena: &'a Bump,
|
||||
mut tags_vec: std::vec::Vec<(TagName, std::vec::Vec<Variable>)>,
|
||||
opt_rec_var: Option<Variable>,
|
||||
|
|
113
shell.nix
113
shell.nix
|
@ -16,72 +16,67 @@ with {
|
|||
with (pkgs);
|
||||
|
||||
let
|
||||
darwin-frameworks =
|
||||
if isMacOS then
|
||||
with pkgs.darwin.apple_sdk.frameworks; [
|
||||
AppKit
|
||||
CoreFoundation
|
||||
CoreServices
|
||||
CoreVideo
|
||||
Foundation
|
||||
Metal
|
||||
Security
|
||||
]
|
||||
else
|
||||
[ ];
|
||||
darwin-frameworks = if isMacOS then
|
||||
with pkgs.darwin.apple_sdk.frameworks; [
|
||||
AppKit
|
||||
CoreFoundation
|
||||
CoreServices
|
||||
CoreVideo
|
||||
Foundation
|
||||
Metal
|
||||
Security
|
||||
]
|
||||
else
|
||||
[ ];
|
||||
|
||||
linux-only = if !isMacOS then [
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
xorg.libX11
|
||||
xorg.libXcursor
|
||||
xorg.libXrandr
|
||||
xorg.libXi
|
||||
] else
|
||||
[ ];
|
||||
|
||||
llvmPkg = pkgs.llvm_10;
|
||||
lldPkg = pkgs.lld_10; # this should match llvm's version
|
||||
clangPkg = pkgs.clang_10; # this should match llvm's version
|
||||
zig = import ./nix/zig.nix { inherit pkgs isMacOS; };
|
||||
inputs =
|
||||
[
|
||||
# build libraries
|
||||
rustc
|
||||
cargo
|
||||
clippy
|
||||
rustfmt
|
||||
cmake
|
||||
git
|
||||
python3
|
||||
llvmPkg
|
||||
clangPkg
|
||||
valgrind
|
||||
pkg-config
|
||||
zig
|
||||
# llb deps
|
||||
libffi
|
||||
libxml2
|
||||
xorg.libX11
|
||||
zlib
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
# faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker
|
||||
lldPkg
|
||||
# dev tools
|
||||
rust-analyzer
|
||||
# (import ./nix/zls.nix { inherit pkgs zig; })
|
||||
ccls
|
||||
];
|
||||
inputs = [
|
||||
# build libraries
|
||||
rustc
|
||||
cargo
|
||||
clippy
|
||||
rustfmt
|
||||
cmake
|
||||
git
|
||||
python3
|
||||
llvmPkg
|
||||
clangPkg
|
||||
valgrind
|
||||
pkg-config
|
||||
zig
|
||||
# llb deps
|
||||
libffi
|
||||
libxml2
|
||||
zlib
|
||||
# faster builds - see https://github.com/rtfeldman/roc/blob/trunk/BUILDING_FROM_SOURCE.md#use-lld-for-the-linker
|
||||
lldPkg
|
||||
# dev tools
|
||||
rust-analyzer
|
||||
# (import ./nix/zls.nix { inherit pkgs zig; })
|
||||
ccls
|
||||
];
|
||||
|
||||
in mkShell {
|
||||
buildInputs = inputs ++ darwin-frameworks;
|
||||
buildInputs = inputs ++ darwin-frameworks ++ linux-only;
|
||||
LLVM_SYS_100_PREFIX = "${llvmPkg}";
|
||||
|
||||
APPEND_LIBRARY_PATH = stdenv.lib.makeLibraryPath [
|
||||
pkgconfig
|
||||
vulkan-headers
|
||||
vulkan-loader
|
||||
vulkan-tools
|
||||
vulkan-validation-layers
|
||||
xorg.libX11
|
||||
xorg.libXcursor
|
||||
xorg.libXrandr
|
||||
xorg.libXi
|
||||
libcxx
|
||||
libcxxabi
|
||||
libunwind
|
||||
];
|
||||
APPEND_LIBRARY_PATH = stdenv.lib.makeLibraryPath
|
||||
([ pkgconfig libcxx libcxxabi libunwind ] ++ linux-only);
|
||||
|
||||
# Aliases don't work cross shell, so we do this
|
||||
shellHook = ''
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue