mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
Merge branch 'trunk' into record-bool-tests
This commit is contained in:
commit
2cc4f1767e
@ -80,7 +80,7 @@ If MacOS and using a version >= 10.15:
|
||||
|
||||
You may prefer to setup up the volume manually by following nix documentation.
|
||||
|
||||
> You my need to restart your terminal
|
||||
> You may need to restart your terminal
|
||||
|
||||
### Usage
|
||||
|
||||
@ -96,6 +96,32 @@ You should be in a shell with everything needed to build already installed. Next
|
||||
|
||||
You should be in a repl now. Have fun!
|
||||
|
||||
### Editor
|
||||
|
||||
When you want to run the editor from Ubuntu inside nix you need to install [nixGL](https://github.com/guibou/nixGL) as well:
|
||||
|
||||
```bash
|
||||
nix-shell
|
||||
git clone https://github.com/guibou/nixGL
|
||||
cd nixGL
|
||||
```
|
||||
|
||||
If you have an Nvidia graphics card, run:
|
||||
```
|
||||
nix-env -f ./ -iA nixVulkanNvidia
|
||||
```
|
||||
If you have integrated Intel graphics, run:
|
||||
```
|
||||
nix-env -f ./ -iA nixVulkanIntel
|
||||
```
|
||||
Check the [nixGL repo](https://github.com/guibou/nixGL) for other configurations.
|
||||
|
||||
Now you should be able to run the editor:
|
||||
```bash
|
||||
cd roc
|
||||
nixVulkanNvidia cargo run edit `# replace Nvidia with the config you chose in the previous step`
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Create an issue if you run into problems not listed here.
|
||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2524,6 +2524,7 @@ dependencies = [
|
||||
"roc_solve",
|
||||
"roc_types",
|
||||
"roc_unify",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -47,7 +47,8 @@ pub fn build_file(
|
||||
let app_o_file = roc_file_path.with_file_name("roc_app.o");
|
||||
let buf = &mut String::with_capacity(1024);
|
||||
|
||||
for (module_id, module_timing) in loaded.timings.iter() {
|
||||
let mut it = loaded.timings.iter().peekable();
|
||||
while let Some((module_id, module_timing)) = it.next() {
|
||||
let module_name = loaded.interns.module_name(*module_id);
|
||||
|
||||
buf.push_str(" ");
|
||||
@ -60,9 +61,23 @@ pub fn build_file(
|
||||
report_timing(buf, "Canonicalize", module_timing.canonicalize);
|
||||
report_timing(buf, "Constrain", module_timing.constrain);
|
||||
report_timing(buf, "Solve", module_timing.solve);
|
||||
report_timing(
|
||||
buf,
|
||||
"Find Specializations",
|
||||
module_timing.find_specializations,
|
||||
);
|
||||
report_timing(
|
||||
buf,
|
||||
"Make Specializations",
|
||||
module_timing.make_specializations,
|
||||
);
|
||||
report_timing(buf, "Other", module_timing.other());
|
||||
buf.push('\n');
|
||||
report_timing(buf, "Total", module_timing.total());
|
||||
|
||||
if it.peek().is_some() {
|
||||
buf.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
println!(
|
||||
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
||||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "main_1_exposed"]
|
||||
#[link_name = "Main_main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
||||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "main_1_exposed"]
|
||||
#[link_name = "Main_main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ pub fn run_cmd(cmd_name: &str, args: &[&str]) -> Out {
|
||||
|
||||
let output = cmd
|
||||
.output()
|
||||
.expect(&format!("failed to execute cmd `{}` in CLI test", cmd_name));
|
||||
.unwrap_or_else(|_| panic!("failed to execute cmd `{}` in CLI test", cmd_name));
|
||||
|
||||
Out {
|
||||
stdout: String::from_utf8(output.stdout).unwrap(),
|
||||
@ -263,12 +263,12 @@ pub fn repl_eval(input: &str) -> Out {
|
||||
|
||||
// Evaluate the expression
|
||||
stdin
|
||||
.write_all("\n".as_bytes())
|
||||
.write_all(b"\n")
|
||||
.expect("Failed to write newline to stdin");
|
||||
|
||||
// Gracefully exit the repl
|
||||
stdin
|
||||
.write_all(":exit\n".as_bytes())
|
||||
.write_all(b":exit\n")
|
||||
.expect("Failed to write :exit to stdin");
|
||||
}
|
||||
|
||||
|
@ -227,15 +227,7 @@ fn can_annotation_help(
|
||||
// instantiate variables
|
||||
actual.substitute(&substitutions);
|
||||
|
||||
// Type::Alias(symbol, vars, Box::new(actual))
|
||||
let actual_var = var_store.fresh();
|
||||
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||
Type::HostExposedAlias {
|
||||
name: symbol,
|
||||
arguments: vars,
|
||||
actual: Box::new(actual),
|
||||
actual_var,
|
||||
}
|
||||
Type::Alias(symbol, vars, Box::new(actual))
|
||||
}
|
||||
None => {
|
||||
let mut args = Vec::new();
|
||||
@ -369,13 +361,17 @@ fn can_annotation_help(
|
||||
|
||||
// Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
|
||||
|
||||
let actual_var = var_store.fresh();
|
||||
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||
Type::HostExposedAlias {
|
||||
name: symbol,
|
||||
arguments: vars,
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
actual_var,
|
||||
if vars.is_empty() && env.home == symbol.module_id() {
|
||||
let actual_var = var_store.fresh();
|
||||
introduced_variables.insert_host_exposed_alias(symbol, actual_var);
|
||||
Type::HostExposedAlias {
|
||||
name: symbol,
|
||||
arguments: vars,
|
||||
actual: Box::new(alias.typ.clone()),
|
||||
actual_var,
|
||||
}
|
||||
} else {
|
||||
Type::Alias(symbol, vars, Box::new(alias.typ.clone()))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -251,8 +251,8 @@ fn num_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
||||
)
|
||||
}
|
||||
|
||||
/// Num a, Num a -> Bool
|
||||
fn num_bool_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
||||
/// Num a, Num a -> b
|
||||
fn num_num_other_binop(symbol: Symbol, var_store: &mut VarStore, op: LowLevel) -> Def {
|
||||
let num_var = var_store.fresh();
|
||||
let bool_var = var_store.fresh();
|
||||
let body = RunLowLevel {
|
||||
@ -381,27 +381,27 @@ fn num_mul(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
|
||||
/// Num.isGt : Num a, Num a -> Bool
|
||||
fn num_gt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bool_binop(symbol, var_store, LowLevel::NumGt)
|
||||
num_num_other_binop(symbol, var_store, LowLevel::NumGt)
|
||||
}
|
||||
|
||||
/// Num.isGte : Num a, Num a -> Bool
|
||||
fn num_gte(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bool_binop(symbol, var_store, LowLevel::NumGte)
|
||||
num_num_other_binop(symbol, var_store, LowLevel::NumGte)
|
||||
}
|
||||
|
||||
/// Num.isLt : Num a, Num a -> Bool
|
||||
fn num_lt(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bool_binop(symbol, var_store, LowLevel::NumLt)
|
||||
num_num_other_binop(symbol, var_store, LowLevel::NumLt)
|
||||
}
|
||||
|
||||
/// Num.isLte : Num a, Num a -> Num a
|
||||
/// Num.isLte : Num a, Num a -> Bool
|
||||
fn num_lte(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bool_binop(symbol, var_store, LowLevel::NumLte)
|
||||
num_num_other_binop(symbol, var_store, LowLevel::NumLte)
|
||||
}
|
||||
|
||||
/// Num.compare : Num a, Num a -> [ LT, EQ, GT ]
|
||||
fn num_compare(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
num_bool_binop(symbol, var_store, LowLevel::NumCompare)
|
||||
num_num_other_binop(symbol, var_store, LowLevel::NumCompare)
|
||||
}
|
||||
|
||||
/// Num.sin : Float -> Float
|
||||
|
@ -184,17 +184,11 @@ pub fn canonicalize_defs<'a>(
|
||||
let mut can_ann =
|
||||
canonicalize_annotation(env, &mut scope, &ann.value, ann.region, var_store);
|
||||
|
||||
// all referenced symbols in an alias must be symbols
|
||||
output
|
||||
.references
|
||||
.referenced_aliases
|
||||
.extend(can_ann.aliases.keys().copied());
|
||||
|
||||
// if an alias definition uses an alias, the used alias is referenced
|
||||
output
|
||||
.references
|
||||
.lookups
|
||||
.extend(can_ann.aliases.keys().copied());
|
||||
// Record all the annotation's references in output.references.lookups
|
||||
for symbol in can_ann.references {
|
||||
output.references.lookups.insert(symbol);
|
||||
output.references.referenced_aliases.insert(symbol);
|
||||
}
|
||||
|
||||
let mut can_vars: Vec<Located<(Lowercase, Variable)>> =
|
||||
Vec::with_capacity(vars.len());
|
||||
|
@ -304,6 +304,13 @@ fn fix_values_captured_in_closure_defs(
|
||||
defs: &mut Vec<crate::def::Def>,
|
||||
no_capture_symbols: &mut MutSet<Symbol>,
|
||||
) {
|
||||
// recursive defs cannot capture each other
|
||||
for def in defs.iter() {
|
||||
no_capture_symbols.extend(crate::pattern::symbols_from_pattern(&def.loc_pattern.value));
|
||||
}
|
||||
|
||||
// TODO mutually recursive functions should both capture the union of both their capture sets
|
||||
|
||||
for def in defs.iter_mut() {
|
||||
fix_values_captured_in_closure_def(def, no_capture_symbols);
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ pub fn constrain_expr(
|
||||
let stored_con = Eq(
|
||||
Type::Variable(*record_var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -391,7 +391,7 @@ pub fn constrain_expr(
|
||||
Eq(
|
||||
Type::Variable(*fn_var),
|
||||
NoExpectation(fn_type),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
),
|
||||
closure_constraint,
|
||||
@ -469,7 +469,7 @@ pub fn constrain_expr(
|
||||
let ast_con = Eq(
|
||||
Type::Variable(*branch_var),
|
||||
NoExpectation(tipe),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -521,7 +521,7 @@ pub fn constrain_expr(
|
||||
branch_cons.push(Eq(
|
||||
Type::Variable(*branch_var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
));
|
||||
branch_cons.push(else_con);
|
||||
@ -556,7 +556,7 @@ pub fn constrain_expr(
|
||||
let ast_con = Eq(
|
||||
Type::Variable(*expr_var),
|
||||
expected.clone(),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
constraints.push(ast_con);
|
||||
@ -743,7 +743,7 @@ pub fn constrain_expr(
|
||||
Eq(
|
||||
Type::Variable(*var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
loc_ret.region,
|
||||
),
|
||||
]),
|
||||
@ -761,7 +761,7 @@ pub fn constrain_expr(
|
||||
Eq(
|
||||
Type::Variable(*var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
loc_ret.region,
|
||||
),
|
||||
]),
|
||||
@ -805,7 +805,7 @@ pub fn constrain_expr(
|
||||
let ast_con = Eq(
|
||||
Type::Variable(*variant_var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -1006,10 +1006,10 @@ pub fn constrain_decls(home: ModuleId, decls: &[Declaration]) -> Constraint {
|
||||
|
||||
match decl {
|
||||
Declaration::Declare(def) | Declaration::Builtin(def) => {
|
||||
constraint = exists(Vec::new(), constrain_def(&env, def, constraint));
|
||||
constraint = constrain_def(&env, def, constraint);
|
||||
}
|
||||
Declaration::DeclareRec(defs) => {
|
||||
constraint = exists(Vec::new(), constrain_recursive_defs(&env, defs, constraint));
|
||||
constraint = constrain_recursive_defs(&env, defs, constraint);
|
||||
}
|
||||
Declaration::InvalidCycle(_, _) => {
|
||||
// invalid cycles give a canonicalization error. we skip them here.
|
||||
@ -1090,7 +1090,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||
pattern_state.constraints.push(Eq(
|
||||
expr_type,
|
||||
annotation_expected.clone(),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
Region::span_across(&annotation.region, &def.loc_expr.region),
|
||||
));
|
||||
|
||||
@ -1173,7 +1173,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||
state.constraints.push(Constraint::Eq(
|
||||
pattern_type.clone(),
|
||||
Expected::NoExpectation(loc_ann.clone()),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
loc_pattern.region,
|
||||
));
|
||||
|
||||
@ -1218,7 +1218,7 @@ fn constrain_def(env: &Env, def: &Def, body_con: Constraint) -> Constraint {
|
||||
Eq(
|
||||
Type::Variable(*fn_var),
|
||||
NoExpectation(fn_type),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
),
|
||||
closure_constraint,
|
||||
@ -1440,32 +1440,31 @@ pub fn rec_defs_help(
|
||||
},
|
||||
def.loc_expr.region,
|
||||
&def.loc_expr.value,
|
||||
NoExpectation(expr_type.clone()),
|
||||
annotation_expected.clone(),
|
||||
);
|
||||
|
||||
// ensure expected type unifies with annotated type
|
||||
rigid_info.constraints.push(Eq(
|
||||
let storage_con = Eq(
|
||||
expr_type,
|
||||
annotation_expected.clone(),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
def.loc_expr.region,
|
||||
));
|
||||
);
|
||||
|
||||
// TODO investigate if this let can be safely removed
|
||||
let def_con = Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: Vec::new(), // empty because Roc function defs have no args
|
||||
def_types: SendMap::default(), // empty because Roc function defs have no args
|
||||
defs_constraint: True, // I think this is correct, once again because there are no args
|
||||
defs_constraint: storage_con,
|
||||
ret_constraint: expr_con,
|
||||
}));
|
||||
|
||||
rigid_info.vars.extend(&new_rigids);
|
||||
// because of how in Roc headers point to variables, we must include the pattern var here
|
||||
rigid_info.vars.extend(pattern_state.vars);
|
||||
|
||||
rigid_info.constraints.push(Let(Box::new(LetConstraint {
|
||||
rigid_vars: new_rigids,
|
||||
flex_vars: Vec::new(), // no flex vars introduced
|
||||
flex_vars: pattern_state.vars,
|
||||
def_types: SendMap::default(), // no headers introduced (at this level)
|
||||
defs_constraint: def_con,
|
||||
ret_constraint: True,
|
||||
|
@ -271,7 +271,7 @@ pub fn constrain_pattern(
|
||||
let whole_con = Constraint::Eq(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(record_type),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -315,7 +315,7 @@ pub fn constrain_pattern(
|
||||
vec![(tag_name.clone(), argument_types)],
|
||||
Box::new(Type::Variable(*ext_var)),
|
||||
)),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
|
@ -317,7 +317,7 @@ fn constrain_pattern(
|
||||
let whole_con = Constraint::Eq(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(record_type),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -381,7 +381,7 @@ fn constrain_pattern(
|
||||
let whole_con = Constraint::Eq(
|
||||
Type::Variable(*whole_var),
|
||||
Expected::NoExpectation(union_type),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -686,7 +686,12 @@ pub fn constrain_expr(
|
||||
constraints.push(Eq(inferred, expected.clone(), Category::List, region));
|
||||
|
||||
let stored = Type::Variable(*list_var);
|
||||
constraints.push(Eq(stored, expected, Category::Storage, region));
|
||||
constraints.push(Eq(
|
||||
stored,
|
||||
expected,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
));
|
||||
|
||||
exists(vec![*elem_var, *list_var, uniq_var], And(constraints))
|
||||
}
|
||||
@ -1044,7 +1049,7 @@ pub fn constrain_expr(
|
||||
Eq(
|
||||
Type::Variable(*var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
loc_ret.region,
|
||||
),
|
||||
]),
|
||||
@ -1079,7 +1084,7 @@ pub fn constrain_expr(
|
||||
Eq(
|
||||
Type::Variable(*var),
|
||||
expected,
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
loc_ret.region,
|
||||
),
|
||||
]),
|
||||
@ -1175,7 +1180,7 @@ pub fn constrain_expr(
|
||||
let ast_con = Eq(
|
||||
Type::Variable(*branch_var),
|
||||
Expected::NoExpectation(tipe),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
region,
|
||||
);
|
||||
|
||||
@ -2278,7 +2283,7 @@ fn constrain_def(
|
||||
pattern_state.constraints.push(Eq(
|
||||
expr_type,
|
||||
annotation_expected.clone(),
|
||||
Category::Storage,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
Region::zero(),
|
||||
));
|
||||
|
||||
@ -2545,32 +2550,30 @@ pub fn rec_defs_help(
|
||||
applied_usage_constraint,
|
||||
def.loc_expr.region,
|
||||
&def.loc_expr.value,
|
||||
Expected::NoExpectation(expr_type.clone()),
|
||||
annotation_expected.clone(),
|
||||
);
|
||||
|
||||
// ensure expected type unifies with annotated type
|
||||
rigid_info.constraints.push(Eq(
|
||||
let storage_con = Eq(
|
||||
expr_type,
|
||||
annotation_expected.clone(),
|
||||
Category::Storage,
|
||||
annotation_expected,
|
||||
Category::Storage(std::file!(), std::line!()),
|
||||
def.loc_expr.region,
|
||||
));
|
||||
);
|
||||
|
||||
// TODO investigate if this let can be safely removed
|
||||
let def_con = Let(Box::new(LetConstraint {
|
||||
rigid_vars: Vec::new(),
|
||||
flex_vars: Vec::new(), // empty because Roc function defs have no args
|
||||
def_types: SendMap::default(), // empty because Roc function defs have no args
|
||||
defs_constraint: True, // I think this is correct, once again because there are no args
|
||||
defs_constraint: storage_con,
|
||||
ret_constraint: expr_con,
|
||||
}));
|
||||
|
||||
rigid_info.vars.extend(&new_rigids);
|
||||
// because of how in Roc headers point to variables, we must include the pattern var here
|
||||
rigid_info.vars.extend(pattern_state.vars);
|
||||
rigid_info.constraints.push(Let(Box::new(LetConstraint {
|
||||
rigid_vars: new_rigids,
|
||||
flex_vars: Vec::new(), // no flex vars introduced
|
||||
flex_vars: pattern_state.vars,
|
||||
def_types: SendMap::default(), // no headers introduced (at this level)
|
||||
defs_constraint: def_con,
|
||||
ret_constraint: True,
|
||||
|
@ -10,7 +10,9 @@ impl LayoutId {
|
||||
// Returns something like "foo#1" when given a symbol that interns to "foo"
|
||||
// and a LayoutId of 1.
|
||||
pub fn to_symbol_string(self, symbol: Symbol, interns: &Interns) -> String {
|
||||
format!("{}_{}", symbol.ident_string(interns), self.0)
|
||||
let ident_string = symbol.ident_string(interns);
|
||||
let module_string = interns.module_ids.get_name(symbol.module_id()).unwrap();
|
||||
format!("{}_{}_{}", module_string, ident_string, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,15 +13,7 @@ mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod gen_num {
|
||||
/*
|
||||
use inkwell::passes::PassManager;
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::OptimizationLevel;
|
||||
use roc_gen::llvm::build::{build_proc, build_proc_header};
|
||||
use roc_gen::llvm::convert::basic_type_from_layout;
|
||||
use roc_mono::layout::Layout;
|
||||
use roc_types::subs::Subs;
|
||||
*/
|
||||
use roc_std::RocOrder;
|
||||
|
||||
#[test]
|
||||
fn f64_sqrt() {
|
||||
@ -584,86 +576,16 @@ mod gen_num {
|
||||
|
||||
#[test]
|
||||
fn int_compare() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.compare 0 1 is
|
||||
LT -> 0
|
||||
EQ -> 1
|
||||
GT -> 2
|
||||
"#
|
||||
),
|
||||
0,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.compare 1 1 is
|
||||
LT -> 0
|
||||
EQ -> 1
|
||||
GT -> 2
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.compare 1 0 is
|
||||
LT -> 0
|
||||
EQ -> 1
|
||||
GT -> 2
|
||||
"#
|
||||
),
|
||||
2,
|
||||
i64
|
||||
);
|
||||
assert_evals_to!("Num.compare 0 1", RocOrder::Lt, RocOrder);
|
||||
assert_evals_to!("Num.compare 1 1", RocOrder::Eq, RocOrder);
|
||||
assert_evals_to!("Num.compare 1 0", RocOrder::Gt, RocOrder);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_compare() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.compare 0 3.14 is
|
||||
LT -> 0
|
||||
EQ -> 1
|
||||
GT -> 2
|
||||
"#
|
||||
),
|
||||
0,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.compare 3.14 3.14 is
|
||||
LT -> 0
|
||||
EQ -> 1
|
||||
GT -> 2
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.compare 3.14 0 is
|
||||
LT -> 0
|
||||
EQ -> 1
|
||||
GT -> 2
|
||||
"#
|
||||
),
|
||||
2,
|
||||
i64
|
||||
);
|
||||
assert_evals_to!("Num.compare 0.01 3.14", RocOrder::Lt, RocOrder);
|
||||
assert_evals_to!("Num.compare 3.14 3.14", RocOrder::Eq, RocOrder);
|
||||
assert_evals_to!("Num.compare 3.14 0.01", RocOrder::Gt, RocOrder);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -549,28 +549,25 @@ mod gen_primitives {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn linked_list_len_0() {
|
||||
assert_evals_to!(
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app LinkedListLen0 provides [ main ] imports []
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
LinkedList a : [ Nil, Cons a (LinkedList a) ]
|
||||
|
||||
# nil : LinkedList Int
|
||||
nil = Cons 0x1 Nil
|
||||
|
||||
# length : [ Nil, Cons a (LinkedList a) ] as LinkedList a -> Int
|
||||
length : LinkedList a -> Int
|
||||
length = \list ->
|
||||
len : LinkedList a -> Int
|
||||
len = \list ->
|
||||
when list is
|
||||
Nil -> 0
|
||||
Cons _ rest -> 1 + length rest
|
||||
|
||||
Cons _ rest -> 1 + len rest
|
||||
|
||||
main =
|
||||
length nil
|
||||
nil : LinkedList Int
|
||||
nil = Nil
|
||||
|
||||
len nil
|
||||
"#
|
||||
),
|
||||
0,
|
||||
@ -579,9 +576,8 @@ mod gen_primitives {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn linked_list_len_twice_0() {
|
||||
assert_evals_to!(
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app LinkedListLenTwice0 provides [ main ] imports []
|
||||
@ -607,9 +603,8 @@ mod gen_primitives {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn linked_list_len_1() {
|
||||
assert_evals_to!(
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
@ -635,9 +630,8 @@ mod gen_primitives {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn linked_list_len_twice_1() {
|
||||
assert_evals_to!(
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
@ -663,9 +657,8 @@ mod gen_primitives {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn linked_list_len_3() {
|
||||
assert_evals_to!(
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
@ -1111,4 +1104,95 @@ mod gen_primitives {
|
||||
|_| 1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_list_is_empty_1() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
empty : ConsList a
|
||||
empty = Nil
|
||||
|
||||
isEmpty : ConsList a -> Bool
|
||||
isEmpty = \list ->
|
||||
when list is
|
||||
Cons _ _ ->
|
||||
False
|
||||
|
||||
Nil ->
|
||||
True
|
||||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList Int
|
||||
myList = empty
|
||||
|
||||
isEmpty myList
|
||||
"#
|
||||
),
|
||||
true,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn linked_list_is_empty_2() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
# isEmpty : ConsList a -> Bool
|
||||
isEmpty = \list ->
|
||||
when list is
|
||||
Cons _ _ ->
|
||||
False
|
||||
|
||||
Nil ->
|
||||
True
|
||||
|
||||
main : Bool
|
||||
main =
|
||||
myList : ConsList Int
|
||||
myList = Cons 0x1 Nil
|
||||
|
||||
isEmpty myList
|
||||
"#
|
||||
),
|
||||
false,
|
||||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_functon_with_rigid() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
State a : { count : Int, x : a }
|
||||
|
||||
foo : State a -> Int
|
||||
foo = \state ->
|
||||
if state.count == 0 then
|
||||
0
|
||||
else
|
||||
1 + foo { count: state.count - 1, x: state.x }
|
||||
|
||||
main : Int
|
||||
main =
|
||||
foo { count: 3, x: {} }
|
||||
"#
|
||||
),
|
||||
3,
|
||||
i64
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ mod gen_tags {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applied_tag_just_unit() {
|
||||
fn applied_tag_just_enum() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
@ -105,7 +105,7 @@ mod gen_tags {
|
||||
"#
|
||||
),
|
||||
(0, 2),
|
||||
(i64, i64)
|
||||
(i64, u8)
|
||||
);
|
||||
}
|
||||
|
||||
@ -236,7 +236,6 @@ mod gen_tags {
|
||||
// }
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn even_odd() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
|
@ -25,6 +25,7 @@ crossbeam = "0.7"
|
||||
num_cpus = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.1.0"
|
||||
pretty_assertions = "0.5.1"
|
||||
maplit = "1.0.1"
|
||||
indoc = "0.3.3"
|
||||
|
793
compiler/load/src/effect_module.rs
Normal file
793
compiler/load/src/effect_module.rs
Normal file
@ -0,0 +1,793 @@
|
||||
use roc_can::def::{Declaration, Def};
|
||||
use roc_can::env::Env;
|
||||
use roc_can::expr::{Expr, Recursive};
|
||||
use roc_can::pattern::Pattern;
|
||||
use roc_can::scope::Scope;
|
||||
use roc_collections::all::SendMap;
|
||||
use roc_module::ident::TagName;
|
||||
use roc_module::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
use roc_types::subs::{VarStore, Variable};
|
||||
use roc_types::types::Type;
|
||||
|
||||
/// Functions that are always implemented for Effect
|
||||
type Builder = for<'r, 's, 't0, 't1> fn(
|
||||
&'r mut Env<'s>,
|
||||
&'t0 mut Scope,
|
||||
Symbol,
|
||||
TagName,
|
||||
&'t1 mut VarStore,
|
||||
) -> (Symbol, Def);
|
||||
|
||||
pub const BUILTIN_EFFECT_FUNCTIONS: [(&str, Builder); 3] = [
|
||||
// Effect.after : Effect a, (a -> Effect b) -> Effect b
|
||||
("after", build_effect_after),
|
||||
// Effect.map : Effect a, (a -> b) -> Effect b
|
||||
("map", build_effect_map),
|
||||
// Effect.always : a -> Effect a
|
||||
("always", build_effect_always),
|
||||
];
|
||||
|
||||
// the Effects alias & associated functions
|
||||
//
|
||||
// A platform can define an Effect type in its header. It can have an arbitrary name
|
||||
// (e.g. Task, IO), but we'll call it an Effect in general.
|
||||
//
|
||||
// From that name, we generate an effect module, an effect alias, and some functions.
|
||||
//
|
||||
// The effect alias is implemented as
|
||||
//
|
||||
// Effect a : [ @Effect ({} -> a) ]
|
||||
//
|
||||
// For this alias we implement the functions defined in BUILTIN_EFFECT_FUNCTIONS with the
|
||||
// standard implementation.
|
||||
|
||||
pub fn build_effect_builtins(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
var_store: &mut VarStore,
|
||||
exposed_vars_by_symbol: &mut Vec<(Symbol, Variable)>,
|
||||
declarations: &mut Vec<Declaration>,
|
||||
) {
|
||||
for (_, f) in BUILTIN_EFFECT_FUNCTIONS.iter() {
|
||||
let (symbol, def) = f(
|
||||
env,
|
||||
scope,
|
||||
effect_symbol,
|
||||
TagName::Private(effect_symbol),
|
||||
var_store,
|
||||
);
|
||||
|
||||
exposed_vars_by_symbol.push((symbol, def.expr_var));
|
||||
declarations.push(Declaration::Declare(def));
|
||||
}
|
||||
}
|
||||
|
||||
fn build_effect_always(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// Effect.always = \value -> @Effect \{} -> value
|
||||
|
||||
let value_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_always_value".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let inner_closure_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_always_inner".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let always_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"always".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// \{} -> value
|
||||
let const_closure = {
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(empty_record_pattern(var_store)),
|
||||
)];
|
||||
|
||||
let body = Expr::Var(value_symbol);
|
||||
|
||||
Expr::Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: inner_closure_symbol,
|
||||
captured_symbols: vec![(value_symbol, var_store.fresh())],
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Located::at_zero(body)),
|
||||
}
|
||||
};
|
||||
|
||||
// \value -> @Effect \{} -> value
|
||||
let (function_var, always_closure) = {
|
||||
// `@Effect \{} -> value`
|
||||
let body = Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name.clone(),
|
||||
arguments: vec![(var_store.fresh(), Located::at_zero(const_closure))],
|
||||
};
|
||||
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::Identifier(value_symbol)),
|
||||
)];
|
||||
|
||||
let function_var = var_store.fresh();
|
||||
let closure = Expr::Closure {
|
||||
function_type: function_var,
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: always_symbol,
|
||||
captured_symbols: Vec::new(),
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Located::at_zero(body)),
|
||||
};
|
||||
|
||||
(function_var, closure)
|
||||
};
|
||||
|
||||
use roc_can::annotation::IntroducedVariables;
|
||||
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
|
||||
let signature = {
|
||||
// Effect.always : a -> Effect a
|
||||
let var_a = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
|
||||
let effect_a = {
|
||||
let actual = build_effect_actual(effect_tag_name, Type::Variable(var_a), var_store);
|
||||
|
||||
Type::Alias(
|
||||
effect_symbol,
|
||||
vec![("a".into(), Type::Variable(var_a))],
|
||||
Box::new(actual),
|
||||
)
|
||||
};
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
|
||||
Type::Function(
|
||||
vec![Type::Variable(var_a)],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(effect_a),
|
||||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let pattern = Pattern::Identifier(always_symbol);
|
||||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(always_symbol, function_var);
|
||||
let def = Def {
|
||||
loc_pattern: Located::at_zero(pattern),
|
||||
loc_expr: Located::at_zero(always_closure),
|
||||
expr_var: function_var,
|
||||
pattern_vars,
|
||||
annotation: Some(def_annotation),
|
||||
};
|
||||
|
||||
(always_symbol, def)
|
||||
}
|
||||
|
||||
fn build_effect_map(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// Effect.map = \@Effect thunk, mapper -> @Effect \{} -> mapper (thunk {})
|
||||
|
||||
let thunk_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_map_thunk".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let mapper_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_map_mapper".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let map_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"map".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// `thunk {}`
|
||||
let force_thunk_call = {
|
||||
let boxed = (
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Expr::Var(thunk_symbol)),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
);
|
||||
|
||||
let arguments = vec![(var_store.fresh(), Located::at_zero(Expr::EmptyRecord))];
|
||||
Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
|
||||
};
|
||||
|
||||
// `toEffect (thunk {})`
|
||||
let mapper_call = {
|
||||
let boxed = (
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Expr::Var(mapper_symbol)),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
);
|
||||
|
||||
let arguments = vec![(var_store.fresh(), Located::at_zero(force_thunk_call))];
|
||||
Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
|
||||
};
|
||||
|
||||
let inner_closure_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_map_inner".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// \{} -> mapper (thunk {})
|
||||
let inner_closure = {
|
||||
let arguments = vec![(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(empty_record_pattern(var_store)),
|
||||
)];
|
||||
|
||||
Expr::Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: inner_closure_symbol,
|
||||
captured_symbols: vec![
|
||||
(thunk_symbol, var_store.fresh()),
|
||||
(mapper_symbol, var_store.fresh()),
|
||||
],
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Located::at_zero(mapper_call)),
|
||||
}
|
||||
};
|
||||
|
||||
let arguments = vec![
|
||||
(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::AppliedTag {
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
tag_name: effect_tag_name.clone(),
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::Identifier(thunk_symbol)),
|
||||
)],
|
||||
}),
|
||||
),
|
||||
(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::Identifier(mapper_symbol)),
|
||||
),
|
||||
];
|
||||
|
||||
// `@Effect \{} -> (mapper (thunk {}))`
|
||||
let body = Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name.clone(),
|
||||
arguments: vec![(var_store.fresh(), Located::at_zero(inner_closure))],
|
||||
};
|
||||
|
||||
let function_var = var_store.fresh();
|
||||
let map_closure = Expr::Closure {
|
||||
function_type: function_var,
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: map_symbol,
|
||||
captured_symbols: Vec::new(),
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Located::at_zero(body)),
|
||||
};
|
||||
|
||||
use roc_can::annotation::IntroducedVariables;
|
||||
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
|
||||
let signature = {
|
||||
// Effect.map : Effect a, (a -> b) -> Effect b
|
||||
let var_a = var_store.fresh();
|
||||
let var_b = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
introduced_variables.insert_named("b".into(), var_b);
|
||||
|
||||
let effect_a = {
|
||||
let actual =
|
||||
build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store);
|
||||
|
||||
Type::Alias(
|
||||
effect_symbol,
|
||||
vec![("a".into(), Type::Variable(var_a))],
|
||||
Box::new(actual),
|
||||
)
|
||||
};
|
||||
|
||||
let effect_b = {
|
||||
let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store);
|
||||
|
||||
Type::Alias(
|
||||
effect_symbol,
|
||||
vec![("b".into(), Type::Variable(var_b))],
|
||||
Box::new(actual),
|
||||
)
|
||||
};
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
let a_to_b = {
|
||||
Type::Function(
|
||||
vec![Type::Variable(var_a)],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(Type::Variable(var_b)),
|
||||
)
|
||||
};
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
Type::Function(
|
||||
vec![effect_a, a_to_b],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(effect_b),
|
||||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let pattern = Pattern::Identifier(map_symbol);
|
||||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(map_symbol, function_var);
|
||||
let def = Def {
|
||||
loc_pattern: Located::at_zero(pattern),
|
||||
loc_expr: Located::at_zero(map_closure),
|
||||
expr_var: function_var,
|
||||
pattern_vars,
|
||||
annotation: Some(def_annotation),
|
||||
};
|
||||
|
||||
(map_symbol, def)
|
||||
}
|
||||
|
||||
fn build_effect_after(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
effect_symbol: Symbol,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
) -> (Symbol, Def) {
|
||||
// Effect.after = \@Effect effect, toEffect -> toEffect (effect {})
|
||||
|
||||
let thunk_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_after_thunk".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let to_effect_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"effect_after_toEffect".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let after_symbol = {
|
||||
scope
|
||||
.introduce(
|
||||
"after".into(),
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// `thunk {}`
|
||||
let force_thunk_call = {
|
||||
let boxed = (
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Expr::Var(thunk_symbol)),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
);
|
||||
|
||||
let arguments = vec![(var_store.fresh(), Located::at_zero(Expr::EmptyRecord))];
|
||||
Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
|
||||
};
|
||||
|
||||
// `toEffect (thunk {})`
|
||||
let to_effect_call = {
|
||||
let boxed = (
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Expr::Var(to_effect_symbol)),
|
||||
var_store.fresh(),
|
||||
var_store.fresh(),
|
||||
);
|
||||
|
||||
let arguments = vec![(var_store.fresh(), Located::at_zero(force_thunk_call))];
|
||||
Expr::Call(Box::new(boxed), arguments, CalledVia::Space)
|
||||
};
|
||||
|
||||
let arguments = vec![
|
||||
(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::AppliedTag {
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
tag_name: effect_tag_name.clone(),
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::Identifier(thunk_symbol)),
|
||||
)],
|
||||
}),
|
||||
),
|
||||
(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(Pattern::Identifier(to_effect_symbol)),
|
||||
),
|
||||
];
|
||||
|
||||
let function_var = var_store.fresh();
|
||||
let after_closure = Expr::Closure {
|
||||
function_type: function_var,
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: after_symbol,
|
||||
captured_symbols: Vec::new(),
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Located::at_zero(to_effect_call)),
|
||||
};
|
||||
|
||||
use roc_can::annotation::IntroducedVariables;
|
||||
|
||||
let mut introduced_variables = IntroducedVariables::default();
|
||||
|
||||
let signature = {
|
||||
let var_a = var_store.fresh();
|
||||
let var_b = var_store.fresh();
|
||||
|
||||
introduced_variables.insert_named("a".into(), var_a);
|
||||
introduced_variables.insert_named("b".into(), var_b);
|
||||
|
||||
let effect_a = {
|
||||
let actual =
|
||||
build_effect_actual(effect_tag_name.clone(), Type::Variable(var_a), var_store);
|
||||
|
||||
Type::Alias(
|
||||
effect_symbol,
|
||||
vec![("a".into(), Type::Variable(var_a))],
|
||||
Box::new(actual),
|
||||
)
|
||||
};
|
||||
|
||||
let effect_b = {
|
||||
let actual = build_effect_actual(effect_tag_name, Type::Variable(var_b), var_store);
|
||||
|
||||
Type::Alias(
|
||||
effect_symbol,
|
||||
vec![("b".into(), Type::Variable(var_b))],
|
||||
Box::new(actual),
|
||||
)
|
||||
};
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
let a_to_effect_b = Type::Function(
|
||||
vec![Type::Variable(var_a)],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(effect_b.clone()),
|
||||
);
|
||||
|
||||
let closure_var = var_store.fresh();
|
||||
introduced_variables.insert_wildcard(closure_var);
|
||||
Type::Function(
|
||||
vec![effect_a, a_to_effect_b],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(effect_b),
|
||||
)
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
signature,
|
||||
introduced_variables,
|
||||
aliases: SendMap::default(),
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
let pattern = Pattern::Identifier(after_symbol);
|
||||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(after_symbol, function_var);
|
||||
let def = Def {
|
||||
loc_pattern: Located::at_zero(pattern),
|
||||
loc_expr: Located::at_zero(after_closure),
|
||||
expr_var: function_var,
|
||||
pattern_vars,
|
||||
annotation: Some(def_annotation),
|
||||
};
|
||||
|
||||
(after_symbol, def)
|
||||
}
|
||||
|
||||
pub fn build_host_exposed_def(
|
||||
env: &mut Env,
|
||||
scope: &mut Scope,
|
||||
symbol: Symbol,
|
||||
ident: &str,
|
||||
effect_tag_name: TagName,
|
||||
var_store: &mut VarStore,
|
||||
annotation: roc_can::annotation::Annotation,
|
||||
) -> Def {
|
||||
let expr_var = var_store.fresh();
|
||||
let pattern = Pattern::Identifier(symbol);
|
||||
let mut pattern_vars = SendMap::default();
|
||||
pattern_vars.insert(symbol, expr_var);
|
||||
|
||||
let mut arguments: Vec<(Variable, Located<Pattern>)> = Vec::new();
|
||||
let mut linked_symbol_arguments: Vec<(Variable, Expr)> = Vec::new();
|
||||
let mut captured_symbols: Vec<(Symbol, Variable)> = Vec::new();
|
||||
|
||||
let def_body = {
|
||||
match annotation.typ.shallow_dealias() {
|
||||
Type::Function(args, _, _) => {
|
||||
for i in 0..args.len() {
|
||||
let name = format!("closure_arg_{}_{}", ident, i);
|
||||
|
||||
let arg_symbol = {
|
||||
let ident = name.clone().into();
|
||||
scope
|
||||
.introduce(
|
||||
ident,
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let arg_var = var_store.fresh();
|
||||
|
||||
arguments.push((arg_var, Located::at_zero(Pattern::Identifier(arg_symbol))));
|
||||
|
||||
captured_symbols.push((arg_symbol, arg_var));
|
||||
linked_symbol_arguments.push((arg_var, Expr::Var(arg_symbol)));
|
||||
}
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{}", ident);
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
ret_var: var_store.fresh(),
|
||||
};
|
||||
|
||||
let effect_closure_symbol = {
|
||||
let name = format!("effect_closure_{}", ident);
|
||||
|
||||
let ident = name.into();
|
||||
scope
|
||||
.introduce(
|
||||
ident,
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let effect_closure = Expr::Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: effect_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(
|
||||
var_store.fresh(),
|
||||
Located::at_zero(empty_record_pattern(var_store)),
|
||||
)],
|
||||
loc_body: Box::new(Located::at_zero(low_level_call)),
|
||||
};
|
||||
|
||||
let body = Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name,
|
||||
arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))],
|
||||
};
|
||||
|
||||
Expr::Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: symbol,
|
||||
captured_symbols: std::vec::Vec::new(),
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments,
|
||||
loc_body: Box::new(Located::at_zero(body)),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// not a function
|
||||
|
||||
let foreign_symbol_name = format!("roc_fx_{}", ident);
|
||||
let low_level_call = Expr::ForeignCall {
|
||||
foreign_symbol: foreign_symbol_name.into(),
|
||||
args: linked_symbol_arguments,
|
||||
ret_var: var_store.fresh(),
|
||||
};
|
||||
|
||||
let effect_closure_symbol = {
|
||||
let name = format!("effect_closure_{}", ident);
|
||||
|
||||
let ident = name.into();
|
||||
scope
|
||||
.introduce(
|
||||
ident,
|
||||
&env.exposed_ident_ids,
|
||||
&mut env.ident_ids,
|
||||
Region::zero(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let empty_record_pattern = Pattern::RecordDestructure {
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
destructs: vec![],
|
||||
};
|
||||
|
||||
let effect_closure = Expr::Closure {
|
||||
function_type: var_store.fresh(),
|
||||
closure_type: var_store.fresh(),
|
||||
closure_ext_var: var_store.fresh(),
|
||||
return_type: var_store.fresh(),
|
||||
name: effect_closure_symbol,
|
||||
captured_symbols,
|
||||
recursive: Recursive::NotRecursive,
|
||||
arguments: vec![(var_store.fresh(), Located::at_zero(empty_record_pattern))],
|
||||
loc_body: Box::new(Located::at_zero(low_level_call)),
|
||||
};
|
||||
|
||||
Expr::Tag {
|
||||
variant_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
name: effect_tag_name,
|
||||
arguments: vec![(var_store.fresh(), Located::at_zero(effect_closure))],
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let def_annotation = roc_can::def::Annotation {
|
||||
signature: annotation.typ,
|
||||
introduced_variables: annotation.introduced_variables,
|
||||
aliases: annotation.aliases,
|
||||
region: Region::zero(),
|
||||
};
|
||||
|
||||
Def {
|
||||
loc_pattern: Located::at_zero(pattern),
|
||||
loc_expr: Located::at_zero(def_body),
|
||||
expr_var,
|
||||
pattern_vars,
|
||||
annotation: Some(def_annotation),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_effect_actual(
|
||||
effect_tag_name: TagName,
|
||||
a_type: Type,
|
||||
var_store: &mut VarStore,
|
||||
) -> Type {
|
||||
let closure_var = var_store.fresh();
|
||||
|
||||
Type::TagUnion(
|
||||
vec![(
|
||||
effect_tag_name,
|
||||
vec![Type::Function(
|
||||
vec![Type::EmptyRec],
|
||||
Box::new(Type::Variable(closure_var)),
|
||||
Box::new(a_type),
|
||||
)],
|
||||
)],
|
||||
Box::new(Type::EmptyTagUnion),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn empty_record_pattern(var_store: &mut VarStore) -> Pattern {
|
||||
Pattern::RecordDestructure {
|
||||
whole_var: var_store.fresh(),
|
||||
ext_var: var_store.fresh(),
|
||||
destructs: vec![],
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -11,4 +11,5 @@
|
||||
// re-enable this when working on performance optimizations than have it block PRs.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
pub mod docs;
|
||||
pub mod effect_module;
|
||||
pub mod file;
|
||||
|
@ -1,4 +1,6 @@
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
#[macro_use]
|
||||
extern crate pretty_assertions;
|
||||
#[macro_use]
|
||||
extern crate maplit;
|
||||
@ -28,6 +30,87 @@ mod test_load {
|
||||
|
||||
// HELPERS
|
||||
|
||||
fn multiple_modules(files: Vec<(&str, &str)>) -> LoadedModule {
|
||||
multiple_modules_help(files).unwrap()
|
||||
}
|
||||
|
||||
fn multiple_modules_help(mut files: Vec<(&str, &str)>) -> Result<LoadedModule, std::io::Error> {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use tempfile::tempdir;
|
||||
|
||||
let arena = Bump::new();
|
||||
let arena = &arena;
|
||||
|
||||
let stdlib = roc_builtins::std::standard_stdlib();
|
||||
|
||||
let mut file_handles: Vec<_> = Vec::new();
|
||||
let exposed_types = MutMap::default();
|
||||
let loaded = {
|
||||
// create a temporary directory
|
||||
let dir = tempdir()?;
|
||||
|
||||
let app_module = files.pop().unwrap();
|
||||
let interfaces = files;
|
||||
|
||||
debug_assert!(
|
||||
app_module.1.starts_with("app"),
|
||||
"The final module should be the application module"
|
||||
);
|
||||
|
||||
for (name, source) in interfaces {
|
||||
let mut filename = PathBuf::from(name);
|
||||
filename.set_extension("roc");
|
||||
let file_path = dir.path().join(filename.clone());
|
||||
let mut file = File::create(file_path)?;
|
||||
writeln!(file, "{}", source)?;
|
||||
file_handles.push(file);
|
||||
}
|
||||
|
||||
let result = {
|
||||
let (name, source) = app_module;
|
||||
|
||||
let filename = PathBuf::from(name);
|
||||
let file_path = dir.path().join(filename.clone());
|
||||
let full_file_path = PathBuf::from(file_path.clone());
|
||||
let mut file = File::create(file_path)?;
|
||||
writeln!(file, "{}", source)?;
|
||||
file_handles.push(file);
|
||||
|
||||
roc_load::file::load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
stdlib,
|
||||
dir.path(),
|
||||
exposed_types,
|
||||
)
|
||||
};
|
||||
|
||||
dir.close()?;
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
let mut loaded_module = loaded.expect("failed to load module");
|
||||
|
||||
let home = loaded_module.module_id;
|
||||
|
||||
assert_eq!(
|
||||
loaded_module.can_problems.remove(&home).unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
assert_eq!(
|
||||
loaded_module
|
||||
.type_problems
|
||||
.remove(&home)
|
||||
.unwrap_or_default(),
|
||||
Vec::new()
|
||||
);
|
||||
|
||||
Ok(loaded_module)
|
||||
}
|
||||
|
||||
fn load_fixture(
|
||||
dir_name: &str,
|
||||
module_name: &str,
|
||||
@ -146,6 +229,46 @@ mod test_load {
|
||||
|
||||
// TESTS
|
||||
|
||||
#[test]
|
||||
fn import_transitive_alias() {
|
||||
// this had a bug where NodeColor was HostExposed, and it's `actual_var` conflicted
|
||||
// with variables in the importee
|
||||
let modules = vec![
|
||||
(
|
||||
"RBTree",
|
||||
indoc!(
|
||||
r#"
|
||||
interface RBTree exposes [ Dict, empty ] imports []
|
||||
|
||||
# The color of a node. Leaves are considered Black.
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
# Create an empty dictionary.
|
||||
empty : Dict k v
|
||||
empty =
|
||||
Empty
|
||||
"#
|
||||
),
|
||||
),
|
||||
(
|
||||
"Main",
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports [ RBTree ]
|
||||
|
||||
empty : RBTree.Dict Int Int
|
||||
empty = RBTree.empty
|
||||
|
||||
main = empty
|
||||
"#
|
||||
),
|
||||
),
|
||||
];
|
||||
multiple_modules(modules);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_with_deps() {
|
||||
let subs_by_module = MutMap::default();
|
||||
|
@ -2173,6 +2173,17 @@ pub fn with_hole<'a>(
|
||||
if let roc_can::expr::Expr::Var(original) = def.loc_expr.value {
|
||||
substitute_in_exprs(env.arena, &mut stmt, symbol, original);
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
stmt = reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(def.expr_var),
|
||||
original,
|
||||
stmt,
|
||||
original,
|
||||
);
|
||||
|
||||
stmt
|
||||
} else {
|
||||
with_hole(
|
||||
@ -3576,6 +3587,17 @@ pub fn from_can<'a>(
|
||||
// a variable is aliased
|
||||
substitute_in_exprs(env.arena, &mut rest, *symbol, original);
|
||||
|
||||
// if the substituted variable is a function, make sure we specialize it
|
||||
rest = reuse_function_symbol(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
Some(def.expr_var),
|
||||
original,
|
||||
rest,
|
||||
original,
|
||||
);
|
||||
|
||||
return rest;
|
||||
}
|
||||
roc_can::expr::Expr::LetNonRec(nested_def, nested_cont, nested_annotation) => {
|
||||
|
@ -1002,7 +1002,7 @@ fn parse_closure_param<'a>(
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>> {
|
||||
one_of!(
|
||||
// An ident is the most common param, e.g. \foo -> ...
|
||||
loc_ident_pattern(min_indent),
|
||||
loc_ident_pattern(min_indent, true),
|
||||
// Underscore is also common, e.g. \_ -> ...
|
||||
loc!(underscore_pattern()),
|
||||
// You can destructure records in params, e.g. \{ x, y } -> ...
|
||||
@ -1026,7 +1026,7 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
one_of!(
|
||||
loc_parenthetical_pattern(min_indent),
|
||||
loc!(underscore_pattern()),
|
||||
loc_ident_pattern(min_indent),
|
||||
loc_ident_pattern(min_indent, true),
|
||||
loc!(record_destructure(min_indent)),
|
||||
loc!(string_pattern()),
|
||||
loc!(number_pattern())
|
||||
@ -1034,6 +1034,38 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Pattern<'a>>>> {
|
||||
zero_or_more!(space1_before(loc_tag_pattern_arg(min_indent), min_indent))
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
skip_first!(
|
||||
// If this is a reserved keyword ("if", "then", "case, "when"), then
|
||||
// it is not a function argument!
|
||||
not(reserved_keyword()),
|
||||
// Don't parse operators, because they have a higher precedence than function application.
|
||||
// If we encounter one, we're done parsing function args!
|
||||
move |arena, state| loc_parse_tag_pattern_arg(min_indent, arena, state)
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_parse_tag_pattern_arg<'a>(
|
||||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>> {
|
||||
one_of!(
|
||||
loc_parenthetical_pattern(min_indent),
|
||||
loc!(underscore_pattern()),
|
||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||
loc_ident_pattern(min_indent, false),
|
||||
loc!(record_destructure(min_indent)),
|
||||
loc!(string_pattern()),
|
||||
loc!(number_pattern())
|
||||
)
|
||||
.parse(arena, state)
|
||||
}
|
||||
|
||||
fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
between!(
|
||||
ascii_char(b'('),
|
||||
@ -1133,51 +1165,64 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||
)
|
||||
}
|
||||
|
||||
fn loc_ident_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
fn loc_ident_pattern<'a>(
|
||||
min_indent: u16,
|
||||
can_have_arguments: bool,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
let (loc_ident, state) = loc!(ident()).parse(arena, state)?;
|
||||
|
||||
match loc_ident.value {
|
||||
Ident::GlobalTag(tag) => {
|
||||
let (loc_args, state) =
|
||||
zero_or_more!(space1_before(loc_pattern(min_indent), min_indent))
|
||||
.parse(arena, state)?;
|
||||
let loc_tag = Located {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::GlobalTag(tag),
|
||||
};
|
||||
|
||||
if loc_args.is_empty() {
|
||||
Ok((loc_tag, state))
|
||||
} else {
|
||||
let region = Region::across_all(
|
||||
std::iter::once(&loc_ident.region)
|
||||
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
||||
);
|
||||
let value = Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||
if can_have_arguments {
|
||||
let (loc_args, state) = loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
||||
|
||||
Ok((Located { region, value }, state))
|
||||
if loc_args.is_empty() {
|
||||
Ok((loc_tag, state))
|
||||
} else {
|
||||
let region = Region::across_all(
|
||||
std::iter::once(&loc_ident.region)
|
||||
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
||||
);
|
||||
let value =
|
||||
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||
|
||||
Ok((Located { region, value }, state))
|
||||
}
|
||||
} else {
|
||||
Ok((loc_tag, state))
|
||||
}
|
||||
}
|
||||
Ident::PrivateTag(tag) => {
|
||||
let (loc_args, state) =
|
||||
zero_or_more!(space1_before(loc_pattern(min_indent), min_indent))
|
||||
.parse(arena, state)?;
|
||||
let loc_tag = Located {
|
||||
region: loc_ident.region,
|
||||
value: Pattern::PrivateTag(tag),
|
||||
};
|
||||
|
||||
if loc_args.is_empty() {
|
||||
Ok((loc_tag, state))
|
||||
} else {
|
||||
let region = Region::across_all(
|
||||
std::iter::once(&loc_ident.region)
|
||||
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
||||
);
|
||||
let value = Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
|
||||
if can_have_arguments {
|
||||
let (loc_args, state) = loc_tag_pattern_args(min_indent).parse(arena, state)?;
|
||||
|
||||
Ok((Located { region, value }, state))
|
||||
if loc_args.is_empty() {
|
||||
Ok((loc_tag, state))
|
||||
} else {
|
||||
let region = Region::across_all(
|
||||
std::iter::once(&loc_ident.region)
|
||||
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
|
||||
);
|
||||
let value =
|
||||
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
|
||||
|
||||
Ok((Located { region, value }, state))
|
||||
}
|
||||
} else {
|
||||
Ok((loc_tag, state))
|
||||
}
|
||||
}
|
||||
Ident::Access { module_name, parts } => {
|
||||
|
@ -40,21 +40,25 @@ pub fn can_problem<'b>(
|
||||
Problem::UnusedArgument(closure_symbol, argument_symbol, region) => {
|
||||
let line = "\". Adding an underscore at the start of a variable name is a way of saying that the variable is not used.";
|
||||
|
||||
alloc.concat(vec![
|
||||
alloc.symbol_unqualified(closure_symbol),
|
||||
alloc.reflow(" doesn't use "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow("."),
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.symbol_unqualified(closure_symbol),
|
||||
alloc.reflow(" doesn't use "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.text("."),
|
||||
]),
|
||||
alloc.region(region),
|
||||
alloc.reflow("If you don't need "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(", then you can just remove it. However, if you really do need "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(" as an argument of "),
|
||||
alloc.symbol_unqualified(closure_symbol),
|
||||
alloc.reflow(", prefix it with an underscore, like this: \"_"),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(line),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("If you don't need "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(", then you can just remove it. However, if you really do need "),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(" as an argument of "),
|
||||
alloc.symbol_unqualified(closure_symbol),
|
||||
alloc.reflow(", prefix it with an underscore, like this: \"_"),
|
||||
alloc.symbol_unqualified(argument_symbol),
|
||||
alloc.reflow(line),
|
||||
])
|
||||
])
|
||||
}
|
||||
Problem::PrecedenceProblem(BothNonAssociative(region, left_bin_op, right_bin_op)) => alloc
|
||||
|
@ -972,7 +972,7 @@ fn add_category<'b>(
|
||||
this_is,
|
||||
alloc.text(" an uniqueness attribute of type:"),
|
||||
]),
|
||||
Storage => alloc.concat(vec![this_is, alloc.text(" a value of type:")]),
|
||||
Storage(_file, _line) => alloc.concat(vec![this_is, alloc.text(" a value of type:")]),
|
||||
|
||||
DefaultValue(_) => alloc.concat(vec![this_is, alloc.text(" a default field of type:")]),
|
||||
}
|
||||
|
@ -3880,4 +3880,54 @@ mod test_reporting {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_using_alias() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
# The color of a node. Leaves are considered Black.
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
# Create an empty dictionary.
|
||||
empty : Dict k v
|
||||
empty =
|
||||
Empty
|
||||
|
||||
empty
|
||||
"#
|
||||
),
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_argument() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \foo -> 1
|
||||
|
||||
f
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||
|
||||
`f` doesn't use `foo`.
|
||||
|
||||
1│ f = \foo -> 1
|
||||
^^^
|
||||
|
||||
If you don't need `foo`, then you can just remove it. However, if you
|
||||
really do need `foo` as an argument of `f`, prefix it with an underscore,
|
||||
like this: "_`foo`". Adding an underscore at the start of a variable
|
||||
name is a way of saying that the variable is not used.
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -154,9 +154,6 @@ pub fn run(
|
||||
constraint,
|
||||
);
|
||||
|
||||
//dbg!(&subs, &state.env.vars_by_symbol);
|
||||
//panic!();
|
||||
|
||||
(Solved(subs), state.env)
|
||||
}
|
||||
|
||||
@ -1139,29 +1136,47 @@ fn adjust_rank(
|
||||
group_rank: Rank,
|
||||
var: Variable,
|
||||
) -> Rank {
|
||||
let mut desc = subs.get(var);
|
||||
let mark = desc.mark;
|
||||
let desc = subs.get(var);
|
||||
|
||||
if mark == young_mark {
|
||||
desc.mark = visit_mark;
|
||||
|
||||
let content = desc.content.clone();
|
||||
let mut marked_desc = desc.clone();
|
||||
if desc.mark == young_mark {
|
||||
let Descriptor {
|
||||
content,
|
||||
rank,
|
||||
mark: _,
|
||||
copy,
|
||||
} = desc;
|
||||
|
||||
// Mark the variable as visited before adjusting content, as it may be cyclic.
|
||||
subs.set(var, desc);
|
||||
subs.set(
|
||||
var,
|
||||
Descriptor {
|
||||
content: content.clone(),
|
||||
rank,
|
||||
mark: visit_mark,
|
||||
copy,
|
||||
},
|
||||
);
|
||||
|
||||
let max_rank = adjust_rank_content(subs, young_mark, visit_mark, group_rank, content);
|
||||
marked_desc.rank = max_rank;
|
||||
let max_rank =
|
||||
adjust_rank_content(subs, young_mark, visit_mark, group_rank, content.clone());
|
||||
|
||||
debug_assert_eq!(marked_desc.mark, visit_mark);
|
||||
|
||||
subs.set(var, marked_desc);
|
||||
subs.set(
|
||||
var,
|
||||
Descriptor {
|
||||
content,
|
||||
rank: max_rank,
|
||||
mark: visit_mark,
|
||||
copy,
|
||||
},
|
||||
);
|
||||
|
||||
max_rank
|
||||
} else if mark == visit_mark {
|
||||
} else if desc.mark == visit_mark {
|
||||
// nothing changes
|
||||
desc.rank
|
||||
} else {
|
||||
let mut desc = desc;
|
||||
|
||||
let min_rank = group_rank.min(desc.rank);
|
||||
|
||||
// TODO from elm-compiler: how can min_rank ever be group_rank?
|
||||
|
@ -2184,7 +2184,7 @@ mod solve_expr {
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
map =
|
||||
map =
|
||||
\peano ->
|
||||
when peano is
|
||||
Z -> Z
|
||||
@ -2919,7 +2919,7 @@ mod solve_expr {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
List.walkRight
|
||||
List.walkRight
|
||||
"#
|
||||
),
|
||||
"List a, (a, b -> b), b -> b",
|
||||
@ -2932,7 +2932,7 @@ mod solve_expr {
|
||||
indoc!(
|
||||
r#"
|
||||
empty : List Int
|
||||
empty =
|
||||
empty =
|
||||
[]
|
||||
|
||||
List.walkRight empty (\a, b -> a + b) 0
|
||||
@ -2980,4 +2980,130 @@ mod solve_expr {
|
||||
"List x",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_tag_application() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
|
||||
main =
|
||||
if 1 == 1 then
|
||||
Foo (Bar) 1
|
||||
else
|
||||
Foo Bar 1
|
||||
"#
|
||||
),
|
||||
"[ Foo [ Bar ]* (Num *) ]*",
|
||||
);
|
||||
|
||||
infer_eq_without_problem("Foo Bar 1", "[ Foo [ Bar ]* (Num *) ]*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_tag_application_pattern_global() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Bar : [ Bar ]
|
||||
Foo : [ Foo Bar Int, Empty ]
|
||||
|
||||
foo : Foo
|
||||
foo = Foo Bar 1
|
||||
|
||||
main =
|
||||
when foo is
|
||||
Foo Bar 1 ->
|
||||
Foo Bar 2
|
||||
|
||||
x ->
|
||||
x
|
||||
"#
|
||||
),
|
||||
"[ Empty, Foo [ Bar ] Int ]",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_tag_application_pattern_private() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Foo : [ @Foo [ @Bar ] Int, @Empty ]
|
||||
|
||||
foo : Foo
|
||||
foo = @Foo @Bar 1
|
||||
|
||||
main =
|
||||
when foo is
|
||||
@Foo @Bar 1 ->
|
||||
@Foo @Bar 2
|
||||
|
||||
x ->
|
||||
x
|
||||
"#
|
||||
),
|
||||
"[ @Empty, @Foo [ @Bar ] Int ]",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recursive_functon_with_rigid() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
State a : { count : Int, x : a }
|
||||
|
||||
foo : State a -> Int
|
||||
foo = \state ->
|
||||
if state.count == 0 then
|
||||
0
|
||||
else
|
||||
1 + foo { count: state.count - 1, x: state.x }
|
||||
|
||||
main : Int
|
||||
main =
|
||||
foo { count: 3, x: {} }
|
||||
"#
|
||||
),
|
||||
"Int",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rbtree_empty() {
|
||||
infer_eq_without_problem(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
# The color of a node. Leaves are considered Black.
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
# Create an empty dictionary.
|
||||
empty : Dict k v
|
||||
empty =
|
||||
Empty
|
||||
|
||||
foo : Dict Int Int
|
||||
foo = empty
|
||||
|
||||
main : Dict Int Int
|
||||
main =
|
||||
foo
|
||||
"#
|
||||
),
|
||||
"Dict Int Int",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1007,7 +1007,7 @@ pub enum Category {
|
||||
StrInterpolation,
|
||||
|
||||
// storing variables in the ast
|
||||
Storage,
|
||||
Storage(&'static str, u32),
|
||||
|
||||
// control flow
|
||||
If,
|
||||
|
@ -4,16 +4,16 @@ use std::alloc::Layout;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "makeClosure_1_exposed"]
|
||||
#[link_name = "Main_makeClosure_1_exposed"]
|
||||
fn make_closure(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "makeClosure_1_size"]
|
||||
#[link_name = "Main_makeClosure_1_size"]
|
||||
fn closure_size() -> i64;
|
||||
|
||||
#[link_name = "makeClosure_1_MyClosure_caller"]
|
||||
#[link_name = "Main_makeClosure_1_MyClosure_caller"]
|
||||
fn call_MyClosure(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "makeClosure_1_MyClosure_size"]
|
||||
#[link_name = "Main_makeClosure_1_MyClosure_size"]
|
||||
fn size_MyClosure() -> i64;
|
||||
}
|
||||
|
||||
|
34
examples/effect/ConsList.roc
Normal file
34
examples/effect/ConsList.roc
Normal file
@ -0,0 +1,34 @@
|
||||
interface ConsList exposes [ConsList, empty, isEmpty, map, len] imports []
|
||||
|
||||
ConsList a : [ Cons a (ConsList a), Nil ]
|
||||
|
||||
empty : ConsList a
|
||||
empty = Nil
|
||||
|
||||
len : ConsList a -> Int
|
||||
len = \list ->
|
||||
when list is
|
||||
Cons _ rest -> 1 + len rest
|
||||
|
||||
Nil ->
|
||||
0
|
||||
|
||||
|
||||
map : ConsList a, (a -> b) -> ConsList b
|
||||
map = \list, f ->
|
||||
when list is
|
||||
Cons x xs ->
|
||||
Cons (f x) (map xs f)
|
||||
|
||||
Nil ->
|
||||
Nil
|
||||
|
||||
|
||||
isEmpty : ConsList a -> Bool
|
||||
isEmpty = \list ->
|
||||
when list is
|
||||
Cons _ _ ->
|
||||
False
|
||||
|
||||
Nil ->
|
||||
True
|
@ -1,7 +1,21 @@
|
||||
app Main provides [ main ] imports [ Effect ]
|
||||
app Main provides [ main ] imports [ Effect, RBTree ]
|
||||
|
||||
foo : RBTree.Dict Int Int
|
||||
foo = Empty # RBTree.empty
|
||||
|
||||
main : Effect.Effect {} as Fx
|
||||
main =
|
||||
Effect.putLine "Write a thing!"
|
||||
|> Effect.after (\{} -> Effect.getLine)
|
||||
|> Effect.after (\line -> Effect.putLine line)
|
||||
# if RBTree.isEmpty empty then
|
||||
if RBTree.size foo == 0 then
|
||||
Effect.putLine "Yay"
|
||||
|> Effect.after (\{} -> Effect.getLine)
|
||||
|> Effect.after (\line -> Effect.putLine line)
|
||||
else
|
||||
Effect.putLine "Nay"
|
||||
|
||||
|
||||
# Effect.always "Write a thing"
|
||||
# |> Effect.map (\line -> Str.concat line "!")
|
||||
# |> Effect.after (\line -> Effect.putLine line)
|
||||
# |> Effect.after (\{} -> Effect.getLine)
|
||||
# |> Effect.after (\line -> Effect.putLine line)
|
||||
|
307
examples/effect/RBTree.roc
Normal file
307
examples/effect/RBTree.roc
Normal file
@ -0,0 +1,307 @@
|
||||
interface RBTree exposes [ Dict, empty, size, singleton ] imports []
|
||||
|
||||
# The color of a node. Leaves are considered Black.
|
||||
NodeColor : [ Red, Black ]
|
||||
|
||||
Dict k v : [ Node NodeColor k v (Dict k v) (Dict k v), Empty ]
|
||||
|
||||
Key k : Num k
|
||||
|
||||
Maybe a : [ Just a, Nothing ]
|
||||
|
||||
# Create an empty dictionary.
|
||||
empty : Dict k v
|
||||
empty =
|
||||
Empty
|
||||
|
||||
# Create a dictionary with one key-value pair.
|
||||
singleton : Key k, v -> Dict (Key k) v
|
||||
singleton = \key, value ->
|
||||
# Root node is always Black
|
||||
Node Black key value Empty Empty
|
||||
|
||||
# {-| Determine the number of key-value pairs in the dictionary. -}
|
||||
size : Dict k v -> Int
|
||||
size = \dict ->
|
||||
sizeHelp 0 dict
|
||||
|
||||
sizeHelp : Int, Dict k v -> Int
|
||||
sizeHelp = \n, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
n
|
||||
|
||||
Node _ _ _ left right ->
|
||||
sizeHelp (sizeHelp (n+1) right) left
|
||||
|
||||
isEmpty : Dict k v -> Bool
|
||||
isEmpty = \dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
True
|
||||
|
||||
Node _ _ _ _ _ ->
|
||||
False
|
||||
|
||||
insert : Key k, v, Dict (Key k) v -> Dict (Key k) v
|
||||
insert = \key, value, dict ->
|
||||
when insertHelp key value dict is
|
||||
Node Red k v l r ->
|
||||
Node Black k v l r
|
||||
|
||||
x ->
|
||||
x
|
||||
|
||||
insertHelp : (Key k), v, Dict (Key k) v -> Dict (Key k) v
|
||||
insertHelp = \key, value, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
# New nodes are always red. If it violates the rules, it will be fixed
|
||||
# when balancing.
|
||||
Node Red key value Empty Empty
|
||||
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
when Num.compare key nKey is
|
||||
LT ->
|
||||
balance nColor nKey nValue (insertHelp key value nLeft) nRight
|
||||
|
||||
EQ ->
|
||||
Node nColor nKey value nLeft nRight
|
||||
|
||||
GT ->
|
||||
balance nColor nKey nValue nLeft (insertHelp key value nRight)
|
||||
|
||||
balance : NodeColor, k, v, Dict k v, Dict k v -> Dict k v
|
||||
balance = \color, key, value, left, right ->
|
||||
when right is
|
||||
Node Red rK rV rLeft rRight ->
|
||||
when left is
|
||||
Node Red lK lV lLeft lRight ->
|
||||
Node
|
||||
Red
|
||||
key
|
||||
value
|
||||
(Node Black lK lV lLeft lRight)
|
||||
(Node Black rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
Node color rK rV (Node Red key value left rLeft) rRight
|
||||
|
||||
_ ->
|
||||
when left is
|
||||
Node Red lK lV (Node Red llK llV llLeft llRight) lRight ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
lV
|
||||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black key value lRight right)
|
||||
|
||||
_ ->
|
||||
Node color key value left right
|
||||
|
||||
|
||||
remove : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
remove = \key, dict ->
|
||||
# Root node is always Black
|
||||
when removeHelp key dict is
|
||||
Node Red k v l r ->
|
||||
Node Black k v l r
|
||||
|
||||
x ->
|
||||
x
|
||||
|
||||
|
||||
|
||||
# The easiest thing to remove from the tree, is a red node. However, when searching for the
|
||||
# node to remove, we have no way of knowing if it will be red or not. This remove implementation
|
||||
# makes sure that the bottom node is red by moving red colors down the tree through rotation
|
||||
# and color flips. Any violations this will cause, can easily be fixed by balancing on the way
|
||||
# up again.
|
||||
removeHelp : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelp = \targetKey, dict ->
|
||||
when dict is
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
Node color key value left right ->
|
||||
if targetKey < key then
|
||||
when left is
|
||||
Node Black _ _ lLeft _ ->
|
||||
when lLeft is
|
||||
Node Red _ _ _ _ ->
|
||||
Node color key value (removeHelp targetKey left) right
|
||||
|
||||
_ ->
|
||||
when moveRedLeft dict is
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
balance nColor nKey nValue (removeHelp targetKey nLeft) nRight
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
_ ->
|
||||
Node color key value (removeHelp targetKey left) right
|
||||
else
|
||||
removeHelpEQGT targetKey (removeHelpPrepEQGT targetKey dict color key value left right)
|
||||
|
||||
|
||||
|
||||
removeHelpPrepEQGT : Key k, Dict (Key k) v, NodeColor, (Key k), v, Dict (Key k) v, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelpPrepEQGT = \_, dict, color, key, value, left, right ->
|
||||
when left is
|
||||
Node Red lK lV lLeft lRight ->
|
||||
Node
|
||||
color
|
||||
lK
|
||||
lV
|
||||
lLeft
|
||||
(Node Red key value lRight right)
|
||||
|
||||
_ ->
|
||||
when right is
|
||||
Node Black _ _ (Node Black _ _ _ _) _ ->
|
||||
moveRedRight dict
|
||||
|
||||
Node Black _ _ Empty _ ->
|
||||
moveRedRight dict
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
|
||||
|
||||
# When we find the node we are looking for, we can remove by replacing the key-value
|
||||
# pair with the key-value pair of the left-most node on the right side (the closest pair).
|
||||
removeHelpEQGT : Key k, Dict (Key k) v -> Dict (Key k) v
|
||||
removeHelpEQGT = \targetKey, dict ->
|
||||
when dict is
|
||||
Node color key value left right ->
|
||||
if targetKey == key then
|
||||
when getMin right is
|
||||
Node _ minKey minValue _ _ ->
|
||||
balance color minKey minValue left (removeMin right)
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
else
|
||||
balance color key value left (removeHelp targetKey right)
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
|
||||
|
||||
getMin : Dict k v -> Dict k v
|
||||
getMin = \dict ->
|
||||
when dict is
|
||||
# Node _ _ _ ((Node _ _ _ _ _) as left) _ ->
|
||||
Node _ _ _ left _ ->
|
||||
when left is
|
||||
Node _ _ _ _ _ -> getMin left
|
||||
_ -> dict
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
moveRedLeft : Dict k v -> Dict k v
|
||||
moveRedLeft = \dict ->
|
||||
when dict is
|
||||
# Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV ((Node Red rlK rlV rlL rlR) as rLeft) rRight) ->
|
||||
Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
when rList is
|
||||
Node Red rlK rlV rlL rlR ->
|
||||
Node
|
||||
Red
|
||||
rlK
|
||||
rlV
|
||||
(Node Black k v (Node Red lK lV lLeft lRight) rlL)
|
||||
(Node Black rK rV rlR rRight)
|
||||
|
||||
_ ->
|
||||
when clr is
|
||||
Black ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
Red ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
moveRedRight : Dict k v -> Dict k v
|
||||
moveRedRight = \dict ->
|
||||
when dict is
|
||||
Node clr k v (Node lClr lK lV (Node Red llK llV llLeft llRight) lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
Node
|
||||
Red
|
||||
lK
|
||||
lV
|
||||
(Node Black llK llV llLeft llRight)
|
||||
(Node Black k v lRight (Node Red rK rV rLeft rRight))
|
||||
|
||||
Node clr k v (Node lClr lK lV lLeft lRight) (Node rClr rK rV rLeft rRight) ->
|
||||
when clr is
|
||||
Black ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
Red ->
|
||||
Node
|
||||
Black
|
||||
k
|
||||
v
|
||||
(Node Red lK lV lLeft lRight)
|
||||
(Node Red rK rV rLeft rRight)
|
||||
|
||||
_ ->
|
||||
dict
|
||||
|
||||
removeMin : Dict k v -> Dict k v
|
||||
removeMin = \dict ->
|
||||
when dict is
|
||||
Node color key value ((Node lColor _ _ lLeft _) as left) right ->
|
||||
when lColor is
|
||||
Black ->
|
||||
when lLeft is
|
||||
Node Red _ _ _ _ ->
|
||||
Node color key value (removeMin left) right
|
||||
|
||||
_ ->
|
||||
when moveRedLeft dict is
|
||||
Node nColor nKey nValue nLeft nRight ->
|
||||
balance nColor nKey nValue (removeMin nLeft) nRight
|
||||
|
||||
Empty ->
|
||||
Empty
|
||||
|
||||
_ ->
|
||||
Node color key value (removeMin left) right
|
||||
|
||||
_ ->
|
||||
Empty
|
||||
|
||||
|
||||
# Update the value of a dictionary for a specific key with a given function.
|
||||
update : Key k, (Maybe v, Maybe v), Dict (Key k) v -> Dict (Key k) v
|
||||
update = \targetKey, alter, dictionary ->
|
||||
when alter (get targetKey dictionary) is
|
||||
Just value ->
|
||||
insert targetKey value dictionary
|
||||
|
||||
Nothing ->
|
||||
remove targetKey dictionary
|
@ -7,16 +7,16 @@ use std::alloc::Layout;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "main_1_exposed"]
|
||||
#[link_name = "Main_main_1_exposed"]
|
||||
fn roc_main(output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "main_1_size"]
|
||||
#[link_name = "Main_main_1_size"]
|
||||
fn roc_main_size() -> i64;
|
||||
|
||||
#[link_name = "main_1_Fx_caller"]
|
||||
#[link_name = "Main_main_1_Fx_caller"]
|
||||
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||
|
||||
#[link_name = "main_1_Fx_size"]
|
||||
#[link_name = "Main_main_1_Fx_size"]
|
||||
fn size_Fx() -> i64;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use roc_std::RocStr;
|
||||
use std::str;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "main_1_exposed"]
|
||||
#[link_name = "Hello_main_1_exposed"]
|
||||
fn say_hello(output: &mut RocCallResult<RocStr>) -> ();
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1_exposed"]
|
||||
#[link_name = "Quicksort_quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1_exposed"]
|
||||
#[link_name = "Quicksort_quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ use roc_std::RocList;
|
||||
use std::time::SystemTime;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "quicksort_1_exposed"]
|
||||
#[link_name = "Main_quicksort_1_exposed"]
|
||||
fn quicksort(list: RocList<i64>, output: &mut RocCallResult<RocList<i64>>) -> ();
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,14 @@ extern "C" {
|
||||
|
||||
const REFCOUNT_1: usize = isize::MIN as usize;
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum RocOrder {
|
||||
Eq = 0,
|
||||
Gt = 1,
|
||||
Lt = 2,
|
||||
}
|
||||
|
||||
//#[macro_export]
|
||||
//macro_rules! roclist {
|
||||
// () => (
|
||||
|
68
shell.nix
68
shell.nix
@ -13,6 +13,8 @@ with {
|
||||
isMacOS = builtins.currentSystem == "x86_64-darwin";
|
||||
};
|
||||
|
||||
with (pkgs);
|
||||
|
||||
let
|
||||
darwin-frameworks =
|
||||
if isMacOS then
|
||||
@ -27,43 +29,63 @@ let
|
||||
]
|
||||
else
|
||||
[ ];
|
||||
llvm = pkgs.llvm_10;
|
||||
lld = pkgs.lld_10; # this should match llvm's version
|
||||
clang = pkgs.clang_10; # this should match llvm's version
|
||||
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
|
||||
pkgs.rustc
|
||||
pkgs.cargo
|
||||
pkgs.clippy
|
||||
pkgs.rustfmt
|
||||
pkgs.cmake
|
||||
pkgs.git
|
||||
pkgs.python3
|
||||
llvm
|
||||
clang
|
||||
pkgs.valgrind
|
||||
pkgs.pkg-config
|
||||
rustc
|
||||
cargo
|
||||
clippy
|
||||
rustfmt
|
||||
cmake
|
||||
git
|
||||
python3
|
||||
llvmPkg
|
||||
clangPkg
|
||||
valgrind
|
||||
pkg-config
|
||||
zig
|
||||
# llb deps
|
||||
pkgs.libffi
|
||||
pkgs.libxml2
|
||||
pkgs.xorg.libX11
|
||||
pkgs.zlib
|
||||
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
|
||||
lld
|
||||
lldPkg
|
||||
# dev tools
|
||||
pkgs.rust-analyzer
|
||||
rust-analyzer
|
||||
# (import ./nix/zls.nix { inherit pkgs zig; })
|
||||
pkgs.ccls
|
||||
ccls
|
||||
];
|
||||
in pkgs.mkShell {
|
||||
in mkShell {
|
||||
buildInputs = inputs ++ darwin-frameworks;
|
||||
LLVM_SYS_100_PREFIX = "${llvm}";
|
||||
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
|
||||
];
|
||||
|
||||
# Aliases don't work cross shell, so we do this
|
||||
shellHook = ''
|
||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$APPEND_LIBRARY_PATH"
|
||||
export PATH="$PATH:$PWD/nix/bin"
|
||||
'';
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user