mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge branch 'trunk' into builtin-count-graphemes
This commit is contained in:
commit
74b09605a7
35 changed files with 1640 additions and 251 deletions
|
@ -162,15 +162,20 @@ mod cli_run {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// #[serial(effect)]
|
||||||
|
// fn run_effect_unoptimized() {
|
||||||
|
// check_output(
|
||||||
|
// &example_file("effect", "Main.roc"),
|
||||||
|
// &[],
|
||||||
|
// "I am Dep2.str2\n",
|
||||||
|
// true,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial(multi_dep_str)]
|
#[serial(multi_dep_str)]
|
||||||
fn run_multi_dep_str_unoptimized() {
|
fn run_multi_dep_str_unoptimized() {
|
||||||
// if true {
|
|
||||||
// todo!(
|
|
||||||
// "fix this test so it no longer deadlocks and hangs during monomorphization! The test never shows the error; to see the panic error, run this: cargo run run cli/tests/fixtures/multi-dep-str/Main.roc"
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
|
|
||||||
check_output(
|
check_output(
|
||||||
&fixture_file("multi-dep-str", "Main.roc"),
|
&fixture_file("multi-dep-str", "Main.roc"),
|
||||||
&[],
|
&[],
|
||||||
|
@ -184,7 +189,7 @@ mod cli_run {
|
||||||
fn run_multi_dep_str_optimized() {
|
fn run_multi_dep_str_optimized() {
|
||||||
check_output(
|
check_output(
|
||||||
&fixture_file("multi-dep-str", "Main.roc"),
|
&fixture_file("multi-dep-str", "Main.roc"),
|
||||||
&[],
|
&["--optimize"],
|
||||||
"I am Dep2.str2\n",
|
"I am Dep2.str2\n",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
@ -202,11 +207,11 @@ mod cli_run {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[serial(multi_dep_str)]
|
#[serial(multi_dep_thunk)]
|
||||||
fn run_multi_dep_thunk_optimized() {
|
fn run_multi_dep_thunk_optimized() {
|
||||||
check_output(
|
check_output(
|
||||||
&fixture_file("multi-dep-thunk", "Main.roc"),
|
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||||
&[],
|
&["--optimize"],
|
||||||
"I am Dep2.value2\n",
|
"I am Dep2.value2\n",
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::procedure::References;
|
||||||
use crate::scope::Scope;
|
use crate::scope::Scope;
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
use roc_collections::all::{ImSet, MutMap, MutSet, SendMap};
|
use roc_collections::all::{ImSet, MutMap, MutSet, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::operator::CalledVia;
|
use roc_module::operator::CalledVia;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -98,6 +98,11 @@ pub enum Expr {
|
||||||
args: Vec<(Variable, Expr)>,
|
args: Vec<(Variable, Expr)>,
|
||||||
ret_var: Variable,
|
ret_var: Variable,
|
||||||
},
|
},
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol: ForeignSymbol,
|
||||||
|
args: Vec<(Variable, Expr)>,
|
||||||
|
ret_var: Variable,
|
||||||
|
},
|
||||||
|
|
||||||
Closure {
|
Closure {
|
||||||
function_type: Variable,
|
function_type: Variable,
|
||||||
|
@ -1170,7 +1175,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||||
| other @ Accessor { .. }
|
| other @ Accessor { .. }
|
||||||
| other @ Update { .. }
|
| other @ Update { .. }
|
||||||
| other @ Var(_)
|
| other @ Var(_)
|
||||||
| other @ RunLowLevel { .. } => other,
|
| other @ RunLowLevel { .. }
|
||||||
|
| other @ ForeignCall { .. } => other,
|
||||||
|
|
||||||
List {
|
List {
|
||||||
list_var,
|
list_var,
|
||||||
|
|
|
@ -445,7 +445,7 @@ fn fix_values_captured_in_closure_expr(
|
||||||
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
fix_values_captured_in_closure_expr(&mut loc_arg.value, no_capture_symbols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RunLowLevel { args, .. } => {
|
RunLowLevel { args, .. } | ForeignCall { args, .. } => {
|
||||||
for (_, arg) in args.iter_mut() {
|
for (_, arg) in args.iter_mut() {
|
||||||
fix_values_captured_in_closure_expr(arg, no_capture_symbols);
|
fix_values_captured_in_closure_expr(arg, no_capture_symbols);
|
||||||
}
|
}
|
||||||
|
|
|
@ -858,6 +858,52 @@ pub fn constrain_expr(
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ForeignCall {
|
||||||
|
args,
|
||||||
|
ret_var,
|
||||||
|
foreign_symbol,
|
||||||
|
} => {
|
||||||
|
// This is a modified version of what we do for function calls.
|
||||||
|
|
||||||
|
// The operation's return type
|
||||||
|
let ret_type = Variable(*ret_var);
|
||||||
|
|
||||||
|
// This will be used in the occurs check
|
||||||
|
let mut vars = Vec::with_capacity(1 + args.len());
|
||||||
|
|
||||||
|
vars.push(*ret_var);
|
||||||
|
|
||||||
|
let mut arg_types = Vec::with_capacity(args.len());
|
||||||
|
let mut arg_cons = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
let mut add_arg = |index, arg_type: Type, arg| {
|
||||||
|
let reason = Reason::ForeignCallArg {
|
||||||
|
foreign_symbol: foreign_symbol.clone(),
|
||||||
|
arg_index: Index::zero_based(index),
|
||||||
|
};
|
||||||
|
let expected_arg = ForReason(reason, arg_type.clone(), Region::zero());
|
||||||
|
let arg_con = constrain_expr(env, Region::zero(), arg, expected_arg);
|
||||||
|
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
arg_cons.push(arg_con);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (index, (arg_var, arg)) in args.iter().enumerate() {
|
||||||
|
vars.push(*arg_var);
|
||||||
|
|
||||||
|
add_arg(index, Variable(*arg_var), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let category = Category::ForeignCall;
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vars,
|
||||||
|
And(vec![
|
||||||
|
And(arg_cons),
|
||||||
|
Eq(ret_type, expected, category, region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
RuntimeError(_) => {
|
RuntimeError(_) => {
|
||||||
// Runtime Errors have no constraints because they're going to crash.
|
// Runtime Errors have no constraints because they're going to crash.
|
||||||
True
|
True
|
||||||
|
|
|
@ -961,6 +961,60 @@ pub fn constrain_expr(
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol,
|
||||||
|
args,
|
||||||
|
ret_var,
|
||||||
|
} => {
|
||||||
|
// This is a modified version of what we do for function calls.
|
||||||
|
|
||||||
|
let ret_type = Variable(*ret_var);
|
||||||
|
let mut vars = Vec::with_capacity(1 + args.len());
|
||||||
|
|
||||||
|
vars.push(*ret_var);
|
||||||
|
|
||||||
|
// Canonicalize the function expression and its arguments
|
||||||
|
|
||||||
|
let mut arg_types = Vec::with_capacity(args.len());
|
||||||
|
let mut arg_cons = Vec::with_capacity(args.len());
|
||||||
|
|
||||||
|
for (index, (arg_var, arg_expr)) in args.iter().enumerate() {
|
||||||
|
let arg_type = Variable(*arg_var);
|
||||||
|
|
||||||
|
let reason = Reason::ForeignCallArg {
|
||||||
|
foreign_symbol: foreign_symbol.clone(),
|
||||||
|
arg_index: Index::zero_based(index),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expected_arg = Expected::ForReason(reason, arg_type.clone(), region);
|
||||||
|
let arg_con = constrain_expr(
|
||||||
|
env,
|
||||||
|
var_store,
|
||||||
|
var_usage,
|
||||||
|
applied_usage_constraint,
|
||||||
|
Region::zero(),
|
||||||
|
arg_expr,
|
||||||
|
expected_arg,
|
||||||
|
);
|
||||||
|
|
||||||
|
vars.push(*arg_var);
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
arg_cons.push(arg_con);
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected_uniq_type = var_store.fresh();
|
||||||
|
vars.push(expected_uniq_type);
|
||||||
|
|
||||||
|
let category = Category::ForeignCall;
|
||||||
|
|
||||||
|
exists(
|
||||||
|
vars,
|
||||||
|
And(vec![
|
||||||
|
And(arg_cons),
|
||||||
|
Eq(ret_type, expected, category, region),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
LetRec(defs, loc_ret, var) => {
|
LetRec(defs, loc_ret, var) => {
|
||||||
// NOTE doesn't currently unregister bound symbols
|
// NOTE doesn't currently unregister bound symbols
|
||||||
// may be a problem when symbols are not globally unique
|
// may be a problem when symbols are not globally unique
|
||||||
|
|
|
@ -810,6 +810,38 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
Literal(literal) => build_exp_literal(env, literal),
|
Literal(literal) => build_exp_literal(env, literal),
|
||||||
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, layout, *op, symbols),
|
RunLowLevel(op, symbols) => run_low_level(env, scope, parent, layout, *op, symbols),
|
||||||
|
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol,
|
||||||
|
arguments,
|
||||||
|
ret_layout,
|
||||||
|
} => {
|
||||||
|
let mut arg_vals: Vec<BasicValueEnum> =
|
||||||
|
Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
|
|
||||||
|
let mut arg_types = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||||
|
|
||||||
|
for arg in arguments.iter() {
|
||||||
|
let (value, layout) = load_symbol_and_layout(env, scope, arg);
|
||||||
|
arg_vals.push(value);
|
||||||
|
let arg_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, layout, env.ptr_bytes);
|
||||||
|
arg_types.push(arg_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret_type =
|
||||||
|
basic_type_from_layout(env.arena, env.context, ret_layout, env.ptr_bytes);
|
||||||
|
let function_type = get_fn_type(&ret_type, &arg_types);
|
||||||
|
let function = get_foreign_symbol(env, foreign_symbol.clone(), function_type);
|
||||||
|
|
||||||
|
let call = env.builder.build_call(function, arg_vals.as_slice(), "tmp");
|
||||||
|
|
||||||
|
// this is a foreign function, use c calling convention
|
||||||
|
call.set_call_convention(C_CALL_CONV);
|
||||||
|
|
||||||
|
call.try_as_basic_value()
|
||||||
|
.left()
|
||||||
|
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||||
|
}
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
call_type: ByName(name),
|
call_type: ByName(name),
|
||||||
full_layout,
|
full_layout,
|
||||||
|
@ -1116,9 +1148,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
env.arena.alloc(format!("closure_field_access_{}_", index)),
|
env.arena.alloc(format!("closure_field_access_{}_", index)),
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
(other, layout) => {
|
(other, layout) => unreachable!(
|
||||||
unreachable!("can only index into struct layout {:?} {:?}", other, layout)
|
"can only index into struct layout\nValue: {:?}\nLayout: {:?}\nIndex: {:?}",
|
||||||
}
|
other, layout, index
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3418,6 +3451,28 @@ fn cxa_rethrow_exception<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> BasicValu
|
||||||
call.try_as_basic_value().left().unwrap()
|
call.try_as_basic_value().left().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_foreign_symbol<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
foreign_symbol: roc_module::ident::ForeignSymbol,
|
||||||
|
function_type: FunctionType<'ctx>,
|
||||||
|
) -> FunctionValue<'ctx> {
|
||||||
|
let module = env.module;
|
||||||
|
|
||||||
|
match module.get_function(foreign_symbol.as_str()) {
|
||||||
|
Some(gvalue) => gvalue,
|
||||||
|
None => {
|
||||||
|
let foreign_function = module.add_function(
|
||||||
|
foreign_symbol.as_str(),
|
||||||
|
function_type,
|
||||||
|
Some(Linkage::External),
|
||||||
|
);
|
||||||
|
foreign_function.set_call_conventions(C_CALL_CONV);
|
||||||
|
|
||||||
|
foreign_function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> {
|
fn get_gxx_personality_v0<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>) -> FunctionValue<'ctx> {
|
||||||
let name = "__gxx_personality_v0";
|
let name = "__gxx_personality_v0";
|
||||||
|
|
||||||
|
|
|
@ -121,11 +121,6 @@ fn comments_or_new_lines_to_docs<'a>(
|
||||||
docs.push_str(doc_str);
|
docs.push_str(doc_str);
|
||||||
docs.push_str("\n");
|
docs.push_str("\n");
|
||||||
}
|
}
|
||||||
// TODO: Lines with only `##` are not being parsed as a
|
|
||||||
// DocComment, but as a LineComment("#\r"). This pattern should cover this.
|
|
||||||
// The problem is that this is only valid if it is at the start
|
|
||||||
// of a line. False positive example: `x = 2 ##`.
|
|
||||||
LineComment("#\r") => docs.push_str("\n"),
|
|
||||||
Newline | LineComment(_) => {}
|
Newline | LineComment(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,6 +23,10 @@ pub struct Lowercase(InlinableString);
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub struct Uppercase(InlinableString);
|
pub struct Uppercase(InlinableString);
|
||||||
|
|
||||||
|
/// A string representing a foreign (linked-in) symbol
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
|
pub struct ForeignSymbol(InlinableString);
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
pub enum TagName {
|
pub enum TagName {
|
||||||
/// Global tags have no module, but tend to be short strings (since they're
|
/// Global tags have no module, but tend to be short strings (since they're
|
||||||
|
@ -125,6 +129,28 @@ impl<'a> Into<Box<str>> for ModuleName {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ForeignSymbol {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
&*self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_inline_str(&self) -> &InlinableString {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for ForeignSymbol {
|
||||||
|
fn from(string: &'a str) -> Self {
|
||||||
|
Self(string.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<String> for ForeignSymbol {
|
||||||
|
fn from(string: String) -> Self {
|
||||||
|
Self(string.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Uppercase {
|
impl Uppercase {
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
&*self.0
|
&*self.0
|
||||||
|
|
|
@ -374,6 +374,15 @@ impl<'a> BorrowInfState<'a> {
|
||||||
self.own_args_using_bools(args, ps);
|
self.own_args_using_bools(args, ps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForeignCall { arguments, .. } => {
|
||||||
|
// very unsure what demand ForeignCall should place upon its arguments
|
||||||
|
self.own_var(z);
|
||||||
|
|
||||||
|
let ps = foreign_borrow_signature(self.arena, arguments.len());
|
||||||
|
|
||||||
|
self.own_args_using_bools(arguments, ps);
|
||||||
|
}
|
||||||
|
|
||||||
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
|
Literal(_) | FunctionPointer(_, _) | RuntimeErrorFunction(_) => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -492,6 +501,11 @@ impl<'a> BorrowInfState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn foreign_borrow_signature(arena: &Bump, arity: usize) -> &[bool] {
|
||||||
|
let all = bumpalo::vec![in arena; false; arity];
|
||||||
|
all.into_bump_slice()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
use LowLevel::*;
|
use LowLevel::*;
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,9 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||||
RunLowLevel(_, args) => {
|
RunLowLevel(_, args) => {
|
||||||
result.extend(args.iter());
|
result.extend(args.iter());
|
||||||
}
|
}
|
||||||
|
ForeignCall { arguments, .. } => {
|
||||||
|
result.extend(arguments.iter());
|
||||||
|
}
|
||||||
|
|
||||||
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
|
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
|
||||||
}
|
}
|
||||||
|
@ -463,6 +466,13 @@ impl<'a> Context<'a> {
|
||||||
self.arena.alloc(Stmt::Let(z, v, l, b))
|
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForeignCall { arguments, .. } => {
|
||||||
|
let ps = crate::borrow::foreign_borrow_signature(self.arena, arguments.len());
|
||||||
|
let b = self.add_dec_after_lowlevel(arguments, ps, b, b_live_vars);
|
||||||
|
|
||||||
|
self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||||
|
}
|
||||||
|
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
args: ys,
|
args: ys,
|
||||||
arg_layouts,
|
arg_layouts,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::layout::{Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem};
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_collections::all::{default_hasher, MutMap, MutSet};
|
use roc_collections::all::{default_hasher, MutMap, MutSet};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
use roc_module::symbol::{IdentIds, ModuleId, Symbol};
|
||||||
use roc_problem::can::RuntimeError;
|
use roc_problem::can::RuntimeError;
|
||||||
|
@ -444,7 +444,7 @@ impl<'a> Procs<'a> {
|
||||||
// register the pending specialization, so this gets code genned later
|
// register the pending specialization, so this gets code genned later
|
||||||
add_pending(pending_specializations, symbol, layout.clone(), pending);
|
add_pending(pending_specializations, symbol, layout.clone(), pending);
|
||||||
|
|
||||||
debug_assert!(!self.partial_procs.contains_key(&symbol), "Procs was told to insert a value for symbol {:?}, but there was already an entry for that key! Procs should never attempt to insert duplicates.", symbol);
|
debug_assert!(!self.partial_procs.contains_key(&symbol), "Procs was told to insert a value for symbol {:?}, but there was already an entry for that key! The same PartialProc should never be added twice", symbol);
|
||||||
|
|
||||||
self.partial_procs.insert(
|
self.partial_procs.insert(
|
||||||
symbol,
|
symbol,
|
||||||
|
@ -829,6 +829,11 @@ pub enum Expr<'a> {
|
||||||
args: &'a [Symbol],
|
args: &'a [Symbol],
|
||||||
},
|
},
|
||||||
RunLowLevel(LowLevel, &'a [Symbol]),
|
RunLowLevel(LowLevel, &'a [Symbol]),
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol: ForeignSymbol,
|
||||||
|
arguments: &'a [Symbol],
|
||||||
|
ret_layout: Layout<'a>,
|
||||||
|
},
|
||||||
|
|
||||||
Tag {
|
Tag {
|
||||||
tag_layout: Layout<'a>,
|
tag_layout: Layout<'a>,
|
||||||
|
@ -943,6 +948,17 @@ impl<'a> Expr<'a> {
|
||||||
.text(format!("lowlevel {:?} ", lowlevel))
|
.text(format!("lowlevel {:?} ", lowlevel))
|
||||||
.append(alloc.intersperse(it, " "))
|
.append(alloc.intersperse(it, " "))
|
||||||
}
|
}
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol,
|
||||||
|
arguments,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let it = arguments.iter().map(|s| symbol_to_doc(alloc, *s));
|
||||||
|
|
||||||
|
alloc
|
||||||
|
.text(format!("foreign {:?} ", foreign_symbol.as_str()))
|
||||||
|
.append(alloc.intersperse(it, " "))
|
||||||
|
}
|
||||||
Tag {
|
Tag {
|
||||||
tag_name,
|
tag_name,
|
||||||
arguments,
|
arguments,
|
||||||
|
@ -1331,8 +1347,27 @@ pub fn specialize_all<'a>(
|
||||||
let it = procs.externals_others_need.specs.clone();
|
let it = procs.externals_others_need.specs.clone();
|
||||||
let it = it
|
let it = it
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(symbol, solved_types)| solved_types.into_iter().map(move |s| (symbol, s)))
|
.map(|(symbol, solved_types)| {
|
||||||
|
// for some unclear reason, the MutSet does not deduplicate according to the hash
|
||||||
|
// instance. So we do it manually here
|
||||||
|
let mut as_vec: std::vec::Vec<_> = solved_types.into_iter().collect();
|
||||||
|
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
let hash_the_thing = |x: &SolvedType| {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
x.hash(&mut hasher);
|
||||||
|
hasher.finish()
|
||||||
|
};
|
||||||
|
|
||||||
|
as_vec.sort_by_key(|x| hash_the_thing(x));
|
||||||
|
as_vec.dedup_by_key(|x| hash_the_thing(x));
|
||||||
|
|
||||||
|
as_vec.into_iter().map(move |s| (symbol, s))
|
||||||
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
for (name, solved_type) in it.into_iter() {
|
for (name, solved_type) in it.into_iter() {
|
||||||
let partial_proc = match procs.partial_procs.get(&name) {
|
let partial_proc = match procs.partial_procs.get(&name) {
|
||||||
Some(v) => v.clone(),
|
Some(v) => v.clone(),
|
||||||
|
@ -1396,7 +1431,6 @@ pub fn specialize_all<'a>(
|
||||||
procs
|
procs
|
||||||
.specialized
|
.specialized
|
||||||
.insert((name, outside_layout.clone()), InProgress);
|
.insert((name, outside_layout.clone()), InProgress);
|
||||||
|
|
||||||
match specialize(
|
match specialize(
|
||||||
env,
|
env,
|
||||||
&mut procs,
|
&mut procs,
|
||||||
|
@ -1407,8 +1441,16 @@ pub fn specialize_all<'a>(
|
||||||
) {
|
) {
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
debug_assert_eq!(outside_layout, layout);
|
debug_assert_eq!(outside_layout, layout);
|
||||||
procs.specialized.remove(&(name, outside_layout));
|
|
||||||
procs.specialized.insert((name, layout), Done(proc));
|
if let Layout::Closure(args, closure, ret) = layout {
|
||||||
|
procs.specialized.remove(&(name, outside_layout));
|
||||||
|
let layout = ClosureLayout::extend_function_layout(
|
||||||
|
env.arena, args, closure, ret,
|
||||||
|
);
|
||||||
|
procs.specialized.insert((name, layout), Done(proc));
|
||||||
|
} else {
|
||||||
|
procs.specialized.insert((name, layout), Done(proc));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error_msg = env.arena.alloc(format!(
|
let error_msg = env.arena.alloc(format!(
|
||||||
|
@ -1451,9 +1493,7 @@ fn specialize_external<'a>(
|
||||||
let unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
let unified = roc_unify::unify::unify(env.subs, annotation, fn_var);
|
||||||
|
|
||||||
let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
let is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||||
debug_assert!(is_valid);
|
debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
||||||
|
|
||||||
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
|
||||||
|
|
||||||
// if this is a closure, add the closure record argument
|
// if this is a closure, add the closure record argument
|
||||||
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
let pattern_symbols = if let CapturedSymbols::Captured(_) = captured_symbols {
|
||||||
|
@ -1467,6 +1507,7 @@ fn specialize_external<'a>(
|
||||||
let (proc_args, opt_closure_layout, ret_layout) =
|
let (proc_args, opt_closure_layout, ret_layout) =
|
||||||
build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?;
|
build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?;
|
||||||
|
|
||||||
|
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
||||||
// unpack the closure symbols, if any
|
// unpack the closure symbols, if any
|
||||||
if let CapturedSymbols::Captured(captured) = captured_symbols {
|
if let CapturedSymbols::Captured(captured) = captured_symbols {
|
||||||
let mut layouts = Vec::with_capacity_in(captured.len(), env.arena);
|
let mut layouts = Vec::with_capacity_in(captured.len(), env.arena);
|
||||||
|
@ -1669,9 +1710,9 @@ fn build_specialized_proc_adapter<'a>(
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn build_specialized_proc<'a>(
|
fn build_specialized_proc<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
_proc_name: Symbol,
|
proc_name: Symbol,
|
||||||
pattern_symbols: &[Symbol],
|
pattern_symbols: &[Symbol],
|
||||||
pattern_layouts: Vec<Layout<'a>>,
|
pattern_layouts: Vec<'a, Layout<'a>>,
|
||||||
opt_closure_layout: Option<ClosureLayout<'a>>,
|
opt_closure_layout: Option<ClosureLayout<'a>>,
|
||||||
ret_layout: Layout<'a>,
|
ret_layout: Layout<'a>,
|
||||||
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
|
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
|
||||||
|
@ -1709,7 +1750,8 @@ fn build_specialized_proc<'a>(
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
pattern_layouts_len + 1,
|
pattern_layouts_len + 1,
|
||||||
pattern_symbols.len(),
|
pattern_symbols.len(),
|
||||||
"Tried to zip two vecs with different lengths!"
|
"Tried to zip two vecs with different lengths in {:?}!",
|
||||||
|
proc_name,
|
||||||
);
|
);
|
||||||
|
|
||||||
let proc_args = proc_args.into_bump_slice();
|
let proc_args = proc_args.into_bump_slice();
|
||||||
|
@ -1738,16 +1780,22 @@ fn build_specialized_proc<'a>(
|
||||||
// make sure there is not arg_closure argument without a closure layout
|
// make sure there is not arg_closure argument without a closure layout
|
||||||
debug_assert!(pattern_symbols.last() != Some(&Symbol::ARG_CLOSURE));
|
debug_assert!(pattern_symbols.last() != Some(&Symbol::ARG_CLOSURE));
|
||||||
|
|
||||||
// since this is not a closure, the number of arguments should match between symbols
|
use std::cmp::Ordering;
|
||||||
// and layout
|
match pattern_layouts_len.cmp(&pattern_symbols.len()) {
|
||||||
debug_assert_eq!(
|
Ordering::Equal => {
|
||||||
pattern_layouts_len,
|
let proc_args = proc_args.into_bump_slice();
|
||||||
pattern_symbols.len(),
|
|
||||||
"Tried to zip two vecs with different lengths!"
|
|
||||||
);
|
|
||||||
let proc_args = proc_args.into_bump_slice();
|
|
||||||
|
|
||||||
Ok((proc_args, None, ret_layout))
|
Ok((proc_args, None, ret_layout))
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
// so far, the problem when hitting this branch was always somewhere else
|
||||||
|
// I think this branch should not be reachable in a bugfree compiler
|
||||||
|
panic!("more arguments (according to the layout) than argument symbols")
|
||||||
|
}
|
||||||
|
Ordering::Less => {
|
||||||
|
panic!("more argument symbols than arguments (according to the layout)")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2943,16 +2991,19 @@ pub fn with_hole<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
match loc_expr.value {
|
match loc_expr.value {
|
||||||
roc_can::expr::Expr::Var(proc_name) if is_known(&proc_name) => call_by_name(
|
roc_can::expr::Expr::Var(proc_name) if is_known(&proc_name) => {
|
||||||
env,
|
// a call by a known name
|
||||||
procs,
|
call_by_name(
|
||||||
fn_var,
|
env,
|
||||||
proc_name,
|
procs,
|
||||||
loc_args,
|
fn_var,
|
||||||
layout_cache,
|
proc_name,
|
||||||
assigned,
|
loc_args,
|
||||||
hole,
|
layout_cache,
|
||||||
),
|
assigned,
|
||||||
|
hole,
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Call by pointer - the closure was anonymous, e.g.
|
// Call by pointer - the closure was anonymous, e.g.
|
||||||
//
|
//
|
||||||
|
@ -3126,6 +3177,42 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol,
|
||||||
|
args,
|
||||||
|
ret_var,
|
||||||
|
} => {
|
||||||
|
let mut arg_symbols = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
|
for (_, arg_expr) in args.iter() {
|
||||||
|
arg_symbols.push(possible_reuse_symbol(env, procs, &arg_expr));
|
||||||
|
}
|
||||||
|
let arg_symbols = arg_symbols.into_bump_slice();
|
||||||
|
|
||||||
|
// layout of the return type
|
||||||
|
let layout = layout_cache
|
||||||
|
.from_var(env.arena, ret_var, env.subs)
|
||||||
|
.unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||||
|
|
||||||
|
let result = Stmt::Let(
|
||||||
|
assigned,
|
||||||
|
Expr::ForeignCall {
|
||||||
|
foreign_symbol,
|
||||||
|
arguments: arg_symbols,
|
||||||
|
ret_layout: layout.clone(),
|
||||||
|
},
|
||||||
|
layout,
|
||||||
|
hole,
|
||||||
|
);
|
||||||
|
|
||||||
|
let iter = args
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.map(|(a, b)| (a, Located::at_zero(b)))
|
||||||
|
.zip(arg_symbols.iter().rev());
|
||||||
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
|
}
|
||||||
|
|
||||||
RunLowLevel { op, args, ret_var } => {
|
RunLowLevel { op, args, ret_var } => {
|
||||||
let op = optimize_low_level(env.subs, op, &args);
|
let op = optimize_low_level(env.subs, op, &args);
|
||||||
|
|
||||||
|
@ -3956,6 +4043,35 @@ fn substitute_in_expr<'a>(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ForeignCall {
|
||||||
|
foreign_symbol,
|
||||||
|
arguments,
|
||||||
|
ret_layout,
|
||||||
|
} => {
|
||||||
|
let mut did_change = false;
|
||||||
|
let new_args = Vec::from_iter_in(
|
||||||
|
arguments.iter().map(|s| match substitute(subs, *s) {
|
||||||
|
None => *s,
|
||||||
|
Some(s) => {
|
||||||
|
did_change = true;
|
||||||
|
s
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
arena,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_change {
|
||||||
|
let args = new_args.into_bump_slice();
|
||||||
|
|
||||||
|
Some(ForeignCall {
|
||||||
|
foreign_symbol: foreign_symbol.clone(),
|
||||||
|
arguments: args,
|
||||||
|
ret_layout: ret_layout.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Tag {
|
Tag {
|
||||||
tag_layout,
|
tag_layout,
|
||||||
|
|
|
@ -109,10 +109,8 @@ impl<'a> ClosureLayout<'a> {
|
||||||
|
|
||||||
use UnionVariant::*;
|
use UnionVariant::*;
|
||||||
match variant {
|
match variant {
|
||||||
Never | Unit => {
|
Never => Ok(None),
|
||||||
// a max closure size of 0 means this is a standard top-level function
|
Unit => Ok(None),
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
BoolUnion { .. } => {
|
BoolUnion { .. } => {
|
||||||
let closure_layout = ClosureLayout::from_bool(arena);
|
let closure_layout = ClosureLayout::from_bool(arena);
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,9 @@ pub struct AppHeader<'a> {
|
||||||
pub struct PlatformHeader<'a> {
|
pub struct PlatformHeader<'a> {
|
||||||
pub name: Loc<PackageName<'a>>,
|
pub name: Loc<PackageName<'a>>,
|
||||||
pub provides: Vec<'a, Loc<ExposesEntry<'a>>>,
|
pub provides: Vec<'a, Loc<ExposesEntry<'a>>>,
|
||||||
pub requires: Vec<'a, Loc<ExposesEntry<'a>>>,
|
pub requires: Vec<'a, Loc<TypedIdent<'a>>>,
|
||||||
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
pub imports: Vec<'a, Loc<ImportsEntry<'a>>>,
|
||||||
pub effects: Vec<'a, Loc<EffectsEntry<'a>>>,
|
pub effects: Effects<'a>,
|
||||||
|
|
||||||
// Potential comments and newlines - these will typically all be empty.
|
// Potential comments and newlines - these will typically all be empty.
|
||||||
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
|
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
|
||||||
|
@ -64,23 +64,31 @@ pub struct PlatformHeader<'a> {
|
||||||
pub after_requires: &'a [CommentOrNewline<'a>],
|
pub after_requires: &'a [CommentOrNewline<'a>],
|
||||||
pub before_imports: &'a [CommentOrNewline<'a>],
|
pub before_imports: &'a [CommentOrNewline<'a>],
|
||||||
pub after_imports: &'a [CommentOrNewline<'a>],
|
pub after_imports: &'a [CommentOrNewline<'a>],
|
||||||
pub before_effects: &'a [CommentOrNewline<'a>],
|
|
||||||
pub after_effects: &'a [CommentOrNewline<'a>],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum EffectsEntry<'a> {
|
pub struct Effects<'a> {
|
||||||
|
pub spaces_before_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||||
|
pub spaces_after_effects_keyword: &'a [CommentOrNewline<'a>],
|
||||||
|
pub spaces_after_type_name: &'a [CommentOrNewline<'a>],
|
||||||
|
pub type_name: &'a str,
|
||||||
|
pub entries: Vec<'a, Loc<TypedIdent<'a>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum TypedIdent<'a> {
|
||||||
/// e.g.
|
/// e.g.
|
||||||
///
|
///
|
||||||
/// printLine : Str -> Effect {}
|
/// printLine : Str -> Effect {}
|
||||||
Effect {
|
Entry {
|
||||||
ident: Loc<&'a str>,
|
ident: Loc<&'a str>,
|
||||||
|
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||||
ann: Loc<TypeAnnotation<'a>>,
|
ann: Loc<TypeAnnotation<'a>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
// Spaces
|
// Spaces
|
||||||
SpaceBefore(&'a EffectsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||||
SpaceAfter(&'a EffectsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -620,12 +628,12 @@ impl<'a> Spaceable<'a> for ImportsEntry<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Spaceable<'a> for EffectsEntry<'a> {
|
impl<'a> Spaceable<'a> for TypedIdent<'a> {
|
||||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||||
EffectsEntry::SpaceBefore(self, spaces)
|
TypedIdent::SpaceBefore(self, spaces)
|
||||||
}
|
}
|
||||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||||
EffectsEntry::SpaceAfter(self, spaces)
|
TypedIdent::SpaceAfter(self, spaces)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,11 +317,26 @@ fn spaces<'a>(
|
||||||
'\n' => {
|
'\n' => {
|
||||||
state = state.newline()?;
|
state = state.newline()?;
|
||||||
|
|
||||||
// This was a newline, so end this line comment.
|
match (comment_line_buf.len(), comment_line_buf.chars().next())
|
||||||
space_list.push(LineComment(comment_line_buf.into_bump_str()));
|
{
|
||||||
comment_line_buf = String::new_in(arena);
|
(1, Some('#')) => {
|
||||||
|
// This is a line with `##` - that is,
|
||||||
|
// a doc comment new line.
|
||||||
|
space_list.push(DocComment(""));
|
||||||
|
comment_line_buf = String::new_in(arena);
|
||||||
|
|
||||||
line_state = LineState::Normal;
|
line_state = LineState::Normal;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// This was a newline, so end this line comment.
|
||||||
|
space_list.push(LineComment(
|
||||||
|
comment_line_buf.into_bump_str(),
|
||||||
|
));
|
||||||
|
comment_line_buf = String::new_in(arena);
|
||||||
|
|
||||||
|
line_state = LineState::Normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
nonblank => {
|
nonblank => {
|
||||||
// Chars can have btye lengths of more than 1!
|
// Chars can have btye lengths of more than 1!
|
||||||
|
|
|
@ -30,7 +30,7 @@ macro_rules! loc_parenthetical_expr {
|
||||||
then(
|
then(
|
||||||
loc!(and!(
|
loc!(and!(
|
||||||
between!(
|
between!(
|
||||||
ascii_char('(' ),
|
ascii_char(b'(' ),
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
space0_around(
|
space0_around(
|
||||||
loc!(move |arena, state| parse_expr($min_indent, arena, state)),
|
loc!(move |arena, state| parse_expr($min_indent, arena, state)),
|
||||||
|
@ -43,7 +43,7 @@ macro_rules! loc_parenthetical_expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
ascii_char(')' )
|
ascii_char(b')' )
|
||||||
),
|
),
|
||||||
optional(either!(
|
optional(either!(
|
||||||
// There may optionally be function args after the ')'
|
// There may optionally be function args after the ')'
|
||||||
|
@ -59,7 +59,7 @@ macro_rules! loc_parenthetical_expr {
|
||||||
// as if there were any args they'd have consumed it anyway
|
// as if there were any args they'd have consumed it anyway
|
||||||
// e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser
|
// e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser
|
||||||
either!(
|
either!(
|
||||||
one_or_more!(skip_first!(ascii_char('.' ), lowercase_ident())),
|
one_or_more!(skip_first!(ascii_char(b'.' ), lowercase_ident())),
|
||||||
and!(space0($min_indent), equals_with_indent())
|
and!(space0($min_indent), equals_with_indent())
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
@ -170,7 +170,7 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
one_of!(
|
one_of!(
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
and!(
|
and!(
|
||||||
loc!(ascii_char('!')),
|
loc!(ascii_char(b'!')),
|
||||||
loc!(move |arena, state| parse_expr(min_indent, arena, state))
|
loc!(move |arena, state| parse_expr(min_indent, arena, state))
|
||||||
),
|
),
|
||||||
|arena: &'a Bump, (loc_op, loc_expr): (Located<()>, Located<Expr<'a>>)| {
|
|arena: &'a Bump, (loc_op, loc_expr): (Located<()>, Located<Expr<'a>>)| {
|
||||||
|
@ -179,7 +179,7 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
),
|
),
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
and!(
|
and!(
|
||||||
loc!(ascii_char('-')),
|
loc!(ascii_char(b'-')),
|
||||||
loc!(move |arena, state| parse_expr(min_indent, arena, state))
|
loc!(move |arena, state| parse_expr(min_indent, arena, state))
|
||||||
),
|
),
|
||||||
|arena: &'a Bump, (loc_op, loc_expr): (Located<()>, Located<Expr<'a>>)| {
|
|arena: &'a Bump, (loc_op, loc_expr): (Located<()>, Located<Expr<'a>>)| {
|
||||||
|
@ -449,9 +449,9 @@ pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
|
||||||
let (loc_tuple, state) = loc!(and!(
|
let (loc_tuple, state) = loc!(and!(
|
||||||
space0_after(
|
space0_after(
|
||||||
between!(
|
between!(
|
||||||
ascii_char('('),
|
ascii_char(b'('),
|
||||||
space0_around(loc_pattern(min_indent), min_indent),
|
space0_around(loc_pattern(min_indent), min_indent),
|
||||||
ascii_char(')')
|
ascii_char(b')')
|
||||||
),
|
),
|
||||||
min_indent,
|
min_indent,
|
||||||
),
|
),
|
||||||
|
@ -481,7 +481,10 @@ pub fn loc_parenthetical_def<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
|
||||||
/// The '=' used in a def can't be followed by another '=' (or else it's actually
|
/// The '=' used in a def can't be followed by another '=' (or else it's actually
|
||||||
/// an "==") and also it can't be followed by '>' (or else it's actually an "=>")
|
/// an "==") and also it can't be followed by '>' (or else it's actually an "=>")
|
||||||
fn equals_for_def<'a>() -> impl Parser<'a, ()> {
|
fn equals_for_def<'a>() -> impl Parser<'a, ()> {
|
||||||
not_followed_by(ascii_char('='), one_of!(ascii_char('='), ascii_char('>')))
|
not_followed_by(
|
||||||
|
ascii_char(b'='),
|
||||||
|
one_of!(ascii_char(b'='), ascii_char(b'>')),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A definition, consisting of one of these:
|
/// A definition, consisting of one of these:
|
||||||
|
@ -512,7 +515,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
|
||||||
),
|
),
|
||||||
// Annotation
|
// Annotation
|
||||||
skip_first!(
|
skip_first!(
|
||||||
ascii_char(':'),
|
ascii_char(b':'),
|
||||||
// Spaces after the ':' (at a normal indentation level) and then the type.
|
// Spaces after the ':' (at a normal indentation level) and then the type.
|
||||||
// The type itself must be indented more than the pattern and ':'
|
// The type itself must be indented more than the pattern and ':'
|
||||||
space0_before(type_annotation::located(indented_more), indented_more)
|
space0_before(type_annotation::located(indented_more), indented_more)
|
||||||
|
@ -838,7 +841,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
// All closures start with a '\' - e.g. (\x -> x + 1)
|
// All closures start with a '\' - e.g. (\x -> x + 1)
|
||||||
ascii_char('\\'),
|
ascii_char(b'\\'),
|
||||||
// Once we see the '\', we're committed to parsing this as a closure.
|
// Once we see the '\', we're committed to parsing this as a closure.
|
||||||
// It may turn out to be malformed, but it is definitely a closure.
|
// It may turn out to be malformed, but it is definitely a closure.
|
||||||
optional(and!(
|
optional(and!(
|
||||||
|
@ -847,7 +850,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
Attempting::ClosureParams,
|
Attempting::ClosureParams,
|
||||||
// Params are comma-separated
|
// Params are comma-separated
|
||||||
sep_by1(
|
sep_by1(
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
space0_around(loc_closure_param(min_indent), min_indent)
|
space0_around(loc_closure_param(min_indent), min_indent)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
@ -896,9 +899,9 @@ fn parse_closure_param<'a>(
|
||||||
// If you wrap it in parens, you can match any arbitrary pattern at all.
|
// If you wrap it in parens, you can match any arbitrary pattern at all.
|
||||||
// e.g. \User.UserId userId -> ...
|
// e.g. \User.UserId userId -> ...
|
||||||
between!(
|
between!(
|
||||||
ascii_char('('),
|
ascii_char(b'('),
|
||||||
space0_around(loc_pattern(min_indent), min_indent),
|
space0_around(loc_pattern(min_indent), min_indent),
|
||||||
ascii_char(')')
|
ascii_char(b')')
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
|
@ -922,9 +925,9 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
|
|
||||||
fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||||
between!(
|
between!(
|
||||||
ascii_char('('),
|
ascii_char(b'('),
|
||||||
move |arena, state| loc_pattern(min_indent).parse(arena, state),
|
move |arena, state| loc_pattern(min_indent).parse(arena, state),
|
||||||
ascii_char(')')
|
ascii_char(b')')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -939,13 +942,13 @@ fn string_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||||
map!(ascii_char('_'), |_| Pattern::Underscore)
|
map!(ascii_char(b'_'), |_| Pattern::Underscore)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||||
then(
|
then(
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('{'),
|
ascii_char(b'{'),
|
||||||
move |arena: &'a bumpalo::Bump,
|
move |arena: &'a bumpalo::Bump,
|
||||||
state: crate::parser::State<'a>|
|
state: crate::parser::State<'a>|
|
||||||
-> crate::parser::ParseResult<'a, Located<crate::ast::Pattern<'a>>> {
|
-> crate::parser::ParseResult<'a, Located<crate::ast::Pattern<'a>>> {
|
||||||
|
@ -963,11 +966,11 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||||
// (This is true in both literals and types.)
|
// (This is true in both literals and types.)
|
||||||
let (opt_loc_val, state) = crate::parser::optional(either!(
|
let (opt_loc_val, state) = crate::parser::optional(either!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
ascii_char(':'),
|
ascii_char(b':'),
|
||||||
space0_before(loc_pattern(min_indent), min_indent)
|
space0_before(loc_pattern(min_indent), min_indent)
|
||||||
),
|
),
|
||||||
skip_first!(
|
skip_first!(
|
||||||
ascii_char('?'),
|
ascii_char(b'?'),
|
||||||
space0_before(loc!(expr(min_indent)), min_indent)
|
space0_before(loc!(expr(min_indent)), min_indent)
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
@ -1006,8 +1009,8 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||||
|
|
||||||
Ok((answer, state))
|
Ok((answer, state))
|
||||||
},
|
},
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char('}'),
|
ascii_char(b'}'),
|
||||||
min_indent
|
min_indent
|
||||||
),
|
),
|
||||||
move |_arena, state, loc_patterns| {
|
move |_arena, state, loc_patterns| {
|
||||||
|
@ -1250,7 +1253,7 @@ mod when {
|
||||||
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>)> {
|
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>)> {
|
||||||
and!(
|
and!(
|
||||||
sep_by1(
|
sep_by1(
|
||||||
ascii_char('|'),
|
ascii_char(b'|'),
|
||||||
space0_around(loc_pattern(min_indent), min_indent),
|
space0_around(loc_pattern(min_indent), min_indent),
|
||||||
),
|
),
|
||||||
optional(skip_first!(
|
optional(skip_first!(
|
||||||
|
@ -1350,14 +1353,14 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
|
||||||
// Try to parse a number literal *before* trying to parse unary negate,
|
// Try to parse a number literal *before* trying to parse unary negate,
|
||||||
// because otherwise (foo -1) will parse as (foo (Num.neg 1))
|
// because otherwise (foo -1) will parse as (foo (Num.neg 1))
|
||||||
loc!(number_literal()),
|
loc!(number_literal()),
|
||||||
loc!(ascii_char('-'))
|
loc!(ascii_char(b'-'))
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
one_of!(
|
one_of!(
|
||||||
ascii_char(' '),
|
ascii_char(b' '),
|
||||||
ascii_char('#'),
|
ascii_char(b'#'),
|
||||||
ascii_char('\n'),
|
ascii_char(b'\n'),
|
||||||
ascii_char('>')
|
ascii_char(b'>')
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
move |arena, state, (spaces, num_or_minus_char)| {
|
move |arena, state, (spaces, num_or_minus_char)| {
|
||||||
|
@ -1660,27 +1663,27 @@ fn binop<'a>() -> impl Parser<'a, BinOp> {
|
||||||
map!(ascii_string("!="), |_| BinOp::NotEquals),
|
map!(ascii_string("!="), |_| BinOp::NotEquals),
|
||||||
map!(ascii_string("&&"), |_| BinOp::And),
|
map!(ascii_string("&&"), |_| BinOp::And),
|
||||||
map!(ascii_string("||"), |_| BinOp::Or),
|
map!(ascii_string("||"), |_| BinOp::Or),
|
||||||
map!(ascii_char('+'), |_| BinOp::Plus),
|
map!(ascii_char(b'+'), |_| BinOp::Plus),
|
||||||
map!(ascii_char('*'), |_| BinOp::Star),
|
map!(ascii_char(b'*'), |_| BinOp::Star),
|
||||||
map!(ascii_char('-'), |_| BinOp::Minus),
|
map!(ascii_char(b'-'), |_| BinOp::Minus),
|
||||||
map!(ascii_string("//"), |_| BinOp::DoubleSlash),
|
map!(ascii_string("//"), |_| BinOp::DoubleSlash),
|
||||||
map!(ascii_char('/'), |_| BinOp::Slash),
|
map!(ascii_char(b'/'), |_| BinOp::Slash),
|
||||||
map!(ascii_string("<="), |_| BinOp::LessThanOrEq),
|
map!(ascii_string("<="), |_| BinOp::LessThanOrEq),
|
||||||
map!(ascii_char('<'), |_| BinOp::LessThan),
|
map!(ascii_char(b'<'), |_| BinOp::LessThan),
|
||||||
map!(ascii_string(">="), |_| BinOp::GreaterThanOrEq),
|
map!(ascii_string(">="), |_| BinOp::GreaterThanOrEq),
|
||||||
map!(ascii_char('>'), |_| BinOp::GreaterThan),
|
map!(ascii_char(b'>'), |_| BinOp::GreaterThan),
|
||||||
map!(ascii_char('^'), |_| BinOp::Caret),
|
map!(ascii_char(b'^'), |_| BinOp::Caret),
|
||||||
map!(ascii_string("%%"), |_| BinOp::DoublePercent),
|
map!(ascii_string("%%"), |_| BinOp::DoublePercent),
|
||||||
map!(ascii_char('%'), |_| BinOp::Percent)
|
map!(ascii_char(b'%'), |_| BinOp::Percent)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
let elems = collection!(
|
let elems = collection!(
|
||||||
ascii_char('['),
|
ascii_char(b'['),
|
||||||
loc!(expr(min_indent)),
|
loc!(expr(min_indent)),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char(']'),
|
ascii_char(b']'),
|
||||||
min_indent
|
min_indent
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1723,7 +1726,7 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
|
|
||||||
// there can be field access, e.g. `{ x : 4 }.x`
|
// there can be field access, e.g. `{ x : 4 }.x`
|
||||||
let (accesses, state) = optional(one_or_more!(skip_first!(
|
let (accesses, state) = optional(one_or_more!(skip_first!(
|
||||||
ascii_char('.'),
|
ascii_char(b'.'),
|
||||||
lowercase_ident()
|
lowercase_ident()
|
||||||
)))
|
)))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
@ -1819,7 +1822,7 @@ pub fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
/// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
|
/// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
|
||||||
pub fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
pub fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
skip_first!(ascii_char('@'), global_tag()),
|
skip_first!(ascii_char(b'@'), global_tag()),
|
||||||
|arena: &'a Bump, name: &'a str| {
|
|arena: &'a Bump, name: &'a str| {
|
||||||
let mut buf = String::with_capacity_in(1 + name.len(), arena);
|
let mut buf = String::with_capacity_in(1 + name.len(), arena);
|
||||||
|
|
||||||
|
|
|
@ -393,6 +393,15 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This could be:
|
||||||
|
///
|
||||||
|
/// * A module name
|
||||||
|
/// * A type name
|
||||||
|
/// * A global tag
|
||||||
|
pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||||
|
global_tag_or_ident(|first_char| first_char.is_uppercase())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str> {
|
pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||||
global_tag_or_ident(|first_char| first_char.is_alphabetic())
|
global_tag_or_ident(|first_char| first_char.is_alphabetic())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
AppHeader, Attempting, CommentOrNewline, Def, EffectsEntry, ExposesEntry, ImportsEntry,
|
AppHeader, Attempting, CommentOrNewline, Def, Effects, ExposesEntry, ImportsEntry,
|
||||||
InterfaceHeader, Module, PlatformHeader,
|
InterfaceHeader, Module, PlatformHeader, TypedIdent,
|
||||||
};
|
};
|
||||||
use crate::blankspace::{space0_around, space1};
|
use crate::blankspace::{space0, space0_around, space0_before, space1};
|
||||||
use crate::expr::def;
|
use crate::expr::def;
|
||||||
use crate::header::{ModuleName, PackageName};
|
use crate::header::{ModuleName, PackageName};
|
||||||
use crate::ident::unqualified_ident;
|
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
|
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
|
||||||
unexpected_eof, ParseResult, Parser, State,
|
unexpected_eof, ParseResult, Parser, State,
|
||||||
|
@ -78,7 +78,7 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>> {
|
||||||
map!(
|
map!(
|
||||||
and!(
|
and!(
|
||||||
parse_package_part,
|
parse_package_part,
|
||||||
skip_first!(ascii_char('/'), parse_package_part)
|
skip_first!(ascii_char(b'/'), parse_package_part)
|
||||||
),
|
),
|
||||||
|(account, pkg)| { PackageName { account, pkg } }
|
|(account, pkg)| { PackageName { account, pkg } }
|
||||||
)
|
)
|
||||||
|
@ -216,10 +216,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||||
((before_provides, after_provides), provides),
|
((before_provides, after_provides), provides),
|
||||||
(
|
(
|
||||||
((before_requires, after_requires), requires),
|
((before_requires, after_requires), requires),
|
||||||
(
|
(((before_imports, after_imports), imports), effects),
|
||||||
((before_imports, after_imports), imports),
|
|
||||||
((before_effects, after_effects), effects),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)| {
|
)| {
|
||||||
|
@ -236,8 +233,6 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||||
after_requires,
|
after_requires,
|
||||||
before_imports,
|
before_imports,
|
||||||
after_imports,
|
after_imports,
|
||||||
before_effects,
|
|
||||||
after_effects,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -259,10 +254,10 @@ fn provides<'a>() -> impl Parser<
|
||||||
and!(
|
and!(
|
||||||
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('['),
|
ascii_char(b'['),
|
||||||
loc!(exposes_entry()),
|
loc!(exposes_entry()),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char(']'),
|
ascii_char(b']'),
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -273,16 +268,16 @@ fn requires<'a>() -> impl Parser<
|
||||||
'a,
|
'a,
|
||||||
(
|
(
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||||
Vec<'a, Located<ExposesEntry<'a>>>,
|
Vec<'a, Located<TypedIdent<'a>>>,
|
||||||
),
|
),
|
||||||
> {
|
> {
|
||||||
and!(
|
and!(
|
||||||
and!(skip_second!(space1(1), ascii_string("requires")), space1(1)),
|
and!(skip_second!(space1(1), ascii_string("requires")), space1(1)),
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('['),
|
ascii_char(b'{'),
|
||||||
loc!(exposes_entry()),
|
loc!(typed_ident()),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char(']'),
|
ascii_char(b'}'),
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -299,10 +294,10 @@ fn exposes<'a>() -> impl Parser<
|
||||||
and!(
|
and!(
|
||||||
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('['),
|
ascii_char(b'['),
|
||||||
loc!(exposes_entry()),
|
loc!(exposes_entry()),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char(']'),
|
ascii_char(b']'),
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -319,44 +314,72 @@ fn imports<'a>() -> impl Parser<
|
||||||
and!(
|
and!(
|
||||||
and!(skip_second!(space1(1), ascii_string("imports")), space1(1)),
|
and!(skip_second!(space1(1), ascii_string("imports")), space1(1)),
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('['),
|
ascii_char(b'['),
|
||||||
loc!(imports_entry()),
|
loc!(imports_entry()),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char(']'),
|
ascii_char(b']'),
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn effects<'a>() -> impl Parser<
|
fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
||||||
'a,
|
move |arena, state| {
|
||||||
(
|
let (spaces_before_effects_keyword, state) =
|
||||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
skip_second!(space1(0), ascii_string("effects")).parse(arena, state)?;
|
||||||
Vec<'a, Located<EffectsEntry<'a>>>,
|
let (spaces_after_effects_keyword, state) = space1(0).parse(arena, state)?;
|
||||||
),
|
let ((type_name, spaces_after_type_name), state) =
|
||||||
> {
|
and!(uppercase_ident(), space1(0)).parse(arena, state)?;
|
||||||
and!(
|
let (entries, state) = collection!(
|
||||||
and!(skip_second!(space1(1), ascii_string("effects")), space1(1)),
|
ascii_char(b'{'),
|
||||||
collection!(
|
loc!(typed_ident()),
|
||||||
ascii_char('{'),
|
ascii_char(b','),
|
||||||
loc!(effects_entry()),
|
ascii_char(b'}'),
|
||||||
ascii_char(','),
|
|
||||||
ascii_char('}'),
|
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
)
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
Effects {
|
||||||
|
spaces_before_effects_keyword,
|
||||||
|
spaces_after_effects_keyword,
|
||||||
|
spaces_after_type_name,
|
||||||
|
type_name,
|
||||||
|
entries,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn effects_entry<'a>() -> impl Parser<'a, EffectsEntry<'a>> {
|
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
||||||
// e.g.
|
move |arena, state| {
|
||||||
//
|
// You must have a field name, e.g. "email"
|
||||||
// printLine : Str -> Effect {}
|
let (ident, state) = loc!(lowercase_ident()).parse(arena, state)?;
|
||||||
map!(
|
|
||||||
and!(loc(unqualified_ident()), type_annotation::located(0)),
|
let (spaces_before_colon, state) = space0(0).parse(arena, state)?;
|
||||||
|(ident, ann)| { EffectsEntry::Effect { ident, ann } }
|
|
||||||
)
|
let (ann, state) = skip_first!(
|
||||||
|
ascii_char(b':'),
|
||||||
|
space0_before(type_annotation::located(0), 0)
|
||||||
|
)
|
||||||
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
// e.g.
|
||||||
|
//
|
||||||
|
// printLine : Str -> Effect {}
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
TypedIdent::Entry {
|
||||||
|
ident,
|
||||||
|
spaces_before_colon,
|
||||||
|
ann,
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -372,12 +395,12 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|
||||||
module_name(),
|
module_name(),
|
||||||
// e.g. `.{ Task, after}`
|
// e.g. `.{ Task, after}`
|
||||||
optional(skip_first!(
|
optional(skip_first!(
|
||||||
ascii_char('.'),
|
ascii_char(b'.'),
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('{'),
|
ascii_char(b'{'),
|
||||||
loc!(exposes_entry()),
|
loc!(exposes_entry()),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char('}'),
|
ascii_char(b'}'),
|
||||||
1
|
1
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
|
|
|
@ -433,14 +433,9 @@ fn line_too_long(attempting: Attempting, state: State<'_>) -> (Fail, State<'_>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single ASCII char.
|
/// A single ASCII char.
|
||||||
pub fn ascii_char<'a>(expected: char) -> impl Parser<'a, ()> {
|
pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, ()> {
|
||||||
// Make sure this really is an ASCII char!
|
|
||||||
debug_assert!(expected.len_utf8() == 1);
|
|
||||||
|
|
||||||
move |_arena, state: State<'a>| match state.bytes.first() {
|
move |_arena, state: State<'a>| match state.bytes.first() {
|
||||||
Some(&actual) if expected == actual as char => {
|
Some(&actual) if expected == actual => Ok(((), state.advance_without_indenting(1)?)),
|
||||||
Ok(((), state.advance_without_indenting(1)?))
|
|
||||||
}
|
|
||||||
Some(_) => Err(unexpected(0, state, Attempting::Keyword)),
|
Some(_) => Err(unexpected(0, state, Attempting::Keyword)),
|
||||||
_ => Err(unexpected_eof(0, Attempting::Keyword, state)),
|
_ => Err(unexpected_eof(0, Attempting::Keyword, state)),
|
||||||
}
|
}
|
||||||
|
@ -788,7 +783,7 @@ macro_rules! collection {
|
||||||
// We could change the AST to add extra storage specifically to
|
// We could change the AST to add extra storage specifically to
|
||||||
// support empty literals containing newlines or comments, but this
|
// support empty literals containing newlines or comments, but this
|
||||||
// does not seem worth even the tiniest regression in compiler performance.
|
// does not seem worth even the tiniest regression in compiler performance.
|
||||||
zero_or_more!($crate::parser::ascii_char(' ')),
|
zero_or_more!($crate::parser::ascii_char(b' ')),
|
||||||
skip_second!(
|
skip_second!(
|
||||||
$crate::parser::sep_by0(
|
$crate::parser::sep_by0(
|
||||||
$delimiter,
|
$delimiter,
|
||||||
|
@ -1025,8 +1020,8 @@ macro_rules! record_field {
|
||||||
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
||||||
// (This is true in both literals and types.)
|
// (This is true in both literals and types.)
|
||||||
let (opt_loc_val, state) = $crate::parser::optional(either!(
|
let (opt_loc_val, state) = $crate::parser::optional(either!(
|
||||||
skip_first!(ascii_char(':'), space0_before($val_parser, $min_indent)),
|
skip_first!(ascii_char(b':'), space0_before($val_parser, $min_indent)),
|
||||||
skip_first!(ascii_char('?'), space0_before($val_parser, $min_indent))
|
skip_first!(ascii_char(b'?'), space0_before($val_parser, $min_indent))
|
||||||
))
|
))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
@ -1055,10 +1050,10 @@ macro_rules! record_field {
|
||||||
macro_rules! record_without_update {
|
macro_rules! record_without_update {
|
||||||
($val_parser:expr, $min_indent:expr) => {
|
($val_parser:expr, $min_indent:expr) => {
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('{'),
|
ascii_char(b'{'),
|
||||||
loc!(record_field!($val_parser, $min_indent)),
|
loc!(record_field!($val_parser, $min_indent)),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char('}'),
|
ascii_char(b'}'),
|
||||||
$min_indent
|
$min_indent
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -1068,7 +1063,7 @@ macro_rules! record_without_update {
|
||||||
macro_rules! record {
|
macro_rules! record {
|
||||||
($val_parser:expr, $min_indent:expr) => {
|
($val_parser:expr, $min_indent:expr) => {
|
||||||
skip_first!(
|
skip_first!(
|
||||||
$crate::parser::ascii_char('{'),
|
$crate::parser::ascii_char(b'{'),
|
||||||
and!(
|
and!(
|
||||||
// You can optionally have an identifier followed by an '&' to
|
// You can optionally have an identifier followed by an '&' to
|
||||||
// make this a record update, e.g. { Foo.user & username: "blah" }.
|
// make this a record update, e.g. { Foo.user & username: "blah" }.
|
||||||
|
@ -1084,7 +1079,7 @@ macro_rules! record {
|
||||||
)),
|
)),
|
||||||
$min_indent
|
$min_indent
|
||||||
),
|
),
|
||||||
$crate::parser::ascii_char('&')
|
$crate::parser::ascii_char(b'&')
|
||||||
)),
|
)),
|
||||||
loc!(skip_first!(
|
loc!(skip_first!(
|
||||||
// We specifically allow space characters inside here, so that
|
// We specifically allow space characters inside here, so that
|
||||||
|
@ -1098,16 +1093,16 @@ macro_rules! record {
|
||||||
// We could change the AST to add extra storage specifically to
|
// We could change the AST to add extra storage specifically to
|
||||||
// support empty literals containing newlines or comments, but this
|
// support empty literals containing newlines or comments, but this
|
||||||
// does not seem worth even the tiniest regression in compiler performance.
|
// does not seem worth even the tiniest regression in compiler performance.
|
||||||
zero_or_more!($crate::parser::ascii_char(' ')),
|
zero_or_more!($crate::parser::ascii_char(b' ')),
|
||||||
skip_second!(
|
skip_second!(
|
||||||
$crate::parser::sep_by0(
|
$crate::parser::sep_by0(
|
||||||
$crate::parser::ascii_char(','),
|
$crate::parser::ascii_char(b','),
|
||||||
$crate::blankspace::space0_around(
|
$crate::blankspace::space0_around(
|
||||||
loc!(record_field!($val_parser, $min_indent)),
|
loc!(record_field!($val_parser, $min_indent)),
|
||||||
$min_indent
|
$min_indent
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
$crate::parser::ascii_char('}')
|
$crate::parser::ascii_char(b'}')
|
||||||
)
|
)
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
|
|
|
@ -162,7 +162,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
||||||
// canonicalization error if that expression variant
|
// canonicalization error if that expression variant
|
||||||
// is not allowed inside a string interpolation.
|
// is not allowed inside a string interpolation.
|
||||||
let (loc_expr, new_state) =
|
let (loc_expr, new_state) =
|
||||||
skip_second!(loc(allocated(expr::expr(0))), ascii_char(')'))
|
skip_second!(loc(allocated(expr::expr(0))), ascii_char(b')'))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
// Advance the iterator past the expr we just parsed.
|
// Advance the iterator past the expr we just parsed.
|
||||||
|
@ -185,9 +185,12 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
||||||
// Parse the hex digits, surrounded by parens, then
|
// Parse the hex digits, surrounded by parens, then
|
||||||
// give a canonicalization error if the digits form
|
// give a canonicalization error if the digits form
|
||||||
// an invalid unicode code point.
|
// an invalid unicode code point.
|
||||||
let (loc_digits, new_state) =
|
let (loc_digits, new_state) = between!(
|
||||||
between!(ascii_char('('), loc(ascii_hex_digits()), ascii_char(')'))
|
ascii_char(b'('),
|
||||||
.parse(arena, state)?;
|
loc(ascii_hex_digits()),
|
||||||
|
ascii_char(b')')
|
||||||
|
)
|
||||||
|
.parse(arena, state)?;
|
||||||
|
|
||||||
// Advance the iterator past the expr we just parsed.
|
// Advance the iterator past the expr we just parsed.
|
||||||
for _ in 0..(original_byte_count - new_state.bytes.len()) {
|
for _ in 0..(original_byte_count - new_state.bytes.len()) {
|
||||||
|
|
|
@ -22,10 +22,10 @@ macro_rules! tag_union {
|
||||||
map!(
|
map!(
|
||||||
and!(
|
and!(
|
||||||
collection!(
|
collection!(
|
||||||
ascii_char('['),
|
ascii_char(b'['),
|
||||||
loc!(tag_type($min_indent)),
|
loc!(tag_type($min_indent)),
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
ascii_char(']'),
|
ascii_char(b']'),
|
||||||
$min_indent
|
$min_indent
|
||||||
),
|
),
|
||||||
optional(
|
optional(
|
||||||
|
@ -89,7 +89,7 @@ pub fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>>
|
||||||
|
|
||||||
/// The `*` type variable, e.g. in (List *) Wildcard,
|
/// The `*` type variable, e.g. in (List *) Wildcard,
|
||||||
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||||
map!(loc!(ascii_char('*')), |loc_val: Located<()>| {
|
map!(loc!(ascii_char(b'*')), |loc_val: Located<()>| {
|
||||||
loc_val.map(|_| TypeAnnotation::Wildcard)
|
loc_val.map(|_| TypeAnnotation::Wildcard)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -112,12 +112,12 @@ pub fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnot
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||||
between!(
|
between!(
|
||||||
ascii_char('('),
|
ascii_char(b'('),
|
||||||
space0_around(
|
space0_around(
|
||||||
move |arena, state| expression(min_indent).parse(arena, state),
|
move |arena, state| expression(min_indent).parse(arena, state),
|
||||||
min_indent,
|
min_indent,
|
||||||
),
|
),
|
||||||
ascii_char(')')
|
ascii_char(b')')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +208,7 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
||||||
move |arena, state: State<'a>| {
|
move |arena, state: State<'a>| {
|
||||||
let (first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
let (first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
||||||
let (rest, state) = zero_or_more!(skip_first!(
|
let (rest, state) = zero_or_more!(skip_first!(
|
||||||
ascii_char(','),
|
ascii_char(b','),
|
||||||
space0_around(term(min_indent), min_indent)
|
space0_around(term(min_indent), min_indent)
|
||||||
))
|
))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
|
@ -2345,18 +2345,20 @@ mod test_parse {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
let newlines = &[Newline, Newline];
|
let newlines = &[Newline, Newline];
|
||||||
let def = Def::Body(
|
let def = Def::Body(
|
||||||
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
|
arena.alloc(Located::new(6, 6, 0, 1, Identifier("x"))),
|
||||||
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
|
arena.alloc(Located::new(6, 6, 4, 5, Num("5"))),
|
||||||
);
|
);
|
||||||
let loc_def = &*arena.alloc(Located::new(4, 4, 0, 1, def));
|
let loc_def = &*arena.alloc(Located::new(6, 6, 0, 1, def));
|
||||||
let defs = &[loc_def];
|
let defs = &[loc_def];
|
||||||
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines);
|
||||||
let loc_ret = Located::new(6, 6, 0, 2, ret);
|
let loc_ret = Located::new(8, 8, 0, 2, ret);
|
||||||
let reset_indentation = &[
|
let reset_indentation = &[
|
||||||
DocComment("first line of docs"),
|
DocComment("first line of docs"),
|
||||||
DocComment(" second line"),
|
DocComment(" second line"),
|
||||||
DocComment(" third line"),
|
DocComment(" third line"),
|
||||||
DocComment("fourth line"),
|
DocComment("fourth line"),
|
||||||
|
DocComment(""),
|
||||||
|
DocComment("sixth line after doc new line"),
|
||||||
];
|
];
|
||||||
let expected = Expr::SpaceBefore(
|
let expected = Expr::SpaceBefore(
|
||||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||||
|
@ -2370,6 +2372,8 @@ mod test_parse {
|
||||||
## second line
|
## second line
|
||||||
## third line
|
## third line
|
||||||
## fourth line
|
## fourth line
|
||||||
|
##
|
||||||
|
## sixth line after doc new line
|
||||||
x = 5
|
x = 5
|
||||||
|
|
||||||
42
|
42
|
||||||
|
|
|
@ -777,6 +777,16 @@ fn to_expr_report<'b>(
|
||||||
op
|
op
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reason::ForeignCallArg {
|
||||||
|
foreign_symbol,
|
||||||
|
arg_index,
|
||||||
|
} => {
|
||||||
|
panic!(
|
||||||
|
"Compiler bug: argument #{} to foreign symbol {:?} was the wrong type!",
|
||||||
|
arg_index.ordinal(),
|
||||||
|
foreign_symbol
|
||||||
|
);
|
||||||
|
}
|
||||||
Reason::FloatLiteral | Reason::IntLiteral | Reason::NumLiteral => {
|
Reason::FloatLiteral | Reason::IntLiteral | Reason::NumLiteral => {
|
||||||
unreachable!("I don't think these can be reached")
|
unreachable!("I don't think these can be reached")
|
||||||
}
|
}
|
||||||
|
@ -954,6 +964,9 @@ fn add_category<'b>(
|
||||||
op
|
op
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
ForeignCall => {
|
||||||
|
panic!("Compiler bug: invalid return type from foreign call",);
|
||||||
|
}
|
||||||
|
|
||||||
Uniqueness => alloc.concat(vec![
|
Uniqueness => alloc.concat(vec![
|
||||||
this_is,
|
this_is,
|
||||||
|
|
|
@ -5,6 +5,7 @@ use roc_collections::all::{ImMap, MutSet, SendMap};
|
||||||
use roc_module::ident::{Lowercase, TagName};
|
use roc_module::ident::{Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// A marker that a given Subs has been solved.
|
/// A marker that a given Subs has been solved.
|
||||||
/// The only way to obtain a Solved<Subs> is by running the solver on it.
|
/// The only way to obtain a Solved<Subs> is by running the solver on it.
|
||||||
|
@ -25,8 +26,136 @@ impl<T> Solved<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A custom hash instance, that treats flex vars specially, so that
|
||||||
|
///
|
||||||
|
/// `Foo 100 200 100` hashes to the same as `Foo 300 100 300`
|
||||||
|
///
|
||||||
|
/// i.e., we can rename the flex variables, so long as it happens consistently.
|
||||||
|
/// this is important so we don't generate the same PartialProc twice.
|
||||||
|
impl Hash for SolvedType {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
hash_solved_type_help(self, &mut Vec::new(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SolvedType {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
|
||||||
|
let mut state = DefaultHasher::new();
|
||||||
|
hash_solved_type_help(self, &mut Vec::new(), &mut state);
|
||||||
|
let hash1 = state.finish();
|
||||||
|
|
||||||
|
let mut state = DefaultHasher::new();
|
||||||
|
hash_solved_type_help(other, &mut Vec::new(), &mut state);
|
||||||
|
let hash2 = state.finish();
|
||||||
|
|
||||||
|
hash1 == hash2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_solved_type_help<H: Hasher>(
|
||||||
|
solved_type: &SolvedType,
|
||||||
|
flex_vars: &mut Vec<VarId>,
|
||||||
|
state: &mut H,
|
||||||
|
) {
|
||||||
|
use SolvedType::*;
|
||||||
|
|
||||||
|
match solved_type {
|
||||||
|
Flex(var_id) => {
|
||||||
|
var_id_hash_help(*var_id, flex_vars, state);
|
||||||
|
}
|
||||||
|
Wildcard => "wildcard".hash(state),
|
||||||
|
EmptyRecord => "empty_record".hash(state),
|
||||||
|
EmptyTagUnion => "empty_tag_union".hash(state),
|
||||||
|
Error => "error".hash(state),
|
||||||
|
Func(arguments, closure, result) => {
|
||||||
|
for x in arguments {
|
||||||
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_solved_type_help(closure, flex_vars, state);
|
||||||
|
hash_solved_type_help(result, flex_vars, state);
|
||||||
|
}
|
||||||
|
Apply(name, arguments) => {
|
||||||
|
name.hash(state);
|
||||||
|
for x in arguments {
|
||||||
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Rigid(name) => name.hash(state),
|
||||||
|
Erroneous(problem) => problem.hash(state),
|
||||||
|
Boolean(solved_bool) => solved_bool.hash(state),
|
||||||
|
|
||||||
|
Record { fields, ext } => {
|
||||||
|
for (name, x) in fields {
|
||||||
|
name.hash(state);
|
||||||
|
"record_field".hash(state);
|
||||||
|
hash_solved_type_help(x.as_inner(), flex_vars, state);
|
||||||
|
}
|
||||||
|
hash_solved_type_help(ext, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
TagUnion(tags, ext) => {
|
||||||
|
for (name, arguments) in tags {
|
||||||
|
name.hash(state);
|
||||||
|
for x in arguments {
|
||||||
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash_solved_type_help(ext, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
RecursiveTagUnion(rec, tags, ext) => {
|
||||||
|
var_id_hash_help(*rec, flex_vars, state);
|
||||||
|
for (name, arguments) in tags {
|
||||||
|
name.hash(state);
|
||||||
|
for x in arguments {
|
||||||
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hash_solved_type_help(ext, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Alias(name, arguments, actual) => {
|
||||||
|
name.hash(state);
|
||||||
|
for (name, x) in arguments {
|
||||||
|
name.hash(state);
|
||||||
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
|
}
|
||||||
|
hash_solved_type_help(actual, flex_vars, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostExposedAlias {
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
actual,
|
||||||
|
actual_var,
|
||||||
|
} => {
|
||||||
|
name.hash(state);
|
||||||
|
for (name, x) in arguments {
|
||||||
|
name.hash(state);
|
||||||
|
hash_solved_type_help(x, flex_vars, state);
|
||||||
|
}
|
||||||
|
hash_solved_type_help(actual, flex_vars, state);
|
||||||
|
var_id_hash_help(*actual_var, flex_vars, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn var_id_hash_help<H: Hasher>(var_id: VarId, flex_vars: &mut Vec<VarId>, state: &mut H) {
|
||||||
|
let opt_index = flex_vars.iter().position(|x| *x == var_id);
|
||||||
|
match opt_index {
|
||||||
|
Some(index) => index.hash(state),
|
||||||
|
None => {
|
||||||
|
flex_vars.len().hash(state);
|
||||||
|
flex_vars.push(var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is a fully solved type, with no Variables remaining in it.
|
/// This is a fully solved type, with no Variables remaining in it.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Eq)]
|
||||||
pub enum SolvedType {
|
pub enum SolvedType {
|
||||||
/// A function. The types of its arguments, then the type of its return value.
|
/// A function. The types of its arguments, then the type of its return value.
|
||||||
Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>),
|
Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::pretty_print::Parens;
|
||||||
use crate::subs::{Subs, VarStore, Variable};
|
use crate::subs::{Subs, VarStore, Variable};
|
||||||
use inlinable_string::InlinableString;
|
use inlinable_string::InlinableString;
|
||||||
use roc_collections::all::{union, ImMap, ImSet, Index, MutMap, MutSet, SendMap};
|
use roc_collections::all::{union, ImMap, ImSet, Index, MutMap, MutSet, SendMap};
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
use roc_module::symbol::{Interns, ModuleId, Symbol};
|
||||||
use roc_region::all::{Located, Region};
|
use roc_region::all::{Located, Region};
|
||||||
|
@ -966,6 +966,10 @@ pub enum Reason {
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
arg_index: Index,
|
arg_index: Index,
|
||||||
},
|
},
|
||||||
|
ForeignCallArg {
|
||||||
|
foreign_symbol: ForeignSymbol,
|
||||||
|
arg_index: Index,
|
||||||
|
},
|
||||||
FloatLiteral,
|
FloatLiteral,
|
||||||
IntLiteral,
|
IntLiteral,
|
||||||
NumLiteral,
|
NumLiteral,
|
||||||
|
@ -992,6 +996,7 @@ pub enum Category {
|
||||||
Lookup(Symbol),
|
Lookup(Symbol),
|
||||||
CallResult(Option<Symbol>),
|
CallResult(Option<Symbol>),
|
||||||
LowLevelOpResult(LowLevel),
|
LowLevelOpResult(LowLevel),
|
||||||
|
ForeignCall,
|
||||||
TagApply {
|
TagApply {
|
||||||
tag_name: TagName,
|
tag_name: TagName,
|
||||||
args_count: usize,
|
args_count: usize,
|
||||||
|
|
|
@ -790,7 +790,8 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
||||||
| Str { .. }
|
| Str { .. }
|
||||||
| EmptyRecord
|
| EmptyRecord
|
||||||
| Accessor { .. }
|
| Accessor { .. }
|
||||||
| RunLowLevel { .. } => {}
|
| RunLowLevel { .. }
|
||||||
|
| ForeignCall { .. } => {}
|
||||||
|
|
||||||
Var(symbol) => usage.register_unique(*symbol),
|
Var(symbol) => usage.register_unique(*symbol),
|
||||||
|
|
||||||
|
|
|
@ -266,8 +266,13 @@ mod test_docs {
|
||||||
},
|
},
|
||||||
ModuleEntry {
|
ModuleEntry {
|
||||||
name: "multiline".to_string(),
|
name: "multiline".to_string(),
|
||||||
docs: "<p>Multiline documentation.\nWithout any complex syntax yet!</p>\n"
|
docs: "<p>Multiline documentation.\nWithout any complex syntax yet!</p>\n".to_string(),
|
||||||
.to_string(),
|
}, ModuleEntry {
|
||||||
|
name: "multiparagraph".to_string(),
|
||||||
|
docs: "<p>Multiparagraph documentation.</p>\n<p>Without any complex syntax yet!</p>\n".to_string(),
|
||||||
|
}, ModuleEntry {
|
||||||
|
name: "codeblock".to_string(),
|
||||||
|
docs: "<p>Turns >>> into code block for now.</p>\n<pre><code class=\"language-roc\">codeblock</code></pre>\n".to_string(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
12
docs/tests/fixtures/Interface.roc
vendored
12
docs/tests/fixtures/Interface.roc
vendored
|
@ -1,5 +1,5 @@
|
||||||
interface Test
|
interface Test
|
||||||
exposes [ singleline, multiline ]
|
exposes [ singleline, multiline, multiparagraph, codeblock ]
|
||||||
imports []
|
imports []
|
||||||
|
|
||||||
## Single line documentation.
|
## Single line documentation.
|
||||||
|
@ -9,5 +9,15 @@ singleline : Bool -> Bool
|
||||||
## Without any complex syntax yet!
|
## Without any complex syntax yet!
|
||||||
multiline : Bool -> Bool
|
multiline : Bool -> Bool
|
||||||
|
|
||||||
|
## Multiparagraph documentation.
|
||||||
|
##
|
||||||
|
## Without any complex syntax yet!
|
||||||
|
multiparagraph : Bool -> Bool
|
||||||
|
|
||||||
## No documentation for not exposed function.
|
## No documentation for not exposed function.
|
||||||
notExposed : Bool -> Bool
|
notExposed : Bool -> Bool
|
||||||
|
|
||||||
|
## Turns >>> into code block for now.
|
||||||
|
##
|
||||||
|
## >>> codeblock
|
||||||
|
codeblock : Bool -> Bool
|
||||||
|
|
6
examples/effect/Effect.roc
Normal file
6
examples/effect/Effect.roc
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
platform folkertdev/foo
|
||||||
|
provides [ mainForHost ]
|
||||||
|
requires { main : Effect {} }
|
||||||
|
imports []
|
||||||
|
effects Effect
|
||||||
|
{ putChar : Int -> Effect {}, putLine : Str -> Effect {} }
|
8
examples/effect/Main.roc
Normal file
8
examples/effect/Main.roc
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
app Main provides [ main ] imports [ Effect ]
|
||||||
|
|
||||||
|
main : Effect.Effect {} as Fx
|
||||||
|
main =
|
||||||
|
Effect.putLine "Hello"
|
||||||
|
|> Effect.after \{} -> Effect.putChar 87
|
||||||
|
# |> Effect.after \{} -> Effect.putLine "orld"
|
||||||
|
|
23
examples/effect/platform/Cargo.lock
generated
Normal file
23
examples/effect/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/effect/platform/Cargo.toml
Normal file
13
examples/effect/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/effect/platform/host.c
Normal file
7
examples/effect/platform/host.c
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
extern int rust_main();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
return rust_main();
|
||||||
|
}
|
107
examples/effect/platform/src/lib.rs
Normal file
107
examples/effect/platform/src/lib.rs
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
use roc_std::alloca;
|
||||||
|
use roc_std::RocCallResult;
|
||||||
|
use roc_std::RocStr;
|
||||||
|
use std::alloc::Layout;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#[link_name = "main_1_exposed"]
|
||||||
|
fn roc_main(output: *mut u8) -> ();
|
||||||
|
|
||||||
|
#[link_name = "main_1_size"]
|
||||||
|
fn roc_main_size() -> i64;
|
||||||
|
|
||||||
|
#[link_name = "main_1_Fx_caller"]
|
||||||
|
fn call_Fx(function_pointer: *const u8, closure_data: *const u8, output: *mut u8) -> ();
|
||||||
|
|
||||||
|
#[link_name = "main_1_Fx_size"]
|
||||||
|
fn size_Fx() -> i64;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn roc_fx_putChar(foo: i64) -> () {
|
||||||
|
let character = foo as u8 as char;
|
||||||
|
print!("{}", character);
|
||||||
|
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn roc_fx_putLine(line: RocStr) -> () {
|
||||||
|
let bytes = line.as_slice();
|
||||||
|
let string = unsafe { std::str::from_utf8_unchecked(bytes) };
|
||||||
|
println!("{}", string);
|
||||||
|
|
||||||
|
()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn call_the_closure(function_pointer: *const u8, closure_data_ptr: *const u8) -> i64 {
|
||||||
|
let size = size_Fx() as usize;
|
||||||
|
|
||||||
|
alloca::with_stack_bytes(size, |buffer| {
|
||||||
|
let buffer: *mut std::ffi::c_void = buffer;
|
||||||
|
let buffer: *mut u8 = buffer as *mut u8;
|
||||||
|
|
||||||
|
call_Fx(
|
||||||
|
function_pointer,
|
||||||
|
closure_data_ptr as *const u8,
|
||||||
|
buffer as *mut u8,
|
||||||
|
);
|
||||||
|
|
||||||
|
let output = &*(buffer as *mut RocCallResult<i64>);
|
||||||
|
|
||||||
|
match output.into() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => panic!("failed with {}", e),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn rust_main() -> isize {
|
||||||
|
println!("Running Roc closure");
|
||||||
|
let start_time = SystemTime::now();
|
||||||
|
|
||||||
|
let size = unsafe { roc_main_size() } as usize;
|
||||||
|
let layout = Layout::array::<u8>(size).unwrap();
|
||||||
|
let answer = unsafe {
|
||||||
|
let buffer = std::alloc::alloc(layout);
|
||||||
|
|
||||||
|
roc_main(buffer);
|
||||||
|
|
||||||
|
let output = &*(buffer as *mut RocCallResult<()>);
|
||||||
|
|
||||||
|
match output.into() {
|
||||||
|
Ok(()) => {
|
||||||
|
let function_pointer = {
|
||||||
|
// this is a pointer to the location where the function pointer is stored
|
||||||
|
// we pass just the function pointer
|
||||||
|
let temp = buffer.offset(8) as *const i64;
|
||||||
|
|
||||||
|
(*temp) as *const u8
|
||||||
|
};
|
||||||
|
|
||||||
|
let closure_data_ptr = buffer.offset(16);
|
||||||
|
|
||||||
|
call_the_closure(function_pointer as *const u8, closure_data_ptr as *const u8)
|
||||||
|
}
|
||||||
|
Err(msg) => {
|
||||||
|
std::alloc::dealloc(buffer, layout);
|
||||||
|
|
||||||
|
panic!("Roc failed with message: {}", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
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
|
||||||
|
}
|
|
@ -1,6 +1,4 @@
|
||||||
app Quicksort
|
app Quicksort provides [ quicksort ] imports [ Utils.{swap} ]
|
||||||
provides [ quicksort ]
|
|
||||||
imports [ Utils.{swap} ]
|
|
||||||
|
|
||||||
|
|
||||||
quicksort : List Int -> List Int
|
quicksort : List Int -> List Int
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue