mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Merge branch 'trunk' into builtin-count-graphemes
This commit is contained in:
commit
74b09605a7
@ -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]
|
||||
#[serial(multi_dep_str)]
|
||||
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(
|
||||
&fixture_file("multi-dep-str", "Main.roc"),
|
||||
&[],
|
||||
@ -184,7 +189,7 @@ mod cli_run {
|
||||
fn run_multi_dep_str_optimized() {
|
||||
check_output(
|
||||
&fixture_file("multi-dep-str", "Main.roc"),
|
||||
&[],
|
||||
&["--optimize"],
|
||||
"I am Dep2.str2\n",
|
||||
true,
|
||||
);
|
||||
@ -202,11 +207,11 @@ mod cli_run {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial(multi_dep_str)]
|
||||
#[serial(multi_dep_thunk)]
|
||||
fn run_multi_dep_thunk_optimized() {
|
||||
check_output(
|
||||
&fixture_file("multi-dep-thunk", "Main.roc"),
|
||||
&[],
|
||||
&["--optimize"],
|
||||
"I am Dep2.value2\n",
|
||||
true,
|
||||
);
|
||||
|
@ -11,7 +11,7 @@ use crate::procedure::References;
|
||||
use crate::scope::Scope;
|
||||
use inlinable_string::InlinableString;
|
||||
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::operator::CalledVia;
|
||||
use roc_module::symbol::Symbol;
|
||||
@ -98,6 +98,11 @@ pub enum Expr {
|
||||
args: Vec<(Variable, Expr)>,
|
||||
ret_var: Variable,
|
||||
},
|
||||
ForeignCall {
|
||||
foreign_symbol: ForeignSymbol,
|
||||
args: Vec<(Variable, Expr)>,
|
||||
ret_var: Variable,
|
||||
},
|
||||
|
||||
Closure {
|
||||
function_type: Variable,
|
||||
@ -1170,7 +1175,8 @@ pub fn inline_calls(var_store: &mut VarStore, scope: &mut Scope, expr: Expr) ->
|
||||
| other @ Accessor { .. }
|
||||
| other @ Update { .. }
|
||||
| other @ Var(_)
|
||||
| other @ RunLowLevel { .. } => other,
|
||||
| other @ RunLowLevel { .. }
|
||||
| other @ ForeignCall { .. } => other,
|
||||
|
||||
List {
|
||||
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);
|
||||
}
|
||||
}
|
||||
RunLowLevel { args, .. } => {
|
||||
RunLowLevel { args, .. } | ForeignCall { args, .. } => {
|
||||
for (_, arg) in args.iter_mut() {
|
||||
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(_) => {
|
||||
// Runtime Errors have no constraints because they're going to crash.
|
||||
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) => {
|
||||
// NOTE doesn't currently unregister bound symbols
|
||||
// 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),
|
||||
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 {
|
||||
call_type: ByName(name),
|
||||
full_layout,
|
||||
@ -1116,9 +1148,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
env.arena.alloc(format!("closure_field_access_{}_", index)),
|
||||
)
|
||||
.unwrap(),
|
||||
(other, layout) => {
|
||||
unreachable!("can only index into struct layout {:?} {:?}", other, layout)
|
||||
}
|
||||
(other, layout) => unreachable!(
|
||||
"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()
|
||||
}
|
||||
|
||||
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> {
|
||||
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("\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(_) => {}
|
||||
}
|
||||
}
|
||||
|
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)]
|
||||
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)]
|
||||
pub enum TagName {
|
||||
/// 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 {
|
||||
pub fn as_str(&self) -> &str {
|
||||
&*self.0
|
||||
|
@ -374,6 +374,15 @@ impl<'a> BorrowInfState<'a> {
|
||||
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(_) => {}
|
||||
}
|
||||
}
|
||||
@ -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] {
|
||||
use LowLevel::*;
|
||||
|
||||
|
@ -125,6 +125,9 @@ pub fn occuring_variables_expr(expr: &Expr<'_>, result: &mut MutSet<Symbol>) {
|
||||
RunLowLevel(_, args) => {
|
||||
result.extend(args.iter());
|
||||
}
|
||||
ForeignCall { arguments, .. } => {
|
||||
result.extend(arguments.iter());
|
||||
}
|
||||
|
||||
EmptyArray | RuntimeErrorFunction(_) | Literal(_) => {}
|
||||
}
|
||||
@ -463,6 +466,13 @@ impl<'a> Context<'a> {
|
||||
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 {
|
||||
args: ys,
|
||||
arg_layouts,
|
||||
|
@ -4,7 +4,7 @@ use crate::layout::{Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
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::symbol::{IdentIds, ModuleId, Symbol};
|
||||
use roc_problem::can::RuntimeError;
|
||||
@ -444,7 +444,7 @@ impl<'a> Procs<'a> {
|
||||
// register the pending specialization, so this gets code genned later
|
||||
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(
|
||||
symbol,
|
||||
@ -829,6 +829,11 @@ pub enum Expr<'a> {
|
||||
args: &'a [Symbol],
|
||||
},
|
||||
RunLowLevel(LowLevel, &'a [Symbol]),
|
||||
ForeignCall {
|
||||
foreign_symbol: ForeignSymbol,
|
||||
arguments: &'a [Symbol],
|
||||
ret_layout: Layout<'a>,
|
||||
},
|
||||
|
||||
Tag {
|
||||
tag_layout: Layout<'a>,
|
||||
@ -943,6 +948,17 @@ impl<'a> Expr<'a> {
|
||||
.text(format!("lowlevel {:?} ", lowlevel))
|
||||
.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_name,
|
||||
arguments,
|
||||
@ -1331,8 +1347,27 @@ pub fn specialize_all<'a>(
|
||||
let it = procs.externals_others_need.specs.clone();
|
||||
let it = it
|
||||
.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();
|
||||
|
||||
for (name, solved_type) in it.into_iter() {
|
||||
let partial_proc = match procs.partial_procs.get(&name) {
|
||||
Some(v) => v.clone(),
|
||||
@ -1396,7 +1431,6 @@ pub fn specialize_all<'a>(
|
||||
procs
|
||||
.specialized
|
||||
.insert((name, outside_layout.clone()), InProgress);
|
||||
|
||||
match specialize(
|
||||
env,
|
||||
&mut procs,
|
||||
@ -1407,8 +1441,16 @@ pub fn specialize_all<'a>(
|
||||
) {
|
||||
Ok((proc, 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) => {
|
||||
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 is_valid = matches!(unified, roc_unify::unify::Unified::Success(_));
|
||||
debug_assert!(is_valid);
|
||||
|
||||
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
||||
debug_assert!(is_valid, "unificaton failure for {:?}", proc_name);
|
||||
|
||||
// if this is a closure, add the closure record argument
|
||||
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) =
|
||||
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
|
||||
if let CapturedSymbols::Captured(captured) = captured_symbols {
|
||||
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)]
|
||||
fn build_specialized_proc<'a>(
|
||||
arena: &'a Bump,
|
||||
_proc_name: Symbol,
|
||||
proc_name: Symbol,
|
||||
pattern_symbols: &[Symbol],
|
||||
pattern_layouts: Vec<Layout<'a>>,
|
||||
pattern_layouts: Vec<'a, Layout<'a>>,
|
||||
opt_closure_layout: Option<ClosureLayout<'a>>,
|
||||
ret_layout: Layout<'a>,
|
||||
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
|
||||
@ -1709,7 +1750,8 @@ fn build_specialized_proc<'a>(
|
||||
debug_assert_eq!(
|
||||
pattern_layouts_len + 1,
|
||||
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();
|
||||
@ -1738,16 +1780,22 @@ fn build_specialized_proc<'a>(
|
||||
// make sure there is not arg_closure argument without a closure layout
|
||||
debug_assert!(pattern_symbols.last() != Some(&Symbol::ARG_CLOSURE));
|
||||
|
||||
// since this is not a closure, the number of arguments should match between symbols
|
||||
// and layout
|
||||
debug_assert_eq!(
|
||||
pattern_layouts_len,
|
||||
pattern_symbols.len(),
|
||||
"Tried to zip two vecs with different lengths!"
|
||||
);
|
||||
let proc_args = proc_args.into_bump_slice();
|
||||
use std::cmp::Ordering;
|
||||
match pattern_layouts_len.cmp(&pattern_symbols.len()) {
|
||||
Ordering::Equal => {
|
||||
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 {
|
||||
roc_can::expr::Expr::Var(proc_name) if is_known(&proc_name) => call_by_name(
|
||||
env,
|
||||
procs,
|
||||
fn_var,
|
||||
proc_name,
|
||||
loc_args,
|
||||
layout_cache,
|
||||
assigned,
|
||||
hole,
|
||||
),
|
||||
roc_can::expr::Expr::Var(proc_name) if is_known(&proc_name) => {
|
||||
// a call by a known name
|
||||
call_by_name(
|
||||
env,
|
||||
procs,
|
||||
fn_var,
|
||||
proc_name,
|
||||
loc_args,
|
||||
layout_cache,
|
||||
assigned,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// 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 } => {
|
||||
let op = optimize_low_level(env.subs, op, &args);
|
||||
|
||||
@ -3956,6 +4043,35 @@ fn substitute_in_expr<'a>(
|
||||
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_layout,
|
||||
|
@ -109,10 +109,8 @@ impl<'a> ClosureLayout<'a> {
|
||||
|
||||
use UnionVariant::*;
|
||||
match variant {
|
||||
Never | Unit => {
|
||||
// a max closure size of 0 means this is a standard top-level function
|
||||
Ok(None)
|
||||
}
|
||||
Never => Ok(None),
|
||||
Unit => Ok(None),
|
||||
BoolUnion { .. } => {
|
||||
let closure_layout = ClosureLayout::from_bool(arena);
|
||||
|
||||
|
@ -52,9 +52,9 @@ pub struct AppHeader<'a> {
|
||||
pub struct PlatformHeader<'a> {
|
||||
pub name: Loc<PackageName<'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 effects: Vec<'a, Loc<EffectsEntry<'a>>>,
|
||||
pub effects: Effects<'a>,
|
||||
|
||||
// Potential comments and newlines - these will typically all be empty.
|
||||
pub after_platform_keyword: &'a [CommentOrNewline<'a>],
|
||||
@ -64,23 +64,31 @@ pub struct PlatformHeader<'a> {
|
||||
pub after_requires: &'a [CommentOrNewline<'a>],
|
||||
pub before_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)]
|
||||
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.
|
||||
///
|
||||
/// printLine : Str -> Effect {}
|
||||
Effect {
|
||||
Entry {
|
||||
ident: Loc<&'a str>,
|
||||
spaces_before_colon: &'a [CommentOrNewline<'a>],
|
||||
ann: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
// Spaces
|
||||
SpaceBefore(&'a EffectsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a EffectsEntry<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceBefore(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypedIdent<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
#[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 {
|
||||
EffectsEntry::SpaceBefore(self, spaces)
|
||||
TypedIdent::SpaceBefore(self, spaces)
|
||||
}
|
||||
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' => {
|
||||
state = state.newline()?;
|
||||
|
||||
// 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);
|
||||
match (comment_line_buf.len(), comment_line_buf.chars().next())
|
||||
{
|
||||
(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 => {
|
||||
// Chars can have btye lengths of more than 1!
|
||||
|
@ -30,7 +30,7 @@ macro_rules! loc_parenthetical_expr {
|
||||
then(
|
||||
loc!(and!(
|
||||
between!(
|
||||
ascii_char('(' ),
|
||||
ascii_char(b'(' ),
|
||||
map_with_arena!(
|
||||
space0_around(
|
||||
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!(
|
||||
// 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
|
||||
// e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser
|
||||
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())
|
||||
)
|
||||
))
|
||||
@ -170,7 +170,7 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
one_of!(
|
||||
map_with_arena!(
|
||||
and!(
|
||||
loc!(ascii_char('!')),
|
||||
loc!(ascii_char(b'!')),
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state))
|
||||
),
|
||||
|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!(
|
||||
and!(
|
||||
loc!(ascii_char('-')),
|
||||
loc!(ascii_char(b'-')),
|
||||
loc!(move |arena, state| parse_expr(min_indent, arena, state))
|
||||
),
|
||||
|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!(
|
||||
space0_after(
|
||||
between!(
|
||||
ascii_char('('),
|
||||
ascii_char(b'('),
|
||||
space0_around(loc_pattern(min_indent), min_indent),
|
||||
ascii_char(')')
|
||||
ascii_char(b')')
|
||||
),
|
||||
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
|
||||
/// an "==") and also it can't be followed by '>' (or else it's actually an "=>")
|
||||
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:
|
||||
@ -512,7 +515,7 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
|
||||
),
|
||||
// Annotation
|
||||
skip_first!(
|
||||
ascii_char(':'),
|
||||
ascii_char(b':'),
|
||||
// Spaces after the ':' (at a normal indentation level) and then the type.
|
||||
// The type itself must be indented more than the pattern and ':'
|
||||
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!(
|
||||
skip_first!(
|
||||
// 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.
|
||||
// It may turn out to be malformed, but it is definitely a closure.
|
||||
optional(and!(
|
||||
@ -847,7 +850,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
Attempting::ClosureParams,
|
||||
// Params are comma-separated
|
||||
sep_by1(
|
||||
ascii_char(','),
|
||||
ascii_char(b','),
|
||||
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.
|
||||
// e.g. \User.UserId userId -> ...
|
||||
between!(
|
||||
ascii_char('('),
|
||||
ascii_char(b'('),
|
||||
space0_around(loc_pattern(min_indent), min_indent),
|
||||
ascii_char(')')
|
||||
ascii_char(b')')
|
||||
)
|
||||
)
|
||||
.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>>> {
|
||||
between!(
|
||||
ascii_char('('),
|
||||
ascii_char(b'('),
|
||||
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>> {
|
||||
map!(ascii_char('_'), |_| Pattern::Underscore)
|
||||
map!(ascii_char(b'_'), |_| Pattern::Underscore)
|
||||
}
|
||||
|
||||
fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||
then(
|
||||
collection!(
|
||||
ascii_char('{'),
|
||||
ascii_char(b'{'),
|
||||
move |arena: &'a bumpalo::Bump,
|
||||
state: crate::parser::State<'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.)
|
||||
let (opt_loc_val, state) = crate::parser::optional(either!(
|
||||
skip_first!(
|
||||
ascii_char(':'),
|
||||
ascii_char(b':'),
|
||||
space0_before(loc_pattern(min_indent), min_indent)
|
||||
),
|
||||
skip_first!(
|
||||
ascii_char('?'),
|
||||
ascii_char(b'?'),
|
||||
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))
|
||||
},
|
||||
ascii_char(','),
|
||||
ascii_char('}'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
min_indent
|
||||
),
|
||||
move |_arena, state, loc_patterns| {
|
||||
@ -1250,7 +1253,7 @@ mod when {
|
||||
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>)> {
|
||||
and!(
|
||||
sep_by1(
|
||||
ascii_char('|'),
|
||||
ascii_char(b'|'),
|
||||
space0_around(loc_pattern(min_indent), min_indent),
|
||||
),
|
||||
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,
|
||||
// because otherwise (foo -1) will parse as (foo (Num.neg 1))
|
||||
loc!(number_literal()),
|
||||
loc!(ascii_char('-'))
|
||||
loc!(ascii_char(b'-'))
|
||||
)
|
||||
),
|
||||
one_of!(
|
||||
ascii_char(' '),
|
||||
ascii_char('#'),
|
||||
ascii_char('\n'),
|
||||
ascii_char('>')
|
||||
ascii_char(b' '),
|
||||
ascii_char(b'#'),
|
||||
ascii_char(b'\n'),
|
||||
ascii_char(b'>')
|
||||
),
|
||||
),
|
||||
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::And),
|
||||
map!(ascii_string("||"), |_| BinOp::Or),
|
||||
map!(ascii_char('+'), |_| BinOp::Plus),
|
||||
map!(ascii_char('*'), |_| BinOp::Star),
|
||||
map!(ascii_char('-'), |_| BinOp::Minus),
|
||||
map!(ascii_char(b'+'), |_| BinOp::Plus),
|
||||
map!(ascii_char(b'*'), |_| BinOp::Star),
|
||||
map!(ascii_char(b'-'), |_| BinOp::Minus),
|
||||
map!(ascii_string("//"), |_| BinOp::DoubleSlash),
|
||||
map!(ascii_char('/'), |_| BinOp::Slash),
|
||||
map!(ascii_char(b'/'), |_| BinOp::Slash),
|
||||
map!(ascii_string("<="), |_| BinOp::LessThanOrEq),
|
||||
map!(ascii_char('<'), |_| BinOp::LessThan),
|
||||
map!(ascii_char(b'<'), |_| BinOp::LessThan),
|
||||
map!(ascii_string(">="), |_| BinOp::GreaterThanOrEq),
|
||||
map!(ascii_char('>'), |_| BinOp::GreaterThan),
|
||||
map!(ascii_char('^'), |_| BinOp::Caret),
|
||||
map!(ascii_char(b'>'), |_| BinOp::GreaterThan),
|
||||
map!(ascii_char(b'^'), |_| BinOp::Caret),
|
||||
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>> {
|
||||
let elems = collection!(
|
||||
ascii_char('['),
|
||||
ascii_char(b'['),
|
||||
loc!(expr(min_indent)),
|
||||
ascii_char(','),
|
||||
ascii_char(']'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
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`
|
||||
let (accesses, state) = optional(one_or_more!(skip_first!(
|
||||
ascii_char('.'),
|
||||
ascii_char(b'.'),
|
||||
lowercase_ident()
|
||||
)))
|
||||
.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 -> ...
|
||||
pub fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||
map_with_arena!(
|
||||
skip_first!(ascii_char('@'), global_tag()),
|
||||
skip_first!(ascii_char(b'@'), global_tag()),
|
||||
|arena: &'a Bump, name: &'a str| {
|
||||
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> {
|
||||
global_tag_or_ident(|first_char| first_char.is_alphabetic())
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::ast::{
|
||||
AppHeader, Attempting, CommentOrNewline, Def, EffectsEntry, ExposesEntry, ImportsEntry,
|
||||
InterfaceHeader, Module, PlatformHeader,
|
||||
AppHeader, Attempting, CommentOrNewline, Def, Effects, ExposesEntry, ImportsEntry,
|
||||
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::header::{ModuleName, PackageName};
|
||||
use crate::ident::unqualified_ident;
|
||||
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
|
||||
use crate::parser::{
|
||||
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
|
||||
unexpected_eof, ParseResult, Parser, State,
|
||||
@ -78,7 +78,7 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
parse_package_part,
|
||||
skip_first!(ascii_char('/'), parse_package_part)
|
||||
skip_first!(ascii_char(b'/'), parse_package_part)
|
||||
),
|
||||
|(account, pkg)| { PackageName { account, pkg } }
|
||||
)
|
||||
@ -216,10 +216,7 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||
((before_provides, after_provides), provides),
|
||||
(
|
||||
((before_requires, after_requires), requires),
|
||||
(
|
||||
((before_imports, after_imports), imports),
|
||||
((before_effects, after_effects), effects),
|
||||
),
|
||||
(((before_imports, after_imports), imports), effects),
|
||||
),
|
||||
),
|
||||
)| {
|
||||
@ -236,8 +233,6 @@ fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||
after_requires,
|
||||
before_imports,
|
||||
after_imports,
|
||||
before_effects,
|
||||
after_effects,
|
||||
}
|
||||
},
|
||||
)
|
||||
@ -259,10 +254,10 @@ fn provides<'a>() -> impl Parser<
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
||||
collection!(
|
||||
ascii_char('['),
|
||||
ascii_char(b'['),
|
||||
loc!(exposes_entry()),
|
||||
ascii_char(','),
|
||||
ascii_char(']'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
)
|
||||
)
|
||||
@ -273,16 +268,16 @@ fn requires<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a>>>,
|
||||
Vec<'a, Located<TypedIdent<'a>>>,
|
||||
),
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("requires")), space1(1)),
|
||||
collection!(
|
||||
ascii_char('['),
|
||||
loc!(exposes_entry()),
|
||||
ascii_char(','),
|
||||
ascii_char(']'),
|
||||
ascii_char(b'{'),
|
||||
loc!(typed_ident()),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
1
|
||||
)
|
||||
)
|
||||
@ -299,10 +294,10 @@ fn exposes<'a>() -> impl Parser<
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
||||
collection!(
|
||||
ascii_char('['),
|
||||
ascii_char(b'['),
|
||||
loc!(exposes_entry()),
|
||||
ascii_char(','),
|
||||
ascii_char(']'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
)
|
||||
)
|
||||
@ -319,44 +314,72 @@ fn imports<'a>() -> impl Parser<
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("imports")), space1(1)),
|
||||
collection!(
|
||||
ascii_char('['),
|
||||
ascii_char(b'['),
|
||||
loc!(imports_entry()),
|
||||
ascii_char(','),
|
||||
ascii_char(']'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
1
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn effects<'a>() -> impl Parser<
|
||||
'a,
|
||||
(
|
||||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<EffectsEntry<'a>>>,
|
||||
),
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("effects")), space1(1)),
|
||||
collection!(
|
||||
ascii_char('{'),
|
||||
loc!(effects_entry()),
|
||||
ascii_char(','),
|
||||
ascii_char('}'),
|
||||
fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
||||
move |arena, state| {
|
||||
let (spaces_before_effects_keyword, state) =
|
||||
skip_second!(space1(0), ascii_string("effects")).parse(arena, state)?;
|
||||
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)?;
|
||||
let (entries, state) = collection!(
|
||||
ascii_char(b'{'),
|
||||
loc!(typed_ident()),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
1
|
||||
)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
Ok((
|
||||
Effects {
|
||||
spaces_before_effects_keyword,
|
||||
spaces_after_effects_keyword,
|
||||
spaces_after_type_name,
|
||||
type_name,
|
||||
entries,
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn effects_entry<'a>() -> impl Parser<'a, EffectsEntry<'a>> {
|
||||
// e.g.
|
||||
//
|
||||
// printLine : Str -> Effect {}
|
||||
map!(
|
||||
and!(loc(unqualified_ident()), type_annotation::located(0)),
|
||||
|(ident, ann)| { EffectsEntry::Effect { ident, ann } }
|
||||
)
|
||||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
||||
move |arena, state| {
|
||||
// You must have a field name, e.g. "email"
|
||||
let (ident, state) = loc!(lowercase_ident()).parse(arena, state)?;
|
||||
|
||||
let (spaces_before_colon, state) = space0(0).parse(arena, state)?;
|
||||
|
||||
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)]
|
||||
@ -372,12 +395,12 @@ fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|
||||
module_name(),
|
||||
// e.g. `.{ Task, after}`
|
||||
optional(skip_first!(
|
||||
ascii_char('.'),
|
||||
ascii_char(b'.'),
|
||||
collection!(
|
||||
ascii_char('{'),
|
||||
ascii_char(b'{'),
|
||||
loc!(exposes_entry()),
|
||||
ascii_char(','),
|
||||
ascii_char('}'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
1
|
||||
)
|
||||
))
|
||||
|
@ -433,14 +433,9 @@ fn line_too_long(attempting: Attempting, state: State<'_>) -> (Fail, State<'_>)
|
||||
}
|
||||
|
||||
/// A single ASCII char.
|
||||
pub fn ascii_char<'a>(expected: char) -> impl Parser<'a, ()> {
|
||||
// Make sure this really is an ASCII char!
|
||||
debug_assert!(expected.len_utf8() == 1);
|
||||
|
||||
pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, ()> {
|
||||
move |_arena, state: State<'a>| match state.bytes.first() {
|
||||
Some(&actual) if expected == actual as char => {
|
||||
Ok(((), state.advance_without_indenting(1)?))
|
||||
}
|
||||
Some(&actual) if expected == actual => Ok(((), state.advance_without_indenting(1)?)),
|
||||
Some(_) => Err(unexpected(0, state, Attempting::Keyword)),
|
||||
_ => 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
|
||||
// support empty literals containing newlines or comments, but this
|
||||
// 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!(
|
||||
$crate::parser::sep_by0(
|
||||
$delimiter,
|
||||
@ -1025,8 +1020,8 @@ macro_rules! record_field {
|
||||
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
||||
// (This is true in both literals and types.)
|
||||
let (opt_loc_val, state) = $crate::parser::optional(either!(
|
||||
skip_first!(ascii_char(':'), 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)),
|
||||
skip_first!(ascii_char(b'?'), space0_before($val_parser, $min_indent))
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
@ -1055,10 +1050,10 @@ macro_rules! record_field {
|
||||
macro_rules! record_without_update {
|
||||
($val_parser:expr, $min_indent:expr) => {
|
||||
collection!(
|
||||
ascii_char('{'),
|
||||
ascii_char(b'{'),
|
||||
loc!(record_field!($val_parser, $min_indent)),
|
||||
ascii_char(','),
|
||||
ascii_char('}'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b'}'),
|
||||
$min_indent
|
||||
)
|
||||
};
|
||||
@ -1068,7 +1063,7 @@ macro_rules! record_without_update {
|
||||
macro_rules! record {
|
||||
($val_parser:expr, $min_indent:expr) => {
|
||||
skip_first!(
|
||||
$crate::parser::ascii_char('{'),
|
||||
$crate::parser::ascii_char(b'{'),
|
||||
and!(
|
||||
// You can optionally have an identifier followed by an '&' to
|
||||
// make this a record update, e.g. { Foo.user & username: "blah" }.
|
||||
@ -1084,7 +1079,7 @@ macro_rules! record {
|
||||
)),
|
||||
$min_indent
|
||||
),
|
||||
$crate::parser::ascii_char('&')
|
||||
$crate::parser::ascii_char(b'&')
|
||||
)),
|
||||
loc!(skip_first!(
|
||||
// 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
|
||||
// support empty literals containing newlines or comments, but this
|
||||
// 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!(
|
||||
$crate::parser::sep_by0(
|
||||
$crate::parser::ascii_char(','),
|
||||
$crate::parser::ascii_char(b','),
|
||||
$crate::blankspace::space0_around(
|
||||
loc!(record_field!($val_parser, $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
|
||||
// is not allowed inside a string interpolation.
|
||||
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)?;
|
||||
|
||||
// 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
|
||||
// give a canonicalization error if the digits form
|
||||
// an invalid unicode code point.
|
||||
let (loc_digits, new_state) =
|
||||
between!(ascii_char('('), loc(ascii_hex_digits()), ascii_char(')'))
|
||||
.parse(arena, state)?;
|
||||
let (loc_digits, new_state) = between!(
|
||||
ascii_char(b'('),
|
||||
loc(ascii_hex_digits()),
|
||||
ascii_char(b')')
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// Advance the iterator past the expr we just parsed.
|
||||
for _ in 0..(original_byte_count - new_state.bytes.len()) {
|
||||
|
@ -22,10 +22,10 @@ macro_rules! tag_union {
|
||||
map!(
|
||||
and!(
|
||||
collection!(
|
||||
ascii_char('['),
|
||||
ascii_char(b'['),
|
||||
loc!(tag_type($min_indent)),
|
||||
ascii_char(','),
|
||||
ascii_char(']'),
|
||||
ascii_char(b','),
|
||||
ascii_char(b']'),
|
||||
$min_indent
|
||||
),
|
||||
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,
|
||||
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)
|
||||
})
|
||||
}
|
||||
@ -112,12 +112,12 @@ pub fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnot
|
||||
#[inline(always)]
|
||||
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
between!(
|
||||
ascii_char('('),
|
||||
ascii_char(b'('),
|
||||
space0_around(
|
||||
move |arena, state| expression(min_indent).parse(arena, state),
|
||||
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>| {
|
||||
let (first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
||||
let (rest, state) = zero_or_more!(skip_first!(
|
||||
ascii_char(','),
|
||||
ascii_char(b','),
|
||||
space0_around(term(min_indent), min_indent)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
@ -2345,18 +2345,20 @@ mod test_parse {
|
||||
let arena = Bump::new();
|
||||
let newlines = &[Newline, Newline];
|
||||
let def = Def::Body(
|
||||
arena.alloc(Located::new(4, 4, 0, 1, Identifier("x"))),
|
||||
arena.alloc(Located::new(4, 4, 4, 5, Num("5"))),
|
||||
arena.alloc(Located::new(6, 6, 0, 1, Identifier("x"))),
|
||||
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 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 = &[
|
||||
DocComment("first line of docs"),
|
||||
DocComment(" second line"),
|
||||
DocComment(" third line"),
|
||||
DocComment("fourth line"),
|
||||
DocComment(""),
|
||||
DocComment("sixth line after doc new line"),
|
||||
];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Defs(defs, arena.alloc(loc_ret))),
|
||||
@ -2370,6 +2372,8 @@ mod test_parse {
|
||||
## second line
|
||||
## third line
|
||||
## fourth line
|
||||
##
|
||||
## sixth line after doc new line
|
||||
x = 5
|
||||
|
||||
42
|
||||
|
@ -777,6 +777,16 @@ fn to_expr_report<'b>(
|
||||
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 => {
|
||||
unreachable!("I don't think these can be reached")
|
||||
}
|
||||
@ -954,6 +964,9 @@ fn add_category<'b>(
|
||||
op
|
||||
);
|
||||
}
|
||||
ForeignCall => {
|
||||
panic!("Compiler bug: invalid return type from foreign call",);
|
||||
}
|
||||
|
||||
Uniqueness => alloc.concat(vec![
|
||||
this_is,
|
||||
|
@ -5,6 +5,7 @@ use roc_collections::all::{ImMap, MutSet, SendMap};
|
||||
use roc_module::ident::{Lowercase, TagName};
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_region::all::{Located, Region};
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
/// A marker that a given Subs has been solved.
|
||||
/// 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.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
pub enum SolvedType {
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
Func(Vec<SolvedType>, Box<SolvedType>, Box<SolvedType>),
|
||||
|
@ -3,7 +3,7 @@ use crate::pretty_print::Parens;
|
||||
use crate::subs::{Subs, VarStore, Variable};
|
||||
use inlinable_string::InlinableString;
|
||||
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::symbol::{Interns, ModuleId, Symbol};
|
||||
use roc_region::all::{Located, Region};
|
||||
@ -966,6 +966,10 @@ pub enum Reason {
|
||||
op: LowLevel,
|
||||
arg_index: Index,
|
||||
},
|
||||
ForeignCallArg {
|
||||
foreign_symbol: ForeignSymbol,
|
||||
arg_index: Index,
|
||||
},
|
||||
FloatLiteral,
|
||||
IntLiteral,
|
||||
NumLiteral,
|
||||
@ -992,6 +996,7 @@ pub enum Category {
|
||||
Lookup(Symbol),
|
||||
CallResult(Option<Symbol>),
|
||||
LowLevelOpResult(LowLevel),
|
||||
ForeignCall,
|
||||
TagApply {
|
||||
tag_name: TagName,
|
||||
args_count: usize,
|
||||
|
@ -790,7 +790,8 @@ pub fn annotate_usage(expr: &Expr, usage: &mut VarUsage) {
|
||||
| Str { .. }
|
||||
| EmptyRecord
|
||||
| Accessor { .. }
|
||||
| RunLowLevel { .. } => {}
|
||||
| RunLowLevel { .. }
|
||||
| ForeignCall { .. } => {}
|
||||
|
||||
Var(symbol) => usage.register_unique(*symbol),
|
||||
|
||||
|
@ -266,8 +266,13 @@ mod test_docs {
|
||||
},
|
||||
ModuleEntry {
|
||||
name: "multiline".to_string(),
|
||||
docs: "<p>Multiline documentation.\nWithout any complex syntax yet!</p>\n"
|
||||
.to_string(),
|
||||
docs: "<p>Multiline documentation.\nWithout any complex syntax yet!</p>\n".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
|
||||
exposes [ singleline, multiline ]
|
||||
exposes [ singleline, multiline, multiparagraph, codeblock ]
|
||||
imports []
|
||||
|
||||
## Single line documentation.
|
||||
@ -9,5 +9,15 @@ singleline : Bool -> Bool
|
||||
## Without any complex syntax yet!
|
||||
multiline : Bool -> Bool
|
||||
|
||||
## Multiparagraph documentation.
|
||||
##
|
||||
## Without any complex syntax yet!
|
||||
multiparagraph : Bool -> Bool
|
||||
|
||||
## No documentation for not exposed function.
|
||||
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
|
||||
provides [ quicksort ]
|
||||
imports [ Utils.{swap} ]
|
||||
app Quicksort provides [ quicksort ] imports [ Utils.{swap} ]
|
||||
|
||||
|
||||
quicksort : List Int -> List Int
|
||||
|
Loading…
Reference in New Issue
Block a user