mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
Merge remote-tracking branch 'origin/trunk' into multi-dep-bugs
This commit is contained in:
commit
cb0bfa3eb7
50 changed files with 1421 additions and 256 deletions
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
|
@ -51,11 +51,6 @@ jobs:
|
|||
command: clippy
|
||||
args: -- -D warnings
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: cargo test
|
||||
with:
|
||||
command: test
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: cargo test -- --ignored
|
||||
continue-on-error: true
|
||||
|
|
|
@ -21,6 +21,16 @@ fn powInt(base: i64, exp: i64) callconv(.C) i64 {
|
|||
return math.pow(i64, base, exp);
|
||||
}
|
||||
|
||||
comptime { @export(acos, .{ .name = math_namespace ++ ".acos", .linkage = .Strong }); }
|
||||
fn acos(num: f64) callconv(.C) f64 {
|
||||
return math.acos(num);
|
||||
}
|
||||
|
||||
comptime { @export(asin, .{ .name = math_namespace ++ ".asin", .linkage = .Strong }); }
|
||||
fn asin(num: f64) callconv(.C) f64 {
|
||||
return math.asin(num);
|
||||
}
|
||||
|
||||
|
||||
// Str.split
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@ pub fn get_bytes() -> Vec<u8> {
|
|||
buffer
|
||||
}
|
||||
|
||||
pub const MATH_ASIN: &str = "roc_builtins.math.asin";
|
||||
pub const MATH_ACOS: &str = "roc_builtins.math.acos";
|
||||
pub const MATH_ATAN: &str = "roc_builtins.math.atan";
|
||||
pub const MATH_IS_FINITE: &str = "roc_builtins.math.is_finite";
|
||||
pub const MATH_POW_INT: &str = "roc_builtins.math.pow_int";
|
||||
|
|
|
@ -350,6 +350,18 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// acos : Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_ACOS,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// asin : Float -> Float
|
||||
add_type(
|
||||
Symbol::NUM_ASIN,
|
||||
top_level_function(vec![float_type()], Box::new(float_type())),
|
||||
);
|
||||
|
||||
// Bool module
|
||||
|
||||
// and : Bool, Bool -> Bool
|
||||
|
|
|
@ -375,6 +375,18 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
|||
unique_function(vec![float_type(star1)], float_type(star2))
|
||||
});
|
||||
|
||||
// acos : Float -> Float
|
||||
add_type(Symbol::NUM_ACOS, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![float_type(star1)], float_type(star2))
|
||||
});
|
||||
|
||||
// asin : Float -> Float
|
||||
add_type(Symbol::NUM_ASIN, {
|
||||
let_tvars! { star1, star2 };
|
||||
unique_function(vec![float_type(star1)], float_type(star2))
|
||||
});
|
||||
|
||||
// Bool module
|
||||
|
||||
// isEq or (==) : Attr * a, Attr * a -> Attr * Bool
|
||||
|
|
|
@ -98,6 +98,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
|||
Symbol::NUM_POW_INT => num_pow_int,
|
||||
Symbol::NUM_FLOOR => num_floor,
|
||||
Symbol::NUM_ATAN => num_atan,
|
||||
Symbol::NUM_ACOS => num_acos,
|
||||
Symbol::NUM_ASIN => num_asin,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -781,6 +783,46 @@ fn num_atan(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
|||
)
|
||||
}
|
||||
|
||||
/// Num.acos : Float -> Float
|
||||
fn num_acos(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let arg_float_var = var_store.fresh();
|
||||
let ret_float_var = var_store.fresh();
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::NumAcos,
|
||||
args: vec![(arg_float_var, Var(Symbol::ARG_1))],
|
||||
ret_var: ret_float_var,
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(arg_float_var, Symbol::ARG_1)],
|
||||
var_store,
|
||||
body,
|
||||
ret_float_var,
|
||||
)
|
||||
}
|
||||
|
||||
/// Num.asin : Float -> Float
|
||||
fn num_asin(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let arg_float_var = var_store.fresh();
|
||||
let ret_float_var = var_store.fresh();
|
||||
|
||||
let body = RunLowLevel {
|
||||
op: LowLevel::NumAsin,
|
||||
args: vec![(arg_float_var, Var(Symbol::ARG_1))],
|
||||
ret_var: ret_float_var,
|
||||
};
|
||||
|
||||
defn(
|
||||
symbol,
|
||||
vec![(arg_float_var, Symbol::ARG_1)],
|
||||
var_store,
|
||||
body,
|
||||
ret_float_var,
|
||||
)
|
||||
}
|
||||
|
||||
/// List.isEmpty : List * -> Bool
|
||||
fn list_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
let list_var = var_store.fresh();
|
||||
|
|
|
@ -1443,6 +1443,8 @@ fn to_pending_def<'a>(
|
|||
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) | Nested(sub_def) => {
|
||||
to_pending_def(env, var_store, sub_def, scope, pattern_type)
|
||||
}
|
||||
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,6 +47,8 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
|
|||
Nested(alias @ Alias { .. }) => Nested(alias),
|
||||
ann @ Annotation(_, _) => Nested(ann),
|
||||
Nested(ann @ Annotation(_, _)) => Nested(ann),
|
||||
Nested(NotYetImplemented(s)) => todo!("{}", s),
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ impl<'a> Formattable<'a> for Def<'a> {
|
|||
spaces.iter().any(|s| is_comment(s)) || sub_def.is_multiline()
|
||||
}
|
||||
Nested(def) => def.is_multiline(),
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,6 +67,7 @@ impl<'a> Formattable<'a> for Def<'a> {
|
|||
fmt_spaces(buf, spaces.iter(), indent);
|
||||
}
|
||||
Nested(def) => def.format(buf, indent),
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1842,6 +1842,252 @@ pub fn create_entry_block_alloca<'a, 'ctx>(
|
|||
builder.build_alloca(basic_type, name)
|
||||
}
|
||||
|
||||
fn expose_function_to_host<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
) {
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
let roc_wrapper_function = make_exception_catching_wrapper(env, roc_function);
|
||||
|
||||
let roc_function_type = roc_wrapper_function.get_type();
|
||||
|
||||
// STEP 1: turn `f : a,b,c -> d` into `f : a,b,c, &d -> {}`
|
||||
let mut argument_types = roc_function_type.get_param_types();
|
||||
let return_type = roc_function_type.get_return_type().unwrap();
|
||||
let output_type = return_type.ptr_type(AddressSpace::Generic);
|
||||
argument_types.push(output_type.into());
|
||||
|
||||
let c_function_type = env.context.void_type().fn_type(&argument_types, false);
|
||||
let c_function_name: String = format!("{}_exposed", roc_function.get_name().to_str().unwrap());
|
||||
|
||||
let c_function = env.module.add_function(
|
||||
c_function_name.as_str(),
|
||||
c_function_type,
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
// STEP 2: build the exposed function's body
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
let entry = context.append_basic_block(c_function, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
// drop the final argument, which is the pointer we write the result into
|
||||
let args = c_function.get_params();
|
||||
let output_arg_index = args.len() - 1;
|
||||
let args = &args[..args.len() - 1];
|
||||
|
||||
debug_assert_eq!(args.len(), roc_function.get_params().len());
|
||||
debug_assert_eq!(args.len(), roc_wrapper_function.get_params().len());
|
||||
|
||||
let call_wrapped = builder.build_call(roc_wrapper_function, args, "call_wrapped_function");
|
||||
call_wrapped.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
let call_result = call_wrapped.try_as_basic_value().left().unwrap();
|
||||
|
||||
let output_arg = c_function
|
||||
.get_nth_param(output_arg_index as u32)
|
||||
.unwrap()
|
||||
.into_pointer_value();
|
||||
|
||||
builder.build_store(output_arg, call_result);
|
||||
|
||||
builder.build_return(None);
|
||||
|
||||
// STEP 3: build a {} -> u64 function that gives the size of the return type
|
||||
let size_function_type = env.context.i64_type().fn_type(&[], false);
|
||||
let size_function_name: String = format!("{}_size", roc_function.get_name().to_str().unwrap());
|
||||
|
||||
let size_function = env.module.add_function(
|
||||
size_function_name.as_str(),
|
||||
size_function_type,
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
let entry = context.append_basic_block(size_function, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let size: BasicValueEnum = return_type.size_of().unwrap().into();
|
||||
builder.build_return(Some(&size));
|
||||
}
|
||||
|
||||
fn make_exception_catching_wrapper<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
roc_function: FunctionValue<'ctx>,
|
||||
) -> FunctionValue<'ctx> {
|
||||
// build the C calling convention wrapper
|
||||
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
|
||||
let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic);
|
||||
|
||||
let roc_function_type = roc_function.get_type();
|
||||
let argument_types = roc_function_type.get_param_types();
|
||||
|
||||
let wrapper_function_name = format!("{}_catcher", roc_function.get_name().to_str().unwrap());
|
||||
|
||||
let wrapper_return_type = context.struct_type(
|
||||
&[
|
||||
context.i64_type().into(),
|
||||
roc_function_type.get_return_type().unwrap(),
|
||||
],
|
||||
false,
|
||||
);
|
||||
|
||||
let wrapper_function_type = wrapper_return_type.fn_type(&argument_types, false);
|
||||
|
||||
// Add main to the module.
|
||||
let wrapper_function =
|
||||
env.module
|
||||
.add_function(&wrapper_function_name, wrapper_function_type, None);
|
||||
|
||||
// our exposed main function adheres to the C calling convention
|
||||
wrapper_function.set_call_conventions(FAST_CALL_CONV);
|
||||
|
||||
// Add main's body
|
||||
let basic_block = context.append_basic_block(wrapper_function, "entry");
|
||||
let then_block = context.append_basic_block(wrapper_function, "then_block");
|
||||
let catch_block = context.append_basic_block(wrapper_function, "catch_block");
|
||||
let cont_block = context.append_basic_block(wrapper_function, "cont_block");
|
||||
|
||||
builder.position_at_end(basic_block);
|
||||
|
||||
let result_alloca = builder.build_alloca(wrapper_return_type, "result");
|
||||
|
||||
// invoke instead of call, so that we can catch any exeptions thrown in Roc code
|
||||
let arguments = wrapper_function.get_params();
|
||||
let call_result = {
|
||||
let call = builder.build_invoke(
|
||||
roc_function,
|
||||
&arguments,
|
||||
then_block,
|
||||
catch_block,
|
||||
"call_roc_function",
|
||||
);
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
call.try_as_basic_value().left().unwrap()
|
||||
};
|
||||
|
||||
// exception handling
|
||||
{
|
||||
builder.position_at_end(catch_block);
|
||||
|
||||
let landing_pad_type = {
|
||||
let exception_ptr = context.i8_type().ptr_type(AddressSpace::Generic).into();
|
||||
let selector_value = context.i32_type().into();
|
||||
|
||||
context.struct_type(&[exception_ptr, selector_value], false)
|
||||
};
|
||||
|
||||
let info = builder
|
||||
.build_catch_all_landing_pad(
|
||||
&landing_pad_type,
|
||||
&BasicValueEnum::IntValue(context.i8_type().const_zero()),
|
||||
context.i8_type().ptr_type(AddressSpace::Generic),
|
||||
"main_landing_pad",
|
||||
)
|
||||
.into_struct_value();
|
||||
|
||||
let exception_ptr = builder
|
||||
.build_extract_value(info, 0, "exception_ptr")
|
||||
.unwrap();
|
||||
|
||||
let thrown = cxa_begin_catch(env, exception_ptr);
|
||||
|
||||
let error_msg = {
|
||||
let exception_type = u8_ptr;
|
||||
let ptr = builder.build_bitcast(
|
||||
thrown,
|
||||
exception_type.ptr_type(AddressSpace::Generic),
|
||||
"cast",
|
||||
);
|
||||
|
||||
builder.build_load(ptr.into_pointer_value(), "error_msg")
|
||||
};
|
||||
|
||||
let return_type = context.struct_type(&[context.i64_type().into(), u8_ptr.into()], false);
|
||||
|
||||
let return_value = {
|
||||
let v1 = return_type.const_zero();
|
||||
|
||||
// flag is non-zero, indicating failure
|
||||
let flag = context.i64_type().const_int(1, false);
|
||||
|
||||
let v2 = builder
|
||||
.build_insert_value(v1, flag, 0, "set_error")
|
||||
.unwrap();
|
||||
|
||||
let v3 = builder
|
||||
.build_insert_value(v2, error_msg, 1, "set_exception")
|
||||
.unwrap();
|
||||
|
||||
v3
|
||||
};
|
||||
|
||||
// bitcast result alloca so we can store our concrete type { flag, error_msg } in there
|
||||
let result_alloca_bitcast = builder
|
||||
.build_bitcast(
|
||||
result_alloca,
|
||||
return_type.ptr_type(AddressSpace::Generic),
|
||||
"result_alloca_bitcast",
|
||||
)
|
||||
.into_pointer_value();
|
||||
|
||||
// store our return value
|
||||
builder.build_store(result_alloca_bitcast, return_value);
|
||||
|
||||
cxa_end_catch(env);
|
||||
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
{
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
let return_value = {
|
||||
let v1 = wrapper_return_type.const_zero();
|
||||
|
||||
let v2 = builder
|
||||
.build_insert_value(v1, context.i64_type().const_zero(), 0, "set_no_error")
|
||||
.unwrap();
|
||||
let v3 = builder
|
||||
.build_insert_value(v2, call_result, 1, "set_call_result")
|
||||
.unwrap();
|
||||
|
||||
v3
|
||||
};
|
||||
|
||||
let ptr = builder.build_bitcast(
|
||||
result_alloca,
|
||||
wrapper_return_type.ptr_type(AddressSpace::Generic),
|
||||
"name",
|
||||
);
|
||||
builder.build_store(ptr.into_pointer_value(), return_value);
|
||||
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
}
|
||||
|
||||
{
|
||||
builder.position_at_end(cont_block);
|
||||
|
||||
let result = builder.build_load(result_alloca, "result");
|
||||
|
||||
builder.build_return(Some(&result));
|
||||
}
|
||||
|
||||
// MUST set the personality at the very end;
|
||||
// doing it earlier can cause the personality to be ignored
|
||||
let personality_func = get_gxx_personality_v0(env);
|
||||
wrapper_function.set_personality_function(personality_func);
|
||||
|
||||
wrapper_function
|
||||
}
|
||||
|
||||
pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -1871,19 +2117,79 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
|
|||
.module
|
||||
.add_function(fn_name.as_str(), fn_type, Some(Linkage::Private));
|
||||
|
||||
if env.exposed_to_host.contains(&symbol) {
|
||||
// If this is an external-facing function, it'll use the C calling convention
|
||||
// and external linkage.
|
||||
fn_val.set_linkage(Linkage::External);
|
||||
fn_val.set_call_conventions(C_CALL_CONV);
|
||||
} else {
|
||||
// If it's an internal-only function, it should use the fast calling conention.
|
||||
fn_val.set_call_conventions(FAST_CALL_CONV);
|
||||
|
||||
if env.exposed_to_host.contains(&symbol) {
|
||||
expose_function_to_host(env, fn_val);
|
||||
}
|
||||
|
||||
fn_val
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn build_closure_caller<'a, 'ctx, 'env>(
|
||||
env: &'a Env<'a, 'ctx, 'env>,
|
||||
closure_function: FunctionValue<'ctx>,
|
||||
) {
|
||||
let context = env.context;
|
||||
let builder = env.builder;
|
||||
// asuming the closure has type `a, b, closure_data -> c`
|
||||
// change that into `a, b, *const closure_data, *mut output -> ()`
|
||||
|
||||
// a function `a, b, closure_data -> RocCallResult<c>`
|
||||
let wrapped_function = make_exception_catching_wrapper(env, closure_function);
|
||||
|
||||
let closure_function_type = closure_function.get_type();
|
||||
let wrapped_function_type = wrapped_function.get_type();
|
||||
|
||||
let mut arguments = closure_function_type.get_param_types();
|
||||
|
||||
// require that the closure data is passed by reference
|
||||
let closure_data_type = arguments.pop().unwrap();
|
||||
let closure_data_ptr_type = get_ptr_type(&closure_data_type, AddressSpace::Generic);
|
||||
arguments.push(closure_data_ptr_type.into());
|
||||
|
||||
// require that a pointer is passed in to write the result into
|
||||
let output_type = get_ptr_type(
|
||||
&wrapped_function_type.get_return_type().unwrap(),
|
||||
AddressSpace::Generic,
|
||||
);
|
||||
arguments.push(output_type.into());
|
||||
|
||||
let caller_function_type = env.context.void_type().fn_type(&arguments, false);
|
||||
let caller_function_name: String =
|
||||
format!("{}_caller", closure_function.get_name().to_str().unwrap());
|
||||
|
||||
let caller_function = env.module.add_function(
|
||||
caller_function_name.as_str(),
|
||||
caller_function_type,
|
||||
Some(Linkage::External),
|
||||
);
|
||||
|
||||
caller_function.set_call_conventions(C_CALL_CONV);
|
||||
|
||||
let entry = context.append_basic_block(caller_function, "entry");
|
||||
|
||||
builder.position_at_end(entry);
|
||||
|
||||
let mut parameters = caller_function.get_params();
|
||||
let output = parameters.pop().unwrap();
|
||||
let closure_data_ptr = parameters.pop().unwrap();
|
||||
|
||||
let closure_data =
|
||||
builder.build_load(closure_data_ptr.into_pointer_value(), "load_closure_data");
|
||||
parameters.push(closure_data);
|
||||
|
||||
let call = builder.build_call(wrapped_function, ¶meters, "call_wrapped_function");
|
||||
call.set_call_convention(FAST_CALL_CONV);
|
||||
|
||||
let result = call.try_as_basic_value().left().unwrap();
|
||||
|
||||
builder.build_store(output.into_pointer_value(), result);
|
||||
|
||||
builder.build_return(None);
|
||||
}
|
||||
|
||||
pub fn build_proc<'a, 'ctx, 'env>(
|
||||
env: &'a Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -1907,6 +2213,10 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
|||
// the closure argument (if any) comes in as an opaque sequence of bytes.
|
||||
// we need to cast that to the specific closure data layout that the body expects
|
||||
let value = if let Symbol::ARG_CLOSURE = *arg_symbol {
|
||||
// generate a caller function (to be used by the host)
|
||||
// build_closure_caller(env, fn_val);
|
||||
// builder.position_at_end(entry);
|
||||
|
||||
// blindly trust that there is a layout available for the closure data
|
||||
let layout = proc.closure_data_layout.clone().unwrap();
|
||||
|
||||
|
@ -2172,7 +2482,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
list_join(env, inplace, parent, list, outer_list_layout)
|
||||
}
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
|
||||
| NumToFloat | NumIsFinite | NumAtan => {
|
||||
| NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
|
@ -2737,6 +3047,8 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
|||
),
|
||||
NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], &bitcode::MATH_IS_FINITE),
|
||||
NumAtan => call_bitcode_fn(NumAtan, env, &[arg.into()], &bitcode::MATH_ATAN),
|
||||
NumAcos => call_bitcode_fn(NumAcos, env, &[arg.into()], &bitcode::MATH_ACOS),
|
||||
NumAsin => call_bitcode_fn(NumAsin, env, &[arg.into()], &bitcode::MATH_ASIN),
|
||||
_ => {
|
||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||
}
|
||||
|
@ -2918,7 +3230,7 @@ fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
|||
}
|
||||
|
||||
fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> {
|
||||
let name = "__cxa_rethrow";
|
||||
let name = "__gxx_personality_v0";
|
||||
|
||||
let module = env.module;
|
||||
let context = env.context;
|
||||
|
|
|
@ -101,6 +101,8 @@ fn generate_module_doc<'a>(
|
|||
} => (acc, None),
|
||||
|
||||
Body(_, _) | Nested(_) => (acc, None),
|
||||
|
||||
NotYetImplemented(s) => todo!("{}", s),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,8 @@ pub enum LowLevel {
|
|||
NumFloor,
|
||||
NumIsFinite,
|
||||
NumAtan,
|
||||
NumAcos,
|
||||
NumAsin,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
|
|
@ -652,6 +652,8 @@ define_builtins! {
|
|||
42 NUM_ADD_WRAP: "addWrap"
|
||||
43 NUM_ADD_CHECKED: "addChecked"
|
||||
44 NUM_ATAN: "atan"
|
||||
45 NUM_ACOS: "acos"
|
||||
46 NUM_ASIN: "asin"
|
||||
}
|
||||
2 BOOL: "Bool" => {
|
||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||
|
|
|
@ -527,6 +527,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
|||
| NumPowInt => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
|
||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||
| NumToFloat | Not | NumIsFinite | NumAtan => arena.alloc_slice_copy(&[irrelevant]),
|
||||
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
||||
arena.alloc_slice_copy(&[irrelevant])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
4
compiler/parse/fuzz/.gitignore
vendored
Normal file
4
compiler/parse/fuzz/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
|
||||
target
|
||||
corpus
|
||||
artifacts
|
182
compiler/parse/fuzz/Cargo.lock
generated
Normal file
182
compiler/parse/fuzz/Cargo.lock
generated
Normal file
|
@ -0,0 +1,182 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db55d72333851e17d572bec876e390cd3b11eb1ef53ae821dd9f3b653d2b4569"
|
||||
|
||||
[[package]]
|
||||
name = "bitmaps"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "im"
|
||||
version = "14.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "696059c87b83c5a258817ecd67c3af915e3ed141891fc35a1e79908801cf0ce7"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"rand_core 0.5.1",
|
||||
"rand_xoshiro",
|
||||
"sized-chunks",
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "im-rc"
|
||||
version = "14.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "303f7e6256d546e01979071417432425f15c1891fb309a5f2d724ee908fabd6e"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"rand_core 0.5.1",
|
||||
"rand_xoshiro",
|
||||
"sized-chunks",
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inlinable_string"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb6ee2a7da03bfc3b66ca47c92c2e392fcc053ea040a85561749b026f7aad09a"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee8c42ab62f43795ed77a965ed07994c5584cdc94fd0ebf14b22ac1524077acc"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||
|
||||
[[package]]
|
||||
name = "rand_xoshiro"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_collections"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"im",
|
||||
"im-rc",
|
||||
"wyhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_module"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"inlinable_string",
|
||||
"lazy_static",
|
||||
"roc_collections",
|
||||
"roc_region",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_parse"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"encode_unicode",
|
||||
"inlinable_string",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_region",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_parse-fuzz"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"libfuzzer-sys",
|
||||
"roc_parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roc_region"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "sized-chunks"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59044ea371ad781ff976f7b06480b9f0180e834eda94114f2afb4afc12b7718"
|
||||
dependencies = [
|
||||
"bitmaps",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "wyhash"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "782a50f48ac4336916227cd199c61c7b42f38d0ad705421b49eb12c74c53ae00"
|
||||
dependencies = [
|
||||
"rand_core 0.4.2",
|
||||
]
|
39
compiler/parse/fuzz/Cargo.toml
Normal file
39
compiler/parse/fuzz/Cargo.toml
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
[package]
|
||||
name = "roc_parse-fuzz"
|
||||
version = "0.0.0"
|
||||
authors = ["Automatically generated"]
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[package.metadata]
|
||||
cargo-fuzz = true
|
||||
|
||||
[dependencies]
|
||||
libfuzzer-sys = "0.3"
|
||||
bumpalo = { version = "3.2", features = ["collections"] }
|
||||
|
||||
[dependencies.roc_parse]
|
||||
path = ".."
|
||||
|
||||
# Prevent this from interfering with workspaces
|
||||
[workspace]
|
||||
members = ["."]
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_expr"
|
||||
path = "fuzz_targets/fuzz_expr.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_defs"
|
||||
path = "fuzz_targets/fuzz_defs.rs"
|
||||
test = false
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_header"
|
||||
path = "fuzz_targets/fuzz_header.rs"
|
||||
test = false
|
||||
doc = false
|
11
compiler/parse/fuzz/README.md
Normal file
11
compiler/parse/fuzz/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
To setup fuzzing you will need to install cargo-fuzz and run with rust nightly:
|
||||
|
||||
```
|
||||
$ cargo install cargo-fuzz
|
||||
$ cargo +nightly fuzz run -j<cores> <target> -- -dict=dict.txt
|
||||
```
|
||||
|
||||
The different targets can be found by running `cargo fuzz list`.
|
||||
|
||||
When a bug is found, it will be reported with commands to run it again and look for a minimized version.
|
||||
If you are going to file a bug, please minimize the input before filing the bug.
|
36
compiler/parse/fuzz/dict.txt
Normal file
36
compiler/parse/fuzz/dict.txt
Normal file
|
@ -0,0 +1,36 @@
|
|||
"if"
|
||||
"then"
|
||||
"else"
|
||||
"when"
|
||||
"as"
|
||||
"is"
|
||||
"expect"
|
||||
|
||||
"app"
|
||||
"platform"
|
||||
"provides"
|
||||
"requires"
|
||||
"exposes"
|
||||
"imports"
|
||||
"effects"
|
||||
"interface"
|
||||
|
||||
"|>"
|
||||
"=="
|
||||
"!="
|
||||
"&&"
|
||||
"||"
|
||||
"+"
|
||||
"*"
|
||||
"-"
|
||||
"//"
|
||||
"/"
|
||||
"<="
|
||||
"<"
|
||||
">="
|
||||
">"
|
||||
"^"
|
||||
"%%"
|
||||
"%"
|
||||
|
||||
"->"
|
11
compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs
Normal file
11
compiler/parse/fuzz/fuzz_targets/fuzz_defs.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![no_main]
|
||||
use bumpalo::Bump;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use roc_parse::test_helpers::parse_defs_with;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(input) = std::str::from_utf8(data) {
|
||||
let arena = Bump::new();
|
||||
let _actual = parse_defs_with(&arena, input.trim());
|
||||
}
|
||||
});
|
11
compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs
Normal file
11
compiler/parse/fuzz/fuzz_targets/fuzz_expr.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![no_main]
|
||||
use bumpalo::Bump;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use roc_parse::test_helpers::parse_expr_with;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(input) = std::str::from_utf8(data) {
|
||||
let arena = Bump::new();
|
||||
let _actual = parse_expr_with(&arena, input.trim());
|
||||
}
|
||||
});
|
11
compiler/parse/fuzz/fuzz_targets/fuzz_header.rs
Normal file
11
compiler/parse/fuzz/fuzz_targets/fuzz_header.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#![no_main]
|
||||
use bumpalo::Bump;
|
||||
use libfuzzer_sys::fuzz_target;
|
||||
use roc_parse::test_helpers::parse_header_with;
|
||||
|
||||
fuzz_target!(|data: &[u8]| {
|
||||
if let Ok(input) = std::str::from_utf8(data) {
|
||||
let arena = Bump::new();
|
||||
let _actual = parse_header_with(&arena, input.trim());
|
||||
}
|
||||
});
|
|
@ -277,6 +277,8 @@ pub enum Def<'a> {
|
|||
/// This is used only to avoid cloning when reordering expressions (e.g. in desugar()).
|
||||
/// It lets us take a (&Def) and create a plain (Def) from it.
|
||||
Nested(&'a Def<'a>),
|
||||
|
||||
NotYetImplemented(&'static str),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -561,7 +561,7 @@ fn annotation_or_alias<'a>(
|
|||
ann: loc_ann,
|
||||
},
|
||||
Apply(_, _) => {
|
||||
panic!("TODO gracefully handle invalid Apply in type annotation");
|
||||
Def::NotYetImplemented("TODO gracefully handle invalid Apply in type annotation")
|
||||
}
|
||||
SpaceAfter(value, spaces_before) => Def::SpaceAfter(
|
||||
arena.alloc(annotation_or_alias(arena, value, region, loc_ann)),
|
||||
|
@ -574,19 +574,19 @@ fn annotation_or_alias<'a>(
|
|||
Nested(value) => annotation_or_alias(arena, value, region, loc_ann),
|
||||
|
||||
PrivateTag(_) => {
|
||||
panic!("TODO gracefully handle trying to use a private tag as an annotation.");
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to use a private tag as an annotation.")
|
||||
}
|
||||
QualifiedIdentifier { .. } => {
|
||||
panic!("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`");
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`")
|
||||
}
|
||||
NumLiteral(_) | NonBase10Literal { .. } | FloatLiteral(_) | StrLiteral(_) => {
|
||||
panic!("TODO gracefully handle trying to annotate a litera");
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to annotate a litera")
|
||||
}
|
||||
Underscore => {
|
||||
panic!("TODO gracefully handle trying to give a type annotation to an undrscore");
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to give a type annotation to an undrscore")
|
||||
}
|
||||
Malformed(_) => {
|
||||
panic!("TODO translate a malformed pattern into a malformed annotation");
|
||||
Def::NotYetImplemented("TODO translate a malformed pattern into a malformed annotation")
|
||||
}
|
||||
Identifier(ident) => {
|
||||
// This is a regular Annotation
|
||||
|
@ -633,7 +633,13 @@ fn parse_def_expr<'a>(
|
|||
))
|
||||
// `<` because '=' should be same indent (or greater) as the entire def-expr
|
||||
} else if equals_sign_indent < def_start_col {
|
||||
todo!("TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", equals_sign_indent, def_start_col);
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(format!("TODO the = in this declaration seems outdented. equals_sign_indent was {} and def_start_col was {}", equals_sign_indent, def_start_col)),
|
||||
},
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
// Indented more beyond the original indent of the entire def-expr.
|
||||
let indented_more = def_start_col + 1;
|
||||
|
@ -720,7 +726,15 @@ fn parse_def_signature<'a>(
|
|||
))
|
||||
// `<` because ':' should be same indent or greater
|
||||
} else if colon_indent < original_indent {
|
||||
panic!("TODO the : in this declaration seems outdented");
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(
|
||||
"TODO the : in this declaration seems outdented".to_string(),
|
||||
),
|
||||
},
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
// Indented more beyond the original indent.
|
||||
let indented_more = original_indent + 1;
|
||||
|
@ -1069,11 +1083,12 @@ fn loc_ident_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>
|
|||
} else {
|
||||
format!("{}.{}", module_name, parts.join("."))
|
||||
};
|
||||
|
||||
Ok((
|
||||
Located {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::Malformed(arena.alloc(malformed_str)),
|
||||
value: Pattern::Malformed(
|
||||
String::from_str_in(&malformed_str, &arena).into_bump_str(),
|
||||
),
|
||||
},
|
||||
state,
|
||||
))
|
||||
|
@ -1120,7 +1135,15 @@ mod when {
|
|||
),
|
||||
move |arena, state, (case_indent, loc_condition)| {
|
||||
if case_indent < min_indent {
|
||||
panic!("TODO case wasn't indented enough");
|
||||
return Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(
|
||||
"TODO case wasn't indented enough".to_string(),
|
||||
),
|
||||
},
|
||||
state,
|
||||
));
|
||||
}
|
||||
|
||||
// Everything in the branches must be indented at least as much as the case itself.
|
||||
|
@ -1178,9 +1201,15 @@ mod when {
|
|||
if alternatives_indented_correctly(&loc_patterns, original_indent) {
|
||||
Ok(((loc_patterns, loc_guard), state))
|
||||
} else {
|
||||
panic!(
|
||||
"TODO additional branch didn't have same indentation as first branch"
|
||||
);
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(
|
||||
"TODO additional branch didn't have same indentation as first branch".to_string(),
|
||||
),
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -1490,10 +1519,16 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
});
|
||||
}
|
||||
Err(malformed) => {
|
||||
panic!(
|
||||
return Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(format!(
|
||||
"TODO early return malformed pattern {:?}",
|
||||
malformed
|
||||
);
|
||||
)),
|
||||
},
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,5 @@ pub mod number_literal;
|
|||
pub mod pattern;
|
||||
pub mod problems;
|
||||
pub mod string_literal;
|
||||
pub mod test_helpers;
|
||||
pub mod type_annotation;
|
||||
|
|
|
@ -224,6 +224,7 @@ pub enum FailReason {
|
|||
BadUtf8,
|
||||
ReservedKeyword(Region),
|
||||
ArgumentsBeforeEquals(Region),
|
||||
NotYetImplemented(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::ast::{Attempting, EscapedChar, StrLiteral, StrSegment};
|
||||
use crate::expr;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof,
|
||||
ParseResult, Parser, State,
|
||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Fail,
|
||||
FailReason, ParseResult, Parser, State,
|
||||
};
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -279,7 +279,16 @@ where
|
|||
// lines.push(line);
|
||||
|
||||
// Ok((StrLiteral::Block(lines.into_bump_slice()), state))
|
||||
todo!("TODO parse this line in a block string: {:?}", line);
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented(format!(
|
||||
"TODO parse this line in a block string: {:?}",
|
||||
line
|
||||
)),
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
Err(reason) => state.fail(reason),
|
||||
};
|
||||
|
|
45
compiler/parse/src/test_helpers.rs
Normal file
45
compiler/parse/src/test_helpers.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use crate::ast::{self, Attempting};
|
||||
use crate::blankspace::space0_before;
|
||||
use crate::expr::expr;
|
||||
use crate::module::{header, module_defs};
|
||||
use crate::parser::{loc, Fail, Parser, State};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Module<'a>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = header().parse(arena, state);
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_defs_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = module_defs().parse(arena, state);
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
}
|
|
@ -4,8 +4,8 @@ use crate::expr::{global_tag, private_tag};
|
|||
use crate::ident::join_module_parts;
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either,
|
||||
ParseResult, Parser, State,
|
||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Either, Fail,
|
||||
FailReason, ParseResult, Parser, State,
|
||||
};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -239,7 +239,13 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
Ok((first, state))
|
||||
} else {
|
||||
// e.g. `Int,Int` without an arrow and return type
|
||||
panic!("Invalid function signature")
|
||||
Err((
|
||||
Fail {
|
||||
attempting: state.attempting,
|
||||
reason: FailReason::NotYetImplemented("TODO: Decide the correct error to return for 'Invalid function signature'".to_string()),
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
extern crate bumpalo;
|
||||
|
||||
use self::bumpalo::Bump;
|
||||
use roc_parse::ast::{self, Attempting};
|
||||
use roc_parse::blankspace::space0_before;
|
||||
use roc_parse::parser::{loc, Fail, Parser, State};
|
||||
use roc_region::all::Located;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Fail> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_loc_with<'a>(arena: &'a Bump, input: &'a str) -> Result<Located<ast::Expr<'a>>, Fail> {
|
||||
let state = State::new(input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(roc_parse::expr::expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
||||
answer
|
||||
.map(|(loc_expr, _)| loc_expr)
|
||||
.map_err(|(fail, _)| fail)
|
||||
}
|
|
@ -11,11 +11,8 @@ extern crate quickcheck_macros;
|
|||
extern crate roc_module;
|
||||
extern crate roc_parse;
|
||||
|
||||
mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_parse {
|
||||
use crate::helpers::parse_with;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::{self, Bump};
|
||||
use roc_module::operator::BinOp::*;
|
||||
|
@ -33,19 +30,20 @@ mod test_parse {
|
|||
use roc_parse::header::ModuleName;
|
||||
use roc_parse::module::{interface_header, module_defs};
|
||||
use roc_parse::parser::{Fail, FailReason, Parser, State};
|
||||
use roc_parse::test_helpers::parse_expr_with;
|
||||
use roc_region::all::{Located, Region};
|
||||
use std::{f64, i64};
|
||||
|
||||
fn assert_parses_to<'a>(input: &'a str, expected_expr: Expr<'a>) {
|
||||
let arena = Bump::new();
|
||||
let actual = parse_with(&arena, input.trim());
|
||||
let actual = parse_expr_with(&arena, input.trim());
|
||||
|
||||
assert_eq!(Ok(expected_expr), actual);
|
||||
}
|
||||
|
||||
fn assert_parsing_fails<'a>(input: &'a str, reason: FailReason, attempting: Attempting) {
|
||||
let arena = Bump::new();
|
||||
let actual = parse_with(&arena, input);
|
||||
let actual = parse_expr_with(&arena, input);
|
||||
let expected_fail = Fail { reason, attempting };
|
||||
|
||||
assert_eq!(Err(expected_fail), actual);
|
||||
|
@ -53,7 +51,7 @@ mod test_parse {
|
|||
|
||||
fn assert_segments<E: Fn(&Bump) -> Vec<'_, ast::StrSegment<'_>>>(input: &str, to_expected: E) {
|
||||
let arena = Bump::new();
|
||||
let actual = parse_with(&arena, arena.alloc(input));
|
||||
let actual = parse_expr_with(&arena, arena.alloc(input));
|
||||
let expected_slice = to_expected(&arena);
|
||||
let expected_expr = Expr::Str(Line(&expected_slice));
|
||||
|
||||
|
@ -77,7 +75,7 @@ mod test_parse {
|
|||
("\\t", EscapedChar::Tab),
|
||||
("\\\"", EscapedChar::Quote),
|
||||
] {
|
||||
let actual = parse_with(&arena, arena.alloc(to_input(string)));
|
||||
let actual = parse_expr_with(&arena, arena.alloc(to_input(string)));
|
||||
let expected_slice = to_expected(*escaped, &arena);
|
||||
let expected_expr = Expr::Str(Line(&expected_slice));
|
||||
|
||||
|
@ -423,7 +421,7 @@ mod test_parse {
|
|||
fields: &[],
|
||||
update: None,
|
||||
};
|
||||
let actual = parse_with(&arena, "{}");
|
||||
let actual = parse_expr_with(&arena, "{}");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -455,7 +453,7 @@ mod test_parse {
|
|||
fields,
|
||||
};
|
||||
|
||||
let actual = parse_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }");
|
||||
let actual = parse_expr_with(&arena, "{ Foo.Bar.baz & x: 5, y: 0 }");
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
||||
|
@ -470,7 +468,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 2, 3, Num("2")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "1+2");
|
||||
let actual = parse_expr_with(&arena, "1+2");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -484,7 +482,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 2, 3, Num("2")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "1-2");
|
||||
let actual = parse_expr_with(&arena, "1-2");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -498,7 +496,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 7, 8, Num("2")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "1 + 2");
|
||||
let actual = parse_expr_with(&arena, "1 + 2");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -512,7 +510,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 7, 8, Num("2")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "1 - 2");
|
||||
let actual = parse_expr_with(&arena, "1 - 2");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -535,7 +533,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 4, 5, Num("2")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "x + 2");
|
||||
let actual = parse_expr_with(&arena, "x + 2");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -557,7 +555,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 4, 5, Num("2")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "x - 2");
|
||||
let actual = parse_expr_with(&arena, "x - 2");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -572,7 +570,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 2, 3, Num("4")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 \n+ 4");
|
||||
let actual = parse_expr_with(&arena, "3 \n+ 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -587,7 +585,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 2, 3, Num("4")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 \n- 4");
|
||||
let actual = parse_expr_with(&arena, "3 \n- 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -602,7 +600,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 2, 3, spaced_int),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 *\n 4");
|
||||
let actual = parse_expr_with(&arena, "3 *\n 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -617,7 +615,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 2, 3, spaced_int),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 -\n 4");
|
||||
let actual = parse_expr_with(&arena, "3 -\n 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -632,7 +630,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 2, 3, Num("4")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 # 2 × 2\n+ 4");
|
||||
let actual = parse_expr_with(&arena, "3 # 2 × 2\n+ 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -647,7 +645,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 2, 3, Num("4")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 # test!\n+ 4");
|
||||
let actual = parse_expr_with(&arena, "3 # test!\n+ 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -662,7 +660,7 @@ mod test_parse {
|
|||
Located::new(1, 1, 1, 3, spaced_int),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "12 * # test!\n 92");
|
||||
let actual = parse_expr_with(&arena, "12 * # test!\n 92");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -678,7 +676,7 @@ mod test_parse {
|
|||
Located::new(3, 3, 2, 3, spaced_int2),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "3 \n+ \n\n 4");
|
||||
let actual = parse_expr_with(&arena, "3 \n+ \n\n 4");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -702,7 +700,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 3, 4, var2),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "x- y");
|
||||
let actual = parse_expr_with(&arena, "x- y");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -716,7 +714,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 4, 5, Num("5")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "-12-5");
|
||||
let actual = parse_expr_with(&arena, "-12-5");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -730,7 +728,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 3, 5, Num("11")),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "10*11");
|
||||
let actual = parse_expr_with(&arena, "10*11");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -749,7 +747,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 3, 9, BinOp(inner)),
|
||||
));
|
||||
let expected = BinOp(outer);
|
||||
let actual = parse_with(&arena, "31*42+534");
|
||||
let actual = parse_expr_with(&arena, "31*42+534");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -771,7 +769,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 3, 4, var2),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "x==y");
|
||||
let actual = parse_expr_with(&arena, "x==y");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -793,7 +791,7 @@ mod test_parse {
|
|||
Located::new(0, 0, 5, 6, var2),
|
||||
));
|
||||
let expected = BinOp(tuple);
|
||||
let actual = parse_with(&arena, "x == y");
|
||||
let actual = parse_expr_with(&arena, "x == y");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -807,7 +805,7 @@ mod test_parse {
|
|||
module_name: "",
|
||||
ident: "whee",
|
||||
};
|
||||
let actual = parse_with(&arena, "whee");
|
||||
let actual = parse_expr_with(&arena, "whee");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -819,7 +817,7 @@ mod test_parse {
|
|||
module_name: "",
|
||||
ident: "whee",
|
||||
}));
|
||||
let actual = parse_with(&arena, "(whee)");
|
||||
let actual = parse_expr_with(&arena, "(whee)");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -831,7 +829,7 @@ mod test_parse {
|
|||
module_name: "One.Two",
|
||||
ident: "whee",
|
||||
};
|
||||
let actual = parse_with(&arena, "One.Two.whee");
|
||||
let actual = parse_expr_with(&arena, "One.Two.whee");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -842,7 +840,7 @@ mod test_parse {
|
|||
fn basic_global_tag() {
|
||||
let arena = Bump::new();
|
||||
let expected = Expr::GlobalTag("Whee");
|
||||
let actual = parse_with(&arena, "Whee");
|
||||
let actual = parse_expr_with(&arena, "Whee");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -851,7 +849,7 @@ mod test_parse {
|
|||
fn basic_private_tag() {
|
||||
let arena = Bump::new();
|
||||
let expected = Expr::PrivateTag("@Whee");
|
||||
let actual = parse_with(&arena, "@Whee");
|
||||
let actual = parse_expr_with(&arena, "@Whee");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -867,7 +865,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "@Whee 12 34");
|
||||
let actual = parse_expr_with(&arena, "@Whee 12 34");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -883,7 +881,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "Whee 12 34");
|
||||
let actual = parse_expr_with(&arena, "Whee 12 34");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -901,7 +899,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "Whee (12) (34)");
|
||||
let actual = parse_expr_with(&arena, "Whee (12) (34)");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -910,7 +908,7 @@ mod test_parse {
|
|||
fn qualified_global_tag() {
|
||||
let arena = Bump::new();
|
||||
let expected = Expr::MalformedIdent("One.Two.Whee");
|
||||
let actual = parse_with(&arena, "One.Two.Whee");
|
||||
let actual = parse_expr_with(&arena, "One.Two.Whee");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -920,7 +918,7 @@ mod test_parse {
|
|||
// fn qualified_private_tag() {
|
||||
// let arena = Bump::new();
|
||||
// let expected = Expr::MalformedIdent("One.Two.@Whee");
|
||||
// let actual = parse_with(&arena, "One.Two.@Whee");
|
||||
// let actual = parse_expr_with(&arena, "One.Two.@Whee");
|
||||
|
||||
// assert_eq!(Ok(expected), actual);
|
||||
// }
|
||||
|
@ -931,7 +929,7 @@ mod test_parse {
|
|||
let pattern = Located::new(0, 0, 1, 6, Pattern::GlobalTag("Thing"));
|
||||
let patterns = &[pattern];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 10, 12, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\Thing -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\Thing -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -940,7 +938,7 @@ mod test_parse {
|
|||
fn private_qualified_tag() {
|
||||
let arena = Bump::new();
|
||||
let expected = Expr::MalformedIdent("@One.Two.Whee");
|
||||
let actual = parse_with(&arena, "@One.Two.Whee");
|
||||
let actual = parse_expr_with(&arena, "@One.Two.Whee");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -952,7 +950,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let elems = &[];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[]");
|
||||
let actual = parse_expr_with(&arena, "[]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -963,7 +961,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let elems = &[];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[ ]");
|
||||
let actual = parse_expr_with(&arena, "[ ]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -973,7 +971,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let elems = &[&*arena.alloc(Located::new(0, 0, 1, 2, Num("1")))];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[1]");
|
||||
let actual = parse_expr_with(&arena, "[1]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -983,7 +981,7 @@ mod test_parse {
|
|||
let arena = Bump::new();
|
||||
let elems = &[&*arena.alloc(Located::new(0, 0, 2, 3, Num("1")))];
|
||||
let expected = List(elems);
|
||||
let actual = parse_with(&arena, "[ 1 ]");
|
||||
let actual = parse_expr_with(&arena, "[ 1 ]");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -998,7 +996,7 @@ mod test_parse {
|
|||
ident: "rec",
|
||||
};
|
||||
let expected = Access(arena.alloc(var), "field");
|
||||
let actual = parse_with(&arena, "rec.field");
|
||||
let actual = parse_expr_with(&arena, "rec.field");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1011,7 +1009,7 @@ mod test_parse {
|
|||
ident: "rec",
|
||||
}));
|
||||
let expected = Access(arena.alloc(paren_var), "field");
|
||||
let actual = parse_with(&arena, "(rec).field");
|
||||
let actual = parse_expr_with(&arena, "(rec).field");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1024,7 +1022,7 @@ mod test_parse {
|
|||
ident: "rec",
|
||||
}));
|
||||
let expected = Access(arena.alloc(paren_var), "field");
|
||||
let actual = parse_with(&arena, "(One.Two.rec).field");
|
||||
let actual = parse_expr_with(&arena, "(One.Two.rec).field");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1040,7 +1038,7 @@ mod test_parse {
|
|||
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
|
||||
"ghi",
|
||||
);
|
||||
let actual = parse_with(&arena, "rec.abc.def.ghi");
|
||||
let actual = parse_expr_with(&arena, "rec.abc.def.ghi");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1056,7 +1054,7 @@ mod test_parse {
|
|||
arena.alloc(Access(arena.alloc(Access(arena.alloc(var), "abc")), "def")),
|
||||
"ghi",
|
||||
);
|
||||
let actual = parse_with(&arena, "One.Two.rec.abc.def.ghi");
|
||||
let actual = parse_expr_with(&arena, "One.Two.rec.abc.def.ghi");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1077,7 +1075,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "whee 1");
|
||||
let actual = parse_expr_with(&arena, "whee 1");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1102,7 +1100,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "whee 12 34");
|
||||
let actual = parse_expr_with(&arena, "whee 12 34");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1155,7 +1153,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "a b c d");
|
||||
let actual = parse_expr_with(&arena, "a b c d");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1174,7 +1172,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "(whee) 1");
|
||||
let actual = parse_expr_with(&arena, "(whee) 1");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1191,7 +1189,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_arg1_expr = Located::new(0, 0, 1, 4, arg1_expr);
|
||||
let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
||||
let actual = parse_with(&arena, "-foo");
|
||||
let actual = parse_expr_with(&arena, "-foo");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1206,7 +1204,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_arg1_expr = Located::new(0, 0, 1, 5, arg1_expr);
|
||||
let expected = UnaryOp(arena.alloc(loc_arg1_expr), loc_op);
|
||||
let actual = parse_with(&arena, "!blah");
|
||||
let actual = parse_expr_with(&arena, "!blah");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1242,7 +1240,7 @@ mod test_parse {
|
|||
CalledVia::Space,
|
||||
);
|
||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op);
|
||||
let actual = parse_with(&arena, "-whee 12 foo");
|
||||
let actual = parse_expr_with(&arena, "-whee 12 foo");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1278,7 +1276,7 @@ mod test_parse {
|
|||
CalledVia::Space,
|
||||
);
|
||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 13, apply_expr)), loc_op);
|
||||
let actual = parse_with(&arena, "!whee 12 foo");
|
||||
let actual = parse_expr_with(&arena, "!whee 12 foo");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1314,7 +1312,7 @@ mod test_parse {
|
|||
CalledVia::Space,
|
||||
)));
|
||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op);
|
||||
let actual = parse_with(&arena, "-(whee 12 foo)");
|
||||
let actual = parse_expr_with(&arena, "-(whee 12 foo)");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1350,7 +1348,7 @@ mod test_parse {
|
|||
CalledVia::Space,
|
||||
)));
|
||||
let expected = UnaryOp(arena.alloc(Located::new(0, 0, 1, 15, apply_expr)), loc_op);
|
||||
let actual = parse_with(&arena, "!(whee 12 foo)");
|
||||
let actual = parse_expr_with(&arena, "!(whee 12 foo)");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1377,7 +1375,7 @@ mod test_parse {
|
|||
args,
|
||||
CalledVia::Space,
|
||||
);
|
||||
let actual = parse_with(&arena, "whee 12 -foo");
|
||||
let actual = parse_expr_with(&arena, "whee 12 -foo");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1394,7 +1392,7 @@ mod test_parse {
|
|||
let access = Access(arena.alloc(var), "field");
|
||||
let loc_access = Located::new(0, 0, 1, 11, access);
|
||||
let expected = UnaryOp(arena.alloc(loc_access), loc_op);
|
||||
let actual = parse_with(&arena, "-rec1.field");
|
||||
let actual = parse_expr_with(&arena, "-rec1.field");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1407,7 +1405,7 @@ mod test_parse {
|
|||
let pattern = Located::new(0, 0, 1, 2, Identifier("a"));
|
||||
let patterns = &[pattern];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\a -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\a -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1418,7 +1416,7 @@ mod test_parse {
|
|||
let pattern = Located::new(0, 0, 1, 2, Underscore);
|
||||
let patterns = &[pattern];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 6, 8, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\_ -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\_ -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1429,7 +1427,7 @@ mod test_parse {
|
|||
// underscore in an argument name, it would parse as three arguments
|
||||
// (and would ignore the underscore as if it had been blank space).
|
||||
let arena = Bump::new();
|
||||
let actual = parse_with(&arena, "\\the_answer -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\the_answer -> 42");
|
||||
|
||||
assert_eq!(Ok(MalformedClosure), actual);
|
||||
}
|
||||
|
@ -1441,7 +1439,7 @@ mod test_parse {
|
|||
let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
|
||||
let patterns = &[arg1, arg2];
|
||||
let expected = Closure(patterns, arena.alloc(Located::new(0, 0, 9, 11, Num("42"))));
|
||||
let actual = parse_with(&arena, "\\a, b -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\a, b -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1457,7 +1455,7 @@ mod test_parse {
|
|||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 12, 14, Num("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\a, b, c -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\a, b, c -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -1472,7 +1470,7 @@ mod test_parse {
|
|||
arena.alloc(patterns),
|
||||
arena.alloc(Located::new(0, 0, 9, 11, Num("42"))),
|
||||
);
|
||||
let actual = parse_with(&arena, "\\_, _ -> 42");
|
||||
let actual = parse_expr_with(&arena, "\\_, _ -> 42");
|
||||
|
||||
assert_eq!(Ok(expected), actual);
|
||||
}
|
||||
|
@ -2040,7 +2038,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_with(
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2084,7 +2082,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_with(
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2133,7 +2131,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_with(
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2183,7 +2181,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_with(
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2490,7 +2488,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_with(
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
|
@ -2535,7 +2533,7 @@ mod test_parse {
|
|||
};
|
||||
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||
let actual = parse_with(
|
||||
let actual = parse_expr_with(
|
||||
&arena,
|
||||
indoc!(
|
||||
r#"
|
||||
|
|
8
examples/closure/Closure.roc
Normal file
8
examples/closure/Closure.roc
Normal file
|
@ -0,0 +1,8 @@
|
|||
app Closure provides [ closure ] imports []
|
||||
|
||||
closure : {} -> Int
|
||||
closure =
|
||||
x = 42
|
||||
|
||||
\{} -> x
|
||||
|
23
examples/closure/platform/Cargo.lock
generated
Normal file
23
examples/closure/platform/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"roc_std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
examples/closure/platform/Cargo.toml
Normal file
13
examples/closure/platform/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
|
||||
[workspace]
|
7
examples/closure/platform/host.c
Normal file
7
examples/closure/platform/host.c
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
extern int rust_main();
|
||||
|
||||
int main() {
|
||||
return rust_main();
|
||||
}
|
102
examples/closure/platform/src/lib.rs
Normal file
102
examples/closure/platform/src/lib.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use std::alloc::Layout;
|
||||
use std::ffi::CString;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_char;
|
||||
use std::time::SystemTime;
|
||||
use RocCallResult::*;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "closure_1_exposed"]
|
||||
fn make_closure(output: *mut u8) -> ();
|
||||
|
||||
// #[link_name = "0_1_caller"]
|
||||
// fn call_closure_0(unit: (), closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "closure_1_size"]
|
||||
fn closure_size() -> i64;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
println!("Running Roc closure");
|
||||
let start_time = SystemTime::now();
|
||||
|
||||
let size = unsafe { closure_size() } as usize;
|
||||
let layout = Layout::array::<u8>(size).unwrap();
|
||||
let roc_closure = unsafe {
|
||||
let buffer = std::alloc::alloc(layout);
|
||||
|
||||
make_closure(buffer);
|
||||
|
||||
type CLOSURE_DATA = i64;
|
||||
let output = &*(buffer as *mut RocCallResult<(fn(CLOSURE_DATA) -> i64, CLOSURE_DATA)>);
|
||||
|
||||
match output.into() {
|
||||
Ok((function_pointer, closure_data)) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
move || function_pointer(closure_data)
|
||||
}
|
||||
Err(msg) => {
|
||||
std::alloc::dealloc(buffer, layout);
|
||||
|
||||
panic!("Roc failed with message: {}", msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
let answer = roc_closure();
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
println!(
|
||||
"Roc closure took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
answer
|
||||
);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum RocCallResult<T> {
|
||||
Success(T),
|
||||
Failure(*mut c_char),
|
||||
}
|
||||
|
||||
impl<T: Sized> Into<Result<T, String>> for RocCallResult<T> {
|
||||
fn into(self) -> Result<T, String> {
|
||||
match self {
|
||||
Success(value) => Ok(value),
|
||||
Failure(failure) => Err({
|
||||
let raw = unsafe { CString::from_raw(failure) };
|
||||
|
||||
let result = format!("{:?}", raw);
|
||||
|
||||
// make sure rust does not try to free the Roc string
|
||||
std::mem::forget(raw);
|
||||
|
||||
result
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sized + Copy> Into<Result<T, String>> for &RocCallResult<T> {
|
||||
fn into(self) -> Result<T, String> {
|
||||
match self {
|
||||
Success(value) => Ok(*value),
|
||||
Failure(failure) => Err({
|
||||
let raw = unsafe { CString::from_raw(*failure) };
|
||||
|
||||
let result = format!("{:?}", raw);
|
||||
|
||||
// make sure rust does not try to free the Roc string
|
||||
std::mem::forget(raw);
|
||||
|
||||
result
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +1,27 @@
|
|||
use roc_std::RocCallResult;
|
||||
use roc_std::RocStr;
|
||||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "main_1"]
|
||||
fn main() -> RocStr;
|
||||
#[link_name = "main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
println!(
|
||||
"Roc says: {}",
|
||||
str::from_utf8(unsafe { main().as_slice() }).unwrap()
|
||||
);
|
||||
let answer = unsafe {
|
||||
use std::mem::MaybeUninit;
|
||||
let mut output: MaybeUninit<RocCallResult<RocStr>> = MaybeUninit::uninit();
|
||||
|
||||
say_hello(&mut *output.as_mut_ptr());
|
||||
|
||||
match output.assume_init().into() {
|
||||
Ok(value) => value,
|
||||
Err(msg) => panic!("roc failed with message {}", msg),
|
||||
}
|
||||
};
|
||||
|
||||
println!("Roc says: {}", str::from_utf8(answer.as_slice()).unwrap());
|
||||
|
||||
// Exit code
|
||||
0
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use roc_std::RocCallResult;
|
||||
use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1"]
|
||||
fn quicksort(list: RocList<i64>) -> RocList<i64>;
|
||||
#[link_name = "quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 100;
|
||||
|
@ -24,7 +25,17 @@ pub fn rust_main() -> isize {
|
|||
|
||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe { quicksort(nums) };
|
||||
let answer = unsafe {
|
||||
use std::mem::MaybeUninit;
|
||||
let mut output = MaybeUninit::uninit();
|
||||
|
||||
quicksort(nums, &mut *output.as_mut_ptr());
|
||||
|
||||
match output.assume_init().into() {
|
||||
Ok(value) => value,
|
||||
Err(msg) => panic!("roc failed with message: {}", msg),
|
||||
}
|
||||
};
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use roc_std::RocCallResult;
|
||||
use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1"]
|
||||
fn quicksort(list: RocList<i64>) -> RocList<i64>;
|
||||
#[link_name = "quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 100;
|
||||
|
@ -24,7 +25,18 @@ pub fn rust_main() -> isize {
|
|||
|
||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe { quicksort(nums) };
|
||||
let answer = unsafe {
|
||||
use std::mem::MaybeUninit;
|
||||
let mut output = MaybeUninit::uninit();
|
||||
|
||||
quicksort(nums, &mut *output.as_mut_ptr());
|
||||
|
||||
match output.assume_init().into() {
|
||||
Ok(value) => value,
|
||||
Err(msg) => panic!("roc failed with message {}", msg),
|
||||
}
|
||||
};
|
||||
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
|
|
23
examples/shared-quicksort/platform/Cargo.lock
generated
Normal file
23
examples/shared-quicksort/platform/Cargo.lock
generated
Normal file
|
@ -0,0 +1,23 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"roc_std 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "roc_std"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[metadata]
|
||||
"checksum libc 0.2.79 (registry+https://github.com/rust-lang/crates.io-index)" = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
|
13
examples/shared-quicksort/platform/Cargo.toml
Normal file
13
examples/shared-quicksort/platform/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "host"
|
||||
version = "0.1.0"
|
||||
authors = ["Richard Feldman <oss@rtfeldman.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
roc_std = { path = "../../../roc_std" }
|
||||
|
||||
[workspace]
|
|
@ -1,49 +0,0 @@
|
|||
# Rebuilding the host from source
|
||||
|
||||
Here are the current steps to rebuild this host. These
|
||||
steps can likely be moved into a `build.rs` script after
|
||||
turning `host.rs` into a `cargo` project, but that hasn't
|
||||
been attempted yet.
|
||||
|
||||
## Compile the Rust and C sources
|
||||
|
||||
Currently this host has both a `host.rs` and a `host.c`.
|
||||
This is only because we haven't figured out a way to convince
|
||||
Rust to emit a `.o` file that doesn't define a `main` entrypoint,
|
||||
but which is capable of being linked into one later.
|
||||
|
||||
As a workaround, we have `host.rs` expose a function called
|
||||
`rust_main` instead of `main`, and all `host.c` does is provide
|
||||
an actual `main` which imports and then calls `rust_main` from
|
||||
the compiled `host.rs`. It's not the most elegant workaround,
|
||||
but [asking on `users.rust-lang.org`](https://users.rust-lang.org/t/error-when-compiling-linking-with-o-files/49635/4)
|
||||
didn't turn up any nicer approaches. Maybe they're out there though!
|
||||
|
||||
To make this workaround happen, we need to compile both `host.rs`
|
||||
and `host.c`. First, `cd` into `platform/host/src/` and then run:
|
||||
|
||||
```
|
||||
$ clang -c host.c -o c_host.o
|
||||
$ rustc host.rs -o rust_host.o
|
||||
```
|
||||
|
||||
Now we should have `c_host.o` and `rust_host.o` in the curent directory.
|
||||
|
||||
## Link together the `.o` files
|
||||
|
||||
Next, combine `c_host.o` and `rust_host.o` into `host.o` using `ld -r` like so:
|
||||
|
||||
```
|
||||
$ ld -r c_host.o rust_host.o -o host.o
|
||||
```
|
||||
|
||||
Move `host.o` into the appropriate `platform/` subdirectory
|
||||
based on your architecture and operating system. For example,
|
||||
on macOS, you'd move `host.o` into the `platform/host/x86_64-unknown-darwin10/` directory.
|
||||
|
||||
## All done!
|
||||
|
||||
Congratulations! You now have an updated host.
|
||||
|
||||
It's now fine to delete `c_host.o` and `rust_host.o`,
|
||||
since they were only needed to produce `host.o`.
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# compile c_host.o and rust_host.o
|
||||
clang -c host.c -o c_host.o
|
||||
rustc host.rs -o rust_host.o
|
||||
|
||||
# link them together into host.o
|
||||
ld -r c_host.o rust_host.o -o host.o
|
||||
|
||||
# clean up
|
||||
rm -f c_host.o
|
||||
rm -f rust_host.o
|
|
@ -1,47 +0,0 @@
|
|||
#![crate_type = "staticlib"]
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[allow(improper_ctypes)]
|
||||
#[link_name = "quicksort_1"]
|
||||
fn quicksort(list: Box<[i64]>) -> Box<[i64]>;
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 10_000;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
let nums: Box<[i64]> = {
|
||||
let mut nums = Vec::with_capacity(NUM_NUMS);
|
||||
|
||||
for index in 0..nums.capacity() {
|
||||
let num = index as i64 % 123;
|
||||
|
||||
nums.push(num);
|
||||
}
|
||||
nums.into()
|
||||
};
|
||||
|
||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe { quicksort(nums) };
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
println!(
|
||||
"Roc quicksort took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
&answer[0..20]
|
||||
);
|
||||
|
||||
// the pointer is to the first _element_ of the list,
|
||||
// but the refcount precedes it. Thus calling free() on
|
||||
// this pointer would segfault/cause badness. Therefore, we
|
||||
// leak it for now
|
||||
Box::leak(answer);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
52
examples/shared-quicksort/platform/src/lib.rs
Normal file
52
examples/shared-quicksort/platform/src/lib.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use roc_std::RocCallResult;
|
||||
use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
const NUM_NUMS: usize = 100;
|
||||
|
||||
#[no_mangle]
|
||||
pub fn rust_main() -> isize {
|
||||
let nums: RocList<i64> = {
|
||||
let mut nums = Vec::with_capacity(NUM_NUMS);
|
||||
|
||||
for index in 0..nums.capacity() {
|
||||
let num = index as i64 % 12;
|
||||
|
||||
nums.push(num);
|
||||
}
|
||||
|
||||
RocList::from_slice(&nums)
|
||||
};
|
||||
|
||||
println!("Running Roc quicksort on {} numbers...", nums.len());
|
||||
let start_time = SystemTime::now();
|
||||
let answer = unsafe {
|
||||
use std::mem::MaybeUninit;
|
||||
let mut output = MaybeUninit::uninit();
|
||||
|
||||
quicksort(nums, &mut *output.as_mut_ptr());
|
||||
|
||||
match output.assume_init().into() {
|
||||
Ok(value) => value,
|
||||
Err(msg) => panic!("roc failed with message {}", msg),
|
||||
}
|
||||
};
|
||||
|
||||
let end_time = SystemTime::now();
|
||||
let duration = end_time.duration_since(start_time).unwrap();
|
||||
|
||||
println!(
|
||||
"Roc quicksort took {:.4} ms to compute this answer: {:?}",
|
||||
duration.as_secs_f64() * 1000.0,
|
||||
// truncate the answer, so stdout is not swamped
|
||||
&answer.as_slice()[0..20]
|
||||
);
|
||||
|
||||
// Exit code
|
||||
0
|
||||
}
|
2
roc_std/.gitignore
vendored
Normal file
2
roc_std/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.o
|
||||
*.a
|
48
roc_std/build.rs
Normal file
48
roc_std/build.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Adapted from https://github.com/TheDan64/scoped_alloca
|
||||
// by Daniel Kolsoi, licensed under the Apache License 2.0
|
||||
// Thank you, Dan!
|
||||
|
||||
use std::env;
|
||||
use std::fs::create_dir;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
|
||||
let cargo_dir = Path::new(manifest_dir.as_str());
|
||||
let lib_dir = cargo_dir.join("lib");
|
||||
let alloca_c = cargo_dir.join("src/alloca.c");
|
||||
let alloca_o = lib_dir.join("alloca.o");
|
||||
let liballoca_a = lib_dir.join("liballoca.a");
|
||||
|
||||
println!("cargo:rustc-link-search=native={}", lib_dir.display());
|
||||
|
||||
// No need to recompile alloca static lib every time. We could
|
||||
// add a feature flag to do so if needed, though
|
||||
if liballoca_a.is_file() {
|
||||
return;
|
||||
}
|
||||
|
||||
if !lib_dir.is_dir() {
|
||||
create_dir(&lib_dir).unwrap();
|
||||
}
|
||||
|
||||
let clang_output = Command::new("clang")
|
||||
.arg("-c")
|
||||
.arg(alloca_c)
|
||||
.arg("-o")
|
||||
.arg(&alloca_o)
|
||||
.output()
|
||||
.expect("Could not execute clang");
|
||||
|
||||
assert!(clang_output.status.success(), "{:?}", clang_output);
|
||||
|
||||
let ar_output = Command::new("ar")
|
||||
.arg("-q")
|
||||
.arg(liballoca_a)
|
||||
.arg(alloca_o)
|
||||
.output()
|
||||
.expect("Could not execute ar");
|
||||
|
||||
assert!(ar_output.status.success(), "{:?}", ar_output);
|
||||
}
|
9
roc_std/src/alloca.c
Normal file
9
roc_std/src/alloca.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include <alloca.h>
|
||||
|
||||
// From https://github.com/TheDan64/scoped_alloca
|
||||
// by Daniel Kolsoi, licensed under the Apache License 2.0
|
||||
// Thank you, Dan!
|
||||
|
||||
void *c_alloca(size_t bytes) {
|
||||
return alloca(bytes);
|
||||
}
|
124
roc_std/src/alloca.rs
Normal file
124
roc_std/src/alloca.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
// Adapted from https://github.com/TheDan64/scoped_alloca
|
||||
// by Daniel Kolsoi, licensed under the Apache License 2.0
|
||||
// Thank you, Dan!
|
||||
|
||||
use libc::{c_void, size_t};
|
||||
|
||||
#[link(name = "alloca")]
|
||||
extern "C" {
|
||||
#[no_mangle]
|
||||
fn c_alloca(_: size_t) -> *mut c_void;
|
||||
}
|
||||
|
||||
/// This calls C's `alloca` function to allocate some bytes on the stack,
|
||||
/// then provides those bytes to the given callback function.
|
||||
///
|
||||
/// It provides a `&mut c_void` to reflect the lifetime of that memory
|
||||
/// (it only lives for the duration of the callback), which you'll probably
|
||||
/// want to cast to something else. To cast `ptr` to the type you want,
|
||||
/// use `&mut *(ptr as *mut _ as *mut _)` - for example:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// with_stack_bytes(size_of::<Foo>(), |ptr| {
|
||||
/// let foo: &mut Foo = &mut *(ptr as *mut _ as *mut _);
|
||||
/// ```
|
||||
///
|
||||
/// Yes, both `as *mut _` casts are necessary! The first one casts it to
|
||||
/// a `*mut c_void` and the second one casts it to the pointer type you want.
|
||||
/// Finally, the `&mut *` converts it from a pointer to a mutable reference.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is `#[inline(always)]`, which means it will allocate
|
||||
/// the bytes on the stack of whoever calls this function.
|
||||
///
|
||||
/// Naturally, if you give this a large number of bytes, it may cause
|
||||
/// stack overflows, so be careful!
|
||||
///
|
||||
/// Due to how Rust FFI works with inlining, in debug builds this actually
|
||||
/// allocates on the heap (using `malloc`) and then deallocates the memory
|
||||
/// (using `free`) before the callback returns. In debug builds, this can lead
|
||||
/// to memory leaks if the callback panics - but release builds should be fine,
|
||||
/// because they only ever allocate on the stack.
|
||||
///
|
||||
#[inline(always)]
|
||||
pub unsafe fn with_stack_bytes<F, R>(bytes: usize, callback: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut c_void) -> R,
|
||||
{
|
||||
let ptr = malloc_or_alloca(bytes);
|
||||
let answer = callback(&mut *ptr);
|
||||
|
||||
free_or_noop(ptr);
|
||||
|
||||
answer
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[inline(always)]
|
||||
unsafe fn malloc_or_alloca(bytes: usize) -> *mut c_void {
|
||||
libc::malloc(bytes)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[inline(always)]
|
||||
unsafe fn malloc_or_alloca(bytes: usize) -> *mut c_void {
|
||||
c_alloca(bytes)
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
#[inline(always)]
|
||||
unsafe fn free_or_noop(ptr: *mut c_void) {
|
||||
libc::free(ptr)
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
#[inline(always)]
|
||||
fn free_or_noop(_ptr: *mut c_void) {
|
||||
// In release builds, we'll have used alloca,
|
||||
// so there's nothing to free.
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::with_stack_bytes;
|
||||
use core::mem::size_of;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct TestStruct {
|
||||
x: u8,
|
||||
y: u16,
|
||||
z: u64,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_alloca() {
|
||||
let test_struct = TestStruct {
|
||||
x: 123,
|
||||
y: 4567,
|
||||
z: 89012345678,
|
||||
};
|
||||
let sum: u64 = unsafe {
|
||||
with_stack_bytes(size_of::<TestStruct>(), |ptr| {
|
||||
// Surprisingly, both casts are necessary; the first one
|
||||
// turns it from &mut c_void to *mut c_void, and the second
|
||||
// one turns it into *mut TestStruct
|
||||
let new_ts: &mut TestStruct = &mut *(ptr as *mut _ as *mut _);
|
||||
|
||||
new_ts.x = test_struct.x;
|
||||
new_ts.y = test_struct.y;
|
||||
new_ts.z = test_struct.z;
|
||||
|
||||
assert_eq!(new_ts, &test_struct);
|
||||
|
||||
new_ts.x as u64 + new_ts.y as u64 + new_ts.z as u64
|
||||
})
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
sum,
|
||||
test_struct.x as u64 + test_struct.y as u64 + test_struct.z as u64
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@
|
|||
#![no_std]
|
||||
use core::fmt;
|
||||
|
||||
pub mod alloca;
|
||||
|
||||
// A list of C functions that are being imported
|
||||
extern "C" {
|
||||
pub fn printf(format: *const u8, ...) -> i32;
|
||||
|
@ -426,3 +428,39 @@ impl Drop for RocStr {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
type c_char = u8;
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum RocCallResult<T> {
|
||||
Success(T),
|
||||
Failure(*mut c_char),
|
||||
}
|
||||
|
||||
impl<T: Sized> Into<Result<T, &'static str>> for RocCallResult<T> {
|
||||
fn into(self) -> Result<T, &'static str> {
|
||||
use RocCallResult::*;
|
||||
|
||||
match self {
|
||||
Success(value) => Ok(value),
|
||||
Failure(failure) => Err({
|
||||
let msg = unsafe {
|
||||
let mut null_byte_index = 0;
|
||||
loop {
|
||||
if *failure.offset(null_byte_index) == 0 {
|
||||
break;
|
||||
}
|
||||
null_byte_index += 1;
|
||||
}
|
||||
|
||||
let bytes = core::slice::from_raw_parts(failure, null_byte_index as usize);
|
||||
|
||||
core::str::from_utf8_unchecked(bytes)
|
||||
};
|
||||
|
||||
msg
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue