From 07bbe99bdb8a1befb3f4c993d74453d8731d682f Mon Sep 17 00:00:00 2001 From: John Konecny <24961694+jfkonecn@users.noreply.github.com> Date: Sat, 13 Jul 2024 12:37:50 -0400 Subject: [PATCH 01/30] Added html details > summary tag support for website --- www/content/tutorial.md | 6 ++++-- www/public/site.css | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/www/content/tutorial.md b/www/content/tutorial.md index 33d4cdaa4d..a9b0e5163d 100644 --- a/www/content/tutorial.md +++ b/www/content/tutorial.md @@ -476,8 +476,10 @@ third = tuple.2 # ["list"] # tuple destructuring (first, second, third) = ("hello", 42, ["list"]) ``` - -By the way, there are two common ways to pronounce "tuple"—one sounds like "two-pull" and the other rhymes with "supple"—and although no clear consensus has emerged in the programming world, people seem generally accepting when others pronounce it differently than they do. +
+ Pronouncing Tuple + By the way, there are two common ways to pronounce "tuple"—one sounds like "two-pull" and the other rhymes with "supple"—and although no clear consensus has emerged in the programming world, people seem generally accepting when others pronounce it differently than they do. +
## [Pattern Matching](#pattern-matching) {#pattern-matching} diff --git a/www/public/site.css b/www/public/site.css index 50ed749b94..58c31aeac1 100644 --- a/www/public/site.css +++ b/www/public/site.css @@ -63,8 +63,18 @@ p { margin-top: 0; } +details summary { + cursor: pointer; + color: var(--primary-1); +} + +details summary:hover { + color: var(--primary-2); +} + p, -li { +li, +details { max-width: 720px; } @@ -220,7 +230,7 @@ h4 { h1 { font-size: 5rem; - line-height: 1.2; + line-height: 1.2; } h2 { @@ -241,7 +251,8 @@ h2 { .article-layout p, .article-layout li, -.article-layout pre { +.article-layout pre, +.article-layout details { font-size: 20px; } @@ -485,7 +496,8 @@ td:last-child { p, aside, -li { +li, +details { font-size: var(--font-size-normal); line-height: 1.85rem; } From ee7f1e39c78858c3d06ed01698dbb7902988f017 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 20 Jul 2024 19:17:11 -0700 Subject: [PATCH 02/30] move all allocas to the entry block --- crates/compiler/gen_llvm/src/llvm/bitcode.rs | 76 ++------ crates/compiler/gen_llvm/src/llvm/build.rs | 171 +++++++----------- .../compiler/gen_llvm/src/llvm/build_list.rs | 27 +-- crates/compiler/gen_llvm/src/llvm/compare.rs | 8 +- crates/compiler/gen_llvm/src/llvm/expect.rs | 6 +- crates/compiler/gen_llvm/src/llvm/lowlevel.rs | 47 ++--- .../compiler/gen_llvm/src/llvm/refcounting.rs | 7 +- crates/compiler/gen_llvm/src/llvm/struct_.rs | 4 +- 8 files changed, 116 insertions(+), 230 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/bitcode.rs b/crates/compiler/gen_llvm/src/llvm/bitcode.rs index cec013529e..14408ad3fa 100644 --- a/crates/compiler/gen_llvm/src/llvm/bitcode.rs +++ b/crates/compiler/gen_llvm/src/llvm/bitcode.rs @@ -72,18 +72,8 @@ fn call_bitcode_fn_help<'ctx>( .map(|x| { if env.target.operating_system() == roc_target::OperatingSystem::Windows { if x.get_type() == env.context.i128_type().into() { - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - - let alloca = create_entry_block_alloca( - env, - parent, - x.get_type(), - "pass_u128_by_reference", - ); + let alloca = + create_entry_block_alloca(env, x.get_type(), "pass_u128_by_reference"); env.builder.build_store(alloca, *x).unwrap(); @@ -163,7 +153,8 @@ pub fn call_bitcode_fn_fixing_for_convention<'a, 'ctx, 'env>( // when we write an i128 into this (happens in NumToInt), zig expects this pointer to // be 16-byte aligned. Not doing so is UB and will immediately fail on CI - let cc_return_value_ptr = env.builder.new_build_alloca(cc_return_type, "return_value"); + let cc_return_value_ptr = + create_entry_block_alloca(env, cc_return_type, "return_value"); cc_return_value_ptr .as_instruction() .unwrap() @@ -818,14 +809,7 @@ impl BitcodeReturns { BitcodeReturns::List => { let list_type = super::convert::zig_list_type(env); - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - - let result = - create_entry_block_alloca(env, parent, list_type.into(), "list_alloca"); + let result = create_entry_block_alloca(env, list_type, "list_alloca"); arguments.push(result.into()); @@ -834,13 +818,7 @@ impl BitcodeReturns { BitcodeReturns::Str => { let str_type = super::convert::zig_str_type(env); - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - - let result = create_entry_block_alloca(env, parent, str_type.into(), "str_alloca"); + let result = create_entry_block_alloca(env, str_type, "str_alloca"); arguments.push(result.into()); @@ -957,14 +935,8 @@ pub(crate) fn pass_list_to_zig_64bit<'ctx>( env: &Env<'_, 'ctx, '_>, list: BasicValueEnum<'ctx>, ) -> PointerValue<'ctx> { - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - let list_type = super::convert::zig_list_type(env); - let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca"); + let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca"); env.builder.new_build_store(list_alloca, list); @@ -975,14 +947,8 @@ pub(crate) fn pass_list_to_zig_wasm<'ctx>( env: &Env<'_, 'ctx, '_>, list: BasicValueEnum<'ctx>, ) -> PointerValue<'ctx> { - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - let list_type = super::convert::zig_list_type(env); - let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca"); + let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca"); env.builder.new_build_store(list_alloca, list); @@ -993,14 +959,8 @@ pub(crate) fn pass_string_to_zig_wasm<'ctx>( env: &Env<'_, 'ctx, '_>, string: BasicValueEnum<'ctx>, ) -> PointerValue<'ctx> { - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - let string_type = super::convert::zig_str_type(env); - let string_alloca = create_entry_block_alloca(env, parent, string_type.into(), "string_alloca"); + let string_alloca = create_entry_block_alloca(env, string_type, "string_alloca"); env.builder.new_build_store(string_alloca, string); @@ -1225,9 +1185,7 @@ pub(crate) fn call_bitcode_fn_with_record_arg<'ctx>( arg: BasicValueEnum<'ctx>, fn_name: &str, ) -> BasicValueEnum<'ctx> { - let roc_call_alloca = env - .builder - .new_build_alloca(arg.get_type(), "roc_call_alloca"); + let roc_call_alloca = create_entry_block_alloca(env, arg.get_type(), "roc_call_alloca"); env.builder.new_build_store(roc_call_alloca, arg); let fn_val = env.module.get_function(fn_name).unwrap(); @@ -1235,9 +1193,7 @@ pub(crate) fn call_bitcode_fn_with_record_arg<'ctx>( let mut args: Vec> = Vec::with_capacity(fn_val.count_params() as usize); if fn_val.get_first_param().unwrap().is_pointer_value() { // call by pointer - let zig_call_alloca = env - .builder - .new_build_alloca(arg.get_type(), "zig_return_alloca"); + let zig_call_alloca = create_entry_block_alloca(env, arg.get_type(), "zig_return_alloca"); env.builder.new_build_store(zig_call_alloca, arg); args.push(zig_call_alloca.into()); } else if fn_val.count_params() == 1 { @@ -1283,16 +1239,14 @@ pub(crate) fn call_bitcode_fn_returning_record<'ctx>( .module .get_struct_type(bitcode_return_type_name) .unwrap(); - zig_return_alloca = env - .builder - .new_build_alloca(bitcode_return_type, "zig_return_alloca"); + zig_return_alloca = + create_entry_block_alloca(env, bitcode_return_type, "zig_return_alloca"); call_void_bitcode_fn(env, &[zig_return_alloca.into(), arg], fn_name); } else { // direct return let zig_result = call_bitcode_fn(env, &[arg], fn_name); - zig_return_alloca = env - .builder - .new_build_alloca(zig_result.get_type(), "zig_return_alloca"); + zig_return_alloca = + create_entry_block_alloca(env, zig_result.get_type(), "zig_return_alloca"); env.builder.new_build_store(zig_return_alloca, zig_result); } diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 83d04ab780..f673ab828d 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -936,9 +936,7 @@ impl<'a, 'ctx, 'env> Env<'a, 'ctx, 'env> { match env.target.ptr_width() { PtrWidth::Bytes4 => { // we need to pass the string by reference, but we currently hold the value. - let alloca = env - .builder - .new_build_alloca(string.get_type(), "alloca_string"); + let alloca = create_entry_block_alloca(env, string.get_type(), "alloca_string"); env.builder.new_build_store(alloca, string); alloca.into() } @@ -1345,7 +1343,6 @@ fn float_with_precision<'ctx>( pub fn build_exp_literal<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, - parent: FunctionValue<'ctx>, layout: InLayout<'_>, literal: &roc_mono::ir::Literal<'a>, ) -> BasicValueEnum<'ctx> { @@ -1382,18 +1379,14 @@ pub fn build_exp_literal<'a, 'ctx>( } Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(), Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(), - Str(str_literal) => build_string_literal(env, parent, str_literal), + Str(str_literal) => build_string_literal(env, str_literal), } } -fn build_string_literal<'ctx>( - env: &Env<'_, 'ctx, '_>, - parent: FunctionValue<'ctx>, - str_literal: &str, -) -> BasicValueEnum<'ctx> { +fn build_string_literal<'ctx>(env: &Env<'_, 'ctx, '_>, str_literal: &str) -> BasicValueEnum<'ctx> { if str_literal.len() < env.small_str_bytes() as usize { match env.small_str_bytes() { - 24 => small_str_ptr_width_8(env, parent, str_literal).into(), + 24 => small_str_ptr_width_8(env, str_literal).into(), 12 => small_str_ptr_width_4(env, str_literal).into(), _ => unreachable!("incorrect small_str_bytes"), } @@ -1401,7 +1394,7 @@ fn build_string_literal<'ctx>( let ptr = define_global_str_literal_ptr(env, str_literal); let number_of_elements = env.ptr_int().const_int(str_literal.len() as u64, false); - let alloca = const_str_alloca_ptr(env, parent, ptr, number_of_elements, number_of_elements); + let alloca = const_str_alloca_ptr(env, ptr, number_of_elements, number_of_elements); match env.target.ptr_width() { PtrWidth::Bytes4 => { @@ -1415,7 +1408,6 @@ fn build_string_literal<'ctx>( fn const_str_alloca_ptr<'ctx>( env: &Env<'_, 'ctx, '_>, - parent: FunctionValue<'ctx>, ptr: PointerValue<'ctx>, len: IntValue<'ctx>, cap: IntValue<'ctx>, @@ -1424,18 +1416,14 @@ fn const_str_alloca_ptr<'ctx>( let value = typ.const_named_struct(&[ptr.into(), len.into(), cap.into()]); - let alloca = create_entry_block_alloca(env, parent, typ.into(), "const_str_store"); + let alloca = create_entry_block_alloca(env, typ, "const_str_store"); env.builder.new_build_store(alloca, value); alloca } -fn small_str_ptr_width_8<'ctx>( - env: &Env<'_, 'ctx, '_>, - parent: FunctionValue<'ctx>, - str_literal: &str, -) -> PointerValue<'ctx> { +fn small_str_ptr_width_8<'ctx>(env: &Env<'_, 'ctx, '_>, str_literal: &str) -> PointerValue<'ctx> { debug_assert_eq!(env.target.ptr_width() as u8, 8); let mut array = [0u8; 24]; @@ -1456,7 +1444,7 @@ fn small_str_ptr_width_8<'ctx>( let ptr_type = env.context.i8_type().ptr_type(address_space); let ptr = env.builder.new_build_int_to_ptr(ptr, ptr_type, "to_u8_ptr"); - const_str_alloca_ptr(env, parent, ptr, len, cap) + const_str_alloca_ptr(env, ptr, len, cap) } fn small_str_ptr_width_4<'ctx>(env: &Env<'_, 'ctx, '_>, str_literal: &str) -> StructValue<'ctx> { @@ -1653,7 +1641,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( use roc_mono::ir::Expr::*; match expr { - Literal(literal) => build_exp_literal(env, layout_interner, parent, layout, literal), + Literal(literal) => build_exp_literal(env, layout_interner, layout, literal), NullPointer => { let basic_type = basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout)); @@ -1918,7 +1906,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( EmptyArray => empty_polymorphic_list(env), Array { elem_layout, elems } => { - list_literal(env, layout_interner, parent, scope, *elem_layout, elems) + list_literal(env, layout_interner, scope, *elem_layout, elems) } RuntimeErrorFunction(_) => todo!(), @@ -2204,7 +2192,7 @@ pub(crate) fn build_exp_expr<'a, 'ctx>( layout_interner, layout_interner.get_repr(*element_layout), ); - let ptr = entry_block_alloca_zerofill(env, element_type, "stack_value"); + let ptr = create_entry_block_alloca(env, element_type, "stack_value"); if let Some(initializer) = initializer { env.builder @@ -2295,21 +2283,6 @@ fn build_wrapped_tag<'a, 'ctx>( } } -pub fn entry_block_alloca_zerofill<'ctx>( - env: &Env<'_, 'ctx, '_>, - basic_type: BasicTypeEnum<'ctx>, - name: &str, -) -> PointerValue<'ctx> { - let parent = env - .builder - .get_insert_block() - .unwrap() - .get_parent() - .unwrap(); - - create_entry_block_alloca(env, parent, basic_type, name) -} - fn build_tag_field_value<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, @@ -2395,10 +2368,7 @@ fn build_tag<'a, 'ctx>( let roc_union = RocUnion::tagged_from_slices(layout_interner, env.context, tags); - let tag_alloca = env - .builder - .new_build_alloca(roc_union.struct_type(), "tag_alloca"); - + let tag_alloca = create_entry_block_alloca(env, roc_union.struct_type(), "tag_alloca"); roc_union.write_struct_data( env, layout_interner, @@ -2692,8 +2662,6 @@ pub fn get_tag_id<'a, 'ctx>( union_layout: &UnionLayout<'a>, argument: BasicValueEnum<'ctx>, ) -> IntValue<'ctx> { - let builder = env.builder; - let tag_id_layout = union_layout.tag_id_layout(); let tag_id_int_type = basic_type_from_layout( env, @@ -2728,7 +2696,7 @@ pub fn get_tag_id<'a, 'ctx>( let else_block = ctx.append_basic_block(parent, "else"); let cont_block = ctx.append_basic_block(parent, "cont"); - let result = builder.new_build_alloca(tag_id_int_type, "result"); + let result = create_entry_block_alloca(env, tag_id_int_type, "result"); env.builder .new_build_conditional_branch(is_null, then_block, else_block); @@ -2801,7 +2769,7 @@ fn lookup_at_index_ptr<'a, 'ctx>( // A recursive pointer in the loaded structure is stored as a `i64*`, but the loaded layout // might want a more precise structure. As such, cast it to the refined type if needed. - cast_if_necessary_for_opaque_recursive_pointers(env.builder, result, target_loaded_type) + cast_if_necessary_for_opaque_recursive_pointers(env, result, target_loaded_type) } fn union_field_ptr_at_index_help<'a, 'ctx>( @@ -2858,8 +2826,7 @@ fn union_field_ptr_at_index<'a, 'ctx>( // might want a more precise structure. As such, cast it to the refined type if needed. let from_value: BasicValueEnum = result.into(); let to_type: BasicTypeEnum = target_loaded_type; - cast_if_necessary_for_opaque_recursive_pointers(env.builder, from_value, to_type) - .into_pointer_value() + cast_if_necessary_for_opaque_recursive_pointers(env, from_value, to_type).into_pointer_value() } fn reserve_with_refcount_union_as_block_of_memory<'a, 'ctx>( @@ -2932,7 +2899,6 @@ pub fn allocate_with_refcount_help<'a, 'ctx, 'env>( fn list_literal<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, - parent: FunctionValue<'ctx>, scope: &Scope<'a, 'ctx>, element_layout: InLayout<'a>, elems: &[ListLiteralElement], @@ -2989,13 +2955,7 @@ fn list_literal<'a, 'ctx>( for (index, element) in elems.iter().enumerate() { match element { ListLiteralElement::Literal(literal) => { - let val = build_exp_literal( - env, - layout_interner, - parent, - element_layout, - literal, - ); + let val = build_exp_literal(env, layout_interner, element_layout, literal); global_elements.push(val.into_int_value()); } ListLiteralElement::Symbol(symbol) => { @@ -3086,7 +3046,7 @@ fn list_literal<'a, 'ctx>( for (index, element) in elems.iter().enumerate() { let val = match element { ListLiteralElement::Literal(literal) => { - build_exp_literal(env, layout_interner, parent, element_layout, literal) + build_exp_literal(env, layout_interner, element_layout, literal) } ListLiteralElement::Symbol(symbol) => scope.load_symbol(symbol), }; @@ -3118,7 +3078,7 @@ pub fn load_roc_value<'a, 'ctx>( let basic_type = basic_type_from_layout(env, layout_interner, layout); if layout.is_passed_by_reference(layout_interner) { - let alloca = entry_block_alloca_zerofill(env, basic_type, name); + let alloca = create_entry_block_alloca(env, basic_type, name); store_roc_value(env, layout_interner, layout, alloca, source.into()); @@ -3136,7 +3096,7 @@ pub fn use_roc_value<'a, 'ctx>( name: &str, ) -> BasicValueEnum<'ctx> { if layout.is_passed_by_reference(layout_interner) { - let alloca = entry_block_alloca_zerofill( + let alloca = create_entry_block_alloca( env, basic_type_from_layout(env, layout_interner, layout), name, @@ -3576,8 +3536,8 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( remainder, } => { if env.mode.runs_expects() { - let location = build_string_literal(env, parent, source_location); - let source = build_string_literal(env, parent, source); + let location = build_string_literal(env, source_location); + let source = build_string_literal(env, source); let message = scope.load_symbol(symbol); env.call_dbg(env, location, source, message); } @@ -3644,7 +3604,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( } roc_target::PtrWidth::Bytes4 => { // temporary WASM implementation - throw_internal_exception(env, parent, "An expectation failed!"); + throw_internal_exception(env, "An expectation failed!"); } } } else { @@ -3712,7 +3672,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( } roc_target::PtrWidth::Bytes4 => { // temporary WASM implementation - throw_internal_exception(env, parent, "An expectation failed!"); + throw_internal_exception(env, "An expectation failed!"); } } } else { @@ -3844,7 +3804,7 @@ fn equivalent_type_constructors(t1: &BasicTypeEnum, t2: &BasicTypeEnum) -> bool /// This will no longer be necessary and should be removed after we employ opaque pointers from /// LLVM. pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: BasicValueEnum<'ctx>, to_type: BasicTypeEnum<'ctx>, ) -> BasicValueEnum<'ctx> { @@ -3853,7 +3813,7 @@ pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>( && equivalent_type_constructors(&from_value.get_type(), &to_type) { complex_bitcast( - builder, + env, from_value, to_type, "bitcast_for_opaque_recursive_pointer", @@ -3865,39 +3825,33 @@ pub fn cast_if_necessary_for_opaque_recursive_pointers<'ctx>( /// Cast a value to another value of the same (or smaller?) size pub fn cast_basic_basic<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: BasicValueEnum<'ctx>, to_type: BasicTypeEnum<'ctx>, ) -> BasicValueEnum<'ctx> { - complex_bitcast(builder, from_value, to_type, "cast_basic_basic") + complex_bitcast(env, from_value, to_type, "cast_basic_basic") } pub fn complex_bitcast_struct_struct<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: StructValue<'ctx>, to_type: StructType<'ctx>, name: &str, ) -> StructValue<'ctx> { - complex_bitcast(builder, from_value.into(), to_type.into(), name).into_struct_value() + complex_bitcast(env, from_value.into(), to_type.into(), name).into_struct_value() } pub fn cast_block_of_memory_to_tag<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: StructValue<'ctx>, to_type: BasicTypeEnum<'ctx>, ) -> StructValue<'ctx> { - complex_bitcast( - builder, - from_value.into(), - to_type, - "block_of_memory_to_tag", - ) - .into_struct_value() + complex_bitcast(env, from_value.into(), to_type, "block_of_memory_to_tag").into_struct_value() } /// Cast a value to another value of the same (or smaller?) size pub fn complex_bitcast<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: BasicValueEnum<'ctx>, to_type: BasicTypeEnum<'ctx>, name: &str, @@ -3908,7 +3862,8 @@ pub fn complex_bitcast<'ctx>( // we can't use the more straightforward bitcast in all cases // it seems like a bitcast only works on integers and pointers // and crucially does not work not on arrays - return builder + return env + .builder .new_build_pointer_cast( from_value.into_pointer_value(), to_type.into_pointer_type(), @@ -3917,7 +3872,7 @@ pub fn complex_bitcast<'ctx>( .into(); } - complex_bitcast_from_bigger_than_to(builder, from_value, to_type, name) + complex_bitcast_from_bigger_than_to(env, from_value, to_type, name) } /// Check the size of the input and output types. Pretending we have more bytes at a pointer than @@ -3965,14 +3920,14 @@ pub fn complex_bitcast_check_size<'ctx>( let then_answer = { env.builder.position_at_end(then_block); - let result = complex_bitcast_from_bigger_than_to(env.builder, from_value, to_type, name); + let result = complex_bitcast_from_bigger_than_to(env, from_value, to_type, name); env.builder.new_build_unconditional_branch(cont_block); result }; let else_answer = { env.builder.position_at_end(else_block); - let result = complex_bitcast_to_bigger_than_from(env.builder, from_value, to_type, name); + let result = complex_bitcast_to_bigger_than_from(env, from_value, to_type, name); env.builder.new_build_unconditional_branch(cont_block); result }; @@ -3987,13 +3942,15 @@ pub fn complex_bitcast_check_size<'ctx>( } fn complex_bitcast_from_bigger_than_to<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: BasicValueEnum<'ctx>, to_type: BasicTypeEnum<'ctx>, name: &str, ) -> BasicValueEnum<'ctx> { + let builder = env.builder; + // store the value in memory - let argument_pointer = builder.new_build_alloca(from_value.get_type(), "cast_alloca"); + let argument_pointer = create_entry_block_alloca(env, from_value.get_type(), "cast_alloca"); builder.new_build_store(argument_pointer, from_value); // then read it back as a different type @@ -4007,15 +3964,16 @@ fn complex_bitcast_from_bigger_than_to<'ctx>( } fn complex_bitcast_to_bigger_than_from<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, from_value: BasicValueEnum<'ctx>, to_type: BasicTypeEnum<'ctx>, name: &str, ) -> BasicValueEnum<'ctx> { + let builder = env.builder; // reserve space in memory with the return type. This way, if the return type is bigger // than the input type, we don't access invalid memory when later taking a pointer to // the cast value - let storage = builder.new_build_alloca(to_type, "cast_alloca"); + let storage = create_entry_block_alloca(env, to_type, "cast_alloca"); // then cast the pointer to our desired type let from_type_pointer = builder.new_build_pointer_cast( @@ -4035,7 +3993,7 @@ fn complex_bitcast_to_bigger_than_from<'ctx>( /// get the tag id out of a pointer to a wrapped (i.e. stores the tag id at runtime) layout fn get_tag_id_wrapped<'a, 'ctx>( - env: &Env<'a, 'ctx, '_>, + env: &Env<'_, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, union_layout: UnionLayout<'a>, from_value: PointerValue<'ctx>, @@ -4308,12 +4266,18 @@ fn build_switch_ir<'a, 'ctx>( } /// Creates a new stack allocation instruction in the entry block of the function. -pub fn create_entry_block_alloca<'ctx>( +pub fn create_entry_block_alloca<'ctx, T: BasicType<'ctx>>( env: &Env<'_, 'ctx, '_>, - parent: FunctionValue<'_>, - basic_type: BasicTypeEnum<'ctx>, + basic_type: T, name: &str, ) -> PointerValue<'ctx> { + let parent = env + .builder + .get_insert_block() + .expect("builder to be in a block") + .get_parent() + .expect("block to be in a function"); + let builder = env.context.create_builder(); let entry = parent.get_first_basic_block().unwrap(); @@ -5212,7 +5176,7 @@ fn set_jump_and_catch_long_jump<'a, 'ctx>( RocReturn::from_layout(layout_interner, layout) }; let call_result_type = roc_call_result_type(env, return_type.as_basic_type_enum()); - let result_alloca = builder.new_build_alloca(call_result_type, "result"); + let result_alloca = create_entry_block_alloca(env, call_result_type, "result"); let then_block = context.append_basic_block(parent, "then_block"); let catch_block = context.append_basic_block(parent, "catch_block"); @@ -6392,7 +6356,7 @@ fn call_roc_function_help<'a, 'ctx>( arguments.pop(); let result_type = basic_type_from_layout(env, layout_interner, result_layout); - let result_alloca = env.builder.new_build_alloca(result_type, "result_value"); + let result_alloca = create_entry_block_alloca(env, result_type, "result_value"); arguments.push(result_alloca.into()); @@ -6410,7 +6374,7 @@ fn call_roc_function_help<'a, 'ctx>( let mut arguments = Vec::from_iter_in(it, env.arena); let result_type = basic_type_from_layout(env, layout_interner, result_layout); - let result_alloca = entry_block_alloca_zerofill(env, result_type, "result_value"); + let result_alloca = create_entry_block_alloca(env, result_type, "result_value"); arguments.push(result_alloca.into()); @@ -6494,9 +6458,7 @@ pub(crate) fn roc_function_call<'a, 'ctx>( layout_interner.get_repr(lambda_set.runtime_representation()), ); - let closure_data_ptr = env - .builder - .new_build_alloca(closure_data_type, "closure_data_ptr"); + let closure_data_ptr = create_entry_block_alloca(env, closure_data_type, "closure_data_ptr"); store_roc_value( env, @@ -6864,7 +6826,9 @@ fn build_foreign_symbol<'a, 'ctx>( Vec::with_capacity_in(fastcc_parameters.len() + 1, env.arena); let return_pointer = match roc_return { - RocReturn::Return => env.builder.new_build_alloca(return_type, "return_value"), + RocReturn::Return => { + create_entry_block_alloca(env, return_type, "return_value") + } RocReturn::ByPointer => fastcc_parameters.pop().unwrap().into_pointer_value(), }; @@ -6882,9 +6846,8 @@ fn build_foreign_symbol<'a, 'ctx>( // we need to pass this value by-reference; put it into an alloca // and bitcast the reference - let param_alloca = env - .builder - .new_build_alloca(param.get_type(), "param_alloca"); + let param_alloca = + create_entry_block_alloca(env, param.get_type(), "param_alloca"); env.builder.new_build_store(param_alloca, param); let as_cc_type = env.builder.new_build_pointer_cast( @@ -7042,14 +7005,10 @@ fn define_global_str_literal<'ctx>( } } -pub(crate) fn throw_internal_exception<'ctx>( - env: &Env<'_, 'ctx, '_>, - parent: FunctionValue<'ctx>, - message: &str, -) { +pub(crate) fn throw_internal_exception<'ctx>(env: &Env<'_, 'ctx, '_>, message: &str) { let builder = env.builder; - let str = build_string_literal(env, parent, message); + let str = build_string_literal(env, message); env.call_panic(env, str, CrashTag::Roc); diff --git a/crates/compiler/gen_llvm/src/llvm/build_list.rs b/crates/compiler/gen_llvm/src/llvm/build_list.rs index 6bd6cd73db..d2ad307804 100644 --- a/crates/compiler/gen_llvm/src/llvm/build_list.rs +++ b/crates/compiler/gen_llvm/src/llvm/build_list.rs @@ -34,14 +34,8 @@ pub(crate) fn list_symbol_to_c_abi<'a, 'ctx>( scope: &Scope<'a, 'ctx>, symbol: Symbol, ) -> PointerValue<'ctx> { - let parent = env - .builder - .get_insert_block() - .and_then(|b| b.get_parent()) - .unwrap(); - let list_type = zig_list_type(env); - let list_alloca = create_entry_block_alloca(env, parent, list_type.into(), "list_alloca"); + let list_alloca = create_entry_block_alloca(env, list_type, "list_alloca"); let list = scope.load_symbol(&symbol); env.builder.new_build_store(list_alloca, list); @@ -67,9 +61,7 @@ fn pass_element_as_opaque<'a, 'ctx>( ) -> BasicValueEnum<'ctx> { let element_type = basic_type_from_layout(env, layout_interner, layout_interner.get_repr(layout)); - let element_ptr = env - .builder - .new_build_alloca(element_type, "element_to_pass_as_opaque"); + let element_ptr = create_entry_block_alloca(env, element_type, "element_to_pass_as_opaque"); store_roc_value( env, layout_interner, @@ -165,7 +157,7 @@ pub(crate) fn list_get_unsafe<'a, 'ctx>( let elem_index = builder.new_build_int_cast(elem_index, env.ptr_int(), "u64_to_usize"); let ptr_type = elem_type.ptr_type(AddressSpace::default()); // Load the pointer to the array data - let array_data_ptr = load_list_ptr(builder, wrapper_struct, ptr_type); + let array_data_ptr = load_list_ptr(env, wrapper_struct, ptr_type); // Assume the bounds have already been checked earlier // (e.g. by List.get or List.first, which wrap List.#getUnsafe) @@ -404,9 +396,7 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>( layout_interner, layout_interner.get_repr(element_layout), ); - let element_ptr = env - .builder - .new_build_alloca(element_type, "output_element_as_opaque"); + let element_ptr = create_entry_block_alloca(env, element_type, "output_element_as_opaque"); // Assume the bounds have already been checked earlier // (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe) @@ -657,7 +647,7 @@ where let zero = env.ptr_int().const_zero(); // allocate a stack slot for the current index - let index_alloca = builder.new_build_alloca(env.ptr_int(), index_name); + let index_alloca = create_entry_block_alloca(env, env.ptr_int(), index_name); builder.new_build_store(index_alloca, zero); let loop_bb = ctx.append_basic_block(parent, "loop"); @@ -698,18 +688,19 @@ pub(crate) fn empty_polymorphic_list<'ctx>(env: &Env<'_, 'ctx, '_>) -> BasicValu } pub(crate) fn load_list_ptr<'ctx>( - builder: &Builder<'ctx>, + env: &Env<'_, 'ctx, '_>, wrapper_struct: StructValue<'ctx>, ptr_type: PointerType<'ctx>, ) -> PointerValue<'ctx> { // a `*mut u8` pointer - let generic_ptr = builder + let generic_ptr = env + .builder .build_extract_value(wrapper_struct, Builtin::WRAPPER_PTR, "read_list_ptr") .unwrap() .into_pointer_value(); // cast to the expected pointer type - cast_basic_basic(builder, generic_ptr.into(), ptr_type.into()).into_pointer_value() + cast_basic_basic(env, generic_ptr.into(), ptr_type.into()).into_pointer_value() } pub(crate) fn allocate_list<'a, 'ctx>( diff --git a/crates/compiler/gen_llvm/src/llvm/compare.rs b/crates/compiler/gen_llvm/src/llvm/compare.rs index 3d1b5e771b..976e50aed6 100644 --- a/crates/compiler/gen_llvm/src/llvm/compare.rs +++ b/crates/compiler/gen_llvm/src/llvm/compare.rs @@ -14,7 +14,7 @@ use roc_mono::layout::{ Builtin, InLayout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, UnionLayout, }; -use super::build::{load_roc_value, BuilderExt}; +use super::build::{create_entry_block_alloca, load_roc_value, BuilderExt}; use super::convert::{argument_type_from_layout, argument_type_from_union_layout}; use super::lowlevel::dec_binop_with_unchecked; use super::struct_; @@ -529,14 +529,14 @@ fn build_list_eq_help<'a, 'ctx>( let builder = env.builder; let element_type = basic_type_from_layout(env, layout_interner, element_layout); let ptr_type = element_type.ptr_type(AddressSpace::default()); - let ptr1 = load_list_ptr(env.builder, list1, ptr_type); - let ptr2 = load_list_ptr(env.builder, list2, ptr_type); + let ptr1 = load_list_ptr(env, list1, ptr_type); + let ptr2 = load_list_ptr(env, list2, ptr_type); // we know that len1 == len2 let end = len1; // allocate a stack slot for the current index - let index_alloca = builder.new_build_alloca(env.ptr_int(), "index"); + let index_alloca = create_entry_block_alloca(env, env.ptr_int(), "index"); builder.new_build_store(index_alloca, env.ptr_int().const_zero()); let loop_bb = ctx.append_basic_block(parent, "loop"); diff --git a/crates/compiler/gen_llvm/src/llvm/expect.rs b/crates/compiler/gen_llvm/src/llvm/expect.rs index 9923531412..a51101906c 100644 --- a/crates/compiler/gen_llvm/src/llvm/expect.rs +++ b/crates/compiler/gen_llvm/src/llvm/expect.rs @@ -1,6 +1,8 @@ use crate::debug_info_init; use crate::llvm::bitcode::call_str_bitcode_fn; -use crate::llvm::build::{get_tag_id, store_roc_value, tag_pointer_clear_tag_id, Env}; +use crate::llvm::build::{ + create_entry_block_alloca, get_tag_id, store_roc_value, tag_pointer_clear_tag_id, Env, +}; use crate::llvm::build_list::{self, incrementing_elem_loop}; use crate::llvm::convert::{basic_type_from_layout, RocUnion}; use inkwell::builder::Builder; @@ -1030,7 +1032,7 @@ fn build_clone_builtin<'a, 'ctx>( ); // if the element has any pointers, we clone them to this offset - let rest_offset = bd.new_build_alloca(env.ptr_int(), "rest_offset"); + let rest_offset = create_entry_block_alloca(env, env.ptr_int(), "rest_offset"); let element_stack_size = env .ptr_int() diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index 9e5d9adefe..c1f7b583c3 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -225,12 +225,8 @@ pub(crate) fn run_low_level<'a, 'ctx>( let return_type = zig_num_parse_result_type(env, return_type_name); - let zig_return_alloca = create_entry_block_alloca( - env, - parent, - return_type.into(), - "str_to_num", - ); + let zig_return_alloca = + create_entry_block_alloca(env, return_type, "str_to_num"); let (a, b) = pass_list_or_string_to_zig_32bit(env, string.into_struct_value()); @@ -324,7 +320,7 @@ pub(crate) fn run_low_level<'a, 'ctx>( let return_type = zig_num_parse_result_type(env, return_type_name); let zig_return_alloca = - create_entry_block_alloca(env, parent, return_type.into(), "str_to_num"); + create_entry_block_alloca(env, return_type, "str_to_num"); call_void_bitcode_fn( env, @@ -411,9 +407,8 @@ pub(crate) fn run_low_level<'a, 'ctx>( } StrFromUtf8 => { let result_type = env.module.get_struct_type("str.FromUtf8Result").unwrap(); - let result_ptr = env - .builder - .new_build_alloca(result_type, "alloca_utf8_validate_bytes_result"); + let result_ptr = + create_entry_block_alloca(env, result_type, "alloca_utf8_validate_bytes_result"); use roc_target::Architecture::*; match env.target.architecture() { @@ -947,7 +942,6 @@ pub(crate) fn run_low_level<'a, 'ctx>( build_int_unary_op( env, layout_interner, - parent, arg.into_int_value(), int_width, int_type, @@ -1396,9 +1390,7 @@ pub(crate) fn run_low_level<'a, 'ctx>( layout_interner, layout_interner.get_repr(layout), ); - let ptr = env - .builder - .new_build_alloca(basic_type, "unreachable_alloca"); + let ptr = create_entry_block_alloca(env, basic_type, "unreachable_alloca"); env.builder.new_build_store(ptr, basic_type.const_zero()); ptr.into() @@ -1933,7 +1925,7 @@ fn throw_because_overflow(env: &Env<'_, '_, '_>, message: &str) { env.builder.position_at_end(entry); // ends in unreachable, so no return is needed - throw_internal_exception(env, function_value, message); + throw_internal_exception(env, message); function_value } @@ -1973,7 +1965,7 @@ fn dec_alloca<'ctx>(env: &Env<'_, 'ctx, '_>, value: IntValue<'ctx>) -> BasicValu Windows => { let dec_type = zig_dec_type(env); - let alloca = env.builder.new_build_alloca(dec_type, "dec_alloca"); + let alloca = create_entry_block_alloca(env, dec_type, "dec_alloca"); let instruction = alloca.as_instruction_value().unwrap(); instruction.set_alignment(16).unwrap(); @@ -2079,11 +2071,7 @@ fn dec_binary_op<'ctx>( fn_name, ); - let block = env.builder.get_insert_block().expect("to be in a function"); - let parent = block.get_parent().expect("to be in a function"); - - let ptr = - create_entry_block_alloca(env, parent, env.context.i128_type().into(), "to_i128"); + let ptr = create_entry_block_alloca(env, env.context.i128_type(), "to_i128"); env.builder.build_store(ptr, lowr_highr).unwrap(); env.builder @@ -2109,7 +2097,7 @@ fn dec_binop_with_overflow<'ctx>( let rhs = rhs.into_int_value(); let return_type = zig_with_overflow_roc_dec(env); - let return_alloca = env.builder.new_build_alloca(return_type, "return_alloca"); + let return_alloca = create_entry_block_alloca(env, return_type, "return_alloca"); match env.target { Target::LinuxX32 | Target::LinuxX64 | Target::MacX64 => { @@ -2195,7 +2183,7 @@ fn change_with_overflow_to_roc_type<'a, 'ctx>( layout_interner, layout_interner.get_repr(return_layout), ); - let casted = cast_basic_basic(env.builder, val.as_basic_value_enum(), return_type); + let casted = cast_basic_basic(env, val.as_basic_value_enum(), return_type); use_roc_value( env, @@ -2341,7 +2329,6 @@ fn int_type_signed_min(int_type: IntType) -> IntValue { fn build_int_unary_op<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_interner: &STLayoutInterner<'a>, - parent: FunctionValue<'ctx>, arg: IntValue<'ctx>, arg_width: IntWidth, arg_int_type: IntType<'ctx>, @@ -2477,12 +2464,8 @@ fn build_int_unary_op<'a, 'ctx, 'env>( target_int_width.type_name(), ); - let zig_return_alloca = create_entry_block_alloca( - env, - parent, - return_type.into(), - "num_to_int", - ); + let zig_return_alloca = + create_entry_block_alloca(env, return_type, "num_to_int"); call_void_bitcode_fn( env, @@ -2584,7 +2567,6 @@ fn int_neg_raise_on_overflow<'ctx>( throw_internal_exception( env, - parent, "Integer negation overflowed because its argument is the minimum value", ); @@ -2615,7 +2597,6 @@ fn int_abs_raise_on_overflow<'ctx>( throw_internal_exception( env, - parent, "Integer absolute overflowed because its argument is the minimum value", ); @@ -2642,7 +2623,7 @@ fn int_abs_with_overflow<'ctx>( let bits_to_shift = int_type.get_bit_width() as u64 - 1; let shift_val = int_type.const_int(bits_to_shift, false); let shifted = bd.new_build_right_shift(arg, shift_val, true, shifted_name); - let alloca = bd.new_build_alloca(int_type, "#int_abs_help"); + let alloca = create_entry_block_alloca(env, int_type, "#int_abs_help"); // shifted = arg >>> 63 bd.new_build_store(alloca, shifted); diff --git a/crates/compiler/gen_llvm/src/llvm/refcounting.rs b/crates/compiler/gen_llvm/src/llvm/refcounting.rs index 204b4de83e..2ab51a3e9f 100644 --- a/crates/compiler/gen_llvm/src/llvm/refcounting.rs +++ b/crates/compiler/gen_llvm/src/llvm/refcounting.rs @@ -646,7 +646,7 @@ fn call_help<'ctx>( value: BasicValueEnum<'ctx>, ) -> inkwell::values::CallSiteValue<'ctx> { let value = cast_if_necessary_for_opaque_recursive_pointers( - env.builder, + env, value, function.get_params()[0].get_type(), ); @@ -1350,7 +1350,7 @@ fn build_rec_union_recursive_decrement<'a, 'ctx>( // therefore we must cast it to our desired type let union_layout = LayoutRepr::Union(union_layout); let union_type = basic_type_from_layout(env, layout_interner, union_layout); - let recursive_field_ptr = cast_basic_basic(env.builder, ptr_as_i64_ptr, union_type); + let recursive_field_ptr = cast_basic_basic(env, ptr_as_i64_ptr, union_type); deferred_rec.push(recursive_field_ptr); } else if layout_interner.contains_refcounted(*field_layout) { @@ -1823,8 +1823,7 @@ fn modify_refcount_nonrecursive_help<'a, 'ctx>( layout_interner, layout_interner.get_repr(union_layout), ); - let recursive_ptr_field_value = - cast_basic_basic(env.builder, field_value, union_type); + let recursive_ptr_field_value = cast_basic_basic(env, field_value, union_type); modify_refcount_layout_help( env, diff --git a/crates/compiler/gen_llvm/src/llvm/struct_.rs b/crates/compiler/gen_llvm/src/llvm/struct_.rs index caae11494f..3f4f4013d5 100644 --- a/crates/compiler/gen_llvm/src/llvm/struct_.rs +++ b/crates/compiler/gen_llvm/src/llvm/struct_.rs @@ -11,7 +11,7 @@ use roc_mono::layout::{InLayout, LayoutInterner, LayoutRepr, STLayoutInterner}; use crate::llvm::build::{load_roc_value, use_roc_value}; use super::{ - build::{store_roc_value, BuilderExt, Env}, + build::{create_entry_block_alloca, store_roc_value, BuilderExt, Env}, convert::basic_type_from_layout, scope::Scope, }; @@ -253,7 +253,7 @@ fn build_struct_alloca_helper<'a, 'ctx>( // Create the struct_type let struct_type = ctx.struct_type(field_types.into_bump_slice(), false); - let alloca = env.builder.new_build_alloca(struct_type, "struct_alloca"); + let alloca = create_entry_block_alloca(env, struct_type, "struct_alloca"); for (i, (field_expr, field_repr)) in field_expr_repr.into_iter().enumerate() { let dst = From c5a74bdc1254152f5faf9bd12ca3d10f6bf2ca8c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 20 Jul 2024 19:52:11 -0700 Subject: [PATCH 03/30] for referenced values, use allocas instead of phi nodes --- crates/compiler/gen_llvm/src/llvm/build.rs | 38 ++++++++++++++----- crates/compiler/gen_llvm/src/llvm/scope.rs | 28 ++++++++++---- .../compiler/test_gen/src/gen_primitives.rs | 37 ++++++++++++++++++ 3 files changed, 87 insertions(+), 16 deletions(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index f673ab828d..02cb49c6a3 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -3292,14 +3292,18 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( layout_interner.get_repr(param.layout), ); - let phi_type = if layout_interner.is_passed_by_reference(param.layout) { - basic_type.ptr_type(AddressSpace::default()).into() + use crate::llvm::scope::JoinPointArg::*; + let joinpoint_arg = if layout_interner.is_passed_by_reference(param.layout) { + Alloca(create_entry_block_alloca( + env, + basic_type, + "joinpoint_arg_alloca", + )) } else { - basic_type + Phi(env.builder.new_build_phi(basic_type, "joinpointarg")) }; - let phi_node = env.builder.new_build_phi(phi_type, "joinpointarg"); - joinpoint_args.push(phi_node); + joinpoint_args.push(joinpoint_arg); } builder.position_at_end(current); @@ -3351,14 +3355,30 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( Jump(join_point, arguments) => { let builder = env.builder; let context = env.context; - let (cont_block, argument_phi_values) = scope.get_join_point(*join_point).unwrap(); + let (cont_block, joinpoint_args) = scope.get_join_point(*join_point).unwrap(); let current_block = builder.get_insert_block().unwrap(); - for (phi_value, argument) in argument_phi_values.iter().zip(arguments.iter()) { - let (value, _) = scope.load_symbol_and_layout(argument); + for (joinpoint_arg, argument) in joinpoint_args.iter().zip(arguments.iter()) { + let (value, layout) = scope.load_symbol_and_layout(argument); - phi_value.add_incoming(&[(&value, current_block)]); + match joinpoint_arg { + crate::llvm::scope::JoinPointArg::Alloca(alloca) => { + let (size, alignment) = layout_interner.stack_size_and_alignment(layout); + builder + .build_memcpy( + *alloca, + alignment, + value.into_pointer_value(), + alignment, + env.ptr_int().const_int(size as _, false), + ) + .unwrap(); + } + crate::llvm::scope::JoinPointArg::Phi(phi) => { + phi.add_incoming(&[(&value, current_block)]); + } + } } builder.new_build_unconditional_branch(*cont_block); diff --git a/crates/compiler/gen_llvm/src/llvm/scope.rs b/crates/compiler/gen_llvm/src/llvm/scope.rs index a771700e66..d13be53ed9 100644 --- a/crates/compiler/gen_llvm/src/llvm/scope.rs +++ b/crates/compiler/gen_llvm/src/llvm/scope.rs @@ -1,6 +1,6 @@ use inkwell::{ basic_block::BasicBlock, - values::{BasicValueEnum, FunctionValue, PhiValue}, + values::{BasicValue, BasicValueEnum, FunctionValue, PhiValue, PointerValue}, }; use roc_collections::ImMap; use roc_module::symbol::{ModuleId, Symbol}; @@ -13,12 +13,18 @@ use roc_mono::{ pub(crate) struct Scope<'a, 'ctx> { symbols: ImMap, BasicValueEnum<'ctx>)>, top_level_thunks: ImMap, FunctionValue<'ctx>)>, - join_points: ImMap, Vec>)>, + join_points: ImMap, Vec>)>, } #[derive(Debug)] pub(crate) struct JoinPointNotFound; +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum JoinPointArg<'ctx> { + Alloca(PointerValue<'ctx>), + Phi(PhiValue<'ctx>), +} + impl<'a, 'ctx> Scope<'a, 'ctx> { pub fn insert(&mut self, symbol: Symbol, layout: InLayout<'a>, value: BasicValueEnum<'ctx>) { self.symbols.insert(symbol, (layout, value)); @@ -62,7 +68,7 @@ impl<'a, 'ctx> Scope<'a, 'ctx> { &mut self, join_point_id: JoinPointId, bb: BasicBlock<'ctx>, - phis: Vec>, + phis: Vec>, ) { self.join_points.insert(join_point_id, (bb, phis)); } @@ -74,7 +80,7 @@ impl<'a, 'ctx> Scope<'a, 'ctx> { pub fn get_join_point( &self, join_point_id: JoinPointId, - ) -> Option<&(BasicBlock<'ctx>, Vec>)> { + ) -> Option<&(BasicBlock<'ctx>, Vec>)> { self.join_points.get(&join_point_id) } @@ -89,9 +95,17 @@ impl<'a, 'ctx> Scope<'a, 'ctx> { .ok_or(JoinPointNotFound)? .1; - for (phi_value, param) in ref_join_points.iter().zip(parameters.into_iter()) { - let value = phi_value.as_basic_value(); - self.symbols.insert(param.symbol, (param.layout, value)); + for (joinpoint_arg, param) in ref_join_points.iter().zip(parameters.into_iter()) { + match joinpoint_arg { + crate::llvm::scope::JoinPointArg::Alloca(alloca) => { + self.symbols + .insert(param.symbol, (param.layout, alloca.as_basic_value_enum())); + } + crate::llvm::scope::JoinPointArg::Phi(phi) => { + self.symbols + .insert(param.symbol, (param.layout, phi.as_basic_value())); + } + } } Ok(()) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 77ddba776e..8ab12b2841 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -4664,3 +4664,40 @@ fn multiple_uses_of_bool_true_tag_union() { bool ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn issue_6139() { + assert_evals_to!( + indoc!( + r#" + buggy = \node, seen -> + if List.contains seen node then + seen + else + # node = "B" + nextNode = stepNode node + + # node = "C" + buggy nextNode (List.append seen node) + + stepNode = \node -> + when node is + "A" -> "B" + "B" -> "C" + "C" -> "D" + "D" -> "A" + _ -> crash "" + + buggy "A" [] + "# + ), + RocList::from_slice(&[ + RocStr::from("A"), + RocStr::from("B"), + RocStr::from("C"), + RocStr::from("D"), + ]), + RocList + ); +} From a81d4b8b16e9d293c0782384a4279aa79c98385c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 20 Jul 2024 20:23:07 -0700 Subject: [PATCH 04/30] add extra test case from issue --- .../compiler/test_gen/src/gen_primitives.rs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/compiler/test_gen/src/gen_primitives.rs b/crates/compiler/test_gen/src/gen_primitives.rs index 8ab12b2841..60bdf275f8 100644 --- a/crates/compiler/test_gen/src/gen_primitives.rs +++ b/crates/compiler/test_gen/src/gen_primitives.rs @@ -4667,7 +4667,7 @@ fn multiple_uses_of_bool_true_tag_union() { #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] -fn issue_6139() { +fn issue_6139_contains() { assert_evals_to!( indoc!( r#" @@ -4701,3 +4701,30 @@ fn issue_6139() { RocList ); } + +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))] +fn issue_6139_prefixes() { + assert_evals_to!( + indoc!( + r#" + prefixes = \str, soFar -> + if Str.isEmpty str then + soFar + + else + graphemes = + Str.toUtf8 str + |> List.map \c -> Str.fromUtf8 [c] |> Result.withDefault "" + remaining = List.dropFirst graphemes 1 + next = Str.joinWith remaining "" + + prefixes next (List.append soFar str) + + prefixes "abc" [] + "# + ), + RocList::from_slice(&[RocStr::from("abc"), RocStr::from("bc"), RocStr::from("c")]), + RocList + ); +} From 703224b27584151d99e054c04199058d020bddaa Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 20 Jul 2024 21:14:31 -0700 Subject: [PATCH 05/30] fix alloca args overwriting eachother --- crates/compiler/gen_llvm/src/llvm/build.rs | 29 +++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 02cb49c6a3..62a279aaeb 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -3359,27 +3359,54 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( let current_block = builder.get_insert_block().unwrap(); + let mut to_resolve = bumpalo::vec![in env.arena]; for (joinpoint_arg, argument) in joinpoint_args.iter().zip(arguments.iter()) { let (value, layout) = scope.load_symbol_and_layout(argument); match joinpoint_arg { crate::llvm::scope::JoinPointArg::Alloca(alloca) => { let (size, alignment) = layout_interner.stack_size_and_alignment(layout); + // I would like to just do a direct memcpy here, but it is not valid. + // Imagine the case where I just swap two referenced args. + // (a, b) -> jump (b, a) + // This would generate `a` overwriting `b` then `b` overwriting `a`. + // That would lead to both values being `b` + + // Instead we copy to a tmp alloca here. + // After we have all tmp allocas, we copy those to the final output. + let basic_type = basic_type_from_layout( + env, + layout_interner, + layout_interner.get_repr(layout), + ); + let tmp = create_entry_block_alloca(env, basic_type, "tmp_output_for_jmp"); builder .build_memcpy( - *alloca, + tmp, alignment, value.into_pointer_value(), alignment, env.ptr_int().const_int(size as _, false), ) .unwrap(); + to_resolve.push((alloca, tmp, alignment, size)); } crate::llvm::scope::JoinPointArg::Phi(phi) => { phi.add_incoming(&[(&value, current_block)]); } } } + for (alloca, tmp, alignment, size) in to_resolve { + builder + .build_memcpy( + *alloca, + alignment, + tmp, + alignment, + env.ptr_int().const_int(size as _, false), + ) + .unwrap(); + } builder.new_build_unconditional_branch(*cont_block); From e804501a92e7a2226f8ca3ce7ac146170fdc821c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 20 Jul 2024 21:21:43 -0700 Subject: [PATCH 06/30] clippy --- crates/compiler/gen_llvm/src/llvm/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 62a279aaeb..4c2e2b1e10 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -7052,7 +7052,7 @@ fn define_global_str_literal<'ctx>( } } -pub(crate) fn throw_internal_exception<'ctx>(env: &Env<'_, 'ctx, '_>, message: &str) { +pub(crate) fn throw_internal_exception(env: &Env<'_, '_, '_>, message: &str) { let builder = env.builder; let str = build_string_literal(env, message); From bccab348336586898d87091d06201c3ba7894656 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sat, 20 Jul 2024 21:30:25 -0700 Subject: [PATCH 07/30] correct comment --- crates/compiler/gen_llvm/src/llvm/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 4c2e2b1e10..c941fff593 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -3369,7 +3369,7 @@ pub(crate) fn build_exp_stmt<'a, 'ctx>( // I would like to just do a direct memcpy here, but it is not valid. // Imagine the case where I just swap two referenced args. // (a, b) -> jump (b, a) - // This would generate `a` overwriting `b` then `b` overwriting `a`. + // This would generate `b` overwriting `a` then `a` overwriting `b`. // That would lead to both values being `b` // Instead we copy to a tmp alloca here. From 7ba44a5cd0c5c0109dc457809d1dd20747693197 Mon Sep 17 00:00:00 2001 From: Luke Boswell Date: Sun, 21 Jul 2024 19:01:40 +1000 Subject: [PATCH 08/30] remove gui and swift examples --- crates/cli/tests/cli_run.rs | 25 - examples/GuiFormatter.roc | 231 ------- examples/gui/Inconsolata-Regular.ttf | Bin 92124 -> 0 bytes examples/gui/hello-guiBROKEN.roc | 17 - examples/gui/platform/Action.roc | 15 - examples/gui/platform/Cargo.toml | 58 -- examples/gui/platform/Elem.roc | 192 ------ examples/gui/platform/build.rs | 9 - examples/gui/platform/host.c | 3 - examples/gui/platform/main.roc | 15 - examples/gui/platform/src/focus.rs | 172 ----- examples/gui/platform/src/graphics/colors.rs | 50 -- .../platform/src/graphics/lowlevel/buffer.rs | 96 --- .../gui/platform/src/graphics/lowlevel/mod.rs | 5 - .../platform/src/graphics/lowlevel/ortho.rs | 118 ---- .../src/graphics/lowlevel/pipelines.rs | 72 -- .../platform/src/graphics/lowlevel/quad.rs | 33 - .../platform/src/graphics/lowlevel/vertex.rs | 35 - examples/gui/platform/src/graphics/mod.rs | 4 - .../platform/src/graphics/primitives/mod.rs | 2 - .../platform/src/graphics/primitives/rect.rs | 27 - .../platform/src/graphics/primitives/text.rs | 137 ---- .../platform/src/graphics/shaders/quad.wgsl | 60 -- examples/gui/platform/src/graphics/style.rs | 1 - examples/gui/platform/src/gui.rs | 651 ------------------ examples/gui/platform/src/lib.rs | 21 - examples/gui/platform/src/main.rs | 3 - examples/gui/platform/src/rects_and_texts.rs | 70 -- examples/gui/platform/src/roc.rs | 203 ------ examples/inspect-gui.roc | 35 - examples/platform-switching/rocLovesSwift.roc | 3 - .../platform-switching/swift-platform/host.h | 9 - .../swift-platform/host.swift | 62 -- .../swift-platform/main.roc | 9 - examples/swiftui/main.roc | 3 - examples/swiftui/platform/host.h | 9 - examples/swiftui/platform/host.swift | 78 --- examples/swiftui/platform/main.roc | 9 - examples/swiftui/run.sh | 9 - 39 files changed, 2551 deletions(-) delete mode 100644 examples/GuiFormatter.roc delete mode 100644 examples/gui/Inconsolata-Regular.ttf delete mode 100644 examples/gui/hello-guiBROKEN.roc delete mode 100644 examples/gui/platform/Action.roc delete mode 100644 examples/gui/platform/Cargo.toml delete mode 100644 examples/gui/platform/Elem.roc delete mode 100644 examples/gui/platform/build.rs delete mode 100644 examples/gui/platform/host.c delete mode 100644 examples/gui/platform/main.roc delete mode 100644 examples/gui/platform/src/focus.rs delete mode 100644 examples/gui/platform/src/graphics/colors.rs delete mode 100644 examples/gui/platform/src/graphics/lowlevel/buffer.rs delete mode 100644 examples/gui/platform/src/graphics/lowlevel/mod.rs delete mode 100644 examples/gui/platform/src/graphics/lowlevel/ortho.rs delete mode 100644 examples/gui/platform/src/graphics/lowlevel/pipelines.rs delete mode 100644 examples/gui/platform/src/graphics/lowlevel/quad.rs delete mode 100644 examples/gui/platform/src/graphics/lowlevel/vertex.rs delete mode 100644 examples/gui/platform/src/graphics/mod.rs delete mode 100644 examples/gui/platform/src/graphics/primitives/mod.rs delete mode 100644 examples/gui/platform/src/graphics/primitives/rect.rs delete mode 100644 examples/gui/platform/src/graphics/primitives/text.rs delete mode 100644 examples/gui/platform/src/graphics/shaders/quad.wgsl delete mode 100644 examples/gui/platform/src/graphics/style.rs delete mode 100644 examples/gui/platform/src/gui.rs delete mode 100644 examples/gui/platform/src/lib.rs delete mode 100644 examples/gui/platform/src/main.rs delete mode 100644 examples/gui/platform/src/rects_and_texts.rs delete mode 100644 examples/gui/platform/src/roc.rs delete mode 100644 examples/inspect-gui.roc delete mode 100644 examples/platform-switching/rocLovesSwift.roc delete mode 100644 examples/platform-switching/swift-platform/host.h delete mode 100644 examples/platform-switching/swift-platform/host.swift delete mode 100644 examples/platform-switching/swift-platform/main.roc delete mode 100644 examples/swiftui/main.roc delete mode 100644 examples/swiftui/platform/host.h delete mode 100644 examples/swiftui/platform/host.swift delete mode 100644 examples/swiftui/platform/main.roc delete mode 100755 examples/swiftui/run.sh diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 81a5ffcc2c..1809a5d6eb 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -542,16 +542,6 @@ mod cli_run { ) } - #[test] - fn platform_switching_swift() { - test_roc_app_slim( - "examples/platform-switching", - "rocLovesSwift.roc", - "Roc <3 Swift!\n", - UseValgrind::Yes, - ) - } - #[test] fn expects_dev_and_test() { // these are in the same test function so we don't have to worry about race conditions @@ -785,11 +775,6 @@ mod cli_run { ) } - #[test] - fn hello_gui() { - test_roc_app_slim("examples/gui", "hello-guiBROKEN.roc", "", UseValgrind::No) - } - #[test] #[cfg_attr(windows, ignore)] fn quicksort() { @@ -910,11 +895,6 @@ mod cli_run { ) } - #[test] - fn swift_ui() { - test_roc_app_slim("examples/swiftui", "main.roc", "", UseValgrind::No) - } - #[test] #[serial(cli_platform)] #[cfg_attr(windows, ignore)] @@ -1056,11 +1036,6 @@ mod cli_run { ) } - #[test] - fn inspect_gui() { - test_roc_app_slim("examples", "inspect-gui.roc", "", UseValgrind::No) - } - // TODO not sure if this cfg should still be here: #[cfg(not(debug_assertions))] // this is for testing the benchmarks, to perform proper benchmarks see crates/cli/benches/README.md mod test_benchmarks { diff --git a/examples/GuiFormatter.roc b/examples/GuiFormatter.roc deleted file mode 100644 index 146da44008..0000000000 --- a/examples/GuiFormatter.roc +++ /dev/null @@ -1,231 +0,0 @@ -module [ - GuiFormatter, - toGui, -] - -## Creates GUI representations of Roc values, for use in inspect-gui.roc - -## This can't depend on the platform, so I just copied all of this. - -Rgba : { r : F32, g : F32, b : F32, a : F32 } -ButtonStyles : { bgColor : Rgba, borderColor : Rgba, borderWidth : F32, textColor : Rgba } -Elem : [Button Elem ButtonStyles, Col (List Elem), Row (List Elem), Text Str] - -GuiFormatter := { nodes : List Elem } - implements [ - InspectFormatter { - init: init, - list: list, - set: set, - dict: dict, - tag: tag, - tuple: tuple, - record: record, - bool: bool, - str: str, - function: function, - opaque: opaque, - u8: u8, - i8: i8, - u16: u16, - i16: i16, - u32: u32, - i32: i32, - u64: u64, - i64: i64, - u128: u128, - i128: i128, - f32: f32, - f64: f64, - dec: dec, - - }, - ] - -init : {} -> GuiFormatter -init = \{} -> @GuiFormatter { nodes: [] } - -list : list, ElemWalker GuiFormatter list elem, (elem -> Inspector GuiFormatter) -> Inspector GuiFormatter -list = \content, walkFn, toInspector -> - f0 <- Inspect.custom - # Use a temporary buffer for the children nodes - (@GuiFormatter { nodes }) = - init {} - |> \f1 -> - f2, elem <- walkFn content f1 - elem - |> toInspector - |> Inspect.apply f2 - - addNode f0 (Col nodes) - -set : set, ElemWalker GuiFormatter set elem, (elem -> Inspector GuiFormatter) -> Inspector GuiFormatter -set = \content, walkFn, toInspector -> - f0 <- Inspect.custom - # Use a temporary buffer for the children nodes - (@GuiFormatter { nodes }) = - init {} - |> \f1 -> - f2, elem <- walkFn content f1 - elem - |> toInspector - |> Inspect.apply f2 - - addNode f0 (Col nodes) - -dict : dict, KeyValWalker GuiFormatter dict key value, (key -> Inspector GuiFormatter), (value -> Inspector GuiFormatter) -> Inspector GuiFormatter -dict = \d, walkFn, keyToInspector, valueToInspector -> - f0 <- Inspect.custom - # Use a temporary buffer for the children nodes - (@GuiFormatter { nodes }) = - init {} - |> \f1 -> - f2, key, value <- walkFn d f1 - (@GuiFormatter { nodes: innerNodes }) = - init {} - |> \x -> Inspect.apply (keyToInspector key) x - |> addNode (Text ":") - |> \x -> Inspect.apply (valueToInspector value) x - - addNode f2 (Row innerNodes) - - addNode f0 (Col nodes) - -tag : Str, List (Inspector GuiFormatter) -> Inspector GuiFormatter -tag = \name, fields -> - f0 <- Inspect.custom - # Use a temporary buffer for the children nodes - (@GuiFormatter { nodes }) = - init {} - |> addNode (Text name) - |> \f1 -> - f2, fieldInspector <- List.walk fields f1 - Inspect.apply fieldInspector f2 - - addNode f0 (Row nodes) - -tuple : List (Inspector GuiFormatter) -> Inspector GuiFormatter -tuple = \fields -> - f0 <- Inspect.custom - # Use a temporary buffer for the children nodes - (@GuiFormatter { nodes }) = - init {} - |> \f1 -> - f2, fieldInspector <- List.walk fields f1 - Inspect.apply fieldInspector f2 - - addNode f0 (Row nodes) - -record : List { key : Str, value : Inspector GuiFormatter } -> Inspector GuiFormatter -record = \fields -> - f0 <- Inspect.custom - # Use a temporary buffer for the children nodes - (@GuiFormatter { nodes }) = - init {} - |> \f1 -> - f2, { key, value } <- List.walk fields f1 - (@GuiFormatter { nodes: innerNodes }) = - init {} - |> addNode (Text key) - |> addNode (Text ":") - |> \x -> Inspect.apply value x - - addNode f2 (Row innerNodes) - - addNode f0 (Col nodes) - -bool : Bool -> Inspector GuiFormatter -bool = \b -> - if b then - f0 <- Inspect.custom - addNode f0 (Text "true") - else - f0 <- Inspect.custom - addNode f0 (Text "false") - -str : Str -> Inspector GuiFormatter -str = \s -> - f0 <- Inspect.custom - addNode f0 (Text "\"$(s)\"") - -opaque : * -> Inspector GuiFormatter -opaque = \_ -> - f0 <- Inspect.custom - addNode f0 (Text "") - -function : * -> Inspector GuiFormatter -function = \_ -> - f0 <- Inspect.custom - addNode f0 (Text "") - -u8 : U8 -> Inspector GuiFormatter -u8 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -i8 : I8 -> Inspector GuiFormatter -i8 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -u16 : U16 -> Inspector GuiFormatter -u16 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -i16 : I16 -> Inspector GuiFormatter -i16 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -u32 : U32 -> Inspector GuiFormatter -u32 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -i32 : I32 -> Inspector GuiFormatter -i32 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -u64 : U64 -> Inspector GuiFormatter -u64 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -i64 : I64 -> Inspector GuiFormatter -i64 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -u128 : U128 -> Inspector GuiFormatter -u128 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -i128 : I128 -> Inspector GuiFormatter -i128 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -f32 : F32 -> Inspector GuiFormatter -f32 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -f64 : F64 -> Inspector GuiFormatter -f64 = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -dec : Dec -> Inspector GuiFormatter -dec = \num -> - f0 <- Inspect.custom - addNode f0 (num |> Num.toStr |> Text) - -addNode : GuiFormatter, Elem -> GuiFormatter -addNode = \@GuiFormatter { nodes }, node -> - @GuiFormatter { nodes: List.append nodes node } - -toGui : GuiFormatter -> Elem -toGui = \@GuiFormatter { nodes } -> Col nodes diff --git a/examples/gui/Inconsolata-Regular.ttf b/examples/gui/Inconsolata-Regular.ttf deleted file mode 100644 index 3e547460b79225335bbc935a679a59da6dc99eae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92124 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 diff --git a/examples/gui/hello-guiBROKEN.roc b/examples/gui/hello-guiBROKEN.roc deleted file mode 100644 index d5166b4e83..0000000000 --- a/examples/gui/hello-guiBROKEN.roc +++ /dev/null @@ -1,17 +0,0 @@ -app # [pf.Action.{ Action }, pf.Elem.{ button, text, row, col }] - [render] { pf: platform "platform/main.roc" } - -render = - rgba = \r, g, b, a -> { r: r / 255, g: g / 255, b: b / 255, a } - - styles = { bgColor: rgba 100 50 50 1, borderColor: rgba 10 20 30 1, borderWidth: 10, textColor: rgba 220 220 250 1 } - - Col [ - Row [ - Button (Text "Corner ") styles, - Button (Text "Top Mid ") { styles & bgColor: rgba 100 100 50 1 }, - Button (Text "Top Right ") { styles & bgColor: rgba 50 50 150 1 }, - ], - Button (Text "Mid Left ") { styles & bgColor: rgba 150 100 100 1 }, - Button (Text "Bottom Left") { styles & bgColor: rgba 150 50 50 1 }, - ] diff --git a/examples/gui/platform/Action.roc b/examples/gui/platform/Action.roc deleted file mode 100644 index 073c517f58..0000000000 --- a/examples/gui/platform/Action.roc +++ /dev/null @@ -1,15 +0,0 @@ -module [Action, none, update, map] - -Action state : [None, Update state] - -none : Action * -none = None - -update : state -> Action state -update = Update - -map : Action a, (a -> b) -> Action b -map = \action, transform -> - when action is - None -> None - Update state -> Update (transform state) diff --git a/examples/gui/platform/Cargo.toml b/examples/gui/platform/Cargo.toml deleted file mode 100644 index 1110e5ddd0..0000000000 --- a/examples/gui/platform/Cargo.toml +++ /dev/null @@ -1,58 +0,0 @@ -[package] -name = "host" -authors = ["The Roc Contributors"] -edition = "2021" -license = "UPL-1.0" -links = "app" -version = "0.0.1" - -[lib] -name = "host" -path = "src/lib.rs" -crate-type = ["staticlib", "lib"] - -[[bin]] -name = "host" -path = "src/main.rs" - -[dependencies] -arrayvec = "0.7.2" -libc = "0.2" -page_size = "0.4.2" -roc_std = { path = "../../../crates/roc_std" } -cgmath = "0.18.0" -colored = "2.0.0" -copypasta = "0.7.1" -fs_extra = "1.2.0" -futures = "0.3.17" -glyph_brush = "0.7.2" -log = "0.4.14" -nonempty = "0.7.0" -palette = "0.6.0" -pest = "2.1.3" -pest_derive = "2.1.0" -serde = { version = "1.0.130", features = ["derive"] } -snafu = { version = "0.6.10", features = ["backtraces"] } -threadpool = "1.8.1" -wgpu = { git = "https://github.com/gfx-rs/wgpu", rev = "0545e36" } -wgpu_glyph = { git = "https://github.com/Anton-4/wgpu_glyph", rev = "257d109" } -winit = "0.26.1" - -[features] -default = [] - -[dependencies.bytemuck] -features = ["derive"] -version = "1.7.2" - -[workspace] - -# Optimizations based on https://deterministic.space/high-performance-rust.html -[profile.release] -codegen-units = 1 -lto = "thin" - -# debug = true # enable when profiling -[profile.bench] -codegen-units = 1 -lto = "thin" diff --git a/examples/gui/platform/Elem.roc b/examples/gui/platform/Elem.roc deleted file mode 100644 index dbfb4c04d2..0000000000 --- a/examples/gui/platform/Elem.roc +++ /dev/null @@ -1,192 +0,0 @@ -module [Elem, PressEvent, row, col, text, button, none, translate, list] - -import Action exposing [Action] - -Elem state : [ - # PERFORMANCE NOTE: - # If there are 8 or fewer tags here, then on a 64-bit system, the tag can be stored - # in the pointer - for massive memory savings. Try extremely hard to always limit the number - # of tags in this union to 8 or fewer! - Button (ButtonConfig state) (Elem state), - Text Str, - Col (List (Elem state)), - Row (List (Elem state)), - Lazy (Result { state, elem : Elem state } [NotCached] -> { state, elem : Elem state }), - # TODO FIXME: using this definition of Lazy causes a stack overflow in the compiler! - # Lazy (Result (Cached state) [NotCached] -> Cached state), - None, -] - -## Used internally in the type definition of Lazy -Cached state : { state, elem : Elem state } - -ButtonConfig state : { onPress : state, PressEvent -> Action state } - -PressEvent : { button : [Touch, Mouse [Left, Right, Middle]] } - -text : Str -> Elem * -text = \str -> - Text str - -button : { onPress : state, PressEvent -> Action state }, Elem state -> Elem state -button = \config, label -> - Button config label - -row : List (Elem state) -> Elem state -row = \children -> - Row children - -col : List (Elem state) -> Elem state -col = \children -> - Col children - -lazy : state, (state -> Elem state) -> Elem state -lazy = \state, render -> - # This function gets called by the host during rendering. It will - # receive the cached state and element (wrapped in Ok) if we've - # ever rendered this before, and Err otherwise. - Lazy - \result -> - when result is - Ok cached if cached.state == state -> - # If we have a cached value, and the new state is the - # same as the cached one, then we can return exactly - # what we had cached. - cached - - _ -> - # Either the state changed or else we didn't have a - # cached value to use. Either way, we need to render - # with the new state and store that for future use. - { state, elem: render state } - -none : Elem * -none = None # I've often wanted this in elm/html. Usually end up resorting to (Html.text "") - this seems nicer. -## Change an element's state type. -## -## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. -## State : { photo : Photo } -## -## render : State -> Elem State -## render = \state -> -## child : Elem State -## child = -## Photo.render state.photo -## |> Elem.translate .photo &photo -## -## col {} [child, otherElems] -## -translate = \child, toChild, toParent -> - when child is - Text str -> - Text str - - Col elems -> - Col (List.map elems \elem -> translate elem toChild toParent) - - Row elems -> - Row (List.map elems \elem -> translate elem toChild toParent) - - Button config label -> - onPress = \parentState, event -> - toChild parentState - |> config.onPress event - |> Action.map \c -> toParent parentState c - - Button { onPress } (translate label toChild toParent) - - Lazy renderChild -> - Lazy - \parentState -> - { elem, state } = renderChild (toChild parentState) - - { - elem: translate toChild toParent newChild, - state: toParent parentState state, - } - - None -> - None - -## Render a list of elements, using [Elem.translate] on each of them. -## -## Convenient when you have a [List] in your state and want to make -## a [List] of child elements out of it. -## -## TODO: indent the following once https://github.com/roc-lang/roc/issues/2585 is fixed. -## State : { photos : List Photo } -## -## render : State -> Elem State -## render = \state -> -## children : List (Elem State) -## children = -## Elem.list Photo.render state .photos &photos -## -## col {} children -## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed -list : (child -> Elem child), parent, (parent -> List child), (parent, List child -> parent) -> List (Elem parent) -list = \renderChild, parent, toChildren, toParent -> - List.mapWithIndex - (toChildren parent) - \index, child -> - toChild = \par -> List.get (toChildren par) index - - newChild = translateOrDrop - child - toChild - \par, ch -> - toChildren par - |> List.set ch index - |> toParent - - renderChild newChild - -## Internal helper function for Elem.list -## -## Tries to translate a child to a parent, but -## if the child has been removed from the parent, -## drops it. -## -## TODO: format as multiline type annotation once https://github.com/roc-lang/roc/issues/2586 is fixed -translateOrDrop : Elem child, (parent -> Result child *), (parent, child -> parent) -> Elem parent -translateOrDrop = \child, toChild, toParent -> - when child is - Text str -> - Text str - - Col elems -> - Col (List.map elems \elem -> translateOrDrop elem toChild toParent) - - Row elems -> - Row (List.map elems \elem -> translateOrDrop elem toChild toParent) - - Button config label -> - onPress = \parentState, event -> - when toChild parentState is - Ok newChild -> - newChild - |> config.onPress event - |> Action.map \c -> toParent parentState c - - Err _ -> - # The child was removed from the list before this onPress handler resolved. - # (For example, by a previous event handler that fired simultaneously.) - Action.none - - Button { onPress } (translateOrDrop label toChild toParent) - - Lazy childState renderChild -> - Lazy - (toParent childState) - \parentState -> - when toChild parentState is - Ok newChild -> - renderChild newChild - |> translateOrDrop toChild toParent - - Err _ -> - None - - # I don't think this should ever happen in practice. - None -> - None diff --git a/examples/gui/platform/build.rs b/examples/gui/platform/build.rs deleted file mode 100644 index 47763b34c3..0000000000 --- a/examples/gui/platform/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - #[cfg(not(windows))] - println!("cargo:rustc-link-lib=dylib=app"); - - #[cfg(windows)] - println!("cargo:rustc-link-lib=dylib=libapp"); - - println!("cargo:rustc-link-search=."); -} diff --git a/examples/gui/platform/host.c b/examples/gui/platform/host.c deleted file mode 100644 index b9214bcf33..0000000000 --- a/examples/gui/platform/host.c +++ /dev/null @@ -1,3 +0,0 @@ -extern int rust_main(); - -int main() { return rust_main(); } \ No newline at end of file diff --git a/examples/gui/platform/main.roc b/examples/gui/platform/main.roc deleted file mode 100644 index 296de4fbd3..0000000000 --- a/examples/gui/platform/main.roc +++ /dev/null @@ -1,15 +0,0 @@ -platform "gui" - requires {} { render : Elem } - exposes [] - packages {} - imports [] - provides [renderForHost] - -Rgba : { r : F32, g : F32, b : F32, a : F32 } - -ButtonStyles : { bgColor : Rgba, borderColor : Rgba, borderWidth : F32, textColor : Rgba } - -Elem : [Button Elem ButtonStyles, Col (List Elem), Row (List Elem), Text Str] - -renderForHost : Elem -renderForHost = render diff --git a/examples/gui/platform/src/focus.rs b/examples/gui/platform/src/focus.rs deleted file mode 100644 index 71b46b6b1c..0000000000 --- a/examples/gui/platform/src/focus.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::roc::{ElemId, RocElem, RocElemTag}; - -#[derive(Debug, PartialEq, Eq)] -pub struct Focus { - focused: Option, - focused_ancestors: Vec<(ElemId, usize)>, -} - -impl Default for Focus { - fn default() -> Self { - Self { - focused: None, - focused_ancestors: Vec::new(), - } - } -} - -impl Focus { - pub fn focused_elem(&self) -> Option { - self.focused - } - - /// e.g. the user pressed Tab. - /// - /// This is in contrast to next_local, which advances within a button group. - /// For example, if I have three radio buttons in a group, pressing the - /// arrow keys will cycle through them over and over without exiting the group - - /// whereas pressing Tab will cycle through them once and then exit the group. - pub fn next_global(&mut self, root: &RocElem) { - match self.focused { - Some(focused) => { - // while let Some((ancestor_id, index)) = self.focused_ancestors.pop() { - // let ancestor = ancestor_id.elem(); - - // // TODO FIXME - right now this will re-traverse a lot of ground! To prevent this, - // // we should remember past indices searched, and tell the ancestors "hey stop searching when" - // // you reach these indices, because they were already covered previously. - // // One potentially easy way to do this: pass a min_index and max_index, and only look between those! - // // - // // Related idea: instead of doing .pop() here, iterate normally so we can `break;` after storing - // // `new_ancestors = Some(next_ancestors);` - this way, we still have access to the full ancestry, and - // // can maybe even pass it in to make it clear what work has already been done! - // if let Some((new_id, new_ancestors)) = - // Self::next_focusable_sibling(focused, Some(ancestor), Some(index)) - // { - // // We found the next element to focus, so record that. - // self.focused = Some(new_id); - - // // We got a path to the new focusable's ancestor(s), so add them to the path. - // // (This may restore some of the ancestors we've been .pop()-ing as we iterated.) - // self.focused_ancestors.extend(new_ancestors); - - // return; - // } - - // // Need to write a bunch of tests for this, especially tests of focus wrapping around - e.g. - // // what happens if it wraps around to a sibling? What happens if it wraps around to something - // // higher up the tree? Lower down the tree? What if nothing is focusable? - // // A separate question: what if we should have a separate text-to-speech concept separate from focus? - // } - } - None => { - // Nothing was focused in the first place, so try to focus the root. - if root.is_focusable() { - self.focused = Some(root.id()); - self.focused_ancestors = Vec::new(); - } else if let Some((new_id, new_ancestors)) = - Self::next_focusable_sibling(root, None, None) - { - // If the root itself is not focusable, use its next focusable sibling. - self.focused = Some(new_id); - self.focused_ancestors = new_ancestors; - } - - // Regardless of whether we found a focusable Elem, we're done. - return; - } - } - } - - /// Return the next focusable sibling element after this one. - /// If this element has no siblings, or no *next* sibling after the given index - /// (e.g. the given index refers to the last element in a Row element), return None. - fn next_focusable_sibling( - elem: &RocElem, - ancestor: Option<&RocElem>, - opt_index: Option, - ) -> Option<(ElemId, Vec<(ElemId, usize)>)> { - use RocElemTag::*; - - match elem.tag() { - Button | Text => None, - Row | Col => { - let children = unsafe { &elem.entry().row_or_col.children.as_slice() }; - let iter = match opt_index { - Some(focus_index) => children[0..focus_index].iter(), - None => children.iter(), - }; - - for child in iter { - if let Some(focused) = Self::next_focusable_sibling(child, ancestor, None) { - return Some(focused); - } - } - - None - } - } - } -} - -#[test] -fn next_global_button_root() { - use crate::roc::{ButtonStyles, RocElem}; - - let child = RocElem::text(""); - let root = RocElem::button(ButtonStyles::default(), child); - let mut focus = Focus::default(); - - // At first, nothing should be focused. - assert_eq!(focus.focused_elem(), None); - - focus.next_global(&root); - - // Buttons should be focusable, so advancing focus should give the button focus. - assert_eq!(focus.focused_elem(), Some(root.id())); - - // Since the button is at the root, advancing again should maintain focus on it. - focus.next_global(&root); - assert_eq!(focus.focused_elem(), Some(root.id())); -} - -#[test] -fn next_global_text_root() { - let root = RocElem::text(""); - let mut focus = Focus::default(); - - // At first, nothing should be focused. - assert_eq!(focus.focused_elem(), None); - - focus.next_global(&root); - - // Text should not be focusable, so advancing focus should have no effect here. - assert_eq!(focus.focused_elem(), None); - - // Just to double-check, advancing a second time should not change this. - focus.next_global(&root); - assert_eq!(focus.focused_elem(), None); -} - -#[test] -fn next_global_row() { - use crate::roc::{ButtonStyles, RocElem}; - - let child = RocElem::text(""); - let button = RocElem::button(ButtonStyles::default(), child); - let button_id = button.id(); - let root = RocElem::row(&[button] as &[_]); - let mut focus = Focus::default(); - - // At first, nothing should be focused. - assert_eq!(focus.focused_elem(), None); - - focus.next_global(&root); - - // Buttons should be focusable, so advancing focus should give the first button in the row focus. - assert_eq!(focus.focused_elem(), Some(button_id)); - - // Since the button is the only element in the row, advancing again should maintain focus on it. - focus.next_global(&root); - assert_eq!(focus.focused_elem(), Some(button_id)); -} diff --git a/examples/gui/platform/src/graphics/colors.rs b/examples/gui/platform/src/graphics/colors.rs deleted file mode 100644 index e0932a1d69..0000000000 --- a/examples/gui/platform/src/graphics/colors.rs +++ /dev/null @@ -1,50 +0,0 @@ -use cgmath::Vector4; -use palette::{FromColor, Hsv, Srgb}; - -/// This order is optimized for what Roc will send -#[repr(C)] -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct Rgba { - a: f32, - b: f32, - g: f32, - r: f32, -} - -impl Rgba { - pub const WHITE: Self = Self::new(1.0, 1.0, 1.0, 1.0); - - pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { - Self { r, g, b, a } - } - - pub const fn to_array(self) -> [f32; 4] { - [self.r, self.b, self.g, self.a] - } - - pub fn from_hsb(hue: usize, saturation: usize, brightness: usize) -> Self { - Self::from_hsba(hue, saturation, brightness, 1.0) - } - - pub fn from_hsba(hue: usize, saturation: usize, brightness: usize, alpha: f32) -> Self { - let rgb = Srgb::from_color(Hsv::new( - hue as f32, - (saturation as f32) / 100.0, - (brightness as f32) / 100.0, - )); - - Self::new(rgb.red, rgb.green, rgb.blue, alpha) - } -} - -impl From for [f32; 4] { - fn from(rgba: Rgba) -> Self { - rgba.to_array() - } -} - -impl From for Vector4 { - fn from(rgba: Rgba) -> Self { - Vector4::new(rgba.r, rgba.b, rgba.g, rgba.a) - } -} diff --git a/examples/gui/platform/src/graphics/lowlevel/buffer.rs b/examples/gui/platform/src/graphics/lowlevel/buffer.rs deleted file mode 100644 index a5a7f54161..0000000000 --- a/examples/gui/platform/src/graphics/lowlevel/buffer.rs +++ /dev/null @@ -1,96 +0,0 @@ -// Contains parts of https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! - -// Contains parts of https://github.com/iced-rs/iced/blob/adce9e04213803bd775538efddf6e7908d1c605e/wgpu/src/shader/quad.wgsl -// By Héctor Ramón, Iced contributors Licensed under the MIT license. -// The license is included in the LEGAL_DETAILS file in the root directory of this distribution. - -// Thank you Héctor Ramón and Iced contributors! - -use std::mem; - -use super::{quad::Quad, vertex::Vertex}; -use crate::graphics::primitives::rect::RectElt; -use wgpu::util::DeviceExt; - -pub struct RectBuffers { - pub vertex_buffer: wgpu::Buffer, - pub index_buffer: wgpu::Buffer, - pub quad_buffer: wgpu::Buffer, -} - -pub const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; - -const QUAD_VERTS: [Vertex; 4] = [ - Vertex { - _position: [0.0, 0.0], - }, - Vertex { - _position: [1.0, 0.0], - }, - Vertex { - _position: [1.0, 1.0], - }, - Vertex { - _position: [0.0, 1.0], - }, -]; - -pub const MAX_QUADS: usize = 1_000; - -pub fn create_rect_buffers( - gpu_device: &wgpu::Device, - cmd_encoder: &mut wgpu::CommandEncoder, - rects: &[RectElt], -) -> RectBuffers { - let vertex_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&QUAD_VERTS), - usage: wgpu::BufferUsages::VERTEX, - }); - - let index_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&QUAD_INDICES), - usage: wgpu::BufferUsages::INDEX, - }); - - let quad_buffer = gpu_device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: mem::size_of::() as u64 * MAX_QUADS as u64, - usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - let quads: Vec = rects.iter().map(|rect| to_quad(rect)).collect(); - - let buffer_size = (quads.len() as u64) * Quad::SIZE; - - let staging_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: None, - contents: bytemuck::cast_slice(&quads), - usage: wgpu::BufferUsages::COPY_SRC, - }); - - cmd_encoder.copy_buffer_to_buffer(&staging_buffer, 0, &quad_buffer, 0, buffer_size); - - RectBuffers { - vertex_buffer, - index_buffer, - quad_buffer, - } -} - -pub fn to_quad(rect_elt: &RectElt) -> Quad { - Quad { - pos: rect_elt.rect.pos.into(), - width: rect_elt.rect.width, - height: rect_elt.rect.height, - color: (rect_elt.color.to_array()), - border_color: rect_elt.border_color.into(), - border_width: rect_elt.border_width, - } -} diff --git a/examples/gui/platform/src/graphics/lowlevel/mod.rs b/examples/gui/platform/src/graphics/lowlevel/mod.rs deleted file mode 100644 index 0add45385d..0000000000 --- a/examples/gui/platform/src/graphics/lowlevel/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod buffer; -pub mod ortho; -pub mod pipelines; -pub mod vertex; -pub mod quad; diff --git a/examples/gui/platform/src/graphics/lowlevel/ortho.rs b/examples/gui/platform/src/graphics/lowlevel/ortho.rs deleted file mode 100644 index 2f4577871a..0000000000 --- a/examples/gui/platform/src/graphics/lowlevel/ortho.rs +++ /dev/null @@ -1,118 +0,0 @@ -use cgmath::{Matrix4, Ortho}; -use wgpu::util::DeviceExt; -use wgpu::{ - BindGroup, BindGroupLayout, BindGroupLayoutDescriptor, BindGroupLayoutEntry, Buffer, - ShaderStages, -}; - -// orthographic projection is used to transform pixel coords to the coordinate system used by wgpu - -#[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct Uniforms { - // We can't use cgmath with bytemuck directly so we'll have - // to convert the Matrix4 into a 4x4 f32 array - ortho: [[f32; 4]; 4], -} - -impl Uniforms { - fn new(w: u32, h: u32) -> Self { - let ortho: Matrix4 = Ortho:: { - left: 0.0, - right: w as f32, - bottom: h as f32, - top: 0.0, - near: -1.0, - far: 1.0, - } - .into(); - Self { - ortho: ortho.into(), - } - } -} - -// update orthographic buffer according to new window size -pub fn update_ortho_buffer( - inner_width: u32, - inner_height: u32, - gpu_device: &wgpu::Device, - ortho_buffer: &Buffer, - cmd_queue: &wgpu::Queue, -) { - let new_uniforms = Uniforms::new(inner_width, inner_height); - - let new_ortho_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Ortho uniform buffer"), - contents: bytemuck::cast_slice(&[new_uniforms]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_SRC, - }); - - // get a command encoder for the current frame - let mut encoder = gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Resize"), - }); - - // overwrite the new buffer over the old one - encoder.copy_buffer_to_buffer( - &new_ortho_buffer, - 0, - ortho_buffer, - 0, - (std::mem::size_of::() * vec![new_uniforms].as_slice().len()) - as wgpu::BufferAddress, - ); - - cmd_queue.submit(Some(encoder.finish())); -} - -#[derive(Debug)] -pub struct OrthoResources { - pub buffer: Buffer, - pub bind_group_layout: BindGroupLayout, - pub bind_group: BindGroup, -} - -pub fn init_ortho( - inner_width: u32, - inner_height: u32, - gpu_device: &wgpu::Device, -) -> OrthoResources { - let uniforms = Uniforms::new(inner_width, inner_height); - - let ortho_buffer = gpu_device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Ortho uniform buffer"), - contents: bytemuck::cast_slice(&[uniforms]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - }); - - // bind groups consist of extra resources that are provided to the shaders - let ortho_bind_group_layout = gpu_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - entries: &[BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::VERTEX, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, - }, - count: None, - }], - label: Some("Ortho bind group layout"), - }); - - let ortho_bind_group = gpu_device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &ortho_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: ortho_buffer.as_entire_binding(), - }], - label: Some("Ortho bind group"), - }); - - OrthoResources { - buffer: ortho_buffer, - bind_group_layout: ortho_bind_group_layout, - bind_group: ortho_bind_group, - } -} diff --git a/examples/gui/platform/src/graphics/lowlevel/pipelines.rs b/examples/gui/platform/src/graphics/lowlevel/pipelines.rs deleted file mode 100644 index a0dc7908ec..0000000000 --- a/examples/gui/platform/src/graphics/lowlevel/pipelines.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::ortho::{init_ortho, OrthoResources}; -use super::quad::Quad; -use super::vertex::Vertex; -use std::borrow::Cow; - -pub struct RectResources { - pub pipeline: wgpu::RenderPipeline, - pub ortho: OrthoResources, -} - -pub fn make_rect_pipeline( - gpu_device: &wgpu::Device, - surface_config: &wgpu::SurfaceConfiguration, -) -> RectResources { - let ortho = init_ortho(surface_config.width, surface_config.height, gpu_device); - - let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - bind_group_layouts: &[&ortho.bind_group_layout], - push_constant_ranges: &[], - }); - let pipeline = create_render_pipeline( - gpu_device, - &pipeline_layout, - surface_config.format, - &wgpu::ShaderModuleDescriptor { - label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("../shaders/quad.wgsl"))), - }, - ); - - RectResources { pipeline, ortho } -} - -pub fn create_render_pipeline( - device: &wgpu::Device, - layout: &wgpu::PipelineLayout, - color_format: wgpu::TextureFormat, - shader_module_desc: &wgpu::ShaderModuleDescriptor, -) -> wgpu::RenderPipeline { - let shader = device.create_shader_module(shader_module_desc); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render pipeline"), - layout: Some(layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::DESC, Quad::DESC], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[wgpu::ColorTargetState { - format: color_format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - operation: wgpu::BlendOperation::Add, - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - }, - alpha: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrites::ALL, - }], - }), - primitive: wgpu::PrimitiveState::default(), - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }) -} diff --git a/examples/gui/platform/src/graphics/lowlevel/quad.rs b/examples/gui/platform/src/graphics/lowlevel/quad.rs deleted file mode 100644 index e8c1f1b568..0000000000 --- a/examples/gui/platform/src/graphics/lowlevel/quad.rs +++ /dev/null @@ -1,33 +0,0 @@ - - -/// A polygon with 4 corners -#[repr(C)] -#[derive(Copy, Clone)] -pub struct Quad { - pub pos: [f32; 2], - pub width: f32, - pub height: f32, - pub color: [f32; 4], - pub border_color: [f32; 4], - pub border_width: f32, -} - -// Safety: repr(C), and as defined will not having padding. -unsafe impl bytemuck::Pod for Quad {} -unsafe impl bytemuck::Zeroable for Quad {} - -impl Quad { - pub const SIZE: wgpu::BufferAddress = std::mem::size_of::() as wgpu::BufferAddress; - pub const DESC: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { - array_stride: Self::SIZE, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &wgpu::vertex_attr_array!( - 1 => Float32x2, - 2 => Float32, - 3 => Float32, - 4 => Float32x4, - 5 => Float32x4, - 6 => Float32, - ), - }; -} diff --git a/examples/gui/platform/src/graphics/lowlevel/vertex.rs b/examples/gui/platform/src/graphics/lowlevel/vertex.rs deleted file mode 100644 index aa45bb7fb7..0000000000 --- a/examples/gui/platform/src/graphics/lowlevel/vertex.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Inspired by https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the LEGAL_DETAILS -// file in the root directory of this distribution. -// -// Thank you, Benjamin! - -// Inspired by https://github.com/iced-rs/iced/blob/adce9e04213803bd775538efddf6e7908d1c605e/wgpu/src/shader/quad.wgsl -// By Héctor Ramón, Iced contributors Licensed under the MIT license. -// The license is included in the LEGAL_DETAILS file in the root directory of this distribution. - -// Thank you Héctor Ramón and Iced contributors! -use bytemuck::{Pod, Zeroable}; - - -#[repr(C)] -#[derive(Copy, Clone, Zeroable, Pod)] -pub struct Vertex { - pub _position: [f32; 2], -} - -impl Vertex { - pub const SIZE: wgpu::BufferAddress = std::mem::size_of::() as wgpu::BufferAddress; - pub const DESC: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { - array_stride: Self::SIZE, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[ - // position - wgpu::VertexAttribute { - offset: 0, - shader_location: 0, - format: wgpu::VertexFormat::Float32x2, - }, - ], - }; -} diff --git a/examples/gui/platform/src/graphics/mod.rs b/examples/gui/platform/src/graphics/mod.rs deleted file mode 100644 index 0eb7fcd6da..0000000000 --- a/examples/gui/platform/src/graphics/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod colors; -pub mod lowlevel; -pub mod primitives; -pub mod style; diff --git a/examples/gui/platform/src/graphics/primitives/mod.rs b/examples/gui/platform/src/graphics/primitives/mod.rs deleted file mode 100644 index a9adb18862..0000000000 --- a/examples/gui/platform/src/graphics/primitives/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod rect; -pub mod text; diff --git a/examples/gui/platform/src/graphics/primitives/rect.rs b/examples/gui/platform/src/graphics/primitives/rect.rs deleted file mode 100644 index 8183fc6f7a..0000000000 --- a/examples/gui/platform/src/graphics/primitives/rect.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::graphics::colors::Rgba; -use cgmath::Vector2; - -#[derive(Debug, Copy, Clone)] -pub struct RectElt { - pub rect: Rect, - pub color: Rgba, - pub border_width: f32, - pub border_color: Rgba, -} - -/// These fields are ordered this way because in Roc, the corresponding stuct is: -/// -/// { top : F32, left : F32, width : F32, height : F32 } -/// -/// alphabetically, that's { height, left, top, width } - which works out to the same as: -/// -/// struct Rect { height: f32, pos: Vector2, width: f32 } -/// -/// ...because Vector2 is a repr(C) struct of { x: f32, y: f32 } -#[derive(Debug, Copy, Clone)] -#[repr(C)] -pub struct Rect { - pub height: f32, - pub pos: Vector2, - pub width: f32, -} diff --git a/examples/gui/platform/src/graphics/primitives/text.rs b/examples/gui/platform/src/graphics/primitives/text.rs deleted file mode 100644 index c746063b77..0000000000 --- a/examples/gui/platform/src/graphics/primitives/text.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Adapted from https://github.com/sotrh/learn-wgpu -// by Benjamin Hansen - license information can be found in the COPYRIGHT -// file in the root directory of this distribution. -// -// Thank you, Benjamin! - -use crate::graphics::colors::Rgba; -use crate::graphics::style::DEFAULT_FONT_SIZE; -use ab_glyph::{FontArc, Glyph, InvalidFont}; -use cgmath::{Vector2, Vector4}; -use glyph_brush::OwnedSection; -use wgpu_glyph::{ab_glyph, GlyphBrush, GlyphBrushBuilder, Section}; - -use super::rect::Rect; - -#[derive(Debug)] -pub struct Text<'a> { - pub position: Vector2, - pub area_bounds: Vector2, - pub color: Rgba, - pub text: &'a str, - pub size: f32, - pub visible: bool, - pub centered: bool, -} - -impl<'a> Default for Text<'a> { - fn default() -> Self { - Self { - position: (0.0, 0.0).into(), - area_bounds: (std::f32::INFINITY, std::f32::INFINITY).into(), - color: Rgba::WHITE, - text: "", - size: DEFAULT_FONT_SIZE, - visible: true, - centered: false, - } - } -} - -pub fn layout_from_text(text: &Text) -> wgpu_glyph::Layout { - wgpu_glyph::Layout::default().h_align(if text.centered { - wgpu_glyph::HorizontalAlign::Center - } else { - wgpu_glyph::HorizontalAlign::Left - }) -} - -fn section_from_text<'a>( - text: &'a Text, - layout: wgpu_glyph::Layout, -) -> wgpu_glyph::Section<'a> { - Section { - screen_position: text.position.into(), - bounds: text.area_bounds.into(), - layout, - ..Section::default() - } - .add_text( - wgpu_glyph::Text::new(text.text) - .with_color(text.color) - .with_scale(text.size), - ) -} - -pub fn owned_section_from_text(text: &Text) -> OwnedSection { - let layout = layout_from_text(text); - - OwnedSection { - screen_position: text.position.into(), - bounds: text.area_bounds.into(), - layout, - ..OwnedSection::default() - } - .add_text( - glyph_brush::OwnedText::new(text.text) - .with_color(Vector4::from(text.color)) - .with_scale(text.size), - ) -} - -pub fn owned_section_from_glyph_texts( - text: Vec, - screen_position: (f32, f32), - area_bounds: (f32, f32), - layout: wgpu_glyph::Layout, -) -> glyph_brush::OwnedSection { - glyph_brush::OwnedSection { - screen_position, - bounds: area_bounds, - layout, - text, - } -} - -pub fn queue_text_draw(text: &Text, glyph_brush: &mut GlyphBrush<()>) { - let layout = layout_from_text(text); - - let section = section_from_text(text, layout); - - glyph_brush.queue(section.clone()); -} - -fn glyph_to_rect(glyph: &wgpu_glyph::SectionGlyph) -> Rect { - let position = glyph.glyph.position; - let px_scale = glyph.glyph.scale; - let width = glyph_width(&glyph.glyph); - let height = px_scale.y; - let top_y = glyph_top_y(&glyph.glyph); - - Rect { - pos: [position.x, top_y].into(), - width, - height, - } -} - -pub fn glyph_top_y(glyph: &Glyph) -> f32 { - let height = glyph.scale.y; - - glyph.position.y - height * 0.75 -} - -pub fn glyph_width(glyph: &Glyph) -> f32 { - glyph.scale.x * 0.4765 -} - -pub fn build_glyph_brush( - gpu_device: &wgpu::Device, - render_format: wgpu::TextureFormat, -) -> Result, InvalidFont> { - let inconsolata = FontArc::try_from_slice(include_bytes!( - "../../../../Inconsolata-Regular.ttf" - ))?; - - Ok(GlyphBrushBuilder::using_font(inconsolata).build(gpu_device, render_format)) -} diff --git a/examples/gui/platform/src/graphics/shaders/quad.wgsl b/examples/gui/platform/src/graphics/shaders/quad.wgsl deleted file mode 100644 index a561e2fc24..0000000000 --- a/examples/gui/platform/src/graphics/shaders/quad.wgsl +++ /dev/null @@ -1,60 +0,0 @@ - - -struct Globals { - ortho: mat4x4; -}; - -@group(0) -@binding(0) -var globals: Globals; - -struct VertexInput { - @location(0) position: vec2; -}; - -struct Quad { - @location(1) pos: vec2; // can't use the name "position" twice for compatibility with metal on MacOS - @location(2) width: f32; - @location(3) height: f32; - @location(4) color: vec4; - @location(5) border_color: vec4; - @location(6) border_width: f32; -}; - -struct VertexOutput { - @builtin(position) position: vec4; - @location(0) color: vec4; - @location(1) border_color: vec4; - @location(2) border_width: f32; -}; - -@stage(vertex) -fn vs_main( - input: VertexInput, - quad: Quad -) -> VertexOutput { - - var transform: mat4x4 = mat4x4( - vec4(quad.width, 0.0, 0.0, 0.0), - vec4(0.0, quad.height, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(quad.pos, 0.0, 1.0) - ); - - var out: VertexOutput; - - out.position = globals.ortho * transform * vec4(input.position, 0.0, 1.0);; - out.color = quad.color; - out.border_color = quad.border_color; - out.border_width = quad.border_width; - - return out; -} - - -@stage(fragment) -fn fs_main( - input: VertexOutput -) -> @location(0) vec4 { - return input.color; -} diff --git a/examples/gui/platform/src/graphics/style.rs b/examples/gui/platform/src/graphics/style.rs deleted file mode 100644 index 11e609075b..0000000000 --- a/examples/gui/platform/src/graphics/style.rs +++ /dev/null @@ -1 +0,0 @@ -pub const DEFAULT_FONT_SIZE: f32 = 30.0; diff --git a/examples/gui/platform/src/gui.rs b/examples/gui/platform/src/gui.rs deleted file mode 100644 index e054a1895b..0000000000 --- a/examples/gui/platform/src/gui.rs +++ /dev/null @@ -1,651 +0,0 @@ -use crate::{ - graphics::{ - colors::Rgba, - lowlevel::buffer::create_rect_buffers, - lowlevel::{buffer::MAX_QUADS, ortho::update_ortho_buffer}, - lowlevel::{buffer::QUAD_INDICES, pipelines}, - primitives::{ - rect::{Rect, RectElt}, - text::build_glyph_brush, - }, - }, - roc::{RocElem, RocElemTag}, -}; -use cgmath::{Vector2, Vector4}; -use glyph_brush::OwnedSection; -use pipelines::RectResources; -use roc_std::RocStr; -use std::error::Error; -use wgpu::{CommandEncoder, LoadOp, RenderPass, TextureView}; -use wgpu_glyph::{GlyphBrush, GlyphCruncher}; -use winit::{ - dpi::PhysicalSize, - event, - event::{Event, ModifiersState}, - event_loop::ControlFlow, - platform::run_return::EventLoopExtRunReturn, -}; - -// Inspired by: -// https://github.com/sotrh/learn-wgpu by Benjamin Hansen, which is licensed under the MIT license -// https://github.com/cloudhead/rgx by Alexis Sellier, which is licensed under the MIT license -// -// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/ - -fn run_event_loop(title: &str, root: RocElem) -> Result<(), Box> { - // Open window and create a surface - let mut event_loop = winit::event_loop::EventLoop::new(); - - let window = winit::window::WindowBuilder::new() - .with_inner_size(PhysicalSize::new(1900.0, 1000.0)) - .with_title(title) - .build(&event_loop) - .unwrap(); - - let instance = wgpu::Instance::new(wgpu::Backends::all()); - - let surface = unsafe { instance.create_surface(&window) }; - - // Initialize GPU - let (gpu_device, cmd_queue) = futures::executor::block_on(async { - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .expect(r#"Request adapter - If you're running this from inside nix, run with: - `nixVulkanIntel `. - See extra docs here: github.com/guibou/nixGL - "#); - - adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, - ) - .await - .expect("Request device") - }); - - // Create staging belt and a local pool - let mut staging_belt = wgpu::util::StagingBelt::new(1024); - let mut local_pool = futures::executor::LocalPool::new(); - let local_spawner = local_pool.spawner(); - - // Prepare swap chain - let render_format = wgpu::TextureFormat::Bgra8Unorm; - let mut size = window.inner_size(); - - let surface_config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: render_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }; - - surface.configure(&gpu_device, &surface_config); - - let rect_resources = pipelines::make_rect_pipeline(&gpu_device, &surface_config); - - let mut glyph_brush = build_glyph_brush(&gpu_device, render_format)?; - - let is_animating = true; - - let mut keyboard_modifiers = ModifiersState::empty(); - - // Render loop - window.request_redraw(); - - event_loop.run_return(|event, _, control_flow| { - // TODO dynamically switch this on/off depending on whether any - // animations are running. Should conserve CPU usage and battery life! - if is_animating { - *control_flow = ControlFlow::Poll; - } else { - *control_flow = ControlFlow::Wait; - } - - match event { - //Close - Event::WindowEvent { - event: event::WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - //Resize - Event::WindowEvent { - event: event::WindowEvent::Resized(new_size), - .. - } => { - size = new_size; - - surface.configure( - &gpu_device, - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: render_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }, - ); - - update_ortho_buffer( - size.width, - size.height, - &gpu_device, - &rect_resources.ortho.buffer, - &cmd_queue, - ); - } - //Received Character - Event::WindowEvent { - event: event::WindowEvent::ReceivedCharacter(_ch), - .. - } => { - // let input_outcome_res = - // app_update::handle_new_char(&ch, &mut app_model, keyboard_modifiers); - // if let Err(e) = input_outcome_res { - // print_err(&e) - // } else if let Ok(InputOutcome::Ignored) = input_outcome_res { - // println!("Input '{}' ignored!", ch); - // } - todo!("TODO handle character input"); - } - //Keyboard Input - Event::WindowEvent { - event: event::WindowEvent::KeyboardInput { input: _, .. }, - .. - } => { - // if let Some(virtual_keycode) = input.virtual_keycode { - // if let Some(ref mut ed_model) = app_model.ed_model_opt { - // if ed_model.has_focus { - // let keydown_res = keyboard_input::handle_keydown( - // input.state, - // virtual_keycode, - // keyboard_modifiers, - // &mut app_model, - // ); - - // if let Err(e) = keydown_res { - // print_err(&e) - // } - // } - // } - // } - // TODO todo!("TODO handle keyboard input"); - } - //Modifiers Changed - Event::WindowEvent { - event: event::WindowEvent::ModifiersChanged(modifiers), - .. - } => { - keyboard_modifiers = modifiers; - } - Event::RedrawRequested { .. } => { - // Get a command cmd_encoder for the current frame - let mut cmd_encoder = - gpu_device.create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Redraw"), - }); - - let surface_texture = surface - .get_current_texture() - .expect("Failed to acquire next SwapChainTexture"); - - let view = surface_texture - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - // for text_section in &rects_and_texts.text_sections_behind { - // let borrowed_text = text_section.to_borrowed(); - - // glyph_brush.queue(borrowed_text); - // } - - // draw first layer of text - // glyph_brush - // .draw_queued( - // &gpu_device, - // &mut staging_belt, - // &mut cmd_encoder, - // &view, - // size.width, - // size.height, - // ) - // .expect("Failed to draw first layer of text."); - - // draw rects on top of first text layer - // draw_rects( - // &rects_and_texts.rects_front, - // &mut cmd_encoder, - // &view, - // &gpu_device, - // &rect_resources, - // wgpu::LoadOp::Load, - // ); - - // TODO use with_capacity based on some heuristic - let (_bounds, drawable) = to_drawable( - &root, - Bounds { - width: size.width as f32, - height: size.height as f32, - }, - &mut glyph_brush, - ); - - process_drawable( - drawable, - &mut staging_belt, - &mut glyph_brush, - &mut cmd_encoder, - &view, - &gpu_device, - &rect_resources, - wgpu::LoadOp::Load, - Bounds { - width: size.width as f32, - height: size.height as f32, - }, - ); - - // for text_section in &rects_and_texts.text_sections_front { - // let borrowed_text = text_section.to_borrowed(); - - // glyph_brush.queue(borrowed_text); - // } - - // draw text - // glyph_brush - // .draw_queued( - // &gpu_device, - // &mut staging_belt, - // &mut cmd_encoder, - // &view, - // size.width, - // size.height, - // ) - // .expect("Failed to draw queued text."); - - staging_belt.finish(); - cmd_queue.submit(Some(cmd_encoder.finish())); - surface_texture.present(); - - // Recall unused staging buffers - use futures::task::SpawnExt; - - local_spawner - .spawn(staging_belt.recall()) - .expect("Recall staging belt"); - - local_pool.run_until_stalled(); - } - _ => { - *control_flow = winit::event_loop::ControlFlow::Wait; - } - } - }); - - Ok(()) -} - -fn draw_rects( - all_rects: &[RectElt], - cmd_encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, -) { - let rect_buffers = create_rect_buffers(gpu_device, cmd_encoder, all_rects); - - let mut render_pass = begin_render_pass(cmd_encoder, texture_view, load_op); - - render_pass.set_pipeline(&rect_resources.pipeline); - render_pass.set_bind_group(0, &rect_resources.ortho.bind_group, &[]); - - render_pass.set_vertex_buffer(0, rect_buffers.vertex_buffer.slice(..)); - render_pass.set_vertex_buffer(1, rect_buffers.quad_buffer.slice(..)); - - render_pass.set_index_buffer( - rect_buffers.index_buffer.slice(..), - wgpu::IndexFormat::Uint16, - ); - - render_pass.draw_indexed(0..QUAD_INDICES.len() as u32, 0, 0..MAX_QUADS as u32); -} - -fn begin_render_pass<'a>( - cmd_encoder: &'a mut CommandEncoder, - texture_view: &'a TextureView, - load_op: LoadOp, -) -> RenderPass<'a> { - cmd_encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachment { - view: texture_view, - resolve_target: None, - ops: wgpu::Operations { - load: load_op, - store: true, - }, - }], - depth_stencil_attachment: None, - label: None, - }) -} - -pub fn render(title: RocStr, root: RocElem) { - run_event_loop(title.as_str(), root).expect("Error running event loop"); -} - -#[derive(Copy, Clone, Debug, Default)] -struct Bounds { - width: f32, - height: f32, -} - -#[derive(Clone, Debug)] -struct Drawable { - bounds: Bounds, - content: DrawableContent, -} - -#[derive(Clone, Debug)] -enum DrawableContent { - /// This stores an actual Section because an earlier step needs to know the bounds of - /// the text, and making a Section is a convenient way to compute those bounds. - Text(OwnedSection, Vector2), - FillRect { - color: Rgba, - border_width: f32, - border_color: Rgba, - }, - Multi(Vec), - Offset(Vec<(Vector2, Drawable)>), -} - -fn process_drawable( - drawable: Drawable, - staging_belt: &mut wgpu::util::StagingBelt, - glyph_brush: &mut GlyphBrush<()>, - cmd_encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, - texture_size: Bounds, -) { - // TODO iterate through drawables, - // calculating a pos using offset, - // calling draw and updating bounding boxes - let pos: Vector2 = (0.0, 0.0).into(); - - draw( - drawable.bounds, - drawable.content, - pos, - staging_belt, - glyph_brush, - cmd_encoder, - texture_view, - gpu_device, - rect_resources, - load_op, - texture_size, - ); -} - -fn draw( - bounds: Bounds, - content: DrawableContent, - pos: Vector2, - staging_belt: &mut wgpu::util::StagingBelt, - glyph_brush: &mut GlyphBrush<()>, - cmd_encoder: &mut CommandEncoder, - texture_view: &TextureView, - gpu_device: &wgpu::Device, - rect_resources: &RectResources, - load_op: LoadOp, - texture_size: Bounds, -) { - use DrawableContent::*; - - match content { - Text(section, offset) => { - glyph_brush.queue(section.with_screen_position(pos + offset).to_borrowed()); - - glyph_brush - .draw_queued( - gpu_device, - staging_belt, - cmd_encoder, - texture_view, - texture_size.width as u32, // TODO why do we make these be u32 and then cast to f32 in orthorgraphic_projection? - texture_size.height as u32, - ) - .expect("Failed to draw text element"); - } - FillRect { - color, - border_width, - border_color, - } => { - // TODO store all these colors and things in FillRect - let rect_elt = RectElt { - rect: Rect { - pos, - width: bounds.width, - height: bounds.height, - }, - color, - border_width, - border_color, - }; - - // TODO inline draw_rects into here! - draw_rects( - &[rect_elt], - cmd_encoder, - texture_view, - gpu_device, - rect_resources, - load_op, - ); - } - Offset(children) => { - for (offset, child) in children.into_iter() { - draw( - child.bounds, - child.content, - pos + offset, - staging_belt, - glyph_brush, - cmd_encoder, - texture_view, - gpu_device, - rect_resources, - load_op, - texture_size, - ); - } - } - Multi(children) => { - for child in children.into_iter() { - draw( - child.bounds, - child.content, - pos, - staging_belt, - glyph_brush, - cmd_encoder, - texture_view, - gpu_device, - rect_resources, - load_op, - texture_size, - ); - } - } - } -} - -fn to_drawable( - elem: &RocElem, - bounds: Bounds, - glyph_brush: &mut GlyphBrush<()>, -) -> (Bounds, Drawable) { - use RocElemTag::*; - - match elem.tag() { - Button => { - let button = unsafe { &elem.entry().button }; - let styles = button.styles; - let (child_bounds, child_drawable) = to_drawable(&*button.child, bounds, glyph_brush); - - let button_drawable = Drawable { - bounds: child_bounds, - content: DrawableContent::FillRect { - color: styles.bg_color, - border_width: styles.border_width, - border_color: styles.border_color, - }, - }; - - let drawable = Drawable { - bounds: child_bounds, - content: DrawableContent::Multi(vec![button_drawable, child_drawable]), - }; - - (child_bounds, drawable) - } - Text => { - // TODO let text color and font settings inherit from parent - let text = unsafe { &elem.entry().text }; - let is_centered = true; // TODO don't hardcode this - let layout = wgpu_glyph::Layout::default().h_align(if is_centered { - wgpu_glyph::HorizontalAlign::Center - } else { - wgpu_glyph::HorizontalAlign::Left - }); - - let section = owned_section_from_str(text.as_str(), bounds, layout); - - // Calculate the bounds and offset by measuring glyphs - let text_bounds; - let offset; - - match glyph_brush.glyph_bounds(section.to_borrowed()) { - Some(glyph_bounds) => { - text_bounds = Bounds { - width: glyph_bounds.max.x - glyph_bounds.min.x, - height: glyph_bounds.max.y - glyph_bounds.min.y, - }; - - offset = (-glyph_bounds.min.x, -glyph_bounds.min.y).into(); - } - None => { - text_bounds = Bounds { - width: 0.0, - height: 0.0, - }; - - offset = (0.0, 0.0).into(); - } - } - - let drawable = Drawable { - bounds: text_bounds, - content: DrawableContent::Text(section, offset), - }; - - (text_bounds, drawable) - } - Row => { - let row = unsafe { &elem.entry().row_or_col }; - let mut final_bounds = Bounds::default(); - let mut offset: Vector2 = (0.0, 0.0).into(); - let mut offset_entries = Vec::with_capacity(row.children.len()); - - for child in row.children.as_slice().iter() { - let (child_bounds, child_drawable) = to_drawable(&child, bounds, glyph_brush); - - offset_entries.push((offset, child_drawable)); - - // Make sure the final height is enough to fit this child - final_bounds.height = final_bounds.height.max(child_bounds.height); - - // Add the child's width to the final width - final_bounds.width = final_bounds.width + child_bounds.width; - - // Offset the next child to make sure it appears after this one. - offset.x += child_bounds.width; - } - - ( - final_bounds, - Drawable { - bounds: final_bounds, - content: DrawableContent::Offset(offset_entries), - }, - ) - } - Col => { - let col = unsafe { &elem.entry().row_or_col }; - let mut final_bounds = Bounds::default(); - let mut offset: Vector2 = (0.0, 0.0).into(); - let mut offset_entries = Vec::with_capacity(col.children.len()); - - for child in col.children.as_slice().iter() { - let (child_bounds, child_drawable) = to_drawable(&child, bounds, glyph_brush); - - offset_entries.push((offset, child_drawable)); - - // Make sure the final width is enough to fit this child - final_bounds.width = final_bounds.width.max(child_bounds.width); - - // Add the child's height to the final height - final_bounds.height = final_bounds.height + child_bounds.height; - - // Offset the next child to make sure it appears after this one. - offset.y += child_bounds.height; - } - - ( - final_bounds, - Drawable { - bounds: final_bounds, - content: DrawableContent::Offset(offset_entries), - }, - ) - } - } -} - -fn owned_section_from_str( - string: &str, - bounds: Bounds, - layout: wgpu_glyph::Layout, -) -> OwnedSection { - // TODO don't hardcode any of this! - let color = Rgba::WHITE; - let size: f32 = 40.0; - - OwnedSection { - bounds: (bounds.width, bounds.height), - layout, - ..OwnedSection::default() - } - .add_text( - glyph_brush::OwnedText::new(string) - .with_color(Vector4::from(color)) - .with_scale(size), - ) -} diff --git a/examples/gui/platform/src/lib.rs b/examples/gui/platform/src/lib.rs deleted file mode 100644 index 17fa0b4d4a..0000000000 --- a/examples/gui/platform/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -mod graphics; -mod gui; -mod rects_and_texts; -mod roc; - -use crate::roc::RocElem; - -extern "C" { - #[link_name = "roc__renderForHost_1_exposed"] - fn roc_render() -> RocElem; -} - -#[no_mangle] -pub extern "C" fn rust_main() -> i32 { - let root_elem = unsafe { roc_render() }; - - gui::render("test title".into(), root_elem); - - // Exit code - 0 -} diff --git a/examples/gui/platform/src/main.rs b/examples/gui/platform/src/main.rs deleted file mode 100644 index 0765384f29..0000000000 --- a/examples/gui/platform/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - std::process::exit(host::rust_main() as _); -} diff --git a/examples/gui/platform/src/rects_and_texts.rs b/examples/gui/platform/src/rects_and_texts.rs deleted file mode 100644 index 3eed8654ac..0000000000 --- a/examples/gui/platform/src/rects_and_texts.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::graphics::primitives::rect::RectElt; -use crate::graphics::primitives::text::{owned_section_from_text, Text}; - -#[derive(Debug)] -pub struct RectsAndTexts { - pub text_sections_behind: Vec, // displayed in front of rect_behind, behind everything else - pub text_sections_front: Vec, // displayed in front of everything - pub rects_behind: Vec, // displayed at lowest depth - pub rects_front: Vec, // displayed in front of text_sections_behind, behind text_sections_front -} - -impl RectsAndTexts { - pub fn new() -> Self { - Self { - text_sections_behind: Vec::new(), - text_sections_front: Vec::new(), - rects_behind: Vec::new(), - rects_front: Vec::new(), - } - } - - pub fn init( - rects_behind: Vec, - texts_behind: Vec, - rects_front: Vec, - texts_front: Vec, - ) -> Self { - Self { - text_sections_behind: texts_behind - .iter() - .map(|txt| owned_section_from_text(txt)) - .collect(), - text_sections_front: texts_front - .iter() - .map(|txt| owned_section_from_text(txt)) - .collect(), - rects_behind, - rects_front, - } - } - - pub fn add_text_behind(&mut self, new_text_section: glyph_brush::OwnedSection) { - self.text_sections_behind.push(new_text_section); - } - - pub fn add_text_front(&mut self, new_text_section: glyph_brush::OwnedSection) { - self.text_sections_front.push(new_text_section); - } - - pub fn add_rect_behind(&mut self, new_rect: RectElt) { - self.rects_behind.push(new_rect); - } - - pub fn add_rects_behind(&mut self, new_rects: Vec) { - self.rects_behind.extend(new_rects); - } - - pub fn add_rect_front(&mut self, new_rect: RectElt) { - self.rects_front.push(new_rect); - } - - pub fn extend(&mut self, rects_and_texts: RectsAndTexts) { - self.text_sections_behind - .extend(rects_and_texts.text_sections_behind); - self.text_sections_front - .extend(rects_and_texts.text_sections_front); - self.rects_behind.extend(rects_and_texts.rects_behind); - self.rects_front.extend(rects_and_texts.rects_front); - } -} diff --git a/examples/gui/platform/src/roc.rs b/examples/gui/platform/src/roc.rs deleted file mode 100644 index eea89d5837..0000000000 --- a/examples/gui/platform/src/roc.rs +++ /dev/null @@ -1,203 +0,0 @@ -use crate::graphics::colors::Rgba; -use core::ffi::c_void; -use core::mem::{self, ManuallyDrop}; -use roc_std::{RocList, RocRefcounted, RocStr}; -use std::ffi::CStr; -use std::ops::DerefMut; -use std::os::raw::c_char; - -#[no_mangle] -pub unsafe extern "C" fn roc_alloc(size: usize, _alignment: u32) -> *mut c_void { - return libc::malloc(size); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_realloc( - c_ptr: *mut c_void, - new_size: usize, - _old_size: usize, - _alignment: u32, -) -> *mut c_void { - return libc::realloc(c_ptr, new_size); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_dealloc(c_ptr: *mut c_void, _alignment: u32) { - return libc::free(c_ptr); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_panic(msg: *mut RocStr, tag_id: u32) { - match tag_id { - 0 => { - eprintln!("Roc standard library hit a panic: {}", &*msg); - } - 1 => { - eprintln!("Application hit a panic: {}", &*msg); - } - _ => unreachable!(), - } - std::process::exit(1); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_dbg(loc: *mut RocStr, msg: *mut RocStr, src: *mut RocStr) { - eprintln!("[{}] {} = {}", &*loc, &*src, &*msg); -} - -#[no_mangle] -pub unsafe extern "C" fn roc_memset(dst: *mut c_void, c: i32, n: usize) -> *mut c_void { - libc::memset(dst, c, n) -} - -#[repr(transparent)] -#[cfg(target_pointer_width = "64")] // on a 64-bit system, the tag fits in this pointer's spare 3 bits -pub struct RocElem { - entry: *mut RocElemEntry, -} - -impl RocElem { - #[cfg(target_pointer_width = "64")] - pub fn tag(&self) -> RocElemTag { - // On a 64-bit system, the last 3 bits of the pointer store the tag - unsafe { mem::transmute::((self.entry as u8) & 0b0000_0111) } - } - - pub fn entry(&self) -> &RocElemEntry { - // On a 64-bit system, the last 3 bits of the pointer store the tag - let cleared = self.entry as usize & !0b111; - - unsafe { &*(cleared as *const RocElemEntry) } - } -} - -#[repr(u8)] -#[allow(unused)] // This is actually used, just via a mem::transmute from u8 -#[derive(Debug, Clone, Copy)] -pub enum RocElemTag { - Button = 0, - Col, - Row, - Text, -} - -impl RocRefcounted for RocElem { - fn inc(&mut self) { - unsafe { - match self.tag() { - RocElemTag::Button => (*self.entry).button.deref_mut().inc(), - RocElemTag::Text => (*self.entry).text.deref_mut().inc(), - RocElemTag::Col | RocElemTag::Row => (*self.entry).row_or_col.deref_mut().inc(), - } - } - } - - fn dec(&mut self) { - unsafe { - match self.tag() { - RocElemTag::Button => (*self.entry).button.deref_mut().dec(), - RocElemTag::Text => (*self.entry).text.deref_mut().dec(), - RocElemTag::Col | RocElemTag::Row => (*self.entry).row_or_col.deref_mut().dec(), - } - } - } - - fn is_refcounted() -> bool { - true - } -} - -#[repr(C)] -#[derive(Clone)] -pub struct RocButton { - pub child: ManuallyDrop, - pub styles: ButtonStyles, -} - -impl RocRefcounted for RocButton { - fn inc(&mut self) { - self.child.deref_mut().inc(); - } - - fn dec(&mut self) { - self.child.deref_mut().dec(); - } - - fn is_refcounted() -> bool { - true - } -} - -#[repr(C)] -#[derive(Clone)] -pub struct RocRowOrCol { - pub children: RocList, -} - -impl RocRefcounted for RocRowOrCol { - fn inc(&mut self) { - self.children.inc(); - } - - fn dec(&mut self) { - self.children.dec(); - } - - fn is_refcounted() -> bool { - true - } -} - -impl Clone for RocElem { - fn clone(&self) -> Self { - unsafe { - match self.tag() { - RocElemTag::Button => Self { - entry: &mut RocElemEntry { - button: (*self.entry).button.clone(), - }, - }, - RocElemTag::Text => Self { - entry: &mut RocElemEntry { - text: (*self.entry).text.clone(), - }, - }, - RocElemTag::Col | RocElemTag::Row => Self { - entry: &mut RocElemEntry { - row_or_col: (*self.entry).row_or_col.clone(), - }, - }, - } - } - } -} - -impl Drop for RocElem { - fn drop(&mut self) { - unsafe { - match self.tag() { - RocElemTag::Button => mem::drop(ManuallyDrop::take(&mut (*self.entry).button)), - RocElemTag::Text => mem::drop(ManuallyDrop::take(&mut (*self.entry).text)), - RocElemTag::Col | RocElemTag::Row => { - mem::drop(ManuallyDrop::take(&mut (*self.entry).row_or_col)) - } - } - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct ButtonStyles { - pub bg_color: Rgba, - pub border_color: Rgba, - pub border_width: f32, - pub text_color: Rgba, -} - -#[repr(C)] -pub union RocElemEntry { - pub button: ManuallyDrop, - pub text: ManuallyDrop, - pub row_or_col: ManuallyDrop, -} diff --git a/examples/inspect-gui.roc b/examples/inspect-gui.roc deleted file mode 100644 index af4a3db399..0000000000 --- a/examples/inspect-gui.roc +++ /dev/null @@ -1,35 +0,0 @@ -# -# Visualizes Roc values in a basic GUI -# -app [render] { pf: platform "gui/platform/main.roc" } - -import Community -import GuiFormatter - -render = - Community.empty - |> Community.addPerson { - firstName: "John", - lastName: "Smith", - age: 27, - hasBeard: Bool.true, - favoriteColor: Blue, - } - |> Community.addPerson { - firstName: "Debby", - lastName: "Johnson", - age: 47, - hasBeard: Bool.false, - favoriteColor: Green, - } - |> Community.addPerson { - firstName: "Jane", - lastName: "Doe", - age: 33, - hasBeard: Bool.false, - favoriteColor: RGB (255, 255, 0), - } - |> Community.addFriend 0 2 - |> Community.addFriend 1 2 - |> Inspect.inspect - |> GuiFormatter.toGui diff --git a/examples/platform-switching/rocLovesSwift.roc b/examples/platform-switching/rocLovesSwift.roc deleted file mode 100644 index fd09c43f01..0000000000 --- a/examples/platform-switching/rocLovesSwift.roc +++ /dev/null @@ -1,3 +0,0 @@ -app [main] { pf: platform "swift-platform/main.roc" } - -main = "Roc <3 Swift!\n" diff --git a/examples/platform-switching/swift-platform/host.h b/examples/platform-switching/swift-platform/host.h deleted file mode 100644 index 7714980c93..0000000000 --- a/examples/platform-switching/swift-platform/host.h +++ /dev/null @@ -1,9 +0,0 @@ -#include - -struct RocStr { - char* bytes; - size_t len; - size_t capacity; -}; - -extern void roc__mainForHost_1_exposed_generic(const struct RocStr *data); diff --git a/examples/platform-switching/swift-platform/host.swift b/examples/platform-switching/swift-platform/host.swift deleted file mode 100644 index 144ed8a54a..0000000000 --- a/examples/platform-switching/swift-platform/host.swift +++ /dev/null @@ -1,62 +0,0 @@ -import Foundation - -@_cdecl("roc_alloc") -func rocAlloc(size: Int, _alignment: UInt) -> UInt { - guard let ptr = malloc(size) else { - return 0 - } - return UInt(bitPattern: ptr) -} - -@_cdecl("roc_dealloc") -func rocDealloc(ptr: UInt, _alignment: UInt) { - free(UnsafeMutableRawPointer(bitPattern: ptr)) -} - -@_cdecl("roc_realloc") -func rocRealloc(ptr: UInt, _oldSize: Int, newSize: Int, _alignment: UInt) -> UInt { - guard let ptr = realloc(UnsafeMutableRawPointer(bitPattern: ptr), newSize) else { - return 0 - } - return UInt(bitPattern: ptr) -} - -func isSmallString(rocStr: RocStr) -> Bool { - return rocStr.capacity < 0 -} - -func getStrLen(rocStr: RocStr) -> Int { - if isSmallString(rocStr: rocStr) { - // Small String length is last in the byte of capacity. - var cap = rocStr.capacity - let count = MemoryLayout.size(ofValue: cap) - let bytes = Data(bytes: &cap, count: count) - let lastByte = bytes[count - 1] - return Int(lastByte ^ 0b1000_0000) - } else { - return rocStr.len - } -} - -func getSwiftString(rocStr: RocStr) -> String { - let length = getStrLen(rocStr: rocStr) - - if isSmallString(rocStr: rocStr) { - let data: Data = withUnsafePointer(to: rocStr) { ptr in - Data(bytes: ptr, count: length) - } - return String(data: data, encoding: .utf8)! - } else { - let data = Data(bytes: rocStr.bytes, count: length) - return String(data: data, encoding: .utf8)! - } -} - -@_cdecl("main") -func main() -> UInt8 { - var rocStr = RocStr() - roc__mainForHost_1_exposed_generic(&rocStr) - - print(getSwiftString(rocStr: rocStr), terminator: "") - return 0 -} diff --git a/examples/platform-switching/swift-platform/main.roc b/examples/platform-switching/swift-platform/main.roc deleted file mode 100644 index a9eb403cd9..0000000000 --- a/examples/platform-switching/swift-platform/main.roc +++ /dev/null @@ -1,9 +0,0 @@ -platform "echo-in-swift" - requires {} { main : Str } - exposes [] - packages {} - imports [] - provides [mainForHost] - -mainForHost : Str -mainForHost = main diff --git a/examples/swiftui/main.roc b/examples/swiftui/main.roc deleted file mode 100644 index ba2e3dd5ff..0000000000 --- a/examples/swiftui/main.roc +++ /dev/null @@ -1,3 +0,0 @@ -app [main] { pf: platform "platform/main.roc" } - -main = "Roc <3 Swift!\n" diff --git a/examples/swiftui/platform/host.h b/examples/swiftui/platform/host.h deleted file mode 100644 index 7714980c93..0000000000 --- a/examples/swiftui/platform/host.h +++ /dev/null @@ -1,9 +0,0 @@ -#include - -struct RocStr { - char* bytes; - size_t len; - size_t capacity; -}; - -extern void roc__mainForHost_1_exposed_generic(const struct RocStr *data); diff --git a/examples/swiftui/platform/host.swift b/examples/swiftui/platform/host.swift deleted file mode 100644 index 371de8baf1..0000000000 --- a/examples/swiftui/platform/host.swift +++ /dev/null @@ -1,78 +0,0 @@ -import Foundation -import SwiftUI - -@_cdecl("roc_alloc") -func rocAlloc(size: Int, _alignment: UInt) -> UInt { - guard let ptr = malloc(size) else { - return 0 - } - return UInt(bitPattern: ptr) -} - -@_cdecl("roc_dealloc") -func rocDealloc(ptr: UInt, _alignment: UInt) { - free(UnsafeMutableRawPointer(bitPattern: ptr)) -} - -@_cdecl("roc_realloc") -func rocRealloc(ptr: UInt, _oldSize: Int, newSize: Int, _alignment: UInt) -> UInt { - guard let ptr = realloc(UnsafeMutableRawPointer(bitPattern: ptr), newSize) else { - return 0 - } - return UInt(bitPattern: ptr) -} - -func isSmallString(rocStr: RocStr) -> Bool { - return rocStr.capacity < 0 -} - -func getStrLen(rocStr: RocStr) -> Int { - if isSmallString(rocStr: rocStr) { - // Small String length is last in the byte of capacity. - var cap = rocStr.capacity - let count = MemoryLayout.size(ofValue: cap) - let bytes = Data(bytes: &cap, count: count) - let lastByte = bytes[count - 1] - return Int(lastByte ^ 0b1000_0000) - } else { - return rocStr.len - } -} - -func getSwiftString(rocStr: RocStr) -> String { - let length = getStrLen(rocStr: rocStr) - - if isSmallString(rocStr: rocStr) { - let data: Data = withUnsafePointer(to: rocStr) { ptr in - Data(bytes: ptr, count: length) - } - return String(data: data, encoding: .utf8)! - } else { - let data = Data(bytes: rocStr.bytes, count: length) - return String(data: data, encoding: .utf8)! - } -} - -struct ContentView: View { - var str: String - - init() { - var rocStr = RocStr() - roc__mainForHost_1_exposed_generic(&rocStr) - self.str = getSwiftString(rocStr: rocStr) - } - - var body: some View { - Text(self.str) - .padding() - } -} - -@main -struct RocTestApp: App { - var body: some Scene { - WindowGroup { - ContentView() - } - } -} \ No newline at end of file diff --git a/examples/swiftui/platform/main.roc b/examples/swiftui/platform/main.roc deleted file mode 100644 index 1aa031ea3d..0000000000 --- a/examples/swiftui/platform/main.roc +++ /dev/null @@ -1,9 +0,0 @@ -platform "swiftui-platform" - requires {} { main : Str } - exposes [] - packages {} - imports [] - provides [mainForHost] - -mainForHost : Str -mainForHost = main diff --git a/examples/swiftui/run.sh b/examples/swiftui/run.sh deleted file mode 100755 index d20a18d314..0000000000 --- a/examples/swiftui/run.sh +++ /dev/null @@ -1,9 +0,0 @@ -#! /usr/bin/env bash - -# https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/ -set -euxo pipefail - -cargo run -- build -mkdir -p SwiftUIDemo.app/Contents/MacOS/ -mv swiftui SwiftUIDemo.app/Contents/MacOS/SwiftUIDemo -open SwiftUIDemo.app \ No newline at end of file From fec875d045b54fc2a0268812d471da8a7bdcbc87 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 21 Jul 2024 14:56:21 -0700 Subject: [PATCH 09/30] have zig generate more efficient copy functions --- crates/compiler/builtins/bitcode/src/list.zig | 167 +++++++++++++----- 1 file changed, 119 insertions(+), 48 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index 2db0f97a76..f018aba396 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -6,10 +6,12 @@ const mem = std.mem; const math = std.math; const expect = std.testing.expect; +const expectEqual = std.testing.expectEqual; -const EqFn = *const fn (?[*]u8, ?[*]u8) callconv(.C) bool; -const CompareFn = *const fn (?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) u8; const Opaque = ?[*]u8; +const EqFn = *const fn (Opaque, Opaque) callconv(.C) bool; +const CompareFn = *const fn (Opaque, Opaque, Opaque) callconv(.C) u8; +const CopyFn = *const fn (Opaque, Opaque, usize) void; const Inc = *const fn (?[*]u8) callconv(.C) void; const IncN = *const fn (?[*]u8, usize) callconv(.C) void; @@ -433,7 +435,7 @@ pub fn listAppendUnsafe( if (output.bytes) |bytes| { if (element) |source| { const target = bytes + old_length * element_width; - @memcpy(target[0..element_width], source[0..element_width]); + copy_element_fn(element_width)(target, source, element_width); } } @@ -468,20 +470,14 @@ pub fn listPrepend( // can't use one memcpy here because source and target overlap if (with_capacity.bytes) |target| { - var i: usize = old_length; - - while (i > 0) { - i -= 1; - - // move the ith element to the (i + 1)th position - const to = target + (i + 1) * element_width; - const from = target + i * element_width; - @memcpy(to[0..element_width], from[0..element_width]); - } + const from = target; + const to = target + element_width; + const size = element_width * old_length; + std.mem.copyBackwards(u8, to[0..size], from[0..size]); // finally copy in the new first element if (element) |source| { - @memcpy(target[0..element_width], source[0..element_width]); + copy_element_fn(element_width)(target, source, element_width); } } @@ -519,7 +515,8 @@ pub fn listSwap( const source_ptr = @as([*]u8, @ptrCast(newList.bytes)); - swapElements(source_ptr, element_width, @as(usize, + const copy_fn_ptr = copy_element_fn(element_width); + swapElements(copy_fn_ptr, source_ptr, element_width, @as(usize, // We already verified that both indices are less than the stored list length, // which is usize, so casting them to usize will definitely be lossless. @intCast(index_1)), @as(usize, @intCast(index_2))); @@ -653,12 +650,9 @@ pub fn listDropAt( if (list.isUnique()) { var i = drop_index; - while (i < size - 1) : (i += 1) { - const copy_target = source_ptr + i * element_width; - const copy_source = copy_target + element_width; - - @memcpy(copy_target[0..element_width], copy_source[0..element_width]); - } + const copy_target = source_ptr; + const copy_source = copy_target + element_width; + std.mem.copyForwards(u8, copy_target[i..size], copy_source[i..size]); var new_list = list; @@ -693,7 +687,7 @@ pub fn listDropAt( } } -fn partition(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) isize { +fn partition(copy_fn_ptr: CopyFn, source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) isize { const pivot = source_ptr + (@as(usize, @intCast(high)) * element_width); var i = (low - 1); // Index of smaller element and indicates the right position of pivot found so far var j = low; @@ -708,22 +702,22 @@ fn partition(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_w utils.Ordering.LT => { // the current element is smaller than the pivot; swap it i += 1; - swapElements(source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j))); + swapElements(copy_fn_ptr, source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j))); }, utils.Ordering.EQ, utils.Ordering.GT => {}, } } - swapElements(source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high))); + swapElements(copy_fn_ptr, source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high))); return (i + 1); } -fn quicksort(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) void { +fn quicksort(copy_fn_ptr: CopyFn, source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) void { if (low < high) { // partition index - const pi = partition(source_ptr, transform, wrapper, element_width, low, high); + const pi = partition(copy_fn_ptr, source_ptr, transform, wrapper, element_width, low, high); - _ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1); // before pi - _ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high); // after pi + _ = quicksort(copy_fn_ptr, source_ptr, transform, wrapper, element_width, low, pi - 1); // before pi + _ = quicksort(copy_fn_ptr, source_ptr, transform, wrapper, element_width, pi + 1, high); // after pi } } @@ -748,7 +742,7 @@ pub fn listSortWith( if (list.bytes) |source_ptr| { const low = 0; const high: isize = @as(isize, @intCast(list.len())) - 1; - quicksort(source_ptr, data, caller, element_width, low, high); + quicksort(copy_element_fn(element_width), source_ptr, data, caller, element_width, low, high); } return list; @@ -756,29 +750,35 @@ pub fn listSortWith( // SWAP ELEMENTS -inline fn swapHelp(width: usize, temporary: [*]u8, ptr1: [*]u8, ptr2: [*]u8) void { - @memcpy(temporary[0..width], ptr1[0..width]); - @memcpy(ptr1[0..width], ptr2[0..width]); - @memcpy(ptr2[0..width], temporary[0..width]); -} - -fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void { - const threshold: usize = 64; - - var width = width_initial; - - var ptr1 = p1; - var ptr2 = p2; +fn swap(copy_fn_ptr: CopyFn, element_width: usize, p1: [*]u8, p2: [*]u8) void { + const threshold: usize = @sizeOf(u256); var buffer_actual: [threshold]u8 = undefined; const buffer: [*]u8 = buffer_actual[0..]; + if (element_width <= threshold) { + copy_fn_ptr(buffer, p1, element_width); + copy_fn_ptr(p1, p2, element_width); + copy_fn_ptr(p2, buffer, element_width); + return; + } + + var width = element_width; + + var ptr1 = p1; + var ptr2 = p2; + + const copy_buffer_ptr = comptime copy_element_fn(threshold); while (true) { if (width < threshold) { - swapHelp(width, buffer, ptr1, ptr2); + @memcpy(buffer[0..width], ptr1[0..width]); + @memcpy(ptr1[0..width], ptr2[0..width]); + @memcpy(ptr2[0..width], buffer[0..width]); return; } else { - swapHelp(threshold, buffer, ptr1, ptr2); + copy_buffer_ptr(buffer, ptr1, threshold); + copy_buffer_ptr(ptr1, ptr2, threshold); + copy_buffer_ptr(ptr2, buffer, threshold); ptr1 += threshold; ptr2 += threshold; @@ -788,11 +788,11 @@ fn swap(width_initial: usize, p1: [*]u8, p2: [*]u8) void { } } -fn swapElements(source_ptr: [*]u8, element_width: usize, index_1: usize, index_2: usize) void { +fn swapElements(copy_fn_ptr: CopyFn, source_ptr: [*]u8, element_width: usize, index_1: usize, index_2: usize) void { const element_at_i = source_ptr + (index_1 * element_width); const element_at_j = source_ptr + (index_2 * element_width); - return swap(element_width, element_at_i, element_at_j); + return swap(copy_fn_ptr, element_width, element_at_i, element_at_j); } pub fn listConcat( @@ -952,11 +952,12 @@ inline fn listReplaceInPlaceHelp( // the element we will replace var element_at_index = (list.bytes orelse unreachable) + (index * element_width); + const copy_fn_ptr = copy_element_fn(element_width); // copy out the old element - @memcpy((out_element orelse unreachable)[0..element_width], element_at_index[0..element_width]); + copy_fn_ptr((out_element orelse unreachable), element_at_index, element_width); // copy in the new element - @memcpy(element_at_index[0..element_width], (element orelse unreachable)[0..element_width]); + copy_fn_ptr(element_at_index, (element orelse unreachable), element_width); return list; } @@ -1029,6 +1030,76 @@ pub fn listConcatUtf8( } } +fn copy_element_fn(element_width: usize) CopyFn { + switch (element_width) { + @sizeOf(u8) => { + return memcpy_T(u8); + }, + @sizeOf(u16) => { + return memcpy_T(u16); + }, + @sizeOf(u32) => { + return memcpy_T(u32); + }, + @sizeOf(u64) => { + return memcpy_T(u64); + }, + @sizeOf(u128) => { + return memcpy_T(u128); + }, + @sizeOf(u256) => { + return memcpy_T(u256); + }, + else => { + return &memcpy_opaque; + }, + } +} + +fn memcpy_opaque(dst: Opaque, src: Opaque, element_width: usize) void { + @memcpy(@as([*]u8, @ptrCast(dst))[0..element_width], @as([*]u8, @ptrCast(src))[0..element_width]); +} + +fn memcpy_T(comptime T: type) CopyFn { + return &(struct { + element_width: usize, + pub fn memcpy(dst: Opaque, src: Opaque, _: usize) void { + @as(*T, @alignCast(@ptrCast(dst))).* = @as(*T, @alignCast(@ptrCast(src))).*; + } + }.memcpy); +} + +test "gen memcpy fn" { + { + const element_width = @sizeOf(u8); + const copy_fn_ptr = copy_element_fn(element_width); + var x: u8 = 7; + var y: u8 = 12; + copy_fn_ptr(@ptrCast(&x), @ptrCast(&y), element_width); + + try expectEqual(y, x); + } + + { + const element_width = @sizeOf(u32); + const copy_fn_ptr = copy_element_fn(element_width); + var x: u32 = 7; + var y: u32 = 0xDEAD_BEEF; + copy_fn_ptr(@ptrCast(&x), @ptrCast(&y), element_width); + + try expectEqual(y, x); + } + { + const element_width = @sizeOf(u512); + const copy_fn_ptr = copy_element_fn(element_width); + var x: u512 = 7; + var y: u512 = 1 << 500; + copy_fn_ptr(@ptrCast(&x), @ptrCast(&y), element_width); + + try expectEqual(y, x); + } +} + test "listConcatUtf8" { const list = RocList.fromSlice(u8, &[_]u8{ 1, 2, 3, 4 }, false); defer list.decref(1, 1, false, &rcNone); From f3cae9147c1f76ff3da3fbc8aebaf2435a91207c Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 21 Jul 2024 17:03:07 -0700 Subject: [PATCH 10/30] generate all static size memcpy's --- crates/compiler/builtins/bitcode/src/list.zig | 35 +++++-------------- 1 file changed, 9 insertions(+), 26 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index f018aba396..4bb712bf03 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -1031,40 +1031,23 @@ pub fn listConcatUtf8( } fn copy_element_fn(element_width: usize) CopyFn { - switch (element_width) { - @sizeOf(u8) => { - return memcpy_T(u8); - }, - @sizeOf(u16) => { - return memcpy_T(u16); - }, - @sizeOf(u32) => { - return memcpy_T(u32); - }, - @sizeOf(u64) => { - return memcpy_T(u64); - }, - @sizeOf(u128) => { - return memcpy_T(u128); - }, - @sizeOf(u256) => { - return memcpy_T(u256); - }, - else => { - return &memcpy_opaque; - }, - } + const max_inline = @sizeOf(u256); + return switch (element_width) { + inline 0...max_inline => |i| memcpy_sized(i), + else => &memcpy_opaque, + }; } fn memcpy_opaque(dst: Opaque, src: Opaque, element_width: usize) void { @memcpy(@as([*]u8, @ptrCast(dst))[0..element_width], @as([*]u8, @ptrCast(src))[0..element_width]); } -fn memcpy_T(comptime T: type) CopyFn { +fn memcpy_sized(comptime size: usize) CopyFn { return &(struct { - element_width: usize, pub fn memcpy(dst: Opaque, src: Opaque, _: usize) void { - @as(*T, @alignCast(@ptrCast(dst))).* = @as(*T, @alignCast(@ptrCast(src))).*; + // due to the memcpy size being known at compile time to zig, llvm can optimize it to not actually be an memcpy call. + // Instead it can inline to direct memory moves. + @memcpy(@as([*]u8, @ptrCast(dst))[0..size], @as([*]u8, @ptrCast(src))[0..size]); } }.memcpy); } From d028aa3c74fe68a0d47177e406af0df9fa204de2 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 21 Jul 2024 18:17:37 -0700 Subject: [PATCH 11/30] try a different formulation. Maybe it will avoid lookup table --- crates/compiler/builtins/bitcode/src/list.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index 4bb712bf03..4b4abbe185 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -1032,10 +1032,10 @@ pub fn listConcatUtf8( fn copy_element_fn(element_width: usize) CopyFn { const max_inline = @sizeOf(u256); - return switch (element_width) { - inline 0...max_inline => |i| memcpy_sized(i), - else => &memcpy_opaque, - }; + switch (element_width) { + inline 0...max_inline => |i| return memcpy_sized(i), + else => return &memcpy_opaque, + } } fn memcpy_opaque(dst: Opaque, src: Opaque, element_width: usize) void { From 82764dc697f894301c6e6f530dbef2ebe7b3fbe5 Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 21 Jul 2024 19:04:57 -0700 Subject: [PATCH 12/30] switch to passing the copy function into zig (implement llvm) --- crates/compiler/builtins/bitcode/src/list.zig | 148 ++++++++---------- crates/compiler/gen_llvm/src/llvm/bitcode.rs | 73 +++++++++ .../compiler/gen_llvm/src/llvm/build_list.rs | 14 +- crates/compiler/gen_llvm/src/llvm/lowlevel.rs | 9 +- crates/compiler/module/src/symbol.rs | 31 ++-- 5 files changed, 173 insertions(+), 102 deletions(-) diff --git a/crates/compiler/builtins/bitcode/src/list.zig b/crates/compiler/builtins/bitcode/src/list.zig index 4b4abbe185..ce82aa4c4e 100644 --- a/crates/compiler/builtins/bitcode/src/list.zig +++ b/crates/compiler/builtins/bitcode/src/list.zig @@ -11,7 +11,7 @@ const expectEqual = std.testing.expectEqual; const Opaque = ?[*]u8; const EqFn = *const fn (Opaque, Opaque) callconv(.C) bool; const CompareFn = *const fn (Opaque, Opaque, Opaque) callconv(.C) u8; -const CopyFn = *const fn (Opaque, Opaque, usize) void; +const CopyFn = *const fn (Opaque, Opaque) callconv(.C) void; const Inc = *const fn (?[*]u8) callconv(.C) void; const IncN = *const fn (?[*]u8, usize) callconv(.C) void; @@ -427,6 +427,7 @@ pub fn listAppendUnsafe( list: RocList, element: Opaque, element_width: usize, + copy: CopyFn, ) callconv(.C) RocList { const old_length = list.len(); var output = list; @@ -435,7 +436,7 @@ pub fn listAppendUnsafe( if (output.bytes) |bytes| { if (element) |source| { const target = bytes + old_length * element_width; - copy_element_fn(element_width)(target, source, element_width); + copy(target, source); } } @@ -450,9 +451,10 @@ fn listAppend( elements_refcounted: bool, inc: Inc, update_mode: UpdateMode, + copy: CopyFn, ) callconv(.C) RocList { const with_capacity = listReserve(list, alignment, 1, element_width, elements_refcounted, inc, update_mode); - return listAppendUnsafe(with_capacity, element, element_width); + return listAppendUnsafe(with_capacity, element, element_width, copy); } pub fn listPrepend( @@ -462,6 +464,7 @@ pub fn listPrepend( element_width: usize, elements_refcounted: bool, inc: Inc, + copy: CopyFn, ) callconv(.C) RocList { const old_length = list.len(); // TODO: properly wire in update mode. @@ -477,7 +480,7 @@ pub fn listPrepend( // finally copy in the new first element if (element) |source| { - copy_element_fn(element_width)(target, source, element_width); + copy(target, source); } } @@ -494,6 +497,7 @@ pub fn listSwap( inc: Inc, dec: Dec, update_mode: UpdateMode, + copy: CopyFn, ) callconv(.C) RocList { // Early exit to avoid swapping the same element. if (index_1 == index_2) @@ -515,11 +519,10 @@ pub fn listSwap( const source_ptr = @as([*]u8, @ptrCast(newList.bytes)); - const copy_fn_ptr = copy_element_fn(element_width); - swapElements(copy_fn_ptr, source_ptr, element_width, @as(usize, + swapElements(source_ptr, element_width, @as(usize, // We already verified that both indices are less than the stored list length, // which is usize, so casting them to usize will definitely be lossless. - @intCast(index_1)), @as(usize, @intCast(index_2))); + @intCast(index_1)), @as(usize, @intCast(index_2)), copy); return newList; } @@ -687,7 +690,15 @@ pub fn listDropAt( } } -fn partition(copy_fn_ptr: CopyFn, source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) isize { +fn partition( + source_ptr: [*]u8, + transform: Opaque, + wrapper: CompareFn, + element_width: usize, + low: isize, + high: isize, + copy: CopyFn, +) isize { const pivot = source_ptr + (@as(usize, @intCast(high)) * element_width); var i = (low - 1); // Index of smaller element and indicates the right position of pivot found so far var j = low; @@ -702,22 +713,30 @@ fn partition(copy_fn_ptr: CopyFn, source_ptr: [*]u8, transform: Opaque, wrapper: utils.Ordering.LT => { // the current element is smaller than the pivot; swap it i += 1; - swapElements(copy_fn_ptr, source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j))); + swapElements(source_ptr, element_width, @as(usize, @intCast(i)), @as(usize, @intCast(j)), copy); }, utils.Ordering.EQ, utils.Ordering.GT => {}, } } - swapElements(copy_fn_ptr, source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high))); + swapElements(source_ptr, element_width, @as(usize, @intCast(i + 1)), @as(usize, @intCast(high)), copy); return (i + 1); } -fn quicksort(copy_fn_ptr: CopyFn, source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_width: usize, low: isize, high: isize) void { +fn quicksort( + source_ptr: [*]u8, + transform: Opaque, + wrapper: CompareFn, + element_width: usize, + low: isize, + high: isize, + copy: CopyFn, +) void { if (low < high) { // partition index - const pi = partition(copy_fn_ptr, source_ptr, transform, wrapper, element_width, low, high); + const pi = partition(source_ptr, transform, wrapper, element_width, low, high, copy); - _ = quicksort(copy_fn_ptr, source_ptr, transform, wrapper, element_width, low, pi - 1); // before pi - _ = quicksort(copy_fn_ptr, source_ptr, transform, wrapper, element_width, pi + 1, high); // after pi + _ = quicksort(source_ptr, transform, wrapper, element_width, low, pi - 1, copy); // before pi + _ = quicksort(source_ptr, transform, wrapper, element_width, pi + 1, high, copy); // after pi } } @@ -732,6 +751,7 @@ pub fn listSortWith( elements_refcounted: bool, inc: Inc, dec: Dec, + copy: CopyFn, ) callconv(.C) RocList { var list = input.makeUnique(alignment, element_width, elements_refcounted, inc, dec); @@ -742,7 +762,7 @@ pub fn listSortWith( if (list.bytes) |source_ptr| { const low = 0; const high: isize = @as(isize, @intCast(list.len())) - 1; - quicksort(copy_element_fn(element_width), source_ptr, data, caller, element_width, low, high); + quicksort(source_ptr, data, caller, element_width, low, high, copy); } return list; @@ -750,16 +770,21 @@ pub fn listSortWith( // SWAP ELEMENTS -fn swap(copy_fn_ptr: CopyFn, element_width: usize, p1: [*]u8, p2: [*]u8) void { - const threshold: usize = @sizeOf(u256); +fn swap( + element_width: usize, + p1: [*]u8, + p2: [*]u8, + copy: CopyFn, +) void { + const threshold: usize = 64; var buffer_actual: [threshold]u8 = undefined; const buffer: [*]u8 = buffer_actual[0..]; if (element_width <= threshold) { - copy_fn_ptr(buffer, p1, element_width); - copy_fn_ptr(p1, p2, element_width); - copy_fn_ptr(p2, buffer, element_width); + copy(buffer, p1); + copy(p1, p2); + copy(p2, buffer); return; } @@ -767,8 +792,6 @@ fn swap(copy_fn_ptr: CopyFn, element_width: usize, p1: [*]u8, p2: [*]u8) void { var ptr1 = p1; var ptr2 = p2; - - const copy_buffer_ptr = comptime copy_element_fn(threshold); while (true) { if (width < threshold) { @memcpy(buffer[0..width], ptr1[0..width]); @@ -776,9 +799,9 @@ fn swap(copy_fn_ptr: CopyFn, element_width: usize, p1: [*]u8, p2: [*]u8) void { @memcpy(ptr2[0..width], buffer[0..width]); return; } else { - copy_buffer_ptr(buffer, ptr1, threshold); - copy_buffer_ptr(ptr1, ptr2, threshold); - copy_buffer_ptr(ptr2, buffer, threshold); + @memcpy(buffer[0..threshold], ptr1[0..threshold]); + @memcpy(ptr1[0..threshold], ptr2[0..threshold]); + @memcpy(ptr2[0..threshold], buffer[0..threshold]); ptr1 += threshold; ptr2 += threshold; @@ -788,11 +811,17 @@ fn swap(copy_fn_ptr: CopyFn, element_width: usize, p1: [*]u8, p2: [*]u8) void { } } -fn swapElements(copy_fn_ptr: CopyFn, source_ptr: [*]u8, element_width: usize, index_1: usize, index_2: usize) void { +fn swapElements( + source_ptr: [*]u8, + element_width: usize, + index_1: usize, + index_2: usize, + copy: CopyFn, +) void { const element_at_i = source_ptr + (index_1 * element_width); const element_at_j = source_ptr + (index_2 * element_width); - return swap(copy_fn_ptr, element_width, element_at_i, element_at_j); + return swap(element_width, element_at_i, element_at_j, copy); } pub fn listConcat( @@ -909,6 +938,7 @@ pub fn listReplaceInPlace( element: Opaque, element_width: usize, out_element: ?[*]u8, + copy: CopyFn, ) callconv(.C) RocList { // INVARIANT: bounds checking happens on the roc side // @@ -917,7 +947,7 @@ pub fn listReplaceInPlace( // so we don't do a bounds check here. Hence, the list is also non-empty, // because inserting into an empty list is always out of bounds, // and it's always safe to cast index to usize. - return listReplaceInPlaceHelp(list, @as(usize, @intCast(index)), element, element_width, out_element); + return listReplaceInPlaceHelp(list, @as(usize, @intCast(index)), element, element_width, out_element, copy); } pub fn listReplace( @@ -930,6 +960,7 @@ pub fn listReplace( inc: Inc, dec: Dec, out_element: ?[*]u8, + copy: CopyFn, ) callconv(.C) RocList { // INVARIANT: bounds checking happens on the roc side // @@ -939,7 +970,7 @@ pub fn listReplace( // because inserting into an empty list is always out of bounds, // and it's always safe to cast index to usize. // because inserting into an empty list is always out of bounds - return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width, elements_refcounted, inc, dec), @as(usize, @intCast(index)), element, element_width, out_element); + return listReplaceInPlaceHelp(list.makeUnique(alignment, element_width, elements_refcounted, inc, dec), @as(usize, @intCast(index)), element, element_width, out_element, copy); } inline fn listReplaceInPlaceHelp( @@ -948,16 +979,16 @@ inline fn listReplaceInPlaceHelp( element: Opaque, element_width: usize, out_element: ?[*]u8, + copy: CopyFn, ) RocList { // the element we will replace var element_at_index = (list.bytes orelse unreachable) + (index * element_width); - const copy_fn_ptr = copy_element_fn(element_width); // copy out the old element - copy_fn_ptr((out_element orelse unreachable), element_at_index, element_width); + copy((out_element orelse unreachable), element_at_index); // copy in the new element - copy_fn_ptr(element_at_index, (element orelse unreachable), element_width); + copy(element_at_index, (element orelse unreachable)); return list; } @@ -1030,59 +1061,6 @@ pub fn listConcatUtf8( } } -fn copy_element_fn(element_width: usize) CopyFn { - const max_inline = @sizeOf(u256); - switch (element_width) { - inline 0...max_inline => |i| return memcpy_sized(i), - else => return &memcpy_opaque, - } -} - -fn memcpy_opaque(dst: Opaque, src: Opaque, element_width: usize) void { - @memcpy(@as([*]u8, @ptrCast(dst))[0..element_width], @as([*]u8, @ptrCast(src))[0..element_width]); -} - -fn memcpy_sized(comptime size: usize) CopyFn { - return &(struct { - pub fn memcpy(dst: Opaque, src: Opaque, _: usize) void { - // due to the memcpy size being known at compile time to zig, llvm can optimize it to not actually be an memcpy call. - // Instead it can inline to direct memory moves. - @memcpy(@as([*]u8, @ptrCast(dst))[0..size], @as([*]u8, @ptrCast(src))[0..size]); - } - }.memcpy); -} - -test "gen memcpy fn" { - { - const element_width = @sizeOf(u8); - const copy_fn_ptr = copy_element_fn(element_width); - var x: u8 = 7; - var y: u8 = 12; - copy_fn_ptr(@ptrCast(&x), @ptrCast(&y), element_width); - - try expectEqual(y, x); - } - - { - const element_width = @sizeOf(u32); - const copy_fn_ptr = copy_element_fn(element_width); - var x: u32 = 7; - var y: u32 = 0xDEAD_BEEF; - copy_fn_ptr(@ptrCast(&x), @ptrCast(&y), element_width); - - try expectEqual(y, x); - } - { - const element_width = @sizeOf(u512); - const copy_fn_ptr = copy_element_fn(element_width); - var x: u512 = 7; - var y: u512 = 1 << 500; - copy_fn_ptr(@ptrCast(&x), @ptrCast(&y), element_width); - - try expectEqual(y, x); - } -} - test "listConcatUtf8" { const list = RocList.fromSlice(u8, &[_]u8{ 1, 2, 3, 4 }, false); defer list.decref(1, 1, false, &rcNone); diff --git a/crates/compiler/gen_llvm/src/llvm/bitcode.rs b/crates/compiler/gen_llvm/src/llvm/bitcode.rs index 14408ad3fa..74e73d1a7d 100644 --- a/crates/compiler/gen_llvm/src/llvm/bitcode.rs +++ b/crates/compiler/gen_llvm/src/llvm/bitcode.rs @@ -5,6 +5,7 @@ use crate::llvm::build::{ FAST_CALL_CONV, }; use crate::llvm::convert::basic_type_from_layout; +use crate::llvm::memcpy::build_memcpy; use crate::llvm::refcounting::{ decrement_refcount_layout, increment_n_refcount_layout, increment_refcount_layout, }; @@ -736,6 +737,78 @@ pub fn build_compare_wrapper<'a, 'ctx>( function_value } +pub fn build_copy_wrapper<'a, 'ctx>( + env: &Env<'a, 'ctx, '_>, + layout_interner: &STLayoutInterner<'a>, + layout_ids: &mut LayoutIds<'a>, + layout: InLayout<'a>, +) -> FunctionValue<'ctx> { + let block = env.builder.get_insert_block().expect("to be in a function"); + let di_location = env.builder.get_current_debug_location().unwrap(); + + let symbol = Symbol::GENERIC_COPY_REF; + let fn_name = layout_ids + .get(symbol, &layout_interner.get_repr(layout)) + .to_symbol_string(symbol, &env.interns); + + let function_value = match env.module.get_function(fn_name.as_str()) { + Some(function_value) => function_value, + None => { + let arg_type = env.context.i8_type().ptr_type(AddressSpace::default()); + + let function_value = crate::llvm::refcounting::build_header_help( + env, + &fn_name, + env.context.void_type().into(), + &[arg_type.into(), arg_type.into()], + ); + + // called from zig, must use C calling convention + function_value.set_call_conventions(C_CALL_CONV); + + let kind_id = Attribute::get_named_enum_kind_id("alwaysinline"); + debug_assert!(kind_id > 0); + let attr = env.context.create_enum_attribute(kind_id, 0); + function_value.add_attribute(AttributeLoc::Function, attr); + + let entry = env.context.append_basic_block(function_value, "entry"); + env.builder.position_at_end(entry); + + debug_info_init!(env, function_value); + + let mut it = function_value.get_param_iter(); + let dst_ptr = it.next().unwrap().into_pointer_value(); + let src_ptr = it.next().unwrap().into_pointer_value(); + + dst_ptr.set_name(Symbol::ARG_1.as_str(&env.interns)); + src_ptr.set_name(Symbol::ARG_2.as_str(&env.interns)); + + let repr = layout_interner.get_repr(layout); + let value_type = basic_type_from_layout(env, layout_interner, repr) + .ptr_type(AddressSpace::default()); + + let dst_cast = env + .builder + .new_build_pointer_cast(dst_ptr, value_type, "load_opaque"); + + let src_cast = env + .builder + .new_build_pointer_cast(src_ptr, value_type, "load_opaque"); + + build_memcpy(env, layout_interner, repr, dst_cast, src_cast); + + env.builder.new_build_return(None); + + function_value + } + }; + + env.builder.position_at_end(block); + env.builder.set_current_debug_location(di_location); + + function_value +} + enum BitcodeReturnValue<'ctx> { List(PointerValue<'ctx>), Str(PointerValue<'ctx>), diff --git a/crates/compiler/gen_llvm/src/llvm/build_list.rs b/crates/compiler/gen_llvm/src/llvm/build_list.rs index d2ad307804..bc50be56f6 100644 --- a/crates/compiler/gen_llvm/src/llvm/build_list.rs +++ b/crates/compiler/gen_llvm/src/llvm/build_list.rs @@ -12,7 +12,7 @@ use roc_mono::layout::{ Builtin, InLayout, Layout, LayoutIds, LayoutInterner, LayoutRepr, STLayoutInterner, }; -use super::bitcode::{build_inc_wrapper, call_list_bitcode_fn, BitcodeReturns}; +use super::bitcode::{build_copy_wrapper, build_inc_wrapper, call_list_bitcode_fn, BitcodeReturns}; use super::build::{ create_entry_block_alloca, load_roc_value, store_roc_value, use_roc_value, BuilderExt, }; @@ -235,16 +235,19 @@ pub(crate) fn list_release_excess_capacity<'a, 'ctx>( pub(crate) fn list_append_unsafe<'a, 'ctx>( env: &Env<'a, 'ctx, '_>, layout_interner: &STLayoutInterner<'a>, + layout_ids: &mut LayoutIds<'a>, original_wrapper: StructValue<'ctx>, element: BasicValueEnum<'ctx>, element_layout: InLayout<'a>, ) -> BasicValueEnum<'ctx> { + let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout); call_list_bitcode_fn_1( env, original_wrapper, &[ pass_element_as_opaque(env, layout_interner, element, element_layout), layout_width(env, layout_interner, element_layout), + copy_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_APPEND_UNSAFE, ) @@ -260,6 +263,7 @@ pub(crate) fn list_prepend<'a, 'ctx>( element_layout: InLayout<'a>, ) -> BasicValueEnum<'ctx> { let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout); + let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout); call_list_bitcode_fn_1( env, original_wrapper, @@ -269,6 +273,7 @@ pub(crate) fn list_prepend<'a, 'ctx>( layout_width(env, layout_interner, element_layout), layout_refcounted(env, layout_interner, element_layout), inc_element_fn.as_global_value().as_pointer_value().into(), + copy_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_PREPEND, ) @@ -310,6 +315,7 @@ pub(crate) fn list_swap<'a, 'ctx>( ) -> BasicValueEnum<'ctx> { let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout); let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout); + let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout); call_list_bitcode_fn_1( env, @@ -323,6 +329,7 @@ pub(crate) fn list_swap<'a, 'ctx>( inc_element_fn.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(), pass_update_mode(env, update_mode), + copy_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_SWAP, ) @@ -397,6 +404,7 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>( layout_interner.get_repr(element_layout), ); let element_ptr = create_entry_block_alloca(env, element_type, "output_element_as_opaque"); + let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout); // Assume the bounds have already been checked earlier // (e.g. by List.replace or List.set, which wrap List.#replaceUnsafe) @@ -409,6 +417,7 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>( pass_element_as_opaque(env, layout_interner, element, element_layout), layout_width(env, layout_interner, element_layout), pass_as_opaque(env, element_ptr), + copy_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_REPLACE_IN_PLACE, ), @@ -429,6 +438,7 @@ pub(crate) fn list_replace_unsafe<'a, 'ctx>( inc_element_fn.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(), pass_as_opaque(env, element_ptr), + copy_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_REPLACE, ) @@ -536,6 +546,7 @@ pub(crate) fn list_sort_with<'a, 'ctx>( ) -> BasicValueEnum<'ctx> { let inc_element_fn = build_inc_wrapper(env, layout_interner, layout_ids, element_layout); let dec_element_fn = build_dec_wrapper(env, layout_interner, layout_ids, element_layout); + let copy_fn = build_copy_wrapper(env, layout_interner, layout_ids, element_layout); call_list_bitcode_fn_1( env, list.into_struct_value(), @@ -549,6 +560,7 @@ pub(crate) fn list_sort_with<'a, 'ctx>( layout_refcounted(env, layout_interner, element_layout), inc_element_fn.as_global_value().as_pointer_value().into(), dec_element_fn.as_global_value().as_pointer_value().into(), + copy_fn.as_global_value().as_pointer_value().into(), ], bitcode::LIST_SORT_WITH, ) diff --git a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs index c1f7b583c3..6ddcad1f7e 100644 --- a/crates/compiler/gen_llvm/src/llvm/lowlevel.rs +++ b/crates/compiler/gen_llvm/src/llvm/lowlevel.rs @@ -670,7 +670,14 @@ pub(crate) fn run_low_level<'a, 'ctx>( let original_wrapper = scope.load_symbol(&args[0]).into_struct_value(); let (elem, elem_layout) = scope.load_symbol_and_layout(&args[1]); - list_append_unsafe(env, layout_interner, original_wrapper, elem, elem_layout) + list_append_unsafe( + env, + layout_interner, + layout_ids, + original_wrapper, + elem, + elem_layout, + ) } ListPrepend => { // List.prepend : List elem, elem -> List elem diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index de10bc509d..2713be16ae 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1175,41 +1175,42 @@ define_builtins! { 16 GENERIC_EQ_REF: "#generic_eq_by_ref" // equality of arbitrary layouts, passed as an opaque pointer 17 GENERIC_RC_REF: "#generic_rc_by_ref" // refcount of arbitrary layouts, passed as an opaque pointer + 18 GENERIC_COPY_REF: "#generic_copy_by_ref" // copy of arbitrary layouts, passed as an opaque pointer - 18 GENERIC_EQ: "#generic_eq" // internal function that checks generic equality + 19 GENERIC_EQ: "#generic_eq" // internal function that checks generic equality // a user-defined function that we need to capture in a closure // see e.g. Set.walk - 19 USER_FUNCTION: "#user_function" + 20 USER_FUNCTION: "#user_function" // A caller (wrapper) that we pass to zig for it to be able to call Roc functions - 20 ZIG_FUNCTION_CALLER: "#zig_function_caller" + 21 ZIG_FUNCTION_CALLER: "#zig_function_caller" // a caller (wrapper) for comparison - 21 GENERIC_COMPARE_REF: "#generic_compare_ref" + 22 GENERIC_COMPARE_REF: "#generic_compare_ref" // used to initialize parameters in borrow.rs - 22 EMPTY_PARAM: "#empty_param" + 23 EMPTY_PARAM: "#empty_param" // used by the dev backend to store the pointer to where to store large return types - 23 RET_POINTER: "#ret_pointer" + 24 RET_POINTER: "#ret_pointer" // used in wasm dev backend to mark temporary values in the VM stack - 24 WASM_TMP: "#wasm_tmp" + 25 WASM_TMP: "#wasm_tmp" // the _ used in mono when a specialized symbol is deleted - 25 REMOVED_SPECIALIZATION: "#removed_specialization" + 26 REMOVED_SPECIALIZATION: "#removed_specialization" // used in dev backend - 26 DEV_TMP: "#dev_tmp" - 27 DEV_TMP2: "#dev_tmp2" - 28 DEV_TMP3: "#dev_tmp3" - 29 DEV_TMP4: "#dev_tmp4" - 30 DEV_TMP5: "#dev_tmp5" + 27 DEV_TMP: "#dev_tmp" + 28 DEV_TMP2: "#dev_tmp2" + 29 DEV_TMP3: "#dev_tmp3" + 30 DEV_TMP4: "#dev_tmp4" + 31 DEV_TMP5: "#dev_tmp5" - 31 ATTR_INVALID: "#attr_invalid" + 32 ATTR_INVALID: "#attr_invalid" - 32 CLONE: "#clone" // internal function that clones a value into a buffer + 33 CLONE: "#clone" // internal function that clones a value into a buffer } // Fake module for synthesizing and storing derived implementations 1 DERIVED_SYNTH: "#Derived" => { From b8fb83af86e3c2beaedadd93c57361348cb12d2d Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 21 Jul 2024 19:53:36 -0700 Subject: [PATCH 13/30] get wasm dev backend working --- crates/compiler/gen_wasm/src/backend.rs | 25 ++++++++++- crates/compiler/gen_wasm/src/low_level.rs | 28 +++++++++++-- .../compiler/mono/src/code_gen_help/copy.rs | 38 +++++++++++++++++ crates/compiler/mono/src/code_gen_help/mod.rs | 42 ++++++++++++++++++- 4 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 crates/compiler/mono/src/code_gen_help/copy.rs diff --git a/crates/compiler/gen_wasm/src/backend.rs b/crates/compiler/gen_wasm/src/backend.rs index 88dd6ba3aa..3293f6b760 100644 --- a/crates/compiler/gen_wasm/src/backend.rs +++ b/crates/compiler/gen_wasm/src/backend.rs @@ -1981,12 +1981,33 @@ impl<'a, 'r> WasmBackend<'a, 'r> { self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper); } - self.get_existing_refcount_fn_index(proc_symbol, layout, op) + self.get_existing_helper_fn_index(proc_symbol, layout, op) + } + + /// Generate a copy helper procedure and return a pointer (table index) to it + /// This allows it to be indirectly called from Zig code + pub fn get_copy_fn_index(&mut self, layout: InLayout<'a>) -> u32 { + let ident_ids = self + .interns + .all_ident_ids + .get_mut(&self.env.module_id) + .unwrap(); + + let (proc_symbol, new_specializations) = + self.helper_proc_gen + .gen_copy_proc(ident_ids, self.layout_interner, layout); + + // If any new specializations were created, register their symbol data + for (spec_sym, spec_layout) in new_specializations.into_iter() { + self.register_helper_proc(spec_sym, spec_layout, ProcSource::Helper); + } + + self.get_existing_helper_fn_index(proc_symbol, layout, HelperOp::IndirectCopy) } /// return a pointer (table index) to a refcount helper procedure. /// This allows it to be indirectly called from Zig code - pub fn get_existing_refcount_fn_index( + pub fn get_existing_helper_fn_index( &mut self, proc_symbol: Symbol, layout: InLayout<'a>, diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index 0c42c82d4f..2d88a79b01 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -352,7 +352,7 @@ impl<'a> LowLevelCall<'a> { elem_layout.stack_size_and_alignment(backend.layout_interner); let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout); - let dec_fn = backend.get_existing_refcount_fn_index( + let dec_fn = backend.get_existing_helper_fn_index( dec_fn_sym, elem_in_layout, HelperOp::IndirectDec, @@ -476,6 +476,7 @@ impl<'a> LowLevelCall<'a> { build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc); let dec_fn_ptr = build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec); + let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout); // Load all the arguments for Zig // (List return pointer) i32 @@ -488,6 +489,7 @@ impl<'a> LowLevelCall<'a> { // inc: Inc i32 // dec: Dec i32 // out_element: ?[*]u8, i32 + // copy: CopyFn, i32 let code_builder = &mut backend.code_builder; @@ -517,6 +519,7 @@ impl<'a> LowLevelCall<'a> { code_builder.i32_const((ret_offset + ret_elem_offset) as i32); code_builder.i32_add(); } + code_builder.i32_const(copy_fn_ptr); // There is an in-place version of this but we don't use it for dev backends. No morphic_lib analysis. backend.call_host_fn_after_loading_args(bitcode::LIST_REPLACE); @@ -689,16 +692,18 @@ impl<'a> LowLevelCall<'a> { let list: Symbol = self.arguments[0]; let elem: Symbol = self.arguments[1]; - let elem_layout = unwrap_list_elem_layout(self.ret_layout_raw); - let elem_width = backend.layout_interner.stack_size(elem_layout); + let elem_in_layout = unwrap_list_elem_layout(self.ret_layout_raw); + let elem_width = backend.layout_interner.stack_size(elem_in_layout); let (elem_local, elem_offset, _) = - ensure_symbol_is_in_memory(backend, elem, elem_layout, backend.env.arena); + ensure_symbol_is_in_memory(backend, elem, elem_in_layout, backend.env.arena); + let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout); // Zig arguments Wasm types // (return pointer) i32 // list: RocList i32 // element: Opaque i32 // element_width: usize i32 + // copy: CopyFn, i32 // return pointer and list backend.storage.load_symbols_for_call( @@ -715,6 +720,7 @@ impl<'a> LowLevelCall<'a> { } backend.code_builder.i32_const(elem_width as i32); + backend.code_builder.i32_const(copy_fn_ptr); backend.call_host_fn_after_loading_args(bitcode::LIST_APPEND_UNSAFE); } @@ -734,6 +740,7 @@ impl<'a> LowLevelCall<'a> { let elem_refcounted = backend.layout_interner.contains_refcounted(elem_in_layout); let inc_fn_ptr = build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc); + let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout); // Zig arguments Wasm types // (return pointer) i32 @@ -743,6 +750,7 @@ impl<'a> LowLevelCall<'a> { // element_width: usize i32 // element_refcounted: bool i32 // inc: Inc i32 + // copy: CopyFn, i32 // return pointer and list backend.storage.load_symbols_for_call( @@ -762,6 +770,7 @@ impl<'a> LowLevelCall<'a> { backend.code_builder.i32_const(elem_width as i32); backend.code_builder.i32_const(elem_refcounted as i32); backend.code_builder.i32_const(inc_fn_ptr); + backend.code_builder.i32_const(copy_fn_ptr); backend.call_host_fn_after_loading_args(bitcode::LIST_PREPEND); } @@ -870,6 +879,7 @@ impl<'a> LowLevelCall<'a> { build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc); let dec_fn_ptr = build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec); + let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout); // Zig arguments Wasm types // (return pointer) i32 @@ -882,6 +892,7 @@ impl<'a> LowLevelCall<'a> { // inc: Inc i32 // dec: Dec i32 // update_mode: UpdateMode, i32 + // copy: CopyFn, i32 // Load the return pointer and the list backend.storage.load_symbols_for_call( @@ -900,6 +911,7 @@ impl<'a> LowLevelCall<'a> { backend.code_builder.i32_const(inc_fn_ptr); backend.code_builder.i32_const(dec_fn_ptr); backend.code_builder.i32_const(UPDATE_MODE_IMMUTABLE); + backend.code_builder.i32_const(copy_fn_ptr); backend.call_host_fn_after_loading_args(bitcode::LIST_SWAP); } @@ -2784,6 +2796,7 @@ pub fn call_higher_order_lowlevel<'a>( build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectInc); let dec_fn_ptr = build_refcount_element_fn(backend, elem_in_layout, HelperOp::IndirectDec); + let copy_fn_ptr = build_copy_element_fn(backend, elem_in_layout); let cb = &mut backend.code_builder; @@ -2798,6 +2811,7 @@ pub fn call_higher_order_lowlevel<'a>( // element_refcounted: bool i32 // inc: Inc i32 // dec: Dec i32 + // copy: CopyFn i32 backend.storage.load_symbols(cb, &[return_sym, *xs]); cb.i32_const(wrapper_fn_ptr); @@ -2817,6 +2831,7 @@ pub fn call_higher_order_lowlevel<'a>( cb.i32_const(elem_refcounted as i32); cb.i32_const(inc_fn_ptr); cb.i32_const(dec_fn_ptr); + cb.i32_const(copy_fn_ptr); backend.call_host_fn_after_loading_args(bitcode::LIST_SORT_WITH); } @@ -2869,3 +2884,8 @@ fn build_refcount_element_fn<'a>( let rc_fn = backend.get_refcount_fn_index(elem_layout, rc_op); backend.get_fn_ptr(rc_fn) } + +fn build_copy_element_fn<'a>(backend: &mut WasmBackend<'a, '_>, elem_layout: InLayout<'a>) -> i32 { + let copy_fn = backend.get_copy_fn_index(elem_layout); + backend.get_fn_ptr(copy_fn) +} diff --git a/crates/compiler/mono/src/code_gen_help/copy.rs b/crates/compiler/mono/src/code_gen_help/copy.rs new file mode 100644 index 0000000000..40019e1fb1 --- /dev/null +++ b/crates/compiler/mono/src/code_gen_help/copy.rs @@ -0,0 +1,38 @@ +use roc_module::symbol::{IdentIds, Symbol}; + +use crate::ir::{Expr, Stmt}; +use crate::layout::{InLayout, Layout, STLayoutInterner}; + +use super::{CodeGenHelp, Context}; + +const ARG_1: Symbol = Symbol::ARG_1; +const ARG_2: Symbol = Symbol::ARG_2; + +pub fn copy_indirect<'a>( + root: &mut CodeGenHelp<'a>, + ident_ids: &mut IdentIds, + _ctx: &mut Context<'a>, + _layout_interner: &mut STLayoutInterner<'a>, + layout: InLayout<'a>, +) -> Stmt<'a> { + let arena = root.arena; + let unit = root.create_symbol(ident_ids, "unit"); + let loaded = root.create_symbol(ident_ids, "loaded"); + Stmt::Let( + loaded, + Expr::ptr_load(arena.alloc(ARG_2)), + layout, + arena.alloc( + // + Stmt::Let( + unit, + Expr::ptr_store(arena.alloc([ARG_1, loaded])), + Layout::UNIT, + arena.alloc( + // + Stmt::Ret(unit), + ), + ), + ), + ) +} diff --git a/crates/compiler/mono/src/code_gen_help/mod.rs b/crates/compiler/mono/src/code_gen_help/mod.rs index c4478d5688..c2dfb107d8 100644 --- a/crates/compiler/mono/src/code_gen_help/mod.rs +++ b/crates/compiler/mono/src/code_gen_help/mod.rs @@ -14,6 +14,7 @@ use crate::layout::{ STLayoutInterner, UnionLayout, }; +mod copy; mod equality; mod refcount; @@ -43,6 +44,7 @@ pub enum HelperOp { Reset, ResetRef, Eq, + IndirectCopy, } impl HelperOp { @@ -57,7 +59,7 @@ impl HelperOp { pub fn is_indirect(&self) -> bool { matches!( self, - Self::IndirectInc | Self::IndirectIncN | Self::IndirectDec + Self::IndirectInc | Self::IndirectIncN | Self::IndirectDec | Self::IndirectCopy ) } } @@ -275,6 +277,25 @@ impl<'a> CodeGenHelp<'a> { (expr, ctx.new_linker_data) } + /// Generate a copy procedure, *without* a Call expression. + /// *This method should be rarely used* - only when the proc is to be called from Zig. + pub fn gen_copy_proc( + &mut self, + ident_ids: &mut IdentIds, + layout_interner: &mut STLayoutInterner<'a>, + layout: InLayout<'a>, + ) -> (Symbol, Vec<'a, (Symbol, ProcLayout<'a>)>) { + let mut ctx = Context { + new_linker_data: Vec::new_in(self.arena), + recursive_union: None, + op: HelperOp::IndirectCopy, + }; + + let proc_name = self.find_or_create_proc(ident_ids, &mut ctx, layout_interner, layout); + + (proc_name, ctx.new_linker_data) + } + // ============================================================================ // // CALL SPECIALIZED OP @@ -321,6 +342,7 @@ impl<'a> CodeGenHelp<'a> { IndirectIncN => (LAYOUT_UNIT, arena.alloc([ptr_arg, self.layout_isize])), IndirectInc => (LAYOUT_UNIT, arena.alloc([ptr_arg])), Eq => (LAYOUT_BOOL, self.arena.alloc([arg, arg])), + IndirectCopy => (LAYOUT_UNIT, self.arena.alloc([ptr_arg, ptr_arg])), } }; @@ -430,6 +452,10 @@ impl<'a> CodeGenHelp<'a> { LAYOUT_BOOL, equality::eq_generic(self, ident_ids, ctx, layout_interner, layout), ), + IndirectCopy => ( + LAYOUT_UNIT, + copy::copy_indirect(self, ident_ids, ctx, layout_interner, layout), + ), }; let args: &'a [(InLayout<'a>, Symbol)] = { @@ -452,6 +478,11 @@ impl<'a> CodeGenHelp<'a> { self.arena.alloc([(ptr_layout, ARG_1)]) } Eq => self.arena.alloc([roc_value, (layout, ARG_2)]), + IndirectCopy => { + let ptr_layout = + layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout)); + self.arena.alloc([(ptr_layout, ARG_1), (ptr_layout, ARG_2)]) + } } }; @@ -524,6 +555,15 @@ impl<'a> CodeGenHelp<'a> { result: LAYOUT_BOOL, niche: Niche::NONE, }, + HelperOp::IndirectCopy => { + let ptr_layout = layout_interner.insert_direct_no_semantic(LayoutRepr::Ptr(layout)); + + ProcLayout { + arguments: self.arena.alloc([ptr_layout, ptr_layout]), + result: LAYOUT_UNIT, + niche: Niche::NONE, + } + } }; (proc_symbol, proc_layout) From 5ad85bcd087c2e20f2779a9710dbf129691ccfcc Mon Sep 17 00:00:00 2001 From: Brendan Hansknecht Date: Sun, 21 Jul 2024 20:07:52 -0700 Subject: [PATCH 14/30] get native dev backend working --- crates/compiler/gen_dev/src/generic64/mod.rs | 77 ++++++++++++++------ crates/compiler/gen_dev/src/lib.rs | 51 +++++++++---- 2 files changed, 92 insertions(+), 36 deletions(-) diff --git a/crates/compiler/gen_dev/src/generic64/mod.rs b/crates/compiler/gen_dev/src/generic64/mod.rs index da1e701cd8..62869ab1df 100644 --- a/crates/compiler/gen_dev/src/generic64/mod.rs +++ b/crates/compiler/gen_dev/src/generic64/mod.rs @@ -2318,6 +2318,22 @@ impl< refcount_proc_name } + fn build_indirect_copy(&mut self, layout: InLayout<'a>) -> Symbol { + let ident_ids = self + .interns + .all_ident_ids + .get_mut(&self.env.module_id) + .unwrap(); + + let (refcount_proc_name, linker_data) = + self.helper_proc_gen + .gen_copy_proc(ident_ids, self.layout_interner, layout); + + self.helper_proc_symbols_mut().extend(linker_data); + + refcount_proc_name + } + fn build_higher_order_lowlevel( &mut self, dst: &Symbol, @@ -2413,8 +2429,9 @@ impl< // Load element_refcounted argument (bool). self.load_layout_refcounted(element_layout, Symbol::DEV_TMP3); - let inc_elem_fn = self.increment_fn_pointer(element_layout); - let dec_elem_fn = self.decrement_fn_pointer(element_layout); + let inc_fn_ptr = self.increment_fn_pointer(element_layout); + let dec_fn_ptr = self.decrement_fn_pointer(element_layout); + let copy_fn_ptr = self.copy_fn_pointer(element_layout); // input: RocList, // caller: CompareFn, @@ -2426,6 +2443,7 @@ impl< // element_refcounted: bool, // inc: Inc, // dec: Dec, + // copy: CopyFn, let arguments = [ xs, @@ -2436,8 +2454,9 @@ impl< alignment, element_width, Symbol::DEV_TMP3, - inc_elem_fn, - dec_elem_fn, + inc_fn_ptr, + dec_fn_ptr, + copy_fn_ptr, ]; let layouts = [ @@ -2451,6 +2470,7 @@ impl< Layout::BOOL, usize_, usize_, + usize_, ]; self.build_fn_call_stack_return( @@ -2493,8 +2513,8 @@ impl< // Load element_refcounted argument (bool). self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3); - let inc_elem_fn = self.increment_fn_pointer(elem_layout); - let dec_elem_fn = self.decrement_fn_pointer(elem_layout); + let inc_fn_ptr = self.increment_fn_pointer(elem_layout); + let dec_fn_ptr = self.decrement_fn_pointer(elem_layout); // Setup the return location. let base_offset = @@ -2510,9 +2530,9 @@ impl< // element_refcounted Symbol::DEV_TMP3, // inc - inc_elem_fn, + inc_fn_ptr, // dec - dec_elem_fn, + dec_fn_ptr, ]; let usize_layout = Layout::U64; let lowlevel_arg_layouts = [ @@ -2564,7 +2584,7 @@ impl< // Load element_refcounted argument (bool). self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3); - let inc_elem_fn = self.increment_fn_pointer(elem_layout); + let inc_fn_ptr = self.increment_fn_pointer(elem_layout); // Setup the return location. let base_offset = @@ -2580,7 +2600,7 @@ impl< // element_refcounted Symbol::DEV_TMP3, // Inc element fn - inc_elem_fn, + inc_fn_ptr, ]; let layout_usize = Layout::U64; let lowlevel_arg_layouts = [ @@ -2637,7 +2657,7 @@ impl< self.load_layout_stack_size(element_layout, Symbol::DEV_TMP2); self.load_layout_refcounted(element_layout, Symbol::DEV_TMP3); - let inc_elem_fn = self.increment_fn_pointer(element_layout); + let inc_fn_ptr = self.increment_fn_pointer(element_layout); // Load UpdateMode.Immutable argument (0u8) let u8_layout = Layout::U8; @@ -2664,7 +2684,7 @@ impl< // element_refcounted Symbol::DEV_TMP3, // Inc element fn - inc_elem_fn, + inc_fn_ptr, // update_mode Symbol::DEV_TMP4, ]; @@ -2733,6 +2753,7 @@ impl< let base_offset = self.storage_manager .claim_stack_area_layout(self.layout_interner, *dst, *ret_layout); + let copy_fn_ptr = self.copy_fn_pointer(elem_layout); let lowlevel_args = [ list, @@ -2740,8 +2761,11 @@ impl< Symbol::DEV_TMP, // element_width Symbol::DEV_TMP2, + // copy + copy_fn_ptr, ]; - let lowlevel_arg_layouts = [list_layout, Layout::U64, Layout::U64]; + let usize_layout = Layout::U64; + let lowlevel_arg_layouts = [list_layout, Layout::U64, Layout::U64, usize_layout]; self.build_fn_call( &Symbol::DEV_TMP3, @@ -2844,8 +2868,9 @@ impl< // Load element_refcounted argument (bool). self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP4); - let inc_elem_fn = self.increment_fn_pointer(elem_layout); - let dec_elem_fn = self.decrement_fn_pointer(elem_layout); + let inc_fn_ptr = self.increment_fn_pointer(elem_layout); + let dec_fn_ptr = self.decrement_fn_pointer(elem_layout); + let copy_fn_ptr = self.copy_fn_pointer(elem_layout); // Setup the return location. let base_offset = @@ -2891,9 +2916,10 @@ impl< Symbol::DEV_TMP2, Symbol::DEV_TMP3, Symbol::DEV_TMP4, - inc_elem_fn, - dec_elem_fn, + inc_fn_ptr, + dec_fn_ptr, Symbol::DEV_TMP5, + copy_fn_ptr, ]; let lowlevel_arg_layouts = [ list_layout, @@ -2905,6 +2931,7 @@ impl< u64_layout, u64_layout, u64_layout, + u64_layout, ]; let out = self.debug_symbol("out"); @@ -2955,8 +2982,8 @@ impl< // Load element_refcounted argument (bool). self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP3); - let inc_elem_fn = self.increment_fn_pointer(elem_layout); - let dec_elem_fn = self.decrement_fn_pointer(elem_layout); + let inc_fn_ptr = self.increment_fn_pointer(elem_layout); + let dec_fn_ptr = self.decrement_fn_pointer(elem_layout); // Setup the return location. let base_offset = @@ -2973,8 +3000,8 @@ impl< Symbol::DEV_TMP2, // element_refcounted Symbol::DEV_TMP3, - inc_elem_fn, - dec_elem_fn, + inc_fn_ptr, + dec_fn_ptr, ]; let lowlevel_arg_layouts = [ list_a_layout, @@ -3042,7 +3069,8 @@ impl< // Load element_refcounted argument (bool). self.load_layout_refcounted(elem_layout, Symbol::DEV_TMP4); - let inc_elem_fn = self.increment_fn_pointer(elem_layout); + let inc_fn_ptr = self.increment_fn_pointer(elem_layout); + let copy_fn_ptr = self.copy_fn_pointer(elem_layout); // Setup the return location. let base_offset = @@ -3060,7 +3088,9 @@ impl< // element_refcounted Symbol::DEV_TMP4, // inc - inc_elem_fn, + inc_fn_ptr, + // copy + copy_fn_ptr, ]; let usize_layout = Layout::U64; let lowlevel_arg_layouts = [ @@ -3070,6 +3100,7 @@ impl< Layout::U64, Layout::BOOL, usize_layout, + usize_layout, ]; self.build_fn_call( diff --git a/crates/compiler/gen_dev/src/lib.rs b/crates/compiler/gen_dev/src/lib.rs index 286fd1abde..08c4ad4129 100644 --- a/crates/compiler/gen_dev/src/lib.rs +++ b/crates/compiler/gen_dev/src/lib.rs @@ -485,6 +485,26 @@ trait Backend<'a> { element_decrement } + fn copy_fn_pointer(&mut self, layout: InLayout<'a>) -> Symbol { + let box_layout = self + .interner_mut() + .insert_direct_no_semantic(LayoutRepr::Ptr(layout)); + + let element_copy = self.debug_symbol("element_copy"); + let element_copy_symbol = self.build_indirect_copy(layout); + + let element_copy_string = self.lambda_name_to_string( + LambdaName::no_niche(element_copy_symbol), + [box_layout, box_layout].into_iter(), + None, + Layout::UNIT, + ); + + self.build_fn_pointer(&element_copy, element_copy_string); + + element_copy + } + fn helper_proc_gen_mut(&mut self) -> &mut CodeGenHelp<'a>; fn helper_proc_symbols_mut(&mut self) -> &mut Vec<'a, (Symbol, ProcLayout<'a>)>; @@ -1955,8 +1975,9 @@ trait Backend<'a> { let update_mode = self.debug_symbol("update_mode"); self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8); - let inc_elem_fn = self.increment_fn_pointer(list_argument.element_layout); - let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout); + let inc_fn_ptr = self.increment_fn_pointer(list_argument.element_layout); + let dec_fn_ptr = self.decrement_fn_pointer(list_argument.element_layout); + let copy_fn_ptr = self.copy_fn_pointer(list_argument.element_layout); let layout_usize = Layout::U64; @@ -1969,6 +1990,7 @@ trait Backend<'a> { // inc: Inc // dec: Dec // update_mode: UpdateMode, + // copy: CopyFn self.build_fn_call( sym, @@ -1980,9 +2002,10 @@ trait Backend<'a> { i, j, list_argument.element_refcounted, - inc_elem_fn, - dec_elem_fn, + inc_fn_ptr, + dec_fn_ptr, update_mode, + copy_fn_ptr, ], &[ list_layout, @@ -1994,6 +2017,7 @@ trait Backend<'a> { layout_usize, layout_usize, Layout::U8, + layout_usize, ], ret_layout, ); @@ -2007,8 +2031,8 @@ trait Backend<'a> { let update_mode = self.debug_symbol("update_mode"); self.load_literal_i8(&update_mode, UpdateMode::Immutable as i8); - let inc_elem_fn = self.increment_fn_pointer(list_argument.element_layout); - let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout); + let inc_fn_ptr = self.increment_fn_pointer(list_argument.element_layout); + let dec_fn_ptr = self.decrement_fn_pointer(list_argument.element_layout); let layout_usize = Layout::U64; @@ -2016,8 +2040,8 @@ trait Backend<'a> { // alignment: u32, // element_width: usize, // element_refcounted: bool, - // inc_elem_fn: Inc, - // dec_elem_fn: Dec, + // inc_fn_ptr: Inc, + // dec_fn_ptr: Dec, // update_mode: UpdateMode, self.build_fn_call( @@ -2028,8 +2052,8 @@ trait Backend<'a> { list_argument.alignment, list_argument.element_width, list_argument.element_refcounted, - inc_elem_fn, - dec_elem_fn, + inc_fn_ptr, + dec_fn_ptr, update_mode, ], &[ @@ -2082,7 +2106,7 @@ trait Backend<'a> { let list_layout = arg_layouts[0]; let list_argument = self.list_argument(list_layout); - let dec_elem_fn = self.decrement_fn_pointer(list_argument.element_layout); + let dec_fn_ptr = self.decrement_fn_pointer(list_argument.element_layout); let layout_usize = Layout::U64; @@ -2090,7 +2114,7 @@ trait Backend<'a> { // alignment: u32, // element_width: usize, // element_refcounted: bool, - // dec_elem_fn: Dec, + // dec_fn_ptr: Dec, self.build_fn_call( sym, @@ -2100,7 +2124,7 @@ trait Backend<'a> { list_argument.alignment, list_argument.element_width, list_argument.element_refcounted, - dec_elem_fn, + dec_fn_ptr, ], &[ list_layout, @@ -2582,6 +2606,7 @@ trait Backend<'a> { fn build_indirect_inc(&mut self, layout: InLayout<'a>) -> Symbol; fn build_indirect_inc_n(&mut self, layout: InLayout<'a>) -> Symbol; fn build_indirect_dec(&mut self, layout: InLayout<'a>) -> Symbol; + fn build_indirect_copy(&mut self, layout: InLayout<'a>) -> Symbol; fn build_list_clone( &mut self, From 56eae5df95a0a16e0e65745dd2126fd7be3a0f38 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Mon, 8 Jul 2024 21:42:01 +0200 Subject: [PATCH 15/30] propagate type annotations to desugared ast --- crates/compiler/can/src/desugar.rs | 34 +++-- crates/compiler/can/src/suffixed.rs | 166 +++++++++++++++------ crates/compiler/can/tests/test_suffixed.rs | 12 ++ 3 files changed, 158 insertions(+), 54 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 051569943e..9143064c4e 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -10,7 +10,7 @@ use roc_module::ident::ModuleName; use roc_parse::ast::Expr::{self, *}; use roc_parse::ast::{ AssignedField, Collection, ModuleImportParams, OldRecordBuilderField, Pattern, StrLiteral, - StrSegment, ValueDef, WhenBranch, + StrSegment, TypeAnnotation, ValueDef, WhenBranch, }; use roc_region::all::{LineInfo, Loc, Region}; @@ -154,15 +154,26 @@ fn desugar_value_def<'a>( IngestedFileImport(_) => *def, Stmt(stmt_expr) => { - // desugar into a Body({}, stmt_expr) - let loc_pattern = arena.alloc(Loc::at( - stmt_expr.region, - Pattern::RecordDestructure(Collection::empty()), - )); - Body( - loc_pattern, - desugar_expr(arena, stmt_expr, src, line_info, module_path), - ) + // desugar `stmt_expr!` to + // _ : {} + // _ = stmt_expr! + + let region = stmt_expr.region; + let new_pat = arena.alloc(Loc::at(region, Pattern::Underscore(""))); + + ValueDef::AnnotatedBody { + ann_pattern: new_pat, + ann_type: arena.alloc(Loc::at( + region, + TypeAnnotation::Record { + fields: Collection::empty(), + ext: None, + }, + )), + comment: None, + body_pattern: new_pat, + body_expr: desugar_expr(arena, stmt_expr, src, line_info, module_path), + } } } } @@ -211,7 +222,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) arena, Body( loc_pattern, - apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new), + apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None), ), ), Err(..) => Body( @@ -254,6 +265,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) sub_arg, sub_pat, sub_new, + Some(ann_type), ), }, ), diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index c3dc5d0e5f..76a1ac8a3a 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -6,7 +6,7 @@ use roc_error_macros::internal_error; use roc_module::called_via::CalledVia; use roc_module::ident::ModuleName; use roc_parse::ast::Expr::{self, *}; -use roc_parse::ast::{is_expr_suffixed, Pattern, ValueDef, WhenBranch}; +use roc_parse::ast::{is_expr_suffixed, Pattern, TypeAnnotation, ValueDef, WhenBranch}; use roc_region::all::{Loc, Region}; use std::cell::Cell; @@ -28,9 +28,18 @@ fn next_unique_suffixed_ident() -> String { } #[derive(Debug)] +/// pub enum EUnwrapped<'a> { + /// First suffixed expression in a definition. Emitted if an expression has def pattern + /// e.g. x = first! (second! 42) + /// The first unwrap will produce + /// `UnwrappedDefExpr` UnwrappedDefExpr(&'a Loc>), + /// Suffixed sub expression + /// e.g. x = first! (second! 42) + /// In this example, the second unwrap (after unwrapping the top level `first!`) will produce + /// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!a0, sub_new: #!a0 }>` UnwrappedSubExpr { /// the unwrapped expression argument for Task.await sub_arg: &'a Loc>, @@ -42,6 +51,7 @@ pub enum EUnwrapped<'a> { sub_new: &'a Loc>, }, + /// Malformed use of the suffix Malformed, } @@ -240,7 +250,7 @@ pub fn unwrap_suffixed_expression_closure_help<'a>( Ok(new_closure) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new); + let new_closure_loc_ret = apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None); let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret))); Ok(new_closure) } @@ -361,8 +371,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( sub_pat, sub_new, }) => { - let unwrapped_expression = - apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new); + let unwrapped_expression = apply_task_await( + arena, + sub_arg.region, + sub_arg, + sub_pat, + sub_new, + None, + ); let mut new_if_thens = Vec::new_in(arena); @@ -437,6 +453,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( sub_arg, sub_pat, new_if, + None, ); return unwrap_suffixed_expression( @@ -464,6 +481,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( sub_arg, sub_pat, after_if, + None, ); let before_if_then = arena.alloc(Loc::at( @@ -500,7 +518,7 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( sub_new, }) => { let unwrapped_final_else = - apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new); + apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None); let new_if = arena.alloc(Loc::at( loc_expr.region, @@ -535,7 +553,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>( if is_expr_suffixed(&branch_loc_expr.value) { let unwrapped_branch_value = match unwrap_suffixed_expression(arena, branch_loc_expr, None) { Ok(unwrapped_branch_value) => unwrapped_branch_value, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new), + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => apply_task_await(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None), Err(..) => return Err(EUnwrapped::Malformed), }; @@ -564,7 +582,7 @@ pub fn unwrap_suffixed_expression_when_help<'a>( } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(sub_new, branches))); - let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when); + let applied_task_await = apply_task_await(arena,loc_expr.region,sub_arg,sub_pat,new_when, None); Ok(applied_task_await) } Err(EUnwrapped::UnwrappedDefExpr(..)) @@ -601,15 +619,16 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( let maybe_suffixed_value_def = match current_value_def { Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None, - AnnotatedBody { body_pattern, body_expr, .. } => Some((body_pattern, body_expr)), - Body (def_pattern, def_expr, .. ) => Some((def_pattern, def_expr)), + AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } if body_pattern.value == ann_pattern.value => Some((body_pattern, body_expr, Some(ann_type))), + AnnotatedBody { body_pattern, body_expr, .. } => Some((body_pattern, body_expr, None)), + Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)), }; match maybe_suffixed_value_def { None => { // We can't unwrap this def type, continue }, - Some((def_pattern, def_expr)) => { + Some((def_pattern, def_expr, ann_type)) => { match unwrap_suffixed_expression(arena, def_expr, Some(def_pattern)) { Ok(unwrapped_def) => { current_value_def.replace_expr(unwrapped_def); @@ -626,14 +645,14 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( Ok(next_expr) => next_expr, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { // We need to apply Task.ok here as the defs final expression was unwrapped - apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new) + apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, ann_type) } Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up return Err(EUnwrapped::Malformed); }, }; - return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr), maybe_def_pat); + return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat); } else if before_empty { // NIL before, SOME after -> FIRST DEF let new_defs = arena.alloc(Loc::at(def_expr.region, Defs(arena.alloc(split_defs.after), loc_ret))); @@ -641,7 +660,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){ Ok(next_expr) => next_expr, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new) + apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, ann_type) } Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up @@ -649,19 +668,19 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( }, }; - return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr), maybe_def_pat); + return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat); } else if after_empty { // SOME before, NIL after -> LAST DEF // We pass None as a def pattern here because it's desugaring of the ret expression match unwrap_suffixed_expression(arena,loc_ret,None){ Ok(new_loc_ret) => { - let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret); + let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); }, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new); - let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret); + let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, ann_type); + let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); } @@ -679,13 +698,13 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( match unwrap_suffixed_expression(arena,after_defs,maybe_def_pat){ Ok(new_loc_ret) => { - let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret); + let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); }, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new); - let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret); + let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, ann_type); + let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); } @@ -700,7 +719,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( let new_body_def = ValueDef::Body(def_pattern, sub_new); local_defs.replace_with_value_def(tag_index,new_body_def, sub_new.region); let new_defs_expr = arena.alloc(Loc::at(def_expr.region,Defs(arena.alloc(local_defs), loc_ret))); - let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr); + let replaced_def = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type); return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat); } Err(err) => return Err(err) @@ -712,10 +731,10 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( // try to unwrap the loc_ret match unwrap_suffixed_expression(arena,loc_ret,maybe_def_pat){ Ok(new_loc_ret) => { - Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)))) + Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)))) }, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new); + let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))); unwrap_suffixed_expression(arena, new_defs, maybe_def_pat) } @@ -761,7 +780,14 @@ fn unwrap_low_level_dbg<'a>( unwrap_suffixed_expression( arena, - apply_task_await(arena, new_dbg.region, sub_arg, sub_pat, new_dbg), + apply_task_await( + arena, + new_dbg.region, + sub_arg, + sub_pat, + new_dbg, + None, + ), maybe_def_pat, ) } @@ -813,33 +839,87 @@ fn unwrap_low_level_dbg<'a>( } } -/// Helper for `Task.await (loc_arg) \loc_pat -> loc_new` +/// Helper for `Task.await loc_expr \loc_pat -> loc_cont` pub fn apply_task_await<'a>( arena: &'a Bump, region: Region, - loc_arg: &'a Loc>, + loc_expr: &'a Loc>, loc_pat: &'a Loc>, - loc_new: &'a Loc>, + loc_cont: &'a Loc>, + maybe_loc_type: Option<&'a Loc>>, ) -> &'a Loc> { - // If the pattern and the new are matching answers then we don't need to unwrap anything - // e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo` - if is_matching_intermediate_answer(loc_pat, loc_new) { - return loc_arg; - } + let task_await_first_arg = match maybe_loc_type { + Some(loc_type) => { + // loc_pat : loc_type + // loc_pat = loc_expr! + // loc_cont - let mut task_await_apply_args: Vec<&'a Loc>> = Vec::new_in(arena); + // desugar to + // Task.await + // ( + // #!a0 : Task loc_type _ + // #!a0 = loc_expr + // #!a0 + // ) + // \loc_pat -> loc_cont + use roc_parse::ast::*; - // apply the unwrapped suffixed expression - task_await_apply_args.push(loc_arg); + // #!a0 + let new_ident = arena.alloc(next_unique_suffixed_ident()); + // #!a0 (pattern) + let new_pat = arena.alloc(Loc::at(region, Pattern::Identifier { ident: new_ident })); - // apply the closure - let mut closure_pattern = Vec::new_in(arena); - closure_pattern.push(*loc_pat); - task_await_apply_args.push(arena.alloc(Loc::at( - region, - Closure(arena.alloc_slice_copy(closure_pattern.as_slice()), loc_new), - ))); + // #!a0 : Task loc_type _ + // #!a0 = loc_expr + let value_def = ValueDef::AnnotatedBody { + ann_pattern: new_pat, + ann_type: arena.alloc(Loc::at( + region, + TypeAnnotation::Apply( + arena.alloc(""), + arena.alloc("Task"), + arena.alloc([*loc_type, Loc::at(region, TypeAnnotation::Inferred)]), + ), + )), + comment: None, + body_pattern: new_pat, + body_expr: loc_expr, + }; + // #!a0 (variable) + let new_var = arena.alloc(Loc::at( + region, + Expr::Var { + module_name: "", + ident: new_ident, + }, + )); + + let mut defs = roc_parse::ast::Defs::default(); + defs.push_value_def(value_def, region, &[], &[]); + + arena.alloc(Loc::at(region, Defs(arena.alloc(defs), new_var))) + } + _ => { + // If the pattern and the new are matching answers then we don't need to unwrap anything + // e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo` + if is_matching_intermediate_answer(loc_pat, loc_cont) { + return loc_expr; + } + + // loc_pat = loc_expr! + // loc_cont + + // desugar to + // Task.await loc_expr \loc_pat -> loc_cont + loc_expr + } + }; + + // \loc_pat -> loc_cont + let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont))); + + // Task.await task_first_arg closure arena.alloc(Loc::at( region, Apply( @@ -850,7 +930,7 @@ pub fn apply_task_await<'a>( ident: "await", }, }), - arena.alloc(task_await_apply_args), + arena.alloc([task_await_first_arg, closure]), CalledVia::BangSuffix, ), )) diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs index 329db34355..9ef2be2965 100644 --- a/crates/compiler/can/tests/test_suffixed.rs +++ b/crates/compiler/can/tests/test_suffixed.rs @@ -546,6 +546,18 @@ mod suffixed_tests { "## ); } + + #[test] + fn type_annotation() { + run_test!( + r##" + f = \x -> + r : A + r = x! + Task.ok r + "## + ); + } } #[cfg(test)] From 3e17168098833085a8515fdb1aae55a0116b1069 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Thu, 11 Jul 2024 12:51:22 +0200 Subject: [PATCH 16/30] print formatted desugared code instead of ast --- Cargo.lock | 1 + crates/compiler/can/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index e44c1472b9..47570a8524 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2347,6 +2347,7 @@ dependencies = [ "roc_collections", "roc_error_macros", "roc_exhaustive", + "roc_fmt", "roc_module", "roc_parse", "roc_problem", diff --git a/crates/compiler/can/Cargo.toml b/crates/compiler/can/Cargo.toml index 76b115bd5a..9ef9239278 100644 --- a/crates/compiler/can/Cargo.toml +++ b/crates/compiler/can/Cargo.toml @@ -13,6 +13,7 @@ roc_error_macros = { path = "../../error_macros" } roc_exhaustive = { path = "../exhaustive" } roc_module = { path = "../module" } roc_parse = { path = "../parse" } +roc_fmt = { path = "../fmt" } roc_problem = { path = "../problem" } roc_region = { path = "../region" } roc_serialize = { path = "../serialize" } From a7ca02dd61c04f10c5dfb59f57f66e6a2f042dab Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Fri, 12 Jul 2024 10:46:06 +0200 Subject: [PATCH 17/30] unwrap identity call --- crates/compiler/can/src/suffixed.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 76a1ac8a3a..091e18d286 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -901,12 +901,6 @@ pub fn apply_task_await<'a>( arena.alloc(Loc::at(region, Defs(arena.alloc(defs), new_var))) } _ => { - // If the pattern and the new are matching answers then we don't need to unwrap anything - // e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo` - if is_matching_intermediate_answer(loc_pat, loc_cont) { - return loc_expr; - } - // loc_pat = loc_expr! // loc_cont @@ -916,6 +910,12 @@ pub fn apply_task_await<'a>( } }; + // If the pattern and the new are matching answers then we don't need to unwrap anything + // e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo` + if is_matching_intermediate_answer(loc_pat, loc_cont) { + return task_await_first_arg; + } + // \loc_pat -> loc_cont let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont))); From 4d5586b68ab9a2648f743d56e4d8cfd729a90c25 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Fri, 12 Jul 2024 12:46:35 +0200 Subject: [PATCH 18/30] simplify identity unwrap --- crates/compiler/can/src/suffixed.rs | 34 +++++++---------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 091e18d286..ae07d7a1ee 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -936,18 +936,6 @@ pub fn apply_task_await<'a>( )) } -fn extract_wrapped_task_ok_value<'a>(loc_expr: &'a Loc>) -> Option<&'a Loc>> { - match loc_expr.value { - Expr::Apply(function, arguments, _) => match function.value { - Var { - module_name, ident, .. - } if module_name == ModuleName::TASK && ident == "ok" => arguments.first().copied(), - _ => None, - }, - _ => None, - } -} - pub fn is_matching_intermediate_answer<'a>( loc_pat: &'a Loc>, loc_new: &'a Loc>, @@ -956,24 +944,18 @@ pub fn is_matching_intermediate_answer<'a>( Pattern::Identifier { ident, .. } => Some(ident), _ => None, }; + let exp_ident = match loc_new.value { Expr::Var { - module_name, ident, .. - } if module_name.is_empty() && ident.starts_with('#') => Some(ident), + module_name: "", + ident, + .. + } => Some(ident), _ => None, }; - let exp_ident_in_task = match extract_wrapped_task_ok_value(loc_new) { - Some(task_expr) => match task_expr.value { - Expr::Var { - module_name, ident, .. - } if module_name.is_empty() && ident.starts_with('#') => Some(ident), - _ => None, - }, - None => None, - }; - match (pat_ident, exp_ident, exp_ident_in_task) { - (Some(a), Some(b), None) => a == b, - (Some(a), None, Some(b)) => a == b, + + match (pat_ident, exp_ident) { + (Some(a), Some(b)) => a == b, _ => false, } } From 9a37aeb82f692baab63ed8c2b1b90335434a1e90 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Fri, 12 Jul 2024 13:14:27 +0200 Subject: [PATCH 19/30] fix types propagation --- crates/compiler/can/src/suffixed.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index ae07d7a1ee..9986e03472 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -645,7 +645,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( Ok(next_expr) => next_expr, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { // We need to apply Task.ok here as the defs final expression was unwrapped - apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, ann_type) + apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None) } Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up @@ -660,7 +660,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( let next_expr = match unwrap_suffixed_expression(arena,new_defs,maybe_def_pat){ Ok(next_expr) => next_expr, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, ann_type) + apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None) } Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { // TODO handle case when we have maybe_def_pat so can return an unwrapped up @@ -668,7 +668,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( }, }; - return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type), maybe_def_pat); + return unwrap_suffixed_expression(arena, apply_task_await(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr,ann_type), maybe_def_pat); } else if after_empty { // SOME before, NIL after -> LAST DEF // We pass None as a def pattern here because it's desugaring of the ret expression @@ -679,7 +679,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); }, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, ann_type); + let new_loc_ret = apply_task_await(arena,def_expr.region,sub_arg,sub_pat,sub_new, None); let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_task_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); @@ -703,7 +703,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); }, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { - let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, ann_type); + let new_loc_ret = apply_task_await(arena, def_expr.region, sub_arg, sub_pat, sub_new, None); let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(split_defs.before), applied_await))); return unwrap_suffixed_expression(arena, new_defs, maybe_def_pat); @@ -729,14 +729,14 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( } // try to unwrap the loc_ret - match unwrap_suffixed_expression(arena,loc_ret,maybe_def_pat){ + match unwrap_suffixed_expression(arena,loc_ret,None){ Ok(new_loc_ret) => { Ok(arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret)))) }, Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { let new_loc_ret = apply_task_await(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))); - unwrap_suffixed_expression(arena, new_defs, maybe_def_pat) + unwrap_suffixed_expression(arena, new_defs, None) } Err(EUnwrapped::UnwrappedDefExpr(..)) => { // TODO confirm this is correct with test case From 10bdabf52e74a841f991bdceb26a24ee767e7160 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Fri, 12 Jul 2024 13:22:14 +0200 Subject: [PATCH 20/30] better desugared idents --- crates/compiler/can/src/desugar.rs | 4 +- crates/compiler/can/src/suffixed.rs | 82 ++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 9143064c4e..a036b1b766 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -159,7 +159,7 @@ fn desugar_value_def<'a>( // _ = stmt_expr! let region = stmt_expr.region; - let new_pat = arena.alloc(Loc::at(region, Pattern::Underscore(""))); + let new_pat = arena.alloc(Loc::at(region, Pattern::Underscore("#!stmt"))); ValueDef::AnnotatedBody { ann_pattern: new_pat, @@ -265,7 +265,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) sub_arg, sub_pat, sub_new, - Some(ann_type), + Some((ann_pattern, ann_type)), ), }, ), diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 9986e03472..4656a0a650 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -23,7 +23,7 @@ fn next_unique_suffixed_ident() -> String { // # is used as prefix because it's impossible for code authors to define names like this. // This makes it easy to see this identifier was created by the compiler. - format!("#!a{}", count) + format!("#!{}", count) }) } @@ -70,19 +70,17 @@ fn init_unwrapped_err<'a>( // Provide an intermediate answer expression and pattern when unwrapping a // (sub) expression. // e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0` - let answer_ident = arena.alloc(next_unique_suffixed_ident()); + let ident = arena.alloc(format!("{}_arg", next_unique_suffixed_ident())); let sub_new = arena.alloc(Loc::at( unwrapped_expr.region, Expr::Var { module_name: "", - ident: answer_ident, + ident, }, )); let sub_pat = arena.alloc(Loc::at( unwrapped_expr.region, - Pattern::Identifier { - ident: answer_ident, - }, + Pattern::Identifier { ident }, )); Err(EUnwrapped::UnwrappedSubExpr { @@ -619,8 +617,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( let maybe_suffixed_value_def = match current_value_def { Annotation(..) | Dbg{..} | Expect{..} | ExpectFx{..} | Stmt(..) | ModuleImport{..} | IngestedFileImport(_) => None, - AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } if body_pattern.value == ann_pattern.value => Some((body_pattern, body_expr, Some(ann_type))), - AnnotatedBody { body_pattern, body_expr, .. } => Some((body_pattern, body_expr, None)), + AnnotatedBody { body_pattern, body_expr, ann_type, ann_pattern, .. } => Some((body_pattern, body_expr, Some((ann_pattern, ann_type)))), Body (def_pattern, def_expr) => Some((def_pattern, def_expr, None)), }; @@ -846,47 +843,72 @@ pub fn apply_task_await<'a>( loc_expr: &'a Loc>, loc_pat: &'a Loc>, loc_cont: &'a Loc>, - maybe_loc_type: Option<&'a Loc>>, + maybe_loc_ann: Option<(&'a Loc, &'a Loc>)>, ) -> &'a Loc> { - let task_await_first_arg = match maybe_loc_type { - Some(loc_type) => { - // loc_pat : loc_type + let task_await_first_arg = match maybe_loc_ann { + Some((loc_ann_pat, loc_type)) => { + // loc_ann_pat : loc_type // loc_pat = loc_expr! // loc_cont // desugar to // Task.await // ( - // #!a0 : Task loc_type _ - // #!a0 = loc_expr - // #!a0 + // #!0_expr : Task loc_type _ + // #!0_expr = loc_expr + // #!0_expr // ) // \loc_pat -> loc_cont use roc_parse::ast::*; // #!a0 - let new_ident = arena.alloc(next_unique_suffixed_ident()); - // #!a0 (pattern) - let new_pat = arena.alloc(Loc::at(region, Pattern::Identifier { ident: new_ident })); + let new_ident = next_unique_suffixed_ident(); + let new_ident = match loc_pat.value { + Pattern::Underscore("#!stmt") => format!("{}_stmt", new_ident), + Pattern::Identifier { ident } + if ident.starts_with('#') && ident.ends_with("_stmt") => + { + format!("{}_stmt", new_ident) + } + _ => format!("{}_expr", new_ident), + }; + let new_ident = arena.alloc(new_ident); - // #!a0 : Task loc_type _ - // #!a0 = loc_expr + // #!0_expr (pattern) + // #!0_expr : Task loc_type _ + // #!0_expr = loc_expr let value_def = ValueDef::AnnotatedBody { - ann_pattern: new_pat, + ann_pattern: arena.alloc(Loc::at( + loc_ann_pat.region, + Pattern::Identifier { + ident: if loc_ann_pat.value.equivalent(&loc_pat.value) { + new_ident + } else { + // create another pattern to preserve inconsistency + arena.alloc(next_unique_suffixed_ident()) + }, + }, + )), ann_type: arena.alloc(Loc::at( - region, + loc_type.region, TypeAnnotation::Apply( arena.alloc(""), arena.alloc("Task"), - arena.alloc([*loc_type, Loc::at(region, TypeAnnotation::Inferred)]), + arena.alloc([ + *loc_type, + Loc::at(loc_type.region, TypeAnnotation::Inferred), + ]), ), )), comment: None, - body_pattern: new_pat, + body_pattern: arena.alloc(Loc::at( + loc_pat.region, + Pattern::Identifier { ident: new_ident }, + )), body_expr: loc_expr, }; - // #!a0 (variable) + // #!0_expr (variable) let new_var = arena.alloc(Loc::at( region, Expr::Var { @@ -895,10 +917,18 @@ pub fn apply_task_await<'a>( }, )); + // ( + // #!0_expr : Task loc_type _ + // #!0_expr = loc_expr + // #!0_expr + // ) let mut defs = roc_parse::ast::Defs::default(); defs.push_value_def(value_def, region, &[], &[]); - arena.alloc(Loc::at(region, Defs(arena.alloc(defs), new_var))) + arena.alloc(Loc::at( + Region::span_across(&loc_ann_pat.region, &loc_expr.region), + Defs(arena.alloc(defs), new_var), + )) } _ => { // loc_pat = loc_expr! From 7a8a5bc9e7076999a9b04aca04dd4fc4243f6dee Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Fri, 12 Jul 2024 16:52:01 +0200 Subject: [PATCH 21/30] update type annotation errors --- crates/reporting/src/error/type.rs | 37 ++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/crates/reporting/src/error/type.rs b/crates/reporting/src/error/type.rs index b13c24781c..65278dd797 100644 --- a/crates/reporting/src/error/type.rs +++ b/crates/reporting/src/error/type.rs @@ -675,9 +675,24 @@ fn to_expr_report<'b>( } } Expected::FromAnnotation(name, _arity, annotation_source, expected_type) => { + use roc_can::pattern::Pattern; use roc_types::types::AnnotationSource::*; + let is_suffixed = match &name.value { + Pattern::Identifier(symbol) => symbol.as_str(alloc.interns).starts_with("#!"), + _ => false, + }; + + let is_suffixed_stmt = match &name.value { + Pattern::Identifier(symbol) => { + let ident = symbol.as_str(alloc.interns); + ident.starts_with("#!") && ident.ends_with("_stmt") + } + _ => false, + }; + let (the_name_text, on_name_text) = match pattern_to_doc(alloc, &name.value) { + _ if is_suffixed => (alloc.text("this suffixed"), alloc.nil()), Some(doc) => ( alloc.concat([alloc.reflow("the "), doc.clone()]), alloc.concat([alloc.reflow(" on "), doc]), @@ -714,6 +729,11 @@ fn to_expr_report<'b>( alloc.keyword("when"), alloc.text(" expression:"), ]), + TypedBody { .. } if is_suffixed_stmt => alloc.concat([ + alloc.text("body of "), + the_name_text, + alloc.text(" statement:"), + ]), TypedBody { .. } => alloc.concat([ alloc.text("body of "), the_name_text, @@ -769,11 +789,18 @@ fn to_expr_report<'b>( expected_type, expectation_context, add_category(alloc, alloc.text(it_is), &category), - alloc.concat([ - alloc.text("But the type annotation"), - on_name_text, - alloc.text(" says it should be:"), - ]), + if is_suffixed_stmt { + // TODO: add a tip for using underscore + alloc.text( + "But a suffixed statement is expected to resolve to an empty record:", + ) + } else { + alloc.concat([ + alloc.text("But the type annotation"), + on_name_text, + alloc.text(" says it should be:"), + ]) + }, None, ) }; From 1f65d95e09f45d5662f582d18c9b76215b7f6be0 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Tue, 16 Jul 2024 08:58:07 +0200 Subject: [PATCH 22/30] update suffixed tests snapshots --- ...ffixed_tests__apply_argument_multiple.snap | 8 +- ...suffixed_tests__apply_argument_single.snap | 4 +- ...ffixed_tests__apply_argument_suffixed.snap | 4 +- ...ffixed_tests__apply_function_suffixed.snap | 4 +- ...ed__suffixed_tests__bang_in_pipe_root.snap | 4 +- .../test_suffixed__suffixed_tests__basic.snap | 56 ++++++- ...ed__suffixed_tests__body_parens_apply.snap | 4 +- ...fixed__suffixed_tests__closure_simple.snap | 70 +++++++-- ...fixed_tests__closure_with_annotations.snap | 93 +++++++---- ...ed__suffixed_tests__closure_with_defs.snap | 132 +++++++++++++--- ...uffixed__suffixed_tests__dbg_stmt_arg.snap | 4 +- ..._suffixed_tests__defs_suffixed_middle.snap | 70 +++++++-- ..._suffixed__suffixed_tests__if_complex.snap | 144 ++++++++++++++---- ...t_suffixed__suffixed_tests__if_simple.snap | 8 +- ...sts__last_stmt_not_top_level_suffixed.snap | 4 +- ...uffixed_tests__last_suffixed_multiple.snap | 112 ++++++++++++-- ...xed__suffixed_tests__multi_defs_stmts.snap | 68 +++++++-- ...ixed__suffixed_tests__multiple_suffix.snap | 101 +++++++----- ...fixed__suffixed_tests__nested_complex.snap | 4 +- ...ffixed__suffixed_tests__nested_simple.snap | 4 +- ...uffixed__suffixed_tests__simple_pizza.snap | 104 +++++++++---- ...ixed__suffixed_tests__trailing_binops.snap | 68 +++++++-- ...ixed__suffixed_tests__type_annotation.snap | 112 ++++++++++++++ ...uffixed__suffixed_tests__var_suffixes.snap | 4 +- ...ffixed__suffixed_tests__when_branches.snap | 72 +++++++-- ...suffixed__suffixed_tests__when_simple.snap | 4 +- 26 files changed, 1004 insertions(+), 258 deletions(-) create mode 100644 crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap index bcf7703830..d5f94a4db2 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap @@ -35,7 +35,7 @@ Defs { @15-22 Closure( [ @17-18 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @15-22 Apply( @@ -51,7 +51,7 @@ Defs { @15-22 Closure( [ @20-21 Identifier { - ident: "#!a1", + ident: "#!1_arg", }, ], @15-22 Defs( @@ -83,11 +83,11 @@ Defs { [ @17-18 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, @20-21 Var { module_name: "", - ident: "#!a1", + ident: "#!1_arg", }, ], Space, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap index d9655bb25e..c121a19025 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap @@ -35,7 +35,7 @@ Defs { @15-19 Closure( [ @17-18 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @15-19 Defs( @@ -67,7 +67,7 @@ Defs { [ @17-18 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ], Space, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap index 682e7623aa..2828856b17 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap @@ -45,7 +45,7 @@ Defs { @15-33 Closure( [ Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @15-33 Defs( @@ -78,7 +78,7 @@ Defs { @20-32 ParensAround( Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ), ], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap index ec3072d8f5..d72b00aad3 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap @@ -45,7 +45,7 @@ Defs { @15-35 Closure( [ Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @15-35 Defs( @@ -73,7 +73,7 @@ Defs { @16-26 ParensAround( Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ), [ diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap index 8a9bb0e191..62aaa64c9f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap @@ -35,7 +35,7 @@ Defs { @15-22 Closure( [ @15-16 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @15-22 Defs( @@ -67,7 +67,7 @@ Defs { [ @15-16 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ], BinOp( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index 9907b760c3..c78f101cc8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -28,14 +28,58 @@ Defs { ident: "await", }, [ - @11-15 Var { - module_name: "", - ident: "foo", - }, + @11-15 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @11-15, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @11-15 Identifier { + ident: "#!0_stmt", + }, + ann_type: @11-15 Apply( + "", + "Task", + [ + @11-15 Record { + fields: [], + ext: None, + }, + @11-15 Inferred, + ], + ), + comment: None, + body_pattern: @11-15 Identifier { + ident: "#!0_stmt", + }, + body_expr: @11-15 Var { + module_name: "", + ident: "foo", + }, + }, + ], + }, + @11-15 Var { + module_name: "", + ident: "#!0_stmt", + }, + ), @11-15 Closure( [ - @11-15 RecordDestructure( - [], + @11-15 Underscore( + "#!stmt", ), ], @21-26 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap index 603e9ebafc..247b3cb142 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap @@ -35,7 +35,7 @@ Defs { @16-35 Closure( [ Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @16-35 Defs( @@ -63,7 +63,7 @@ Defs { @17-29 ParensAround( Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ), [ diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index e07bf01969..4ed25bf707 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -55,25 +55,69 @@ Defs { ident: "await", }, [ - @31-42 Apply( + @31-42 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @31-42, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @31-42 Identifier { + ident: "#!0_stmt", + }, + ann_type: @31-42 Apply( + "", + "Task", + [ + @31-42 Record { + fields: [], + ext: None, + }, + @31-42 Inferred, + ], + ), + comment: None, + body_pattern: @31-42 Identifier { + ident: "#!0_stmt", + }, + body_expr: @31-42 Apply( + @31-42 Var { + module_name: "", + ident: "line", + }, + [ + @31-34 Var { + module_name: "", + ident: "msg", + }, + ], + BinOp( + Pizza, + ), + ), + }, + ], + }, @31-42 Var { module_name: "", - ident: "line", + ident: "#!0_stmt", }, - [ - @31-34 Var { - module_name: "", - ident: "msg", - }, - ], - BinOp( - Pizza, - ), ), @31-42 Closure( [ - @31-42 RecordDestructure( - [], + @31-42 Underscore( + "#!stmt", ), ], @52-57 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index ef9f601967..c88b75ab11 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -70,38 +70,69 @@ Defs { ident: "msg", }, ], - @78-91 Apply( - @78-91 Var { - module_name: "Task", - ident: "await", + @56-91 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @78-91, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @56-57 Identifier { + ident: "#!0_expr", + }, + ann_type: @60-69 Apply( + "", + "Task", + [ + @60-69 Apply( + "", + "Task", + [ + @65-67 Record { + fields: [], + ext: None, + }, + @68-69 Inferred, + ], + ), + @60-69 Inferred, + ], + ), + comment: None, + body_pattern: @78-79 Identifier { + ident: "#!0_expr", + }, + body_expr: @78-91 Apply( + @78-91 Var { + module_name: "", + ident: "line", + }, + [ + @88-91 Var { + module_name: "", + ident: "msg", + }, + ], + Space, + ), + }, + ], + }, + @78-91 Var { + module_name: "", + ident: "#!0_expr", }, - [ - @78-91 Apply( - @78-91 Var { - module_name: "", - ident: "line", - }, - [ - @88-91 Var { - module_name: "", - ident: "msg", - }, - ], - Space, - ), - @78-91 Closure( - [ - @78-79 Identifier { - ident: "y", - }, - ], - @100-101 Var { - module_name: "", - ident: "y", - }, - ), - ], - BangSuffix, ), ), }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index 12aee871b8..490fddac5a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -98,23 +98,67 @@ Defs { ident: "await", }, [ - @76-83 Apply( + @76-83 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @76-83, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @76-83 Identifier { + ident: "#!1_stmt", + }, + ann_type: @76-83 Apply( + "", + "Task", + [ + @76-83 Record { + fields: [], + ext: None, + }, + @76-83 Inferred, + ], + ), + comment: None, + body_pattern: @76-83 Identifier { + ident: "#!1_stmt", + }, + body_expr: @76-83 Apply( + @76-83 Var { + module_name: "", + ident: "line", + }, + [ + @82-83 Var { + module_name: "", + ident: "a", + }, + ], + Space, + ), + }, + ], + }, @76-83 Var { module_name: "", - ident: "line", + ident: "#!1_stmt", }, - [ - @82-83 Var { - module_name: "", - ident: "a", - }, - ], - Space, ), @76-83 Closure( [ - @76-83 RecordDestructure( - [], + @76-83 Underscore( + "#!stmt", ), ], @92-99 Apply( @@ -123,23 +167,67 @@ Defs { ident: "await", }, [ - @92-99 Apply( + @92-99 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @92-99, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @92-99 Identifier { + ident: "#!0_stmt", + }, + ann_type: @92-99 Apply( + "", + "Task", + [ + @92-99 Record { + fields: [], + ext: None, + }, + @92-99 Inferred, + ], + ), + comment: None, + body_pattern: @92-99 Identifier { + ident: "#!0_stmt", + }, + body_expr: @92-99 Apply( + @92-99 Var { + module_name: "", + ident: "line", + }, + [ + @98-99 Var { + module_name: "", + ident: "b", + }, + ], + Space, + ), + }, + ], + }, @92-99 Var { module_name: "", - ident: "line", + ident: "#!0_stmt", }, - [ - @98-99 Var { - module_name: "", - ident: "b", - }, - ], - Space, ), @92-99 Closure( [ - @92-99 RecordDestructure( - [], + @92-99 Underscore( + "#!stmt", ), ], @109-119 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap index e3a74788fe..d3918ce09b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap @@ -35,7 +35,7 @@ Defs { @11-24 Closure( [ @15-17 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @11-24 LowLevelDbg( @@ -51,7 +51,7 @@ Defs { [ @15-17 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ], Space, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index 996bf5b081..a07119b6c8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -64,23 +64,67 @@ Defs { ident: "await", }, [ - @25-39 Apply( - @25-39 Var { - module_name: "Stdout", - ident: "line", + @25-39 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @25-39, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @25-39 Identifier { + ident: "#!1_stmt", + }, + ann_type: @25-39 Apply( + "", + "Task", + [ + @25-39 Record { + fields: [], + ext: None, + }, + @25-39 Inferred, + ], + ), + comment: None, + body_pattern: @25-39 Identifier { + ident: "#!1_stmt", + }, + body_expr: @25-39 Apply( + @25-39 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @38-39 Var { + module_name: "", + ident: "a", + }, + ], + Space, + ), + }, + ], + }, + @11-54 Var { + module_name: "", + ident: "#!1_stmt", }, - [ - @38-39 Var { - module_name: "", - ident: "a", - }, - ], - Space, ), @11-54 Closure( [ - @25-39 RecordDestructure( - [], + @25-39 Underscore( + "#!stmt", ), ], @45-54 Var { diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index c517084dd8..9e62fed352 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -129,7 +129,7 @@ Defs { Closure( [ Identifier { - ident: "#!a0", + ident: "#!1_arg", }, ], @109-298 If( @@ -144,7 +144,7 @@ Defs { @114-121 ParensAround( Var { module_name: "", - ident: "#!a0", + ident: "#!1_arg", }, ), ], @@ -158,24 +158,68 @@ Defs { ident: "await", }, [ - @140-152 Apply( + @140-152 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @140-152, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @140-152 Identifier { + ident: "#!0_stmt", + }, + ann_type: @140-152 Apply( + "", + "Task", + [ + @140-152 Record { + fields: [], + ext: None, + }, + @140-152 Inferred, + ], + ), + comment: None, + body_pattern: @140-152 Identifier { + ident: "#!0_stmt", + }, + body_expr: @140-152 Apply( + @140-152 Var { + module_name: "", + ident: "line", + }, + [ + @146-152 Str( + PlainLine( + "fail", + ), + ), + ], + Space, + ), + }, + ], + }, @140-152 Var { module_name: "", - ident: "line", + ident: "#!0_stmt", }, - [ - @146-152 Str( - PlainLine( - "fail", - ), - ), - ], - Space, ), @140-152 Closure( [ - @140-152 RecordDestructure( - [], + @140-152 Underscore( + "#!stmt", ), ], @165-170 Apply( @@ -218,7 +262,7 @@ Defs { Closure( [ Identifier { - ident: "#!a1", + ident: "#!3_arg", }, ], @109-298 If( @@ -227,7 +271,7 @@ Defs { @187-209 ParensAround( Var { module_name: "", - ident: "#!a1", + ident: "#!3_arg", }, ), @227-239 Apply( @@ -236,24 +280,68 @@ Defs { ident: "await", }, [ - @227-239 Apply( + @227-239 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @227-239, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @227-239 Identifier { + ident: "#!2_stmt", + }, + ann_type: @227-239 Apply( + "", + "Task", + [ + @227-239 Record { + fields: [], + ext: None, + }, + @227-239 Inferred, + ], + ), + comment: None, + body_pattern: @227-239 Identifier { + ident: "#!2_stmt", + }, + body_expr: @227-239 Apply( + @227-239 Var { + module_name: "", + ident: "line", + }, + [ + @233-239 Str( + PlainLine( + "nope", + ), + ), + ], + Space, + ), + }, + ], + }, @227-239 Var { module_name: "", - ident: "line", + ident: "#!2_stmt", }, - [ - @233-239 Str( - PlainLine( - "nope", - ), - ), - ], - Space, ), @227-239 Closure( [ - @227-239 RecordDestructure( - [], + @227-239 Underscore( + "#!stmt", ), ], @252-257 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap index 2bdacac9ef..02592ffa7b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap @@ -96,7 +96,7 @@ Defs { @79-87 Closure( [ @79-87 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @76-189 If( @@ -104,7 +104,7 @@ Defs { ( @79-87 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, @101-112 Apply( @101-105 Var { @@ -135,7 +135,7 @@ Defs { @125-132 Closure( [ @125-132 Identifier { - ident: "#!a1", + ident: "#!1_arg", }, ], @76-189 If( @@ -143,7 +143,7 @@ Defs { ( @125-132 Var { module_name: "", - ident: "#!a1", + ident: "#!1_arg", }, @146-160 Apply( @146-150 Var { diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap index c3d1d5865c..8af15de1a8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap @@ -62,7 +62,7 @@ Defs { @11-26 Closure( [ @24-25 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @22-26 Apply( @@ -73,7 +73,7 @@ Defs { [ @24-25 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ], Space, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index d4f68f6bea..8a09018284 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -28,14 +28,58 @@ Defs { ident: "await", }, [ - @11-15 Var { - module_name: "", - ident: "foo", - }, + @11-15 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @11-15, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @11-15 Identifier { + ident: "#!2_stmt", + }, + ann_type: @11-15 Apply( + "", + "Task", + [ + @11-15 Record { + fields: [], + ext: None, + }, + @11-15 Inferred, + ], + ), + comment: None, + body_pattern: @11-15 Identifier { + ident: "#!2_stmt", + }, + body_expr: @11-15 Var { + module_name: "", + ident: "foo", + }, + }, + ], + }, + @11-15 Var { + module_name: "", + ident: "#!2_stmt", + }, + ), @11-15 Closure( [ - @11-15 RecordDestructure( - [], + @11-15 Underscore( + "#!stmt", ), ], @20-24 Apply( @@ -44,14 +88,58 @@ Defs { ident: "await", }, [ - @20-24 Var { - module_name: "", - ident: "bar", - }, + @20-24 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @20-24, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @20-24 Identifier { + ident: "#!1_stmt", + }, + ann_type: @20-24 Apply( + "", + "Task", + [ + @20-24 Record { + fields: [], + ext: None, + }, + @20-24 Inferred, + ], + ), + comment: None, + body_pattern: @20-24 Identifier { + ident: "#!1_stmt", + }, + body_expr: @20-24 Var { + module_name: "", + ident: "bar", + }, + }, + ], + }, + @20-24 Var { + module_name: "", + ident: "#!1_stmt", + }, + ), @20-24 Closure( [ - @20-24 RecordDestructure( - [], + @20-24 Underscore( + "#!stmt", ), ], @29-33 Var { diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index c5ced02d17..3f9caf99ca 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -28,24 +28,68 @@ Defs { ident: "await", }, [ - @11-23 Apply( + @11-23 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @11-23, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @11-23 Identifier { + ident: "#!0_stmt", + }, + ann_type: @11-23 Apply( + "", + "Task", + [ + @11-23 Record { + fields: [], + ext: None, + }, + @11-23 Inferred, + ], + ), + comment: None, + body_pattern: @11-23 Identifier { + ident: "#!0_stmt", + }, + body_expr: @11-23 Apply( + @11-23 Var { + module_name: "", + ident: "line", + }, + [ + @17-23 Str( + PlainLine( + "Ahoy", + ), + ), + ], + Space, + ), + }, + ], + }, @11-23 Var { module_name: "", - ident: "line", + ident: "#!0_stmt", }, - [ - @17-23 Str( - PlainLine( - "Ahoy", - ), - ), - ], - Space, ), @11-23 Closure( [ - @11-23 RecordDestructure( - [], + @11-23 Underscore( + "#!stmt", ), ], @33-55 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index 945758bd5e..4e2c7cb925 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -22,46 +22,77 @@ Defs { @0-4 Identifier { ident: "main", }, - @11-24 Apply( - @11-24 Var { + @11-16 Apply( + @11-16 Var { module_name: "Task", ident: "await", }, [ - @11-16 Var { - module_name: "", - ident: "foo", - }, - @11-24 Closure( - [ - @11-16 Identifier { - ident: "#!a0", - }, - ], - @11-16 Apply( - @11-16 Var { - module_name: "Task", - ident: "await", - }, - [ - @11-16 Var { - module_name: "", - ident: "#!a0", - }, - @11-16 Closure( - [ - @11-16 RecordDestructure( - [], - ), - ], - @21-24 Var { - module_name: "", - ident: "bar", - }, - ), + @11-16 Defs( + Defs { + tags: [ + Index(2147483648), ], - BangSuffix, - ), + regions: [ + @11-16, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @11-16 Identifier { + ident: "#!1_stmt", + }, + ann_type: @11-16 Apply( + "", + "Task", + [ + @11-16 Apply( + "", + "Task", + [ + @11-16 Record { + fields: [], + ext: None, + }, + @11-16 Inferred, + ], + ), + @11-16 Inferred, + ], + ), + comment: None, + body_pattern: @11-16 Identifier { + ident: "#!1_stmt", + }, + body_expr: @11-16 Var { + module_name: "", + ident: "foo", + }, + }, + ], + }, + @11-16 Var { + module_name: "", + ident: "#!1_stmt", + }, + ), + @11-16 Closure( + [ + @11-16 Underscore( + "#!stmt", + ), + ], + @21-24 Var { + module_name: "", + ident: "bar", + }, ), ], BangSuffix, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap index 0e75e9dfe9..f9a595f7f9 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap @@ -44,7 +44,7 @@ Defs { @15-43 Closure( [ Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @15-43 Apply( @@ -62,7 +62,7 @@ Defs { @21-29 ParensAround( Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ), @32-42 ParensAround( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap index 8e0d3d2023..b670cca0e4 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap @@ -35,7 +35,7 @@ Defs { @0-22 Closure( [ Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @0-22 Apply( @@ -47,7 +47,7 @@ Defs { @13-21 ParensAround( Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, ), ], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 2f8ede049b..5bf0ebd4e0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -28,42 +28,86 @@ Defs { ident: "await", }, [ - @11-56 Apply( + @26-56 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @11-56, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @26-56 Identifier { + ident: "#!0_stmt", + }, + ann_type: @26-56 Apply( + "", + "Task", + [ + @26-56 Record { + fields: [], + ext: None, + }, + @26-56 Inferred, + ], + ), + comment: None, + body_pattern: @26-56 Identifier { + ident: "#!0_stmt", + }, + body_expr: @11-56 Apply( + @11-56 Var { + module_name: "", + ident: "line", + }, + [ + @11-44 Apply( + @26-36 Var { + module_name: "Str", + ident: "concat", + }, + [ + @11-18 Str( + PlainLine( + "hello", + ), + ), + @37-44 Str( + PlainLine( + "world", + ), + ), + ], + BinOp( + Pizza, + ), + ), + ], + BinOp( + Pizza, + ), + ), + }, + ], + }, @11-56 Var { module_name: "", - ident: "line", + ident: "#!0_stmt", }, - [ - @11-44 Apply( - @26-36 Var { - module_name: "Str", - ident: "concat", - }, - [ - @11-18 Str( - PlainLine( - "hello", - ), - ), - @37-44 Str( - PlainLine( - "world", - ), - ), - ], - BinOp( - Pizza, - ), - ), - ], - BinOp( - Pizza, - ), ), @11-56 Closure( [ - @26-56 RecordDestructure( - [], + @26-56 Underscore( + "#!stmt", ), ], @63-73 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index d616d3f91f..52dbd27158 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -37,24 +37,68 @@ Defs { ident: "await", }, [ - @19-30 Apply( + @19-30 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @19-30, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @19-30 Identifier { + ident: "#!1_stmt", + }, + ann_type: @19-30 Apply( + "", + "Task", + [ + @19-30 Record { + fields: [], + ext: None, + }, + @19-30 Inferred, + ], + ), + comment: None, + body_pattern: @19-30 Identifier { + ident: "#!1_stmt", + }, + body_expr: @19-30 Apply( + @19-30 Var { + module_name: "", + ident: "line", + }, + [ + @25-30 Str( + PlainLine( + "FOO", + ), + ), + ], + Space, + ), + }, + ], + }, @19-30 Var { module_name: "", - ident: "line", + ident: "#!1_stmt", }, - [ - @25-30 Str( - PlainLine( - "FOO", - ), - ), - ], - Space, ), @19-30 Closure( [ - @19-30 RecordDestructure( - [], + @19-30 Underscore( + "#!stmt", ), ], @36-67 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap new file mode 100644 index 0000000000..e53888deea --- /dev/null +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -0,0 +1,112 @@ +--- +source: crates/compiler/can/tests/test_suffixed.rs +expression: snapshot +--- +Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-44, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "f", + }, + @4-44 Closure( + [ + @5-6 Identifier { + ident: "x", + }, + ], + @24-30 Apply( + @24-30 Var { + module_name: "Task", + ident: "await", + }, + [ + @14-30 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @24-30, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @14-15 Identifier { + ident: "#!0_expr", + }, + ann_type: @18-19 Apply( + "", + "Task", + [ + @18-19 Apply( + "", + "A", + [], + ), + @18-19 Inferred, + ], + ), + comment: None, + body_pattern: @24-25 Identifier { + ident: "#!0_expr", + }, + body_expr: @24-30 Var { + module_name: "", + ident: "x", + }, + }, + ], + }, + @24-30 Var { + module_name: "", + ident: "#!0_expr", + }, + ), + @24-30 Closure( + [ + @24-25 Identifier { + ident: "r", + }, + ], + @35-44 Apply( + @35-42 Var { + module_name: "Task", + ident: "ok", + }, + [ + @43-44 Var { + module_name: "", + ident: "r", + }, + ], + Space, + ), + ), + ], + BangSuffix, + ), + ), + ), + ], +} diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap index 7bd511af7a..89099ba13b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap @@ -51,7 +51,7 @@ Defs { @15-19 Closure( [ @24-33 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @24-33 Apply( @@ -62,7 +62,7 @@ Defs { [ @24-33 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, @24-33 Closure( [ diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index be4fbe082a..c8ef89774f 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -35,13 +35,13 @@ Defs { @11-120 Closure( [ @16-24 Identifier { - ident: "#!a1", + ident: "#!2_arg", }, ], @11-120 When( @16-24 Var { module_name: "", - ident: "#!a1", + ident: "#!2_arg", }, [ WhenBranch { @@ -56,24 +56,68 @@ Defs { ident: "await", }, [ - @54-65 Apply( + @54-65 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @54-65, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @54-65 Identifier { + ident: "#!1_stmt", + }, + ann_type: @54-65 Apply( + "", + "Task", + [ + @54-65 Record { + fields: [], + ext: None, + }, + @54-65 Inferred, + ], + ), + comment: None, + body_pattern: @54-65 Identifier { + ident: "#!1_stmt", + }, + body_expr: @54-65 Apply( + @54-65 Var { + module_name: "", + ident: "line", + }, + [ + @60-65 Str( + PlainLine( + "foo", + ), + ), + ], + Space, + ), + }, + ], + }, @54-65 Var { module_name: "", - ident: "line", + ident: "#!1_stmt", }, - [ - @60-65 Str( - PlainLine( - "foo", - ), - ), - ], - Space, ), @54-65 Closure( [ - @54-65 RecordDestructure( - [], + @54-65 Underscore( + "#!stmt", ), ], @78-89 Apply( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap index 1aaeca4b28..443dc9d065 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap @@ -35,13 +35,13 @@ Defs { @11-74 Closure( [ @16-24 Identifier { - ident: "#!a0", + ident: "#!0_arg", }, ], @11-74 When( @16-24 Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }, [ WhenBranch { From 863fb3b29fe180e6db857c64daed10b7228d3e3f Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Tue, 16 Jul 2024 11:33:47 +0200 Subject: [PATCH 23/30] remove obsolete test --- crates/compiler/can/tests/test_suffixed.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs index 9ef2be2965..0b9e618ec4 100644 --- a/crates/compiler/can/tests/test_suffixed.rs +++ b/crates/compiler/can/tests/test_suffixed.rs @@ -564,8 +564,6 @@ mod suffixed_tests { mod test_suffixed_helpers { use roc_can::suffixed::is_matching_intermediate_answer; - use roc_module::called_via::CalledVia; - use roc_module::ident::ModuleName; use roc_parse::ast::Expr; use roc_parse::ast::Pattern; use roc_region::all::Loc; @@ -580,21 +578,4 @@ mod test_suffixed_helpers { std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new)); } - - #[test] - fn test_matching_answer_task_ok() { - let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!a0" }); - let intermetiate = &[&Loc::at_zero(Expr::Var { - module_name: "", - ident: "#!a0", - })]; - let task_ok = Loc::at_zero(Expr::Var { - module_name: ModuleName::TASK, - ident: "ok", - }); - - let loc_new = Loc::at_zero(Expr::Apply(&task_ok, intermetiate, CalledVia::BangSuffix)); - - std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new)); - } } From 692f8e8090056056f72253ec140cf98a158b012c Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Tue, 16 Jul 2024 11:50:28 +0200 Subject: [PATCH 24/30] remove redundant dependency --- Cargo.lock | 1 - crates/compiler/can/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47570a8524..e44c1472b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2347,7 +2347,6 @@ dependencies = [ "roc_collections", "roc_error_macros", "roc_exhaustive", - "roc_fmt", "roc_module", "roc_parse", "roc_problem", diff --git a/crates/compiler/can/Cargo.toml b/crates/compiler/can/Cargo.toml index 9ef9239278..76b115bd5a 100644 --- a/crates/compiler/can/Cargo.toml +++ b/crates/compiler/can/Cargo.toml @@ -13,7 +13,6 @@ roc_error_macros = { path = "../../error_macros" } roc_exhaustive = { path = "../exhaustive" } roc_module = { path = "../module" } roc_parse = { path = "../parse" } -roc_fmt = { path = "../fmt" } roc_problem = { path = "../problem" } roc_region = { path = "../region" } roc_serialize = { path = "../serialize" } From 0086a531a20b6769598f90a9ae9f2739fcdcc14f Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Tue, 16 Jul 2024 11:56:42 +0200 Subject: [PATCH 25/30] add todo tests --- crates/compiler/load/tests/test_reporting.rs | 34 ++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 71418e4288..4eb61cef50 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -14503,4 +14503,38 @@ In roc, functions are always written as a lambda, like{} make partial application explicit. " ); + + // TODO: add the following tests after built-in Tasks are added + // https://github.com/roc-lang/roc/pull/6836 + + // test_report!( + // suffixed_stmt_invalid_type, + // indoc!( + // r###" + // app "test" provides [main] to "./platform" + + // main : Task U64 _ -> _ + // main = \task -> + // task! + // 42 + // "### + // ), + // @r"" + // ); + + // test_report!( + // suffixed_expr_invalid_type, + // indoc!( + // r###" + // app "test" provides [main] to "./platform" + + // main : Task U64 _ -> _ + // main = \task -> + // result : U32 + // result = task! + // result + // "### + // ), + // @r"" + // ); } From 7aa31a16390d0ff82b5ae662f53af2e7ba1ebe57 Mon Sep 17 00:00:00 2001 From: Kiryl Dziamura Date: Mon, 22 Jul 2024 20:04:43 +0200 Subject: [PATCH 26/30] code cleanup --- crates/compiler/can/src/suffixed.rs | 14 ++++++++------ crates/compiler/can/tests/test_suffixed.rs | 8 ++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 4656a0a650..b9299566d2 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -39,7 +39,7 @@ pub enum EUnwrapped<'a> { /// Suffixed sub expression /// e.g. x = first! (second! 42) /// In this example, the second unwrap (after unwrapping the top level `first!`) will produce - /// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!a0, sub_new: #!a0 }>` + /// `UnwrappedSubExpr<{ sub_arg: second 42, sub_pat: #!0_arg, sub_new: #!0_arg }>` UnwrappedSubExpr { /// the unwrapped expression argument for Task.await sub_arg: &'a Loc>, @@ -69,7 +69,7 @@ fn init_unwrapped_err<'a>( None => { // Provide an intermediate answer expression and pattern when unwrapping a // (sub) expression. - // e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!a0 -> foo #!a0` + // e.g. `x = foo (bar!)` unwraps to `x = Task.await (bar) \#!0_arg -> foo #!0_arg` let ident = arena.alloc(format!("{}_arg", next_unique_suffixed_ident())); let sub_new = arena.alloc(Loc::at( unwrapped_expr.region, @@ -861,7 +861,7 @@ pub fn apply_task_await<'a>( // \loc_pat -> loc_cont use roc_parse::ast::*; - // #!a0 + // #!0_expr or #!0_stmt let new_ident = next_unique_suffixed_ident(); let new_ident = match loc_pat.value { Pattern::Underscore("#!stmt") => format!("{}_stmt", new_ident), @@ -930,7 +930,7 @@ pub fn apply_task_await<'a>( Defs(arena.alloc(defs), new_var), )) } - _ => { + None => { // loc_pat = loc_expr! // loc_cont @@ -940,8 +940,10 @@ pub fn apply_task_await<'a>( } }; - // If the pattern and the new are matching answers then we don't need to unwrap anything - // e.g. `Task.await foo \#!a1 -> Task.ok #!a1` is the same as `foo` + // If the last expression is suffixed - don't await + // e.g. + // \x -> x! + // \x -> x if is_matching_intermediate_answer(loc_pat, loc_cont) { return task_await_first_arg; } diff --git a/crates/compiler/can/tests/test_suffixed.rs b/crates/compiler/can/tests/test_suffixed.rs index 0b9e618ec4..48ac9fb91c 100644 --- a/crates/compiler/can/tests/test_suffixed.rs +++ b/crates/compiler/can/tests/test_suffixed.rs @@ -128,7 +128,7 @@ mod suffixed_tests { * Example with a parens suffixed sub-expression * in the function part of an Apply. * - * Note how the parens unwraps into an intermediate answer #!a0 instead of + * Note how the parens unwraps into an intermediate answer #!0_arg instead of * unwrapping the def `do`. * */ @@ -162,7 +162,7 @@ mod suffixed_tests { /** * Example with a multiple suffixed Var * - * Note it unwraps into an intermediate answer `#!a0` + * Note it unwraps into an intermediate answer `#!0_arg` * */ #[test] @@ -570,10 +570,10 @@ mod test_suffixed_helpers { #[test] fn test_matching_answer() { - let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!a0" }); + let loc_pat = Loc::at_zero(Pattern::Identifier { ident: "#!0_arg" }); let loc_new = Loc::at_zero(Expr::Var { module_name: "", - ident: "#!a0", + ident: "#!0_arg", }); std::assert!(is_matching_intermediate_answer(&loc_pat, &loc_new)); From 4f32f4304827b176fd324c611593df96c4f7b2b1 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Mon, 8 Jul 2024 21:14:51 -0700 Subject: [PATCH 27/30] Implement block / indent based parsing ... and enforce that defs can only occur in blocks (or, inside parenthesized expressions) --- crates/cli/src/format.rs | 2 +- crates/compiler/can/src/desugar.rs | 4 - ...ffixed_tests__apply_argument_multiple.snap | 6 +- ...suffixed_tests__apply_argument_single.snap | 6 +- ...ffixed_tests__apply_argument_suffixed.snap | 6 +- ...ffixed_tests__apply_function_suffixed.snap | 6 +- ...ed__suffixed_tests__bang_in_pipe_root.snap | 6 +- .../test_suffixed__suffixed_tests__basic.snap | 6 +- ...ed__suffixed_tests__body_parens_apply.snap | 6 +- ...fixed__suffixed_tests__closure_simple.snap | 18 +- ...fixed_tests__closure_with_annotations.snap | 14 +- ...ed__suffixed_tests__closure_with_defs.snap | 6 +- ..._suffixed__suffixed_tests__dbg_simple.snap | 8 +- ...uffixed__suffixed_tests__dbg_stmt_arg.snap | 14 +- ...t_suffixed__suffixed_tests__deep_when.snap | 6 +- ..._suffixed_tests__defs_suffixed_middle.snap | 3 +- ...ixed__suffixed_tests__deps_final_expr.snap | 6 +- ...xed__suffixed_tests__expect_then_bang.snap | 61 +- ..._suffixed__suffixed_tests__if_complex.snap | 6 +- ...t_suffixed__suffixed_tests__if_simple.snap | 6 +- ...sts__last_stmt_not_top_level_suffixed.snap | 6 +- ...uffixed_tests__last_suffixed_multiple.snap | 6 +- ..._suffixed_tests__last_suffixed_single.snap | 10 +- ...xed__suffixed_tests__multi_defs_stmts.snap | 6 +- ...ed_tests__multiple_def_first_suffixed.snap | 10 +- ...ixed__suffixed_tests__multiple_suffix.snap | 6 +- ...fixed__suffixed_tests__nested_complex.snap | 6 +- ...suffixed__suffixed_tests__nested_defs.snap | 6 +- ...ffixed__suffixed_tests__nested_simple.snap | 16 +- ...uffixed__suffixed_tests__simple_pizza.snap | 20 +- ...ixed__suffixed_tests__trailing_binops.snap | 6 +- ...ed_tests__trailing_suffix_inside_when.snap | 6 +- ...ixed__suffixed_tests__type_annotation.snap | 18 +- ...uffixed__suffixed_tests__var_suffixes.snap | 18 +- ...ffixed__suffixed_tests__when_branches.snap | 6 +- ...suffixed__suffixed_tests__when_simple.snap | 6 +- crates/compiler/can/tests/test_can.rs | 4 +- crates/compiler/fmt/src/expr.rs | 10 +- crates/compiler/fmt/src/module.rs | 8 - crates/compiler/fmt/src/pattern.rs | 20 +- crates/compiler/fmt/src/spaces.rs | 865 +---- crates/compiler/load/tests/test_reporting.rs | 91 +- .../compiler/load_internal/tests/test_load.rs | 2 +- crates/compiler/module/src/called_via.rs | 60 +- crates/compiler/parse/src/ast.rs | 43 + crates/compiler/parse/src/blankspace.rs | 58 +- crates/compiler/parse/src/expr.rs | 2980 +++++++++-------- crates/compiler/parse/src/lib.rs | 1 + crates/compiler/parse/src/parser.rs | 10 +- crates/compiler/parse/src/remove_spaces.rs | 1749 ++++++++++ crates/compiler/parse/src/state.rs | 4 + crates/compiler/parse/src/test_helpers.rs | 6 +- crates/compiler/test_gen/src/gen_tags.rs | 4 +- crates/compiler/test_syntax/fuzz/Cargo.toml | 1 + .../fuzz/fuzz_targets/fuzz_expr.rs | 10 +- .../compiler/test_syntax/src/bin/minimize.rs | 23 + crates/compiler/test_syntax/src/lib.rs | 1 + crates/compiler/test_syntax/src/minimize.rs | 204 ++ .../compiler/test_syntax/src/test_helpers.rs | 27 +- ...ckpassing_after_annotation.expr.result-ast | 1 + .../backpassing_after_annotation.expr.roc | 2 + .../bound_variable.expr.formatted.roc | 0 .../fail/bound_variable.expr.result-ast | 1 + .../{pass => fail}/bound_variable.expr.roc | 0 ...f_missing_final_expression.expr.result-ast | 2 +- .../def_without_newline.expr.formatted.roc | 0 .../fail/def_without_newline.expr.result-ast | 1 + .../def_without_newline.expr.roc | 0 ...expression_indentation_end.expr.result-ast | 2 +- .../fail/if_missing_else.expr.result-ast | 2 +- .../fail/list_without_end.expr.result-ast | 2 +- ...fore_operator_with_defs.expr.formatted.roc | 0 ..._before_operator_with_defs.expr.result-ast | 1 + ...newline_before_operator_with_defs.expr.roc | 0 ...e_type_def_with_newline.expr.formatted.roc | 3 + ...aque_type_def_with_newline.expr.result-ast | 1 + .../opaque_type_def_with_newline.expr.roc | 0 .../pattern_in_parens_end.expr.result-ast | 2 +- ...attern_in_parens_end_comma.expr.result-ast | 2 +- ...tern_in_parens_indent_open.expr.result-ast | 2 +- .../pattern_in_parens_open.expr.result-ast | 2 +- .../fail/record_type_end.expr.result-ast | 2 +- .../fail/record_type_open.expr.result-ast | 2 +- .../record_type_open_indent.expr.result-ast | 2 +- .../fail/tag_union_end.expr.result-ast | 2 +- .../fail/tag_union_open.expr.result-ast | 2 +- .../fail/type_in_parens_end.expr.result-ast | 2 +- .../fail/type_in_parens_start.expr.result-ast | 2 +- ..._closure_pattern_in_parens.expr.result-ast | 2 +- ...mport_as_or_exposing.moduledefs.result-ast | 1 + ...ished_import_as_or_exposing.moduledefs.roc | 1 + .../fail/when_missing_arrow.expr.result-ast | 2 +- .../when_outdented_branch.expr.result-ast | 2 +- .../when_over_indented_int.expr.result-ast | 2 +- ...n_over_indented_underscore.expr.result-ast | 2 +- .../fail/where_type_variable.expr.result-ast | 2 +- .../fail/wild_case_arrow.expr.result-ast | 2 +- ...ed_pattern_field_access.expr.formatted.roc | 3 + ...ormed_pattern_field_access.expr.result-ast | 79 +- ...med_pattern_module_name.expr.formatted.roc | 3 + ...formed_pattern_module_name.expr.result-ast | 79 +- ...and_signature_is_multiline.expr.result-ast | 123 +- .../pass/ability_multi_line.expr.result-ast | 153 +- .../ability_single_line.expr.formatted.roc | 3 + .../pass/ability_single_line.expr.result-ast | 129 +- .../ability_two_in_a_row.expr.formatted.roc | 5 + .../pass/ability_two_in_a_row.expr.result-ast | 205 +- ...notated_record_destructure.expr.result-ast | 149 +- .../annotated_tag_destructure.expr.result-ast | 155 +- ...nnotated_tuple_destructure.expr.result-ast | 137 +- .../pass/basic_docs.expr.formatted.roc | 9 + .../snapshots/pass/basic_docs.expr.result-ast | 69 +- .../pass/bound_variable.expr.result-ast | 36 - .../call_with_newlines.expr.formatted.roc | 3 - .../pass/call_with_newlines.expr.result-ast | 25 - .../pass/call_with_newlines.expr.roc | 3 - .../pass/closure_in_binop.expr.formatted.roc | 3 - .../pass/closure_in_binop.expr.result-ast | 35 - .../snapshots/pass/closure_in_binop.expr.roc | 2 - ...refixed_indented_record.expr.formatted.roc | 8 + ...a_prefixed_indented_record.expr.result-ast | 130 + .../comma_prefixed_indented_record.expr.roc | 8 + ...comment_after_def.moduledefs.formatted.roc | 1 - ...omment_after_tag_in_def.expr.formatted.roc | 4 - .../comment_after_tag_in_def.expr.result-ast | 54 - .../pass/comment_after_tag_in_def.expr.roc | 4 - .../snapshots/pass/crash.expr.result-ast | 397 +-- .../snapshots/pass/dbg.expr.formatted.roc | 3 + .../tests/snapshots/pass/dbg.expr.result-ast | 40 +- .../tests/snapshots/pass/dbg.expr.roc | 1 + .../pass/dbg_multiline.expr.result-ast | 49 +- .../pass/def_without_newline.expr.result-ast | 32 - ...dle_extra_indents.moduledefs.formatted.roc | 9 + ...middle_extra_indents.moduledefs.result-ast | 159 + ...ffixed_middle_extra_indents.moduledefs.roc | 9 + ...destructure_tag_assignment.expr.result-ast | 103 +- .../tests/snapshots/pass/docs.expr.result-ast | 69 +- .../empty_hosted_header.header.formatted.roc | 1 - .../empty_module_header.header.formatted.roc | 1 - .../empty_package_header.header.formatted.roc | 1 - ...empty_platform_header.header.formatted.roc | 1 - .../pass/empty_string.expr.formatted.roc | 1 + .../pass/empty_string.expr.result-ast | 11 +- .../snapshots/pass/expect.expr.formatted.roc | 3 +- .../snapshots/pass/expect.expr.result-ast | 66 +- .../pass/expect_defs.moduledefs.result-ast | 453 +++ .../snapshots/pass/expect_defs.moduledefs.roc | 20 + .../pass/expect_fx.moduledefs.formatted.roc | 2 - .../pass/expect_fx.moduledefs.result-ast | 5 +- .../expect_single_line.expr.formatted.roc | 7 + .../pass/expect_single_line.expr.result-ast | 91 + .../pass/expect_single_line.expr.roc | 7 + .../pass/extra_newline.expr.formatted.roc | 6 + .../pass/extra_newline.expr.result-ast | 80 + .../snapshots/pass/extra_newline.expr.roc | 5 + ...ion_with_tuple_ext_type.expr.formatted.roc | 4 + ...nction_with_tuple_ext_type.expr.result-ast | 167 +- ...unction_with_tuple_type.expr.formatted.roc | 4 + .../function_with_tuple_type.expr.result-ast | 175 +- .../snapshots/pass/if_def.expr.result-ast | 69 +- .../pass/import.moduledefs.result-ast | 6 +- .../import_from_package.moduledefs.result-ast | 13 +- .../import_with_alias.moduledefs.result-ast | 7 +- ...import_with_comments.moduledefs.result-ast | 31 +- .../import_with_exposed.moduledefs.result-ast | 11 +- .../import_with_params.moduledefs.result-ast | 13 +- ...after_multi_backpassing.expr.formatted.roc | 6 + ...ed_after_multi_backpassing.expr.result-ast | 196 ++ .../indented_after_multi_backpassing.expr.roc | 6 + .../pass/ingested_file.moduledefs.result-ast | 7 +- .../pass/inline_import.expr.formatted.roc | 4 + .../pass/inline_import.expr.result-ast | 201 +- .../inline_ingested_file.expr.formatted.roc | 3 + .../pass/inline_ingested_file.expr.result-ast | 122 +- ...ne_ingested_file_no_ann.expr.formatted.roc | 3 + ...nline_ingested_file_no_ann.expr.result-ast | 104 +- ..._closing_indent_not_enough.expr.result-ast | 173 +- ...e_indent_no_trailing_comma.expr.result-ast | 99 +- ...ent_with_trailing_comma.expr.formatted.roc | 5 + ...indent_with_trailing_comma.expr.result-ast | 109 +- .../list_pattern_weird_indent.expr.result-ast | 73 +- .../pass/list_patterns.expr.result-ast | 453 +-- .../minimal_app_header.header.formatted.roc | 1 - .../pass/mixed_docs.expr.formatted.roc | 7 + .../snapshots/pass/mixed_docs.expr.result-ast | 69 +- ...odule_def_newline.moduledefs.formatted.roc | 4 - .../module_def_newline.moduledefs.result-ast | 6 +- .../module_with_newline.header.formatted.roc | 1 - .../pass/multi_backpassing.expr.formatted.roc | 3 + .../pass/multi_backpassing.expr.result-ast | 85 +- ...i_backpassing_in_def.moduledefs.result-ast | 6 +- .../pass/multi_char_string.expr.formatted.roc | 1 + .../pass/multi_char_string.expr.result-ast | 11 +- ...inop_when_with_comments.expr.formatted.roc | 25 + ...e_binop_when_with_comments.expr.result-ast | 268 ++ ...ultiline_binop_when_with_comments.expr.roc | 25 + .../multiline_str_in_pat.expr.formatted.roc | 4 + .../pass/multiline_str_in_pat.expr.result-ast | 62 + .../pass/multiline_str_in_pat.expr.roc | 2 + .../pass/multiline_string.expr.result-ast | 165 +- ...ssing_no_newline_before.expr.formatted.roc | 7 - ...kpassing_no_newline_before.expr.result-ast | 97 - ...ted_backpassing_no_newline_before.expr.roc | 6 - ...ed_def_annotation.moduledefs.formatted.roc | 6 - ...ested_def_annotation.moduledefs.result-ast | 6 +- ...ted_def_without_newline.expr.formatted.roc | 4 - ...nested_def_without_newline.expr.result-ast | 64 - .../pass/nested_def_without_newline.expr.roc | 2 - .../snapshots/pass/nested_if.expr.result-ast | 97 +- .../newline_after_equals.expr.formatted.roc | 4 + .../pass/newline_after_equals.expr.result-ast | 77 +- ..._before_operator_with_defs.expr.result-ast | 49 - .../pass/newline_in_packages.full.result-ast | 3 +- .../number_literal_suffixes.expr.result-ast | 721 ++-- .../pass/old_app_header.full.formatted.roc | 1 + .../pass/old_app_header.full.result-ast | 4 +- .../pass/one_backpassing.expr.result-ast | 51 +- .../pass/one_char_string.expr.formatted.roc | 1 + .../pass/one_char_string.expr.result-ast | 11 +- .../snapshots/pass/one_def.expr.result-ast | 69 +- .../pass/one_spaced_def.expr.formatted.roc | 4 + .../pass/one_spaced_def.expr.result-ast | 69 +- ...ructure_first_item_in_body.expr.result-ast | 135 +- .../pass/opaque_has_abilities.expr.result-ast | 923 ++--- .../opaque_reference_expr.expr.formatted.roc | 1 + .../opaque_reference_expr.expr.result-ast | 9 +- ...nce_expr_with_arguments.expr.formatted.roc | 1 + ...erence_expr_with_arguments.expr.result-ast | 29 +- .../opaque_reference_pattern.expr.result-ast | 47 +- ...nce_pattern_with_arguments.expr.result-ast | 83 +- .../opaque_simple.moduledefs.formatted.roc | 1 - .../pass/opaque_simple.moduledefs.result-ast | 6 +- ...e_type_def_with_newline.expr.formatted.roc | 4 - ...aque_type_def_with_newline.expr.result-ast | 54 - ..._with_type_arguments.moduledefs.result-ast | 8 +- .../outdented_app_with_record.expr.result-ast | 135 +- .../outdented_colon_in_record.expr.result-ast | 121 +- .../pass/outdented_list.expr.result-ast | 107 +- .../pass/outdented_record.expr.result-ast | 115 +- ...in_value_def_annotation.expr.formatted.roc | 3 - ...ns_in_value_def_annotation.expr.result-ast | 52 - .../parens_in_value_def_annotation.expr.roc | 4 - .../parenthesized_type_def.expr.result-ast | 2 +- ...ized_type_def_space_before.expr.result-ast | 2 +- .../pass/parse_alias.expr.formatted.roc | 3 + .../pass/parse_alias.expr.result-ast | 105 +- .../pass/parse_as_ann.expr.formatted.roc | 3 + .../pass/parse_as_ann.expr.result-ast | 113 +- .../pass/pattern_as.expr.formatted.roc | 2 + .../snapshots/pass/pattern_as.expr.result-ast | 57 +- .../pattern_as_list_rest.expr.formatted.roc | 2 + .../pass/pattern_as_list_rest.expr.result-ast | 69 +- .../pass/pattern_as_spaces.expr.formatted.roc | 4 + .../pass/pattern_as_spaces.expr.result-ast | 77 +- ...ttern_with_space_in_parens.expr.result-ast | 139 +- .../snapshots/pass/plus_if.expr.formatted.roc | 1 + .../snapshots/pass/plus_if.expr.result-ast | 39 +- .../snapshots/pass/plus_when.expr.result-ast | 99 +- .../pass/provides_type.header.formatted.roc | 1 - .../record_destructure_def.expr.formatted.roc | 5 + .../record_destructure_def.expr.result-ast | 109 +- .../record_func_type_decl.expr.result-ast | 223 +- ...cord_type_with_function.expr.formatted.roc | 3 + .../record_type_with_function.expr.result-ast | 171 +- .../pass/separate_defs.moduledefs.result-ast | 137 + .../pass/separate_defs.moduledefs.roc | 7 + .../pass/space_before_colon.full.result-ast | 3 +- ...alone_module_defs.moduledefs.formatted.roc | 7 - ...ffixed_multiple_defs.moduledefs.result-ast | 2 +- .../pass/suffixed_one_def.full.formatted.roc | 2 + .../pass/suffixed_one_def.full.result-ast | 107 +- .../suffixed_optional_last.full.result-ast | 3 +- .../pass/two_backpassing.expr.result-ast | 77 +- .../pass/two_branch_when.expr.result-ast | 87 +- .../pass/two_spaced_def.expr.formatted.roc | 5 + .../pass/two_spaced_def.expr.result-ast | 95 +- .../underscore_backpassing.expr.result-ast | 51 +- ...core_in_assignment_pattern.expr.result-ast | 425 +-- .../pass/when_if_guard.expr.formatted.roc | 9 + .../pass/when_if_guard.expr.result-ast | 145 +- .../pass/when_in_parens.expr.result-ast | 53 +- .../when_in_parens_indented.expr.result-ast | 57 +- ..._with_alternative_patterns.expr.result-ast | 149 +- ..._with_function_application.expr.result-ast | 91 +- ...when_with_negative_numbers.expr.result-ast | 79 +- .../pass/when_with_numbers.expr.result-ast | 79 +- .../pass/when_with_records.expr.result-ast | 101 +- .../when_with_tuple_in_record.expr.result-ast | 173 +- .../pass/when_with_tuples.expr.result-ast | 137 +- .../where_clause_function.expr.formatted.roc | 3 + .../where_clause_function.expr.result-ast | 117 +- ...e_multiple_bound_abilities.expr.result-ast | 287 +- ...ere_clause_multiple_has.expr.formatted.roc | 3 + .../where_clause_multiple_has.expr.result-ast | 157 +- ...ltiple_has_across_newlines.expr.result-ast | 181 +- ...ere_clause_non_function.expr.formatted.roc | 3 + .../where_clause_non_function.expr.result-ast | 99 +- .../where_clause_on_newline.expr.result-ast | 121 +- .../pass/where_ident.expr.result-ast | 123 +- crates/compiler/test_syntax/tests/test_fmt.rs | 83 +- .../test_syntax/tests/test_snapshots.rs | 33 +- crates/repl_ui/src/lib.rs | 42 +- crates/repl_ui/src/repl_state.rs | 507 ++- crates/reporting/src/error/parse.rs | 23 + 304 files changed, 12050 insertions(+), 8876 deletions(-) create mode 100644 crates/compiler/parse/src/remove_spaces.rs create mode 100644 crates/compiler/test_syntax/src/bin/minimize.rs create mode 100644 crates/compiler/test_syntax/src/minimize.rs create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/bound_variable.expr.formatted.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/bound_variable.expr.roc (100%) rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/def_without_newline.expr.formatted.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/def_without_newline.expr.roc (100%) rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/newline_before_operator_with_defs.expr.formatted.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/newline_before_operator_with_defs.expr.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/{pass => fail}/opaque_type_def_with_newline.expr.roc (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc diff --git a/crates/cli/src/format.rs b/crates/cli/src/format.rs index a5a56903aa..7d33833fb4 100644 --- a/crates/cli/src/format.rs +++ b/crates/cli/src/format.rs @@ -6,9 +6,9 @@ use bumpalo::Bump; use roc_error_macros::{internal_error, user_error}; use roc_fmt::def::fmt_defs; use roc_fmt::module::fmt_module; -use roc_fmt::spaces::RemoveSpaces; use roc_fmt::{Ast, Buf}; use roc_parse::module::parse_module_defs; +use roc_parse::remove_spaces::RemoveSpaces; use roc_parse::{module, parser::SyntaxError, state::State}; #[derive(Copy, Clone, Debug)] diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index a036b1b766..2ab9a5ec9c 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -1361,10 +1361,6 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) { And => (ModuleName::BOOL, "and"), Or => (ModuleName::BOOL, "or"), Pizza => unreachable!("Cannot desugar the |> operator"), - Assignment => unreachable!("Cannot desugar the = operator"), - IsAliasType => unreachable!("Cannot desugar the : operator"), - IsOpaqueType => unreachable!("Cannot desugar the := operator"), - Backpassing => unreachable!("Cannot desugar the <- operator"), } } diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap index d5f94a4db2..8b9089316e 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_multiple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap index c121a19025..faf24d92ad 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_single.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap index 2828856b17..e314e29d1c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_argument_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap index d72b00aad3..4152556637 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__apply_function_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap index 62aaa64c9f..3806e86373 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__bang_in_pipe_root.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap index c78f101cc8..7f5bb96242 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__basic.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap index 247b3cb142..217f69856c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__body_parens_apply.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap index 4ed25bf707..f5ee75bfe5 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -73,22 +75,22 @@ Defs { type_defs: [], value_defs: [ AnnotatedBody { - ann_pattern: @31-42 Identifier { + ann_pattern: @31-43 Identifier { ident: "#!0_stmt", }, - ann_type: @31-42 Apply( + ann_type: @31-43 Apply( "", "Task", [ - @31-42 Record { + @31-43 Record { fields: [], ext: None, }, - @31-42 Inferred, + @31-43 Inferred, ], ), comment: None, - body_pattern: @31-42 Identifier { + body_pattern: @31-43 Identifier { ident: "#!0_stmt", }, body_expr: @31-42 Apply( @@ -116,7 +118,7 @@ Defs { ), @31-42 Closure( [ - @31-42 Underscore( + @31-43 Underscore( "#!stmt", ), ], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap index c88b75ab11..36a6dc4c3a 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_annotations.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -76,7 +78,7 @@ Defs { Index(2147483648), ], regions: [ - @78-91, + @82-91, ], space_before: [ Slice(start = 0, length = 0), @@ -113,8 +115,8 @@ Defs { body_pattern: @78-79 Identifier { ident: "#!0_expr", }, - body_expr: @78-91 Apply( - @78-91 Var { + body_expr: @82-91 Apply( + @82-91 Var { module_name: "", ident: "line", }, @@ -129,7 +131,7 @@ Defs { }, ], }, - @78-91 Var { + @82-91 Var { module_name: "", ident: "#!0_expr", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap index 490fddac5a..ceb0ca47ec 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__closure_with_defs.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap index ad5037f246..5b606704d0 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -38,7 +40,7 @@ Defs { ident: "foo", }, ], - @29-49 LowLevelDbg( + @29-36 LowLevelDbg( ( "test.roc:3", " ", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap index d3918ce09b..4da8bb363c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__dbg_stmt_arg.snap @@ -13,17 +13,19 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-4 Identifier { ident: "main", }, - @11-24 Apply( - @11-24 Var { + @11-17 Apply( + @11-17 Var { module_name: "Task", ident: "await", }, @@ -32,13 +34,13 @@ Defs { module_name: "", ident: "a", }, - @11-24 Closure( + @11-17 Closure( [ @15-17 Identifier { ident: "#!0_arg", }, ], - @11-24 LowLevelDbg( + @11-17 LowLevelDbg( ( "test.roc:2", "in", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap index 31dfae24db..421aa22f3b 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deep_when.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap index a07119b6c8..79dd90e6d8 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__defs_suffixed_middle.snap @@ -17,11 +17,12 @@ Defs { ], space_after: [ Slice(start = 0, length = 0), - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap index 326b127051..bafdfd47ec 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__deps_final_expr.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap index 5c38b6513c..e7fa4402ef 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__expect_then_bang.snap @@ -13,33 +13,56 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-4 Identifier { ident: "main", }, - @11-31 Expect( - @18-24 Apply( - @20-22 Var { - module_name: "Bool", - ident: "isEq", - }, - [ - @18-19 Num( - "1", - ), - @23-24 Num( - "2", - ), + @11-31 Defs( + Defs { + tags: [ + Index(2147483648), ], - BinOp( - Equals, - ), - ), + regions: [ + @11-24, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Expect { + condition: @18-24 Apply( + @20-22 Var { + module_name: "Bool", + ident: "isEq", + }, + [ + @18-19 Num( + "1", + ), + @23-24 Num( + "2", + ), + ], + BinOp( + Equals, + ), + ), + preceding_comment: @11-11, + }, + ], + }, @29-31 Var { module_name: "", ident: "x", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap index 9e62fed352..68c040cfad 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_complex.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap index 02592ffa7b..cdf56fb5bb 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__if_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap index 8af15de1a8..d7f364a77d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_stmt_not_top_level_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap index 8a09018284..59355d640e 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_multiple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap index 1d6cde10da..dd4437e831 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__last_suffixed_single.snap @@ -13,17 +13,19 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-4 Identifier { ident: "main", }, - @0-26 Apply( - @0-26 Var { + @7-26 Apply( + @7-26 Var { module_name: "", ident: "foo", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap index 3f9caf99ca..4245226948 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multi_defs_stmts.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap index 47d204a6c0..cce6f34b26 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_def_first_suffixed.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -57,8 +59,8 @@ Defs { ident: "await", }, [ - @29-41 Apply( - @29-41 Var { + @33-41 Apply( + @33-41 Var { module_name: "", ident: "foo", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap index 4e2c7cb925..56ecae8ecb 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__multiple_suffix.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap index f9a595f7f9..90fbd9e664 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_complex.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap index e06ae06272..e077450d59 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_defs.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap index b670cca0e4..8ce7be3381 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__nested_simple.snap @@ -13,17 +13,19 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @0-3 Identifier { ident: "run", }, - @0-22 Apply( - @0-22 Var { + @6-22 Apply( + @6-22 Var { module_name: "Task", ident: "await", }, @@ -32,14 +34,14 @@ Defs { module_name: "", ident: "nextMsg", }, - @0-22 Closure( + @6-22 Closure( [ Identifier { ident: "#!0_arg", }, ], - @0-22 Apply( - @0-22 Var { + @6-22 Apply( + @6-22 Var { module_name: "", ident: "line", }, diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap index 5bf0ebd4e0..d734a3c20c 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__simple_pizza.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -28,7 +30,7 @@ Defs { ident: "await", }, [ - @26-56 Defs( + @11-56 Defs( Defs { tags: [ Index(2147483648), @@ -46,22 +48,22 @@ Defs { type_defs: [], value_defs: [ AnnotatedBody { - ann_pattern: @26-56 Identifier { + ann_pattern: @11-57 Identifier { ident: "#!0_stmt", }, - ann_type: @26-56 Apply( + ann_type: @11-57 Apply( "", "Task", [ - @26-56 Record { + @11-57 Record { fields: [], ext: None, }, - @26-56 Inferred, + @11-57 Inferred, ], ), comment: None, - body_pattern: @26-56 Identifier { + body_pattern: @11-57 Identifier { ident: "#!0_stmt", }, body_expr: @11-56 Apply( @@ -106,7 +108,7 @@ Defs { ), @11-56 Closure( [ - @26-56 Underscore( + @11-57 Underscore( "#!stmt", ), ], diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap index 52dbd27158..4e35c89c3d 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_binops.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap index da7e5a73e1..e987494e70 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__trailing_suffix_inside_when.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap index e53888deea..6ce8b488d9 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__type_annotation.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -28,8 +30,8 @@ Defs { ident: "x", }, ], - @24-30 Apply( - @24-30 Var { + @28-30 Apply( + @28-30 Var { module_name: "Task", ident: "await", }, @@ -40,7 +42,7 @@ Defs { Index(2147483648), ], regions: [ - @24-30, + @28-30, ], space_before: [ Slice(start = 0, length = 0), @@ -71,19 +73,19 @@ Defs { body_pattern: @24-25 Identifier { ident: "#!0_expr", }, - body_expr: @24-30 Var { + body_expr: @28-30 Var { module_name: "", ident: "x", }, }, ], }, - @24-30 Var { + @28-30 Var { module_name: "", ident: "#!0_expr", }, ), - @24-30 Closure( + @28-30 Closure( [ @24-25 Identifier { ident: "r", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap index 89099ba13b..2791e5b319 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__var_suffixes.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( @@ -44,27 +46,27 @@ Defs { ident: "await", }, [ - @24-33 Var { + @28-33 Var { module_name: "", ident: "bar", }, @15-19 Closure( [ - @24-33 Identifier { + @28-33 Identifier { ident: "#!0_arg", }, ], - @24-33 Apply( - @24-33 Var { + @28-33 Apply( + @28-33 Var { module_name: "Task", ident: "await", }, [ - @24-33 Var { + @28-33 Var { module_name: "", ident: "#!0_arg", }, - @24-33 Closure( + @28-33 Closure( [ @24-25 Identifier { ident: "b", diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap index c8ef89774f..def55dfc30 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_branches.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap index 443dc9d065..f803d115a3 100644 --- a/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap +++ b/crates/compiler/can/tests/snapshots/test_suffixed__suffixed_tests__when_simple.snap @@ -13,9 +13,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/can/tests/test_can.rs b/crates/compiler/can/tests/test_can.rs index 429924b43b..724d1f24b1 100644 --- a/crates/compiler/can/tests/test_can.rs +++ b/crates/compiler/can/tests/test_can.rs @@ -382,7 +382,7 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 0); + assert_eq!(problems, Vec::new()); } #[test] @@ -399,7 +399,7 @@ mod test_can { let arena = Bump::new(); let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src); - assert_eq!(problems.len(), 0); + assert_eq!(problems, Vec::new()); } #[test] diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 2f42225f00..7793bd125d 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -543,7 +543,7 @@ impl<'a> Formattable for Expr<'a> { } } -fn is_str_multiline(literal: &StrLiteral) -> bool { +pub fn is_str_multiline(literal: &StrLiteral) -> bool { use roc_parse::ast::StrLiteral::*; match literal { @@ -671,10 +671,6 @@ fn push_op(buf: &mut Buf, op: BinOp) { called_via::BinOp::And => buf.push_str("&&"), called_via::BinOp::Or => buf.push_str("||"), called_via::BinOp::Pizza => buf.push_str("|>"), - called_via::BinOp::Assignment => unreachable!(), - called_via::BinOp::IsAliasType => unreachable!(), - called_via::BinOp::IsOpaqueType => unreachable!(), - called_via::BinOp::Backpassing => unreachable!(), } } @@ -1708,10 +1704,6 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool { | BinOp::And | BinOp::Or | BinOp::Pizza => true, - BinOp::Assignment - | BinOp::IsAliasType - | BinOp::IsOpaqueType - | BinOp::Backpassing => false, }) } Expr::If(_, _) => true, diff --git a/crates/compiler/fmt/src/module.rs b/crates/compiler/fmt/src/module.rs index fc0db50567..76568f85af 100644 --- a/crates/compiler/fmt/src/module.rs +++ b/crates/compiler/fmt/src/module.rs @@ -3,10 +3,8 @@ use std::cmp::max; use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens}; use crate::collection::{fmt_collection, Braces}; use crate::expr::fmt_str_literal; -use crate::spaces::RemoveSpaces; use crate::spaces::{fmt_comments_only, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT}; use crate::Buf; -use bumpalo::Bump; use roc_parse::ast::{Collection, CommentOrNewline, Header, Module, Spaced, Spaces}; use roc_parse::header::{ AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, @@ -58,12 +56,6 @@ macro_rules! keywords { buf.push_str($name::KEYWORD); } } - - impl<'a> RemoveSpaces<'a> for $name { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } - } )* } } diff --git a/crates/compiler/fmt/src/pattern.rs b/crates/compiler/fmt/src/pattern.rs index d18cabd383..015c739a58 100644 --- a/crates/compiler/fmt/src/pattern.rs +++ b/crates/compiler/fmt/src/pattern.rs @@ -1,5 +1,5 @@ use crate::annotation::{Formattable, Newlines, Parens}; -use crate::expr::{fmt_str_literal, format_sq_literal}; +use crate::expr::{fmt_str_literal, format_sq_literal, is_str_multiline}; use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT}; use crate::Buf; use roc_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs}; @@ -48,7 +48,7 @@ impl<'a> Formattable for Pattern<'a> { pattern ); - spaces.iter().any(|s| s.is_comment()) + spaces.iter().any(|s| s.is_comment()) || pattern.is_multiline() } Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()), @@ -63,15 +63,17 @@ impl<'a> Formattable for Pattern<'a> { list_rest_spaces.iter().any(|s| s.is_comment()) || pattern_as.is_multiline() } }, + Pattern::StrLiteral(literal) => is_str_multiline(literal), + Pattern::Apply(pat, args) => { + pat.is_multiline() || args.iter().any(|a| a.is_multiline()) + } Pattern::Identifier { .. } | Pattern::Tag(_) | Pattern::OpaqueRef(_) - | Pattern::Apply(_, _) | Pattern::NumLiteral(..) | Pattern::NonBase10Literal { .. } | Pattern::FloatLiteral(..) - | Pattern::StrLiteral(_) | Pattern::SingleQuote(_) | Pattern::Underscore(_) | Pattern::Malformed(_) @@ -100,7 +102,13 @@ impl<'a> Formattable for Pattern<'a> { buf.indent(indent); // Sometimes, an Apply pattern needs parens around it. // In particular when an Apply's argument is itself an Apply (> 0) arguments - let parens = !loc_arg_patterns.is_empty() && parens == Parens::InApply; + let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply); + + let indent_more = if self.is_multiline() { + indent + INDENT + } else { + indent + }; if parens { buf.push('('); @@ -110,7 +118,7 @@ impl<'a> Formattable for Pattern<'a> { for loc_arg in loc_arg_patterns.iter() { buf.spaces(1); - loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent); + loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent_more); } if parens { diff --git a/crates/compiler/fmt/src/spaces.rs b/crates/compiler/fmt/src/spaces.rs index 7bb057bedc..e794a3c67a 100644 --- a/crates/compiler/fmt/src/spaces.rs +++ b/crates/compiler/fmt/src/spaces.rs @@ -1,23 +1,5 @@ -use bumpalo::collections::vec::Vec; use bumpalo::Bump; -use roc_module::called_via::{BinOp, UnaryOp}; -use roc_parse::{ - ast::{ - AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, - Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, - ImportAsKeyword, ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, - IngestedFileImport, Module, ModuleImport, ModuleImportParams, OldRecordBuilderField, - Pattern, PatternAs, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, - TypeHeader, ValueDef, WhenBranch, - }, - header::{ - AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName, - ModuleParams, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, - ProvidesTo, To, TypedIdent, - }, - ident::{BadIdent, UppercaseIdent}, -}; -use roc_region::all::{Loc, Position, Region}; +use roc_parse::{ast::CommentOrNewline, remove_spaces::RemoveSpaces}; use crate::{Ast, Buf}; @@ -211,20 +193,6 @@ fn fmt_docs(buf: &mut Buf, docs: &str) { buf.push_str(docs.trim_end()); } -/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting. -/// -/// Currently this consists of: -/// * Removing newlines -/// * Removing comments -/// * Removing parens in Exprs -/// -/// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting) -/// - but there are currently several bugs where they're _not_ preserved. -/// TODO: ensure formatting retains comments -pub trait RemoveSpaces<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self; -} - impl<'a> RemoveSpaces<'a> for Ast<'a> { fn remove_spaces(&self, arena: &'a Bump) -> Self { Ast { @@ -233,834 +201,3 @@ impl<'a> RemoveSpaces<'a> for Ast<'a> { } } } - -impl<'a> RemoveSpaces<'a> for Defs<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let mut defs = self.clone(); - - defs.spaces.clear(); - defs.space_before.clear(); - defs.space_after.clear(); - - for type_def in defs.type_defs.iter_mut() { - *type_def = type_def.remove_spaces(arena); - } - - for value_def in defs.value_defs.iter_mut() { - *value_def = value_def.remove_spaces(arena); - } - - for region_def in defs.regions.iter_mut() { - *region_def = region_def.remove_spaces(arena); - } - - defs - } -} - -impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - Spaces { - before: &[], - item: self.item.remove_spaces(arena), - after: &[], - } - } -} - -impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - KeywordItem { - keyword: self.keyword.remove_spaces(arena), - item: self.item.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ProvidesTo { - provides_keyword: self.provides_keyword.remove_spaces(arena), - entries: self.entries.remove_spaces(arena), - types: self.types.remove_spaces(arena), - to_keyword: self.to_keyword.remove_spaces(arena), - to: self.to.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for Module<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let header = match &self.header { - Header::Module(header) => Header::Module(ModuleHeader { - after_keyword: &[], - params: header.params.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - interface_imports: header.interface_imports.remove_spaces(arena), - }), - Header::App(header) => Header::App(AppHeader { - before_provides: &[], - provides: header.provides.remove_spaces(arena), - before_packages: &[], - packages: header.packages.remove_spaces(arena), - old_imports: header.old_imports.remove_spaces(arena), - old_provides_to_new_package: header - .old_provides_to_new_package - .remove_spaces(arena), - }), - Header::Package(header) => Header::Package(PackageHeader { - before_exposes: &[], - exposes: header.exposes.remove_spaces(arena), - before_packages: &[], - packages: header.packages.remove_spaces(arena), - }), - Header::Platform(header) => Header::Platform(PlatformHeader { - before_name: &[], - name: header.name.remove_spaces(arena), - requires: header.requires.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - packages: header.packages.remove_spaces(arena), - imports: header.imports.remove_spaces(arena), - provides: header.provides.remove_spaces(arena), - }), - Header::Hosted(header) => Header::Hosted(HostedHeader { - before_name: &[], - name: header.name.remove_spaces(arena), - exposes: header.exposes.remove_spaces(arena), - imports: header.imports.remove_spaces(arena), - generates: header.generates.remove_spaces(arena), - generates_with: header.generates_with.remove_spaces(arena), - }), - }; - Module { - comments: &[], - header, - } - } -} - -impl<'a> RemoveSpaces<'a> for ModuleParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ModuleParams { - params: self.params.remove_spaces(arena), - before_arrow: &[], - after_arrow: &[], - } - } -} - -impl<'a> RemoveSpaces<'a> for Region { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - Region::zero() - } -} - -impl<'a> RemoveSpaces<'a> for &'a str { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - self - } -} - -impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)), - Spaced::SpaceBefore(a, _) => a.remove_spaces(arena), - Spaced::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ExposedName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for ModuleName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for PackageName<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for To<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - To::ExistingPackage(a) => To::ExistingPackage(a), - To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)), - } - } -} - -impl<'a> RemoveSpaces<'a> for TypedIdent<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - TypedIdent { - ident: self.ident.remove_spaces(arena), - spaces_before_colon: &[], - ann: self.ann.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - PlatformRequires { - rigids: self.rigids.remove_spaces(arena), - signature: self.signature.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for PackageEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - PackageEntry { - shorthand: self.shorthand, - spaces_after_shorthand: &[], - platform_marker: match self.platform_marker { - Some(_) => Some(&[]), - None => None, - }, - package_name: self.package_name.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)), - ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)), - ImportsEntry::IngestedFile(a, b) => { - ImportsEntry::IngestedFile(a, b.remove_spaces(arena)) - } - } - } -} - -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - self.as_ref().map(|a| a.remove_spaces(arena)) - } -} - -impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let res = self.value.remove_spaces(arena); - Loc::at(Region::zero(), res) - } -} - -impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - (self.0.remove_spaces(arena), self.1.remove_spaces(arena)) - } -} - -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let mut items = Vec::with_capacity_in(self.items.len(), arena); - for item in self.items { - items.push(item.remove_spaces(arena)); - } - Collection::with_items(items.into_bump_slice()) - } -} - -impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - let mut items = Vec::with_capacity_in(self.len(), arena); - for item in *self { - let res = item.remove_spaces(arena); - items.push(res); - } - items.into_bump_slice() - } -} - -impl<'a> RemoveSpaces<'a> for UnaryOp { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for BinOp { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - arena.alloc((*self).remove_spaces(arena)) - } -} - -impl<'a> RemoveSpaces<'a> for TypeDef<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - use TypeDef::*; - - match *self { - Alias { - header: TypeHeader { name, vars }, - ann, - } => Alias { - header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - ann: ann.remove_spaces(arena), - }, - Opaque { - header: TypeHeader { name, vars }, - typ, - derived, - } => Opaque { - header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - typ: typ.remove_spaces(arena), - derived: derived.remove_spaces(arena), - }, - Ability { - header: TypeHeader { name, vars }, - loc_implements: loc_has, - members, - } => Ability { - header: TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - loc_implements: loc_has.remove_spaces(arena), - members: members.remove_spaces(arena), - }, - } - } -} - -impl<'a> RemoveSpaces<'a> for ValueDef<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - use ValueDef::*; - - match *self { - Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)), - Body(a, b) => Body( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - AnnotatedBody { - ann_pattern, - ann_type, - comment: _, - body_pattern, - body_expr, - } => AnnotatedBody { - ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), - ann_type: arena.alloc(ann_type.remove_spaces(arena)), - comment: None, - body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), - body_expr: arena.alloc(body_expr.remove_spaces(arena)), - }, - Dbg { - condition, - preceding_comment: _, - } => Dbg { - condition: arena.alloc(condition.remove_spaces(arena)), - preceding_comment: Region::zero(), - }, - Expect { - condition, - preceding_comment: _, - } => Expect { - condition: arena.alloc(condition.remove_spaces(arena)), - preceding_comment: Region::zero(), - }, - ExpectFx { - condition, - preceding_comment: _, - } => ExpectFx { - condition: arena.alloc(condition.remove_spaces(arena)), - preceding_comment: Region::zero(), - }, - ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)), - IngestedFileImport(ingested_file_import) => { - IngestedFileImport(ingested_file_import.remove_spaces(arena)) - } - Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))), - } - } -} - -impl<'a> RemoveSpaces<'a> for ModuleImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ModuleImport { - before_name: &[], - name: self.name.remove_spaces(arena), - params: self.params.remove_spaces(arena), - alias: self.alias.remove_spaces(arena), - exposed: self.exposed.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ModuleImportParams { - before: &[], - params: self.params.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - IngestedFileImport { - before_path: &[], - path: self.path.remove_spaces(arena), - name: self.name.remove_spaces(arena), - annotation: self.annotation.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ImportedModuleName { - package: self.package.remove_spaces(arena), - name: self.name.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImportAlias<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for ImportAsKeyword { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for ImportExposingKeyword { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - *self - } -} - -impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - IngestedFileAnnotation { - before_colon: &[], - annotation: self.annotation.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for Implements<'a> { - fn remove_spaces(&self, _arena: &'a Bump) -> Self { - Implements::Implements - } -} - -impl<'a> RemoveSpaces<'a> for AbilityMember<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - AbilityMember { - name: self.name.remove_spaces(arena), - typ: self.typ.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for WhenBranch<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - WhenBranch { - patterns: self.patterns.remove_spaces(arena), - value: self.value.remove_spaces(arena), - guard: self.guard.remove_spaces(arena), - } - } -} - -impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue( - a.remove_spaces(arena), - arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), - ), - AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue( - a.remove_spaces(arena), - arena.alloc([]), - arena.alloc(c.remove_spaces(arena)), - ), - AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)), - AssignedField::Malformed(a) => AssignedField::Malformed(a), - AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena), - AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value( - a.remove_spaces(arena), - &[], - arena.alloc(c.remove_spaces(arena)), - ), - OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue( - a.remove_spaces(arena), - &[], - &[], - arena.alloc(c.remove_spaces(arena)), - ), - OldRecordBuilderField::LabelOnly(a) => { - OldRecordBuilderField::LabelOnly(a.remove_spaces(arena)) - } - OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a), - OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena), - OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t), - StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)), - StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)), - } - } -} - -impl<'a> RemoveSpaces<'a> for StrSegment<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - StrSegment::Plaintext(t) => StrSegment::Plaintext(t), - StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)), - StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c), - StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)), - StrSegment::DeprecatedInterpolated(t) => { - StrSegment::DeprecatedInterpolated(t.remove_spaces(arena)) - } - } - } -} - -impl<'a> RemoveSpaces<'a> for Expr<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Expr::Float(a) => Expr::Float(a), - Expr::Num(a) => Expr::Num(a), - Expr::NonBase10Int { - string, - base, - is_negative, - } => Expr::NonBase10Int { - string, - base, - is_negative, - }, - Expr::Str(a) => Expr::Str(a.remove_spaces(arena)), - Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b), - Expr::AccessorFunction(a) => Expr::AccessorFunction(a), - Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b), - Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))), - Expr::List(a) => Expr::List(a.remove_spaces(arena)), - Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { - update: arena.alloc(update.remove_spaces(arena)), - fields: fields.remove_spaces(arena), - }, - Expr::Record(a) => Expr::Record(a.remove_spaces(arena)), - Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)), - Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder { - mapper: arena.alloc(mapper.remove_spaces(arena)), - fields: fields.remove_spaces(arena), - }, - Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)), - Expr::Var { module_name, ident } => Expr::Var { module_name, ident }, - Expr::Underscore(a) => Expr::Underscore(a), - Expr::Tag(a) => Expr::Tag(a), - Expr::OpaqueRef(a) => Expr::OpaqueRef(a), - Expr::Closure(a, b) => Expr::Closure( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::Crash => Expr::Crash, - Expr::Defs(a, b) => { - let mut defs = a.clone(); - defs.space_before = vec![Default::default(); defs.len()]; - defs.space_after = vec![Default::default(); defs.len()]; - defs.regions = vec![Region::zero(); defs.len()]; - defs.spaces.clear(); - - for type_def in defs.type_defs.iter_mut() { - *type_def = type_def.remove_spaces(arena); - } - - for value_def in defs.value_defs.iter_mut() { - *value_def = value_def.remove_spaces(arena); - } - - Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena))) - } - Expr::Backpassing(a, b, c) => Expr::Backpassing( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - arena.alloc(c.remove_spaces(arena)), - ), - Expr::Expect(a, b) => Expr::Expect( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::Dbg(a, b) => Expr::Dbg( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Expr::LowLevelDbg(_, _, _) => unreachable!( - "LowLevelDbg should only exist after desugaring, not during formatting" - ), - Expr::Apply(a, b, c) => Expr::Apply( - arena.alloc(a.remove_spaces(arena)), - b.remove_spaces(arena), - c, - ), - Expr::BinOps(a, b) => { - Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))) - } - Expr::UnaryOp(a, b) => { - Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) - } - Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))), - Expr::When(a, b) => { - Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) - } - Expr::ParensAround(a) => { - // The formatter can remove redundant parentheses, so also remove these when normalizing for comparison. - a.remove_spaces(arena) - } - Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), - Expr::MalformedClosure => Expr::MalformedClosure, - Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), - Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), - Expr::MultipleOldRecordBuilders(a) => Expr::MultipleOldRecordBuilders(a), - Expr::UnappliedOldRecordBuilder(a) => Expr::UnappliedOldRecordBuilder(a), - Expr::EmptyRecordBuilder(a) => Expr::EmptyRecordBuilder(a), - Expr::SingleFieldRecordBuilder(a) => Expr::SingleFieldRecordBuilder(a), - Expr::OptionalFieldInRecordBuilder(name, a) => { - Expr::OptionalFieldInRecordBuilder(name, a) - } - Expr::SpaceBefore(a, _) => a.remove_spaces(arena), - Expr::SpaceAfter(a, _) => a.remove_spaces(arena), - Expr::SingleQuote(a) => Expr::Num(a), - } - } -} - -fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent { - match ident { - BadIdent::Start(_) => BadIdent::Start(Position::zero()), - BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()), - BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()), - BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()), - BadIdent::UnderscoreAtStart { - position: _, - declaration_region, - } => BadIdent::UnderscoreAtStart { - position: Position::zero(), - declaration_region, - }, - BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()), - BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()), - BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()), - BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()), - BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()), - BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()), - BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()), - } -} - -impl<'a> RemoveSpaces<'a> for Pattern<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Pattern::Identifier { ident } => Pattern::Identifier { ident }, - Pattern::Tag(a) => Pattern::Tag(a), - Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), - Pattern::Apply(a, b) => Pattern::Apply( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)), - Pattern::RequiredField(a, b) => { - Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena))) - } - Pattern::OptionalField(a, b) => { - Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena))) - } - Pattern::As(pattern, pattern_as) => Pattern::As( - arena.alloc(pattern.remove_spaces(arena)), - pattern_as.remove_spaces(arena), - ), - Pattern::NumLiteral(a) => Pattern::NumLiteral(a), - Pattern::NonBase10Literal { - string, - base, - is_negative, - } => Pattern::NonBase10Literal { - string, - base, - is_negative, - }, - Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a), - Pattern::StrLiteral(a) => Pattern::StrLiteral(a), - Pattern::Underscore(a) => Pattern::Underscore(a), - Pattern::Malformed(a) => Pattern::Malformed(a), - Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)), - Pattern::QualifiedIdentifier { module_name, ident } => { - Pattern::QualifiedIdentifier { module_name, ident } - } - Pattern::SpaceBefore(a, _) => a.remove_spaces(arena), - Pattern::SpaceAfter(a, _) => a.remove_spaces(arena), - Pattern::SingleQuote(a) => Pattern::SingleQuote(a), - Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)), - Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)), - Pattern::ListRest(opt_pattern_as) => Pattern::ListRest( - opt_pattern_as - .map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))), - ), - } - } -} - -impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - TypeAnnotation::Function(a, b) => TypeAnnotation::Function( - arena.alloc(a.remove_spaces(arena)), - arena.alloc(b.remove_spaces(arena)), - ), - TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)), - TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a), - TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As( - arena.alloc(a.remove_spaces(arena)), - &[], - TypeHeader { - name: name.remove_spaces(arena), - vars: vars.remove_spaces(arena), - }, - ), - TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple { - elems: fields.remove_spaces(arena), - ext: ext.remove_spaces(arena), - }, - TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record { - fields: fields.remove_spaces(arena), - ext: ext.remove_spaces(arena), - }, - TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion { - ext: ext.remove_spaces(arena), - tags: tags.remove_spaces(arena), - }, - TypeAnnotation::Inferred => TypeAnnotation::Inferred, - TypeAnnotation::Wildcard => TypeAnnotation::Wildcard, - TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where( - arena.alloc(annot.remove_spaces(arena)), - arena.alloc(has_clauses.remove_spaces(arena)), - ), - TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena), - TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena), - TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a), - } - } -} - -impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - ImplementsClause { - var: self.var.remove_spaces(arena), - abilities: self.abilities.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for Tag<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - Tag::Apply { name, args } => Tag::Apply { - name: name.remove_spaces(arena), - args: args.remove_spaces(arena), - }, - Tag::Malformed(a) => Tag::Malformed(a), - Tag::SpaceBefore(a, _) => a.remove_spaces(arena), - Tag::SpaceAfter(a, _) => a.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - AbilityImpls::AbilityImpls(impls) => { - AbilityImpls::AbilityImpls(impls.remove_spaces(arena)) - } - AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => { - has.remove_spaces(arena) - } - } - } -} - -impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - ImplementsAbility::ImplementsAbility { ability, impls } => { - ImplementsAbility::ImplementsAbility { - ability: ability.remove_spaces(arena), - impls: impls.remove_spaces(arena), - } - } - ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => { - has.remove_spaces(arena) - } - } - } -} - -impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - match *self { - ImplementsAbilities::Implements(derived) => { - ImplementsAbilities::Implements(derived.remove_spaces(arena)) - } - ImplementsAbilities::SpaceBefore(derived, _) - | ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena), - } - } -} - -impl<'a> RemoveSpaces<'a> for PatternAs<'a> { - fn remove_spaces(&self, arena: &'a Bump) -> Self { - PatternAs { - spaces_before: &[], - identifier: self.identifier.remove_spaces(arena), - } - } -} diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index 4eb61cef50..c50f453235 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -4320,16 +4320,26 @@ mod test_reporting { { x, y } " ), - @r" - ── TOO MANY ARGS in /code/proj/Main.roc ──────────────────────────────────────── + @r###" + ── STATEMENT AFTER EXPRESSION in tmp/double_equals_in_def/Test.roc ───────────── - This value is not a function, but it was given 3 arguments: + I just finished parsing an expression with a series of definitions, + and this line is indented as if it's intended to be part of that + expression: + + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ x = 3 + 5│ y = 6│ x == 5 - ^ + 7│ Num.add 1 2 + ^ - Are there any missing commas? Or missing parentheses? - " + However, I already saw the final expression in that series of + definitions. + "### ); test_report!( @@ -5018,7 +5028,7 @@ mod test_reporting { I was partway through parsing an `import`, but I got stuck here: 4│ import svg.Path a - ^ + ^ I was expecting to see the `as` keyword, like: @@ -5417,14 +5427,25 @@ mod test_reporting { 2 -> 2 " ), - @r" - ── NOT END OF FILE in tmp/when_outdented_branch/Test.roc ─────────────────────── + @r###" + ── UNKNOWN OPERATOR in tmp/when_outdented_branch/Test.roc ────────────────────── - I expected to reach the end of the file, but got stuck here: + This looks like an operator, but it's not one I recognize! + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ when 4 is + 5│ 5 -> 2 6│ 2 -> 2 - ^ - " + ^^ + + Looks like you are trying to define a function. + + In roc, functions are always written as a lambda, like + + increment = \n -> n + 1 + "### ); test_report!( @@ -5436,12 +5457,13 @@ mod test_reporting { _ -> 2 " ), - @r" + @r###" ── UNEXPECTED ARROW in tmp/when_over_indented_underscore/Test.roc ────────────── I am parsing a `when` expression right now, but this arrow is confusing me: + 4│ when 4 is 5│ 5 -> 2 6│ _ -> 2 ^^ @@ -5461,7 +5483,7 @@ mod test_reporting { Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! - " + "### ); test_report!( @@ -5473,12 +5495,13 @@ mod test_reporting { 2 -> 2 " ), - @r" + @r###" ── UNEXPECTED ARROW in tmp/when_over_indented_int/Test.roc ───────────────────── I am parsing a `when` expression right now, but this arrow is confusing me: + 4│ when 4 is 5│ 5 -> Num.neg 6│ 2 -> 2 ^^ @@ -5498,7 +5521,7 @@ mod test_reporting { Notice the indentation. All patterns are aligned, and each branch is indented a bit more than the corresponding pattern. That is important! - " + "### ); // TODO I think we can do better here @@ -6136,27 +6159,21 @@ All branches in an `if` must have the same type! main = 5 -> 3 " ), - |golden| pretty_assertions::assert_eq!( - golden, - &format!( - r#"── UNKNOWN OPERATOR in tmp/wild_case_arrow/Test.roc ──────────────────────────── + @r###" + ── SYNTAX PROBLEM in tmp/wild_case_arrow/Test.roc ────────────────────────────── -This looks like an operator, but it's not one I recognize! + I got stuck here: -1│ app "test" provides [main] to "./platform" -2│ -3│ main = -4│ main = 5 -> 3 - ^^ + 1│ app "test" provides [main] to "./platform" + 2│ + 3│ main = + 4│ main = 5 -> 3 + ^ -Looks like you are trying to define a function.{} - -In roc, functions are always written as a lambda, like{} - - increment = \n -> n + 1"#, - ' ', ' ' - ) - ) + Whatever I am running into is confusing me a lot! Normally I can give + fairly specific hints, but something is really tripping me up this + time. + "### ); #[test] @@ -10971,13 +10988,13 @@ In roc, functions are always written as a lambda, like{} 0 " ), - @r" + @r###" ── UNNECESSARY DEFINITION in /code/proj/Main.roc ─────────────────────────────── This destructure assignment doesn't introduce any new variables: 4│ Pair _ _ = Pair 0 1 - ^^^^ + ^^^^^^^^ If you don't need to use the value on the right-hand-side of this assignment, consider removing the assignment. Since Roc is purely @@ -11019,7 +11036,7 @@ In roc, functions are always written as a lambda, like{} assignment, consider removing the assignment. Since Roc is purely functional, assignments that don't introduce variables cannot affect a program's behavior! - " + "### ); test_report!( diff --git a/crates/compiler/load_internal/tests/test_load.rs b/crates/compiler/load_internal/tests/test_load.rs index de6014feba..ddb664568a 100644 --- a/crates/compiler/load_internal/tests/test_load.rs +++ b/crates/compiler/load_internal/tests/test_load.rs @@ -833,7 +833,7 @@ fn platform_parse_error() { match multiple_modules("platform_parse_error", modules) { Err(report) => { - assert!(report.contains("NOT END OF FILE")); + assert!(report.contains("STATEMENT AFTER EXPRESSION")); assert!(report.contains("blah 1 2 3 # causing a parse error on purpose")); } Ok(_) => unreachable!("we expect failure here"), diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index 72fc2c9781..43780ba9e1 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -3,7 +3,7 @@ use self::BinOp::*; use std::cmp::Ordering; use std::fmt; -const PRECEDENCES: [(BinOp, u8); 20] = [ +const PRECEDENCES: [(BinOp, u8); 16] = [ (Caret, 8), (Star, 7), (Slash, 7), @@ -20,14 +20,9 @@ const PRECEDENCES: [(BinOp, u8); 20] = [ (GreaterThanOrEq, 2), (And, 1), (Or, 0), - // These should never come up - (Assignment, 255), - (IsAliasType, 255), - (IsOpaqueType, 255), - (Backpassing, 255), ]; -const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [ +const ASSOCIATIVITIES: [(BinOp, Associativity); 16] = [ (Caret, RightAssociative), (Star, LeftAssociative), (Slash, LeftAssociative), @@ -44,14 +39,9 @@ const ASSOCIATIVITIES: [(BinOp, Associativity); 20] = [ (GreaterThanOrEq, NonAssociative), (And, RightAssociative), (Or, RightAssociative), - // These should never come up - (Assignment, LeftAssociative), - (IsAliasType, LeftAssociative), - (IsOpaqueType, LeftAssociative), - (Backpassing, LeftAssociative), ]; -const DISPLAY_STRINGS: [(BinOp, &str); 20] = [ +const DISPLAY_STRINGS: [(BinOp, &str); 16] = [ (Caret, "^"), (Star, "*"), (Slash, "/"), @@ -68,10 +58,6 @@ const DISPLAY_STRINGS: [(BinOp, &str); 20] = [ (GreaterThanOrEq, ">="), (And, "&&"), (Or, "||"), - (Assignment, "="), - (IsAliasType, ":"), - (IsOpaqueType, ":="), - (Backpassing, "<-"), ]; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -147,10 +133,6 @@ pub enum BinOp { GreaterThanOrEq, And, Or, - Assignment, - IsAliasType, - IsOpaqueType, - Backpassing, // lowest precedence } @@ -161,7 +143,6 @@ impl BinOp { Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1, DoubleSlash | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq | And | Or | Pizza => 2, - Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(), } } } @@ -195,25 +176,13 @@ pub enum Associativity { impl BinOp { pub fn associativity(self) -> Associativity { - // The compiler should never pass any of these to this function! - debug_assert_ne!(self, Assignment); - debug_assert_ne!(self, IsAliasType); - debug_assert_ne!(self, IsOpaqueType); - debug_assert_ne!(self, Backpassing); - - const ASSOCIATIVITY_TABLE: [Associativity; 20] = generate_associativity_table(); + const ASSOCIATIVITY_TABLE: [Associativity; 16] = generate_associativity_table(); ASSOCIATIVITY_TABLE[self as usize] } fn precedence(self) -> u8 { - // The compiler should never pass any of these to this function! - debug_assert_ne!(self, Assignment); - debug_assert_ne!(self, IsAliasType); - debug_assert_ne!(self, IsOpaqueType); - debug_assert_ne!(self, Backpassing); - - const PRECEDENCE_TABLE: [u8; 20] = generate_precedence_table(); + const PRECEDENCE_TABLE: [u8; 16] = generate_precedence_table(); PRECEDENCE_TABLE[self as usize] } @@ -233,19 +202,14 @@ impl Ord for BinOp { impl std::fmt::Display for BinOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - debug_assert_ne!(*self, Assignment); - debug_assert_ne!(*self, IsAliasType); - debug_assert_ne!(*self, IsOpaqueType); - debug_assert_ne!(*self, Backpassing); - - const DISPLAY_TABLE: [&str; 20] = generate_display_table(); + const DISPLAY_TABLE: [&str; 16] = generate_display_table(); write!(f, "{}", DISPLAY_TABLE[*self as usize]) } } -const fn generate_precedence_table() -> [u8; 20] { - let mut table = [0u8; 20]; +const fn generate_precedence_table() -> [u8; 16] { + let mut table = [0u8; 16]; let mut i = 0; while i < PRECEDENCES.len() { @@ -256,8 +220,8 @@ const fn generate_precedence_table() -> [u8; 20] { table } -const fn generate_associativity_table() -> [Associativity; 20] { - let mut table = [NonAssociative; 20]; +const fn generate_associativity_table() -> [Associativity; 16] { + let mut table = [NonAssociative; 16]; let mut i = 0; while i < ASSOCIATIVITIES.len() { @@ -268,8 +232,8 @@ const fn generate_associativity_table() -> [Associativity; 20] { table } -const fn generate_display_table() -> [&'static str; 20] { - let mut table = [""; 20]; +const fn generate_display_table() -> [&'static str; 16] { + let mut table = [""; 16]; let mut i = 0; while i < DISPLAY_STRINGS.len() { diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 81b3e408bf..4ff90c5a52 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -21,6 +21,12 @@ pub struct Spaces<'a, T> { pub after: &'a [CommentOrNewline<'a>], } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct SpacesBefore<'a, T> { + pub before: &'a [CommentOrNewline<'a>], + pub item: T, +} + #[derive(Copy, Clone, PartialEq)] pub enum Spaced<'a, T> { Item(T), @@ -1204,6 +1210,21 @@ impl<'a> Defs<'a> { }) } + pub fn loc_defs<'b>( + &'b self, + ) -> impl Iterator>, Loc>>> + 'b { + self.tags + .iter() + .enumerate() + .map(|(i, tag)| match tag.split() { + Ok(type_index) => Ok(Loc::at(self.regions[i], self.type_defs[type_index.index()])), + Err(value_index) => Err(Loc::at( + self.regions[i], + self.value_defs[value_index.index()], + )), + }) + } + pub fn list_value_defs(&self) -> impl Iterator)> { self.tags .iter() @@ -2072,6 +2093,28 @@ pub trait Spaceable<'a> { fn before(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self; fn after(&'a self, _: &'a [CommentOrNewline<'a>]) -> Self; + fn maybe_before(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self + where + Self: Sized + 'a, + { + if spaces.is_empty() { + self + } else { + arena.alloc(self).before(spaces) + } + } + + fn maybe_after(self, arena: &'a Bump, spaces: &'a [CommentOrNewline<'a>]) -> Self + where + Self: Sized + 'a, + { + if spaces.is_empty() { + self + } else { + arena.alloc(self).after(spaces) + } + } + fn with_spaces_before(&'a self, spaces: &'a [CommentOrNewline<'a>], region: Region) -> Loc where Self: Sized, diff --git a/crates/compiler/parse/src/blankspace.rs b/crates/compiler/parse/src/blankspace.rs index 9b57207619..2d3f36eda6 100644 --- a/crates/compiler/parse/src/blankspace.rs +++ b/crates/compiler/parse/src/blankspace.rs @@ -333,7 +333,7 @@ where let start = state.pos(); match spaces().parse(arena, state, min_indent) { Ok((progress, spaces, state)) => { - if progress == NoProgress || state.column() >= min_indent { + if spaces.is_empty() || state.column() >= min_indent { Ok((progress, spaces, state)) } else { Err((progress, indent_problem(start))) @@ -344,6 +344,60 @@ where } } +pub fn require_newline_or_eof<'a, E>(newline_problem: fn(Position) -> E) -> impl Parser<'a, (), E> +where + E: 'a + SpaceProblem, +{ + move |arena: &'a Bump, state: State<'a>, min_indent| { + // TODO: we can do this more efficiently by stopping as soon as we see a '#' or a newline + let (_, res, _) = space0_e(newline_problem).parse(arena, state.clone(), min_indent)?; + + if !res.is_empty() || state.has_reached_end() { + Ok((NoProgress, (), state)) + } else { + Err((NoProgress, newline_problem(state.pos()))) + } + } +} + +pub fn loc_space0_e<'a, E>( + indent_problem: fn(Position) -> E, +) -> impl Parser<'a, Loc<&'a [CommentOrNewline<'a>]>, E> +where + E: 'a + SpaceProblem, +{ + move |arena, state: State<'a>, min_indent: u32| { + let mut newlines = Vec::new_in(arena); + let start = state.pos(); + let mut comment_start = None; + let mut comment_end = None; + + let res = consume_spaces(state, |start, space, end| { + newlines.push(space); + if !matches!(space, CommentOrNewline::Newline) { + if comment_start.is_none() { + comment_start = Some(start); + } + comment_end = Some(end); + } + }); + + match res { + Ok((progress, state)) => { + if newlines.is_empty() || state.column() >= min_indent { + let start = comment_start.unwrap_or(state.pos()); + let end = comment_end.unwrap_or(state.pos()); + let region = Region::new(start, end); + Ok((progress, Loc::at(region, newlines.into_bump_slice()), state)) + } else { + Err((progress, indent_problem(start))) + } + } + Err((progress, err)) => Err((progress, err)), + } + } +} + fn begins_with_crlf(bytes: &[u8]) -> bool { bytes.len() >= 2 && bytes[0] == b'\r' && bytes[1] == b'\n' } @@ -387,7 +441,7 @@ where F: FnMut(Position, CommentOrNewline<'a>, Position), { let mut progress = NoProgress; - let mut found_newline = false; + let mut found_newline = state.is_at_start_of_file(); loop { let whitespace = fast_eat_whitespace(state.bytes()); if whitespace > 0 { diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 072bae3136..eaa5a1eacc 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,12 +1,12 @@ use crate::ast::{ - is_expr_suffixed, is_top_level_suffixed, AssignedField, Collection, CommentOrNewline, Defs, - Expr, ExtractSpaces, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, - ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, - ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, + is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, + Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword, + ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, + ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore, TypeAnnotation, TypeDef, TypeHeader, ValueDef, }; use crate::blankspace::{ - space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e, + loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before, }; use crate::ident::{ @@ -15,13 +15,13 @@ use crate::ident::{ use crate::module::module_name_help; use crate::parser::{ self, and, backtrackable, between, byte, byte_indent, collection_inner, - collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, - line_min_indent, loc, map, map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, - set_min_indent, skip_first, skip_second, specialize_err, specialize_err_ref, then, two_bytes, - zero_or_more, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, - EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser, + collection_trailing_sep_e, either, increment_min_indent, indented_seq_skip_first, loc, map, + map_with_arena, optional, reset_min_indent, sep_by1, sep_by1_e, set_min_indent, skip_first, + skip_second, specialize_err, specialize_err_ref, then, two_bytes, zero_or_more, EClosure, + EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber, EPattern, ERecord, + EString, EType, EWhen, Either, ParseResult, Parser, SpaceProblem, }; -use crate::pattern::{closure_param, loc_implements_parser}; +use crate::pattern::closure_param; use crate::state::State; use crate::string_literal::{self, StrLikeLiteral}; use crate::{header, keyword}; @@ -51,7 +51,7 @@ pub fn test_parse_expr<'a>( state: State<'a>, ) -> Result>, EExpr<'a>> { let parser = skip_second( - space0_before_optional_after(loc_expr(true), EExpr::IndentStart, EExpr::IndentEnd), + space0_before_optional_after(loc_expr_block(true), EExpr::IndentStart, EExpr::IndentEnd), expr_end(), ); @@ -88,7 +88,11 @@ fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc>, EInParens<'a> then( loc(collection_trailing_sep_e( byte(b'(', EInParens::Open), - specialize_err_ref(EInParens::Expr, loc_expr(false)), + specialize_err_ref( + EInParens::Expr, + // space0_before_e( + loc_expr_block(false), + ), byte(b',', EInParens::End), byte(b')', EInParens::End), Expr::SpaceBefore, @@ -177,7 +181,7 @@ fn loc_term_or_underscore_or_conditional<'a>( one_of!( loc_expr_in_parens_etc_help(), loc(specialize_err(EExpr::If, if_expr_help(options))), - loc(specialize_err(EExpr::When, when::expr_help(options))), + loc(specialize_err(EExpr::When, when::when_expr_help(options))), loc(specialize_err(EExpr::Str, string_like_literal_help())), loc(specialize_err( EExpr::Number, @@ -190,6 +194,7 @@ fn loc_term_or_underscore_or_conditional<'a>( loc(specialize_err(EExpr::List, list_literal_help())), ident_seq(), ) + .trace("term_or_underscore_or_conditional") } /// In some contexts we want to parse the `_` as an expression, so it can then be turned into a @@ -210,6 +215,7 @@ fn loc_term_or_underscore<'a>( loc(specialize_err(EExpr::List, list_literal_help())), ident_seq(), ) + .trace("term_or_underscore") } fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, EExpr<'a>> { @@ -336,10 +342,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> { fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, EExpr<'a>> { one_of![ loc(specialize_err(EExpr::If, if_expr_help(options))), - loc(specialize_err(EExpr::When, when::expr_help(options))), - loc(specialize_err(EExpr::Expect, expect_help(options))), - loc(specialize_err(EExpr::Dbg, dbg_help(options))), - loc(import_help(options)), + loc(specialize_err(EExpr::When, when::when_expr_help(options))), loc(specialize_err(EExpr::Closure, closure_help(options))), loc(expr_operator_chain(options)), fail_expr_start_e() @@ -348,59 +351,328 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, E } fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { - line_min_indent(move |arena, state: State<'a>, min_indent: u32| { - let end = state.pos(); + (move |arena, state: State<'a>, min_indent: u32| { + parse_expr_operator_chain(arena, state, min_indent, options) + }) + .trace("expr_operator_chain") +} - let (_, expr, state) = - loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?; +fn parse_expr_operator_chain<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + options: ExprParseOptions, +) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> { + let line_indent = state.line_indent(); - let initial_state = state.clone(); + let (_, expr, state) = + loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?; - let new_min_indent = if is_expr_suffixed(&expr.value) { - min_indent + 1 - } else { - min_indent + let mut initial_state = state.clone(); + + let (spaces_before_op, state) = + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => return Ok((MadeProgress, expr.value, state)), + Ok((_, spaces_before_op, state)) => (spaces_before_op, state), }; - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => Ok((MadeProgress, expr.value, state)), - Ok((_, spaces_before_op, state)) => { - let expr_state = ExprState { - operators: Vec::new_in(arena), - arguments: Vec::new_in(arena), - expr, - spaces_after: spaces_before_op, - end: initial_state.pos(), - }; + let mut expr_state = ExprState { + operators: Vec::new_in(arena), + arguments: Vec::new_in(arena), + expr, + spaces_after: spaces_before_op, + end: initial_state.pos(), + }; - match parse_expr_end( - new_min_indent, - options, - expr_state, + let mut state = state; + + let call_min_indent = line_indent + 1; + + loop { + let parser = skip_first( + crate::blankspace::check_indent(EExpr::IndentEnd), + loc_term_or_underscore(options), + ); + match parser.parse(arena, state.clone(), call_min_indent) { + Err((MadeProgress, f)) => return Err((MadeProgress, f)), + Err((NoProgress, _)) => { + let before_op = state.clone(); + // try an operator + return parse_expr_after_apply( arena, state, + min_indent, + call_min_indent, + options, + true, + expr_state, + before_op, initial_state, + ); + } + Ok((_, arg, new_state)) => { + state = new_state; + initial_state = state.clone(); + + if parse_after_expr_arg_and_check_final( + arena, + &mut state, + min_indent, + &mut expr_state, + arg, ) { - Err(err) => Err(err), - Ok((progress, expr, new_state)) => { - // We need to check if we have just parsed a suffixed statement, - // if so, this is a defs node. - if is_top_level_suffixed(&expr) { - let def_region = Region::new(end, new_state.pos()); - let value_def = ValueDef::Stmt(arena.alloc(Loc::at(def_region, expr))); - - let mut defs = Defs::default(); - defs.push_value_def(value_def, def_region, &[], &[]); - - return parse_defs_expr(options, min_indent, defs, arena, new_state); - } else { - Ok((progress, expr, new_state)) - } - } + let expr = parse_expr_final(expr_state, arena); + return Ok((MadeProgress, expr, state)); } + + continue; } } + } +} + +#[allow(clippy::too_many_arguments)] +fn parse_expr_after_apply<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + check_for_defs: bool, + mut expr_state: ExprState<'a>, + before_op: State<'a>, + initial_state: State<'a>, +) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> { + match loc(bin_op(check_for_defs)).parse(arena, state.clone(), min_indent) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), + Ok((_, loc_op, state)) => { + expr_state.consume_spaces(arena); + let initial_state = before_op; + parse_expr_operator( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + loc_op, + initial_state, + ) + } + Err((NoProgress, _)) => { + let expr = parse_expr_final(expr_state, arena); + // roll back space parsing + Ok((MadeProgress, expr, initial_state)) + } + } +} + +fn expr_to_stmt(expr: Loc>) -> Loc> { + Loc::at(expr.region, Stmt::Expr(expr.value)) +} + +pub fn parse_repl_defs_and_optional_expr<'a>( + arena: &'a Bump, + state: State<'a>, +) -> ParseResult<'a, (Defs<'a>, Option>>), EExpr<'a>> { + let initial_state = state.clone(); + let (spaces_before, state) = match loc(space0_e(EExpr::IndentEnd)).parse(arena, state, 0) { + Err((NoProgress, _)) => return Ok((NoProgress, (Defs::default(), None), initial_state)), + Err((MadeProgress, e)) => return Err((MadeProgress, e)), + Ok((_, sp, state)) => (sp, state), + }; + + let (_, stmts, state) = parse_stmt_seq( + arena, + state, + |e, _| e.clone(), + ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }, + 0, + spaces_before, + EExpr::IndentEnd, + )?; + + let state = match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), 0) { + Err((NoProgress, _)) => state, + Err((MadeProgress, e)) => return Err((MadeProgress, e)), + Ok((_, _, state)) => state, + }; + + if !state.has_reached_end() { + return Err((MadeProgress, EExpr::End(state.pos()))); + } + + let (defs, last_expr) = + stmts_to_defs(&stmts, Defs::default(), false, arena).map_err(|e| (MadeProgress, e))?; + + Ok((MadeProgress, (defs, last_expr), state)) +} + +fn stmt_start<'a>( + options: ExprParseOptions, + preceding_comment: Region, +) -> impl Parser<'a, Loc>, EExpr<'a>> { + one_of![ + map( + loc(specialize_err(EExpr::If, if_expr_help(options))), + expr_to_stmt + ), + map( + loc(specialize_err(EExpr::When, when::when_expr_help(options))), + expr_to_stmt + ), + loc(specialize_err( + EExpr::Expect, + expect_help(options, preceding_comment) + )), + loc(specialize_err( + EExpr::Dbg, + dbg_help(options, preceding_comment) + )), + loc(specialize_err(EExpr::Import, map(import(), Stmt::ValueDef))), + map( + loc(specialize_err(EExpr::Closure, closure_help(options))), + expr_to_stmt + ), + loc(stmt_operator_chain(options)), + fail_expr_start_e() + ] + .trace("stmt_start") +} + +fn stmt_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Stmt<'a>, EExpr<'a>> { + (move |arena, state: State<'a>, min_indent: u32| { + parse_stmt_operator_chain(arena, state, min_indent, options) }) + .trace("stmt_operator_chain") +} + +fn parse_stmt_operator_chain<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + options: ExprParseOptions, +) -> Result<(Progress, Stmt<'a>, State<'a>), (Progress, EExpr<'a>)> { + let line_indent = state.line_indent(); + + let (_, expr, state) = + loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?; + + let mut initial_state = state.clone(); + let end = state.pos(); + + let (spaces_before_op, state) = + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => return Ok((MadeProgress, Stmt::Expr(expr.value), state)), + Ok((_, spaces_before_op, state)) => (spaces_before_op, state), + }; + + let mut expr_state = ExprState { + operators: Vec::new_in(arena), + arguments: Vec::new_in(arena), + expr, + spaces_after: spaces_before_op, + end, + }; + + let mut state = state; + + let call_min_indent = line_indent + 1; + + loop { + let parser = skip_first( + crate::blankspace::check_indent(EExpr::IndentEnd), + loc_term_or_underscore(options), + ); + match parser.parse(arena, state.clone(), call_min_indent) { + Err((MadeProgress, f)) => return Err((MadeProgress, f)), + Ok(( + _, + implements @ Loc { + value: + Expr::Var { + module_name: "", + ident: crate::keyword::IMPLEMENTS, + .. + }, + .. + }, + state, + )) if matches!(expr_state.expr.value, Expr::Tag(..)) => { + return parse_ability_def(expr_state, state, arena, implements, call_min_indent) + .map(|(td, s)| (MadeProgress, Stmt::TypeDef(td), s)); + } + Err((NoProgress, _)) => { + // try an operator + return parse_stmt_after_apply( + arena, + state, + min_indent, + call_min_indent, + expr_state, + options, + initial_state, + ); + } + Ok((_, arg, new_state)) => { + state = new_state; + initial_state = state.clone(); + + if parse_after_expr_arg_and_check_final( + arena, + &mut state, + min_indent, + &mut expr_state, + arg, + ) { + let expr = parse_expr_final(expr_state, arena); + return Ok((MadeProgress, Stmt::Expr(expr), state)); + } + + continue; + } + } + } +} + +fn parse_after_expr_arg_and_check_final<'a>( + arena: &'a Bump, + state: &mut State<'a>, + min_indent: u32, + expr_state: &mut ExprState<'a>, + mut arg: Loc>, +) -> bool { + let new_end = state.pos(); + + if !expr_state.spaces_after.is_empty() { + arg = arena + .alloc(arg.value) + .with_spaces_before(expr_state.spaces_after, arg.region); + + expr_state.spaces_after = &[]; + } + let new_spaces = match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = &[]; + + return true; + } + Ok((_, new_spaces, new_state)) => { + *state = new_state; + new_spaces + } + }; + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = new_spaces; + + false } #[derive(Debug)] @@ -437,7 +709,7 @@ impl<'a> ExprState<'a> { fn validate_assignment_or_backpassing( mut self, arena: &'a Bump, - loc_op: Loc, + loc_op: Loc, argument_error: F, ) -> Result>, EExpr<'a>> where @@ -446,8 +718,8 @@ impl<'a> ExprState<'a> { if !self.operators.is_empty() { // this `=` or `<-` likely occurred inline; treat it as an invalid operator let opchar = match loc_op.value { - BinOp::Assignment => "=", - BinOp::Backpassing => "<-", + OperatorOrDef::Assignment => "=", + OperatorOrDef::Backpassing => "<-", _ => unreachable!(), }; @@ -471,24 +743,15 @@ impl<'a> ExprState<'a> { fn validate_is_type_def( mut self, arena: &'a Bump, - loc_op: Loc, - kind: AliasOrOpaque, + kind: Loc, ) -> Result<(Loc>, Vec<'a, &'a Loc>>), EExpr<'a>> { - debug_assert_eq!( - loc_op.value, - match kind { - AliasOrOpaque::Alias => BinOp::IsAliasType, - AliasOrOpaque::Opaque => BinOp::IsOpaqueType, - } - ); - if !self.operators.is_empty() { // this `:`/`:=` likely occurred inline; treat it as an invalid operator - let op = match kind { + let op = match kind.value { AliasOrOpaque::Alias => ":", AliasOrOpaque::Opaque => ":=", }; - let fail = EExpr::BadOperator(op, loc_op.region.start()); + let fail = EExpr::BadOperator(op, kind.region.start()); Err(fail) } else { @@ -607,376 +870,6 @@ fn numeric_negate_expression<'a, T>( } } -pub fn parse_single_def<'a>( - options: ExprParseOptions, - min_indent: u32, - arena: &'a Bump, - state: State<'a>, -) -> ParseResult<'a, Option>, EExpr<'a>> { - let initial = state.clone(); - - let mut spaces_before_current = &[] as &[_]; - let spaces_before_current_start = state.pos(); - - let state = match space0_e(EExpr::IndentStart).parse(arena, state, min_indent) { - Err((MadeProgress, bad_input @ EExpr::Space(_, _))) => { - return Err((MadeProgress, bad_input)); - } - Err((MadeProgress, _)) => { - return Err((MadeProgress, EExpr::DefMissingFinalExpr(initial.pos()))); - } - Ok((_, spaces, state)) => { - spaces_before_current = spaces; - state - } - Err((NoProgress, _)) => initial.clone(), - }; - - let start = state.pos(); - - let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect); - let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect); - let parse_expect = either(parse_expect_fx, parse_expect_vanilla); - - match space0_after_e(crate::pattern::loc_pattern_help(), EPattern::IndentEnd).parse( - arena, - state.clone(), - min_indent, - ) { - Err((NoProgress, _)) => { - let pos_before_import = state.pos(); - match import().parse(arena, state.clone(), min_indent) { - Err((NoProgress, _)) => { - match parse_expect.parse(arena, state.clone(), min_indent) { - Err((_, _)) => { - // a hacky way to get expression-based error messages. TODO fix this - Ok((NoProgress, None, initial)) - } - Ok((_, expect_flavor, state)) => parse_statement_inside_def( - arena, - state, - min_indent, - options, - start, - spaces_before_current_start, - spaces_before_current, - |preceding_comment, loc_def_expr| match expect_flavor { - Either::Second(_) => ValueDef::Expect { - condition: arena.alloc(loc_def_expr), - preceding_comment, - }, - Either::First(_) => ValueDef::ExpectFx { - condition: arena.alloc(loc_def_expr), - preceding_comment, - }, - }, - ), - } - } - Err((MadeProgress, err)) => { - Err((MadeProgress, EExpr::Import(err, pos_before_import))) - } - Ok((_, (loc_import, spaces_after), state)) => Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(loc_import.value), - region: loc_import.region, - spaces_before: spaces_before_current, - spaces_after, - }), - state, - )), - } - } - Err((MadeProgress, _)) => { - // Try to parse as a Statement - match parse_statement_inside_def( - arena, - initial.clone(), - min_indent, - options, - start, - spaces_before_current_start, - // TODO including spaces_before_current here doubles things up - &[], - |_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) }, - ) { - Ok((_, Some(single_def), state)) => match single_def.type_or_value { - Either::Second(ValueDef::Stmt(loc_expr)) - if is_expr_suffixed(&loc_expr.value) => - { - Ok((MadeProgress, Some(single_def), state)) - } - _ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this - }, - _ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this - } - } - Ok((_, loc_pattern, state)) => { - // First let's check whether this is an ability definition. - let opt_tag_and_args: Option<(&str, Region, &[Loc])> = match loc_pattern.value - { - Pattern::Apply( - Loc { - value: Pattern::Tag(name), - region, - }, - args, - ) => Some((name, *region, args)), - Pattern::Tag(name) => Some((name, loc_pattern.region, &[])), - _ => None, - }; - - if let Some((name, name_region, args)) = opt_tag_and_args { - if let Ok((_, loc_implements, state)) = - loc_implements_parser().parse(arena, state.clone(), min_indent) - { - let (_, (type_def, def_region), state) = finish_parsing_ability_def_help( - min_indent, - Loc::at(name_region, name), - args, - loc_implements, - arena, - state, - )?; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region: def_region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - } - - // This may be a def or alias. - let operator_result = operator().parse(arena, state.clone(), min_indent); - - if let Ok((_, BinOp::Assignment, operator_result_state)) = operator_result { - return parse_single_def_assignment( - options, - // to support statements we have to increase the indent here so that we can parse a child def - // within a def and still continue to parse the final expression for this def - // e.g. - // main = - // Stdout.line! "Bar" - // a=Stdout.line! "Foo" - // Task.ok {} - &operator_result_state.line_indent() + 1, - arena, - operator_result_state, - loc_pattern, - spaces_before_current, - ); - }; - - if let Ok((_, BinOp::IsAliasType, state)) = operator_result { - // the increment_min_indent here is probably _wrong_, since alias_signature_with_space_before does - // that internally. - // TODO: re-evaluate this - let parser = increment_min_indent(alias_signature_with_space_before()); - let (_, ann_type, state) = parser.parse(arena, state, min_indent)?; - let region = Region::span_across(&loc_pattern.region, &ann_type.region); - - match &loc_pattern.value.extract_spaces().item { - Pattern::Apply( - Loc { - value: Pattern::Tag(name), - .. - }, - alias_arguments, - ) => { - let name = Loc::at(loc_pattern.region, *name); - let header = TypeHeader { - name, - vars: alias_arguments, - }; - - let type_def = TypeDef::Alias { - header, - ann: ann_type, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - Pattern::Tag(name) => { - let name = Loc::at(loc_pattern.region, *name); - let pattern_arguments: &'a [Loc>] = &[]; - let header = TypeHeader { - name, - vars: pattern_arguments, - }; - - let type_def = TypeDef::Alias { - header, - ann: ann_type, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - _ => { - let value_def = ValueDef::Annotation(loc_pattern, ann_type); - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - } - }; - - if let Ok((_, BinOp::IsOpaqueType, state)) = operator_result { - let (_, (signature, derived), state) = - opaque_signature_with_space_before().parse(arena, state, min_indent + 1)?; - let region = Region::span_across(&loc_pattern.region, &signature.region); - - match &loc_pattern.value.extract_spaces().item { - Pattern::Apply( - Loc { - value: Pattern::Tag(name), - .. - }, - alias_arguments, - ) => { - let name = Loc::at(loc_pattern.region, *name); - let header = TypeHeader { - name, - vars: alias_arguments, - }; - - let type_def = TypeDef::Opaque { - header, - typ: signature, - derived, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - Pattern::Tag(name) => { - let name = Loc::at(loc_pattern.region, *name); - let pattern_arguments: &'a [Loc>] = &[]; - let header = TypeHeader { - name, - vars: pattern_arguments, - }; - - let type_def = TypeDef::Opaque { - header, - typ: signature, - derived, - }; - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::First(type_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - _ => { - let value_def = ValueDef::Annotation(loc_pattern, signature); - - return Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )); - } - } - }; - - // Otherwise try to re-parse as a Statement - match parse_statement_inside_def( - arena, - initial.clone(), - min_indent, - options, - start, - spaces_before_current_start, - // TODO figure out why including spaces_before_current here doubles things up - &[], - |_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) }, - ) { - Ok((_, Some(single_def), state)) => match single_def.type_or_value { - Either::Second(ValueDef::Stmt(loc_expr)) - if is_expr_suffixed(&loc_expr.value) => - { - Ok((MadeProgress, Some(single_def), state)) - } - _ => Ok((NoProgress, None, initial)), - }, - _ => Ok((NoProgress, None, initial)), - } - } - } -} - -fn import<'a>() -> impl Parser<'a, (Loc>, &'a [CommentOrNewline<'a>]), EImport<'a>> { - then( - and( - loc(skip_first( - parser::keyword(keyword::IMPORT, EImport::Import), - increment_min_indent(one_of!(import_body(), import_ingested_file_body())), - )), - space0_e(EImport::EndNewline), - ), - |_arena, state, progress, (import, spaces_after)| { - if !spaces_after.is_empty() || state.has_reached_end() { - Ok((progress, (import, spaces_after), state)) - } else { - // We require EOF, comment, or newline after import - Err((progress, EImport::EndNewline(state.pos()))) - } - }, - ) -} - fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> { map( record!(ModuleImport { @@ -1147,353 +1040,11 @@ fn import_ingested_file_annotation<'a>() -> impl Parser<'a, IngestedFileAnnotati }) } -pub fn parse_single_def_assignment<'a>( - options: ExprParseOptions, - min_indent: u32, - arena: &'a Bump, - initial_state: State<'a>, - def_loc_pattern: Loc>, - spaces_before_current: &'a [CommentOrNewline<'a>], -) -> ParseResult<'a, Option>, EExpr<'a>> { - // Try and parse the expression - let parse_def_expr = - space0_before_e(increment_min_indent(expr_start(options)), EExpr::IndentEnd); - let (progress_after_first, first_loc_expr, state_after_first_expression) = - parse_def_expr.parse(arena, initial_state, min_indent)?; - - let region = Region::span_across(&def_loc_pattern.region, &first_loc_expr.region); - - // If the expression is actually a suffixed statement, then we need to continue - // to parse the rest of the expression - if is_top_level_suffixed(&first_loc_expr.value) { - let mut defs = Defs::default(); - // Take the suffixed value and make it a e.g. Body(`{}=`, Apply(Var(...))) - // we will keep the pattern `def_loc_pattern` for the new Defs - defs.push_value_def( - ValueDef::Stmt(arena.alloc(first_loc_expr)), - region, - spaces_before_current, - &[], - ); - - // Try to parse the rest of the expression as multiple defs, which may contain sub-assignments - match parse_defs_expr( - options, - min_indent, - defs, - arena, - state_after_first_expression, - ) { - Ok((progress_after_rest_of_def, expr, state_after_rest_of_def)) => { - let final_loc_expr = arena.alloc(Loc::at(region, expr)); - - let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), final_loc_expr); - - Ok(( - progress_after_rest_of_def, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state_after_rest_of_def, - )) - } - Err((progress, err)) => Err((progress, err)), - } - } else { - let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr)); - - Ok(( - progress_after_first, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state_after_first_expression, - )) - } +fn alias_signature<'a>() -> impl Parser<'a, Loc>, EExpr<'a>> { + increment_min_indent(specialize_err(EExpr::Type, type_annotation::located(false))) } -/// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg` -#[allow(clippy::too_many_arguments)] -fn parse_statement_inside_def<'a>( - arena: &'a Bump, - state: State<'a>, - min_indent: u32, - options: ExprParseOptions, - start: Position, - spaces_before_current_start: Position, - spaces_before_current: &'a [CommentOrNewline<'a>], - get_value_def: impl Fn(Region, Loc>) -> ValueDef<'a>, -) -> Result<(Progress, Option>, State<'a>), (Progress, EExpr<'a>)> { - let parse_def_expr = - space0_before_e(increment_min_indent(expr_start(options)), EExpr::IndentEnd); - let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state, min_indent)?; - let end = loc_def_expr.region.end(); - let region = Region::new(start, end); - - // drop newlines before the preceding comment - let spaces_before_start = spaces_before_current_start.offset as usize; - let spaces_before_end = start.offset as usize; - let mut spaces_before_current_start = spaces_before_current_start; - - for byte in &state.original_bytes()[spaces_before_start..spaces_before_end] { - match byte { - b' ' | b'\n' => { - spaces_before_current_start.offset += 1; - } - _ => break, - } - } - - let preceding_comment = Region::new(spaces_before_current_start, start); - - let value_def = get_value_def(preceding_comment, loc_def_expr); - - Ok(( - MadeProgress, - Some(SingleDef { - type_or_value: Either::Second(value_def), - region, - spaces_before: spaces_before_current, - spaces_after: &[], - }), - state, - )) -} - -// This is a macro only because trying to make it be a function caused lifetime issues. -#[macro_export] -macro_rules! join_ann_to_body { - ($arena:expr, $loc_pattern:expr, $loc_def_expr:expr, $ann_pattern:expr, $ann_type:expr, $spaces_before_current:expr, $region:expr) => {{ - // join this body with the preceding annotation - - let value_def = ValueDef::AnnotatedBody { - ann_pattern: $arena.alloc(*$ann_pattern), - ann_type: $arena.alloc(*$ann_type), - comment: $spaces_before_current - .first() - .and_then($crate::ast::CommentOrNewline::comment_str), - body_pattern: $arena.alloc($loc_pattern), - body_expr: *$arena.alloc($loc_def_expr), - }; - - ( - value_def, - roc_region::all::Region::span_across(&$ann_pattern.region, &$region), - ) - }}; -} - -// This is a macro only because trying to make it be a function caused lifetime issues. -#[macro_export] -macro_rules! join_alias_to_body { - ($arena:expr, $loc_pattern:expr, $loc_def_expr:expr, $header:expr, $ann_type:expr, $spaces_before_current:expr, $region:expr) => {{ - use roc_region::all::Region; - - // This is a case like - // UserId x : [UserId Int] - // UserId x = UserId 42 - // We optimistically parsed the first line as an alias; we now turn it - // into an annotation. - - let loc_name = $arena.alloc($header.name.map(|x| Pattern::Tag(x))); - let ann_pattern = Pattern::Apply(loc_name, $header.vars); - - let vars_region = Region::across_all($header.vars.iter().map(|v| &v.region)); - let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region); - let loc_ann_pattern = Loc::at(region_ann_pattern, ann_pattern); - - let value_def = ValueDef::AnnotatedBody { - ann_pattern: $arena.alloc(loc_ann_pattern), - ann_type: $arena.alloc(*$ann_type), - comment: $spaces_before_current - .first() - .and_then($crate::ast::CommentOrNewline::comment_str), - body_pattern: $arena.alloc($loc_pattern), - body_expr: *$arena.alloc($loc_def_expr), - }; - - ( - value_def, - Region::span_across(&$header.name.region, &$region), - ) - }}; -} - -fn parse_defs_end<'a>( - options: ExprParseOptions, - min_indent: u32, - mut defs: Defs<'a>, - arena: &'a Bump, - state: State<'a>, -) -> ParseResult<'a, Defs<'a>, EExpr<'a>> { - let mut global_state = state; - - loop { - // keep a copy in the event we get an EExpr::DefMissingFinalExpr - let state_before = global_state.clone(); - - let state = global_state; - - global_state = match parse_single_def(options, min_indent, arena, state) { - Ok((_, Some(single_def), next_state)) => { - let region = single_def.region; - let spaces_before_current = single_def.spaces_before; - let spaces_after_current = single_def.spaces_after; - - match single_def.type_or_value { - Either::First(type_def) => { - defs.push_type_def( - type_def, - region, - spaces_before_current, - spaces_after_current, - ); - } - Either::Second(value_def) => { - // If we got a ValueDef::Body, check if a type annotation preceded it. - // If so, we may need to combine them into an AnnotatedBody. - let joined_def = match value_def { - ValueDef::Body(loc_pattern, loc_def_expr) => { - let region = - Region::span_across(&loc_pattern.region, &loc_def_expr.region); - - let signature_must_match_value = spaces_before_current.len() <= 1; - let value_name = &loc_pattern.value; - - match defs.last() { - Some(Err(ValueDef::Annotation(ann_pattern, ann_type))) - if signature_must_match_value - || ann_pattern.value.equivalent(value_name) => - { - Some(join_ann_to_body!( - arena, - loc_pattern, - loc_def_expr, - ann_pattern, - ann_type, - spaces_before_current, - region - )) - } - Some(Ok(TypeDef::Alias { - header, - ann: ann_type, - })) if signature_must_match_value - || header - .vars - .first() - .map(|var| var.value.equivalent(value_name)) - .unwrap_or(false) => - { - Some(join_alias_to_body!( - arena, - loc_pattern, - loc_def_expr, - header, - ann_type, - spaces_before_current, - region - )) - } - _ => None, - } - } - _ => None, - }; - if let Some((joined_def, region)) = joined_def { - defs.replace_with_value_def(defs.tags.len() - 1, joined_def, region); - } else { - defs.push_value_def( - value_def, - region, - spaces_before_current, - spaces_after_current, - ); - } - } - } - - next_state - } - Ok((progress, None, s)) => return Ok((progress, defs, s)), - Err((MadeProgress, EExpr::DefMissingFinalExpr(..))) - | Err((MadeProgress, EExpr::DefMissingFinalExpr2(..))) => { - return Ok((MadeProgress, defs, state_before)) - } - Err((progress, err)) => return Err((progress, err)), - }; - } -} - -#[derive(Debug)] -pub struct SingleDef<'a> { - pub type_or_value: Either, ValueDef<'a>>, - pub region: Region, - pub spaces_before: &'a [CommentOrNewline<'a>], - pub spaces_after: &'a [CommentOrNewline<'a>], -} - -fn parse_defs_expr<'a>( - options: ExprParseOptions, - min_indent: u32, - defs: Defs<'a>, - arena: &'a Bump, - state: State<'a>, -) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { - match parse_defs_end(options, min_indent, defs, arena, state) { - Err(bad) => Err(bad), - Ok((_, def_state, state)) => { - // this is no def, because there is no `=` or `:`; parse as an expr - match space0_before_e(expr_start(options), EExpr::IndentEnd).parse( - arena, - state.clone(), - min_indent, - ) { - Err((_, fail)) => { - let mut def_state = def_state; - match def_state.pop_last_value() { - Some(loc_ret) => { - // If the poped value was the only item in defs - just return it as an expression - if def_state.is_empty() { - Ok((MadeProgress, loc_ret.value, state)) - } else { - Ok(( - MadeProgress, - Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)), - state, - )) - } - } - None => Err(( - MadeProgress, - EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()), - )), - } - } - Ok((_, loc_ret, state)) => Ok(( - MadeProgress, - Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)), - state, - )), - } - } - } -} - -fn alias_signature_with_space_before<'a>() -> impl Parser<'a, Loc>, EExpr<'a>> { - increment_min_indent(specialize_err( - EExpr::Type, - space0_before_e(type_annotation::located(false), EType::TIndentStart), - )) -} - -fn opaque_signature_with_space_before<'a>() -> impl Parser< +fn opaque_signature<'a>() -> impl Parser< 'a, ( Loc>, @@ -1502,13 +1053,7 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser< EExpr<'a>, > { and( - specialize_err( - EExpr::Type, - space0_before_e( - type_annotation::located_opaque_signature(true), - EType::TIndentStart, - ), - ), + specialize_err(EExpr::Type, type_annotation::located_opaque_signature(true)), optional(backtrackable(specialize_err( EExpr::Type, space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart), @@ -1516,10 +1061,10 @@ fn opaque_signature_with_space_before<'a>() -> impl Parser< ) } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] enum AliasOrOpaque { - Alias, - Opaque, + Alias, // ':' + Opaque, // ':=' } fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option> { @@ -1546,26 +1091,22 @@ fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option( - min_indent: u32, - options: ExprParseOptions, - expr_state: ExprState<'a>, - loc_op: Loc, +fn parse_stmt_alias_or_opaque<'a>( arena: &'a Bump, state: State<'a>, + min_indent: u32, + expr_state: ExprState<'a>, + kind: Loc, spaces_after_operator: &'a [CommentOrNewline<'a>], - kind: AliasOrOpaque, -) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { let expr_region = expr_state.expr.region; let indented_more = min_indent + 1; let (expr, arguments) = expr_state - .validate_is_type_def(arena, loc_op, kind) + .validate_is_type_def(arena, kind) .map_err(|fail| (MadeProgress, fail))?; - let mut defs = Defs::default(); - - let state = if let Some(tag) = extract_tag_and_spaces(arena, expr.value) { + let (res, state) = if let Some(tag) = extract_tag_and_spaces(arena, expr.value) { let name = tag.item; let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena); @@ -1586,12 +1127,13 @@ fn finish_parsing_alias_or_opaque<'a>( } } - match kind { + match kind.value { AliasOrOpaque::Alias => { - let (_, signature, state) = - alias_signature_with_space_before().parse(arena, state, min_indent)?; + let (_, signature, state) = alias_signature().parse(arena, state, min_indent)?; - let def_region = Region::span_across(&expr.region, &signature.region); + // TODO: this code used to be broken and it dropped the spaces after the operator. + // The formatter is not expecting this, so let's keep it as is for now. + // let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator)); let header = TypeHeader { name: Loc::at(expr.region, name), @@ -1603,16 +1145,16 @@ fn finish_parsing_alias_or_opaque<'a>( ann: signature, }; - defs.push_type_def(def, def_region, &[], &[]); - - state + (Stmt::TypeDef(def), state) } AliasOrOpaque::Opaque => { let (_, (signature, derived), state) = - opaque_signature_with_space_before().parse(arena, state, indented_more)?; + opaque_signature().parse(arena, state, indented_more)?; - let def_region = Region::span_across(&expr.region, &signature.region); + // TODO: this code used to be broken and it dropped the spaces after the operator. + // The formatter is not expecting this, so let's keep it as is for now. + // let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator)); let header = TypeHeader { name: Loc::at(expr.region, name), @@ -1625,9 +1167,7 @@ fn finish_parsing_alias_or_opaque<'a>( derived, }; - defs.push_type_def(def, def_region, &[], &[]); - - state + (Stmt::TypeDef(def), state) } } } else { @@ -1653,37 +1193,35 @@ fn finish_parsing_alias_or_opaque<'a>( .with_spaces_before(spaces_after_operator, ann_type.region); } - let def_region = Region::span_across(&call.region, &ann_type.region); - let value_def = ValueDef::Annotation(Loc::at(expr_region, good), ann_type); - defs.push_value_def(value_def, def_region, &[], &[]); - - state + (Stmt::ValueDef(value_def), state) } } } Err(_) => { // this `:`/`:=` likely occurred inline; treat it as an invalid operator - let op = match kind { + let op = match kind.value { AliasOrOpaque::Alias => ":", AliasOrOpaque::Opaque => ":=", }; - let fail = EExpr::BadOperator(op, loc_op.region.start()); + let fail = EExpr::BadOperator(op, kind.region.start()); return Err((MadeProgress, fail)); } } }; - parse_defs_expr(options, min_indent, defs, arena, state) + Ok((MadeProgress, res, state)) } mod ability { + use parser::absolute_indented_seq; + use super::*; use crate::{ ast::{AbilityMember, Spaceable, Spaced}, - parser::{absolute_indented_seq, EAbility}, + parser::EAbility, }; /// Parses a single ability demand line; see `parse_demand`. @@ -1794,7 +1332,7 @@ mod ability { } fn finish_parsing_ability_def_help<'a>( - start_column: u32, + call_min_indent: u32, name: Loc<&'a str>, args: &'a [Loc>], loc_implements: Loc>, @@ -1803,22 +1341,21 @@ fn finish_parsing_ability_def_help<'a>( ) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> { let mut demands = Vec::with_capacity_in(2, arena); - let min_indent_for_demand = start_column + 1; - // Parse the first demand. This will determine the indentation level all the // other demands must observe. let start = state.pos(); let (_, (demand_indent_level, first_demand), mut state) = - ability::parse_demand(ability::IndentLevel::PendingMin(min_indent_for_demand)) - .parse(arena, state, min_indent_for_demand) + ability::parse_demand(ability::IndentLevel::PendingMin(call_min_indent)) + .trace("ability_demand") + .parse(arena, state, call_min_indent) .map_err(|(progress, err)| (progress, EExpr::Ability(err, start)))?; demands.push(first_demand); let demand_indent = ability::IndentLevel::Exact(demand_indent_level); - let demand_parser = ability::parse_demand(demand_indent); + let demand_parser = ability::parse_demand(demand_indent).trace("ability_demand"); loop { - match demand_parser.parse(arena, state.clone(), min_indent_for_demand) { + match demand_parser.parse(arena, state.clone(), call_min_indent) { Ok((_, (_indent, demand), next_state)) => { state = next_state; demands.push(demand); @@ -1842,15 +1379,99 @@ fn finish_parsing_ability_def_help<'a>( Ok((MadeProgress, (type_def, def_region), state)) } +#[derive(Debug, Clone, Copy)] +pub enum Stmt<'a> { + Expr(Expr<'a>), + Backpassing(&'a [Loc>], &'a Loc>), + TypeDef(TypeDef<'a>), + ValueDef(ValueDef<'a>), +} + #[allow(clippy::too_many_arguments)] -fn parse_expr_operator<'a>( - min_indent: u32, - options: ExprParseOptions, - mut expr_state: ExprState<'a>, - loc_op: Loc, - line_indent: u32, +fn parse_stmt_operator<'a>( arena: &'a Bump, state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + expr_state: ExprState<'a>, + loc_op: Loc, + initial_state: State<'a>, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + let (_, spaces_after_operator, state) = + loc_space0_e(EExpr::IndentEnd).parse(arena, state, min_indent)?; + + // a `-` is unary if it is preceded by a space and not followed by a space + + let op = loc_op.value; + let op_start = loc_op.region.start(); + let op_end = loc_op.region.end(); + let new_start = state.pos(); + match op { + OperatorOrDef::BinOp(BinOp::Minus) if expr_state.end != op_start && op_end == new_start => { + parse_negated_term( + arena, + state, + min_indent, + call_min_indent, + expr_state, + options, + initial_state, + loc_op.with_value(BinOp::Minus), + ) + .map(|(progress, expr, state)| (progress, Stmt::Expr(expr), state)) + } + OperatorOrDef::BinOp(op) => parse_after_binop( + arena, + state, + min_indent, + call_min_indent, + options, + true, + spaces_after_operator.value, + expr_state, + loc_op.with_value(op), + ) + .map(|(progress, expr, state)| (progress, Stmt::Expr(expr), state)), + OperatorOrDef::Assignment => parse_stmt_assignment( + arena, + state, + call_min_indent, + expr_state, + loc_op, + options, + spaces_after_operator, + ), + OperatorOrDef::Backpassing => parse_stmt_backpassing( + arena, + state, + call_min_indent, + expr_state, + loc_op, + options, + spaces_after_operator.value, + ), + OperatorOrDef::AliasOrOpaque(kind) => parse_stmt_alias_or_opaque( + arena, + state, + call_min_indent, + expr_state, + loc_op.with_value(kind), + spaces_after_operator.value, + ), + } +} + +#[allow(clippy::too_many_arguments)] +fn parse_expr_operator<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + check_for_defs: bool, + expr_state: ExprState<'a>, + loc_op: Loc, initial_state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { let (_, spaces_after_operator, state) = @@ -1863,242 +1484,310 @@ fn parse_expr_operator<'a>( let op_end = loc_op.region.end(); let new_start = state.pos(); match op { - BinOp::Minus if expr_state.end != op_start && op_end == new_start => { - // negative terms - - let (_, negated_expr, state) = loc_term(options).parse(arena, state, min_indent)?; - let new_end = state.pos(); - - let arg = numeric_negate_expression( - arena, - initial_state, - loc_op, - negated_expr, - expr_state.spaces_after, - ); - - let initial_state = state.clone(); - - let (spaces, state) = - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => (&[] as &[_], state), - Ok((_, spaces, state)) => (spaces, state), - }; - - expr_state.arguments.push(arena.alloc(arg)); - expr_state.spaces_after = spaces; - expr_state.end = new_end; - - parse_expr_end(min_indent, options, expr_state, arena, state, initial_state) - } - BinOp::Assignment => { - let expr_region = expr_state.expr.region; - - let indented_more = line_indent + 1; - - let call = expr_state - .validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction) - .map_err(|fail| (MadeProgress, fail))?; - - let (value_def, def_region, state) = { - match expr_to_pattern_help(arena, &call.value) { - Ok(good) => { - let (_, mut body, state) = - expr_start(options).parse(arena, state, indented_more)?; - - // put the spaces from after the operator in front of the call - if !spaces_after_operator.is_empty() { - body = arena - .alloc(body.value) - .with_spaces_before(spaces_after_operator, body.region); - } - - let body_region = Region::span_across(&call.region, &body.region); - - let alias = ValueDef::Body( - arena.alloc(Loc::at(expr_region, good)), - arena.alloc(body), - ); - - (alias, body_region, state) - } - Err(_) => { - // this `=` likely occurred inline; treat it as an invalid operator - let fail = EExpr::BadOperator(arena.alloc("="), loc_op.region.start()); - - return Err((MadeProgress, fail)); - } - } - }; - - let mut defs = Defs::default(); - defs.push_value_def(value_def, def_region, &[], &[]); - - parse_defs_expr(options, min_indent, defs, arena, state) - } - BinOp::Backpassing => { - let expr_region = expr_state.expr.region; - let indented_more = min_indent + 1; - - let call = expr_state - .validate_assignment_or_backpassing(arena, loc_op, |_, pos| { - EExpr::BadOperator("<-", pos) - }) - .map_err(|fail| (MadeProgress, fail))?; - - let (loc_pattern, loc_body, state) = { - match expr_to_pattern_help(arena, &call.value) { - Ok(good) => { - let (_, mut ann_type, state) = - expr_start(options).parse(arena, state, indented_more)?; - - // put the spaces from after the operator in front of the call - if !spaces_after_operator.is_empty() { - ann_type = arena - .alloc(ann_type.value) - .with_spaces_before(spaces_after_operator, ann_type.region); - } - - (Loc::at(expr_region, good), ann_type, state) - } - Err(_) => { - // this `=` likely occurred inline; treat it as an invalid operator - let fail = EExpr::BadOperator("=", loc_op.region.start()); - - return Err((MadeProgress, fail)); - } - } - }; - - let parse_cont = space0_before_e(expr_start(options), EExpr::IndentEnd); - - let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?; - - let ret = Expr::Backpassing( - arena.alloc([loc_pattern]), - arena.alloc(loc_body), - arena.alloc(loc_cont), - ); - - Ok((MadeProgress, ret, state)) - } - BinOp::IsAliasType | BinOp::IsOpaqueType => finish_parsing_alias_or_opaque( - min_indent, - options, - expr_state, - loc_op, + BinOp::Minus if expr_state.end != op_start && op_end == new_start => parse_negated_term( arena, state, - spaces_after_operator, - match op { - BinOp::IsAliasType => AliasOrOpaque::Alias, - BinOp::IsOpaqueType => AliasOrOpaque::Opaque, - _ => unreachable!(), - }, - ), - _ => match loc_possibly_negative_or_negated_term(options).parse( - arena, - state.clone(), min_indent, - ) { - Err((MadeProgress, f)) => Err((MadeProgress, f)), - Ok((_, mut new_expr, state)) => { - let new_end = state.pos(); - - let initial_state = state.clone(); - - // put the spaces from after the operator in front of the new_expr - if !spaces_after_operator.is_empty() { - new_expr = arena - .alloc(new_expr.value) - .with_spaces_before(spaces_after_operator, new_expr.region); - } - - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => { - let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); - - let call = to_call(arena, args, expr_state.expr); - - expr_state.operators.push((call, loc_op)); - expr_state.expr = new_expr; - expr_state.end = new_end; - expr_state.spaces_after = &[]; - - let expr = parse_expr_final(expr_state, arena); - Ok((MadeProgress, expr, state)) - } - Ok((_, spaces, state)) => { - let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); - - let call = to_call(arena, args, expr_state.expr); - - expr_state.operators.push((call, loc_op)); - expr_state.expr = new_expr; - expr_state.end = new_end; - expr_state.spaces_after = spaces; - - let new_min_indent = if is_expr_suffixed(&new_expr.value) { - min_indent + 1 - } else { - min_indent - }; - - match parse_expr_end( - new_min_indent, - options, - expr_state, - arena, - state, - initial_state, - ) { - Ok((progress, expr, state)) => { - if let Expr::BinOps(..) = expr { - let def_region = expr.get_region_spanning_binops(); - let mut new_expr = Loc::at(def_region, expr); - - if is_expr_suffixed(&new_expr.value) { - // We have parsed a statement such as `"hello" |> line!` - // put the spaces from after the operator in front of the call - if !spaces_after_operator.is_empty() { - new_expr = arena.alloc(expr).with_spaces_before( - spaces_after_operator, - def_region, - ); - } - - let value_def = ValueDef::Stmt(arena.alloc(new_expr)); - - let mut defs = Defs::default(); - defs.push_value_def(value_def, def_region, &[], &[]); - - return parse_defs_expr( - options, min_indent, defs, arena, state, - ); - } - } - - // else return the parsed expression - Ok((progress, expr, state)) - } - Err(err) => Err(err), - } - } - } - } - Err((NoProgress, _e)) => { - return Err((MadeProgress, EExpr::TrailingOperator(state.pos()))); - } - }, + call_min_indent, + expr_state, + options, + initial_state, + loc_op, + ), + _ => parse_after_binop( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + spaces_after_operator, + expr_state, + loc_op, + ), } } -fn parse_expr_end<'a>( +#[allow(clippy::too_many_arguments)] +fn parse_after_binop<'a>( + arena: &'a Bump, + state: State<'a>, min_indent: u32, + call_min_indent: u32, options: ExprParseOptions, + check_for_defs: bool, + spaces_after_operator: &'a [CommentOrNewline], + mut expr_state: ExprState<'a>, + loc_op: Loc, +) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { + match loc_possibly_negative_or_negated_term(options).parse( + arena, + state.clone(), + call_min_indent, + ) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), + Ok((_, mut new_expr, state)) => { + let new_end = state.pos(); + + let initial_state = state.clone(); + + // put the spaces from after the operator in front of the new_expr + if !spaces_after_operator.is_empty() { + new_expr = arena + .alloc(new_expr.value) + .with_spaces_before(spaces_after_operator, new_expr.region); + } + + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { + let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); + + let call = to_call(arena, args, expr_state.expr); + + expr_state.operators.push((call, loc_op)); + expr_state.expr = new_expr; + expr_state.end = new_end; + expr_state.spaces_after = &[]; + + let expr = parse_expr_final(expr_state, arena); + Ok((MadeProgress, expr, state)) + } + Ok((_, spaces, state)) => { + let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena)); + + let call = to_call(arena, args, expr_state.expr); + + expr_state.operators.push((call, loc_op)); + expr_state.expr = new_expr; + expr_state.end = new_end; + expr_state.spaces_after = spaces; + + parse_expr_end( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + initial_state, + ) + } + } + } + Err((NoProgress, _e)) => { + return Err((MadeProgress, EExpr::TrailingOperator(state.pos()))); + } + } +} + +fn parse_stmt_backpassing<'a>( + arena: &'a Bump, + state: State<'a>, + call_min_indent: u32, + expr_state: ExprState<'a>, + loc_op: Loc, + options: ExprParseOptions, + spaces_after_operator: &'a [CommentOrNewline], +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + // called after parsing the <- operator + + let expr_region = expr_state.expr.region; + + let call = expr_state + .validate_assignment_or_backpassing(arena, loc_op, |_, pos| EExpr::BadOperator("<-", pos)) + .map_err(|fail| (MadeProgress, fail))?; + + let (loc_pattern, loc_body, state) = { + match expr_to_pattern_help(arena, &call.value) { + Ok(good) => { + let (_, mut ann_type, state) = + expr_start(options).parse(arena, state, call_min_indent)?; + + // put the spaces from after the operator in front of the call + if !spaces_after_operator.is_empty() { + ann_type = arena + .alloc(ann_type.value) + .with_spaces_before(spaces_after_operator, ann_type.region); + } + + (Loc::at(expr_region, good), ann_type, state) + } + Err(_) => { + // this `=` likely occurred inline; treat it as an invalid operator + let fail = EExpr::BadOperator("=", loc_op.region.start()); + + return Err((MadeProgress, fail)); + } + } + }; + + let ret = Stmt::Backpassing(arena.alloc([loc_pattern]), arena.alloc(loc_body)); + + Ok((MadeProgress, ret, state)) +} + +fn parse_stmt_multi_backpassing<'a>( mut expr_state: ExprState<'a>, arena: &'a Bump, state: State<'a>, + min_indent: u32, + options: ExprParseOptions, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + // called after parsing the first , in `a, b <- c` (e.g.) + + let (_, mut patterns, state) = specialize_err_ref( + EExpr::Pattern, + crate::parser::sep_by0( + byte(b',', EPattern::Start), + space0_around_ee( + crate::pattern::loc_pattern_help(), + EPattern::Start, + EPattern::IndentEnd, + ), + ), + ) + .parse(arena, state, min_indent) + .map_err(|(progress, err)| { + // We were expecting the end of an expression, and parsed a comma + // therefore we are either on the LHS of backpassing or this is was + // in an invalid position. + if let EExpr::Pattern(EPattern::IndentEnd(_), pos) = err { + (progress, EExpr::UnexpectedComma(pos.sub(1))) + } else { + (progress, err) + } + })?; + + expr_state.consume_spaces(arena); + let call = to_call(arena, expr_state.arguments, expr_state.expr); + + let pattern = expr_to_pattern_help(arena, &call.value).map_err(|()| { + ( + MadeProgress, + EExpr::Pattern(arena.alloc(EPattern::NotAPattern(state.pos())), state.pos()), + ) + })?; + + let loc_pattern = Loc::at(call.region, pattern); + + patterns.insert(0, loc_pattern); + + let line_indent = state.line_indent(); + + match two_bytes(b'<', b'-', EExpr::BackpassArrow).parse(arena, state.clone(), min_indent) { + Err((_, fail)) => Err((MadeProgress, fail)), + Ok((_, _, state)) => { + let parse_body = space0_before_e(expr_start(options), EExpr::IndentEnd); + + let (_, loc_body, state) = parse_body.parse(arena, state, line_indent + 1)?; + + let ret = Stmt::Backpassing(patterns.into_bump_slice(), arena.alloc(loc_body)); + + Ok((MadeProgress, ret, state)) + } + } +} + +fn parse_stmt_assignment<'a>( + arena: &'a Bump, + state: State<'a>, + call_min_indent: u32, + expr_state: ExprState<'a>, + loc_op: Loc, + options: ExprParseOptions, + spaces_after_operator: Loc<&'a [CommentOrNewline]>, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + let call = expr_state + .validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction) + .map_err(|fail| (MadeProgress, fail))?; + + let (value_def, state) = { + match expr_to_pattern_help(arena, &call.value) { + Ok(good) => { + let (_, body, state) = parse_block_inner( + options, + arena, + state, + call_min_indent, + EExpr::IndentEnd, + |a, _| a.clone(), + spaces_after_operator, + !spaces_after_operator.value.is_empty(), + )?; + + let alias = + ValueDef::Body(arena.alloc(Loc::at(call.region, good)), arena.alloc(body)); + + (alias, state) + } + Err(_) => { + // this `=` likely occurred inline; treat it as an invalid operator + let fail = EExpr::BadOperator(arena.alloc("="), loc_op.region.start()); + + return Err((MadeProgress, fail)); + } + } + }; + + Ok((MadeProgress, Stmt::ValueDef(value_def), state)) +} + +#[allow(clippy::too_many_arguments)] +fn parse_negated_term<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + mut expr_state: ExprState<'a>, + options: ExprParseOptions, + initial_state: State<'a>, + loc_op: Loc, +) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { + let (_, negated_expr, state) = loc_term(options).parse(arena, state, min_indent)?; + let new_end = state.pos(); + + let arg = numeric_negate_expression( + arena, + initial_state, + loc_op, + negated_expr, + expr_state.spaces_after, + ); + + let initial_state = state.clone(); + + let (spaces, state) = match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => (&[] as &[_], state), + Ok((_, spaces, state)) => (spaces, state), + }; + + expr_state.arguments.push(arena.alloc(arg)); + expr_state.spaces_after = spaces; + expr_state.end = new_end; + + // TODO: this should probably be handled in the caller, not here + parse_expr_end( + arena, + state, + min_indent, + call_min_indent, + options, + true, + expr_state, + initial_state, + ) +} + +#[allow(clippy::too_many_arguments)] +fn parse_expr_end<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + options: ExprParseOptions, + check_for_defs: bool, + mut expr_state: ExprState<'a>, initial_state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { let parser = skip_first( @@ -2106,208 +1795,217 @@ fn parse_expr_end<'a>( loc_term_or_underscore(options), ); - match parser.parse(arena, state.clone(), min_indent) { + match parser.parse(arena, state.clone(), call_min_indent) { Err((MadeProgress, f)) => Err((MadeProgress, f)), - Ok(( - _, - implements @ Loc { - value: - Expr::Var { - module_name: "", - ident: crate::keyword::IMPLEMENTS, - .. - }, - .. - }, + Ok((_, arg, state)) => parse_apply_arg( + arena, state, - )) if matches!(expr_state.expr.value, Expr::Tag(..)) => { - // This is an ability definition, `Ability arg1 ... implements ...`. - - let name = expr_state.expr.map_owned(|e| match e { - Expr::Tag(name) => name, - _ => unreachable!(), - }); - - let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena); - for argument in expr_state.arguments { - match expr_to_pattern_help(arena, &argument.value) { - Ok(good) => { - arguments.push(Loc::at(argument.region, good)); - } - Err(_) => { - let start = argument.region.start(); - let err = &*arena.alloc(EPattern::Start(start)); - return Err((MadeProgress, EExpr::Pattern(err, argument.region.start()))); - } - } - } - - // Attach any spaces to the `implements` keyword - let implements = if !expr_state.spaces_after.is_empty() { - arena - .alloc(Implements::Implements) - .with_spaces_before(expr_state.spaces_after, implements.region) - } else { - Loc::at(implements.region, Implements::Implements) - }; - - let args = arguments.into_bump_slice(); - let (_, (type_def, def_region), state) = - finish_parsing_ability_def_help(min_indent, name, args, implements, arena, state)?; - - let mut defs = Defs::default(); - - defs.push_type_def(type_def, def_region, &[], &[]); - - parse_defs_expr(options, min_indent, defs, arena, state) - } - Ok((_, mut arg, state)) => { - let new_end = state.pos(); - - let min_indent = if is_expr_suffixed(&arg.value) { - min_indent + 1 - } else { - min_indent - }; - - // now that we have `function arg1 ... argn`, attach the spaces to the `argn` - if !expr_state.spaces_after.is_empty() { - arg = arena - .alloc(arg.value) - .with_spaces_before(expr_state.spaces_after, arg.region); - - expr_state.spaces_after = &[]; - } - let initial_state = state.clone(); - - match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { - Err((_, _)) => { - expr_state.arguments.push(arena.alloc(arg)); - expr_state.end = new_end; - expr_state.spaces_after = &[]; - - let expr = parse_expr_final(expr_state, arena); - Ok((MadeProgress, expr, state)) - } - Ok((_, new_spaces, state)) => { - expr_state.arguments.push(arena.alloc(arg)); - expr_state.end = new_end; - expr_state.spaces_after = new_spaces; - - parse_expr_end(min_indent, options, expr_state, arena, state, initial_state) - } - } - } + min_indent, + call_min_indent, + expr_state, + arg, + options, + check_for_defs, + ), Err((NoProgress, _)) => { let before_op = state.clone(); // try an operator - let line_indent = state.line_indent(); - match loc(operator()).parse(arena, state.clone(), min_indent) { + match loc(bin_op(check_for_defs)).parse(arena, state.clone(), min_indent) { Err((MadeProgress, f)) => Err((MadeProgress, f)), Ok((_, loc_op, state)) => { expr_state.consume_spaces(arena); let initial_state = before_op; parse_expr_operator( - min_indent, - options, - expr_state, - loc_op, - line_indent, arena, state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + loc_op, initial_state, ) } Err((NoProgress, _)) => { - let mut state = state; - // try multi-backpassing - if options.accept_multi_backpassing && state.bytes().starts_with(b",") { - state = state.advance(1); - - let (_, mut patterns, state) = specialize_err_ref( - EExpr::Pattern, - crate::parser::sep_by0( - byte(b',', EPattern::Start), - space0_around_ee( - crate::pattern::loc_pattern_help(), - EPattern::Start, - EPattern::IndentEnd, - ), - ), - ) - .parse(arena, state, min_indent) - .map_err(|(progress, err)| { - // We were expecting the end of an expression, and parsed a comma - // therefore we are either on the LHS of backpassing or this is was - // in an invalid position. - if let EExpr::Pattern(EPattern::IndentEnd(_), pos) = err { - (progress, EExpr::UnexpectedComma(pos.sub(1))) - } else { - (progress, err) - } - })?; - - expr_state.consume_spaces(arena); - let call = to_call(arena, expr_state.arguments, expr_state.expr); - - let pattern = expr_to_pattern_help(arena, &call.value).map_err(|()| { - ( - MadeProgress, - EExpr::Pattern( - arena.alloc(EPattern::NotAPattern(state.pos())), - state.pos(), - ), - ) - })?; - - let loc_pattern = Loc::at(call.region, pattern); - - patterns.insert(0, loc_pattern); - - match two_bytes(b'<', b'-', EExpr::BackpassArrow).parse( - arena, - state.clone(), - min_indent, - ) { - Err((_, fail)) => Err((MadeProgress, fail)), - Ok((_, _, state)) => { - let parse_body = space0_before_e( - increment_min_indent(expr_start(options)), - EExpr::IndentEnd, - ); - - let (_, loc_body, state) = - parse_body.parse(arena, state, min_indent)?; - - let parse_cont = - space0_before_e(expr_start(options), EExpr::IndentEnd); - - let (_, loc_cont, state) = - parse_cont.parse(arena, state, min_indent)?; - - let ret = Expr::Backpassing( - patterns.into_bump_slice(), - arena.alloc(loc_body), - arena.alloc(loc_cont), - ); - - Ok((MadeProgress, ret, state)) - } - } - } else if options.check_for_arrow && state.bytes().starts_with(b"->") { - Err((MadeProgress, EExpr::BadOperator("->", state.pos()))) - } else { - let expr = parse_expr_final(expr_state, arena); - - // roll back space parsing - Ok((MadeProgress, expr, initial_state)) - } + let expr = parse_expr_final(expr_state, arena); + // roll back space parsing + Ok((MadeProgress, expr, initial_state)) } } } } } +fn parse_stmt_after_apply<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + mut expr_state: ExprState<'a>, + options: ExprParseOptions, + initial_state: State<'a>, +) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { + let before_op = state.clone(); + match loc(operator()).parse(arena, state.clone(), min_indent) { + Err((MadeProgress, f)) => Err((MadeProgress, f)), + Ok((_, loc_op, state)) => { + expr_state.consume_spaces(arena); + let initial_state = before_op; + parse_stmt_operator( + arena, + state, + min_indent, + call_min_indent, + options, + expr_state, + loc_op, + initial_state, + ) + } + Err((NoProgress, _)) => { + let mut state = state; + // try multi-backpassing + if options.accept_multi_backpassing && state.bytes().starts_with(b",") { + state = state.advance(1); + + parse_stmt_multi_backpassing(expr_state, arena, state, min_indent, options) + } else if options.check_for_arrow && state.bytes().starts_with(b"->") { + Err((MadeProgress, EExpr::BadOperator("->", state.pos()))) + } else { + let expr = parse_expr_final(expr_state, arena); + + // roll back space parsing + Ok((MadeProgress, Stmt::Expr(expr), initial_state)) + } + } + } +} + +#[allow(clippy::too_many_arguments)] +fn parse_apply_arg<'a>( + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + call_min_indent: u32, + mut expr_state: ExprState<'a>, + mut arg: Loc>, + options: ExprParseOptions, + check_for_defs: bool, +) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { + let new_end = state.pos(); + + // now that we have `function arg1 ... argn`, attach the spaces to the `argn` + if !expr_state.spaces_after.is_empty() { + arg = arena + .alloc(arg.value) + .with_spaces_before(expr_state.spaces_after, arg.region); + + expr_state.spaces_after = &[]; + } + let initial_state = state.clone(); + + match space0_e(EExpr::IndentEnd).parse(arena, state.clone(), min_indent) { + Err((_, _)) => { + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = &[]; + + let expr = parse_expr_final(expr_state, arena); + Ok((MadeProgress, expr, state)) + } + Ok((_, new_spaces, state)) => { + expr_state.arguments.push(arena.alloc(arg)); + expr_state.end = new_end; + expr_state.spaces_after = new_spaces; + + parse_expr_end( + arena, + state, + min_indent, + call_min_indent, + options, + check_for_defs, + expr_state, + initial_state, + ) + } + } +} + +fn parse_ability_def<'a>( + expr_state: ExprState<'a>, + state: State<'a>, + arena: &'a Bump, + implements: Loc>, + call_min_indent: u32, +) -> Result<(TypeDef<'a>, State<'a>), (Progress, EExpr<'a>)> { + // This is an ability definition, `Ability arg1 ... implements ...`. + + let name = expr_state.expr.map_owned(|e| match e { + Expr::Tag(name) => name, + _ => unreachable!(), + }); + + let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena); + for argument in expr_state.arguments { + match expr_to_pattern_help(arena, &argument.value) { + Ok(good) => { + arguments.push(Loc::at(argument.region, good)); + } + Err(_) => { + let start = argument.region.start(); + let err = &*arena.alloc(EPattern::Start(start)); + return Err((MadeProgress, EExpr::Pattern(err, argument.region.start()))); + } + } + } + + // Attach any spaces to the `implements` keyword + let implements = if !expr_state.spaces_after.is_empty() { + arena + .alloc(Implements::Implements) + .with_spaces_before(expr_state.spaces_after, implements.region) + } else { + Loc::at(implements.region, Implements::Implements) + }; + + let args = arguments.into_bump_slice(); + let (_, (type_def, _), state) = + finish_parsing_ability_def_help(call_min_indent, name, args, implements, arena, state)?; + + Ok((type_def, state)) +} + +pub fn loc_expr_block<'a>( + accept_multi_backpassing: bool, +) -> impl Parser<'a, Loc>, EExpr<'a>> { + space0_after_e( + move |arena: &'a Bump, state: State<'a>, min_indent: u32| { + let options = ExprParseOptions { + accept_multi_backpassing, + check_for_arrow: true, + }; + + let (_, loc_first_space, state) = + loc_space0_e(EExpr::IndentStart).parse(arena, state, min_indent)?; + + parse_block_inner( + options, + arena, + state, + min_indent, + EExpr::IndentStart, + |a, _| a.clone(), + loc_first_space, + true, + ) + }, + EExpr::IndentEnd, + ) + .trace("loc_expr_block") +} + pub fn loc_expr<'a>(accept_multi_backpassing: bool) -> impl Parser<'a, Loc>, EExpr<'a>> { space0_before_e( expr_start(ExprParseOptions { @@ -2507,32 +2205,39 @@ fn assigned_expr_field_to_pattern_help<'a>( pub fn parse_top_level_defs<'a>( arena: &'a bumpalo::Bump, state: State<'a>, - mut output: Defs<'a>, + output: Defs<'a>, ) -> ParseResult<'a, Defs<'a>, EExpr<'a>> { - let (_, initial_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, 0)?; + let (_, loc_first_space, state) = loc_space0_e(EExpr::IndentStart).parse(arena, state, 0)?; - let start_column = state.column(); + let (_, stmts, state) = parse_stmt_seq( + arena, + state, + |e, _| e.clone(), + ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }, + 0, + loc_first_space, + EExpr::IndentEnd, + )?; - let options = ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }; + let (_, last_space, state) = space0_e(EExpr::IndentStart).parse(arena, state, 0)?; let existing_len = output.tags.len(); - let before = Slice::extend_new(&mut output.spaces, initial_space.iter().copied()); + let (mut output, last_expr) = + stmts_to_defs(&stmts, output, false, arena).map_err(|e| (MadeProgress, e))?; - let (_, mut output, state) = parse_defs_end(options, start_column, output, arena, state)?; - - let (_, final_space, state) = space0_e(EExpr::IndentEnd).parse(arena, state, start_column)?; + if let Some(expr) = last_expr { + return Err(( + MadeProgress, + EExpr::UnexpectedTopLevelExpr(expr.region.start()), + )); + } if output.tags.len() > existing_len { - // add surrounding whitespace - let after = Slice::extend_new(&mut output.spaces, final_space.iter().copied()); - - debug_assert!(output.space_before[existing_len].is_empty()); - output.space_before[existing_len] = before; - + let after = Slice::extend_new(&mut output.spaces, last_space.iter().copied()); let last = output.tags.len() - 1; debug_assert!(output.space_after[last].is_empty() || after.is_empty()); output.space_after[last] = after; @@ -2568,10 +2273,7 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo // Parse the -> which separates params from body two_bytes(b'-', b'>', EClosure::Arrow), // Parse the body - space0_before_e( - specialize_err_ref(EClosure::Body, expr_start(options)), - EClosure::IndentBody, - ), + block(options, true, EClosure::IndentBody, EClosure::Body), ), ), ), @@ -2584,11 +2286,13 @@ fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClo } mod when { + use parser::indented_seq_skip_first; + use super::*; - use crate::ast::WhenBranch; + use crate::{ast::WhenBranch, blankspace::space0_around_e_no_after_indent_check}; /// Parser for when expressions. - pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> { + pub fn when_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> { map_with_arena( and( indented_seq_skip_first( @@ -2610,7 +2314,7 @@ mod when { move |arena: &'a Bump, (loc_condition, branches): (Loc>, Vec<'a, &'a WhenBranch<'a>>)| { Expr::When(arena.alloc(loc_condition), branches.into_bump_slice()) } - ) + ).trace("when") } fn branches<'a>( @@ -2718,6 +2422,16 @@ mod when { ) } + fn error_on_arrow<'a, T, E: 'a>(f: impl Fn(Position) -> E) -> impl Parser<'a, T, E> { + move |_, state: State<'a>, _| { + if state.bytes().starts_with(b"->") { + Err((MadeProgress, f(state.pos()))) + } else { + Err((NoProgress, f(state.pos()))) + } + } + } + fn branch_single_alternative<'a>() -> impl Parser<'a, Loc>, EWhen<'a>> { move |arena, state, min_indent| { let (_, spaces, state) = @@ -2754,8 +2468,7 @@ mod when { Ok((_progress, spaces, state)) => { match pattern_indent_level { Some(wanted) if state.column() > wanted => { - // this branch is indented too much - Err((NoProgress, EWhen::IndentPattern(state.pos()))) + error_on_arrow(EWhen::IndentPattern).parse(arena, state, min_indent) } Some(wanted) if state.column() < wanted => { let indent = wanted - state.column(); @@ -2800,13 +2513,14 @@ mod when { /// Parsing the righthandside of a branch in a when conditional. fn branch_result<'a>(indent: u32) -> impl Parser<'a, Loc>, EWhen<'a>> { + let options = ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }; move |arena, state, _min_indent| { skip_first( two_bytes(b'-', b'>', EWhen::Arrow), - space0_before_e( - specialize_err_ref(EWhen::Branch, loc_expr(true)), - EWhen::IndentBranch, - ), + block(options, true, EWhen::IndentBranch, EWhen::Branch), ) .parse(arena, state, indent) } @@ -2814,6 +2528,10 @@ mod when { } fn if_branch<'a>() -> impl Parser<'a, (Loc>, Loc>), EIf<'a>> { + let options = ExprParseOptions { + accept_multi_backpassing: true, + check_for_arrow: true, + }; skip_second( and( skip_second( @@ -2824,86 +2542,95 @@ fn if_branch<'a>() -> impl Parser<'a, (Loc>, Loc>), EIf<'a>> { ), parser::keyword(keyword::THEN, EIf::Then), ), - space0_around_ee( - specialize_err_ref(EIf::ThenBranch, loc_expr(true)), - EIf::IndentThenBranch, - EIf::IndentElseToken, + map_with_arena( + space0_after_e( + block(options, false, EIf::IndentThenBranch, EIf::ThenBranch), + EIf::IndentElseToken, + ), + |arena: &'a Bump, block: Loc>| match block.value { + Expr::SpaceAfter(&Expr::SpaceBefore(x, before), after) => block.with_value( + Expr::SpaceBefore(arena.alloc(Expr::SpaceAfter(x, after)), before), + ), + _ => block, + }, ), ), parser::keyword(keyword::ELSE, EIf::Else), ) } -fn expect_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<'a>> { +fn expect_help<'a>( + options: ExprParseOptions, + preceding_comment: Region, +) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> { move |arena: &'a Bump, state: State<'a>, min_indent| { - let start_column = state.column(); + let parse_expect_vanilla = crate::parser::keyword(crate::keyword::EXPECT, EExpect::Expect); + let parse_expect_fx = crate::parser::keyword(crate::keyword::EXPECT_FX, EExpect::Expect); + let parse_expect = either(parse_expect_vanilla, parse_expect_fx); - let (_, _, state) = - parser::keyword(keyword::EXPECT, EExpect::Expect).parse(arena, state, min_indent)?; + let (_, kw, state) = parse_expect.parse(arena, state, min_indent)?; - let (_, condition, state) = space0_before_e( - specialize_err_ref( - EExpect::Condition, - set_min_indent(start_column + 1, expr_start(options)), - ), + let (_, condition, state) = parse_block( + options, + arena, + state, + true, EExpect::IndentCondition, + EExpect::Condition, ) - .parse(arena, state, start_column + 1) .map_err(|(_, f)| (MadeProgress, f))?; - let parse_cont = specialize_err_ref( - EExpect::Continuation, - space0_before_e(expr_start(options), EExpr::IndentEnd), - ); + let vd = match kw { + Either::First(_) => ValueDef::Expect { + condition: arena.alloc(condition), + preceding_comment, + }, + Either::Second(_) => ValueDef::ExpectFx { + condition: arena.alloc(condition), + preceding_comment, + }, + }; - let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?; - - let expr = Expr::Expect(arena.alloc(condition), arena.alloc(loc_cont)); - - Ok((MadeProgress, expr, state)) + Ok((MadeProgress, Stmt::ValueDef(vd), state)) } } -fn dbg_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<'a>> { - move |arena: &'a Bump, state: State<'a>, min_indent| { - let start_column = state.column(); - +fn dbg_help<'a>( + options: ExprParseOptions, + preceding_comment: Region, +) -> impl Parser<'a, Stmt<'a>, EExpect<'a>> { + (move |arena: &'a Bump, state: State<'a>, min_indent| { let (_, _, state) = parser::keyword(keyword::DBG, EExpect::Dbg).parse(arena, state, min_indent)?; - let (_, condition, state) = space0_before_e( - specialize_err_ref( - EExpect::Condition, - set_min_indent(start_column + 1, expr_start(options)), - ), + let (_, condition, state) = parse_block( + options, + arena, + state, + true, EExpect::IndentCondition, + EExpect::Condition, ) - .parse(arena, state, start_column + 1) .map_err(|(_, f)| (MadeProgress, f))?; - let parse_cont = specialize_err_ref( - EExpect::Continuation, - space0_before_e(expr_start(options), EExpr::IndentEnd), - ); + let stmt = Stmt::ValueDef(ValueDef::Dbg { + condition: arena.alloc(condition), + preceding_comment, + }); - let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?; - - let expr = Expr::Dbg(arena.alloc(condition), arena.alloc(loc_cont)); - - Ok((MadeProgress, expr, state)) - } + Ok((MadeProgress, stmt, state)) + }) + .trace("dbg_help") } -fn import_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { - move |arena: &'a Bump, state: State<'a>, min_indent: u32| { - let (_, (import_def, spaces_after), state) = - specialize_err(EExpr::Import, import()).parse(arena, state, min_indent)?; - - let mut defs = Defs::default(); - defs.push_value_def(import_def.value, import_def.region, &[], spaces_after); - - parse_defs_expr(options, min_indent, defs, arena, state) - } +fn import<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> { + skip_second( + skip_first( + parser::keyword(keyword::IMPORT, EImport::Import), + increment_min_indent(one_of!(import_body(), import_ingested_file_body())), + ), + require_newline_or_eof(EImport::EndNewline), + ) } fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<'a>> { @@ -2937,12 +2664,14 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< } }; - let (_, else_branch, state) = space0_before_e( - specialize_err_ref(EIf::ElseBranch, expr_start(options)), + let (_, else_branch, state) = parse_block( + options, + arena, + state_final_else, + true, EIf::IndentElseBranch, - ) - .parse(arena, state_final_else, min_indent) - .map_err(|(_, f)| (MadeProgress, f))?; + EIf::ElseBranch, + )?; let expr = Expr::If(branches.into_bump_slice(), arena.alloc(else_branch)); @@ -2950,6 +2679,486 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< } } +fn block<'a, E>( + options: ExprParseOptions, + require_indent: bool, + indent_problem: fn(Position) -> E, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, +) -> impl Parser<'a, Loc>, E> +where + E: 'a + SpaceProblem, +{ + (move |arena: &'a Bump, state, _min_indent| { + parse_block( + options, + arena, + state, + require_indent, + indent_problem, + wrap_error, + ) + }) + .trace("block") +} + +fn parse_block<'a, E>( + options: ExprParseOptions, + arena: &'a Bump, + state: State<'a>, + require_indent: bool, + indent_problem: fn(Position) -> E, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, +) -> ParseResult<'a, Loc>, E> +where + E: 'a + SpaceProblem, +{ + let min_indent = if require_indent { + state.line_indent() + 1 + } else { + 0 + }; + + let (_, loc_first_space, state) = + loc_space0_e(indent_problem).parse(arena, state, min_indent)?; + + let allow_defs = !loc_first_space.value.is_empty(); + + parse_block_inner( + options, + arena, + state, + min_indent, + indent_problem, + wrap_error, + loc_first_space, + allow_defs, + ) +} + +#[allow(clippy::too_many_arguments)] +fn parse_block_inner<'a, E>( + options: ExprParseOptions, + arena: &'a Bump, + state: State<'a>, + min_indent: u32, + indent_problem: fn(Position) -> E, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, + first_space: Loc<&'a [CommentOrNewline<'a>]>, + allow_defs: bool, +) -> ParseResult<'a, Loc>, E> +where + E: 'a + SpaceProblem, +{ + if allow_defs { + let (_, stmts, state) = parse_stmt_seq( + arena, + state, + wrap_error, + options, + min_indent, + Loc::at(first_space.region, &[]), + indent_problem, + )?; + + if stmts.is_empty() { + return Err(( + NoProgress, + wrap_error(arena.alloc(EExpr::Start(state.pos())), state.pos()), + )); + } + + let last_pos = state.pos(); + + let loc_expr = stmts_to_expr(&stmts, arena) + .map_err(|e| (MadeProgress, wrap_error(arena.alloc(e), last_pos)))?; + + let loc_expr = if first_space.value.is_empty() { + loc_expr + } else { + arena + .alloc(loc_expr.value) + .with_spaces_before(first_space.value, loc_expr.region) + }; + + Ok((MadeProgress, loc_expr, state)) + } else { + let (p2, loc_expr, state) = + specialize_err_ref(wrap_error, expr_start(options)).parse(arena, state, min_indent)?; + + let loc_expr = if first_space.value.is_empty() { + loc_expr + } else { + arena + .alloc(loc_expr.value) + .with_spaces_before(first_space.value, loc_expr.region) + }; + + Ok((p2, loc_expr, state)) + } +} + +fn parse_stmt_seq<'a, E: SpaceProblem + 'a>( + arena: &'a Bump, + mut state: State<'a>, + wrap_error: fn(&'a EExpr<'a>, Position) -> E, + options: ExprParseOptions, + min_indent: u32, + mut last_space: Loc<&'a [CommentOrNewline<'a>]>, + indent_problem: fn(Position) -> E, +) -> ParseResult<'a, Vec<'a, SpacesBefore<'a, Loc>>>, E> { + let mut stmts = Vec::new_in(arena); + let mut state_before_space = state.clone(); + loop { + if at_terminator(&state) { + state = state_before_space; + break; + } + + let loc_stmt = match specialize_err_ref(wrap_error, stmt_start(options, last_space.region)) + .parse(arena, state.clone(), min_indent) + { + Ok((_p, s, new_state)) => { + state_before_space = new_state.clone(); + state = new_state; + s + } + Err((NoProgress, _)) => { + if stmts.is_empty() { + return Err(( + NoProgress, + wrap_error(arena.alloc(EExpr::Start(state.pos())), state.pos()), + )); + } + + state = state_before_space; + break; + } + Err((MadeProgress, e)) => { + return Err((MadeProgress, e)); + } + }; + + stmts.push(SpacesBefore { + before: last_space.value, + item: loc_stmt, + }); + + match loc_space0_e(indent_problem).parse(arena, state.clone(), min_indent) { + Ok((_p, s_loc, new_state)) => { + if s_loc.value.is_empty() { + // require a newline or a terminator after the statement + if at_terminator(&new_state) { + state = state_before_space; + break; + } + + return Err(( + MadeProgress, + wrap_error(arena.alloc(EExpr::BadExprEnd(state.pos())), state.pos()), + )); + } + last_space = s_loc; + state = new_state; + } + Err(_) => { + break; + } + }; + } + Ok((MadeProgress, stmts, state)) +} + +fn at_terminator(state: &State<'_>) -> bool { + matches!( + state.bytes().first(), + None | Some(b']' | b'}' | b')' | b',') + ) +} + +fn stmts_to_expr<'a>( + stmts: &[SpacesBefore<'a, Loc>>], + arena: &'a Bump, +) -> Result>, EExpr<'a>> { + if stmts.len() > 1 { + let first_pos = stmts.first().unwrap().item.region.start(); + let last_pos = stmts.last().unwrap().item.region.end(); + + let (defs, last_expr) = stmts_to_defs(stmts, Defs::default(), true, arena)?; + + let final_expr = match last_expr { + Some(e) => e, + None => return Err(EExpr::DefMissingFinalExpr(last_pos)), + }; + + let region = Region::new(first_pos, last_pos); + + if defs.is_empty() { + Ok(final_expr) + } else { + Ok(Loc::at( + region, + Expr::Defs(arena.alloc(defs), arena.alloc(final_expr)), + )) + } + } else { + let SpacesBefore { + before: space, + item: loc_stmt, + } = *stmts.last().unwrap(); + let expr = match loc_stmt.value { + Stmt::Expr(e) => { + if space.is_empty() { + e + } else { + arena.alloc(e).before(space) + } + } + Stmt::ValueDef(ValueDef::Dbg { .. }) => { + return Err(EExpr::Dbg( + EExpect::Continuation( + arena.alloc(EExpr::IndentEnd(loc_stmt.region.end())), + loc_stmt.region.end(), + ), + loc_stmt.region.start(), + )); + } + Stmt::ValueDef(ValueDef::Expect { .. }) => { + return Err(EExpr::Expect( + EExpect::Continuation( + arena.alloc(EExpr::IndentEnd(loc_stmt.region.end())), + loc_stmt.region.end(), + ), + loc_stmt.region.start(), + )); + } + Stmt::Backpassing(..) | Stmt::TypeDef(_) | Stmt::ValueDef(_) => { + return Err(EExpr::IndentEnd(loc_stmt.region.end())) + } + }; + + Ok(loc_stmt.with_value(expr)) + } +} + +fn stmts_to_defs<'a>( + stmts: &[SpacesBefore<'a, Loc>>], + mut defs: Defs<'a>, + exprify_dbg: bool, + arena: &'a Bump, +) -> Result<(Defs<'a>, Option>>), EExpr<'a>> { + let mut last_expr = None; + let mut i = 0; + while i < stmts.len() { + let sp_stmt = stmts[i]; + match sp_stmt.item.value { + Stmt::Expr(e) => { + if is_expr_suffixed(&e) && i + 1 < stmts.len() { + defs.push_value_def( + ValueDef::Stmt(arena.alloc(Loc::at(sp_stmt.item.region, e))), + sp_stmt.item.region, + sp_stmt.before, + &[], + ); + } else { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + last_expr = Some(sp_stmt.item.with_value(e)); + } + } + Stmt::Backpassing(pats, call) => { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + if i + 1 >= stmts.len() { + return Err(EExpr::BackpassContinue(sp_stmt.item.region.end())); + } + + let rest = stmts_to_expr(&stmts[i + 1..], arena)?; + + let e = Expr::Backpassing(arena.alloc(pats), arena.alloc(call), arena.alloc(rest)); + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + let region = Region::new(sp_stmt.item.region.start(), rest.region.end()); + + last_expr = Some(Loc::at(region, e)); + + // don't re-process the rest of the statements; they got consumed by the backpassing + break; + } + + Stmt::TypeDef(td) => { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + if let ( + TypeDef::Alias { + header, + ann: ann_type, + }, + Some(( + spaces_middle, + Stmt::ValueDef(ValueDef::Body(loc_pattern, loc_def_expr)), + )), + ) = (td, stmts.get(i + 1).map(|s| (s.before, s.item.value))) + { + if spaces_middle.len() <= 1 + || header + .vars + .first() + .map(|var| var.value.equivalent(&loc_pattern.value)) + .unwrap_or(false) + { + // This is a case like + // UserId x : [UserId Int] + // UserId x = UserId 42 + // We optimistically parsed the first line as an alias; we now turn it + // into an annotation. + + let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region); + + let value_def = join_alias_to_body( + arena, + header, + ann_type, + spaces_middle, + loc_pattern, + loc_def_expr, + ); + + defs.push_value_def( + value_def, + Region::span_across(&header.name.region, ®ion), + sp_stmt.before, + &[], + ); + + i += 1; + } else { + defs.push_type_def(td, sp_stmt.item.region, sp_stmt.before, &[]) + } + } else { + defs.push_type_def(td, sp_stmt.item.region, sp_stmt.before, &[]) + } + } + Stmt::ValueDef(vd) => { + if last_expr.is_some() { + return Err(EExpr::StmtAfterExpr(sp_stmt.item.region.start())); + } + + // NOTE: it shouldn't be necessary to convert ValueDef::Dbg into an expr, but + // it turns out that ValueDef::Dbg exposes some bugs in the rest of the compiler. + // In particular, it seems that the solver thinks the dbg expr must be a bool. + if let ValueDef::Dbg { + condition, + preceding_comment: _, + } = vd + { + if exprify_dbg { + if i + 1 >= stmts.len() { + return Err(EExpr::DbgContinue(sp_stmt.item.region.end())); + } + + let rest = stmts_to_expr(&stmts[i + 1..], arena)?; + + let e = Expr::Dbg(arena.alloc(condition), arena.alloc(rest)); + + let e = if sp_stmt.before.is_empty() { + e + } else { + arena.alloc(e).before(sp_stmt.before) + }; + + last_expr = Some(Loc::at(sp_stmt.item.region, e)); + + // don't re-process the rest of the statements; they got consumed by the dbg expr + break; + } + } + + if let ( + ValueDef::Annotation(ann_pattern, ann_type), + Some(( + spaces_middle, + Stmt::ValueDef(ValueDef::Body(loc_pattern, loc_def_expr)), + )), + ) = (vd, stmts.get(i + 1).map(|s| (s.before, s.item.value))) + { + if spaces_middle.len() <= 1 || ann_pattern.value.equivalent(&loc_pattern.value) + { + let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region); + + let value_def = ValueDef::AnnotatedBody { + ann_pattern: arena.alloc(ann_pattern), + ann_type: arena.alloc(ann_type), + comment: spaces_middle + .first() // TODO: Why do we drop all but the first comment???? + .and_then(crate::ast::CommentOrNewline::comment_str), + body_pattern: loc_pattern, + body_expr: loc_def_expr, + }; + + defs.push_value_def( + value_def, + roc_region::all::Region::span_across(&ann_pattern.region, ®ion), + sp_stmt.before, + &[], + ); + i += 1; + } else { + defs.push_value_def(vd, sp_stmt.item.region, sp_stmt.before, &[]) + } + } else { + defs.push_value_def(vd, sp_stmt.item.region, sp_stmt.before, &[]) + } + } + } + + i += 1; + } + Ok((defs, last_expr)) +} + +pub fn join_alias_to_body<'a>( + arena: &'a Bump, + header: TypeHeader<'a>, + ann_type: Loc>, + spaces_middle: &'a [CommentOrNewline<'a>], + body_pattern: &'a Loc>, + body_expr: &'a Loc>, +) -> ValueDef<'a> { + let loc_name = arena.alloc(header.name.map(|x| Pattern::Tag(x))); + let ann_pattern = Pattern::Apply(loc_name, header.vars); + + let vars_region = Region::across_all(header.vars.iter().map(|v| &v.region)); + let region_ann_pattern = Region::span_across(&loc_name.region, &vars_region); + let loc_ann_pattern = Loc::at(region_ann_pattern, ann_pattern); + + ValueDef::AnnotatedBody { + ann_pattern: arena.alloc(loc_ann_pattern), + ann_type: arena.alloc(ann_type), + comment: spaces_middle + .first() // TODO: Why do we drop all but the first comment???? + .and_then(crate::ast::CommentOrNewline::comment_str), + body_pattern, + body_expr, + } +} + /// This is a helper function for parsing function args. /// The rules for (-) are special-cased, and they come up in function args. /// @@ -2973,7 +3182,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< /// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else. fn assign_or_destructure_identifier<'a>() -> impl Parser<'a, Ident<'a>, EExpr<'a>> { - crate::ident::parse_ident + parse_ident } #[allow(dead_code)] @@ -3242,6 +3451,11 @@ fn record_field_expr<'a>() -> impl Parser<'a, RecordFieldExpr<'a>, ERecord<'a>> ) } +enum RecordHelpPrefix { + Update, + Mapper, +} + fn record_prefix_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> { specialize_err( |_, pos| ERecord::Prefix(pos), @@ -3249,11 +3463,6 @@ fn record_prefix_identifier<'a>() -> impl Parser<'a, Expr<'a>, ERecord<'a>> { ) } -enum RecordHelpPrefix { - Update, - Mapper, -} - struct RecordHelp<'a> { prefix: Option<(Loc>, RecordHelpPrefix)>, fields: Collection<'a, Loc>>, @@ -3266,13 +3475,11 @@ fn record_help<'a>() -> impl Parser<'a, RecordHelp<'a>, ERecord<'a>> { // You can optionally have an identifier followed by an '&' to // make this a record update, e.g. { Foo.user & username: "blah" }. prefix: optional(backtrackable(and( - spaces_around( - // We wrap the ident in an Expr here, - // so that we have a Spaceable value to work with, - // and then in canonicalization verify that it's an Expr::Var - // (and not e.g. an `Expr::Access`) and extract its string. - loc(record_prefix_identifier()), - ), + // We wrap the ident in an Expr here, + // so that we have a Spaceable value to work with, + // and then in canonicalization verify that it's an Expr::Var + // (and not e.g. an `Expr::Access`) and extract its string. + spaces_around(loc(record_prefix_identifier())), map_with_arena( either( byte(b'&', ERecord::Ampersand), @@ -3489,8 +3696,39 @@ const BINOP_CHAR_MASK: [bool; 125] = { result }; -fn operator<'a>() -> impl Parser<'a, BinOp, EExpr<'a>> { - |_, state, _m| operator_help(EExpr::Start, EExpr::BadOperator, state) +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum OperatorOrDef { + BinOp(BinOp), + Assignment, + AliasOrOpaque(AliasOrOpaque), + Backpassing, +} + +fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> { + move |_, state: State<'a>, _m| { + let start = state.pos(); + let (_, op, state) = operator_help(EExpr::Start, EExpr::BadOperator, state)?; + let err_progress = if check_for_defs { + MadeProgress + } else { + NoProgress + }; + match op { + OperatorOrDef::BinOp(op) => Ok((MadeProgress, op, state)), + OperatorOrDef::Assignment => Err((err_progress, EExpr::BadOperator("=", start))), + OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Alias) => { + Err((err_progress, EExpr::BadOperator(":", start))) + } + OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Opaque) => { + Err((err_progress, EExpr::BadOperator(":=", start))) + } + OperatorOrDef::Backpassing => Err((err_progress, EExpr::BadOperator("<-", start))), + } + } +} + +fn operator<'a>() -> impl Parser<'a, OperatorOrDef, EExpr<'a>> { + (move |_, state, _m| operator_help(EExpr::Start, EExpr::BadOperator, state)).trace("operator") } #[inline(always)] @@ -3498,7 +3736,7 @@ fn operator_help<'a, F, G, E>( to_expectation: F, to_error: G, mut state: State<'a>, -) -> ParseResult<'a, BinOp, E> +) -> ParseResult<'a, OperatorOrDef, E> where F: Fn(Position) -> E, G: Fn(&'a str, Position) -> E, @@ -3522,34 +3760,34 @@ where match chomped { "" => Err((NoProgress, to_expectation(state.pos()))), - "+" => good!(BinOp::Plus, 1), - "-" => good!(BinOp::Minus, 1), - "*" => good!(BinOp::Star, 1), - "/" => good!(BinOp::Slash, 1), - "%" => good!(BinOp::Percent, 1), - "^" => good!(BinOp::Caret, 1), - ">" => good!(BinOp::GreaterThan, 1), - "<" => good!(BinOp::LessThan, 1), + "+" => good!(OperatorOrDef::BinOp(BinOp::Plus), 1), + "-" => good!(OperatorOrDef::BinOp(BinOp::Minus), 1), + "*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1), + "/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1), + "%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1), + "^" => good!(OperatorOrDef::BinOp(BinOp::Caret), 1), + ">" => good!(OperatorOrDef::BinOp(BinOp::GreaterThan), 1), + "<" => good!(OperatorOrDef::BinOp(BinOp::LessThan), 1), "." => { // a `.` makes no progress, so it does not interfere with `.foo` access(or) Err((NoProgress, to_error(".", state.pos()))) } - "=" => good!(BinOp::Assignment, 1), - ":=" => good!(BinOp::IsOpaqueType, 2), - ":" => good!(BinOp::IsAliasType, 1), - "|>" => good!(BinOp::Pizza, 2), - "==" => good!(BinOp::Equals, 2), - "!=" => good!(BinOp::NotEquals, 2), - ">=" => good!(BinOp::GreaterThanOrEq, 2), - "<=" => good!(BinOp::LessThanOrEq, 2), - "&&" => good!(BinOp::And, 2), - "||" => good!(BinOp::Or, 2), - "//" => good!(BinOp::DoubleSlash, 2), + "=" => good!(OperatorOrDef::Assignment, 1), + ":=" => good!(OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Opaque), 2), + ":" => good!(OperatorOrDef::AliasOrOpaque(AliasOrOpaque::Alias), 1), + "|>" => good!(OperatorOrDef::BinOp(BinOp::Pizza), 2), + "==" => good!(OperatorOrDef::BinOp(BinOp::Equals), 2), + "!=" => good!(OperatorOrDef::BinOp(BinOp::NotEquals), 2), + ">=" => good!(OperatorOrDef::BinOp(BinOp::GreaterThanOrEq), 2), + "<=" => good!(OperatorOrDef::BinOp(BinOp::LessThanOrEq), 2), + "&&" => good!(OperatorOrDef::BinOp(BinOp::And), 2), + "||" => good!(OperatorOrDef::BinOp(BinOp::Or), 2), + "//" => good!(OperatorOrDef::BinOp(BinOp::DoubleSlash), 2), "->" => { // makes no progress, so it does not interfere with `_ if isGood -> ...` Err((NoProgress, to_error("->", state.pos()))) } - "<-" => good!(BinOp::Backpassing, 2), + "<-" => good!(OperatorOrDef::Backpassing, 2), "!" => Err((NoProgress, to_error("!", state.pos()))), _ => bad_made_progress!(chomped), } diff --git a/crates/compiler/parse/src/lib.rs b/crates/compiler/parse/src/lib.rs index 007f933eff..dc8a9a3aaa 100644 --- a/crates/compiler/parse/src/lib.rs +++ b/crates/compiler/parse/src/lib.rs @@ -17,6 +17,7 @@ pub mod module; pub mod number_literal; pub mod pattern; pub mod problems; +pub mod remove_spaces; pub mod src64; pub mod state; pub mod string_literal; diff --git a/crates/compiler/parse/src/parser.rs b/crates/compiler/parse/src/parser.rs index 57ba95dbd2..7eec1b83e9 100644 --- a/crates/compiler/parse/src/parser.rs +++ b/crates/compiler/parse/src/parser.rs @@ -330,6 +330,7 @@ pub enum EExpr<'a> { Start(Position), End(Position), BadExprEnd(Position), + StmtAfterExpr(Position), Space(BadInputError, Position), Dot(Position), @@ -355,6 +356,8 @@ pub enum EExpr<'a> { QualifiedTag(Position), BackpassComma(Position), BackpassArrow(Position), + BackpassContinue(Position), + DbgContinue(Position), When(EWhen<'a>, Position), If(EIf<'a>, Position), @@ -383,6 +386,7 @@ pub enum EExpr<'a> { IndentEnd(Position), UnexpectedComma(Position), + UnexpectedTopLevelExpr(Position), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -851,8 +855,9 @@ where let cur_indent = INDENT.with(|i| *i.borrow()); println!( - "{:<5?}: {}{:<50}", + "{:<5?}:{:<2} {}{:<50}", state.pos(), + min_indent, &indent_text[..cur_indent * 2], self.message ); @@ -868,8 +873,9 @@ where }; println!( - "{:<5?}: {}{:<50} {:<15} {:?}", + "{:<5?}:{:<2} {}{:<50} {:<15} {:?}", state.pos(), + min_indent, &indent_text[..cur_indent * 2], self.message, format!("{:?}", progress), diff --git a/crates/compiler/parse/src/remove_spaces.rs b/crates/compiler/parse/src/remove_spaces.rs new file mode 100644 index 0000000000..9670578323 --- /dev/null +++ b/crates/compiler/parse/src/remove_spaces.rs @@ -0,0 +1,1749 @@ +use bumpalo::collections::Vec; +use bumpalo::Bump; +use roc_module::called_via::{BinOp, UnaryOp}; +use roc_region::all::{Loc, Position, Region}; + +use crate::{ + ast::{ + AbilityImpls, AbilityMember, AssignedField, Collection, Defs, Expr, Header, Implements, + ImplementsAbilities, ImplementsAbility, ImplementsClause, ImportAlias, ImportAsKeyword, + ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, + Module, ModuleImport, ModuleImportParams, OldRecordBuilderField, Pattern, PatternAs, + Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, + WhenBranch, + }, + header::{ + AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword, HostedHeader, ImportsEntry, + ImportsKeyword, KeywordItem, ModuleHeader, ModuleName, ModuleParams, PackageEntry, + PackageHeader, PackageKeyword, PackageName, PackagesKeyword, PlatformHeader, + PlatformKeyword, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, + ToKeyword, TypedIdent, WithKeyword, + }, + ident::{BadIdent, UppercaseIdent}, + parser::{ + EAbility, EClosure, EExpect, EExposes, EExpr, EGenerates, EGeneratesWith, EHeader, EIf, + EImport, EImportParams, EImports, EInParens, EList, EPackageEntry, EPackageName, EPackages, + EParams, EPattern, EProvides, ERecord, ERequires, EString, EType, ETypeAbilityImpl, + ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ETypedIdent, + EWhen, PInParens, PList, PRecord, SyntaxError, + }, +}; + +/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting. +/// +/// Currently this consists of: +/// * Removing newlines +/// * Removing comments +/// * Removing parens in Exprs +/// +/// Long term, we actually want this transform to preserve comments (so we can assert they're maintained by formatting) +/// - but there are currently several bugs where they're _not_ preserved. +/// TODO: ensure formatting retains comments +pub trait RemoveSpaces<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self; +} + +macro_rules! keywords { + ($($name:ident),* $(,)?) => { + $( + impl<'a> RemoveSpaces<'a> for $name { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } + } + )* + } +} + +keywords! { + ExposesKeyword, + ImportsKeyword, + WithKeyword, + GeneratesKeyword, + PackageKeyword, + PackagesKeyword, + RequiresKeyword, + ProvidesKeyword, + ToKeyword, + PlatformKeyword, +} + +impl<'a> RemoveSpaces<'a> for Defs<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let mut defs = self.clone(); + + defs.spaces.clear(); + defs.space_before.clear(); + defs.space_after.clear(); + + for type_def in defs.type_defs.iter_mut() { + *type_def = type_def.remove_spaces(arena); + } + + for value_def in defs.value_defs.iter_mut() { + *value_def = value_def.remove_spaces(arena); + } + + for region_def in defs.regions.iter_mut() { + *region_def = region_def.remove_spaces(arena); + } + + defs + } +} + +impl<'a, V: RemoveSpaces<'a>> RemoveSpaces<'a> for Spaces<'a, V> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + Spaces { + before: &[], + item: self.item.remove_spaces(arena), + after: &[], + } + } +} + +impl<'a, K: RemoveSpaces<'a>, V: RemoveSpaces<'a>> RemoveSpaces<'a> for KeywordItem<'a, K, V> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + KeywordItem { + keyword: self.keyword.remove_spaces(arena), + item: self.item.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ProvidesTo<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ProvidesTo { + provides_keyword: self.provides_keyword.remove_spaces(arena), + entries: self.entries.remove_spaces(arena), + types: self.types.remove_spaces(arena), + to_keyword: self.to_keyword.remove_spaces(arena), + to: self.to.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Module<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let header = match &self.header { + Header::Module(header) => Header::Module(ModuleHeader { + after_keyword: &[], + params: header.params.remove_spaces(arena), + exposes: header.exposes.remove_spaces(arena), + interface_imports: header.interface_imports.remove_spaces(arena), + }), + Header::App(header) => Header::App(AppHeader { + before_provides: &[], + provides: header.provides.remove_spaces(arena), + before_packages: &[], + packages: header.packages.remove_spaces(arena), + old_imports: header.old_imports.remove_spaces(arena), + old_provides_to_new_package: header + .old_provides_to_new_package + .remove_spaces(arena), + }), + Header::Package(header) => Header::Package(PackageHeader { + before_exposes: &[], + exposes: header.exposes.remove_spaces(arena), + before_packages: &[], + packages: header.packages.remove_spaces(arena), + }), + Header::Platform(header) => Header::Platform(PlatformHeader { + before_name: &[], + name: header.name.remove_spaces(arena), + requires: header.requires.remove_spaces(arena), + exposes: header.exposes.remove_spaces(arena), + packages: header.packages.remove_spaces(arena), + imports: header.imports.remove_spaces(arena), + provides: header.provides.remove_spaces(arena), + }), + Header::Hosted(header) => Header::Hosted(HostedHeader { + before_name: &[], + name: header.name.remove_spaces(arena), + exposes: header.exposes.remove_spaces(arena), + imports: header.imports.remove_spaces(arena), + generates: header.generates.remove_spaces(arena), + generates_with: header.generates_with.remove_spaces(arena), + }), + }; + Module { + comments: &[], + header, + } + } +} + +impl<'a> RemoveSpaces<'a> for ModuleParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ModuleParams { + params: self.params.remove_spaces(arena), + before_arrow: &[], + after_arrow: &[], + } + } +} + +impl<'a> RemoveSpaces<'a> for Region { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + Region::zero() + } +} + +impl<'a> RemoveSpaces<'a> for &'a str { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + self + } +} + +impl<'a, T: RemoveSpaces<'a> + Copy> RemoveSpaces<'a> for Spaced<'a, T> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Spaced::Item(a) => Spaced::Item(a.remove_spaces(arena)), + Spaced::SpaceBefore(a, _) => a.remove_spaces(arena), + Spaced::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ExposedName<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for ModuleName<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for PackageName<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for To<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + To::ExistingPackage(a) => To::ExistingPackage(a), + To::NewPackage(a) => To::NewPackage(a.remove_spaces(arena)), + } + } +} + +impl<'a> RemoveSpaces<'a> for TypedIdent<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + TypedIdent { + ident: self.ident.remove_spaces(arena), + spaces_before_colon: &[], + ann: self.ann.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for PlatformRequires<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + PlatformRequires { + rigids: self.rigids.remove_spaces(arena), + signature: self.signature.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for UppercaseIdent<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for PackageEntry<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + PackageEntry { + shorthand: self.shorthand, + spaces_after_shorthand: &[], + platform_marker: match self.platform_marker { + Some(_) => Some(&[]), + None => None, + }, + package_name: self.package_name.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImportsEntry<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + ImportsEntry::Module(a, b) => ImportsEntry::Module(a, b.remove_spaces(arena)), + ImportsEntry::Package(a, b, c) => ImportsEntry::Package(a, b, c.remove_spaces(arena)), + ImportsEntry::IngestedFile(a, b) => { + ImportsEntry::IngestedFile(a, b.remove_spaces(arena)) + } + } + } +} + +impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Option { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + self.as_ref().map(|a| a.remove_spaces(arena)) + } +} + +impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for Loc { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let res = self.value.remove_spaces(arena); + Loc::at(Region::zero(), res) + } +} + +impl<'a, A: RemoveSpaces<'a>, B: RemoveSpaces<'a>> RemoveSpaces<'a> for (A, B) { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + (self.0.remove_spaces(arena), self.1.remove_spaces(arena)) + } +} + +impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for Collection<'a, T> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let mut items = Vec::with_capacity_in(self.items.len(), arena); + for item in self.items { + items.push(item.remove_spaces(arena)); + } + Collection::with_items(items.into_bump_slice()) + } +} + +impl<'a, T: RemoveSpaces<'a> + std::fmt::Debug> RemoveSpaces<'a> for &'a [T] { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + let mut items = Vec::with_capacity_in(self.len(), arena); + for item in *self { + let res = item.remove_spaces(arena); + items.push(res); + } + items.into_bump_slice() + } +} + +impl<'a> RemoveSpaces<'a> for UnaryOp { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for BinOp { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a, T: RemoveSpaces<'a>> RemoveSpaces<'a> for &'a T { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + arena.alloc((*self).remove_spaces(arena)) + } +} + +impl<'a> RemoveSpaces<'a> for TypeDef<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + use TypeDef::*; + + match *self { + Alias { + header: TypeHeader { name, vars }, + ann, + } => Alias { + header: TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + ann: ann.remove_spaces(arena), + }, + Opaque { + header: TypeHeader { name, vars }, + typ, + derived, + } => Opaque { + header: TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + typ: typ.remove_spaces(arena), + derived: derived.remove_spaces(arena), + }, + Ability { + header: TypeHeader { name, vars }, + loc_implements: loc_has, + members, + } => Ability { + header: TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + loc_implements: loc_has.remove_spaces(arena), + members: members.remove_spaces(arena), + }, + } + } +} + +impl<'a> RemoveSpaces<'a> for ValueDef<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + use ValueDef::*; + + match *self { + Annotation(a, b) => Annotation(a.remove_spaces(arena), b.remove_spaces(arena)), + Body(a, b) => Body( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + AnnotatedBody { + ann_pattern, + ann_type, + comment: _, + body_pattern, + body_expr, + } => AnnotatedBody { + ann_pattern: arena.alloc(ann_pattern.remove_spaces(arena)), + ann_type: arena.alloc(ann_type.remove_spaces(arena)), + comment: None, + body_pattern: arena.alloc(body_pattern.remove_spaces(arena)), + body_expr: arena.alloc(body_expr.remove_spaces(arena)), + }, + Dbg { + condition, + preceding_comment: _, + } => Dbg { + condition: arena.alloc(condition.remove_spaces(arena)), + preceding_comment: Region::zero(), + }, + Expect { + condition, + preceding_comment: _, + } => Expect { + condition: arena.alloc(condition.remove_spaces(arena)), + preceding_comment: Region::zero(), + }, + ExpectFx { + condition, + preceding_comment: _, + } => ExpectFx { + condition: arena.alloc(condition.remove_spaces(arena)), + preceding_comment: Region::zero(), + }, + ModuleImport(module_import) => ModuleImport(module_import.remove_spaces(arena)), + IngestedFileImport(ingested_file_import) => { + IngestedFileImport(ingested_file_import.remove_spaces(arena)) + } + Stmt(loc_expr) => Stmt(arena.alloc(loc_expr.remove_spaces(arena))), + } + } +} + +impl<'a> RemoveSpaces<'a> for ModuleImport<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ModuleImport { + before_name: &[], + name: self.name.remove_spaces(arena), + params: self.params.remove_spaces(arena), + alias: self.alias.remove_spaces(arena), + exposed: self.exposed.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ModuleImportParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ModuleImportParams { + before: &[], + params: self.params.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for IngestedFileImport<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + IngestedFileImport { + before_path: &[], + path: self.path.remove_spaces(arena), + name: self.name.remove_spaces(arena), + annotation: self.annotation.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImportedModuleName<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ImportedModuleName { + package: self.package.remove_spaces(arena), + name: self.name.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImportAlias<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for ImportAsKeyword { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for ImportExposingKeyword { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + *self + } +} + +impl<'a> RemoveSpaces<'a> for IngestedFileAnnotation<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + IngestedFileAnnotation { + before_colon: &[], + annotation: self.annotation.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Implements<'a> { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + Implements::Implements + } +} + +impl<'a> RemoveSpaces<'a> for AbilityMember<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + AbilityMember { + name: self.name.remove_spaces(arena), + typ: self.typ.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for WhenBranch<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + WhenBranch { + patterns: self.patterns.remove_spaces(arena), + value: self.value.remove_spaces(arena), + guard: self.guard.remove_spaces(arena), + } + } +} + +impl<'a, T: RemoveSpaces<'a> + Copy + std::fmt::Debug> RemoveSpaces<'a> for AssignedField<'a, T> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + AssignedField::RequiredValue(a, _, c) => AssignedField::RequiredValue( + a.remove_spaces(arena), + arena.alloc([]), + arena.alloc(c.remove_spaces(arena)), + ), + AssignedField::OptionalValue(a, _, c) => AssignedField::OptionalValue( + a.remove_spaces(arena), + arena.alloc([]), + arena.alloc(c.remove_spaces(arena)), + ), + AssignedField::LabelOnly(a) => AssignedField::LabelOnly(a.remove_spaces(arena)), + AssignedField::Malformed(a) => AssignedField::Malformed(a), + AssignedField::SpaceBefore(a, _) => a.remove_spaces(arena), + AssignedField::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for OldRecordBuilderField<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + OldRecordBuilderField::Value(a, _, c) => OldRecordBuilderField::Value( + a.remove_spaces(arena), + &[], + arena.alloc(c.remove_spaces(arena)), + ), + OldRecordBuilderField::ApplyValue(a, _, _, c) => OldRecordBuilderField::ApplyValue( + a.remove_spaces(arena), + &[], + &[], + arena.alloc(c.remove_spaces(arena)), + ), + OldRecordBuilderField::LabelOnly(a) => { + OldRecordBuilderField::LabelOnly(a.remove_spaces(arena)) + } + OldRecordBuilderField::Malformed(a) => OldRecordBuilderField::Malformed(a), + OldRecordBuilderField::SpaceBefore(a, _) => a.remove_spaces(arena), + OldRecordBuilderField::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for StrLiteral<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t), + StrLiteral::Line(t) => StrLiteral::Line(t.remove_spaces(arena)), + StrLiteral::Block(t) => StrLiteral::Block(t.remove_spaces(arena)), + } + } +} + +impl<'a> RemoveSpaces<'a> for StrSegment<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + StrSegment::Plaintext(t) => StrSegment::Plaintext(t), + StrSegment::Unicode(t) => StrSegment::Unicode(t.remove_spaces(arena)), + StrSegment::EscapedChar(c) => StrSegment::EscapedChar(c), + StrSegment::Interpolated(t) => StrSegment::Interpolated(t.remove_spaces(arena)), + StrSegment::DeprecatedInterpolated(t) => { + StrSegment::DeprecatedInterpolated(t.remove_spaces(arena)) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for Expr<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Expr::Float(a) => Expr::Float(a), + Expr::Num(a) => Expr::Num(a), + Expr::NonBase10Int { + string, + base, + is_negative, + } => Expr::NonBase10Int { + string, + base, + is_negative, + }, + Expr::Str(a) => Expr::Str(a.remove_spaces(arena)), + Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b), + Expr::AccessorFunction(a) => Expr::AccessorFunction(a), + Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b), + Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))), + Expr::List(a) => Expr::List(a.remove_spaces(arena)), + Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { + update: arena.alloc(update.remove_spaces(arena)), + fields: fields.remove_spaces(arena), + }, + Expr::Record(a) => Expr::Record(a.remove_spaces(arena)), + Expr::OldRecordBuilder(a) => Expr::OldRecordBuilder(a.remove_spaces(arena)), + Expr::RecordBuilder { mapper, fields } => Expr::RecordBuilder { + mapper: arena.alloc(mapper.remove_spaces(arena)), + fields: fields.remove_spaces(arena), + }, + Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)), + Expr::Var { module_name, ident } => Expr::Var { module_name, ident }, + Expr::Underscore(a) => Expr::Underscore(a), + Expr::Tag(a) => Expr::Tag(a), + Expr::OpaqueRef(a) => Expr::OpaqueRef(a), + Expr::Closure(a, b) => Expr::Closure( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::Crash => Expr::Crash, + Expr::Defs(a, b) => { + let mut defs = a.clone(); + defs.space_before = vec![Default::default(); defs.len()]; + defs.space_after = vec![Default::default(); defs.len()]; + defs.regions = vec![Region::zero(); defs.len()]; + defs.spaces.clear(); + + for type_def in defs.type_defs.iter_mut() { + *type_def = type_def.remove_spaces(arena); + } + + for value_def in defs.value_defs.iter_mut() { + *value_def = value_def.remove_spaces(arena); + } + + Expr::Defs(arena.alloc(defs), arena.alloc(b.remove_spaces(arena))) + } + Expr::Backpassing(a, b, c) => Expr::Backpassing( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + arena.alloc(c.remove_spaces(arena)), + ), + Expr::Expect(a, b) => Expr::Expect( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::Dbg(a, b) => Expr::Dbg( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::LowLevelDbg(x, a, b) => Expr::LowLevelDbg( + x, + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Expr::Apply(a, b, c) => Expr::Apply( + arena.alloc(a.remove_spaces(arena)), + b.remove_spaces(arena), + c, + ), + Expr::BinOps(a, b) => { + Expr::BinOps(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))) + } + Expr::UnaryOp(a, b) => { + Expr::UnaryOp(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) + } + Expr::If(a, b) => Expr::If(a.remove_spaces(arena), arena.alloc(b.remove_spaces(arena))), + Expr::When(a, b) => { + Expr::When(arena.alloc(a.remove_spaces(arena)), b.remove_spaces(arena)) + } + Expr::ParensAround(a) => { + // The formatter can remove redundant parentheses, so also remove these when normalizing for comparison. + a.remove_spaces(arena) + } + Expr::MalformedIdent(a, b) => Expr::MalformedIdent(a, remove_spaces_bad_ident(b)), + Expr::MalformedClosure => Expr::MalformedClosure, + Expr::MalformedSuffixed(a) => Expr::MalformedSuffixed(a), + Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a), + Expr::SpaceBefore(a, _) => a.remove_spaces(arena), + Expr::SpaceAfter(a, _) => a.remove_spaces(arena), + Expr::SingleQuote(a) => Expr::Num(a), + Expr::MultipleOldRecordBuilders(a) => { + Expr::MultipleOldRecordBuilders(arena.alloc(a.remove_spaces(arena))) + } + Expr::UnappliedOldRecordBuilder(a) => { + Expr::UnappliedOldRecordBuilder(arena.alloc(a.remove_spaces(arena))) + } + Expr::EmptyRecordBuilder(a) => { + Expr::EmptyRecordBuilder(arena.alloc(a.remove_spaces(arena))) + } + Expr::SingleFieldRecordBuilder(a) => { + Expr::SingleFieldRecordBuilder(arena.alloc(a.remove_spaces(arena))) + } + Expr::OptionalFieldInRecordBuilder(a, b) => Expr::OptionalFieldInRecordBuilder( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + } + } +} + +fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent { + match ident { + BadIdent::Start(_) => BadIdent::Start(Position::zero()), + BadIdent::Space(e, _) => BadIdent::Space(e, Position::zero()), + BadIdent::UnderscoreAlone(_) => BadIdent::UnderscoreAlone(Position::zero()), + BadIdent::UnderscoreInMiddle(_) => BadIdent::UnderscoreInMiddle(Position::zero()), + BadIdent::UnderscoreAtStart { + position: _, + declaration_region, + } => BadIdent::UnderscoreAtStart { + position: Position::zero(), + declaration_region, + }, + BadIdent::QualifiedTag(_) => BadIdent::QualifiedTag(Position::zero()), + BadIdent::WeirdAccessor(_) => BadIdent::WeirdAccessor(Position::zero()), + BadIdent::WeirdDotAccess(_) => BadIdent::WeirdDotAccess(Position::zero()), + BadIdent::WeirdDotQualified(_) => BadIdent::WeirdDotQualified(Position::zero()), + BadIdent::StrayDot(_) => BadIdent::StrayDot(Position::zero()), + BadIdent::BadOpaqueRef(_) => BadIdent::BadOpaqueRef(Position::zero()), + BadIdent::QualifiedTupleAccessor(_) => BadIdent::QualifiedTupleAccessor(Position::zero()), + } +} + +impl<'a> RemoveSpaces<'a> for Pattern<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Pattern::Identifier { ident } => Pattern::Identifier { ident }, + Pattern::Tag(a) => Pattern::Tag(a), + Pattern::OpaqueRef(a) => Pattern::OpaqueRef(a), + Pattern::Apply(a, b) => Pattern::Apply( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + Pattern::RecordDestructure(a) => Pattern::RecordDestructure(a.remove_spaces(arena)), + Pattern::RequiredField(a, b) => { + Pattern::RequiredField(a, arena.alloc(b.remove_spaces(arena))) + } + Pattern::OptionalField(a, b) => { + Pattern::OptionalField(a, arena.alloc(b.remove_spaces(arena))) + } + Pattern::As(pattern, pattern_as) => Pattern::As( + arena.alloc(pattern.remove_spaces(arena)), + pattern_as.remove_spaces(arena), + ), + Pattern::NumLiteral(a) => Pattern::NumLiteral(a), + Pattern::NonBase10Literal { + string, + base, + is_negative, + } => Pattern::NonBase10Literal { + string, + base, + is_negative, + }, + Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a), + Pattern::StrLiteral(a) => Pattern::StrLiteral(a), + Pattern::Underscore(a) => Pattern::Underscore(a), + Pattern::Malformed(a) => Pattern::Malformed(a), + Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)), + Pattern::QualifiedIdentifier { module_name, ident } => { + Pattern::QualifiedIdentifier { module_name, ident } + } + Pattern::SpaceBefore(a, _) => a.remove_spaces(arena), + Pattern::SpaceAfter(a, _) => a.remove_spaces(arena), + Pattern::SingleQuote(a) => Pattern::SingleQuote(a), + Pattern::List(pats) => Pattern::List(pats.remove_spaces(arena)), + Pattern::Tuple(pats) => Pattern::Tuple(pats.remove_spaces(arena)), + Pattern::ListRest(opt_pattern_as) => Pattern::ListRest( + opt_pattern_as + .map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))), + ), + } + } +} + +impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + TypeAnnotation::Function(a, b) => TypeAnnotation::Function( + arena.alloc(a.remove_spaces(arena)), + arena.alloc(b.remove_spaces(arena)), + ), + TypeAnnotation::Apply(a, b, c) => TypeAnnotation::Apply(a, b, c.remove_spaces(arena)), + TypeAnnotation::BoundVariable(a) => TypeAnnotation::BoundVariable(a), + TypeAnnotation::As(a, _, TypeHeader { name, vars }) => TypeAnnotation::As( + arena.alloc(a.remove_spaces(arena)), + &[], + TypeHeader { + name: name.remove_spaces(arena), + vars: vars.remove_spaces(arena), + }, + ), + TypeAnnotation::Tuple { elems: fields, ext } => TypeAnnotation::Tuple { + elems: fields.remove_spaces(arena), + ext: ext.remove_spaces(arena), + }, + TypeAnnotation::Record { fields, ext } => TypeAnnotation::Record { + fields: fields.remove_spaces(arena), + ext: ext.remove_spaces(arena), + }, + TypeAnnotation::TagUnion { ext, tags } => TypeAnnotation::TagUnion { + ext: ext.remove_spaces(arena), + tags: tags.remove_spaces(arena), + }, + TypeAnnotation::Inferred => TypeAnnotation::Inferred, + TypeAnnotation::Wildcard => TypeAnnotation::Wildcard, + TypeAnnotation::Where(annot, has_clauses) => TypeAnnotation::Where( + arena.alloc(annot.remove_spaces(arena)), + arena.alloc(has_clauses.remove_spaces(arena)), + ), + TypeAnnotation::SpaceBefore(a, _) => a.remove_spaces(arena), + TypeAnnotation::SpaceAfter(a, _) => a.remove_spaces(arena), + TypeAnnotation::Malformed(a) => TypeAnnotation::Malformed(a), + } + } +} + +impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + ImplementsClause { + var: self.var.remove_spaces(arena), + abilities: self.abilities.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for Tag<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + Tag::Apply { name, args } => Tag::Apply { + name: name.remove_spaces(arena), + args: args.remove_spaces(arena), + }, + Tag::Malformed(a) => Tag::Malformed(a), + Tag::SpaceBefore(a, _) => a.remove_spaces(arena), + Tag::SpaceAfter(a, _) => a.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + AbilityImpls::AbilityImpls(impls) => { + AbilityImpls::AbilityImpls(impls.remove_spaces(arena)) + } + AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => { + has.remove_spaces(arena) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + ImplementsAbility::ImplementsAbility { ability, impls } => { + ImplementsAbility::ImplementsAbility { + ability: ability.remove_spaces(arena), + impls: impls.remove_spaces(arena), + } + } + ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => { + has.remove_spaces(arena) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match *self { + ImplementsAbilities::Implements(derived) => { + ImplementsAbilities::Implements(derived.remove_spaces(arena)) + } + ImplementsAbilities::SpaceBefore(derived, _) + | ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for PatternAs<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + PatternAs { + spaces_before: &[], + identifier: self.identifier.remove_spaces(arena), + } + } +} + +impl<'a> RemoveSpaces<'a> for EExpr<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EExpr::TrailingOperator(_pos) => EExpr::TrailingOperator(Position::zero()), + EExpr::Start(_pos) => EExpr::Start(Position::zero()), + EExpr::End(_pos) => EExpr::End(Position::zero()), + EExpr::BadExprEnd(_pos) => EExpr::BadExprEnd(Position::zero()), + EExpr::Space(inner_err, _pos) => EExpr::Space(*inner_err, Position::zero()), + EExpr::Dot(_pos) => EExpr::Dot(Position::zero()), + EExpr::Access(_pos) => EExpr::Access(Position::zero()), + EExpr::UnaryNot(_pos) => EExpr::UnaryNot(Position::zero()), + EExpr::UnaryNegate(_pos) => EExpr::UnaryNegate(Position::zero()), + EExpr::BadOperator(inner_err, _pos) => EExpr::BadOperator( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpr::DefMissingFinalExpr(_pos) => EExpr::DefMissingFinalExpr(Position::zero()), + EExpr::DefMissingFinalExpr2(inner_err, _pos) => EExpr::DefMissingFinalExpr2( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpr::Type(inner_err, _pos) => { + EExpr::Type(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Pattern(inner_err, _pos) => EExpr::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpr::Ability(inner_err, _pos) => { + EExpr::Ability(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::IndentDefBody(_pos) => EExpr::IndentDefBody(Position::zero()), + EExpr::IndentEquals(_pos) => EExpr::IndentEquals(Position::zero()), + EExpr::IndentAnnotation(_pos) => EExpr::IndentAnnotation(Position::zero()), + EExpr::Equals(_pos) => EExpr::Equals(Position::zero()), + EExpr::Colon(_pos) => EExpr::Colon(Position::zero()), + EExpr::DoubleColon(_pos) => EExpr::DoubleColon(Position::zero()), + EExpr::Ident(_pos) => EExpr::Ident(Position::zero()), + EExpr::ElmStyleFunction(_region, _pos) => { + EExpr::ElmStyleFunction(Region::zero(), Position::zero()) + } + EExpr::MalformedPattern(_pos) => EExpr::MalformedPattern(Position::zero()), + EExpr::QualifiedTag(_pos) => EExpr::QualifiedTag(Position::zero()), + EExpr::BackpassComma(_pos) => EExpr::BackpassComma(Position::zero()), + EExpr::BackpassArrow(_pos) => EExpr::BackpassArrow(Position::zero()), + EExpr::BackpassContinue(_pos) => EExpr::BackpassContinue(Position::zero()), + EExpr::DbgContinue(_pos) => EExpr::DbgContinue(Position::zero()), + EExpr::When(inner_err, _pos) => { + EExpr::When(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::If(inner_err, _pos) => { + EExpr::If(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Expect(inner_err, _pos) => { + EExpr::Expect(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Dbg(inner_err, _pos) => { + EExpr::Dbg(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Import(inner_err, _pos) => { + EExpr::Import(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Closure(inner_err, _pos) => { + EExpr::Closure(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Underscore(_pos) => EExpr::Underscore(Position::zero()), + EExpr::Crash(_pos) => EExpr::Crash(Position::zero()), + EExpr::InParens(inner_err, _pos) => { + EExpr::InParens(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Record(inner_err, _pos) => { + EExpr::Record(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::OptionalValueInRecordBuilder(_pos) => { + EExpr::OptionalValueInRecordBuilder(Region::zero()) + } + EExpr::Str(inner_err, _pos) => { + EExpr::Str(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::Number(inner_err, _pos) => EExpr::Number(inner_err.clone(), Position::zero()), + EExpr::List(inner_err, _pos) => { + EExpr::List(inner_err.remove_spaces(arena), Position::zero()) + } + EExpr::IndentStart(_pos) => EExpr::IndentStart(Position::zero()), + EExpr::IndentEnd(_pos) => EExpr::IndentEnd(Position::zero()), + EExpr::UnexpectedComma(_pos) => EExpr::UnexpectedComma(Position::zero()), + EExpr::UnexpectedTopLevelExpr(_pos) => EExpr::UnexpectedTopLevelExpr(Position::zero()), + EExpr::StmtAfterExpr(_pos) => EExpr::StmtAfterExpr(Position::zero()), + EExpr::RecordUpdateAccumulator(_) => EExpr::RecordUpdateAccumulator(Region::zero()), + EExpr::RecordBuilderAccumulator(_) => EExpr::RecordBuilderAccumulator(Region::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EList<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EList::Open(_pos) => EList::Open(Position::zero()), + EList::End(_pos) => EList::End(Position::zero()), + EList::Space(inner_err, _pos) => EList::Space(*inner_err, Position::zero()), + EList::Expr(inner_err, _pos) => EList::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + } + } +} + +impl<'a> RemoveSpaces<'a> for EString<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EString::Open(_) => EString::Open(Position::zero()), + EString::CodePtOpen(_) => EString::CodePtOpen(Position::zero()), + EString::CodePtEnd(_) => EString::CodePtEnd(Position::zero()), + EString::InvalidSingleQuote(inner, _) => { + EString::InvalidSingleQuote(*inner, Position::zero()) + } + EString::Space(inner, _) => EString::Space(*inner, Position::zero()), + EString::EndlessSingleLine(_) => EString::EndlessSingleLine(Position::zero()), + EString::EndlessMultiLine(_) => EString::EndlessMultiLine(Position::zero()), + EString::EndlessSingleQuote(_) => EString::EndlessSingleQuote(Position::zero()), + EString::UnknownEscape(_) => EString::UnknownEscape(Position::zero()), + EString::Format(inner, _) => { + EString::Format(arena.alloc(inner.remove_spaces(arena)), Position::zero()) + } + EString::FormatEnd(_) => EString::FormatEnd(Position::zero()), + EString::MultilineInsufficientIndent(_) => { + EString::MultilineInsufficientIndent(Position::zero()) + } + EString::ExpectedDoubleQuoteGotSingleQuote(_) => { + EString::ExpectedDoubleQuoteGotSingleQuote(Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EClosure<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EClosure::Space(inner_err, _) => EClosure::Space(*inner_err, Position::zero()), + EClosure::Start(_) => EClosure::Start(Position::zero()), + EClosure::Arrow(_) => EClosure::Arrow(Position::zero()), + EClosure::Comma(_) => EClosure::Comma(Position::zero()), + EClosure::Arg(_) => EClosure::Arg(Position::zero()), + EClosure::Pattern(inner_err, _) => { + EClosure::Pattern(inner_err.remove_spaces(arena), Position::zero()) + } + EClosure::Body(inner_err, _) => EClosure::Body( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()), + EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()), + EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EInParens<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EInParens::End(_) => EInParens::End(Position::zero()), + EInParens::Open(_) => EInParens::Open(Position::zero()), + EInParens::Empty(_) => EInParens::Empty(Position::zero()), + EInParens::Expr(inner_err, _) => EInParens::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EInParens::Space(inner_err, _) => EInParens::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ERecord<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ERecord::End(_) => ERecord::End(Position::zero()), + ERecord::Open(_) => ERecord::Open(Position::zero()), + ERecord::Field(_pos) => ERecord::Field(Position::zero()), + ERecord::Colon(_) => ERecord::Colon(Position::zero()), + ERecord::QuestionMark(_) => ERecord::QuestionMark(Position::zero()), + ERecord::Arrow(_) => ERecord::Arrow(Position::zero()), + ERecord::Ampersand(_) => ERecord::Ampersand(Position::zero()), + ERecord::Expr(inner_err, _) => ERecord::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ERecord::Space(inner_err, _) => ERecord::Space(*inner_err, Position::zero()), + ERecord::Prefix(_) => ERecord::Prefix(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPattern<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPattern::Record(inner_err, _) => { + EPattern::Record(inner_err.remove_spaces(arena), Position::zero()) + } + EPattern::List(inner_err, _) => { + EPattern::List(inner_err.remove_spaces(arena), Position::zero()) + } + EPattern::AsKeyword(_) => EPattern::AsKeyword(Position::zero()), + EPattern::AsIdentifier(_) => EPattern::AsIdentifier(Position::zero()), + EPattern::Underscore(_) => EPattern::Underscore(Position::zero()), + EPattern::NotAPattern(_) => EPattern::NotAPattern(Position::zero()), + EPattern::Start(_) => EPattern::Start(Position::zero()), + EPattern::End(_) => EPattern::End(Position::zero()), + EPattern::Space(inner_err, _) => EPattern::Space(*inner_err, Position::zero()), + EPattern::PInParens(inner_err, _) => { + EPattern::PInParens(inner_err.remove_spaces(arena), Position::zero()) + } + EPattern::NumLiteral(inner_err, _) => { + EPattern::NumLiteral(inner_err.clone(), Position::zero()) + } + EPattern::IndentStart(_) => EPattern::IndentStart(Position::zero()), + EPattern::IndentEnd(_) => EPattern::IndentEnd(Position::zero()), + EPattern::AsIndentStart(_) => EPattern::AsIndentStart(Position::zero()), + EPattern::AccessorFunction(_) => EPattern::AccessorFunction(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EImport<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EImport::Import(_) => EImport::Import(Position::zero()), + EImport::IndentStart(_) => EImport::IndentStart(Position::zero()), + EImport::PackageShorthand(_) => EImport::PackageShorthand(Position::zero()), + EImport::PackageShorthandDot(_) => EImport::PackageShorthandDot(Position::zero()), + EImport::ModuleName(_) => EImport::ModuleName(Position::zero()), + EImport::Params(inner_err, _) => { + EImport::Params(inner_err.remove_spaces(arena), Position::zero()) + } + EImport::IndentAs(_) => EImport::IndentAs(Position::zero()), + EImport::As(_) => EImport::As(Position::zero()), + EImport::IndentAlias(_) => EImport::IndentAlias(Position::zero()), + EImport::Alias(_) => EImport::Alias(Position::zero()), + EImport::LowercaseAlias(_) => EImport::LowercaseAlias(Region::zero()), + EImport::IndentExposing(_) => EImport::IndentExposing(Position::zero()), + EImport::Exposing(_) => EImport::Exposing(Position::zero()), + EImport::ExposingListStart(_) => EImport::ExposingListStart(Position::zero()), + EImport::ExposedName(_) => EImport::ExposedName(Position::zero()), + EImport::ExposingListEnd(_) => EImport::ExposingListEnd(Position::zero()), + EImport::IndentIngestedPath(_) => EImport::IndentIngestedPath(Position::zero()), + EImport::IngestedPath(_) => EImport::IngestedPath(Position::zero()), + EImport::IndentIngestedName(_) => EImport::IndentIngestedName(Position::zero()), + EImport::IngestedName(_) => EImport::IngestedName(Position::zero()), + EImport::IndentColon(_) => EImport::IndentColon(Position::zero()), + EImport::Colon(_) => EImport::Colon(Position::zero()), + EImport::IndentAnnotation(_) => EImport::IndentAnnotation(Position::zero()), + EImport::Annotation(inner_err, _) => { + EImport::Annotation(inner_err.remove_spaces(arena), Position::zero()) + } + EImport::Space(inner_err, _) => EImport::Space(*inner_err, Position::zero()), + EImport::EndNewline(_) => EImport::EndNewline(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EType<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EType::Space(inner_err, _) => EType::Space(*inner_err, Position::zero()), + EType::UnderscoreSpacing(_) => EType::UnderscoreSpacing(Position::zero()), + EType::TRecord(inner_err, _) => { + EType::TRecord(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TTagUnion(inner_err, _) => { + EType::TTagUnion(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TInParens(inner_err, _) => { + EType::TInParens(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TApply(inner_err, _) => { + EType::TApply(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TInlineAlias(inner_err, _) => { + EType::TInlineAlias(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TBadTypeVariable(_) => EType::TBadTypeVariable(Position::zero()), + EType::TWildcard(_) => EType::TWildcard(Position::zero()), + EType::TInferred(_) => EType::TInferred(Position::zero()), + EType::TStart(_) => EType::TStart(Position::zero()), + EType::TEnd(_) => EType::TEnd(Position::zero()), + EType::TFunctionArgument(_) => EType::TFunctionArgument(Position::zero()), + EType::TWhereBar(_) => EType::TWhereBar(Position::zero()), + EType::TImplementsClause(_) => EType::TImplementsClause(Position::zero()), + EType::TAbilityImpl(inner_err, _) => { + EType::TAbilityImpl(inner_err.remove_spaces(arena), Position::zero()) + } + EType::TIndentStart(_) => EType::TIndentStart(Position::zero()), + EType::TIndentEnd(_) => EType::TIndentEnd(Position::zero()), + EType::TAsIndentStart(_) => EType::TAsIndentStart(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EImportParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EImportParams::Indent(_) => EImportParams::Indent(Position::zero()), + EImportParams::Record(inner_err, _) => { + EImportParams::Record(inner_err.remove_spaces(arena), Position::zero()) + } + EImportParams::RecordUpdateFound(_) => EImportParams::RecordUpdateFound(Region::zero()), + EImportParams::RecordApplyFound(_) => EImportParams::RecordApplyFound(Region::zero()), + EImportParams::Space(inner_err, _) => { + EImportParams::Space(*inner_err, Position::zero()) + } + EImportParams::RecordBuilderFound(_) => { + EImportParams::RecordBuilderFound(Region::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for PInParens<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + PInParens::Empty(_) => PInParens::Empty(Position::zero()), + PInParens::End(_) => PInParens::End(Position::zero()), + PInParens::Open(_) => PInParens::Open(Position::zero()), + PInParens::Pattern(inner_err, _) => PInParens::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PInParens::Space(inner_err, _) => PInParens::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeAbilityImpl<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeAbilityImpl::End(_) => ETypeAbilityImpl::End(Position::zero()), + ETypeAbilityImpl::Open(_) => ETypeAbilityImpl::Open(Position::zero()), + ETypeAbilityImpl::Field(_) => ETypeAbilityImpl::Field(Position::zero()), + ETypeAbilityImpl::Colon(_) => ETypeAbilityImpl::Colon(Position::zero()), + ETypeAbilityImpl::Arrow(_) => ETypeAbilityImpl::Arrow(Position::zero()), + ETypeAbilityImpl::Optional(_) => ETypeAbilityImpl::Optional(Position::zero()), + ETypeAbilityImpl::Type(inner_err, _) => ETypeAbilityImpl::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeAbilityImpl::Space(inner_err, _) => { + ETypeAbilityImpl::Space(*inner_err, Position::zero()) + } + ETypeAbilityImpl::QuestionMark(_) => ETypeAbilityImpl::QuestionMark(Position::zero()), + ETypeAbilityImpl::Ampersand(_) => ETypeAbilityImpl::Ampersand(Position::zero()), + ETypeAbilityImpl::Expr(inner_err, _) => ETypeAbilityImpl::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeAbilityImpl::IndentBar(_) => ETypeAbilityImpl::IndentBar(Position::zero()), + ETypeAbilityImpl::IndentAmpersand(_) => { + ETypeAbilityImpl::IndentAmpersand(Position::zero()) + } + ETypeAbilityImpl::Prefix(_) => ETypeAbilityImpl::Prefix(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeInlineAlias { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + ETypeInlineAlias::NotAnAlias(_pos) => ETypeInlineAlias::NotAnAlias(Position::zero()), + ETypeInlineAlias::Qualified(_pos) => ETypeInlineAlias::Qualified(Position::zero()), + ETypeInlineAlias::ArgumentNotLowercase(_pos) => { + ETypeInlineAlias::ArgumentNotLowercase(Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeApply { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + ETypeApply::StartNotUppercase(_) => ETypeApply::StartNotUppercase(Position::zero()), + ETypeApply::End(_) => ETypeApply::End(Position::zero()), + ETypeApply::Space(inner_err, _) => ETypeApply::Space(*inner_err, Position::zero()), + ETypeApply::DoubleDot(_) => ETypeApply::DoubleDot(Position::zero()), + ETypeApply::TrailingDot(_) => ETypeApply::TrailingDot(Position::zero()), + ETypeApply::StartIsNumber(_) => ETypeApply::StartIsNumber(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeInParens<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeInParens::Empty(_) => ETypeInParens::Empty(Position::zero()), + ETypeInParens::End(_) => ETypeInParens::End(Position::zero()), + ETypeInParens::Open(_) => ETypeInParens::Open(Position::zero()), + ETypeInParens::Type(inner_err, _) => ETypeInParens::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeInParens::Space(inner_err, _) => { + ETypeInParens::Space(*inner_err, Position::zero()) + } + ETypeInParens::IndentOpen(_) => ETypeInParens::IndentOpen(Position::zero()), + ETypeInParens::IndentEnd(_) => ETypeInParens::IndentEnd(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeTagUnion<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeTagUnion::End(_) => ETypeTagUnion::End(Position::zero()), + ETypeTagUnion::Open(_) => ETypeTagUnion::Open(Position::zero()), + ETypeTagUnion::Type(inner_err, _) => ETypeTagUnion::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeTagUnion::Space(inner_err, _) => { + ETypeTagUnion::Space(*inner_err, Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypeRecord<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypeRecord::End(_) => ETypeRecord::End(Position::zero()), + ETypeRecord::Open(_) => ETypeRecord::Open(Position::zero()), + ETypeRecord::Field(_) => ETypeRecord::Field(Position::zero()), + ETypeRecord::Colon(_) => ETypeRecord::Colon(Position::zero()), + ETypeRecord::Optional(_) => ETypeRecord::Optional(Position::zero()), + ETypeRecord::Type(inner_err, _) => ETypeRecord::Type( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + ETypeRecord::Space(inner_err, _) => ETypeRecord::Space(*inner_err, Position::zero()), + ETypeRecord::IndentOpen(_) => ETypeRecord::IndentOpen(Position::zero()), + ETypeRecord::IndentColon(_) => ETypeRecord::IndentColon(Position::zero()), + ETypeRecord::IndentOptional(_) => ETypeRecord::IndentOptional(Position::zero()), + ETypeRecord::IndentEnd(_) => ETypeRecord::IndentEnd(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for PRecord<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + PRecord::End(_) => PRecord::End(Position::zero()), + PRecord::Open(_) => PRecord::Open(Position::zero()), + PRecord::Field(_) => PRecord::Field(Position::zero()), + PRecord::Colon(_) => PRecord::Colon(Position::zero()), + PRecord::Optional(_) => PRecord::Optional(Position::zero()), + PRecord::Pattern(inner_err, _) => PRecord::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PRecord::Expr(inner_err, _) => PRecord::Expr( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PRecord::Space(inner_err, _) => PRecord::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for PList<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + PList::End(_) => PList::End(Position::zero()), + PList::Open(_) => PList::Open(Position::zero()), + PList::Rest(_) => PList::Rest(Position::zero()), + PList::Pattern(inner_err, _) => PList::Pattern( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + PList::Space(inner_err, _) => PList::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EExpect<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EExpect::Space(inner_err, _) => EExpect::Space(*inner_err, Position::zero()), + EExpect::Dbg(_) => EExpect::Dbg(Position::zero()), + EExpect::Expect(_) => EExpect::Expect(Position::zero()), + EExpect::Condition(inner_err, _) => EExpect::Condition( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpect::Continuation(inner_err, _) => EExpect::Continuation( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()), + } + } +} +impl<'a> RemoveSpaces<'a> for EIf<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EIf::Space(inner_err, _) => EIf::Space(*inner_err, Position::zero()), + EIf::If(_) => EIf::If(Position::zero()), + EIf::Then(_) => EIf::Then(Position::zero()), + EIf::Else(_) => EIf::Else(Position::zero()), + EIf::Condition(inner_err, _) => EIf::Condition( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EIf::ThenBranch(inner_err, _) => EIf::ThenBranch( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EIf::ElseBranch(inner_err, _) => EIf::ElseBranch( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EIf::IndentCondition(_) => EIf::IndentCondition(Position::zero()), + EIf::IndentIf(_) => EIf::IndentIf(Position::zero()), + EIf::IndentThenToken(_) => EIf::IndentThenToken(Position::zero()), + EIf::IndentElseToken(_) => EIf::IndentElseToken(Position::zero()), + EIf::IndentThenBranch(_) => EIf::IndentThenBranch(Position::zero()), + EIf::IndentElseBranch(_) => EIf::IndentElseBranch(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EWhen<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EWhen::Space(inner_err, _) => EWhen::Space(*inner_err, Position::zero()), + EWhen::When(_) => EWhen::When(Position::zero()), + EWhen::Is(_) => EWhen::Is(Position::zero()), + EWhen::Pattern(inner_err, _) => { + EWhen::Pattern(inner_err.remove_spaces(arena), Position::zero()) + } + EWhen::Arrow(_) => EWhen::Arrow(Position::zero()), + EWhen::Bar(_) => EWhen::Bar(Position::zero()), + EWhen::IfToken(_) => EWhen::IfToken(Position::zero()), + EWhen::IfGuard(inner_err, _) => EWhen::IfGuard( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EWhen::Condition(inner_err, _) => EWhen::Condition( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EWhen::Branch(inner_err, _) => EWhen::Branch( + arena.alloc(inner_err.remove_spaces(arena)), + Position::zero(), + ), + EWhen::IndentCondition(_) => EWhen::IndentCondition(Position::zero()), + EWhen::IndentPattern(_) => EWhen::IndentPattern(Position::zero()), + EWhen::IndentArrow(_) => EWhen::IndentArrow(Position::zero()), + EWhen::IndentBranch(_) => EWhen::IndentBranch(Position::zero()), + EWhen::IndentIfGuard(_) => EWhen::IndentIfGuard(Position::zero()), + EWhen::PatternAlignment(_alignment, _) => EWhen::PatternAlignment(0, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EAbility<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EAbility::Space(inner_err, _) => EAbility::Space(*inner_err, Position::zero()), + EAbility::Type(inner_err, _) => { + EAbility::Type(inner_err.remove_spaces(arena), Position::zero()) + } + EAbility::DemandAlignment(_alignment, _) => { + EAbility::DemandAlignment(0, Position::zero()) + } + EAbility::DemandName(_) => EAbility::DemandName(Position::zero()), + EAbility::DemandColon(_) => EAbility::DemandColon(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EGeneratesWith { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EGeneratesWith::Open(_) => EGeneratesWith::Open(Position::zero()), + EGeneratesWith::With(_) => EGeneratesWith::With(Position::zero()), + EGeneratesWith::IndentWith(_) => EGeneratesWith::IndentWith(Position::zero()), + EGeneratesWith::IndentListStart(_) => EGeneratesWith::IndentListStart(Position::zero()), + EGeneratesWith::IndentListEnd(_) => EGeneratesWith::IndentListEnd(Position::zero()), + EGeneratesWith::ListStart(_) => EGeneratesWith::ListStart(Position::zero()), + EGeneratesWith::ListEnd(_) => EGeneratesWith::ListEnd(Position::zero()), + EGeneratesWith::Identifier(_) => EGeneratesWith::Identifier(Position::zero()), + EGeneratesWith::Space(inner_err, _) => { + EGeneratesWith::Space(*inner_err, Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EGenerates { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EGenerates::Open(_) => EGenerates::Open(Position::zero()), + EGenerates::Generates(_) => EGenerates::Generates(Position::zero()), + EGenerates::IndentGenerates(_) => EGenerates::IndentGenerates(Position::zero()), + EGenerates::Identifier(_) => EGenerates::Identifier(Position::zero()), + EGenerates::Space(inner_err, _) => EGenerates::Space(*inner_err, Position::zero()), + EGenerates::IndentTypeStart(_) => EGenerates::IndentTypeStart(Position::zero()), + EGenerates::IndentTypeEnd(_) => EGenerates::IndentTypeEnd(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPackages<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPackages::Open(_) => EPackages::Open(Position::zero()), + EPackages::Space(inner_err, _) => EPackages::Space(*inner_err, Position::zero()), + EPackages::Packages(_) => EPackages::Packages(Position::zero()), + EPackages::IndentPackages(_) => EPackages::IndentPackages(Position::zero()), + EPackages::ListStart(_) => EPackages::ListStart(Position::zero()), + EPackages::ListEnd(_) => EPackages::ListEnd(Position::zero()), + EPackages::IndentListStart(_) => EPackages::IndentListStart(Position::zero()), + EPackages::IndentListEnd(_) => EPackages::IndentListEnd(Position::zero()), + EPackages::PackageEntry(inner_err, _) => { + EPackages::PackageEntry(inner_err.remove_spaces(arena), Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EHeader<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EHeader::Provides(inner_err, _) => { + EHeader::Provides(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Params(inner_err, _) => { + EHeader::Params(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Exposes(inner_err, _) => { + EHeader::Exposes(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Imports(inner_err, _) => { + EHeader::Imports(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Requires(inner_err, _) => { + EHeader::Requires(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Packages(inner_err, _) => { + EHeader::Packages(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Generates(inner_err, _) => { + EHeader::Generates(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::GeneratesWith(inner_err, _) => { + EHeader::GeneratesWith(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::Space(inner_err, _) => EHeader::Space(*inner_err, Position::zero()), + EHeader::Start(_) => EHeader::Start(Position::zero()), + EHeader::ModuleName(_) => EHeader::ModuleName(Position::zero()), + EHeader::AppName(inner_err, _) => { + EHeader::AppName(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::PackageName(inner_err, _) => { + EHeader::PackageName(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::PlatformName(inner_err, _) => { + EHeader::PlatformName(inner_err.remove_spaces(arena), Position::zero()) + } + EHeader::IndentStart(_) => EHeader::IndentStart(Position::zero()), + EHeader::InconsistentModuleName(_) => EHeader::InconsistentModuleName(Region::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPackageName<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPackageName::BadPath(inner_err, _) => { + EPackageName::BadPath(inner_err.remove_spaces(arena), Position::zero()) + } + EPackageName::Escapes(_) => EPackageName::Escapes(Position::zero()), + EPackageName::Multiline(_) => EPackageName::Multiline(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for SyntaxError<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + SyntaxError::Unexpected(_) => SyntaxError::Unexpected(Region::zero()), + SyntaxError::OutdentedTooFar => SyntaxError::OutdentedTooFar, + SyntaxError::Eof(_) => SyntaxError::Eof(Region::zero()), + SyntaxError::InvalidPattern => SyntaxError::InvalidPattern, + SyntaxError::BadUtf8 => SyntaxError::BadUtf8, + SyntaxError::ReservedKeyword(_) => SyntaxError::ReservedKeyword(Region::zero()), + SyntaxError::ArgumentsBeforeEquals(_) => { + SyntaxError::ArgumentsBeforeEquals(Region::zero()) + } + SyntaxError::NotYetImplemented(text) => SyntaxError::NotYetImplemented(text.clone()), + SyntaxError::Todo => SyntaxError::Todo, + SyntaxError::Type(err) => SyntaxError::Type(err.remove_spaces(arena)), + SyntaxError::Pattern(err) => SyntaxError::Pattern(err.remove_spaces(arena)), + SyntaxError::Expr(err, _) => { + SyntaxError::Expr(err.remove_spaces(arena), Position::zero()) + } + SyntaxError::Header(err) => SyntaxError::Header(err.remove_spaces(arena)), + SyntaxError::Space(inner_err) => SyntaxError::Space(*inner_err), + SyntaxError::NotEndOfFile(_) => SyntaxError::NotEndOfFile(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EPackageEntry<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EPackageEntry::BadPackage(inner_err, _) => { + EPackageEntry::BadPackage(inner_err.remove_spaces(arena), Position::zero()) + } + EPackageEntry::Shorthand(_) => EPackageEntry::Shorthand(Position::zero()), + EPackageEntry::Colon(_) => EPackageEntry::Colon(Position::zero()), + EPackageEntry::IndentPackage(_) => EPackageEntry::IndentPackage(Position::zero()), + EPackageEntry::IndentPlatform(_) => EPackageEntry::IndentPlatform(Position::zero()), + EPackageEntry::Platform(_) => EPackageEntry::Platform(Position::zero()), + EPackageEntry::Space(inner_err, _) => { + EPackageEntry::Space(*inner_err, Position::zero()) + } + } + } +} + +impl<'a> RemoveSpaces<'a> for EProvides<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EProvides::Provides(_) => EProvides::Provides(Position::zero()), + EProvides::Open(_) => EProvides::Open(Position::zero()), + EProvides::To(_) => EProvides::To(Position::zero()), + EProvides::IndentProvides(_) => EProvides::IndentProvides(Position::zero()), + EProvides::IndentTo(_) => EProvides::IndentTo(Position::zero()), + EProvides::IndentListStart(_) => EProvides::IndentListStart(Position::zero()), + EProvides::IndentPackage(_) => EProvides::IndentPackage(Position::zero()), + EProvides::ListStart(_) => EProvides::ListStart(Position::zero()), + EProvides::ListEnd(_) => EProvides::ListEnd(Position::zero()), + EProvides::Identifier(_) => EProvides::Identifier(Position::zero()), + EProvides::Package(inner_err, _) => { + EProvides::Package(inner_err.remove_spaces(arena), Position::zero()) + } + EProvides::Space(inner_err, _) => EProvides::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EParams<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + EParams::Pattern(inner_err, _) => { + EParams::Pattern(inner_err.remove_spaces(arena), Position::zero()) + } + EParams::BeforeArrow(_) => EParams::BeforeArrow(Position::zero()), + EParams::Arrow(_) => EParams::Arrow(Position::zero()), + EParams::AfterArrow(_) => EParams::AfterArrow(Position::zero()), + EParams::Space(inner_err, _) => EParams::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EExposes { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EExposes::Exposes(_) => EExposes::Exposes(Position::zero()), + EExposes::Open(_) => EExposes::Open(Position::zero()), + EExposes::IndentExposes(_) => EExposes::IndentExposes(Position::zero()), + EExposes::IndentListStart(_) => EExposes::IndentListStart(Position::zero()), + EExposes::ListStart(_) => EExposes::ListStart(Position::zero()), + EExposes::ListEnd(_) => EExposes::ListEnd(Position::zero()), + EExposes::Identifier(_) => EExposes::Identifier(Position::zero()), + EExposes::Space(inner_err, _) => EExposes::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for EImports { + fn remove_spaces(&self, _arena: &'a Bump) -> Self { + match self { + EImports::Open(_) => EImports::Open(Position::zero()), + EImports::Imports(_) => EImports::Imports(Position::zero()), + EImports::IndentImports(_) => EImports::IndentImports(Position::zero()), + EImports::IndentListStart(_) => EImports::IndentListStart(Position::zero()), + EImports::IndentListEnd(_) => EImports::IndentListEnd(Position::zero()), + EImports::ListStart(_) => EImports::ListStart(Position::zero()), + EImports::ListEnd(_) => EImports::ListEnd(Position::zero()), + EImports::Identifier(_) => EImports::Identifier(Position::zero()), + EImports::ExposingDot(_) => EImports::ExposingDot(Position::zero()), + EImports::ShorthandDot(_) => EImports::ShorthandDot(Position::zero()), + EImports::Shorthand(_) => EImports::Shorthand(Position::zero()), + EImports::ModuleName(_) => EImports::ModuleName(Position::zero()), + EImports::Space(inner_err, _) => EImports::Space(*inner_err, Position::zero()), + EImports::IndentSetStart(_) => EImports::IndentSetStart(Position::zero()), + EImports::SetStart(_) => EImports::SetStart(Position::zero()), + EImports::SetEnd(_) => EImports::SetEnd(Position::zero()), + EImports::TypedIdent(_) => EImports::TypedIdent(Position::zero()), + EImports::AsKeyword(_) => EImports::AsKeyword(Position::zero()), + EImports::StrLiteral(_) => EImports::StrLiteral(Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ERequires<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ERequires::Requires(_) => ERequires::Requires(Position::zero()), + ERequires::Open(_) => ERequires::Open(Position::zero()), + ERequires::IndentRequires(_) => ERequires::IndentRequires(Position::zero()), + ERequires::IndentListStart(_) => ERequires::IndentListStart(Position::zero()), + ERequires::ListStart(_) => ERequires::ListStart(Position::zero()), + ERequires::ListEnd(_) => ERequires::ListEnd(Position::zero()), + ERequires::TypedIdent(inner_err, _) => { + ERequires::TypedIdent(inner_err.remove_spaces(arena), Position::zero()) + } + ERequires::Rigid(_) => ERequires::Rigid(Position::zero()), + ERequires::Space(inner_err, _) => ERequires::Space(*inner_err, Position::zero()), + } + } +} + +impl<'a> RemoveSpaces<'a> for ETypedIdent<'a> { + fn remove_spaces(&self, arena: &'a Bump) -> Self { + match self { + ETypedIdent::Space(inner_err, _) => ETypedIdent::Space(*inner_err, Position::zero()), + ETypedIdent::HasType(_) => ETypedIdent::HasType(Position::zero()), + ETypedIdent::IndentHasType(_) => ETypedIdent::IndentHasType(Position::zero()), + ETypedIdent::Name(_) => ETypedIdent::Name(Position::zero()), + ETypedIdent::Type(inner_err, _) => { + ETypedIdent::Type(inner_err.remove_spaces(arena), Position::zero()) + } + ETypedIdent::IndentType(_) => ETypedIdent::IndentType(Position::zero()), + ETypedIdent::Identifier(_) => ETypedIdent::Identifier(Position::zero()), + } + } +} diff --git a/crates/compiler/parse/src/state.rs b/crates/compiler/parse/src/state.rs index 7e4207584f..c73318bd02 100644 --- a/crates/compiler/parse/src/state.rs +++ b/crates/compiler/parse/src/state.rs @@ -129,6 +129,10 @@ impl<'a> State<'a> { pub fn len_region(&self, length: u32) -> Region { Region::new(self.pos(), self.pos().bump_column(length)) } + + pub fn is_at_start_of_file(&self) -> bool { + self.offset == 0 + } } impl<'a> fmt::Debug for State<'a> { diff --git a/crates/compiler/parse/src/test_helpers.rs b/crates/compiler/parse/src/test_helpers.rs index 5df2fbb263..91d1f24b19 100644 --- a/crates/compiler/parse/src/test_helpers.rs +++ b/crates/compiler/parse/src/test_helpers.rs @@ -22,7 +22,7 @@ pub fn parse_loc_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result>, SourceError<'a, SyntaxError<'a>>> { - let state = State::new(input.trim().as_bytes()); + let state = State::new(input.as_bytes()); match crate::expr::test_parse_expr(0, arena, state.clone()) { Ok(loc_expr) => Ok(loc_expr), @@ -31,7 +31,7 @@ pub fn parse_loc_with<'a>( } pub fn parse_defs_with<'a>(arena: &'a Bump, input: &'a str) -> Result, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); + let state = State::new(input.as_bytes()); parse_module_defs(arena, state, Defs::default()) } @@ -40,7 +40,7 @@ pub fn parse_header_with<'a>( arena: &'a Bump, input: &'a str, ) -> Result, SyntaxError<'a>> { - let state = State::new(input.trim().as_bytes()); + let state = State::new(input.as_bytes()); match crate::module::parse_header(arena, state.clone()) { Ok((header, _)) => Ok(header), diff --git a/crates/compiler/test_gen/src/gen_tags.rs b/crates/compiler/test_gen/src/gen_tags.rs index ce632a98bc..df25fae446 100644 --- a/crates/compiler/test_gen/src/gen_tags.rs +++ b/crates/compiler/test_gen/src/gen_tags.rs @@ -2176,8 +2176,8 @@ fn refcount_nullable_unwrapped_needing_no_refcount_issue_5027() { await : Effect, (Str -> Effect) -> Effect await = \fx, cont -> after - fx - cont + fx + cont succeed : {} -> Effect succeed = \{} -> (\{} -> "success") diff --git a/crates/compiler/test_syntax/fuzz/Cargo.toml b/crates/compiler/test_syntax/fuzz/Cargo.toml index b1389d04d7..42e896950a 100644 --- a/crates/compiler/test_syntax/fuzz/Cargo.toml +++ b/crates/compiler/test_syntax/fuzz/Cargo.toml @@ -11,6 +11,7 @@ cargo-fuzz = true [dependencies] test_syntax = { path = "../../test_syntax" } +roc_parse = { path = "../../parse" } bumpalo = { version = "3.12.0", features = ["collections"] } libfuzzer-sys = "0.4" diff --git a/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs b/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs index 1811947ec8..1a5b07598e 100644 --- a/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs +++ b/crates/compiler/test_syntax/fuzz/fuzz_targets/fuzz_expr.rs @@ -1,14 +1,18 @@ #![no_main] -use libfuzzer_sys::fuzz_target; use bumpalo::Bump; +use libfuzzer_sys::fuzz_target; +use roc_parse::ast::Malformed; use test_syntax::test_helpers::Input; fuzz_target!(|data: &[u8]| { if let Ok(input) = std::str::from_utf8(data) { let input = Input::Expr(input); let arena = Bump::new(); - if input.parse_in(&arena).is_ok() { - input.check_invariants(|_| (), true); + let ast = input.parse_in(&arena); + if let Ok(ast) = ast { + if !ast.is_malformed() { + input.check_invariants(|_| (), true); + } } } }); diff --git a/crates/compiler/test_syntax/src/bin/minimize.rs b/crates/compiler/test_syntax/src/bin/minimize.rs new file mode 100644 index 0000000000..404a45b5a6 --- /dev/null +++ b/crates/compiler/test_syntax/src/bin/minimize.rs @@ -0,0 +1,23 @@ +use test_syntax::{minimize::print_minimizations, test_helpers::InputKind}; + +fn main() { + let args = std::env::args().collect::>(); + if args.len() != 3 { + eprintln!("Usage: {} [expr|full|moduledefs|header] ", args[0]); + std::process::exit(1); + } + + let kind = match args[1].as_str() { + "expr" => InputKind::Expr, + "full" => InputKind::Full, + "moduledefs" => InputKind::ModuleDefs, + "header" => InputKind::Header, + _ => { + eprintln!("Invalid input kind: {}", args[1]); + std::process::exit(1); + } + }; + + let text = std::fs::read_to_string(&args[2]).unwrap(); + print_minimizations(&text, kind); +} diff --git a/crates/compiler/test_syntax/src/lib.rs b/crates/compiler/test_syntax/src/lib.rs index 6687a71727..e929d8cfe6 100644 --- a/crates/compiler/test_syntax/src/lib.rs +++ b/crates/compiler/test_syntax/src/lib.rs @@ -1 +1,2 @@ +pub mod minimize; pub mod test_helpers; diff --git a/crates/compiler/test_syntax/src/minimize.rs b/crates/compiler/test_syntax/src/minimize.rs new file mode 100644 index 0000000000..d8fd4647d4 --- /dev/null +++ b/crates/compiler/test_syntax/src/minimize.rs @@ -0,0 +1,204 @@ +use crate::test_helpers::{Input, InputKind}; +use bumpalo::Bump; +use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces}; + +pub fn print_minimizations(text: &str, kind: InputKind) { + let Some(original_error) = round_trip_once_and_extract_error(text, kind) else { + eprintln!("No error found"); + return; + }; + + eprintln!("Error found: {}", original_error); + eprintln!("Proceeding with minimization"); + + let mut s = text.to_string(); + + loop { + let mut found = false; + for update in candidate_minimizations(s.clone()) { + let mut new_s = String::with_capacity(s.len()); + let mut offset = 0; + for (start, end, replacement) in update.replacements.clone() { + new_s.push_str(&s[offset..start]); + new_s.push_str(&replacement); + offset = end; + } + new_s.push_str(&s[offset..]); + + assert!( + new_s.len() < s.len(), + "replacements: {:?}", + update.replacements + ); + + if let Some(result) = round_trip_once_and_extract_error(&new_s, kind) { + if result == original_error { + eprintln!("Successfully minimized, new length: {}", new_s.len()); + s = new_s; + found = true; + break; + } + } + } + + if !found { + eprintln!("No more minimizations found"); + break; + } + } + + eprintln!("Final result:"); + println!("{}", s); +} + +fn round_trip_once_and_extract_error(text: &str, kind: InputKind) -> Option { + let input = kind.with_text(text); + let res = std::panic::catch_unwind(|| round_trip_once(input)); + + match res { + Ok(res) => res, + Err(e) => { + if let Some(s) = e.downcast_ref::<&'static str>() { + return Some(s.to_string()); + } + if let Some(s) = e.downcast_ref::() { + return Some(s.clone()); + } + Some("Panic during parsing".to_string()) + } + } +} + +fn round_trip_once(input: Input<'_>) -> Option { + let arena = Bump::new(); + + let actual = match input.parse_in(&arena) { + Ok(a) => a, + Err(e) => { + return Some(format!( + "Initial parse failed: {:?}", + e.remove_spaces(&arena) + )) + } // todo: strip pos info, use the error + }; + + if actual.is_malformed() { + return Some("Initial parse is malformed".to_string()); + } + + let output = actual.format(); + + let reparsed_ast = match output.as_ref().parse_in(&arena) { + Ok(r) => r, + Err(e) => return Some(format!("Reparse failed: {:?}", e.remove_spaces(&arena))), + }; + + let ast_normalized = actual.remove_spaces(&arena); + let reparsed_ast_normalized = reparsed_ast.remove_spaces(&arena); + + if format!("{ast_normalized:?}") != format!("{reparsed_ast_normalized:?}") { + return Some("Different ast".to_string()); + } + + None +} + +struct Update { + replacements: Vec<(usize, usize, String)>, +} + +fn candidate_minimizations(s: String) -> Box> { + let mut line_offsets = vec![0]; + line_offsets.extend(s.match_indices('\n').map(|(i, _)| i + 1)); + let line_count = line_offsets.len(); + let s_len = s.len(); + + let line_indents = line_offsets + .iter() + .map(|&offset| s[offset..].chars().take_while(|&c| c == ' ').count()) + .collect::>(); + + let line_offsets_clone = line_offsets.clone(); + + // first, try to remove every group of 1, 2, 3, ... lines - in reverse order (so, trying removing n lines first, then n-1, etc) + let line_removals = (1..=line_count).rev().flat_map(move |n| { + let line_offsets_clone = line_offsets.clone(); + (0..line_count - n).map(move |start| { + let end = start + n; + let start_offset = line_offsets_clone[start]; + let end_offset = line_offsets_clone[end]; + let replacement = String::new(); + let replacements = vec![(start_offset, end_offset, replacement)]; + Update { replacements } + }) + }); + + let line_offsets = line_offsets_clone; + let line_offsets_clone = line_offsets.clone(); + + // then, try to dedent every group of 1, 2, 3, ... lines - in reverse order (so, trying dedenting n lines first, then n-1, etc) + // just remove one space at a time, for now + let line_dedents = (1..=line_count).rev().flat_map(move |n| { + let line_offsets_clone = line_offsets.clone(); + let line_indents_clone = line_indents.clone(); + (0..line_count - n).filter_map(move |start| { + // first check if all lines are either zero-width or have greater than zero indent + let end = start + n; + for i in start..end { + if line_indents_clone[i] == 0 + && line_offsets_clone[i] + 1 + < line_offsets_clone.get(i + 1).cloned().unwrap_or(s_len) + { + return None; + } + } + + let mut replacements = vec![]; + for i in start..end { + let offset = line_offsets_clone[i]; + let indent = line_indents_clone[i]; + if indent > 0 { + replacements.push((offset, offset + 1, String::new())); + } + } + Some(Update { replacements }) + }) + }); + + // then, try to select every range of 1, 2, 3, ... lines - in normal order this time! + // we remove the lines before and after the range + let line_selects = (1..line_count - 1).flat_map(move |n| { + assert!(n > 0); + let line_offsets_clone = line_offsets_clone.clone(); + (0..line_count - n).map(move |start| { + let end = start + n; + let start_offset = line_offsets_clone[start]; + let end_offset = line_offsets_clone[end]; + assert!(end_offset > start_offset); + assert!(start_offset > 0 || end_offset < s_len); + let replacements = vec![ + (0, start_offset, String::new()), + (end_offset, s_len, String::new()), + ]; + Update { replacements } + }) + }); + + // then, try to remove every range of 1, 2, 3, ... characters - in reverse order (so, trying removing n characters first, then n-1, etc) + let charseq_removals = (1..s.len()).rev().flat_map(move |n| { + (0..s.len() - n).map(move |start| { + let end = start + n; + let replacement = String::new(); + let replacements = vec![(start, end, replacement)]; + Update { replacements } + }) + }); + + Box::new( + line_removals + .chain(line_dedents) + .chain(line_selects) + .chain(charseq_removals) + .filter(|u| !u.replacements.is_empty()), + ) +} diff --git a/crates/compiler/test_syntax/src/test_helpers.rs b/crates/compiler/test_syntax/src/test_helpers.rs index 96b5ae2ef8..64411b54e6 100644 --- a/crates/compiler/test_syntax/src/test_helpers.rs +++ b/crates/compiler/test_syntax/src/test_helpers.rs @@ -4,12 +4,12 @@ use roc_parse::{ ast::{Defs, Expr, Malformed, Module}, module::parse_module_defs, parser::{Parser, SyntaxError}, + remove_spaces::RemoveSpaces, state::State, test_helpers::{parse_defs_with, parse_expr_with, parse_header_with}, }; use roc_test_utils::assert_multiline_str_eq; -use roc_fmt::spaces::RemoveSpaces; use roc_fmt::Buf; /// Source code to parse. Usually in the form of a test case. @@ -28,6 +28,25 @@ pub enum Input<'a> { Full(&'a str), } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum InputKind { + Header, + ModuleDefs, + Expr, + Full, +} + +impl InputKind { + pub fn with_text(self, text: &str) -> Input { + match self { + InputKind::Header => Input::Header(text), + InputKind::ModuleDefs => Input::ModuleDefs(text), + InputKind::Expr => Input::Expr(text), + InputKind::Full => Input::Full(text), + } + } +} + // Owned version of `Input` #[derive(Debug, Clone, PartialEq, Eq)] pub enum InputOwned { @@ -38,7 +57,7 @@ pub enum InputOwned { } impl InputOwned { - fn as_ref(&self) -> Input { + pub fn as_ref(&self) -> Input { match self { InputOwned::Header(s) => Input::Header(s), InputOwned::ModuleDefs(s) => Input::ModuleDefs(s), @@ -64,7 +83,7 @@ pub enum Output<'a> { } impl<'a> Output<'a> { - fn format(&self) -> InputOwned { + pub fn format(&self) -> InputOwned { let arena = Bump::new(); let mut buf = Buf::new_in(&arena); match self { @@ -172,7 +191,7 @@ impl<'a> Input<'a> { let (header, defs) = header.upgrade_header_imports(arena); - let module_defs = parse_module_defs(arena, state, defs).unwrap(); + let module_defs = parse_module_defs(arena, state, defs)?; Ok(Output::Full { header, diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast new file mode 100644 index 0000000000..88ad924cba --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.result-ast @@ -0,0 +1 @@ +Expr(BackpassContinue(@8), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc new file mode 100644 index 0000000000..38c82263c8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/backpassing_after_annotation.expr.roc @@ -0,0 +1,2 @@ +u:i +e<-x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast new file mode 100644 index 0000000000..9ac9315f17 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.result-ast @@ -0,0 +1 @@ +Expr(BadExprEnd(@4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/bound_variable.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast index 8fa9f08716..537223e3eb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/def_missing_final_expression.expr.result-ast @@ -1 +1 @@ -Expr(DefMissingFinalExpr2(Start(@11), @11), @0) \ No newline at end of file +Expr(IndentEnd(@11), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast new file mode 100644 index 0000000000..fecbed76f7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.result-ast @@ -0,0 +1 @@ +Expr(BadExprEnd(@3), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/def_without_newline.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast index 8357240265..d2f1c32c26 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/expression_indentation_end.expr.result-ast @@ -1 +1 @@ -Expr(Start(@0), @0) \ No newline at end of file +Expr(IndentEnd(@12), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast index c7af95fd95..72cd39a508 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/if_missing_else.expr.result-ast @@ -1 +1 @@ -Expr(If(Else(@16), @0), @0) \ No newline at end of file +Expr(If(Else(@17), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast index 98c1ed8a32..92672b11ae 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/list_without_end.expr.result-ast @@ -1 +1 @@ -Expr(List(End(@6), @0), @0) \ No newline at end of file +Expr(List(End(@7), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast new file mode 100644 index 0000000000..55da942260 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.result-ast @@ -0,0 +1 @@ +Expr(InParens(Expr(BadExprEnd(@8), @5), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/newline_before_operator_with_defs.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc new file mode 100644 index 0000000000..eeaad94b06 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.formatted.roc @@ -0,0 +1,3 @@ +a : e +Na := e +e0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast new file mode 100644 index 0000000000..0a44a046d5 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.result-ast @@ -0,0 +1 @@ +Expr(BadExprEnd(@11), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/fail/opaque_type_def_with_newline.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast index 73f840b6e9..e45c50b689 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast index e45c50b689..5d5dd4298c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_end_comma.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@6), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast index a465d965e4..810e58ea1c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_indent_open.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@2), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@3), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast index 73f840b6e9..e45c50b689 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/pattern_in_parens_open.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Pattern(PInParens(End(@4), @1), @1), @0), @0) \ No newline at end of file +Expr(Closure(Pattern(PInParens(End(@5), @1), @1), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast index 143c16842e..8289168c15 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_end.expr.result-ast @@ -1 +1 @@ -Expr(Type(TRecord(End(@13), @4), @4), @0) \ No newline at end of file +Expr(Type(TRecord(End(@14), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast index 6b9fe2cc71..0d2c7c0bfe 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open.expr.result-ast @@ -1 +1 @@ -Expr(Type(TRecord(End(@5), @4), @4), @0) \ No newline at end of file +Expr(Type(TRecord(End(@6), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast index 6965e36202..9851faa040 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/record_type_open_indent.expr.result-ast @@ -1 +1 @@ -Expr(Type(TRecord(End(@16), @4), @4), @0) \ No newline at end of file +Expr(Type(TRecord(End(@17), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast index 7c24e2662e..29e52c819a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_end.expr.result-ast @@ -1 +1 @@ -Expr(Type(TTagUnion(End(@9), @4), @4), @0) \ No newline at end of file +Expr(Type(TTagUnion(End(@10), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast index 3375d4db33..7eacf4383a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/tag_union_open.expr.result-ast @@ -1 +1 @@ -Expr(Type(TTagUnion(End(@5), @4), @4), @0) \ No newline at end of file +Expr(Type(TTagUnion(End(@6), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast index 22658fc618..eedba7ebfb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_end.expr.result-ast @@ -1 +1 @@ -Expr(Type(TInParens(End(@9), @4), @4), @0) \ No newline at end of file +Expr(Type(TInParens(End(@10), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast index 1afa91bf38..e89dbbe6a6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/type_in_parens_start.expr.result-ast @@ -1 +1 @@ -Expr(Type(TInParens(End(@5), @4), @4), @0) \ No newline at end of file +Expr(Type(TInParens(End(@6), @4), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast index af23c95379..37c92c7aa6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_closure_pattern_in_parens.expr.result-ast @@ -1 +1 @@ -Expr(Closure(Arrow(@10), @4), @0) \ No newline at end of file +Expr(Closure(IndentArrow(@10), @4), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast new file mode 100644 index 0000000000..6bafebb563 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.result-ast @@ -0,0 +1 @@ +Expr(Import(EndNewline(@15), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc new file mode 100644 index 0000000000..c925700662 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/fail/unfinished_import_as_or_exposing.moduledefs.roc @@ -0,0 +1 @@ +import svg.Path a diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast index 5f421211a3..5b5d49fa8c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_missing_arrow.expr.result-ast @@ -1 +1 @@ -Expr(When(Arrow(@26), @0), @0) \ No newline at end of file +Expr(When(IndentPattern(@26), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast index 3d9b09ae77..9e066cfe7e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_outdented_branch.expr.result-ast @@ -1 +1 @@ -Expr(BadExprEnd(@22), @0) \ No newline at end of file +Expr(BadOperator("->", @24), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast index d0b15de0e2..1c7c9f7bdd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_int.expr.result-ast @@ -1 +1 @@ -Expr(When(Branch(BadOperator("->", @34), @19), @0), @0) \ No newline at end of file +Expr(When(IndentPattern(@34), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast index 629914f2c0..0078479cab 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/when_over_indented_underscore.expr.result-ast @@ -1 +1 @@ -Expr(When(Branch(BadOperator("->", @28), @19), @0), @0) \ No newline at end of file +Expr(When(IndentPattern(@28), @0), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast index 24766ae346..0a44a046d5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/where_type_variable.expr.result-ast @@ -1 +1 @@ -Expr(DefMissingFinalExpr2(ElmStyleFunction(@18-22, @23), @11), @0) \ No newline at end of file +Expr(BadExprEnd(@11), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast index 47ec4d27f6..f24ddecfd0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/fail/wild_case_arrow.expr.result-ast @@ -1 +1 @@ -Expr(BadOperator("->", @9), @0) \ No newline at end of file +Expr(BadExprEnd(@8), @0) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc new file mode 100644 index 0000000000..62430a7666 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.formatted.roc @@ -0,0 +1,3 @@ +when x is + bar.and -> 1 + _ -> 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast index 526bf8a5cd..a71960a712 100644 --- a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_field_access.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-21 SpaceBefore( + Malformed( + "bar.and", + ), + [ + Newline, + ], + ), + ], + value: @25-26 Num( + "1", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @31-32 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], + ), + ], + value: @36-37 Num( + "4", + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @14-21 SpaceBefore( - Malformed( - "bar.and", - ), - [ - Newline, - ], - ), - ], - value: @25-26 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @31-32 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - ], - ), - ], - value: @36-37 Num( - "4", - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc new file mode 100644 index 0000000000..d7120ecf86 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.formatted.roc @@ -0,0 +1,3 @@ +when x is + Foo.and -> 1 + _ -> 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast index fa23b429e9..97e2e5b594 100644 --- a/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/malformed/malformed_pattern_module_name.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-21 SpaceBefore( + Malformed( + "Foo.and", + ), + [ + Newline, + ], + ), + ], + value: @25-26 Num( + "1", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @31-32 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], + ), + ], + value: @36-37 Num( + "4", + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @14-21 SpaceBefore( - Malformed( - "Foo.and", - ), - [ - Newline, - ], - ), - ], - value: @25-26 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @31-32 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - ], - ), - ], - value: @36-37 Num( - "4", - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast index 03168073e5..ef7a2615c7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_demand_signature_is_multiline.expr.result-ast @@ -1,63 +1,68 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-43, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-4 "Hash", - vars: [], - }, - loc_implements: @5-15 Implements, - members: [ - AbilityMember { - name: @18-22 SpaceBefore( - "hash", - [ - Newline, - ], - ), - typ: @25-43 Function( - [ - @25-26 SpaceAfter( - BoundVariable( - "a", - ), - [ - Newline, - ], - ), - ], - @40-43 Apply( - "", - "U64", - [], - ), - ), +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-43, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-4 "Hash", + vars: [], }, - ], - }, - ], - value_defs: [], - }, - @45-46 SpaceBefore( - Num( - "1", + loc_implements: @5-15 Implements, + members: [ + AbilityMember { + name: @18-22 SpaceBefore( + "hash", + [ + Newline, + ], + ), + typ: @25-43 Function( + [ + @25-26 SpaceAfter( + BoundVariable( + "a", + ), + [ + Newline, + ], + ), + ], + @40-43 Apply( + "", + "U64", + [], + ), + ), + }, + ], + }, + ], + value_defs: [], + }, + @45-46 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast index 9203211925..aeeaa7d0f8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_multi_line.expr.result-ast @@ -1,78 +1,83 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-52, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-4 "Hash", - vars: [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-52, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-4 "Hash", + vars: [], + }, + loc_implements: @5-15 Implements, + members: [ + AbilityMember { + name: @18-22 SpaceBefore( + "hash", + [ + Newline, + ], + ), + typ: @25-33 Function( + [ + @25-26 BoundVariable( + "a", + ), + ], + @30-33 Apply( + "", + "U64", + [], + ), + ), + }, + AbilityMember { + name: @36-41 SpaceBefore( + "hash2", + [ + Newline, + ], + ), + typ: @44-52 Function( + [ + @44-45 BoundVariable( + "a", + ), + ], + @49-52 Apply( + "", + "U64", + [], + ), + ), + }, + ], }, - loc_implements: @5-15 Implements, - members: [ - AbilityMember { - name: @18-22 SpaceBefore( - "hash", - [ - Newline, - ], - ), - typ: @25-33 Function( - [ - @25-26 BoundVariable( - "a", - ), - ], - @30-33 Apply( - "", - "U64", - [], - ), - ), - }, - AbilityMember { - name: @36-41 SpaceBefore( - "hash2", - [ - Newline, - ], - ), - typ: @44-52 Function( - [ - @44-45 BoundVariable( - "a", - ), - ], - @49-52 Apply( - "", - "U64", - [], - ), - ), - }, - ], - }, - ], - value_defs: [], - }, - @54-55 SpaceBefore( - Num( - "1", + ], + value_defs: [], + }, + @54-55 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc new file mode 100644 index 0000000000..537edf3203 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.formatted.roc @@ -0,0 +1,3 @@ +Hash implements hash : a -> U64 where a implements Hash + +1 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast index 2847f04b1d..0bc39b910f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_single_line.expr.result-ast @@ -1,67 +1,72 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-55, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-4 "Hash", - vars: [], - }, - loc_implements: @5-15 Implements, - members: [ - AbilityMember { - name: @16-20 "hash", - typ: @23-55 Where( - @23-31 Function( - [ - @23-24 BoundVariable( - "a", - ), - ], - @28-31 Apply( - "", - "U64", - [], - ), - ), - [ - @38-55 ImplementsClause { - var: @38-39 "a", - abilities: [ - @51-55 Apply( - "", - "Hash", - [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-55, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-4 "Hash", + vars: [], + }, + loc_implements: @5-15 Implements, + members: [ + AbilityMember { + name: @16-20 "hash", + typ: @23-55 Where( + @23-31 Function( + [ + @23-24 BoundVariable( + "a", ), ], - }, - ], - ), - }, - ], - }, - ], - value_defs: [], - }, - @57-58 SpaceBefore( - Num( - "1", + @28-31 Apply( + "", + "U64", + [], + ), + ), + [ + @38-55 ImplementsClause { + var: @38-39 "a", + abilities: [ + @51-55 Apply( + "", + "Hash", + [], + ), + ], + }, + ], + ), + }, + ], + }, + ], + value_defs: [], + }, + @57-58 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc new file mode 100644 index 0000000000..6751436b6a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.formatted.roc @@ -0,0 +1,5 @@ +Ab1 implements ab1 : a -> {} where a implements Ab1 + +Ab2 implements ab2 : a -> {} where a implements Ab2 + +1 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast index d4285ec53d..a4f1abe8e1 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ability_two_in_a_row.expr.result-ast @@ -1,110 +1,115 @@ -Defs( - Defs { - tags: [ - Index(0), - Index(1), - ], - regions: [ - @0-51, - @53-104, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [ - Ability { - header: TypeHeader { - name: @0-3 "Ab1", - vars: [], - }, - loc_implements: @4-14 Implements, - members: [ - AbilityMember { - name: @15-18 "ab1", - typ: @21-51 Where( - @21-28 Function( - [ - @21-22 BoundVariable( - "a", - ), - ], - @26-28 Record { - fields: [], - ext: None, - }, - ), - [ - @35-51 ImplementsClause { - var: @35-36 "a", - abilities: [ - @48-51 Apply( - "", - "Ab1", - [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + Index(1), + ], + regions: [ + @0-51, + @53-104, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [ + Ability { + header: TypeHeader { + name: @0-3 "Ab1", + vars: [], + }, + loc_implements: @4-14 Implements, + members: [ + AbilityMember { + name: @15-18 "ab1", + typ: @21-51 Where( + @21-28 Function( + [ + @21-22 BoundVariable( + "a", ), ], - }, - ], - ), - }, - ], - }, - Ability { - header: TypeHeader { - name: @53-56 "Ab2", - vars: [], - }, - loc_implements: @57-67 Implements, - members: [ - AbilityMember { - name: @68-71 "ab2", - typ: @74-104 Where( - @74-81 Function( + @26-28 Record { + fields: [], + ext: None, + }, + ), [ - @74-75 BoundVariable( - "a", - ), + @35-51 ImplementsClause { + var: @35-36 "a", + abilities: [ + @48-51 Apply( + "", + "Ab1", + [], + ), + ], + }, ], - @79-81 Record { - fields: [], - ext: None, - }, ), - [ - @88-104 ImplementsClause { - var: @88-89 "a", - abilities: [ - @101-104 Apply( - "", - "Ab2", - [], + }, + ], + }, + Ability { + header: TypeHeader { + name: @53-56 "Ab2", + vars: [], + }, + loc_implements: @57-67 Implements, + members: [ + AbilityMember { + name: @68-71 "ab2", + typ: @74-104 Where( + @74-81 Function( + [ + @74-75 BoundVariable( + "a", ), ], - }, - ], - ), - }, - ], - }, - ], - value_defs: [], - }, - @106-107 SpaceBefore( - Num( - "1", + @79-81 Record { + fields: [], + ext: None, + }, + ), + [ + @88-104 ImplementsClause { + var: @88-89 "a", + abilities: [ + @101-104 Apply( + "", + "Ab2", + [], + ), + ], + }, + ], + ), + }, + ], + }, + ], + value_defs: [], + }, + @106-107 SpaceBefore( + Num( + "1", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast index 52cac4a3ca..9ad1418cac 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_record_destructure.expr.result-ast @@ -1,78 +1,83 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-49, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-8 RecordDestructure( - [ - @2-3 Identifier { - ident: "x", - }, - @5-7 Identifier { - ident: "y", - }, - ], - ), - ann_type: @11-14 Apply( - "", - "Foo", - [], - ), - comment: None, - body_pattern: @15-23 RecordDestructure( - [ - @17-18 Identifier { - ident: "x", - }, - @20-21 Identifier { - ident: "y", - }, - ], - ), - body_expr: @26-49 Record( - [ - @28-37 RequiredValue( - @28-29 "x", - [], - @32-37 Str( - PlainLine( - "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-49, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-8 RecordDestructure( + [ + @2-3 Identifier { + ident: "x", + }, + @5-7 Identifier { + ident: "y", + }, + ], + ), + ann_type: @11-14 Apply( + "", + "Foo", + [], + ), + comment: None, + body_pattern: @15-23 RecordDestructure( + [ + @17-18 Identifier { + ident: "x", + }, + @20-22 Identifier { + ident: "y", + }, + ], + ), + body_expr: @26-49 Record( + [ + @28-37 RequiredValue( + @28-29 "x", + [], + @32-37 Str( + PlainLine( + "foo", + ), ), ), - ), - @39-47 RequiredValue( - @39-40 "y", - [], - @43-47 Float( - "3.14", + @39-47 RequiredValue( + @39-40 "y", + [], + @43-47 Float( + "3.14", + ), ), - ), - ], - ), - }, - ], - }, - @51-52 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], + ), + }, + ], }, - [ - Newline, - Newline, - ], + @51-52 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast index 6b6a39c5b0..1eec16a870 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tag_destructure.expr.result-ast @@ -1,79 +1,84 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-46, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-8 Apply( - @0-6 Tag( - "UserId", - ), - [ - @7-8 Identifier { - ident: "x", - }, - ], - ), - ann_type: @11-25 TagUnion { - ext: None, - tags: [ - @13-23 Apply { - name: @13-19 "UserId", - args: [ - @20-23 Apply( - "", - "I64", - [], - ), - ], - }, - ], - }, - comment: None, - body_pattern: @26-34 Apply( - @26-32 Tag( - "UserId", - ), - [ - @33-34 Identifier { - ident: "x", - }, - ], - ), - body_expr: @37-46 Apply( - @37-43 Tag( - "UserId", - ), - [ - @44-46 Num( - "42", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-46, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-8 Apply( + @0-6 Tag( + "UserId", ), - ], - Space, - ), - }, - ], - }, - @48-49 SpaceBefore( - Var { - module_name: "", - ident: "x", + [ + @7-8 Identifier { + ident: "x", + }, + ], + ), + ann_type: @11-25 TagUnion { + ext: None, + tags: [ + @13-23 Apply { + name: @13-19 "UserId", + args: [ + @20-23 Apply( + "", + "I64", + [], + ), + ], + }, + ], + }, + comment: None, + body_pattern: @26-34 Apply( + @26-32 Tag( + "UserId", + ), + [ + @33-34 Identifier { + ident: "x", + }, + ], + ), + body_expr: @37-46 Apply( + @37-43 Tag( + "UserId", + ), + [ + @44-46 Num( + "42", + ), + ], + Space, + ), + }, + ], }, - [ - Newline, - Newline, - ], + @48-49 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast index 49768c2156..3ac22b49de 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/annotated_tuple_destructure.expr.result-ast @@ -1,70 +1,75 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-41, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-8 Tuple( - [ - @2-3 Identifier { - ident: "x", - }, - @5-6 Identifier { - ident: "y", - }, - ], - ), - ann_type: @11-14 Apply( - "", - "Foo", - [], - ), - comment: None, - body_pattern: @15-23 Tuple( - [ - @17-18 Identifier { - ident: "x", - }, - @20-21 Identifier { - ident: "y", - }, - ], - ), - body_expr: @26-41 Tuple( - [ - @28-33 Str( - PlainLine( - "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-41, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-8 Tuple( + [ + @2-3 Identifier { + ident: "x", + }, + @5-6 Identifier { + ident: "y", + }, + ], + ), + ann_type: @11-14 Apply( + "", + "Foo", + [], + ), + comment: None, + body_pattern: @15-23 Tuple( + [ + @17-18 Identifier { + ident: "x", + }, + @20-21 Identifier { + ident: "y", + }, + ], + ), + body_expr: @26-41 Tuple( + [ + @28-33 Str( + PlainLine( + "foo", + ), ), - ), - @35-39 Float( - "3.14", - ), - ], - ), - }, - ], - }, - @43-44 SpaceBefore( - Var { - module_name: "", - ident: "x", + @35-39 Float( + "3.14", + ), + ], + ), + }, + ], }, - [ - Newline, - Newline, - ], + @43-44 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc new file mode 100644 index 0000000000..8ebd4435d4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.formatted.roc @@ -0,0 +1,9 @@ +## first line of docs +## second line +## third line +## fourth line +## +## sixth line after doc new line +x = 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast index c74acaf899..af12c61975 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/basic_docs.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @107-112, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @107-108 Identifier { - ident: "x", - }, - @111-112 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @107-112, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @107-108 Identifier { + ident: "x", + }, + @111-112 Num( + "5", + ), ), + ], + }, + @114-116 SpaceBefore( + Num( + "42", ), - ], - }, - @114-116 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ DocComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast deleted file mode 100644 index c5b57b1c72..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/bound_variable.expr.result-ast +++ /dev/null @@ -1,36 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-4, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "a", - }, - @3-4 SpaceBefore( - BoundVariable( - "c", - ), - [ - Newline, - ], - ), - ), - ], - }, - @5-6 Num( - "0", - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc deleted file mode 100644 index c1feee4538..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.formatted.roc +++ /dev/null @@ -1,3 +0,0 @@ -f - -5 - 2 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast deleted file mode 100644 index 1fb8443efb..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.result-ast +++ /dev/null @@ -1,25 +0,0 @@ -Apply( - @0-1 SpaceAfter( - Var { - module_name: "", - ident: "f", - }, - [ - Newline, - ], - ), - [ - @2-4 Num( - "-5", - ), - @5-6 SpaceBefore( - Num( - "2", - ), - [ - Newline, - ], - ), - ], - Space, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc deleted file mode 100644 index 2f398535c7..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/call_with_newlines.expr.roc +++ /dev/null @@ -1,3 +0,0 @@ -f --5 -2 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc deleted file mode 100644 index 6b3df9a06e..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.formatted.roc +++ /dev/null @@ -1,3 +0,0 @@ -a -&& (\x -> x) - 8 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast deleted file mode 100644 index 73825d7211..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.result-ast +++ /dev/null @@ -1,35 +0,0 @@ -BinOps( - [ - ( - @0-1 Var { - module_name: "", - ident: "a", - }, - @2-4 And, - ), - ], - @5-12 Apply( - @5-10 Closure( - [ - @6-7 Identifier { - ident: "x", - }, - ], - @9-10 Var { - module_name: "", - ident: "x", - }, - ), - [ - @11-12 SpaceBefore( - Num( - "8", - ), - [ - Newline, - ], - ), - ], - Space, - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc deleted file mode 100644 index f58fe8a282..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/closure_in_binop.expr.roc +++ /dev/null @@ -1,2 +0,0 @@ -a && \x->x -8 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc new file mode 100644 index 0000000000..0bddb5ce24 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.formatted.roc @@ -0,0 +1,8 @@ +Model position : { + evaluated : Set position, + openSet : Set position, + costs : Dict.Dict position F64, + cameFrom : Dict.Dict position position, +} + +a \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast new file mode 100644 index 0000000000..2e0614b3ba --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.result-ast @@ -0,0 +1,130 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-164, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Alias { + header: TypeHeader { + name: @0-5 "Model", + vars: [ + @6-14 Identifier { + ident: "position", + }, + ], + }, + ann: @21-164 Record { + fields: [ + @23-47 SpaceAfter( + RequiredValue( + @23-32 "evaluated", + [], + @35-47 Apply( + "", + "Set", + [ + @39-47 BoundVariable( + "position", + ), + ], + ), + ), + [ + Newline, + ], + ), + @54-76 SpaceAfter( + RequiredValue( + @54-61 "openSet", + [], + @64-76 Apply( + "", + "Set", + [ + @68-76 BoundVariable( + "position", + ), + ], + ), + ), + [ + Newline, + ], + ), + @83-113 SpaceAfter( + RequiredValue( + @83-88 "costs", + [], + @91-113 Apply( + "Dict", + "Dict", + [ + @101-109 BoundVariable( + "position", + ), + @110-113 Apply( + "", + "F64", + [], + ), + ], + ), + ), + [ + Newline, + ], + ), + @120-158 SpaceAfter( + RequiredValue( + @120-128 "cameFrom", + [], + @131-158 Apply( + "Dict", + "Dict", + [ + @141-149 BoundVariable( + "position", + ), + @150-158 BoundVariable( + "position", + ), + ], + ), + ), + [ + Newline, + ], + ), + ], + ext: None, + }, + }, + ], + value_defs: [], + }, + @166-167 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc new file mode 100644 index 0000000000..bb3f03927d --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/comma_prefixed_indented_record.expr.roc @@ -0,0 +1,8 @@ +Model position : + { evaluated : Set position + , openSet : Set position + , costs : Dict.Dict position F64 + , cameFrom : Dict.Dict position position + } + +a diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc deleted file mode 100644 index 11c39e4de9..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_def.moduledefs.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -foo = 1 # comment after diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc deleted file mode 100644 index 878f99f997..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -Z # -h - : a -j \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast deleted file mode 100644 index e7bb56f766..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.result-ast +++ /dev/null @@ -1,54 +0,0 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-7, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Alias { - header: TypeHeader { - name: @0-1 "Z", - vars: [ - @3-4 SpaceAfter( - SpaceBefore( - Identifier { - ident: "h", - }, - [ - LineComment( - "", - ), - ], - ), - [ - Newline, - ], - ), - ], - }, - ann: @6-7 BoundVariable( - "a", - ), - }, - ], - value_defs: [], - }, - @8-9 SpaceBefore( - Var { - module_name: "", - ident: "j", - }, - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc deleted file mode 100644 index 3737e7a247..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/comment_after_tag_in_def.expr.roc +++ /dev/null @@ -1,4 +0,0 @@ -Z# -h -:a -j \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast index 88b452be8a..05b3ab13f8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/crash.expr.result-ast @@ -1,197 +1,50 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), - Index(2147483651), - Index(2147483652), - ], - regions: [ - @0-12, - @13-28, - @29-45, - @46-74, - @75-101, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 1), - Slice(start = 2, length = 1), - Slice(start = 3, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - Slice(start = 4, length = 0), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-1 Underscore( - "", - ), - @4-12 Apply( - @4-9 Crash, - [ - @10-12 Str( - PlainLine( - "", - ), - ), - ], - Space, - ), - ), - Body( - @13-14 Underscore( - "", - ), - @17-28 Apply( - @17-22 Crash, - [ - @23-25 Str( - PlainLine( - "", - ), - ), - @26-28 Str( - PlainLine( - "", - ), - ), - ], - Space, - ), - ), - Body( - @29-30 Underscore( - "", - ), - @33-45 Apply( - @33-38 Crash, - [ - @39-41 Num( - "15", - ), - @42-45 Num( - "123", - ), - ], - Space, - ), - ), - Body( - @46-47 Underscore( - "", - ), - @50-74 Apply( - @50-53 Var { - module_name: "", - ident: "try", - }, - [ - @54-57 Var { - module_name: "", - ident: "foo", - }, - @59-73 ParensAround( - Closure( - [ - @60-61 Underscore( - "", - ), - ], - @65-73 Apply( - @65-70 Crash, - [ - @71-73 Str( - PlainLine( - "", - ), - ), - ], - Space, - ), - ), - ), - ], - Space, - ), - ), - Body( - @75-76 Underscore( - "", - ), - @81-101 SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @81-93, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @81-82 Underscore( - "", - ), - @85-93 Apply( - @85-90 Crash, - [ - @91-93 Str( - PlainLine( - "", - ), - ), - ], - Space, - ), - ), - ], - }, - @96-101 SpaceBefore( - Crash, - [ - Newline, - ], - ), +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + Index(2147483651), + Index(2147483652), + ], + regions: [ + @0-12, + @13-28, + @29-45, + @46-74, + @75-101, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + Slice(start = 2, length = 1), + Slice(start = 3, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-1 Underscore( + "", ), - [ - Newline, - ], - ), - ), - ], - }, - @103-118 SpaceBefore( - Record( - [ - @105-116 RequiredValue( - @105-106 "f", - [], - @108-116 Apply( - @108-113 Crash, + @4-12 Apply( + @4-9 Crash, [ - @114-116 Str( + @10-12 Str( PlainLine( "", ), @@ -200,11 +53,163 @@ Defs( Space, ), ), + Body( + @13-14 Underscore( + "", + ), + @17-28 Apply( + @17-22 Crash, + [ + @23-25 Str( + PlainLine( + "", + ), + ), + @26-28 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + Body( + @29-30 Underscore( + "", + ), + @33-45 Apply( + @33-38 Crash, + [ + @39-41 Num( + "15", + ), + @42-45 Num( + "123", + ), + ], + Space, + ), + ), + Body( + @46-47 Underscore( + "", + ), + @50-74 Apply( + @50-53 Var { + module_name: "", + ident: "try", + }, + [ + @54-57 Var { + module_name: "", + ident: "foo", + }, + @59-73 ParensAround( + Closure( + [ + @60-61 Underscore( + "", + ), + ], + @65-73 Apply( + @65-70 Crash, + [ + @71-73 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + ), + ], + Space, + ), + ), + Body( + @75-76 Underscore( + "", + ), + @81-101 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @81-93, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @81-82 Underscore( + "", + ), + @85-93 Apply( + @85-90 Crash, + [ + @91-93 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + ], + }, + @96-101 SpaceBefore( + Crash, + [ + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + ], + }, + @103-118 SpaceBefore( + Record( + [ + @105-116 RequiredValue( + @105-106 "f", + [], + @108-116 Apply( + @108-113 Crash, + [ + @114-116 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + ], + ), + [ + Newline, + Newline, ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc new file mode 100644 index 0000000000..44de35e8d2 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.formatted.roc @@ -0,0 +1,3 @@ +dbg 1 == 1 + +4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast index e928afdee5..ef6e22eecb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.result-ast @@ -1,24 +1,34 @@ -Dbg( - @4-10 BinOps( - [ - ( - @4-5 Num( +SpaceBefore( + SpaceAfter( + Dbg( + @5-11 BinOps( + [ + ( + @5-6 Num( + "1", + ), + @7-9 Equals, + ), + ], + @10-11 Num( "1", ), - @6-8 Equals, ), - ], - @9-10 Num( - "1", - ), - ), - @12-13 SpaceBefore( - Num( - "4", + @13-14 SpaceBefore( + Num( + "4", + ), + [ + Newline, + Newline, + ], + ), ), [ Newline, - Newline, ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc index c1dc7243cb..fab415486d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg.expr.roc @@ -1,3 +1,4 @@ + dbg 1 == 1 4 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast index 5ec65c119d..8bb47ecd8d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/dbg_multiline.expr.result-ast @@ -1,26 +1,31 @@ -Dbg( - @4-16 Tuple( - [ - @5-6 Num( - "5", - ), - @12-15 SpaceBefore( - Num( - "666", +SpaceAfter( + Dbg( + @4-16 Tuple( + [ + @5-6 Num( + "5", ), - [ - Newline, - ], - ), - ], - ), - @18-19 SpaceBefore( - Num( - "4", + @12-15 SpaceBefore( + Num( + "666", + ), + [ + Newline, + ], + ), + ], + ), + @18-19 SpaceBefore( + Num( + "4", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast deleted file mode 100644 index 6d30592907..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/def_without_newline.expr.result-ast +++ /dev/null @@ -1,32 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-3, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "a", - }, - @2-3 BoundVariable( - "b", - ), - ), - ], - }, - @4-5 Var { - module_name: "", - ident: "i", - }, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc new file mode 100644 index 0000000000..c0294a60f0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.formatted.roc @@ -0,0 +1,9 @@ +main = + a = "Foo" + Stdout.line! a + + printBar! + +printBar = + b = "Bar" + Stdout.line b diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast new file mode 100644 index 0000000000..5b346e9c0c --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.result-ast @@ -0,0 +1,159 @@ +Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @24-150, + @176-266, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @24-28 Identifier { + ident: "main", + }, + @59-150 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @59-68, + @97-111, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @59-60 Identifier { + ident: "a", + }, + @63-68 Str( + PlainLine( + "Foo", + ), + ), + ), + Stmt( + @97-111 Apply( + @97-108 TaskAwaitBang( + Var { + module_name: "Stdout", + ident: "line", + }, + ), + [ + @110-111 Var { + module_name: "", + ident: "a", + }, + ], + Space, + ), + ), + ], + }, + @141-150 SpaceBefore( + TaskAwaitBang( + Var { + module_name: "", + ident: "printBar", + }, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + Body( + @176-184 Identifier { + ident: "printBar", + }, + @215-266 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @215-224, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @215-216 Identifier { + ident: "b", + }, + @219-224 Str( + PlainLine( + "Bar", + ), + ), + ), + ], + }, + @253-266 SpaceBefore( + Apply( + @253-264 Var { + module_name: "Stdout", + ident: "line", + }, + [ + @265-266 Var { + module_name: "", + ident: "b", + }, + ], + Space, + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc new file mode 100644 index 0000000000..12e697a294 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/defs_suffixed_middle_extra_indents.moduledefs.roc @@ -0,0 +1,9 @@ + main = + a = "Foo" + Stdout.line! a + + printBar! + + printBar = + b = "Bar" + Stdout.line b diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast index 1adeb71343..2e1a546396 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/destructure_tag_assignment.expr.result-ast @@ -1,54 +1,59 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-36, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-5 Apply( - @0-5 Tag( - "Email", - ), - [ - @6-9 Identifier { - ident: "str", - }, - ], - ), - @12-36 Apply( - @12-17 Tag( - "Email", - ), - [ - @18-36 Str( - PlainLine( - "blah@example.com", - ), +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-36, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-9 Apply( + @0-5 Tag( + "Email", ), - ], - Space, + [ + @6-9 Identifier { + ident: "str", + }, + ], + ), + @12-36 Apply( + @12-17 Tag( + "Email", + ), + [ + @18-36 Str( + PlainLine( + "blah@example.com", + ), + ), + ], + Space, + ), ), - ), - ], - }, - @37-40 SpaceBefore( - Var { - module_name: "", - ident: "str", + ], }, - [ - Newline, - ], + @37-40 SpaceBefore( + Var { + module_name: "", + ident: "str", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast index 49f839bc4d..808b4c36fc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/docs.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @48-53, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @48-49 Identifier { - ident: "x", - }, - @52-53 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @48-53, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @48-49 Identifier { + ident: "x", + }, + @52-53 Num( + "5", + ), ), + ], + }, + @55-57 SpaceBefore( + Num( + "42", ), - ], - }, - @55-57 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc deleted file mode 100644 index e43d69f4a1..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_hosted_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -hosted Foo exposes [] imports [] generates Bar with [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc deleted file mode 100644 index 9fd98c5b23..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_module_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -module [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc deleted file mode 100644 index 313246d359..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_package_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -package [] {} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc deleted file mode 100644 index 04d71d738d..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_platform_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -platform "rtfeldman/blah" requires {} { main : {} } exposes [] packages {} imports [] provides [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc new file mode 100644 index 0000000000..3cc762b550 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.formatted.roc @@ -0,0 +1 @@ +"" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast index a00db728af..5929994e77 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/empty_string.expr.result-ast @@ -1,5 +1,10 @@ -Str( - PlainLine( - "", +SpaceAfter( + Str( + PlainLine( + "", + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc index d0ec1ce6d3..2f47c144be 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.formatted.roc @@ -1,4 +1,3 @@ -expect - 1 == 1 +expect 1 == 1 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast index fb56585387..5ffea14bab 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect.expr.result-ast @@ -1,24 +1,50 @@ -Expect( - @7-13 BinOps( - [ - ( - @7-8 Num( - "1", - ), - @9-11 Equals, +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-13, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Expect { + condition: @7-13 BinOps( + [ + ( + @7-8 Num( + "1", + ), + @9-11 Equals, + ), + ], + @12-13 Num( + "1", + ), + ), + preceding_comment: …, + }, + ], + }, + @15-16 SpaceBefore( + Num( + "4", ), - ], - @12-13 Num( - "1", + [ + Newline, + Newline, + ], ), ), - @15-16 SpaceBefore( - Num( - "4", - ), - [ - Newline, - Newline, - ], - ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast new file mode 100644 index 0000000000..1fb2e93bc3 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.result-ast @@ -0,0 +1,453 @@ +Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-644, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Expect { + condition: @11-644 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @11-118, + @124-252, + @258-556, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @11-15 Identifier { + ident: "html", + }, + ann_type: @18-25 Apply( + "", + "Html", + [ + @23-25 Record { + fields: [], + ext: None, + }, + ], + ), + comment: None, + body_pattern: @30-34 Identifier { + ident: "html", + }, + body_expr: @45-118 SpaceBefore( + Apply( + @45-52 Tag( + "Element", + ), + [ + @53-56 Str( + PlainLine( + "a", + ), + ), + @57-59 Num( + "43", + ), + @60-105 List( + [ + @61-104 Apply( + @61-69 Tag( + "HtmlAttr", + ), + [ + @70-76 Str( + PlainLine( + "href", + ), + ), + @77-104 Str( + PlainLine( + "https://www.roc-lang.org/", + ), + ), + ], + Space, + ), + ], + ), + @106-118 List( + [ + @107-117 Apply( + @107-111 Tag( + "Text", + ), + [ + @112-117 Str( + PlainLine( + "Roc", + ), + ), + ], + Space, + ), + ], + ), + ], + Space, + ), + [ + Newline, + ], + ), + }, + AnnotatedBody { + ann_pattern: @124-130 Identifier { + ident: "actual", + }, + ann_type: @133-185 Record { + fields: [ + @135-160 RequiredValue( + @135-140 "nodes", + [], + @143-160 Apply( + "", + "List", + [ + @148-160 Apply( + "", + "RenderedNode", + [], + ), + ], + ), + ), + @162-183 RequiredValue( + @162-172 "siblingIds", + [], + @175-183 Apply( + "", + "List", + [ + @180-183 Apply( + "", + "U64", + [], + ), + ], + ), + ), + ], + ext: None, + }, + comment: None, + body_pattern: @190-196 Identifier { + ident: "actual", + }, + body_expr: @207-252 SpaceBefore( + Apply( + @207-217 Var { + module_name: "", + ident: "indexNodes", + }, + [ + @218-247 Record( + [ + @220-229 RequiredValue( + @220-225 "nodes", + [], + @227-229 List( + [], + ), + ), + @231-245 RequiredValue( + @231-241 "siblingIds", + [], + @243-245 List( + [], + ), + ), + ], + ), + @248-252 Var { + module_name: "", + ident: "html", + }, + ], + Space, + ), + [ + Newline, + ], + ), + }, + AnnotatedBody { + ann_pattern: @258-266 Identifier { + ident: "expected", + }, + ann_type: @269-321 Record { + fields: [ + @271-296 RequiredValue( + @271-276 "nodes", + [], + @279-296 Apply( + "", + "List", + [ + @284-296 Apply( + "", + "RenderedNode", + [], + ), + ], + ), + ), + @298-319 RequiredValue( + @298-308 "siblingIds", + [], + @311-319 Apply( + "", + "List", + [ + @316-319 Apply( + "", + "U64", + [], + ), + ], + ), + ), + ], + ext: None, + }, + comment: None, + body_pattern: @326-334 Identifier { + ident: "expected", + }, + body_expr: @337-556 Record( + Collection { + items: [ + @347-524 SpaceBefore( + RequiredValue( + @347-352 "nodes", + [], + @354-524 List( + Collection { + items: [ + @368-386 SpaceBefore( + Apply( + @368-380 Tag( + "RenderedText", + ), + [ + @381-386 Str( + PlainLine( + "Roc", + ), + ), + ], + Space, + ), + [ + Newline, + ], + ), + @400-513 SpaceBefore( + Apply( + @400-415 Tag( + "RenderedElement", + ), + [ + @416-419 Str( + PlainLine( + "a", + ), + ), + @420-509 RecordUpdate { + update: @422-440 Var { + module_name: "", + ident: "emptyRenderedAttrs", + }, + fields: [ + @443-507 RequiredValue( + @443-452 "htmlAttrs", + [], + @454-507 Apply( + @454-467 Var { + module_name: "Dict", + ident: "fromList", + }, + [ + @468-507 List( + [ + @469-506 Tuple( + [ + @470-476 Str( + PlainLine( + "href", + ), + ), + @478-505 Str( + PlainLine( + "https://www.roc-lang.org/", + ), + ), + ], + ), + ], + ), + ], + Space, + ), + ), + ], + }, + @510-513 List( + [ + @511-512 Num( + "0", + ), + ], + ), + ], + Space, + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + ), + [ + Newline, + ], + ), + @534-549 SpaceBefore( + RequiredValue( + @534-544 "siblingIds", + [], + @546-549 List( + [ + @547-548 Num( + "1", + ), + ], + ), + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + }, + ], + }, + @562-644 SpaceBefore( + BinOps( + [ + ( + @563-593 SpaceAfter( + ParensAround( + BinOps( + [ + ( + @563-575 RecordAccess( + Var { + module_name: "", + ident: "actual", + }, + "nodes", + ), + @576-578 Equals, + ), + ], + @579-593 RecordAccess( + Var { + module_name: "", + ident: "expected", + }, + "nodes", + ), + ), + ), + [ + Newline, + ], + ), + @599-601 And, + ), + ], + @603-643 ParensAround( + BinOps( + [ + ( + @603-620 RecordAccess( + Var { + module_name: "", + ident: "actual", + }, + "siblingIds", + ), + @621-623 Equals, + ), + ], + @624-643 RecordAccess( + Var { + module_name: "", + ident: "expected", + }, + "siblingIds", + ), + ), + ), + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + preceding_comment: …, + }, + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc new file mode 100644 index 0000000000..713be555b0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_defs.moduledefs.roc @@ -0,0 +1,20 @@ +expect + html : Html {} + html = + Element "a" 43 [HtmlAttr "href" "https://www.roc-lang.org/"] [Text "Roc"] + + actual : { nodes : List RenderedNode, siblingIds : List U64 } + actual = + indexNodes { nodes: [], siblingIds: [] } html + + expected : { nodes : List RenderedNode, siblingIds : List U64 } + expected = { + nodes: [ + RenderedText "Roc", + RenderedElement "a" { emptyRenderedAttrs & htmlAttrs: Dict.fromList [("href", "https://www.roc-lang.org/")] } [0], + ], + siblingIds: [1], + } + + (actual.nodes == expected.nodes) + && (actual.siblingIds == expected.siblingIds) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc deleted file mode 100644 index 0c0307e978..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.formatted.roc +++ /dev/null @@ -1,2 +0,0 @@ -# expecting some effects -expect-fx 5 == 2 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast index 477eac6deb..1e12f3ed39 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_fx.moduledefs.result-ast @@ -9,12 +9,13 @@ Defs { Slice(start = 0, length = 1), ], space_after: [ - Slice(start = 1, length = 0), + Slice(start = 1, length = 1), ], spaces: [ LineComment( " expecting some effects", ), + Newline, ], type_defs: [], value_defs: [ @@ -32,7 +33,7 @@ Defs { "2", ), ), - preceding_comment: @25-25, + preceding_comment: @0-24, }, ], } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc new file mode 100644 index 0000000000..e87550b805 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.formatted.roc @@ -0,0 +1,7 @@ +x = 5 + +expect x == y + +expect y == z + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast new file mode 100644 index 0000000000..80bf61088e --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.result-ast @@ -0,0 +1,91 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @0-5, + @7-20, + @22-35, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", + }, + @4-5 Num( + "5", + ), + ), + Expect { + condition: @14-20 BinOps( + [ + ( + @14-15 Var { + module_name: "", + ident: "x", + }, + @16-18 Equals, + ), + ], + @19-20 Var { + module_name: "", + ident: "y", + }, + ), + preceding_comment: @7-7, + }, + Expect { + condition: @29-35 BinOps( + [ + ( + @29-30 Var { + module_name: "", + ident: "y", + }, + @31-33 Equals, + ), + ], + @34-35 Var { + module_name: "", + ident: "z", + }, + ), + preceding_comment: @22-22, + }, + ], + }, + @37-39 SpaceBefore( + Num( + "42", + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc new file mode 100644 index 0000000000..b90a629804 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/expect_single_line.expr.roc @@ -0,0 +1,7 @@ +x = 5 + +expect x == y + +expect y == z + +42 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc new file mode 100644 index 0000000000..51a94ceb1e --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.formatted.roc @@ -0,0 +1,6 @@ +if foo then + x = a # 1 + x # 2 +else + # 3 + c # 4 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast new file mode 100644 index 0000000000..b9aa239ccb --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.result-ast @@ -0,0 +1,80 @@ +SpaceAfter( + If( + [ + ( + @3-6 Var { + module_name: "", + ident: "foo", + }, + @16-31 SpaceBefore( + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @16-21, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @16-17 Identifier { + ident: "x", + }, + @20-21 Var { + module_name: "", + ident: "a", + }, + ), + ], + }, + @30-31 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + LineComment( + " 1", + ), + ], + ), + ), + [ + LineComment( + " 2", + ), + ], + ), + [ + Newline, + ], + ), + ), + ], + @49-50 SpaceBefore( + Var { + module_name: "", + ident: "c", + }, + [ + LineComment( + " 3", + ), + ], + ), + ), + [ + LineComment( + " 4", + ), + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc new file mode 100644 index 0000000000..73f9076ea9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/extra_newline.expr.roc @@ -0,0 +1,5 @@ +if foo then + x = a # 1 + x # 2 +else # 3 + c # 4 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc new file mode 100644 index 0000000000..0f097ea6a8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.formatted.roc @@ -0,0 +1,4 @@ +f : (Str)a -> (Str)a +f = \x -> x + +f ("Str", 42) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast index 3f0d8f387e..1d55e4203c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_ext_type.expr.result-ast @@ -1,99 +1,104 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-32, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-1 Identifier { - ident: "f", - }, - ann_type: @4-20 Function( - [ - @4-10 Tuple { +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-32, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-1 Identifier { + ident: "f", + }, + ann_type: @4-20 Function( + [ + @4-10 Tuple { + elems: [ + @5-8 Apply( + "", + "Str", + [], + ), + ], + ext: Some( + @9-10 BoundVariable( + "a", + ), + ), + }, + ], + @14-20 Tuple { elems: [ - @5-8 Apply( + @15-18 Apply( "", "Str", [], ), ], ext: Some( - @9-10 BoundVariable( + @19-20 BoundVariable( "a", ), ), }, - ], - @14-20 Tuple { - elems: [ - @15-18 Apply( - "", - "Str", - [], - ), - ], - ext: Some( - @19-20 BoundVariable( - "a", - ), - ), + ), + comment: None, + body_pattern: @21-22 Identifier { + ident: "f", }, - ), - comment: None, - body_pattern: @21-22 Identifier { - ident: "f", - }, - body_expr: @25-32 Closure( - [ - @26-27 Identifier { + body_expr: @25-32 Closure( + [ + @26-27 Identifier { + ident: "x", + }, + ], + @31-32 Var { + module_name: "", ident: "x", }, - ], - @31-32 Var { - module_name: "", - ident: "x", - }, - ), - }, - ], - }, - @34-47 SpaceBefore( - Apply( - @34-35 Var { - module_name: "", - ident: "f", - }, - [ - @36-47 Tuple( - [ - @37-42 Str( - PlainLine( - "Str", - ), - ), - @44-46 Num( - "42", - ), - ], - ), + ), + }, + ], + }, + @34-47 SpaceBefore( + Apply( + @34-35 Var { + module_name: "", + ident: "f", + }, + [ + @36-47 Tuple( + [ + @37-42 Str( + PlainLine( + "Str", + ), + ), + @44-46 Num( + "42", + ), + ], + ), + ], + Space, + ), + [ + Newline, + Newline, ], - Space, ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc new file mode 100644 index 0000000000..98cad54ca6 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.formatted.roc @@ -0,0 +1,4 @@ +f : I64 -> (I64, I64) +f = \x -> (x, x + 1) + +f 42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast index c150ac7896..cd1051ffb2 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/function_with_tuple_type.expr.result-ast @@ -1,100 +1,105 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-42, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-1 Identifier { - ident: "f", - }, - ann_type: @4-21 Function( - [ - @4-7 Apply( - "", - "I64", - [], - ), - ], - @11-21 Tuple { - elems: [ - @12-15 Apply( - "", - "I64", - [], - ), - @17-20 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-42, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-1 Identifier { + ident: "f", + }, + ann_type: @4-21 Function( + [ + @4-7 Apply( "", "I64", [], ), ], - ext: None, - }, - ), - comment: None, - body_pattern: @22-23 Identifier { - ident: "f", - }, - body_expr: @26-42 Closure( - [ - @27-28 Identifier { - ident: "x", + @11-21 Tuple { + elems: [ + @12-15 Apply( + "", + "I64", + [], + ), + @17-20 Apply( + "", + "I64", + [], + ), + ], + ext: None, }, - ], - @32-42 Tuple( + ), + comment: None, + body_pattern: @22-23 Identifier { + ident: "f", + }, + body_expr: @26-42 Closure( [ - @33-34 Var { - module_name: "", + @27-28 Identifier { ident: "x", }, - @36-41 BinOps( - [ - ( - @36-37 Var { - module_name: "", - ident: "x", - }, - @38-39 Plus, - ), - ], - @40-41 Num( - "1", - ), - ), ], + @32-42 Tuple( + [ + @33-34 Var { + module_name: "", + ident: "x", + }, + @36-41 BinOps( + [ + ( + @36-37 Var { + module_name: "", + ident: "x", + }, + @38-39 Plus, + ), + ], + @40-41 Num( + "1", + ), + ), + ], + ), ), - ), - }, - ], - }, - @44-48 SpaceBefore( - Apply( - @44-45 Var { - module_name: "", - ident: "f", - }, - [ - @46-48 Num( - "42", - ), + }, + ], + }, + @44-48 SpaceBefore( + Apply( + @44-45 Var { + module_name: "", + ident: "f", + }, + [ + @46-48 Num( + "42", + ), + ], + Space, + ), + [ + Newline, + Newline, ], - Space, ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast index b5f729a47a..b36dc45d82 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/if_def.expr.result-ast @@ -1,37 +1,42 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-6, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "iffy", - }, - @5-6 Num( - "5", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-6, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-4 Identifier { + ident: "iffy", + }, + @5-6 Num( + "5", + ), ), + ], + }, + @8-10 SpaceBefore( + Num( + "42", ), - ], - }, - @8-10 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast index 8f808a2c9c..5a6793dd0d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ ModuleImport( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast index 148257a05c..c8338c771c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_from_package.moduledefs.result-ast @@ -13,20 +13,21 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - ], - space_after: [ Slice(start = 0, length = 1), Slice(start = 1, length = 1), Slice(start = 2, length = 1), - Slice(start = 3, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 1), ], spaces: [ Newline, Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast index d38b6a5171..d2f47eda5c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_alias.moduledefs.result-ast @@ -9,14 +9,15 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), + Slice(start = 0, length = 1), ], space_after: [ - Slice(start = 0, length = 1), - Slice(start = 1, length = 0), + Slice(start = 0, length = 0), + Slice(start = 1, length = 1), ], spaces: [ Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast index 332bcb876e..7d794b25e8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_comments.moduledefs.result-ast @@ -31,20 +31,6 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - Slice(start = 4, length = 0), - Slice(start = 6, length = 0), - Slice(start = 8, length = 0), - Slice(start = 10, length = 0), - Slice(start = 12, length = 0), - Slice(start = 14, length = 0), - Slice(start = 16, length = 0), - Slice(start = 18, length = 0), - Slice(start = 20, length = 0), - Slice(start = 23, length = 0), - Slice(start = 25, length = 0), - ], - space_after: [ Slice(start = 0, length = 2), Slice(start = 2, length = 2), Slice(start = 4, length = 2), @@ -57,7 +43,21 @@ Defs { Slice(start = 18, length = 2), Slice(start = 20, length = 3), Slice(start = 23, length = 2), - Slice(start = 25, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + Slice(start = 6, length = 0), + Slice(start = 8, length = 0), + Slice(start = 10, length = 0), + Slice(start = 12, length = 0), + Slice(start = 14, length = 0), + Slice(start = 16, length = 0), + Slice(start = 18, length = 0), + Slice(start = 20, length = 0), + Slice(start = 23, length = 0), + Slice(start = 25, length = 1), ], spaces: [ Newline, @@ -87,6 +87,7 @@ Defs { LineComment( " comment between imports", ), + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast index 27e3b2ca5e..cdf8ee1050 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_exposed.moduledefs.result-ast @@ -11,17 +11,18 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - ], - space_after: [ Slice(start = 0, length = 1), Slice(start = 1, length = 1), - Slice(start = 2, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast index d3dd6f81cb..94c9cf7137 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/import_with_params.moduledefs.result-ast @@ -13,20 +13,21 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - ], - space_after: [ Slice(start = 0, length = 1), Slice(start = 1, length = 1), Slice(start = 2, length = 1), - Slice(start = 3, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 1), ], spaces: [ Newline, Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc new file mode 100644 index 0000000000..9ccdd8a175 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.formatted.roc @@ -0,0 +1,6 @@ +fillBucketsFromData = \buckets0, data, shifts -> + buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0 + (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts + placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex + +foo \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast new file mode 100644 index 0000000000..59e7f66a64 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.result-ast @@ -0,0 +1,196 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-288, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-19 Identifier { + ident: "fillBucketsFromData", + }, + @22-288 Closure( + [ + @23-31 Identifier { + ident: "buckets0", + }, + @33-37 Identifier { + ident: "data", + }, + @39-45 Identifier { + ident: "shifts", + }, + ], + @53-288 SpaceBefore( + Backpassing( + [ + @53-61 Identifier { + ident: "buckets1", + }, + @63-71 Tuple( + [ + @64-67 Identifier { + ident: "key", + }, + @69-70 Underscore( + "", + ), + ], + ), + @73-82 Identifier { + ident: "dataIndex", + }, + ], + @86-118 Apply( + @86-104 Var { + module_name: "List", + ident: "walkWithIndex", + }, + [ + @105-109 Var { + module_name: "", + ident: "data", + }, + @110-118 Var { + module_name: "", + ident: "buckets0", + }, + ], + Space, + ), + @123-288 Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @123-192, + ], + space_before: [ + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @123-156 Tuple( + [ + @124-135 Identifier { + ident: "bucketIndex", + }, + @137-155 Identifier { + ident: "distAndFingerprint", + }, + ], + ), + @159-192 Apply( + @159-172 Var { + module_name: "", + ident: "nextWhileLess", + }, + [ + @173-181 Var { + module_name: "", + ident: "buckets1", + }, + @182-185 Var { + module_name: "", + ident: "key", + }, + @186-192 Var { + module_name: "", + ident: "shifts", + }, + ], + Space, + ), + ), + ], + }, + @197-288 SpaceBefore( + Apply( + @197-212 Var { + module_name: "", + ident: "placeAndShiftUp", + }, + [ + @213-221 Var { + module_name: "", + ident: "buckets1", + }, + @222-276 Record( + [ + @224-242 LabelOnly( + @224-242 "distAndFingerprint", + ), + @244-274 RequiredValue( + @244-253 "dataIndex", + [], + @255-274 Apply( + @255-264 Var { + module_name: "Num", + ident: "toU32", + }, + [ + @265-274 Var { + module_name: "", + ident: "dataIndex", + }, + ], + Space, + ), + ), + ], + ), + @277-288 Var { + module_name: "", + ident: "bucketIndex", + }, + ], + Space, + ), + [ + Newline, + ], + ), + ), + ), + [ + Newline, + ], + ), + ), + ), + ], + }, + @290-293 SpaceBefore( + Var { + module_name: "", + ident: "foo", + }, + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc new file mode 100644 index 0000000000..c1bac9b614 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/indented_after_multi_backpassing.expr.roc @@ -0,0 +1,6 @@ +fillBucketsFromData = \buckets0, data, shifts -> + buckets1, (key, _), dataIndex <- List.walkWithIndex data buckets0 + (bucketIndex, distAndFingerprint) = nextWhileLess buckets1 key shifts + placeAndShiftUp buckets1 { distAndFingerprint, dataIndex: Num.toU32 dataIndex } bucketIndex + +foo diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast index b15064f593..f14ad2b55b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/ingested_file.moduledefs.result-ast @@ -9,14 +9,15 @@ Defs { ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 1, length = 0), + Slice(start = 0, length = 1), ], space_after: [ - Slice(start = 0, length = 1), - Slice(start = 1, length = 0), + Slice(start = 0, length = 0), + Slice(start = 1, length = 1), ], spaces: [ Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc new file mode 100644 index 0000000000..9289474d2c --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.formatted.roc @@ -0,0 +1,4 @@ +import Json exposing [int] +import Json.Encode as JE + +JE.encode (int 42) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast index 8e58215fa3..4a34bd020f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_import.expr.result-ast @@ -1,103 +1,112 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @0-26, - @27-51, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - space_after: [ - Slice(start = 0, length = 1), - Slice(start = 1, length = 2), - ], - spaces: [ - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - ModuleImport( - ModuleImport { - before_name: [], - name: @7-11 ImportedModuleName { - package: None, - name: ModuleName( - "Json", - ), - }, - params: None, - alias: None, - exposed: Some( - KeywordItem { - keyword: Spaces { - before: [], - item: ImportExposingKeyword, - after: [], - }, - item: [ - @22-25 ExposedName( - "int", - ), - ], - }, - ), - }, - ), - ModuleImport( - ModuleImport { - before_name: [], - name: @34-45 ImportedModuleName { - package: None, - name: ModuleName( - "Json.Encode", - ), - }, - params: None, - alias: Some( - KeywordItem { - keyword: Spaces { - before: [], - item: ImportAsKeyword, - after: [], - }, - item: @49-51 ImportAlias( - "JE", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @0-26, + @27-51, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + ModuleImport( + ModuleImport { + before_name: [], + name: @7-11 ImportedModuleName { + package: None, + name: ModuleName( + "Json", ), }, - ), - exposed: None, - }, - ), - ], - }, - @53-71 Apply( - @53-62 Var { - module_name: "JE", - ident: "encode", - }, - [ - @64-70 ParensAround( - Apply( - @64-67 Var { - module_name: "", - ident: "int", - }, - [ - @68-70 Num( - "42", + params: None, + alias: None, + exposed: Some( + KeywordItem { + keyword: Spaces { + before: [], + item: ImportExposingKeyword, + after: [], + }, + item: [ + @22-25 ExposedName( + "int", + ), + ], + }, ), - ], - Space, + }, ), + ModuleImport( + ModuleImport { + before_name: [], + name: @34-45 ImportedModuleName { + package: None, + name: ModuleName( + "Json.Encode", + ), + }, + params: None, + alias: Some( + KeywordItem { + keyword: Spaces { + before: [], + item: ImportAsKeyword, + after: [], + }, + item: @49-51 ImportAlias( + "JE", + ), + }, + ), + exposed: None, + }, + ), + ], + }, + @53-71 SpaceBefore( + Apply( + @53-62 Var { + module_name: "JE", + ident: "encode", + }, + [ + @64-70 ParensAround( + Apply( + @64-67 Var { + module_name: "", + ident: "int", + }, + [ + @68-70 Num( + "42", + ), + ], + Space, + ), + ), + ], + Space, ), - ], - Space, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc new file mode 100644 index 0000000000..743aa4e3b7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.formatted.roc @@ -0,0 +1,3 @@ +import "users.json" as data : Str + +parseJson data \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast index c71b523d09..7323b043f4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file.expr.result-ast @@ -1,62 +1,70 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-33, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 2), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - IngestedFileImport( - IngestedFileImport { - before_path: [], - path: @7-19 PlainLine( - "users.json", - ), - name: KeywordItem { - keyword: Spaces { - before: [], - item: ImportAsKeyword, - after: [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-33, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + IngestedFileImport( + IngestedFileImport { + before_path: [], + path: @7-19 PlainLine( + "users.json", + ), + name: KeywordItem { + keyword: Spaces { + before: [], + item: ImportAsKeyword, + after: [], + }, + item: @23-27 "data", }, - item: @23-27 "data", + annotation: Some( + IngestedFileAnnotation { + before_colon: [], + annotation: @30-33 Apply( + "", + "Str", + [], + ), + }, + ), }, - annotation: Some( - IngestedFileAnnotation { - before_colon: [], - annotation: @30-33 Apply( - "", - "Str", - [], - ), - }, - ), - }, - ), - ], - }, - @35-49 Apply( - @35-44 Var { - module_name: "", - ident: "parseJson", + ), + ], }, - [ - @45-49 Var { - module_name: "", - ident: "data", - }, - ], - Space, + @35-49 SpaceBefore( + Apply( + @35-44 Var { + module_name: "", + ident: "parseJson", + }, + [ + @45-49 Var { + module_name: "", + ident: "data", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc new file mode 100644 index 0000000000..39a0036607 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.formatted.roc @@ -0,0 +1,3 @@ +import "users.json" as data + +parseJson data \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast index 8a91304d12..00a09fe467 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/inline_ingested_file_no_ann.expr.result-ast @@ -1,53 +1,61 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-27, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 2), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - IngestedFileImport( - IngestedFileImport { - before_path: [], - path: @7-19 PlainLine( - "users.json", - ), - name: KeywordItem { - keyword: Spaces { - before: [], - item: ImportAsKeyword, - after: [], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-27, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + IngestedFileImport( + IngestedFileImport { + before_path: [], + path: @7-19 PlainLine( + "users.json", + ), + name: KeywordItem { + keyword: Spaces { + before: [], + item: ImportAsKeyword, + after: [], + }, + item: @23-27 "data", }, - item: @23-27 "data", + annotation: None, }, - annotation: None, - }, - ), - ], - }, - @29-43 Apply( - @29-38 Var { - module_name: "", - ident: "parseJson", + ), + ], }, - [ - @39-43 Var { - module_name: "", - ident: "data", - }, - ], - Space, + @29-43 SpaceBefore( + Apply( + @29-38 Var { + module_name: "", + ident: "parseJson", + }, + [ + @39-43 Var { + module_name: "", + ident: "data", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast index 48f73696b8..85c58d4317 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_indent_not_enough.expr.result-ast @@ -1,90 +1,95 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-57, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Identifier { - ident: "myList", - }, - @9-57 List( - Collection { - items: [ - @15-16 SpaceBefore( - Num( - "0", - ), - [ - Newline, - ], - ), - @22-47 SpaceBefore( - List( - Collection { - items: [ - @32-33 SpaceBefore( - Var { - module_name: "", - ident: "a", - }, - [ - Newline, - ], - ), - @43-44 SpaceBefore( - Var { - module_name: "", - ident: "b", - }, - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, - ), - [ - Newline, - ], - ), - @53-54 SpaceBefore( - Num( - "1", - ), - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-57, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-6 Identifier { + ident: "myList", }, + @9-57 List( + Collection { + items: [ + @15-16 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], + ), + @22-47 SpaceBefore( + List( + Collection { + items: [ + @32-33 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + ], + ), + @43-44 SpaceBefore( + Var { + module_name: "", + ident: "b", + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), + [ + Newline, + ], + ), + @53-54 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), ), + ], + }, + @58-60 SpaceBefore( + Num( + "42", ), - ], - }, - @58-60 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast index f9f33d2f02..867438c338 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_no_trailing_comma.expr.result-ast @@ -1,58 +1,63 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-25, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Identifier { - ident: "myList", - }, - @9-25 List( - [ - @15-16 SpaceBefore( - Num( - "0", - ), - [ - Newline, - ], - ), - @22-23 SpaceBefore( - SpaceAfter( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-25, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-6 Identifier { + ident: "myList", + }, + @9-25 List( + [ + @15-16 SpaceBefore( Num( - "1", + "0", ), [ Newline, ], ), - [ - Newline, - ], - ), - ], + @22-23 SpaceBefore( + SpaceAfter( + Num( + "1", + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ], + ), ), + ], + }, + @26-28 SpaceBefore( + Num( + "42", ), - ], - }, - @26-28 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc new file mode 100644 index 0000000000..5a937217c9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.formatted.roc @@ -0,0 +1,5 @@ +myList = [ + 0, + 1, +] +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast index 622b1b9d9d..99d07201ab 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_closing_same_indent_with_trailing_comma.expr.result-ast @@ -1,58 +1,63 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Identifier { - ident: "myList", - }, - @9-26 List( - Collection { - items: [ - @15-16 SpaceBefore( - Num( - "0", - ), - [ - Newline, - ], - ), - @22-23 SpaceBefore( - Num( - "1", - ), - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-26, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-6 Identifier { + ident: "myList", }, + @9-26 List( + Collection { + items: [ + @15-16 SpaceBefore( + Num( + "0", + ), + [ + Newline, + ], + ), + @22-23 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + ), ), + ], + }, + @27-29 SpaceBefore( + Num( + "42", ), - ], - }, - @27-29 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast index 25dfb5a65a..11c170d545 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_pattern_weird_indent.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-7 List( - [], - ), - [ - WhenBranch { - patterns: [ - @15-24 SpaceBefore( - List( - [ - @16-17 NumLiteral( - "1", - ), - @19-20 NumLiteral( - "2", - ), - @22-23 SpaceBefore( - NumLiteral( - "3", +SpaceAfter( + When( + @5-7 List( + [], + ), + [ + WhenBranch { + patterns: [ + @15-24 SpaceBefore( + List( + [ + @16-17 NumLiteral( + "1", ), - [ - Newline, - ], - ), + @19-20 NumLiteral( + "2", + ), + @22-23 SpaceBefore( + NumLiteral( + "3", + ), + [ + Newline, + ], + ), + ], + ), + [ + Newline, ], ), - [ - Newline, - ], + ], + value: @28-30 Str( + PlainLine( + "", + ), ), - ], - value: @28-30 Str( - PlainLine( - "", - ), - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast index 99f74fc8cb..5d31966b51 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/list_patterns.expr.result-ast @@ -1,228 +1,233 @@ -When( - @5-7 List( - [], +SpaceAfter( + When( + @5-7 List( + [], + ), + [ + WhenBranch { + patterns: [ + @13-15 SpaceBefore( + List( + [], + ), + [ + Newline, + ], + ), + ], + value: @19-21 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @24-28 SpaceBefore( + List( + [ + @25-27 ListRest( + None, + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @32-34 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @37-51 SpaceBefore( + List( + [ + @38-39 Underscore( + "", + ), + @41-43 ListRest( + None, + ), + @45-46 Underscore( + "", + ), + @48-50 ListRest( + None, + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @55-57 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @60-72 SpaceBefore( + List( + [ + @61-62 Identifier { + ident: "a", + }, + @64-65 Identifier { + ident: "b", + }, + @67-68 Identifier { + ident: "c", + }, + @70-71 Identifier { + ident: "d", + }, + ], + ), + [ + Newline, + ], + ), + ], + value: @76-78 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @81-91 SpaceBefore( + List( + [ + @82-83 Identifier { + ident: "a", + }, + @85-86 Identifier { + ident: "b", + }, + @88-90 ListRest( + None, + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @95-97 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @100-110 SpaceBefore( + List( + [ + @101-103 ListRest( + None, + ), + @105-106 Identifier { + ident: "c", + }, + @108-109 Identifier { + ident: "d", + }, + ], + ), + [ + Newline, + ], + ), + ], + value: @114-116 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @119-135 SpaceBefore( + List( + [ + @120-123 List( + [ + @121-122 Tag( + "A", + ), + ], + ), + @125-129 List( + [ + @126-128 ListRest( + None, + ), + ], + ), + @131-134 List( + [ + @132-133 Identifier { + ident: "a", + }, + ], + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @139-141 Record( + [], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @144-163 SpaceBefore( + List( + [ + @145-153 List( + [ + @146-148 List( + [], + ), + @150-152 List( + [], + ), + ], + ), + @155-162 List( + [ + @156-158 List( + [], + ), + @160-161 Identifier { + ident: "x", + }, + ], + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @167-169 Record( + [], + ), + guard: None, + }, + ], ), [ - WhenBranch { - patterns: [ - @13-15 SpaceBefore( - List( - [], - ), - [ - Newline, - ], - ), - ], - value: @19-21 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @24-28 SpaceBefore( - List( - [ - @25-27 ListRest( - None, - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @32-34 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @37-51 SpaceBefore( - List( - [ - @38-39 Underscore( - "", - ), - @41-43 ListRest( - None, - ), - @45-46 Underscore( - "", - ), - @48-50 ListRest( - None, - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @55-57 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @60-72 SpaceBefore( - List( - [ - @61-62 Identifier { - ident: "a", - }, - @64-65 Identifier { - ident: "b", - }, - @67-68 Identifier { - ident: "c", - }, - @70-71 Identifier { - ident: "d", - }, - ], - ), - [ - Newline, - ], - ), - ], - value: @76-78 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @81-91 SpaceBefore( - List( - [ - @82-83 Identifier { - ident: "a", - }, - @85-86 Identifier { - ident: "b", - }, - @88-90 ListRest( - None, - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @95-97 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @100-110 SpaceBefore( - List( - [ - @101-103 ListRest( - None, - ), - @105-106 Identifier { - ident: "c", - }, - @108-109 Identifier { - ident: "d", - }, - ], - ), - [ - Newline, - ], - ), - ], - value: @114-116 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @119-135 SpaceBefore( - List( - [ - @120-123 List( - [ - @121-122 Tag( - "A", - ), - ], - ), - @125-129 List( - [ - @126-128 ListRest( - None, - ), - ], - ), - @131-134 List( - [ - @132-133 Identifier { - ident: "a", - }, - ], - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @139-141 Record( - [], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @144-163 SpaceBefore( - List( - [ - @145-153 List( - [ - @146-148 List( - [], - ), - @150-152 List( - [], - ), - ], - ), - @155-162 List( - [ - @156-158 List( - [], - ), - @160-161 Identifier { - ident: "x", - }, - ], - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @167-169 Record( - [], - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc deleted file mode 100644 index 1ee25711f7..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/minimal_app_header.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -app [] {} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc new file mode 100644 index 0000000000..0160e797ba --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.formatted.roc @@ -0,0 +1,7 @@ +### not docs! +## docs, but with a problem +## (namely that this is a mix of docs and regular comments) +# not docs +x = 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast index ec6f2288f3..0179f6a3cd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/mixed_docs.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @113-118, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @113-114 Identifier { - ident: "x", - }, - @117-118 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @113-118, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @113-114 Identifier { + ident: "x", + }, + @117-118 Num( + "5", + ), ), + ], + }, + @120-122 SpaceBefore( + Num( + "42", ), - ], - }, - @120-122 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc deleted file mode 100644 index c14cf34253..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -main = - i = 64 - - i diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast index e333a08770..5c8706f54b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/module_def_newline.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc deleted file mode 100644 index 9fd98c5b23..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/module_with_newline.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -module [] diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc new file mode 100644 index 0000000000..d4c0fbdec3 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.formatted.roc @@ -0,0 +1,3 @@ +x, y <- List.map2 [] [] + +x + y \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast index ba2df50c34..9b33085515 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing.expr.result-ast @@ -1,46 +1,51 @@ -Backpassing( - [ - @0-1 Identifier { - ident: "x", - }, - @3-4 Identifier { - ident: "y", - }, - ], - @8-23 Apply( - @8-17 Var { - module_name: "List", - ident: "map2", - }, +SpaceAfter( + Backpassing( [ - @18-20 List( - [], - ), - @21-23 List( - [], - ), - ], - Space, - ), - @25-30 SpaceBefore( - BinOps( - [ - ( - @25-26 Var { - module_name: "", - ident: "x", - }, - @27-28 Plus, - ), - ], - @29-30 Var { - module_name: "", + @0-1 Identifier { + ident: "x", + }, + @3-4 Identifier { ident: "y", }, - ), - [ - Newline, - Newline, ], + @8-23 Apply( + @8-17 Var { + module_name: "List", + ident: "map2", + }, + [ + @18-20 List( + [], + ), + @21-23 List( + [], + ), + ], + Space, + ), + @25-30 SpaceBefore( + BinOps( + [ + ( + @25-26 Var { + module_name: "", + ident: "x", + }, + @27-28 Plus, + ), + ], + @29-30 Var { + module_name: "", + ident: "y", + }, + ), + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast index a94ee98745..5249cd6a17 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_backpassing_in_def.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc new file mode 100644 index 0000000000..7e9668e783 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.formatted.roc @@ -0,0 +1 @@ +"foo" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast index 21f0b7d0a6..a090b1b353 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multi_char_string.expr.result-ast @@ -1,5 +1,10 @@ -Str( - PlainLine( - "foo", +SpaceAfter( + Str( + PlainLine( + "foo", + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc new file mode 100644 index 0000000000..057218582a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.formatted.roc @@ -0,0 +1,25 @@ +when + x + + 1 # comment 1 + > 0 # comment 2 +is + y -> + 3 + * 2 # comment 3 + < 1 # comment 4 + + z -> + 4 + / 5 # comment 5 + < 1 # comment 6 + + 46 # first pattern comment + | 95 # alternative comment 1 + | 126 # alternative comment 2 + | 150 -> # This comment came after the -> + # This comment is for the expr + foo bar + |> Result.withDefault "" # one last comment + + _ -> + 42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast new file mode 100644 index 0000000000..4827eabd52 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.result-ast @@ -0,0 +1,268 @@ +When( + @9-38 SpaceBefore( + SpaceAfter( + BinOps( + [ + ( + @9-10 SpaceAfter( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), + @15-16 Plus, + ), + ( + @17-18 SpaceAfter( + Num( + "1", + ), + [ + LineComment( + " comment 1", + ), + ], + ), + @35-36 GreaterThan, + ), + ], + @37-38 Num( + "0", + ), + ), + [ + LineComment( + " comment 2", + ), + ], + ), + [ + Newline, + ], + ), + [ + WhenBranch { + patterns: [ + @58-59 SpaceBefore( + Identifier { + ident: "y", + }, + [ + Newline, + ], + ), + ], + value: @71-108 SpaceBefore( + BinOps( + [ + ( + @71-72 SpaceAfter( + Num( + "3", + ), + [ + Newline, + ], + ), + @81-82 Star, + ), + ( + @83-84 SpaceAfter( + Num( + "2", + ), + [ + LineComment( + " comment 3", + ), + ], + ), + @105-106 LessThan, + ), + ], + @107-108 Num( + "1", + ), + ), + [ + Newline, + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @126-127 SpaceBefore( + Identifier { + ident: "z", + }, + [ + LineComment( + " comment 4", + ), + Newline, + ], + ), + ], + value: @139-189 SpaceBefore( + BinOps( + [ + ( + @139-140 SpaceAfter( + Num( + "4", + ), + [ + Newline, + ], + ), + @153-154 Slash, + ), + ( + @155-156 SpaceAfter( + Num( + "5", + ), + [ + LineComment( + " comment 5", + ), + ], + ), + @186-187 LessThan, + ), + ], + @188-189 Num( + "1", + ), + ), + [ + Newline, + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @210-212 SpaceBefore( + SpaceAfter( + NumLiteral( + "46", + ), + [ + LineComment( + " first pattern comment", + ), + ], + ), + [ + LineComment( + " comment 6", + ), + Newline, + ], + ), + @243-245 SpaceAfter( + NumLiteral( + "95", + ), + [ + LineComment( + " alternative comment 1", + ), + ], + ), + @276-279 SpaceAfter( + NumLiteral( + "126", + ), + [ + LineComment( + " alternative comment 2", + ), + ], + ), + @310-313 NumLiteral( + "150", + ), + ], + value: @401-449 SpaceBefore( + BinOps( + [ + ( + @401-408 SpaceAfter( + Apply( + @401-404 Var { + module_name: "", + ident: "foo", + }, + [ + @405-408 Var { + module_name: "", + ident: "bar", + }, + ], + Space, + ), + [ + Newline, + ], + ), + @425-427 Pizza, + ), + ], + @428-449 Apply( + @428-446 Var { + module_name: "Result", + ident: "withDefault", + }, + [ + @447-449 Str( + PlainLine( + "", + ), + ), + ], + Space, + ), + ), + [ + LineComment( + " This comment came after the ->", + ), + LineComment( + " This comment is for the expr", + ), + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @474-475 SpaceBefore( + Underscore( + "", + ), + [ + LineComment( + " one last comment", + ), + Newline, + ], + ), + ], + value: @487-489 SpaceBefore( + Num( + "42", + ), + [ + Newline, + ], + ), + guard: None, + }, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc new file mode 100644 index 0000000000..1ae16e1f18 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_binop_when_with_comments.expr.roc @@ -0,0 +1,25 @@ +when + x + + 1 # comment 1 + > 0 # comment 2 +is + y -> + 3 + * 2 # comment 3 + < 1 # comment 4 + + z -> + 4 + / 5 # comment 5 + < 1 # comment 6 + + 46 # first pattern comment + | 95 # alternative comment 1 + | 126 # alternative comment 2 + | 150 -> # This comment came after the -> + # This comment is for the expr + foo bar + |> Result.withDefault "" # one last comment + + _ -> + 42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc new file mode 100644 index 0000000000..be56926ac8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.formatted.roc @@ -0,0 +1,4 @@ +1 + """ + """ "^" 2 : A +"" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast new file mode 100644 index 0000000000..3cd900154f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.result-ast @@ -0,0 +1,62 @@ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-13, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Apply( + @0-1 NumLiteral( + "1", + ), + [ + @1-7 StrLiteral( + Block( + [], + ), + ), + @7-10 StrLiteral( + PlainLine( + "^", + ), + ), + @10-11 NumLiteral( + "2", + ), + ], + ), + @12-13 Apply( + "", + "A", + [], + ), + ), + ], + }, + @14-16 SpaceBefore( + Str( + PlainLine( + "", + ), + ), + [ + Newline, + ], + ), + ), + [ + Newline, + ], +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc new file mode 100644 index 0000000000..59d9b1d8ee --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_str_in_pat.expr.roc @@ -0,0 +1,2 @@ +1"""""""^"2:A +"" diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast index 708bdb8a46..a29131e320 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/multiline_string.expr.result-ast @@ -1,61 +1,38 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), - ], - regions: [ - @0-22, - @23-49, - @50-92, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "a", - }, - @4-22 Str( - Line( - [ - Plaintext( - "Hello,", - ), - EscapedChar( - Newline, - ), - EscapedChar( - Newline, - ), - Plaintext( - "World!", - ), - ], - ), - ), - ), - Body( - @23-24 Identifier { - ident: "b", - }, - @27-49 Str( - Block( - [ +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @0-22, + @23-49, + @50-92, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "a", + }, + @4-22 Str( + Line( [ Plaintext( "Hello,", @@ -70,24 +47,25 @@ Defs( "World!", ), ], - ], + ), ), ), - ), - Body( - @50-51 Identifier { - ident: "c", - }, - @58-92 SpaceBefore( - Str( + Body( + @23-24 Identifier { + ident: "b", + }, + @27-49 Str( Block( [ [ Plaintext( - "Hello,\n", + "Hello,", ), - Plaintext( - "\n", + EscapedChar( + Newline, + ), + EscapedChar( + Newline, ), Plaintext( "World!", @@ -96,19 +74,46 @@ Defs( ], ), ), - [ - Newline, - ], ), + Body( + @50-51 Identifier { + ident: "c", + }, + @58-92 SpaceBefore( + Str( + Block( + [ + [ + Plaintext( + "Hello,\n", + ), + Plaintext( + "\n", + ), + Plaintext( + "World!", + ), + ], + ], + ), + ), + [ + Newline, + ], + ), + ), + ], + }, + @93-95 SpaceBefore( + Num( + "42", ), - ], - }, - @93-95 SpaceBefore( - Num( - "42", + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc deleted file mode 100644 index 28f581a04d..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.formatted.roc +++ /dev/null @@ -1,7 +0,0 @@ -main = - task = - file <- - foo - bar - task -42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast deleted file mode 100644 index ace33dc1fe..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.result-ast +++ /dev/null @@ -1,97 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-71, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-4 Identifier { - ident: "main", - }, - @11-71 SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @11-62, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @11-15 Identifier { - ident: "task", - }, - @18-62 Backpassing( - [ - @18-22 Identifier { - ident: "file", - }, - ], - @43-46 SpaceBefore( - Var { - module_name: "", - ident: "foo", - }, - [ - Newline, - ], - ), - @59-62 SpaceBefore( - Var { - module_name: "", - ident: "bar", - }, - [ - Newline, - ], - ), - ), - ), - ], - }, - @67-71 SpaceBefore( - Var { - module_name: "", - ident: "task", - }, - [ - Newline, - ], - ), - ), - [ - Newline, - ], - ), - ), - ], - }, - @72-74 SpaceBefore( - Num( - "42", - ), - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc deleted file mode 100644 index d5ced4d9f7..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_backpassing_no_newline_before.expr.roc +++ /dev/null @@ -1,6 +0,0 @@ -main = - task = file <- - foo - bar - task -42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc deleted file mode 100644 index a7d12703ae..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.formatted.roc +++ /dev/null @@ -1,6 +0,0 @@ -main = - wrappedNotEq : a, a -> Bool - wrappedNotEq = \num1, num2 -> - num1 != num2 - - wrappedNotEq 2 3 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast index fe6b7bbee4..df05b75201 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_annotation.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [], value_defs: [ Body( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc deleted file mode 100644 index 1f29cd8a05..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -x = - a : n - 4 -_ \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast deleted file mode 100644 index c0cf621ea6..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.result-ast +++ /dev/null @@ -1,64 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-7, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @2-7 Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @2-5, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @2-3 Identifier { - ident: "a", - }, - @4-5 BoundVariable( - "n", - ), - ), - ], - }, - @6-7 Num( - "4", - ), - ), - ), - ], - }, - @8-9 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc deleted file mode 100644 index 35709dbea4..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_def_without_newline.expr.roc +++ /dev/null @@ -1,2 +0,0 @@ -x=a:n 4 -_ \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast index c4542c48eb..a393541c92 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/nested_if.expr.result-ast @@ -1,50 +1,55 @@ -If( - [ - ( - @3-5 Var { - module_name: "", - ident: "t1", - }, - @13-14 SpaceBefore( - SpaceAfter( - Num( - "1", - ), - [ - Newline, - ], - ), - [ - Newline, - ], - ), - ), - ( - @23-25 Var { - module_name: "", - ident: "t2", - }, - @33-34 SpaceBefore( - SpaceAfter( - Num( - "2", - ), - [ - Newline, - ], - ), - [ - Newline, - ], - ), - ), - ], - @42-43 SpaceBefore( - Num( - "3", - ), +SpaceAfter( + If( [ - Newline, + ( + @3-5 Var { + module_name: "", + ident: "t1", + }, + @13-14 SpaceBefore( + SpaceAfter( + Num( + "1", + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ), + ( + @23-25 Var { + module_name: "", + ident: "t2", + }, + @33-34 SpaceBefore( + SpaceAfter( + Num( + "2", + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ), ], + @42-43 SpaceBefore( + Num( + "3", + ), + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc new file mode 100644 index 0000000000..efd7601492 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.formatted.roc @@ -0,0 +1,4 @@ +x = + 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast index 87a156b495..2a7da5e664 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_after_equals.expr.result-ast @@ -1,42 +1,47 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-9, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @8-9 SpaceBefore( - Num( - "5", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-9, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", + }, + @8-9 SpaceBefore( + Num( + "5", + ), + [ + Newline, + ], ), - [ - Newline, - ], ), + ], + }, + @11-13 SpaceBefore( + Num( + "42", ), - ], - }, - @11-13 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast deleted file mode 100644 index 4a401f61bb..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_before_operator_with_defs.expr.result-ast +++ /dev/null @@ -1,49 +0,0 @@ -BinOps( - [ - ( - @0-1 SpaceAfter( - Num( - "7", - ), - [ - Newline, - ], - ), - @2-4 Equals, - ), - ], - @5-11 ParensAround( - Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @5-8, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Alias { - header: TypeHeader { - name: @5-6 "Q", - vars: [], - }, - ann: @7-8 BoundVariable( - "c", - ), - }, - ], - value_defs: [], - }, - @9-11 Num( - "42", - ), - ), - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast index 07e8f8fc19..616fe065f8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/newline_in_packages.full.result-ast @@ -43,11 +43,12 @@ Full { Slice(start = 0, length = 2), ], space_after: [ - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast index 9fafa599ed..b3e603bc2d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/number_literal_suffixes.expr.result-ast @@ -1,413 +1,418 @@ -Record( - Collection { - items: [ - @4-15 SpaceBefore( - RequiredValue( - @4-6 "u8", - [], - @10-15 Num( - "123u8", +SpaceAfter( + Record( + Collection { + items: [ + @4-15 SpaceBefore( + RequiredValue( + @4-6 "u8", + [], + @10-15 Num( + "123u8", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @19-31 SpaceBefore( - RequiredValue( - @19-22 "u16", - [], - @25-31 Num( - "123u16", + @19-31 SpaceBefore( + RequiredValue( + @19-22 "u16", + [], + @25-31 Num( + "123u16", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @35-47 SpaceBefore( - RequiredValue( - @35-38 "u32", - [], - @41-47 Num( - "123u32", + @35-47 SpaceBefore( + RequiredValue( + @35-38 "u32", + [], + @41-47 Num( + "123u32", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @51-63 SpaceBefore( - RequiredValue( - @51-54 "u64", - [], - @57-63 Num( - "123u64", + @51-63 SpaceBefore( + RequiredValue( + @51-54 "u64", + [], + @57-63 Num( + "123u64", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @67-80 SpaceBefore( - RequiredValue( - @67-71 "u128", - [], - @73-80 Num( - "123u128", + @67-80 SpaceBefore( + RequiredValue( + @67-71 "u128", + [], + @73-80 Num( + "123u128", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @84-95 SpaceBefore( - RequiredValue( - @84-86 "i8", - [], - @90-95 Num( - "123i8", + @84-95 SpaceBefore( + RequiredValue( + @84-86 "i8", + [], + @90-95 Num( + "123i8", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @99-111 SpaceBefore( - RequiredValue( - @99-102 "i16", - [], - @105-111 Num( - "123i16", + @99-111 SpaceBefore( + RequiredValue( + @99-102 "i16", + [], + @105-111 Num( + "123i16", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @115-127 SpaceBefore( - RequiredValue( - @115-118 "i32", - [], - @121-127 Num( - "123i32", + @115-127 SpaceBefore( + RequiredValue( + @115-118 "i32", + [], + @121-127 Num( + "123i32", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @131-143 SpaceBefore( - RequiredValue( - @131-134 "i64", - [], - @137-143 Num( - "123i64", + @131-143 SpaceBefore( + RequiredValue( + @131-134 "i64", + [], + @137-143 Num( + "123i64", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @147-160 SpaceBefore( - RequiredValue( - @147-151 "i128", - [], - @153-160 Num( - "123i128", + @147-160 SpaceBefore( + RequiredValue( + @147-151 "i128", + [], + @153-160 Num( + "123i128", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @164-176 SpaceBefore( - RequiredValue( - @164-167 "dec", - [], - @170-176 Num( - "123dec", + @164-176 SpaceBefore( + RequiredValue( + @164-167 "dec", + [], + @170-176 Num( + "123dec", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @180-195 SpaceBefore( - RequiredValue( - @180-185 "u8Neg", - [], - @189-195 Num( - "-123u8", + @180-195 SpaceBefore( + RequiredValue( + @180-185 "u8Neg", + [], + @189-195 Num( + "-123u8", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @199-215 SpaceBefore( - RequiredValue( - @199-205 "u16Neg", - [], - @208-215 Num( - "-123u16", + @199-215 SpaceBefore( + RequiredValue( + @199-205 "u16Neg", + [], + @208-215 Num( + "-123u16", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @219-235 SpaceBefore( - RequiredValue( - @219-225 "u32Neg", - [], - @228-235 Num( - "-123u32", + @219-235 SpaceBefore( + RequiredValue( + @219-225 "u32Neg", + [], + @228-235 Num( + "-123u32", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @239-255 SpaceBefore( - RequiredValue( - @239-245 "u64Neg", - [], - @248-255 Num( - "-123u64", + @239-255 SpaceBefore( + RequiredValue( + @239-245 "u64Neg", + [], + @248-255 Num( + "-123u64", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @259-276 SpaceBefore( - RequiredValue( - @259-266 "u128Neg", - [], - @268-276 Num( - "-123u128", + @259-276 SpaceBefore( + RequiredValue( + @259-266 "u128Neg", + [], + @268-276 Num( + "-123u128", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @280-295 SpaceBefore( - RequiredValue( - @280-285 "i8Neg", - [], - @289-295 Num( - "-123i8", + @280-295 SpaceBefore( + RequiredValue( + @280-285 "i8Neg", + [], + @289-295 Num( + "-123i8", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @299-315 SpaceBefore( - RequiredValue( - @299-305 "i16Neg", - [], - @308-315 Num( - "-123i16", + @299-315 SpaceBefore( + RequiredValue( + @299-305 "i16Neg", + [], + @308-315 Num( + "-123i16", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @319-335 SpaceBefore( - RequiredValue( - @319-325 "i32Neg", - [], - @328-335 Num( - "-123i32", + @319-335 SpaceBefore( + RequiredValue( + @319-325 "i32Neg", + [], + @328-335 Num( + "-123i32", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @339-355 SpaceBefore( - RequiredValue( - @339-345 "i64Neg", - [], - @348-355 Num( - "-123i64", + @339-355 SpaceBefore( + RequiredValue( + @339-345 "i64Neg", + [], + @348-355 Num( + "-123i64", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @359-376 SpaceBefore( - RequiredValue( - @359-366 "i128Neg", - [], - @368-376 Num( - "-123i128", + @359-376 SpaceBefore( + RequiredValue( + @359-366 "i128Neg", + [], + @368-376 Num( + "-123i128", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @380-396 SpaceBefore( - RequiredValue( - @380-386 "decNeg", - [], - @389-396 Num( - "-123dec", + @380-396 SpaceBefore( + RequiredValue( + @380-386 "decNeg", + [], + @389-396 Num( + "-123dec", + ), ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @400-416 SpaceBefore( - RequiredValue( - @400-405 "u8Bin", - [], - @409-416 NonBase10Int { - string: "101u8", - base: Binary, - is_negative: false, - }, + @400-416 SpaceBefore( + RequiredValue( + @400-405 "u8Bin", + [], + @409-416 NonBase10Int { + string: "101u8", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @420-437 SpaceBefore( - RequiredValue( - @420-426 "u16Bin", - [], - @429-437 NonBase10Int { - string: "101u16", - base: Binary, - is_negative: false, - }, + @420-437 SpaceBefore( + RequiredValue( + @420-426 "u16Bin", + [], + @429-437 NonBase10Int { + string: "101u16", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @441-458 SpaceBefore( - RequiredValue( - @441-447 "u32Bin", - [], - @450-458 NonBase10Int { - string: "101u32", - base: Binary, - is_negative: false, - }, + @441-458 SpaceBefore( + RequiredValue( + @441-447 "u32Bin", + [], + @450-458 NonBase10Int { + string: "101u32", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @462-479 SpaceBefore( - RequiredValue( - @462-468 "u64Bin", - [], - @471-479 NonBase10Int { - string: "101u64", - base: Binary, - is_negative: false, - }, + @462-479 SpaceBefore( + RequiredValue( + @462-468 "u64Bin", + [], + @471-479 NonBase10Int { + string: "101u64", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @483-501 SpaceBefore( - RequiredValue( - @483-490 "u128Bin", - [], - @492-501 NonBase10Int { - string: "101u128", - base: Binary, - is_negative: false, - }, + @483-501 SpaceBefore( + RequiredValue( + @483-490 "u128Bin", + [], + @492-501 NonBase10Int { + string: "101u128", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @505-521 SpaceBefore( - RequiredValue( - @505-510 "i8Bin", - [], - @514-521 NonBase10Int { - string: "101i8", - base: Binary, - is_negative: false, - }, + @505-521 SpaceBefore( + RequiredValue( + @505-510 "i8Bin", + [], + @514-521 NonBase10Int { + string: "101i8", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @525-542 SpaceBefore( - RequiredValue( - @525-531 "i16Bin", - [], - @534-542 NonBase10Int { - string: "101i16", - base: Binary, - is_negative: false, - }, + @525-542 SpaceBefore( + RequiredValue( + @525-531 "i16Bin", + [], + @534-542 NonBase10Int { + string: "101i16", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @546-563 SpaceBefore( - RequiredValue( - @546-552 "i32Bin", - [], - @555-563 NonBase10Int { - string: "101i32", - base: Binary, - is_negative: false, - }, + @546-563 SpaceBefore( + RequiredValue( + @546-552 "i32Bin", + [], + @555-563 NonBase10Int { + string: "101i32", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @567-584 SpaceBefore( - RequiredValue( - @567-573 "i64Bin", - [], - @576-584 NonBase10Int { - string: "101i64", - base: Binary, - is_negative: false, - }, + @567-584 SpaceBefore( + RequiredValue( + @567-573 "i64Bin", + [], + @576-584 NonBase10Int { + string: "101i64", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @588-606 SpaceBefore( - RequiredValue( - @588-595 "i128Bin", - [], - @597-606 NonBase10Int { - string: "101i128", - base: Binary, - is_negative: false, - }, + @588-606 SpaceBefore( + RequiredValue( + @588-595 "i128Bin", + [], + @597-606 NonBase10Int { + string: "101i128", + base: Binary, + is_negative: false, + }, + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - ], - final_comments: [ - Newline, - ], - }, + ], + final_comments: [ + Newline, + ], + }, + ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc index 5be9c64e00..aeeef68b29 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.formatted.roc @@ -6,3 +6,4 @@ import cli.Stdout main = Stdout.line "hello" + diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast index bf6d78c970..acb6368daa 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/old_app_header.full.result-ast @@ -56,7 +56,7 @@ Full { ], space_after: [ Slice(start = 3, length = 3), - Slice(start = 8, length = 0), + Slice(start = 8, length = 2), ], spaces: [ Newline, @@ -67,6 +67,8 @@ Full { Newline, Newline, Newline, + Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast index 963976ac90..dbbe6f46c4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_backpassing.expr.result-ast @@ -1,33 +1,38 @@ SpaceBefore( - Backpassing( - [ - @18-19 Identifier { - ident: "x", - }, - ], - @23-32 ParensAround( - Closure( - [ - @25-26 Identifier { + SpaceAfter( + Backpassing( + [ + @18-19 Identifier { + ident: "x", + }, + ], + @23-32 ParensAround( + Closure( + [ + @25-26 Identifier { + ident: "y", + }, + ], + @30-31 Var { + module_name: "", ident: "y", }, - ], - @30-31 Var { + ), + ), + @34-35 SpaceBefore( + Var { module_name: "", - ident: "y", + ident: "x", }, + [ + Newline, + Newline, + ], ), ), - @34-35 SpaceBefore( - Var { - module_name: "", - ident: "x", - }, - [ - Newline, - Newline, - ], - ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc new file mode 100644 index 0000000000..3403a0c7f4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.formatted.roc @@ -0,0 +1 @@ +"x" \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast index e15ee75ac8..94dee6c867 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_char_string.expr.result-ast @@ -1,5 +1,10 @@ -Str( - PlainLine( - "x", +SpaceAfter( + Str( + PlainLine( + "x", + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast index d5d5b85144..4267eea078 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_def.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @18-21, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @18-19 Identifier { - ident: "x", - }, - @20-21 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @18-21, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @18-19 Identifier { + ident: "x", + }, + @20-21 Num( + "5", + ), ), + ], + }, + @23-25 SpaceBefore( + Num( + "42", ), - ], - }, - @23-25 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc new file mode 100644 index 0000000000..577e02f273 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.formatted.roc @@ -0,0 +1,4 @@ +# leading comment +x = 5 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast index 38c887ba9c..8be31b142b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/one_spaced_def.expr.result-ast @@ -1,40 +1,45 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @18-23, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @18-19 Identifier { - ident: "x", - }, - @22-23 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @18-23, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @18-19 Identifier { + ident: "x", + }, + @22-23 Num( + "5", + ), ), + ], + }, + @25-27 SpaceBefore( + Num( + "42", ), - ], - }, - @25-27 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast index 152d1e596d..759019618b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_destructure_first_item_in_body.expr.result-ast @@ -1,71 +1,76 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-22, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-6 Apply( - @0-6 OpaqueRef( - "@Thunk", - ), - [ - @7-9 Identifier { - ident: "it", - }, - ], - ), - @12-22 Apply( - @12-14 Var { - module_name: "", - ident: "id", - }, - [ - @16-21 ParensAround( - Apply( - @16-18 OpaqueRef( - "@A", - ), - [ - @19-21 Record( - [], - ), - ], - Space, - ), +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-22, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-9 Apply( + @0-6 OpaqueRef( + "@Thunk", ), - ], - Space, - ), - ), - ], - }, - @23-28 SpaceBefore( - Apply( - @23-25 Var { - module_name: "", - ident: "it", - }, - [ - @26-28 Record( - [], + [ + @7-9 Identifier { + ident: "it", + }, + ], + ), + @12-22 Apply( + @12-14 Var { + module_name: "", + ident: "id", + }, + [ + @16-21 ParensAround( + Apply( + @16-18 OpaqueRef( + "@A", + ), + [ + @19-21 Record( + [], + ), + ], + Space, + ), + ), + ], + Space, + ), ), ], - Space, + }, + @23-28 SpaceBefore( + Apply( + @23-25 Var { + module_name: "", + ident: "it", + }, + [ + @26-28 Record( + [], + ), + ], + Space, + ), + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast index 6426fd40e6..c07edf253f 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_has_abilities.expr.result-ast @@ -1,444 +1,243 @@ -Defs( - Defs { - tags: [ - Index(0), - Index(1), - Index(2), - Index(3), - Index(4), - Index(5), - Index(6), - Index(7), - Index(8), - Index(9), - ], - regions: [ - @0-7, - @31-62, - @86-117, - @146-153, - @189-196, - @224-231, - @265-272, - @306-313, - @329-360, - @401-408, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 2), - Slice(start = 2, length = 2), - Slice(start = 4, length = 2), - Slice(start = 6, length = 2), - Slice(start = 8, length = 2), - Slice(start = 10, length = 2), - Slice(start = 12, length = 2), - Slice(start = 14, length = 2), - Slice(start = 16, length = 2), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - Slice(start = 4, length = 0), - Slice(start = 6, length = 0), - Slice(start = 8, length = 0), - Slice(start = 10, length = 0), - Slice(start = 12, length = 0), - Slice(start = 14, length = 0), - Slice(start = 16, length = 0), - Slice(start = 18, length = 0), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [ - Opaque { - header: TypeHeader { - name: @0-1 "A", - vars: [], - }, - typ: @5-7 Apply( - "", - "U8", - [], - ), - derived: Some( - @19-29 Implements( - [ - @20-22 ImplementsAbility { - ability: @20-22 Apply( - "", - "Eq", - [], - ), - impls: None, - }, - @24-28 ImplementsAbility { - ability: @24-28 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @31-32 "A", - vars: [], - }, - typ: @36-62 Where( - @36-37 BoundVariable( - "a", - ), - [ - @44-62 ImplementsClause { - var: @44-45 "a", - abilities: [ - @57-62 Apply( - "", - "Other", - [], - ), - ], - }, - ], - ), - derived: Some( - @74-84 Implements( - [ - @75-77 ImplementsAbility { - ability: @75-77 Apply( - "", - "Eq", - [], - ), - impls: None, - }, - @79-83 ImplementsAbility { - ability: @79-83 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @86-87 "A", - vars: [], - }, - typ: @91-117 Where( - @91-92 BoundVariable( - "a", - ), - [ - @99-117 ImplementsClause { - var: @99-100 "a", - abilities: [ - @112-117 Apply( - "", - "Other", - [], - ), - ], - }, - ], - ), - derived: Some( - @134-144 SpaceBefore( - Implements( - [ - @135-137 ImplementsAbility { - ability: @135-137 Apply( - "", - "Eq", - [], - ), - impls: None, - }, - @139-143 ImplementsAbility { - ability: @139-143 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - ], - ), - [ - Newline, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @146-147 "A", - vars: [], - }, - typ: @151-153 Apply( - "", - "U8", - [], - ), - derived: Some( - @165-187 Implements( - [ - @166-173 ImplementsAbility { - ability: @166-168 Apply( - "", - "Eq", - [], - ), - impls: Some( - @169-173 AbilityImpls( - [ - @170-172 LabelOnly( - @170-172 "eq", - ), - ], - ), - ), - }, - @175-186 ImplementsAbility { - ability: @175-179 Apply( - "", - "Hash", - [], - ), - impls: Some( - @180-186 AbilityImpls( - [ - @181-185 LabelOnly( - @181-185 "hash", - ), - ], - ), - ), - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @189-190 "A", - vars: [], - }, - typ: @194-196 Apply( - "", - "U8", - [], - ), - derived: Some( - @208-222 Implements( - [ - @209-221 ImplementsAbility { - ability: @209-211 Apply( - "", - "Eq", - [], - ), - impls: Some( - @212-221 AbilityImpls( - [ - @213-215 LabelOnly( - @213-215 "eq", - ), - @217-220 LabelOnly( - @217-220 "eq1", - ), - ], - ), - ), - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @224-225 "A", - vars: [], - }, - typ: @229-231 Apply( - "", - "U8", - [], - ), - derived: Some( - @243-263 Implements( - [ - @244-256 ImplementsAbility { - ability: @244-246 Apply( - "", - "Eq", - [], - ), - impls: Some( - @247-256 AbilityImpls( - [ - @248-250 LabelOnly( - @248-250 "eq", - ), - @252-255 LabelOnly( - @252-255 "eq1", - ), - ], - ), - ), - }, - @258-262 ImplementsAbility { - ability: @258-262 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @265-266 "A", - vars: [], - }, - typ: @270-272 Apply( - "", - "U8", - [], - ), - derived: Some( - @284-304 Implements( - [ - @285-289 ImplementsAbility { - ability: @285-289 Apply( - "", - "Hash", - [], - ), - impls: None, - }, - @291-303 ImplementsAbility { - ability: @291-293 Apply( - "", - "Eq", - [], - ), - impls: Some( - @294-303 AbilityImpls( - [ - @295-297 LabelOnly( - @295-297 "eq", - ), - @299-302 LabelOnly( - @299-302 "eq1", - ), - ], - ), - ), - }, - ], - ), - ), - }, - Opaque { - header: TypeHeader { - name: @306-307 "A", - vars: [], - }, - typ: @311-313 Apply( - "", - "U8", - [], - ), - derived: Some( - @325-327 Implements( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + Index(1), + Index(2), + Index(3), + Index(4), + Index(5), + Index(6), + Index(7), + Index(8), + Index(9), + ], + regions: [ + @0-29, + @31-84, + @86-144, + @146-187, + @189-222, + @224-263, + @265-304, + @306-327, + @329-399, + @401-427, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + Slice(start = 4, length = 2), + Slice(start = 6, length = 2), + Slice(start = 8, length = 2), + Slice(start = 10, length = 2), + Slice(start = 12, length = 2), + Slice(start = 14, length = 2), + Slice(start = 16, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + Slice(start = 6, length = 0), + Slice(start = 8, length = 0), + Slice(start = 10, length = 0), + Slice(start = 12, length = 0), + Slice(start = 14, length = 0), + Slice(start = 16, length = 0), + Slice(start = 18, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [ + Opaque { + header: TypeHeader { + name: @0-1 "A", + vars: [], + }, + typ: @5-7 Apply( + "", + "U8", [], ), - ), - }, - Opaque { - header: TypeHeader { - name: @329-330 "A", - vars: [], - }, - typ: @334-360 Where( - @334-335 BoundVariable( - "a", - ), - [ - @342-360 ImplementsClause { - var: @342-343 "a", - abilities: [ - @355-360 Apply( - "", - "Other", - [], - ), - ], - }, - ], - ), - derived: Some( - @377-399 SpaceBefore( - Implements( + derived: Some( + @19-29 Implements( [ - @378-385 ImplementsAbility { - ability: @378-380 Apply( + @20-22 ImplementsAbility { + ability: @20-22 Apply( + "", + "Eq", + [], + ), + impls: None, + }, + @24-28 ImplementsAbility { + ability: @24-28 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @31-32 "A", + vars: [], + }, + typ: @36-62 Where( + @36-37 BoundVariable( + "a", + ), + [ + @44-62 ImplementsClause { + var: @44-45 "a", + abilities: [ + @57-62 Apply( + "", + "Other", + [], + ), + ], + }, + ], + ), + derived: Some( + @74-84 Implements( + [ + @75-77 ImplementsAbility { + ability: @75-77 Apply( + "", + "Eq", + [], + ), + impls: None, + }, + @79-83 ImplementsAbility { + ability: @79-83 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @86-87 "A", + vars: [], + }, + typ: @91-117 Where( + @91-92 BoundVariable( + "a", + ), + [ + @99-117 ImplementsClause { + var: @99-100 "a", + abilities: [ + @112-117 Apply( + "", + "Other", + [], + ), + ], + }, + ], + ), + derived: Some( + @134-144 SpaceBefore( + Implements( + [ + @135-137 ImplementsAbility { + ability: @135-137 Apply( + "", + "Eq", + [], + ), + impls: None, + }, + @139-143 ImplementsAbility { + ability: @139-143 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), + [ + Newline, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @146-147 "A", + vars: [], + }, + typ: @151-153 Apply( + "", + "U8", + [], + ), + derived: Some( + @165-187 Implements( + [ + @166-173 ImplementsAbility { + ability: @166-168 Apply( "", "Eq", [], ), impls: Some( - @381-385 AbilityImpls( + @169-173 AbilityImpls( [ - @382-384 LabelOnly( - @382-384 "eq", + @170-172 LabelOnly( + @170-172 "eq", ), ], ), ), }, - @387-398 ImplementsAbility { - ability: @387-391 Apply( + @175-186 ImplementsAbility { + ability: @175-179 Apply( "", "Hash", [], ), impls: Some( - @392-398 AbilityImpls( + @180-186 AbilityImpls( [ - @393-397 LabelOnly( - @393-397 "hash", + @181-185 LabelOnly( + @181-185 "hash", ), ], ), @@ -446,51 +245,257 @@ Defs( }, ], ), - [ - Newline, - ], ), - ), - }, - Opaque { - header: TypeHeader { - name: @401-402 "A", - vars: [], }, - typ: @406-408 Apply( - "", - "U8", - [], - ), - derived: Some( - @420-427 Implements( - [ - @421-426 ImplementsAbility { - ability: @421-423 Apply( - "", - "Eq", - [], - ), - impls: Some( - @424-426 AbilityImpls( + Opaque { + header: TypeHeader { + name: @189-190 "A", + vars: [], + }, + typ: @194-196 Apply( + "", + "U8", + [], + ), + derived: Some( + @208-222 Implements( + [ + @209-221 ImplementsAbility { + ability: @209-211 Apply( + "", + "Eq", [], ), - ), + impls: Some( + @212-221 AbilityImpls( + [ + @213-215 LabelOnly( + @213-215 "eq", + ), + @217-220 LabelOnly( + @217-220 "eq1", + ), + ], + ), + ), + }, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @224-225 "A", + vars: [], + }, + typ: @229-231 Apply( + "", + "U8", + [], + ), + derived: Some( + @243-263 Implements( + [ + @244-256 ImplementsAbility { + ability: @244-246 Apply( + "", + "Eq", + [], + ), + impls: Some( + @247-256 AbilityImpls( + [ + @248-250 LabelOnly( + @248-250 "eq", + ), + @252-255 LabelOnly( + @252-255 "eq1", + ), + ], + ), + ), + }, + @258-262 ImplementsAbility { + ability: @258-262 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @265-266 "A", + vars: [], + }, + typ: @270-272 Apply( + "", + "U8", + [], + ), + derived: Some( + @284-304 Implements( + [ + @285-289 ImplementsAbility { + ability: @285-289 Apply( + "", + "Hash", + [], + ), + impls: None, + }, + @291-303 ImplementsAbility { + ability: @291-293 Apply( + "", + "Eq", + [], + ), + impls: Some( + @294-303 AbilityImpls( + [ + @295-297 LabelOnly( + @295-297 "eq", + ), + @299-302 LabelOnly( + @299-302 "eq1", + ), + ], + ), + ), + }, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @306-307 "A", + vars: [], + }, + typ: @311-313 Apply( + "", + "U8", + [], + ), + derived: Some( + @325-327 Implements( + [], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @329-330 "A", + vars: [], + }, + typ: @334-360 Where( + @334-335 BoundVariable( + "a", + ), + [ + @342-360 ImplementsClause { + var: @342-343 "a", + abilities: [ + @355-360 Apply( + "", + "Other", + [], + ), + ], }, ], ), - ), - }, - ], - value_defs: [], - }, - @429-430 SpaceBefore( - Num( - "0", + derived: Some( + @377-399 SpaceBefore( + Implements( + [ + @378-385 ImplementsAbility { + ability: @378-380 Apply( + "", + "Eq", + [], + ), + impls: Some( + @381-385 AbilityImpls( + [ + @382-384 LabelOnly( + @382-384 "eq", + ), + ], + ), + ), + }, + @387-398 ImplementsAbility { + ability: @387-391 Apply( + "", + "Hash", + [], + ), + impls: Some( + @392-398 AbilityImpls( + [ + @393-397 LabelOnly( + @393-397 "hash", + ), + ], + ), + ), + }, + ], + ), + [ + Newline, + ], + ), + ), + }, + Opaque { + header: TypeHeader { + name: @401-402 "A", + vars: [], + }, + typ: @406-408 Apply( + "", + "U8", + [], + ), + derived: Some( + @420-427 Implements( + [ + @421-426 ImplementsAbility { + ability: @421-423 Apply( + "", + "Eq", + [], + ), + impls: Some( + @424-426 AbilityImpls( + [], + ), + ), + }, + ], + ), + ), + }, + ], + value_defs: [], + }, + @429-430 SpaceBefore( + Num( + "0", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc new file mode 100644 index 0000000000..320a3f5774 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.formatted.roc @@ -0,0 +1 @@ +@Age \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast index 4dfbe2dc65..620e120d1b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr.expr.result-ast @@ -1,3 +1,8 @@ -OpaqueRef( - "@Age", +SpaceAfter( + OpaqueRef( + "@Age", + ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc new file mode 100644 index 0000000000..0aeddca9ea --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.formatted.roc @@ -0,0 +1 @@ +@Age m n \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast index c42661df16..3ad5fad17d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_expr_with_arguments.expr.result-ast @@ -1,16 +1,21 @@ -Apply( - @0-4 OpaqueRef( - "@Age", +SpaceAfter( + Apply( + @0-4 OpaqueRef( + "@Age", + ), + [ + @5-6 Var { + module_name: "", + ident: "m", + }, + @7-8 Var { + module_name: "", + ident: "n", + }, + ], + Space, ), [ - @5-6 Var { - module_name: "", - ident: "m", - }, - @7-8 Var { - module_name: "", - ident: "n", - }, + Newline, ], - Space, ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast index 0dd1a3462e..146d18a99e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern.expr.result-ast @@ -1,24 +1,29 @@ -When( - @5-6 Var { - module_name: "", - ident: "n", - }, - [ - WhenBranch { - patterns: [ - @12-16 SpaceBefore( - OpaqueRef( - "@Age", - ), - [ - Newline, - ], - ), - ], - value: @20-21 Num( - "1", - ), - guard: None, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "n", }, + [ + WhenBranch { + patterns: [ + @12-16 SpaceBefore( + OpaqueRef( + "@Age", + ), + [ + Newline, + ], + ), + ], + value: @20-21 Num( + "1", + ), + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast index 9694879ba4..eb07397e0c 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_reference_pattern_with_arguments.expr.result-ast @@ -1,46 +1,51 @@ -When( - @5-6 Var { - module_name: "", - ident: "n", - }, - [ - WhenBranch { - patterns: [ - @12-20 SpaceBefore( - Apply( - @12-16 OpaqueRef( - "@Add", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "n", + }, + [ + WhenBranch { + patterns: [ + @12-20 SpaceBefore( + Apply( + @12-16 OpaqueRef( + "@Add", + ), + [ + @17-18 Identifier { + ident: "n", + }, + @19-20 Identifier { + ident: "m", + }, + ], ), [ - @17-18 Identifier { - ident: "n", - }, - @19-20 Identifier { - ident: "m", - }, + Newline, ], ), - [ - Newline, - ], - ), - ], - value: @24-29 BinOps( - [ - ( - @24-25 Var { - module_name: "", - ident: "n", - }, - @26-27 Plus, - ), ], - @28-29 Var { - module_name: "", - ident: "m", - }, - ), - guard: None, - }, + value: @24-29 BinOps( + [ + ( + @24-25 Var { + module_name: "", + ident: "n", + }, + @26-27 Plus, + ), + ], + @28-29 Var { + module_name: "", + ident: "m", + }, + ), + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc deleted file mode 100644 index 151cb0215e..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -Age := U8 diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast index 4184232f3c..d1464898db 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_simple.moduledefs.result-ast @@ -9,9 +9,11 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [ Opaque { header: TypeHeader { diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc deleted file mode 100644 index d8c2642763..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.formatted.roc +++ /dev/null @@ -1,4 +0,0 @@ -a : e -Na := - e -e0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast deleted file mode 100644 index 0ee155bf93..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_type_def_with_newline.expr.result-ast +++ /dev/null @@ -1,54 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(0), - ], - regions: [ - @0-3, - @4-11, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - spaces: [ - Newline, - ], - type_defs: [ - Opaque { - header: TypeHeader { - name: @4-6 "Na", - vars: [], - }, - typ: @10-11 SpaceBefore( - BoundVariable( - "e", - ), - [ - Newline, - ], - ), - derived: None, - }, - ], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "a", - }, - @2-3 BoundVariable( - "e", - ), - ), - ], - }, - @12-14 Var { - module_name: "", - ident: "e0", - }, -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast index f60cc590b6..552e8eb2bd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/opaque_with_type_arguments.moduledefs.result-ast @@ -9,13 +9,15 @@ Defs { Slice(start = 0, length = 0), ], space_after: [ - Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, ], - spaces: [], type_defs: [ Opaque { header: TypeHeader { - name: @0-10 "Bookmark", + name: @0-8 "Bookmark", vars: [ @9-10 Identifier { ident: "a", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast index bad6ae5651..5599969492 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_app_with_record.expr.result-ast @@ -1,76 +1,81 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-29, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @4-29 Apply( - @4-7 Var { - module_name: "", - ident: "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-29, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", }, - [ - @9-28 ParensAround( - Apply( - @9-12 Var { - module_name: "", - ident: "baz", - }, - [ - @13-28 Record( - [ - @17-26 SpaceBefore( - SpaceAfter( - RequiredValue( - @17-20 "bar", - [], - @22-26 Var { - module_name: "", - ident: "blah", - }, + @4-29 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @9-28 ParensAround( + Apply( + @9-12 Var { + module_name: "", + ident: "baz", + }, + [ + @13-28 Record( + [ + @17-26 SpaceBefore( + SpaceAfter( + RequiredValue( + @17-20 "bar", + [], + @22-26 Var { + module_name: "", + ident: "blah", + }, + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], - ), - ], - Space, + ], + ), + ], + Space, + ), ), - ), - ], - Space, + ], + Space, + ), ), - ), - ], - }, - @30-31 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], }, - [ - Newline, - ], + @30-31 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast index c99bc7d791..e3a47bfc4e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_colon_in_record.expr.result-ast @@ -1,72 +1,77 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-22, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @4-22 Apply( - @4-7 Var { - module_name: "", - ident: "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-22, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", }, - [ - @8-22 Record( - [ - @10-20 SpaceBefore( - SpaceAfter( - RequiredValue( - @10-13 "bar", - [ - Newline, - ], - @16-20 SpaceBefore( - Var { - module_name: "", - ident: "blah", - }, + @4-22 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @8-22 Record( + [ + @10-20 SpaceBefore( + SpaceAfter( + RequiredValue( + @10-13 "bar", [ Newline, ], + @16-20 SpaceBefore( + Var { + module_name: "", + ident: "blah", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], - ), - ], - Space, + ], + ), + ], + Space, + ), ), - ), - ], - }, - @23-24 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], }, - [ - Newline, - ], + @23-24 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast index 7796fc43a0..2e05eed017 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_list.expr.result-ast @@ -1,57 +1,62 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-17, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "a", - }, - @4-17 List( - [ - @8-9 SpaceBefore( - Num( - "1", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-17, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "a", + }, + @4-17 List( + [ + @8-9 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], ), - [ - Newline, - ], - ), - @11-12 Num( - "2", - ), - @14-15 SpaceAfter( - Num( - "3", + @11-12 Num( + "2", ), - [ - Newline, - ], - ), - ], + @14-15 SpaceAfter( + Num( + "3", + ), + [ + Newline, + ], + ), + ], + ), ), - ), - ], - }, - @18-19 SpaceBefore( - Var { - module_name: "", - ident: "a", + ], }, - [ - Newline, - ], + @18-19 SpaceBefore( + Var { + module_name: "", + ident: "a", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast index 3b9dba7114..60ae689a94 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/outdented_record.expr.result-ast @@ -1,65 +1,70 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-23, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Body( - @0-1 Identifier { - ident: "x", - }, - @4-23 Apply( - @4-7 Var { - module_name: "", - ident: "foo", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-23, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Body( + @0-1 Identifier { + ident: "x", }, - [ - @8-23 Record( - [ - @12-21 SpaceBefore( - SpaceAfter( - RequiredValue( - @12-15 "bar", - [], - @17-21 Var { - module_name: "", - ident: "blah", - }, + @4-23 Apply( + @4-7 Var { + module_name: "", + ident: "foo", + }, + [ + @8-23 Record( + [ + @12-21 SpaceBefore( + SpaceAfter( + RequiredValue( + @12-15 "bar", + [], + @17-21 Var { + module_name: "", + ident: "blah", + }, + ), + [ + Newline, + ], ), [ Newline, ], ), - [ - Newline, - ], - ), - ], - ), - ], - Space, + ], + ), + ], + Space, + ), ), - ), - ], - }, - @24-25 SpaceBefore( - Var { - module_name: "", - ident: "x", + ], }, - [ - Newline, - ], + @24-25 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc deleted file mode 100644 index 5b6326454b..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.formatted.roc +++ /dev/null @@ -1,3 +0,0 @@ -i # -N : b -a \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast deleted file mode 100644 index 57adf1d09e..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.result-ast +++ /dev/null @@ -1,52 +0,0 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-9, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Apply( - @0-1 Identifier { - ident: "i", - }, - [ - @5-6 SpaceBefore( - Tag( - "N", - ), - [ - Newline, - LineComment( - "", - ), - ], - ), - ], - ), - @8-9 BoundVariable( - "b", - ), - ), - ], - }, - @10-11 SpaceBefore( - Var { - module_name: "", - ident: "a", - }, - [ - Newline, - ], - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc deleted file mode 100644 index 280f36ca11..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parens_in_value_def_annotation.expr.roc +++ /dev/null @@ -1,4 +0,0 @@ -i -(# -N):b -a \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast index fbc4c78d44..0e588aed8e 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def.expr.result-ast @@ -4,7 +4,7 @@ Defs( Index(0), ], regions: [ - @1-5, + @0-5, ], space_before: [ Slice(start = 0, length = 0), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast index 3a8d3030da..0e7991f580 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parenthesized_type_def_space_before.expr.result-ast @@ -4,7 +4,7 @@ Defs( Index(0), ], regions: [ - @2-6, + @0-6, ], space_before: [ Slice(start = 0, length = 0), diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc new file mode 100644 index 0000000000..d5b68ad051 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.formatted.roc @@ -0,0 +1,3 @@ +Blah a b : Foo.Bar.Baz x y + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast index 39188e402b..bddfadf4bb 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_alias.expr.result-ast @@ -1,54 +1,59 @@ -Defs( - Defs { - tags: [ - Index(0), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [ - Alias { - header: TypeHeader { - name: @0-4 "Blah", - vars: [ - @5-6 Identifier { - ident: "a", - }, - @7-8 Identifier { - ident: "b", - }, - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(0), + ], + regions: [ + @0-26, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [ + Alias { + header: TypeHeader { + name: @0-4 "Blah", + vars: [ + @5-6 Identifier { + ident: "a", + }, + @7-8 Identifier { + ident: "b", + }, + ], + }, + ann: @11-26 Apply( + "Foo.Bar", + "Baz", + [ + @23-24 BoundVariable( + "x", + ), + @25-26 BoundVariable( + "y", + ), + ], + ), }, - ann: @11-26 Apply( - "Foo.Bar", - "Baz", - [ - @23-24 BoundVariable( - "x", - ), - @25-26 BoundVariable( - "y", - ), - ], - ), - }, - ], - value_defs: [], - }, - @28-30 SpaceBefore( - Num( - "42", + ], + value_defs: [], + }, + @28-30 SpaceBefore( + Num( + "42", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc new file mode 100644 index 0000000000..c24a0d5326 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.formatted.roc @@ -0,0 +1,3 @@ +foo : Foo.Bar.Baz x y as Blah a b + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast index c8c4f05ac3..b209055966 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/parse_as_ann.expr.result-ast @@ -1,60 +1,65 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-33, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-3 Identifier { - ident: "foo", - }, - @6-33 As( - @6-21 Apply( - "Foo.Bar", - "Baz", - [ - @18-19 BoundVariable( - "x", - ), - @20-21 BoundVariable( - "y", - ), - ], - ), - [], - TypeHeader { - name: @25-29 "Blah", - vars: [ - @30-31 Identifier { - ident: "a", - }, - @32-33 Identifier { - ident: "b", - }, - ], +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-33, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-3 Identifier { + ident: "foo", }, + @6-33 As( + @6-21 Apply( + "Foo.Bar", + "Baz", + [ + @18-19 BoundVariable( + "x", + ), + @20-21 BoundVariable( + "y", + ), + ], + ), + [], + TypeHeader { + name: @25-29 "Blah", + vars: [ + @30-31 Identifier { + ident: "a", + }, + @32-33 Identifier { + ident: "b", + }, + ], + }, + ), ), + ], + }, + @35-37 SpaceBefore( + Num( + "42", ), - ], - }, - @35-37 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc new file mode 100644 index 0000000000..0d1be03bea --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.formatted.roc @@ -0,0 +1,2 @@ +when 0 is + _ as n -> n \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast index 2a3831a554..68f01fd552 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as.expr.result-ast @@ -1,30 +1,35 @@ -When( - @5-6 Num( - "0", +SpaceAfter( + When( + @5-6 Num( + "0", + ), + [ + WhenBranch { + patterns: [ + @14-20 SpaceBefore( + As( + @14-15 Underscore( + "", + ), + PatternAs { + spaces_before: [], + identifier: @19-20 "n", + }, + ), + [ + Newline, + ], + ), + ], + value: @24-25 Var { + module_name: "", + ident: "n", + }, + guard: None, + }, + ], ), [ - WhenBranch { - patterns: [ - @14-20 SpaceBefore( - As( - @14-15 Underscore( - "", - ), - PatternAs { - spaces_before: [], - identifier: @19-20 "n", - }, - ), - [ - Newline, - ], - ), - ], - value: @24-25 Var { - module_name: "", - ident: "n", - }, - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc new file mode 100644 index 0000000000..87d7a34047 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.formatted.roc @@ -0,0 +1,2 @@ +when myList is + [first, .. as rest] -> 0 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast index ff0aec60dc..c068d9ce2b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_list_rest.expr.result-ast @@ -1,39 +1,44 @@ -When( - @5-11 Var { - module_name: "", - ident: "myList", - }, - [ - WhenBranch { - patterns: [ - @19-38 SpaceBefore( - List( - [ - @20-25 Identifier { - ident: "first", - }, - @27-37 ListRest( - Some( - ( - [], - PatternAs { - spaces_before: [], - identifier: @33-37 "rest", - }, +SpaceAfter( + When( + @5-11 Var { + module_name: "", + ident: "myList", + }, + [ + WhenBranch { + patterns: [ + @19-38 SpaceBefore( + List( + [ + @20-25 Identifier { + ident: "first", + }, + @27-37 ListRest( + Some( + ( + [], + PatternAs { + spaces_before: [], + identifier: @33-37 "rest", + }, + ), ), ), - ), + ], + ), + [ + Newline, ], ), - [ - Newline, - ], + ], + value: @42-43 Num( + "0", ), - ], - value: @42-43 Num( - "0", - ), - guard: None, - }, + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc new file mode 100644 index 0000000000..58f487d11e --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.formatted.roc @@ -0,0 +1,4 @@ +when 0 is + 0 # foobar + as # barfoo + n -> {} \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast index 83e37e7658..caf964ee71 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_as_spaces.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Num( - "0", +SpaceAfter( + When( + @5-6 Num( + "0", + ), + [ + WhenBranch { + patterns: [ + @14-54 SpaceBefore( + As( + @14-15 SpaceAfter( + NumLiteral( + "0", + ), + [ + LineComment( + " foobar", + ), + ], + ), + PatternAs { + spaces_before: [ + LineComment( + " barfoo", + ), + ], + identifier: @53-54 "n", + }, + ), + [ + Newline, + ], + ), + ], + value: @58-60 Record( + [], + ), + guard: None, + }, + ], ), [ - WhenBranch { - patterns: [ - @14-54 SpaceBefore( - As( - @14-15 SpaceAfter( - NumLiteral( - "0", - ), - [ - LineComment( - " foobar", - ), - ], - ), - PatternAs { - spaces_before: [ - LineComment( - " barfoo", - ), - ], - identifier: @53-54 "n", - }, - ), - [ - Newline, - ], - ), - ], - value: @58-60 Record( - [], - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast index a6c26c8500..64d1d66e82 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pattern_with_space_in_parens.expr.result-ast @@ -1,81 +1,86 @@ -When( - @5-22 Apply( - @5-11 Tag( - "Delmin", +SpaceAfter( + When( + @5-22 Apply( + @5-11 Tag( + "Delmin", + ), + [ + @13-19 ParensAround( + Apply( + @13-16 Tag( + "Del", + ), + [ + @17-19 Var { + module_name: "", + ident: "rx", + }, + ], + Space, + ), + ), + @21-22 Num( + "0", + ), + ], + Space, ), [ - @13-19 ParensAround( - Apply( - @13-16 Tag( - "Del", + WhenBranch { + patterns: [ + @30-48 SpaceBefore( + Apply( + @30-36 Tag( + "Delmin", + ), + [ + @38-44 Apply( + @38-41 Tag( + "Del", + ), + [ + @42-44 Identifier { + ident: "ry", + }, + ], + ), + @47-48 Underscore( + "", + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @52-78 Apply( + @52-56 Tag( + "Node", ), [ - @17-19 Var { + @57-62 Tag( + "Black", + ), + @63-64 Num( + "0", + ), + @65-75 Var { + module_name: "Bool", + ident: "false", + }, + @76-78 Var { module_name: "", - ident: "rx", + ident: "ry", }, ], Space, ), - ), - @21-22 Num( - "0", - ), + guard: None, + }, ], - Space, ), [ - WhenBranch { - patterns: [ - @30-48 SpaceBefore( - Apply( - @30-36 Tag( - "Delmin", - ), - [ - @38-44 Apply( - @38-41 Tag( - "Del", - ), - [ - @42-44 Identifier { - ident: "ry", - }, - ], - ), - @47-48 Underscore( - "", - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @52-78 Apply( - @52-56 Tag( - "Node", - ), - [ - @57-62 Tag( - "Black", - ), - @63-64 Num( - "0", - ), - @65-75 Var { - module_name: "Bool", - ident: "false", - }, - @76-78 Var { - module_name: "", - ident: "ry", - }, - ], - Space, - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc new file mode 100644 index 0000000000..047cd4eb6a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.formatted.roc @@ -0,0 +1 @@ +1 * if Bool.true then 1 else 1 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast index 3375d05218..9157138188 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/plus_if.expr.result-ast @@ -1,26 +1,31 @@ -BinOps( - [ - ( - @0-1 Num( - "1", - ), - @2-3 Star, - ), - ], - @4-30 If( +SpaceAfter( + BinOps( [ ( - @7-16 Var { - module_name: "Bool", - ident: "true", - }, - @22-23 Num( + @0-1 Num( "1", ), + @2-3 Star, ), ], - @29-30 Num( - "1", + @4-30 If( + [ + ( + @7-16 Var { + module_name: "Bool", + ident: "true", + }, + @22-23 Num( + "1", + ), + ), + ], + @29-30 Num( + "1", + ), ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast index 15d220aa1c..9b1bd33992 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/plus_when.expr.result-ast @@ -1,54 +1,59 @@ -BinOps( - [ - ( - @0-1 Num( - "1", +SpaceAfter( + BinOps( + [ + ( + @0-1 Num( + "1", + ), + @2-3 Plus, ), - @2-3 Plus, - ), - ], - @8-53 SpaceBefore( - When( - @13-16 Tag( - "Foo", + ], + @8-53 SpaceBefore( + When( + @13-16 Tag( + "Foo", + ), + [ + WhenBranch { + patterns: [ + @28-31 SpaceBefore( + Tag( + "Foo", + ), + [ + Newline, + ], + ), + ], + value: @35-36 Num( + "2", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @45-48 SpaceBefore( + Tag( + "Bar", + ), + [ + Newline, + ], + ), + ], + value: @52-53 Num( + "3", + ), + guard: None, + }, + ], ), [ - WhenBranch { - patterns: [ - @28-31 SpaceBefore( - Tag( - "Foo", - ), - [ - Newline, - ], - ), - ], - value: @35-36 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @45-48 SpaceBefore( - Tag( - "Bar", - ), - [ - Newline, - ], - ), - ], - value: @52-53 Num( - "3", - ), - guard: None, - }, + Newline, ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc deleted file mode 100644 index 407c12d334..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/provides_type.header.formatted.roc +++ /dev/null @@ -1 +0,0 @@ -app [quicksort, Flags, Model] { pf: "./platform" } diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc new file mode 100644 index 0000000000..4c8ea1d6f4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.formatted.roc @@ -0,0 +1,5 @@ +# leading comment +{ x, y } = 5 +y = 6 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast index 6f64d4ce7d..961dca80ba 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_destructure_def.expr.result-ast @@ -1,61 +1,66 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @18-30, - @31-36, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @18-26 RecordDestructure( - [ - @20-21 Identifier { - ident: "x", - }, - @23-25 Identifier { - ident: "y", - }, - ], + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @18-30, + @31-36, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @18-26 RecordDestructure( + [ + @20-21 Identifier { + ident: "x", + }, + @23-25 Identifier { + ident: "y", + }, + ], + ), + @29-30 Num( + "5", + ), ), - @29-30 Num( - "5", + Body( + @31-32 Identifier { + ident: "y", + }, + @35-36 Num( + "6", + ), ), + ], + }, + @38-40 SpaceBefore( + Num( + "42", ), - Body( - @31-32 Identifier { - ident: "y", - }, - @35-36 Num( - "6", - ), - ), - ], - }, - @38-40 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast index d2378aaacc..5ecb61a232 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_func_type_decl.expr.result-ast @@ -1,128 +1,133 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-122, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @8-122 SpaceBefore( - Record { - fields: [ - @18-38 SpaceBefore( - RequiredValue( - @18-25 "getLine", - [], - @28-38 Apply( - "", - "Effect", - [ - @35-38 Apply( - "", - "Str", - [], - ), - ], - ), - ), - [ - Newline, - ], - ), - @48-75 SpaceBefore( - RequiredValue( - @48-55 "putLine", - [], - @58-75 Function( - [ - @58-61 Apply( - "", - "Str", - [], - ), - ], - @65-75 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-122, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @8-122 SpaceBefore( + Record { + fields: [ + @18-38 SpaceBefore( + RequiredValue( + @18-25 "getLine", + [], + @28-38 Apply( "", "Effect", [ - @72-75 Apply( + @35-38 Apply( "", - "Int", + "Str", [], ), ], ), ), - ), - [ - Newline, - ], - ), - @85-94 SpaceBefore( - RequiredValue( - @85-89 "text", - [], - @91-94 Apply( - "", - "Str", - [], - ), - ), - [ - Newline, - ], - ), - @104-116 SpaceBefore( - SpaceAfter( - RequiredValue( - @104-109 "value", - [], - @111-116 Apply( - "", - "Int", - [ - @115-116 Wildcard, - ], - ), - ), [ Newline, ], ), - [ - Newline, - ], - ), + @48-75 SpaceBefore( + RequiredValue( + @48-55 "putLine", + [], + @58-75 Function( + [ + @58-61 Apply( + "", + "Str", + [], + ), + ], + @65-75 Apply( + "", + "Effect", + [ + @72-75 Apply( + "", + "Int", + [], + ), + ], + ), + ), + ), + [ + Newline, + ], + ), + @85-94 SpaceBefore( + RequiredValue( + @85-89 "text", + [], + @91-94 Apply( + "", + "Str", + [], + ), + ), + [ + Newline, + ], + ), + @104-116 SpaceBefore( + SpaceAfter( + RequiredValue( + @104-109 "value", + [], + @111-116 Apply( + "", + "Int", + [ + @115-116 Wildcard, + ], + ), + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + ], + ext: None, + }, + [ + Newline, ], - ext: None, - }, - [ - Newline, - ], + ), ), + ], + }, + @124-126 SpaceBefore( + Num( + "42", ), - ], - }, - @124-126 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc new file mode 100644 index 0000000000..0f373a1046 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.formatted.roc @@ -0,0 +1,3 @@ +x : { init : {} -> Model, update : Model, Str -> Model, view : Model -> Str } + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast index 925374f414..075390b3f7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/record_type_with_function.expr.result-ast @@ -1,97 +1,102 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-77, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "x", - }, - @4-77 Record { - fields: [ - @6-24 RequiredValue( - @6-10 "init", - [], - @13-24 Function( - [ - @13-15 Record { - fields: [], - ext: None, - }, - ], - @19-24 Apply( - "", - "Model", - [], - ), - ), - ), - @26-54 RequiredValue( - @26-32 "update", - [], - @35-54 Function( - [ - @35-40 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-77, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "x", + }, + @4-77 Record { + fields: [ + @6-24 RequiredValue( + @6-10 "init", + [], + @13-24 Function( + [ + @13-15 Record { + fields: [], + ext: None, + }, + ], + @19-24 Apply( "", "Model", [], ), - @42-45 Apply( + ), + ), + @26-54 RequiredValue( + @26-32 "update", + [], + @35-54 Function( + [ + @35-40 Apply( + "", + "Model", + [], + ), + @42-45 Apply( + "", + "Str", + [], + ), + ], + @49-54 Apply( + "", + "Model", + [], + ), + ), + ), + @56-75 RequiredValue( + @56-60 "view", + [], + @63-75 Function( + [ + @63-68 Apply( + "", + "Model", + [], + ), + ], + @72-75 Apply( "", "Str", [], ), - ], - @49-54 Apply( - "", - "Model", - [], ), ), - ), - @56-75 RequiredValue( - @56-60 "view", - [], - @63-75 Function( - [ - @63-68 Apply( - "", - "Model", - [], - ), - ], - @72-75 Apply( - "", - "Str", - [], - ), - ), - ), - ], - ext: None, - }, + ], + ext: None, + }, + ), + ], + }, + @79-81 SpaceBefore( + Num( + "42", ), - ], - }, - @79-81 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast new file mode 100644 index 0000000000..61c3ba5ea8 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.result-ast @@ -0,0 +1,137 @@ +Defs { + tags: [ + Index(0), + Index(1), + Index(2), + Index(2147483648), + Index(2147483649), + Index(2147483650), + ], + regions: [ + @0-60, + @61-105, + @106-160, + @162-200, + @201-233, + @234-266, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + Slice(start = 2, length = 2), + Slice(start = 4, length = 1), + Slice(start = 5, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 0), + Slice(start = 5, length = 0), + Slice(start = 6, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [ + Alias { + header: TypeHeader { + name: @0-3 "App", + vars: [ + @4-9 Identifier { + ident: "state", + }, + @10-18 Identifier { + ident: "initData", + }, + ], + }, + ann: @21-60 Apply( + "Html.Internal.Shared", + "App", + [ + @46-51 BoundVariable( + "state", + ), + @52-60 BoundVariable( + "initData", + ), + ], + ), + }, + Alias { + header: TypeHeader { + name: @61-65 "Html", + vars: [ + @66-71 Identifier { + ident: "state", + }, + ], + }, + ann: @74-105 Apply( + "Html.Internal.Shared", + "Html", + [ + @100-105 BoundVariable( + "state", + ), + ], + ), + }, + Alias { + header: TypeHeader { + name: @106-115 "Attribute", + vars: [ + @116-121 Identifier { + ident: "state", + }, + ], + }, + ann: @124-160 Apply( + "Html.Internal.Shared", + "Attribute", + [ + @155-160 BoundVariable( + "state", + ), + ], + ), + }, + ], + value_defs: [ + Body( + @162-169 Identifier { + ident: "element", + }, + @172-200 Var { + module_name: "Html.Internal.Shared", + ident: "element", + }, + ), + Body( + @201-205 Identifier { + ident: "text", + }, + @208-233 Var { + module_name: "Html.Internal.Shared", + ident: "text", + }, + ), + Body( + @234-238 Identifier { + ident: "none", + }, + @241-266 Var { + module_name: "Html.Internal.Shared", + ident: "none", + }, + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc new file mode 100644 index 0000000000..20d2977aac --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/separate_defs.moduledefs.roc @@ -0,0 +1,7 @@ +App state initData : Html.Internal.Shared.App state initData +Html state : Html.Internal.Shared.Html state +Attribute state : Html.Internal.Shared.Attribute state + +element = Html.Internal.Shared.element +text = Html.Internal.Shared.text +none = Html.Internal.Shared.none diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast index 0853b0e4f5..b3777d5493 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/space_before_colon.full.result-ast @@ -38,11 +38,12 @@ Full { Slice(start = 0, length = 2), ], space_after: [ - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc deleted file mode 100644 index 2c6e02bbfd..0000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/standalone_module_defs.moduledefs.formatted.roc +++ /dev/null @@ -1,7 +0,0 @@ -# comment 1 -foo = 1 - -# comment 2 -bar = "hi" -baz = "stuff" -# comment n diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast index 87c2c1ae42..aa86b9b304 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast @@ -64,7 +64,7 @@ Defs { @26-27 Identifier { ident: "x", }, - @26-39 Apply( + @29-39 Apply( @29-32 TaskAwaitBang( Var { module_name: "B", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc index acc4f596f4..831a23396a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc @@ -7,9 +7,11 @@ import cli.Stdout main = # is this a valid statement? "Foo" |> A.x! + # what about this? "Bar" |> B.y! { config: "config" } C.z "Bar" + diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast index 8cf6d157d8..6f8c31abc0 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast @@ -46,17 +46,19 @@ Full { ], space_before: [ Slice(start = 0, length = 2), - Slice(start = 4, length = 0), + Slice(start = 2, length = 2), ], space_after: [ - Slice(start = 2, length = 2), - Slice(start = 4, length = 0), + Slice(start = 2, length = 0), + Slice(start = 4, length = 2), ], spaces: [ Newline, Newline, Newline, Newline, + Newline, + Newline, ], type_defs: [], value_defs: [ @@ -88,22 +90,28 @@ Full { Index(2147483649), ], regions: [ - @120-132, + @120-133, @162-205, ], space_before: [ Slice(start = 0, length = 0), - Slice(start = 0, length = 0), + Slice(start = 0, length = 3), ], space_after: [ Slice(start = 0, length = 0), - Slice(start = 0, length = 0), + Slice(start = 3, length = 0), + ], + spaces: [ + Newline, + Newline, + LineComment( + " what about this?", + ), ], - spaces: [], type_defs: [], value_defs: [ Stmt( - @120-132 BinOps( + @120-133 BinOps( [ ( @120-125 Str( @@ -123,55 +131,46 @@ Full { ), ), Stmt( - @162-205 SpaceBefore( - BinOps( - [ - ( - @162-167 Str( - PlainLine( - "Bar", - ), - ), - @168-170 Pizza, - ), - ], - @171-205 Apply( - @171-174 TaskAwaitBang( - Var { - module_name: "B", - ident: "y", - }, - ), - [ - @185-205 SpaceBefore( - Record( - [ - @187-203 RequiredValue( - @187-193 "config", - [], - @195-203 Str( - PlainLine( - "config", - ), - ), - ), - ], - ), - [ - Newline, - ], - ), - ], - Space, - ), - ), + @162-205 BinOps( [ - Newline, - Newline, - LineComment( - " what about this?", + ( + @162-167 Str( + PlainLine( + "Bar", + ), + ), + @168-170 Pizza, ), ], + @171-205 Apply( + @171-174 TaskAwaitBang( + Var { + module_name: "B", + ident: "y", + }, + ), + [ + @185-205 SpaceBefore( + Record( + [ + @187-203 RequiredValue( + @187-193 "config", + [], + @195-203 Str( + PlainLine( + "config", + ), + ), + ), + ], + ), + [ + Newline, + ], + ), + ], + Space, + ), ), ), ], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast index 21d5d8e515..c4e24e09b6 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast @@ -52,11 +52,12 @@ Full { Slice(start = 0, length = 2), ], space_after: [ - Slice(start = 2, length = 0), + Slice(start = 2, length = 1), ], spaces: [ Newline, Newline, + Newline, ], type_defs: [], value_defs: [ diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast index 7490de4952..d808186997 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_backpassing.expr.result-ast @@ -1,48 +1,53 @@ SpaceBefore( - Backpassing( - [ - @18-19 Identifier { - ident: "x", - }, - ], - @23-32 ParensAround( - Closure( - [ - @25-26 Identifier { + SpaceAfter( + Backpassing( + [ + @18-19 Identifier { + ident: "x", + }, + ], + @23-32 ParensAround( + Closure( + [ + @25-26 Identifier { + ident: "y", + }, + ], + @30-31 Var { + module_name: "", ident: "y", }, - ], - @30-31 Var { - module_name: "", - ident: "y", - }, - ), - ), - @33-43 SpaceBefore( - Backpassing( - [ - @33-34 Identifier { - ident: "z", - }, - ], - @38-40 Record( - [], ), - @42-43 SpaceBefore( - Var { - module_name: "", - ident: "x", - }, + ), + @33-43 SpaceBefore( + Backpassing( [ - Newline, - Newline, + @33-34 Identifier { + ident: "z", + }, ], + @38-40 Record( + [], + ), + @42-43 SpaceBefore( + Var { + module_name: "", + ident: "x", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ), - [ - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast index 7cab905c51..f2661ac204 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_branch_when.expr.result-ast @@ -1,44 +1,49 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-13 SpaceBefore( + StrLiteral( + PlainLine( + "", + ), + ), + [ + Newline, + ], + ), + ], + value: @17-18 Num( + "1", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @20-26 SpaceBefore( + StrLiteral( + PlainLine( + "mise", + ), + ), + [ + Newline, + ], + ), + ], + value: @30-31 Num( + "2", + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @11-13 SpaceBefore( - StrLiteral( - PlainLine( - "", - ), - ), - [ - Newline, - ], - ), - ], - value: @17-18 Num( - "1", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @20-26 SpaceBefore( - StrLiteral( - PlainLine( - "mise", - ), - ), - [ - Newline, - ], - ), - ], - value: @30-31 Num( - "2", - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc new file mode 100644 index 0000000000..9d573d1810 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.formatted.roc @@ -0,0 +1,5 @@ +# leading comment +x = 5 +y = 6 + +42 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast index e306d5d495..1980a11e66 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/two_spaced_def.expr.result-ast @@ -1,54 +1,59 @@ SpaceBefore( - Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @18-23, - @24-29, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - ], - spaces: [ - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @18-19 Identifier { - ident: "x", - }, - @22-23 Num( - "5", + SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @18-23, + @24-29, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @18-19 Identifier { + ident: "x", + }, + @22-23 Num( + "5", + ), ), - ), - Body( - @24-25 Identifier { - ident: "y", - }, - @28-29 Num( - "6", + Body( + @24-25 Identifier { + ident: "y", + }, + @28-29 Num( + "6", + ), ), + ], + }, + @31-33 SpaceBefore( + Num( + "42", ), - ], - }, - @31-33 SpaceBefore( - Num( - "42", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast index 9c957001dc..abdddd4526 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_backpassing.expr.result-ast @@ -1,32 +1,37 @@ SpaceBefore( - Backpassing( - [ - @18-19 Underscore( - "", - ), - ], - @23-32 ParensAround( - Closure( - [ - @25-26 Identifier { + SpaceAfter( + Backpassing( + [ + @18-19 Underscore( + "", + ), + ], + @23-32 ParensAround( + Closure( + [ + @25-26 Identifier { + ident: "y", + }, + ], + @30-31 Var { + module_name: "", ident: "y", }, + ), + ), + @34-35 SpaceBefore( + Num( + "4", + ), + [ + Newline, + Newline, ], - @30-31 Var { - module_name: "", - ident: "y", - }, ), ), - @34-35 SpaceBefore( - Num( - "4", - ), - [ - Newline, - Newline, - ], - ), + [ + Newline, + ], ), [ LineComment( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast index 247fe016c7..2194583230 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/underscore_in_assignment_pattern.expr.result-ast @@ -1,231 +1,236 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - Index(2147483650), - Index(2147483651), - Index(2147483652), - ], - regions: [ - @0-19, - @20-39, - @40-59, - @60-72, - @73-128, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 1), - Slice(start = 1, length = 1), - Slice(start = 2, length = 1), - Slice(start = 3, length = 1), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 1, length = 0), - Slice(start = 2, length = 0), - Slice(start = 3, length = 0), - Slice(start = 4, length = 0), - ], - spaces: [ - Newline, - Newline, - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Body( - @0-4 Apply( - @0-4 Tag( - "Pair", - ), - [ - @5-6 Identifier { - ident: "x", - }, - @7-8 Underscore( - "", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + Index(2147483650), + Index(2147483651), + Index(2147483652), + ], + regions: [ + @0-19, + @20-39, + @40-59, + @60-72, + @73-128, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 1), + Slice(start = 1, length = 1), + Slice(start = 2, length = 1), + Slice(start = 3, length = 1), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 1, length = 0), + Slice(start = 2, length = 0), + Slice(start = 3, length = 0), + Slice(start = 4, length = 0), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-8 Apply( + @0-4 Tag( + "Pair", ), - ], - ), - @11-19 Apply( - @11-15 Tag( - "Pair", - ), - [ - @16-17 Num( - "0", - ), - @18-19 Num( - "1", - ), - ], - Space, - ), - ), - Body( - @20-28 Apply( - @20-24 Tag( - "Pair", - ), - [ - @25-26 Underscore( - "", - ), - @27-28 Identifier { - ident: "y", - }, - ], - ), - @31-39 Apply( - @31-35 Tag( - "Pair", - ), - [ - @36-37 Num( - "0", - ), - @38-39 Num( - "1", - ), - ], - Space, - ), - ), - Body( - @40-48 Apply( - @40-44 Tag( - "Pair", - ), - [ - @45-46 Underscore( - "", - ), - @47-48 Underscore( - "", - ), - ], - ), - @51-59 Apply( - @51-55 Tag( - "Pair", - ), - [ - @56-57 Num( - "0", - ), - @58-59 Num( - "1", - ), - ], - Space, - ), - ), - Body( - @60-61 Underscore( - "", - ), - @64-72 Apply( - @64-68 Tag( - "Pair", - ), - [ - @69-70 Num( - "0", - ), - @71-72 Num( - "1", - ), - ], - Space, - ), - ), - Body( - @73-98 Apply( - @73-77 Tag( - "Pair", - ), - [ - @79-87 Apply( - @79-83 Tag( - "Pair", + [ + @5-6 Identifier { + ident: "x", + }, + @7-8 Underscore( + "", ), - [ - @84-85 Identifier { - ident: "x", - }, - @86-87 Underscore( - "", - ), - ], - ), - @90-98 Apply( - @90-94 Tag( - "Pair", - ), - [ - @95-96 Underscore( - "", - ), - @97-98 Identifier { - ident: "y", - }, - ], - ), - ], - ), - @102-128 Apply( - @102-106 Tag( - "Pair", + ], ), - [ - @108-116 ParensAround( - Apply( - @108-112 Tag( + @11-19 Apply( + @11-15 Tag( + "Pair", + ), + [ + @16-17 Num( + "0", + ), + @18-19 Num( + "1", + ), + ], + Space, + ), + ), + Body( + @20-28 Apply( + @20-24 Tag( + "Pair", + ), + [ + @25-26 Underscore( + "", + ), + @27-28 Identifier { + ident: "y", + }, + ], + ), + @31-39 Apply( + @31-35 Tag( + "Pair", + ), + [ + @36-37 Num( + "0", + ), + @38-39 Num( + "1", + ), + ], + Space, + ), + ), + Body( + @40-48 Apply( + @40-44 Tag( + "Pair", + ), + [ + @45-46 Underscore( + "", + ), + @47-48 Underscore( + "", + ), + ], + ), + @51-59 Apply( + @51-55 Tag( + "Pair", + ), + [ + @56-57 Num( + "0", + ), + @58-59 Num( + "1", + ), + ], + Space, + ), + ), + Body( + @60-61 Underscore( + "", + ), + @64-72 Apply( + @64-68 Tag( + "Pair", + ), + [ + @69-70 Num( + "0", + ), + @71-72 Num( + "1", + ), + ], + Space, + ), + ), + Body( + @73-98 Apply( + @73-77 Tag( + "Pair", + ), + [ + @79-87 Apply( + @79-83 Tag( "Pair", ), [ - @113-114 Num( - "0", - ), - @115-116 Num( - "1", + @84-85 Identifier { + ident: "x", + }, + @86-87 Underscore( + "", ), ], - Space, ), - ), - @119-127 ParensAround( - Apply( - @119-123 Tag( + @90-98 Apply( + @90-94 Tag( "Pair", ), [ - @124-125 Num( - "2", - ), - @126-127 Num( - "3", + @95-96 Underscore( + "", ), + @97-98 Identifier { + ident: "y", + }, ], - Space, ), + ], + ), + @102-128 Apply( + @102-106 Tag( + "Pair", ), - ], - Space, + [ + @108-116 ParensAround( + Apply( + @108-112 Tag( + "Pair", + ), + [ + @113-114 Num( + "0", + ), + @115-116 Num( + "1", + ), + ], + Space, + ), + ), + @119-127 ParensAround( + Apply( + @119-123 Tag( + "Pair", + ), + [ + @124-125 Num( + "2", + ), + @126-127 Num( + "3", + ), + ], + Space, + ), + ), + ], + Space, + ), ), + ], + }, + @130-131 SpaceBefore( + Num( + "0", ), - ], - }, - @130-131 SpaceBefore( - Num( - "0", + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc new file mode 100644 index 0000000000..a58ff1f7d1 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.formatted.roc @@ -0,0 +1,9 @@ +when x is + _ -> + 1 + + _ -> + 2 + + Ok -> + 3 \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast index bdd18adf88..4d479df936 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_if_guard.expr.result-ast @@ -1,73 +1,78 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-15 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], + ), + ], + value: @27-28 SpaceBefore( + Num( + "1", + ), + [ + Newline, + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @34-35 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + Newline, + ], + ), + ], + value: @47-48 SpaceBefore( + Num( + "2", + ), + [ + Newline, + ], + ), + guard: None, + }, + WhenBranch { + patterns: [ + @54-56 SpaceBefore( + Tag( + "Ok", + ), + [ + Newline, + Newline, + ], + ), + ], + value: @68-69 SpaceBefore( + Num( + "3", + ), + [ + Newline, + ], + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @14-15 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - ], - ), - ], - value: @27-28 SpaceBefore( - Num( - "1", - ), - [ - Newline, - ], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @34-35 SpaceBefore( - Underscore( - "", - ), - [ - Newline, - Newline, - ], - ), - ], - value: @47-48 SpaceBefore( - Num( - "2", - ), - [ - Newline, - ], - ), - guard: None, - }, - WhenBranch { - patterns: [ - @54-56 SpaceBefore( - Tag( - "Ok", - ), - [ - Newline, - Newline, - ], - ), - ], - value: @68-69 SpaceBefore( - Num( - "3", - ), - [ - Newline, - ], - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast index 2844887e56..4f6485e83a 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens.expr.result-ast @@ -1,31 +1,36 @@ -ParensAround( - When( - @6-7 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @15-17 SpaceBefore( - Tag( - "Ok", +SpaceAfter( + ParensAround( + When( + @6-7 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @15-17 SpaceBefore( + Tag( + "Ok", + ), + [ + Newline, + ], + ), + ], + value: @29-30 SpaceBefore( + Num( + "3", ), [ Newline, ], ), - ], - value: @29-30 SpaceBefore( - Num( - "3", - ), - [ - Newline, - ], - ), - guard: None, - }, - ], + guard: None, + }, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast index 419e8c07ad..f9ce146b75 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_in_parens_indented.expr.result-ast @@ -1,31 +1,36 @@ -ParensAround( - SpaceAfter( - When( - @6-7 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @15-17 SpaceBefore( - Tag( - "Ok", - ), - [ - Newline, - ], - ), - ], - value: @21-22 Num( - "3", - ), - guard: None, +SpaceAfter( + ParensAround( + SpaceAfter( + When( + @6-7 Var { + module_name: "", + ident: "x", }, + [ + WhenBranch { + patterns: [ + @15-17 SpaceBefore( + Tag( + "Ok", + ), + [ + Newline, + ], + ), + ], + value: @21-22 Num( + "3", + ), + guard: None, + }, + ], + ), + [ + Newline, ], ), - [ - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast index 2b573943f6..99e2008218 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_alternative_patterns.expr.result-ast @@ -1,87 +1,92 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @11-17 SpaceBefore( - StrLiteral( - PlainLine( - "blah", - ), - ), - [ - Newline, - ], - ), - @20-26 StrLiteral( - PlainLine( - "blop", - ), - ), - ], - value: @30-31 Num( - "1", - ), - guard: None, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", }, - WhenBranch { - patterns: [ - @33-38 SpaceBefore( - StrLiteral( - PlainLine( - "foo", - ), - ), - [ - Newline, - ], - ), - @43-48 SpaceBefore( - SpaceAfter( + [ + WhenBranch { + patterns: [ + @11-17 SpaceBefore( StrLiteral( PlainLine( - "bar", + "blah", ), ), [ Newline, ], ), - [ - Newline, - ], - ), - @51-56 StrLiteral( - PlainLine( - "baz", - ), - ), - ], - value: @60-61 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @63-70 SpaceBefore( - StrLiteral( + @20-26 StrLiteral( PlainLine( - "stuff", + "blop", ), ), - [ - Newline, - ], + ], + value: @30-31 Num( + "1", ), - ], - value: @74-75 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + WhenBranch { + patterns: [ + @33-38 SpaceBefore( + StrLiteral( + PlainLine( + "foo", + ), + ), + [ + Newline, + ], + ), + @43-48 SpaceBefore( + SpaceAfter( + StrLiteral( + PlainLine( + "bar", + ), + ), + [ + Newline, + ], + ), + [ + Newline, + ], + ), + @51-56 StrLiteral( + PlainLine( + "baz", + ), + ), + ], + value: @60-61 Num( + "2", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @63-70 SpaceBefore( + StrLiteral( + PlainLine( + "stuff", + ), + ), + [ + Newline, + ], + ), + ], + value: @74-75 Num( + "4", + ), + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast index 1822dfa962..9944ab2eff 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_function_application.expr.result-ast @@ -1,54 +1,59 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, - [ - WhenBranch { - patterns: [ - @14-15 SpaceBefore( - NumLiteral( - "1", - ), - [ - Newline, - ], - ), - ], - value: @19-33 Apply( - @19-26 Var { - module_name: "Num", - ident: "neg", - }, - [ - @32-33 SpaceBefore( - Num( - "2", +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @14-15 SpaceBefore( + NumLiteral( + "1", ), [ Newline, ], ), ], - Space, - ), - guard: None, - }, - WhenBranch { - patterns: [ - @38-39 SpaceBefore( - Underscore( - "", - ), + value: @19-33 Apply( + @19-26 Var { + module_name: "Num", + ident: "neg", + }, [ - Newline, + @32-33 SpaceBefore( + Num( + "2", + ), + [ + Newline, + ], + ), ], + Space, ), - ], - value: @43-44 Num( - "4", - ), - guard: None, - }, + guard: None, + }, + WhenBranch { + patterns: [ + @38-39 SpaceBefore( + Underscore( + "", + ), + [ + Newline, + ], + ), + ], + value: @43-44 Num( + "4", + ), + guard: None, + }, + ], + ), + [ + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast index 8c6a5e4e61..30c773b5fc 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_negative_numbers.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-12 SpaceBefore( + NumLiteral( + "1", + ), + [ + Newline, + ], + ), + ], + value: @16-17 Num( + "2", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @19-21 SpaceBefore( + NumLiteral( + "-3", + ), + [ + Newline, + ], + ), + ], + value: @25-26 Num( + "4", + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @11-12 SpaceBefore( - NumLiteral( - "1", - ), - [ - Newline, - ], - ), - ], - value: @16-17 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @19-21 SpaceBefore( - NumLiteral( - "-3", - ), - [ - Newline, - ], - ), - ], - value: @25-26 Num( - "4", - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast index 8866aef903..30f11b3781 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_numbers.expr.result-ast @@ -1,40 +1,45 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-12 SpaceBefore( + NumLiteral( + "1", + ), + [ + Newline, + ], + ), + ], + value: @16-17 Num( + "2", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @19-20 SpaceBefore( + NumLiteral( + "3", + ), + [ + Newline, + ], + ), + ], + value: @24-25 Num( + "4", + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @11-12 SpaceBefore( - NumLiteral( - "1", - ), - [ - Newline, - ], - ), - ], - value: @16-17 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @19-20 SpaceBefore( - NumLiteral( - "3", - ), - [ - Newline, - ], - ), - ], - value: @24-25 Num( - "4", - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast index 33e249c2a2..deb3e72ddd 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_records.expr.result-ast @@ -1,51 +1,56 @@ -When( - @5-6 Var { - module_name: "", - ident: "x", - }, +SpaceAfter( + When( + @5-6 Var { + module_name: "", + ident: "x", + }, + [ + WhenBranch { + patterns: [ + @11-16 SpaceBefore( + RecordDestructure( + [ + @13-14 Identifier { + ident: "y", + }, + ], + ), + [ + Newline, + ], + ), + ], + value: @20-21 Num( + "2", + ), + guard: None, + }, + WhenBranch { + patterns: [ + @23-31 SpaceBefore( + RecordDestructure( + [ + @25-26 Identifier { + ident: "z", + }, + @28-29 Identifier { + ident: "w", + }, + ], + ), + [ + Newline, + ], + ), + ], + value: @35-36 Num( + "4", + ), + guard: None, + }, + ], + ), [ - WhenBranch { - patterns: [ - @11-16 SpaceBefore( - RecordDestructure( - [ - @13-14 Identifier { - ident: "y", - }, - ], - ), - [ - Newline, - ], - ), - ], - value: @20-21 Num( - "2", - ), - guard: None, - }, - WhenBranch { - patterns: [ - @23-31 SpaceBefore( - RecordDestructure( - [ - @25-26 Identifier { - ident: "z", - }, - @28-29 Identifier { - ident: "w", - }, - ], - ), - [ - Newline, - ], - ), - ], - value: @35-36 Num( - "4", - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast index b36a554854..e7092abefe 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuple_in_record.expr.result-ast @@ -1,94 +1,99 @@ -When( - @5-18 Record( +SpaceAfter( + When( + @5-18 Record( + [ + @6-17 RequiredValue( + @6-9 "foo", + [], + @11-17 Tuple( + [ + @12-13 Num( + "1", + ), + @15-16 Num( + "2", + ), + ], + ), + ), + ], + ), [ - @6-17 RequiredValue( - @6-9 "foo", - [], - @11-17 Tuple( - [ - @12-13 Num( - "1", + WhenBranch { + patterns: [ + @23-36 SpaceBefore( + RecordDestructure( + [ + @24-35 RequiredField( + "foo", + @29-35 Tuple( + [ + @30-31 NumLiteral( + "1", + ), + @33-34 Identifier { + ident: "x", + }, + ], + ), + ), + ], ), - @15-16 Num( - "2", + [ + Newline, + ], + ), + ], + value: @40-41 Var { + module_name: "", + ident: "x", + }, + guard: None, + }, + WhenBranch { + patterns: [ + @43-56 SpaceBefore( + RecordDestructure( + [ + @44-55 RequiredField( + "foo", + @49-55 Tuple( + [ + @50-51 Underscore( + "", + ), + @53-54 Identifier { + ident: "b", + }, + ], + ), + ), + ], + ), + [ + Newline, + ], + ), + ], + value: @60-65 BinOps( + [ + ( + @60-61 Num( + "3", + ), + @62-63 Plus, ), ], + @64-65 Var { + module_name: "", + ident: "b", + }, ), - ), + guard: None, + }, ], ), [ - WhenBranch { - patterns: [ - @23-36 SpaceBefore( - RecordDestructure( - [ - @24-35 RequiredField( - "foo", - @29-35 Tuple( - [ - @30-31 NumLiteral( - "1", - ), - @33-34 Identifier { - ident: "x", - }, - ], - ), - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @40-41 Var { - module_name: "", - ident: "x", - }, - guard: None, - }, - WhenBranch { - patterns: [ - @43-56 SpaceBefore( - RecordDestructure( - [ - @44-55 RequiredField( - "foo", - @49-55 Tuple( - [ - @50-51 Underscore( - "", - ), - @53-54 Identifier { - ident: "b", - }, - ], - ), - ), - ], - ), - [ - Newline, - ], - ), - ], - value: @60-65 BinOps( - [ - ( - @60-61 Num( - "3", - ), - @62-63 Plus, - ), - ], - @64-65 Var { - module_name: "", - ident: "b", - }, - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast index 687ef1ac7c..3c1698072b 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/when_with_tuples.expr.result-ast @@ -1,72 +1,77 @@ -When( - @5-11 Tuple( +SpaceAfter( + When( + @5-11 Tuple( + [ + @6-7 Num( + "1", + ), + @9-10 Num( + "2", + ), + ], + ), [ - @6-7 Num( - "1", - ), - @9-10 Num( - "2", - ), + WhenBranch { + patterns: [ + @16-22 SpaceBefore( + Tuple( + [ + @17-18 NumLiteral( + "1", + ), + @20-21 Identifier { + ident: "x", + }, + ], + ), + [ + Newline, + ], + ), + ], + value: @26-27 Var { + module_name: "", + ident: "x", + }, + guard: None, + }, + WhenBranch { + patterns: [ + @29-35 SpaceBefore( + Tuple( + [ + @30-31 Underscore( + "", + ), + @33-34 Identifier { + ident: "b", + }, + ], + ), + [ + Newline, + ], + ), + ], + value: @39-44 BinOps( + [ + ( + @39-40 Num( + "3", + ), + @41-42 Plus, + ), + ], + @43-44 Var { + module_name: "", + ident: "b", + }, + ), + guard: None, + }, ], ), [ - WhenBranch { - patterns: [ - @16-22 SpaceBefore( - Tuple( - [ - @17-18 NumLiteral( - "1", - ), - @20-21 Identifier { - ident: "x", - }, - ], - ), - [ - Newline, - ], - ), - ], - value: @26-27 Var { - module_name: "", - ident: "x", - }, - guard: None, - }, - WhenBranch { - patterns: [ - @29-35 SpaceBefore( - Tuple( - [ - @30-31 Underscore( - "", - ), - @33-34 Identifier { - ident: "b", - }, - ], - ), - [ - Newline, - ], - ), - ], - value: @39-44 BinOps( - [ - ( - @39-40 Num( - "3", - ), - @41-42 Plus, - ), - ], - @43-44 Var { - module_name: "", - ident: "b", - }, - ), - guard: None, - }, + Newline, ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc new file mode 100644 index 0000000000..8aecb8257f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.formatted.roc @@ -0,0 +1,3 @@ +f : a -> (b -> c) where a implements A + +f \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast index d2ddc4d1bd..dcdf36e269 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_function.expr.result-ast @@ -1,66 +1,71 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-38, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-38 Where( - @4-16 Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @10-16 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-38, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-38 Where( + @4-16 Function( [ - @10-11 BoundVariable( - "b", + @4-5 BoundVariable( + "a", ), ], - @15-16 BoundVariable( - "c", + @10-16 Function( + [ + @10-11 BoundVariable( + "b", + ), + ], + @15-16 BoundVariable( + "c", + ), ), ), + [ + @24-38 ImplementsClause { + var: @24-25 "a", + abilities: [ + @37-38 Apply( + "", + "A", + [], + ), + ], + }, + ], ), - [ - @24-38 ImplementsClause { - var: @24-25 "a", - abilities: [ - @37-38 Apply( - "", - "A", - [], - ), - ], - }, - ], ), - ), - ], - }, - @40-41 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @40-41 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast index a0e3798f03..8df0cb9b00 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_bound_abilities.expr.result-ast @@ -1,155 +1,160 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - Index(2147483649), - ], - regions: [ - @0-73, - @75-154, - ], - space_before: [ - Slice(start = 0, length = 0), - Slice(start = 0, length = 2), - ], - space_after: [ - Slice(start = 0, length = 0), - Slice(start = 2, length = 0), - ], - spaces: [ - Newline, - Newline, - ], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-73 Where( - @4-10 Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @9-10 BoundVariable( - "b", - ), - ), - [ - @17-39 ImplementsClause { - var: @17-18 "a", - abilities: [ - @30-34 Apply( - "", - "Hash", - [], - ), - @37-39 Apply( - "", - "Eq", - [], - ), - ], - }, - @41-73 ImplementsClause { - var: @41-42 "b", - abilities: [ - @54-56 Apply( - "", - "Eq", - [], - ), - @59-63 Apply( - "", - "Hash", - [], - ), - @66-73 Apply( - "", - "Display", - [], - ), - ], - }, - ], - ), - ), - Annotation( - @75-76 Identifier { - ident: "f", - }, - @79-154 Where( - @79-85 SpaceAfter( - Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @0-73, + @75-154, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 2, length = 0), + ], + spaces: [ + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-73 Where( + @4-10 Function( [ - @79-80 BoundVariable( + @4-5 BoundVariable( "a", ), ], - @84-85 BoundVariable( + @9-10 BoundVariable( "b", ), ), [ - Newline, + @17-39 ImplementsClause { + var: @17-18 "a", + abilities: [ + @30-34 Apply( + "", + "Hash", + [], + ), + @37-39 Apply( + "", + "Eq", + [], + ), + ], + }, + @41-73 ImplementsClause { + var: @41-42 "b", + abilities: [ + @54-56 Apply( + "", + "Eq", + [], + ), + @59-63 Apply( + "", + "Hash", + [], + ), + @66-73 Apply( + "", + "Display", + [], + ), + ], + }, ], ), - [ - @94-116 ImplementsClause { - var: @94-95 "a", - abilities: [ - @107-111 Apply( - "", - "Hash", - [], - ), - @114-116 Apply( - "", - "Eq", - [], - ), - ], - }, - @122-154 ImplementsClause { - var: @122-123 SpaceBefore( - "b", - [ - Newline, - ], - ), - abilities: [ - @135-139 Apply( - "", - "Hash", - [], - ), - @142-149 Apply( - "", - "Display", - [], - ), - @152-154 Apply( - "", - "Eq", - [], - ), - ], - }, - ], ), - ), - ], - }, - @156-157 SpaceBefore( - Var { - module_name: "", - ident: "f", + Annotation( + @75-76 Identifier { + ident: "f", + }, + @79-154 Where( + @79-85 SpaceAfter( + Function( + [ + @79-80 BoundVariable( + "a", + ), + ], + @84-85 BoundVariable( + "b", + ), + ), + [ + Newline, + ], + ), + [ + @94-116 ImplementsClause { + var: @94-95 "a", + abilities: [ + @107-111 Apply( + "", + "Hash", + [], + ), + @114-116 Apply( + "", + "Eq", + [], + ), + ], + }, + @122-154 ImplementsClause { + var: @122-123 SpaceBefore( + "b", + [ + Newline, + ], + ), + abilities: [ + @135-139 Apply( + "", + "Hash", + [], + ), + @142-149 Apply( + "", + "Display", + [], + ), + @152-154 Apply( + "", + "Eq", + [], + ), + ], + }, + ], + ), + ), + ], }, - [ - Newline, - Newline, - ], + @156-157 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc new file mode 100644 index 0000000000..e8f053233a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.formatted.roc @@ -0,0 +1,3 @@ +f : a -> (b -> c) where a implements A, b implements Eq, c implements Ord + +f \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast index a900672d36..7a57a5fe01 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has.expr.result-ast @@ -1,86 +1,91 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-73, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-73 Where( - @4-16 Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @10-16 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-73, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-73 Where( + @4-16 Function( [ - @10-11 BoundVariable( - "b", + @4-5 BoundVariable( + "a", ), ], - @15-16 BoundVariable( - "c", + @10-16 Function( + [ + @10-11 BoundVariable( + "b", + ), + ], + @15-16 BoundVariable( + "c", + ), ), ), + [ + @24-38 ImplementsClause { + var: @24-25 "a", + abilities: [ + @37-38 Apply( + "", + "A", + [], + ), + ], + }, + @40-55 ImplementsClause { + var: @40-41 "b", + abilities: [ + @53-55 Apply( + "", + "Eq", + [], + ), + ], + }, + @57-73 ImplementsClause { + var: @57-58 "c", + abilities: [ + @70-73 Apply( + "", + "Ord", + [], + ), + ], + }, + ], ), - [ - @24-38 ImplementsClause { - var: @24-25 "a", - abilities: [ - @37-38 Apply( - "", - "A", - [], - ), - ], - }, - @40-55 ImplementsClause { - var: @40-41 "b", - abilities: [ - @53-55 Apply( - "", - "Eq", - [], - ), - ], - }, - @57-73 ImplementsClause { - var: @57-58 "c", - abilities: [ - @70-73 Apply( - "", - "Ord", - [], - ), - ], - }, - ], ), - ), - ], - }, - @75-76 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @75-76 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast index 69fa499336..86b323a124 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_multiple_has_across_newlines.expr.result-ast @@ -1,101 +1,106 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-92, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-92 Where( - @4-16 SpaceAfter( - Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @10-16 Function( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-92, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-92 Where( + @4-16 SpaceAfter( + Function( [ - @10-11 BoundVariable( - "b", + @4-5 BoundVariable( + "a", ), ], - @15-16 BoundVariable( - "c", + @10-16 Function( + [ + @10-11 BoundVariable( + "b", + ), + ], + @15-16 BoundVariable( + "c", + ), ), ), + [ + Newline, + ], ), [ - Newline, + @28-45 ImplementsClause { + var: @28-29 "a", + abilities: [ + @41-45 Apply( + "", + "Hash", + [], + ), + ], + }, + @53-68 ImplementsClause { + var: @53-54 SpaceBefore( + "b", + [ + Newline, + ], + ), + abilities: [ + @66-68 Apply( + "", + "Eq", + [], + ), + ], + }, + @76-92 ImplementsClause { + var: @76-77 SpaceBefore( + "c", + [ + Newline, + ], + ), + abilities: [ + @89-92 Apply( + "", + "Ord", + [], + ), + ], + }, ], ), - [ - @28-45 ImplementsClause { - var: @28-29 "a", - abilities: [ - @41-45 Apply( - "", - "Hash", - [], - ), - ], - }, - @53-68 ImplementsClause { - var: @53-54 SpaceBefore( - "b", - [ - Newline, - ], - ), - abilities: [ - @66-68 Apply( - "", - "Eq", - [], - ), - ], - }, - @76-92 ImplementsClause { - var: @76-77 SpaceBefore( - "c", - [ - Newline, - ], - ), - abilities: [ - @89-92 Apply( - "", - "Ord", - [], - ), - ], - }, - ], ), - ), - ], - }, - @94-95 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @94-95 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc new file mode 100644 index 0000000000..a175ef55b3 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.formatted.roc @@ -0,0 +1,3 @@ +f : a where a implements A + +f \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast index c1ae8292a2..8c99195e4d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_non_function.expr.result-ast @@ -1,52 +1,57 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-26, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-26 Where( - @4-5 BoundVariable( - "a", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-26, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-26 Where( + @4-5 BoundVariable( + "a", + ), + [ + @12-26 ImplementsClause { + var: @12-13 "a", + abilities: [ + @25-26 Apply( + "", + "A", + [], + ), + ], + }, + ], ), - [ - @12-26 ImplementsClause { - var: @12-13 "a", - abilities: [ - @25-26 Apply( - "", - "A", - [], - ), - ], - }, - ], ), - ), - ], - }, - @28-29 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @28-29 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast index bbe411ff15..c60a3cad6d 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_clause_on_newline.expr.result-ast @@ -1,66 +1,71 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-40, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - Annotation( - @0-1 Identifier { - ident: "f", - }, - @4-40 Where( - @4-12 SpaceAfter( - Function( - [ - @4-5 BoundVariable( - "a", - ), - ], - @9-12 Apply( - "", - "U64", - [], - ), - ), - [ - Newline, - ], - ), - [ - @23-40 ImplementsClause { - var: @23-24 "a", - abilities: [ - @36-40 Apply( +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-40, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + Annotation( + @0-1 Identifier { + ident: "f", + }, + @4-40 Where( + @4-12 SpaceAfter( + Function( + [ + @4-5 BoundVariable( + "a", + ), + ], + @9-12 Apply( "", - "Hash", + "U64", [], ), + ), + [ + Newline, ], - }, - ], + ), + [ + @23-40 ImplementsClause { + var: @23-24 "a", + abilities: [ + @36-40 Apply( + "", + "Hash", + [], + ), + ], + }, + ], + ), ), - ), - ], - }, - @42-43 SpaceBefore( - Var { - module_name: "", - ident: "f", + ], }, - [ - Newline, - Newline, - ], + @42-43 SpaceBefore( + Var { + module_name: "", + ident: "f", + }, + [ + Newline, + Newline, + ], + ), ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast index b362ed0e8d..06fa1daab4 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/where_ident.expr.result-ast @@ -1,67 +1,72 @@ -Defs( - Defs { - tags: [ - Index(2147483648), - ], - regions: [ - @0-39, - ], - space_before: [ - Slice(start = 0, length = 0), - ], - space_after: [ - Slice(start = 0, length = 0), - ], - spaces: [], - type_defs: [], - value_defs: [ - AnnotatedBody { - ann_pattern: @0-5 Identifier { - ident: "where", - }, - ann_type: @8-20 Record { - fields: [ - @9-19 RequiredValue( - @9-14 "where", - [], - @16-19 Apply( - "", - "I32", +SpaceAfter( + Defs( + Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-39, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 0), + ], + spaces: [], + type_defs: [], + value_defs: [ + AnnotatedBody { + ann_pattern: @0-5 Identifier { + ident: "where", + }, + ann_type: @8-20 Record { + fields: [ + @9-19 RequiredValue( + @9-14 "where", [], + @16-19 Apply( + "", + "I32", + [], + ), ), - ), - ], - ext: None, + ], + ext: None, + }, + comment: None, + body_pattern: @21-26 Identifier { + ident: "where", + }, + body_expr: @29-39 Record( + [ + @30-38 RequiredValue( + @30-35 "where", + [], + @37-38 Num( + "1", + ), + ), + ], + ), }, - comment: None, - body_pattern: @21-26 Identifier { + ], + }, + @41-52 SpaceBefore( + RecordAccess( + Var { + module_name: "", ident: "where", }, - body_expr: @29-39 Record( - [ - @30-38 RequiredValue( - @30-35 "where", - [], - @37-38 Num( - "1", - ), - ), - ], - ), - }, - ], - }, - @41-52 SpaceBefore( - RecordAccess( - Var { - module_name: "", - ident: "where", - }, - "where", + "where", + ), + [ + Newline, + Newline, + ], ), - [ - Newline, - Newline, - ], ), + [ + Newline, + ], ) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 8691f589cb..683084b6f4 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -42,9 +42,16 @@ mod test_fmt { Ok(loc_defs) => { fmt_defs(buf, &loc_defs, 0); } - Err(error) => panic!( - r"Unexpected parse failure when parsing this for defs formatting:\n\n{src:?}\n\nParse error was:\n\n{error:?}\n\n" - ), + Err(error) => { + let src = if src.len() > 1000 { + "" + } else { + src + }; + panic!( + "Unexpected parse failure when parsing this for defs formatting:\n\n{src}\n\nParse error was:\n\n{error:?}\n\n" + ) + } } } @@ -56,7 +63,7 @@ mod test_fmt { match module::parse_header(&arena, State::new(src.as_bytes())) { Ok((actual, state)) => { - use roc_fmt::spaces::RemoveSpaces; + use roc_parse::remove_spaces::RemoveSpaces; let mut buf = Buf::new_in(&arena); @@ -3923,6 +3930,7 @@ mod test_fmt { } #[test] + #[ignore] fn with_multiline_pattern_indentation() { expr_formats_to( indoc!( @@ -5434,73 +5442,6 @@ mod test_fmt { )); } - #[test] - fn backpassing_parens_body() { - expr_formats_same(indoc!( - r" - Task.fromResult - ( - b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - )); - - expr_formats_to( - indoc!( - r" - Task.fromResult - (b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - ), - indoc!( - r" - Task.fromResult - ( - b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - ), - ); - - expr_formats_to( - indoc!( - r" - Task.fromResult - (b <- binaryOp ctx - if a == b then - -1 - else - 0) - " - ), - indoc!( - r" - Task.fromResult - ( - b <- binaryOp ctx - if a == b then - -1 - else - 0 - ) - " - ), - ); - } - #[test] fn backpassing_body_on_newline() { expr_formats_same(indoc!( diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 59bd318ccc..c4ceb8de79 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -22,16 +22,16 @@ mod test_snapshots { macro_rules! snapshot_input { (expr => $input:expr) => { - Input::Expr($input.trim()) + Input::Expr($input) }; (header => $input:expr) => { - Input::Header($input.trim()) + Input::Header($input) }; (moduledefs => $input:expr) => { - Input::ModuleDefs($input.trim()) + Input::ModuleDefs($input) }; (full => $input:expr) => { - Input::Full($input.trim()) + Input::Full($input) }; } @@ -187,8 +187,11 @@ mod test_snapshots { fail/ability_first_demand_not_indented_enough.expr, fail/ability_non_signature_expression.expr, fail/alias_or_opaque_fail.expr, + fail/backpassing_after_annotation.expr, + fail/bound_variable.expr, fail/comment_with_tab.expr, fail/def_missing_final_expression.expr, + fail/def_without_newline.expr, fail/double_plus.expr, fail/elm_function_syntax.expr, fail/empty_or_pattern.expr, @@ -215,6 +218,8 @@ mod test_snapshots { fail/module_params_with_missing_arrow.header, fail/module_with_unfinished_params.header, fail/multi_no_end.expr, + fail/newline_before_operator_with_defs.expr, + fail/opaque_type_def_with_newline.expr, fail/pattern_binds_keyword.expr, fail/pattern_in_parens_end.expr, fail/pattern_in_parens_end_comma.expr, @@ -243,6 +248,7 @@ mod test_snapshots { fail/type_inline_alias.expr, fail/underscore_name_type_annotation.expr, fail/unfinished_closure_pattern_in_parens.expr, + fail/unfinished_import_as_or_exposing.moduledefs, fail/unicode_not_hex.expr, fail/weird_escape.expr, fail/when_missing_arrow.expr, @@ -281,16 +287,13 @@ mod test_snapshots { pass/basic_tag.expr, pass/basic_tuple.expr, pass/basic_var.expr, - pass/bound_variable.expr, - pass/call_with_newlines.expr, - pass/closure_in_binop.expr, pass/closure_in_binop_with_spaces.expr, pass/closure_with_underscores.expr, + pass/comma_prefixed_indented_record.expr, pass/comment_after_annotation.expr, pass/comment_after_def.moduledefs, pass/comment_after_expr_in_parens.expr, pass/comment_after_op.expr, - pass/comment_after_tag_in_def.expr, pass/comment_before_colon_def.expr, pass/comment_before_equals_def.expr, pass/comment_before_op.expr, @@ -300,7 +303,7 @@ mod test_snapshots { pass/crash.expr, pass/dbg.expr, pass/dbg_multiline.expr, - pass/def_without_newline.expr, + pass/defs_suffixed_middle_extra_indents.moduledefs, pass/destructure_tag_assignment.expr, pass/docs.expr, pass/empty_app_header.header, @@ -315,7 +318,10 @@ mod test_snapshots { pass/equals.expr, pass/equals_with_spaces.expr, pass/expect.expr, + pass/expect_defs.moduledefs, pass/expect_fx.moduledefs, + pass/expect_single_line.expr, + pass/extra_newline.expr, pass/extra_newline_in_parens.expr, pass/float_with_underscores.expr, pass/fn_with_record_arg.expr, @@ -333,6 +339,7 @@ mod test_snapshots { pass/import_with_comments.moduledefs, pass/import_with_exposed.moduledefs, pass/import_with_params.moduledefs, + pass/indented_after_multi_backpassing.expr, pass/ingested_file.moduledefs, pass/inline_import.expr, pass/inline_ingested_file.expr, @@ -362,6 +369,8 @@ mod test_snapshots { pass/multi_backpassing_in_def.moduledefs, pass/multi_backpassing_with_apply.expr, pass/multi_char_string.expr, + pass/multiline_binop_when_with_comments.expr, + pass/multiline_str_in_pat.expr, pass/multiline_string.expr, pass/multiline_string_in_apply.expr, pass/multiline_tuple_with_comments.expr, @@ -375,9 +384,7 @@ mod test_snapshots { pass/negative_float.expr, pass/negative_in_apply_def.expr, pass/negative_int.expr, - pass/nested_backpassing_no_newline_before.expr, pass/nested_def_annotation.moduledefs, - pass/nested_def_without_newline.expr, pass/nested_if.expr, pass/newline_after_equals.expr, // Regression test for https://github.com/roc-lang/roc/issues/51 pass/newline_after_mul.expr, @@ -385,7 +392,6 @@ mod test_snapshots { pass/newline_after_sub.expr, pass/newline_and_spaces_before_less_than.expr, pass/newline_before_add.expr, - pass/newline_before_operator_with_defs.expr, pass/newline_before_sub.expr, pass/newline_in_packages.full, pass/newline_in_type_alias_application.expr, @@ -412,7 +418,6 @@ mod test_snapshots { pass/opaque_reference_pattern.expr, pass/opaque_reference_pattern_with_arguments.expr, pass/opaque_simple.moduledefs, - pass/opaque_type_def_with_newline.expr, pass/opaque_with_type_arguments.moduledefs, pass/ops_with_newlines.expr, pass/outdented_app_with_record.expr, @@ -421,7 +426,6 @@ mod test_snapshots { pass/outdented_record.expr, pass/packed_singleton_list.expr, pass/parens_in_type_def_apply.expr, - pass/parens_in_value_def_annotation.expr, pass/parenthesized_type_def.expr, pass/parenthesized_type_def_space_before.expr, pass/parenthetical_apply.expr, @@ -449,6 +453,7 @@ mod test_snapshots { pass/record_update.expr, pass/record_with_if.expr, pass/requires_type.header, + pass/separate_defs.moduledefs, pass/single_arg_closure.expr, pass/single_underscore_closure.expr, pass/space_before_colon.full, diff --git a/crates/repl_ui/src/lib.rs b/crates/repl_ui/src/lib.rs index 75cf256848..105b0e78db 100644 --- a/crates/repl_ui/src/lib.rs +++ b/crates/repl_ui/src/lib.rs @@ -7,7 +7,7 @@ use bumpalo::Bump; use colors::{CYAN, END_COL, GREEN}; use const_format::concatcp; use repl_state::{parse_src, ParseOutcome}; -use roc_parse::ast::{Expr, ValueDef}; +use roc_parse::ast::{Expr, ExtractSpaces, ValueDef}; use roc_repl_eval::gen::{Problems, ReplOutput}; use roc_reporting::report::StyleCodes; @@ -63,24 +63,30 @@ pub fn is_incomplete(input: &str) -> bool { match parse_src(&arena, input) { ParseOutcome::Incomplete => !input.ends_with('\n'), - // Standalone annotations are default incomplete, because we can't know - // whether they're about to annotate a body on the next line - // (or if not, meaning they stay standalone) until you press Enter again! - // - // So it's Incomplete until you've pressed Enter again (causing the input to end in "\n") - ParseOutcome::ValueDef(ValueDef::Annotation(_, _)) if !input.ends_with('\n') => true, - ParseOutcome::Expr(Expr::When(_, _)) => { - // There might be lots of `when` branches, so don't assume the user is done entering - // them until they enter a blank line! - !input.ends_with('\n') + ParseOutcome::DefsAndExpr(defs, None) => { + // Standalone annotations are default incomplete, because we can't know + // whether they're about to annotate a body on the next line + // (or if not, meaning they stay standalone) until you press Enter again! + // + // So it's Incomplete until you've pressed Enter again (causing the input to end in "\n") + if matches!(defs.last(), Some(Err(ValueDef::Annotation(_, _)))) { + !input.ends_with('\n') + } else { + false + } + } + ParseOutcome::DefsAndExpr(_, Some(expr)) => { + if matches!(expr.extract_spaces().item, Expr::When(..)) { + // There might be lots of `when` branches, so don't assume the user is done entering + // them until they enter a blank line! + !input.ends_with('\n') + } else { + false + } + } + ParseOutcome::Empty | ParseOutcome::Help | ParseOutcome::Exit | ParseOutcome::SyntaxErr => { + false } - ParseOutcome::Empty - | ParseOutcome::Help - | ParseOutcome::Exit - | ParseOutcome::ValueDef(_) - | ParseOutcome::TypeDef(_) - | ParseOutcome::SyntaxErr - | ParseOutcome::Expr(_) => false, } } diff --git a/crates/repl_ui/src/repl_state.rs b/crates/repl_ui/src/repl_state.rs index 2998fb29ac..2b612d832c 100644 --- a/crates/repl_ui/src/repl_state.rs +++ b/crates/repl_ui/src/repl_state.rs @@ -4,13 +4,11 @@ use std::{fs, io}; use bumpalo::Bump; use roc_collections::MutSet; use roc_load::MonomorphizedModule; -use roc_parse::ast::{Expr, Pattern, StrLiteral, TypeDef, TypeHeader, ValueDef}; -use roc_parse::expr::{parse_single_def, ExprParseOptions, SingleDef}; -use roc_parse::parser::Parser; +use roc_parse::ast::{Defs, Expr, Pattern, StrLiteral, TypeDef, TypeHeader, ValueDef}; +use roc_parse::expr::parse_repl_defs_and_optional_expr; +use roc_parse::parser::EWhen; use roc_parse::parser::{EClosure, EExpr, EPattern}; -use roc_parse::parser::{EWhen, Either}; use roc_parse::state::State; -use roc_parse::{join_alias_to_body, join_ann_to_body}; use roc_region::all::Loc; use roc_repl_eval::gen::{compile_to_mono, Problems}; use roc_reporting::report::Palette; @@ -64,11 +62,11 @@ impl ReplState { target: Target, palette: Palette, ) -> ReplAction<'a> { - let pending_past_def; + let mut pending_past_def = None; let src: &str = match parse_src(arena, line) { ParseOutcome::Empty | ParseOutcome::Help => return ReplAction::Help, ParseOutcome::Exit => return ReplAction::Exit, - ParseOutcome::Expr(_) | ParseOutcome::Incomplete | ParseOutcome::SyntaxErr => { + ParseOutcome::Incomplete | ParseOutcome::SyntaxErr => { pending_past_def = None; // If it's a SyntaxErr (or Incomplete at this point, meaning it will @@ -76,149 +74,188 @@ impl ReplState { // proceed as normal and let the error reporting happen during eval. line } - ParseOutcome::ValueDef(value_def) => { - match value_def { - ValueDef::Annotation( - Loc { - // TODO is this right for suffixed - value: Pattern::Identifier { ident }, - .. - }, - _, - ) => { - // Record the standalone type annotation for future use. - self.add_past_def(ident.trim_end().to_string(), line.to_string()); + ParseOutcome::DefsAndExpr(_defs, Some(_expr)) => { + // For now if we have a expr, we bundle everything together into a single + // Defs expr, matching the older behavior of the parser. If we instead + // use the branch below, it would trigger a bug further in the compiler. - // Return early without running eval, since standalone annotations - // cannot be evaluated as expressions. - return ReplAction::Nothing; - } - ValueDef::Body( - Loc { - // TODO is this right for suffixed - value: Pattern::Identifier { ident }, - .. - }, - _, - ) - | ValueDef::AnnotatedBody { - body_pattern: - Loc { - // TODO is this right for suffixed - value: Pattern::Identifier { ident }, - .. - }, - .. - } => { - pending_past_def = Some((ident.to_string(), line.to_string())); - - // Recreate the body of the def and then evaluate it as a lookup. - // We do this so that any errors will get reported as part of this expr; - // if we just did a lookup on the past def, then errors wouldn't get - // reported because we filter out errors whose regions are in past defs. - let mut buf = bumpalo::collections::string::String::with_capacity_in( - ident.len() + line.len() + 1, - arena, - ); - - buf.push_str(line); - buf.push('\n'); - buf.push_str(ident); - - buf.into_bump_str() - } - ValueDef::Annotation(_, _) - | ValueDef::Body(_, _) - | ValueDef::AnnotatedBody { .. } => { - todo!("handle pattern other than identifier (which repl doesn't support).\ - \nTip: this error can be triggered when trying to define a variable with a character that is not allowed, \ - like starting with an uppercase character or using underdash (_).") - } - ValueDef::Dbg { .. } => { - todo!("handle receiving a `dbg` - what should the repl do for that?") - } - ValueDef::Expect { .. } => { - todo!("handle receiving an `expect` - what should the repl do for that?") - } - ValueDef::ExpectFx { .. } => { - todo!("handle receiving an `expect-fx` - what should the repl do for that?") - } - ValueDef::ModuleImport(import) => match import.name.value.package { - Some(_) => { - todo!("handle importing a module from a package") - } - None => { - let mut filename = PathBuf::new(); - - for part in import.name.value.name.parts() { - filename.push(part); - } - - filename.set_extension("roc"); - - // Check we can read the file before we add it to past defs. - // If we didn't do this, the bad import would remain in past_defs - // and we'd report it on every subsequent evaluation. - if let Err(err) = fs::metadata(&filename) { - return ReplAction::FileProblem { - filename, - error: err.kind(), - }; - } - - self.past_defs.push(PastDef::Import(line.to_string())); - - return ReplAction::Nothing; - } - }, - ValueDef::IngestedFileImport(file) => { - if let StrLiteral::PlainLine(path) = file.path.value { - let filename = PathBuf::from(path); - if let Err(err) = fs::metadata(&filename) { - return ReplAction::FileProblem { - filename, - error: err.kind(), - }; - } - } - - self.past_defs.push(PastDef::Import(line.to_string())); - - return ReplAction::Nothing; - } - ValueDef::Stmt(_) => todo!(), - } + pending_past_def = None; + line } - ParseOutcome::TypeDef(TypeDef::Alias { - header: - TypeHeader { - name: Loc { value: ident, .. }, - .. - }, - .. - }) - | ParseOutcome::TypeDef(TypeDef::Opaque { - header: - TypeHeader { - name: Loc { value: ident, .. }, - .. - }, - .. - }) - | ParseOutcome::TypeDef(TypeDef::Ability { - header: - TypeHeader { - name: Loc { value: ident, .. }, - .. - }, - .. - }) => { - // Record the type for future use. - self.add_past_def(ident.trim_end().to_string(), line.to_string()); + ParseOutcome::DefsAndExpr(defs, None) => { + let mut last_src = None; - // Return early without running eval, since none of these - // can be evaluated as expressions. - return ReplAction::Nothing; + for def in defs.loc_defs() { + match def { + Ok(td) => { + match td.value { + TypeDef::Alias { + header: + TypeHeader { + name: Loc { value: ident, .. }, + .. + }, + .. + } + | TypeDef::Opaque { + header: + TypeHeader { + name: Loc { value: ident, .. }, + .. + }, + .. + } + | TypeDef::Ability { + header: + TypeHeader { + name: Loc { value: ident, .. }, + .. + }, + .. + } => { + // Record the type for future use. + self.add_past_def( + ident.trim_end().to_string(), + line[td.byte_range()].to_string(), + ); + + // Return early without running eval, since none of these + // can be evaluated as expressions. + return ReplAction::Nothing; + } + } + } + Err(vd) => { + match vd.value { + ValueDef::Annotation( + Loc { + // TODO is this right for suffixed + value: Pattern::Identifier { ident }, + .. + }, + _, + ) => { + // Record the standalone type annotation for future use. + self.add_past_def( + ident.trim_end().to_string(), + line[vd.byte_range()].to_string(), + ); + + // Return early without running eval, since standalone annotations + // cannot be evaluated as expressions. + return ReplAction::Nothing; + } + ValueDef::Body( + Loc { + // TODO is this right for suffixed + value: Pattern::Identifier { ident }, + .. + }, + _, + ) + | ValueDef::AnnotatedBody { + body_pattern: + Loc { + // TODO is this right for suffixed + value: Pattern::Identifier { ident }, + .. + }, + .. + } => { + pending_past_def = Some(( + ident.to_string(), + line[vd.byte_range()].to_string(), + )); + + // Recreate the body of the def and then evaluate it as a lookup. + // We do this so that any errors will get reported as part of this expr; + // if we just did a lookup on the past def, then errors wouldn't get + // reported because we filter out errors whose regions are in past defs. + let mut buf = + bumpalo::collections::string::String::with_capacity_in( + ident.len() + line.len() + 1, + arena, + ); + + buf.push_str(line); + buf.push('\n'); + buf.push_str(ident); + + last_src = Some(buf.into_bump_str()); + } + ValueDef::Annotation(_, _) + | ValueDef::Body(_, _) + | ValueDef::AnnotatedBody { .. } => { + todo!("handle pattern other than identifier (which repl doesn't support).\ + \nTip: this error can be triggered when trying to define a variable with a character that is not allowed, \ + like starting with an uppercase character or using underdash (_).") + } + ValueDef::Dbg { .. } => { + todo!("handle receiving a `dbg` - what should the repl do for that?") + } + ValueDef::Expect { .. } => { + todo!("handle receiving an `expect` - what should the repl do for that?") + } + ValueDef::ExpectFx { .. } => { + todo!("handle receiving an `expect-fx` - what should the repl do for that?") + } + ValueDef::ModuleImport(import) => match import.name.value.package { + Some(_) => { + todo!("handle importing a module from a package") + } + None => { + let mut filename = PathBuf::new(); + + for part in import.name.value.name.parts() { + filename.push(part); + } + + filename.set_extension("roc"); + + // Check we can read the file before we add it to past defs. + // If we didn't do this, the bad import would remain in past_defs + // and we'd report it on every subsequent evaluation. + if let Err(err) = fs::metadata(&filename) { + return ReplAction::FileProblem { + filename, + error: err.kind(), + }; + } + + self.past_defs.push(PastDef::Import( + line[vd.byte_range()].to_string(), + )); + + return ReplAction::Nothing; + } + }, + ValueDef::IngestedFileImport(file) => { + if let StrLiteral::PlainLine(path) = file.path.value { + let filename = PathBuf::from(path); + if let Err(err) = fs::metadata(&filename) { + return ReplAction::FileProblem { + filename, + error: err.kind(), + }; + } + } + + self.past_defs + .push(PastDef::Import(line[vd.byte_range()].to_string())); + + return ReplAction::Nothing; + } + ValueDef::Stmt(_) => todo!(), + } + } + } + } + + if let Some(src) = last_src { + src + } else { + return ReplAction::Nothing; + } } }; @@ -251,9 +288,7 @@ impl ReplState { #[derive(Debug, PartialEq)] pub enum ParseOutcome<'a> { - ValueDef(ValueDef<'a>), - TypeDef(TypeDef<'a>), - Expr(Expr<'a>), + DefsAndExpr(Defs<'a>, Option>>), Incomplete, SyntaxErr, Empty, @@ -261,6 +296,18 @@ pub enum ParseOutcome<'a> { Exit, } +/// Special case some syntax errors to allow for multi-line inputs +fn parse_outcome_for_error(e: EExpr<'_>) -> ParseOutcome<'_> { + match e { + EExpr::Closure(EClosure::Body(_, _), _) + | EExpr::When(EWhen::Pattern(EPattern::Start(_), _), _) + | EExpr::Record(_, _) + | EExpr::Start(_) + | EExpr::IndentStart(_) => ParseOutcome::Incomplete, + _ => ParseOutcome::SyntaxErr, + } +} + pub fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> { match line.trim().to_lowercase().as_str() { "" => ParseOutcome::Empty, @@ -273,161 +320,15 @@ pub fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> { _ => { let src_bytes = line.as_bytes(); - match roc_parse::expr::loc_expr(true).parse(arena, State::new(src_bytes), 0) { - Ok((_, loc_expr, _)) => ParseOutcome::Expr(loc_expr.value), - Err((roc_parse::parser::Progress::MadeProgress, EExpr::Start(_))) => { - ParseOutcome::Empty - } - // Special case some syntax errors to allow for multi-line inputs - Err((_, EExpr::Closure(EClosure::Body(_, _), _))) - | Err((_, EExpr::When(EWhen::Pattern(EPattern::Start(_), _), _))) - | Err((_, EExpr::Record(_, _))) - | Err((_, EExpr::Start(_))) - | Err((_, EExpr::IndentStart(_))) => ParseOutcome::Incomplete, - Err((_, EExpr::DefMissingFinalExpr(_))) - | Err((_, EExpr::DefMissingFinalExpr2(_, _))) => { - // This indicates that we had an attempted def; re-parse it as a single-line def. - match parse_single_def( - ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }, - 0, - arena, - State::new(src_bytes), - ) { - Ok(( - _, - Some(SingleDef { - type_or_value: Either::First(TypeDef::Alias { header, ann }), - .. - }), - state, - )) => { - // This *could* be an AnnotatedBody, e.g. in a case like this: - // - // UserId x : [UserId Int] - // UserId x = UserId 42 - // - // We optimistically parsed the first line as an alias; we might now - // turn it into an annotation. - match parse_single_def( - ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }, - 0, - arena, - state, - ) { - Ok(( - _, - Some(SingleDef { - type_or_value: - Either::Second(ValueDef::Body(loc_pattern, loc_def_expr)), - region, - spaces_before, - spaces_after: _, - }), - _, - )) if spaces_before.len() <= 1 => { - // This was, in fact, an AnnotatedBody! Build and return it. - let (value_def, _) = join_alias_to_body!( - arena, - loc_pattern, - loc_def_expr, - header, - &ann, - spaces_before, - region - ); - - ParseOutcome::ValueDef(value_def) - } - _ => { - // This was not an AnnotatedBody, so return the alias. - ParseOutcome::TypeDef(TypeDef::Alias { header, ann }) - } - } - } - Ok(( - _, - Some(SingleDef { - type_or_value: - Either::Second(ValueDef::Annotation(ann_pattern, ann_type)), - .. - }), - state, - )) => { - // This *could* be an AnnotatedBody, if the next line is a body. - match parse_single_def( - ExprParseOptions { - accept_multi_backpassing: true, - check_for_arrow: true, - }, - 0, - arena, - state, - ) { - Ok(( - _, - Some(SingleDef { - type_or_value: - Either::Second(ValueDef::Body(loc_pattern, loc_def_expr)), - region, - spaces_before, - spaces_after: _, - }), - _, - )) if spaces_before.len() <= 1 => { - // Inlining this borrow makes clippy unhappy for some reason. - let ann_pattern = &ann_pattern; - - // This was, in fact, an AnnotatedBody! Build and return it. - let (value_def, _) = join_ann_to_body!( - arena, - loc_pattern, - loc_def_expr, - ann_pattern, - &ann_type, - spaces_before, - region - ); - - ParseOutcome::ValueDef(value_def) - } - _ => { - // This was not an AnnotatedBody, so return the standalone annotation. - ParseOutcome::ValueDef(ValueDef::Annotation( - ann_pattern, - ann_type, - )) - } - } - } - Ok(( - _, - Some(SingleDef { - type_or_value: Either::First(type_def), - .. - }), - _, - )) => ParseOutcome::TypeDef(type_def), - Ok(( - _, - Some(SingleDef { - type_or_value: Either::Second(value_def), - .. - }), - _, - )) => ParseOutcome::ValueDef(value_def), - Ok((_, None, _)) => { - todo!("TODO determine appropriate ParseOutcome for Ok(None)") - } - Err(_) => ParseOutcome::SyntaxErr, + match parse_repl_defs_and_optional_expr(arena, State::new(src_bytes)) { + Err((_, e)) => parse_outcome_for_error(e), + Ok((_, (defs, opt_last_expr), _state)) => { + if defs.is_empty() && opt_last_expr.is_none() { + ParseOutcome::Empty + } else { + ParseOutcome::DefsAndExpr(defs, opt_last_expr) } } - Err(_) => ParseOutcome::SyntaxErr, } } } diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index 111c523ccc..e0a53ea3b0 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -695,6 +695,29 @@ fn to_expr_report<'a>( severity, } } + EExpr::StmtAfterExpr(pos) => { + let surroundings = Region::new(start, *pos); + let region = LineColumnRegion::from_pos(lines.convert_pos(*pos)); + + let doc = alloc.stack([ + alloc + .reflow(r"I just finished parsing an expression with a series of definitions,"), + alloc.reflow( + r"and this line is indented as if it's intended to be part of that expression:", + ), + alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), + alloc.concat([alloc.reflow( + "However, I already saw the final expression in that series of definitions.", + )]), + ]); + + Report { + filename, + doc, + title: "STATEMENT AFTER EXPRESSION".to_string(), + severity, + } + } _ => todo!("unhandled parse error: {:?}", parse_problem), } } From ecb8b12167706778b4dc6f0a2a71ebb0e8cd1bba Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sat, 27 Jul 2024 14:49:56 -0700 Subject: [PATCH 28/30] Add back working tests --- crates/compiler/test_syntax/tests/test_fmt.rs | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/crates/compiler/test_syntax/tests/test_fmt.rs b/crates/compiler/test_syntax/tests/test_fmt.rs index 683084b6f4..ec4ee347f5 100644 --- a/crates/compiler/test_syntax/tests/test_fmt.rs +++ b/crates/compiler/test_syntax/tests/test_fmt.rs @@ -5442,6 +5442,73 @@ mod test_fmt { )); } + #[test] + fn backpassing_parens_body() { + expr_formats_same(indoc!( + r" + Task.fromResult + ( + b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + )); + + expr_formats_to( + indoc!( + r" + Task.fromResult + (b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + ), + indoc!( + r" + Task.fromResult + ( + b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + ), + ); + + expr_formats_to( + indoc!( + r" + Task.fromResult + (b <- binaryOp ctx + if a == b then + -1 + else + 0) + " + ), + indoc!( + r" + Task.fromResult + ( + b <- binaryOp ctx + if a == b then + -1 + else + 0 + ) + " + ), + ); + } + #[test] fn backpassing_body_on_newline() { expr_formats_same(indoc!( From 413de7f72ed84aa8ebc10505af4c01928d0410f8 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Sun, 28 Jul 2024 11:37:40 -0400 Subject: [PATCH 29/30] s/roc/Roc in an error message Signed-off-by: Richard Feldman --- crates/compiler/load/tests/test_reporting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index c50f453235..f76c575fca 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -5442,7 +5442,7 @@ mod test_reporting { Looks like you are trying to define a function. - In roc, functions are always written as a lambda, like + In Roc, functions are always written as a lambda, like increment = \n -> n + 1 "### From df915b936df1b8a73e9c26909880db75bfd8e0c6 Mon Sep 17 00:00:00 2001 From: Joshua Warner Date: Sun, 28 Jul 2024 12:01:20 -0700 Subject: [PATCH 30/30] Feedback: add doc comments, verbiage changes, capitalize Roc, remove a resolved TODO --- crates/compiler/load/tests/test_reporting.rs | 8 +-- crates/compiler/parse/src/expr.rs | 66 ++++++++++++++++++- .../compiler/test_syntax/src/bin/minimize.rs | 12 ++++ crates/compiler/test_syntax/src/minimize.rs | 9 ++- crates/repl_test/src/tests.rs | 2 +- crates/reporting/src/error/parse.rs | 8 +-- 6 files changed, 93 insertions(+), 12 deletions(-) diff --git a/crates/compiler/load/tests/test_reporting.rs b/crates/compiler/load/tests/test_reporting.rs index f76c575fca..5c6eee967c 100644 --- a/crates/compiler/load/tests/test_reporting.rs +++ b/crates/compiler/load/tests/test_reporting.rs @@ -3374,7 +3374,7 @@ mod test_reporting { f x y = x " ), - @r#" + @r###" ── ARGUMENTS BEFORE EQUALS in tmp/elm_function_syntax/Test.roc ───────────────── I am partway through parsing a definition, but I got stuck here: @@ -3385,9 +3385,9 @@ mod test_reporting { 4│ f x y = x ^^^ - Looks like you are trying to define a function. In roc, functions are + Looks like you are trying to define a function. In Roc, functions are always written as a lambda, like increment = \n -> n + 1. - "# + "### ); test_report!( @@ -5030,7 +5030,7 @@ mod test_reporting { 4│ import svg.Path a ^ - I was expecting to see the `as` keyword, like: + I was expecting to see the `as` keyword next, like: import svg.Path as SvgPath diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index eaa5a1eacc..8b7948994d 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -339,6 +339,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> { } } +/// Entry point for parsing an expression. fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, EExpr<'a>> { one_of![ loc(specialize_err(EExpr::If, if_expr_help(options))), @@ -350,6 +351,7 @@ fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc>, E .trace("expr_start") } +/// Parse a chain of expressions separated by operators. Also handles function application. fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> { (move |arena, state: State<'a>, min_indent: u32| { parse_expr_operator_chain(arena, state, min_indent, options) @@ -431,6 +433,9 @@ fn parse_expr_operator_chain<'a>( } } +/// We're part way thru parsing an expression, e.g. `bar foo `. +/// We just tried parsing an argument and determined we couldn't - +/// so we're going to try parsing an operator. #[allow(clippy::too_many_arguments)] fn parse_expr_after_apply<'a>( arena: &'a Bump, @@ -1090,6 +1095,7 @@ fn extract_tag_and_spaces<'a>(arena: &'a Bump, expr: Expr<'a>) -> Option( arena: &'a Bump, @@ -1331,6 +1337,7 @@ mod ability { } } +/// Parse the series of "demands" (e.g. similar to methods in a rust trait), for an ability definition. fn finish_parsing_ability_def_help<'a>( call_min_indent: u32, name: Loc<&'a str>, @@ -1379,6 +1386,16 @@ fn finish_parsing_ability_def_help<'a>( Ok((MadeProgress, (type_def, def_region), state)) } +/// A Stmt is an intermediate representation used only during parsing. +/// It consists of a fragment of code that hasn't been fully stitched together yet. +/// For example, each of the following lines is a Stmt: +/// - `foo bar` (Expr) +/// - `foo, bar <- baz` (Backpassing) +/// - `Foo : [A, B, C]` (TypeDef) +/// - `foo = \x -> x + 1` (ValueDef) +/// +/// Note in particular that the Backpassing Stmt doesn't make any sense on its own; +/// we need to link it up with the following stmts to make a complete expression. #[derive(Debug, Clone, Copy)] pub enum Stmt<'a> { Expr(Expr<'a>), @@ -1387,6 +1404,11 @@ pub enum Stmt<'a> { ValueDef(ValueDef<'a>), } +/// Having just parsed an operator, we need to dispatch to the appropriate +/// parsing function based on the operator. +/// +/// Note, this function is very similar to `parse_expr_operator`, but it +/// handles additional cases to allow assignments / type annotations / etc. #[allow(clippy::too_many_arguments)] fn parse_stmt_operator<'a>( arena: &'a Bump, @@ -1462,6 +1484,8 @@ fn parse_stmt_operator<'a>( } } +/// We just parsed an operator. Parse the expression that follows, taking special care +/// that this might be a negated term. (`-x` is a negated term, not a binary operation) #[allow(clippy::too_many_arguments)] fn parse_expr_operator<'a>( arena: &'a Bump, @@ -1508,6 +1532,7 @@ fn parse_expr_operator<'a>( } } +/// Continue parsing terms after we just parsed a binary operator #[allow(clippy::too_many_arguments)] fn parse_after_binop<'a>( arena: &'a Bump, @@ -1581,6 +1606,7 @@ fn parse_after_binop<'a>( } } +/// Parse the rest of a backpassing statement, after the <- operator fn parse_stmt_backpassing<'a>( arena: &'a Bump, state: State<'a>, @@ -1590,8 +1616,6 @@ fn parse_stmt_backpassing<'a>( options: ExprParseOptions, spaces_after_operator: &'a [CommentOrNewline], ) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> { - // called after parsing the <- operator - let expr_region = expr_state.expr.region; let call = expr_state @@ -1627,6 +1651,8 @@ fn parse_stmt_backpassing<'a>( Ok((MadeProgress, ret, state)) } +/// We just saw a `,` that we think is part of a backpassing statement. +/// Parse the rest of the statement. fn parse_stmt_multi_backpassing<'a>( mut expr_state: ExprState<'a>, arena: &'a Bump, @@ -1689,6 +1715,7 @@ fn parse_stmt_multi_backpassing<'a>( } } +/// We just saw the '=' operator of an assignment stmt. Continue parsing from there. fn parse_stmt_assignment<'a>( arena: &'a Bump, state: State<'a>, @@ -1733,6 +1760,7 @@ fn parse_stmt_assignment<'a>( Ok((MadeProgress, Stmt::ValueDef(value_def), state)) } +/// We just saw a unary negation operator, and now we need to parse the expression. #[allow(clippy::too_many_arguments)] fn parse_negated_term<'a>( arena: &'a Bump, @@ -1779,6 +1807,8 @@ fn parse_negated_term<'a>( ) } +/// Parse an expression, not allowing `if`/`when`/etc. +/// TODO: this should probably be subsumed into `parse_expr_operator_chain` #[allow(clippy::too_many_arguments)] fn parse_expr_end<'a>( arena: &'a Bump, @@ -1837,6 +1867,13 @@ fn parse_expr_end<'a>( } } +/// We're part way thru parsing an expression, e.g. `bar foo `. +/// We just tried parsing an argument and determined we couldn't - +/// so we're going to try parsing an operator. +/// +/// Note that this looks a lot like `parse_expr_after_apply`, except +/// we handle the additional case of backpassing, which is valid +/// at the statement level but not at the expression level. fn parse_stmt_after_apply<'a>( arena: &'a Bump, state: State<'a>, @@ -2679,6 +2716,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf< } } +/// Parse a block of statements (parser combinator version of `parse_block`) fn block<'a, E>( options: ExprParseOptions, require_indent: bool, @@ -2701,6 +2739,11 @@ where .trace("block") } +/// Parse a block of statements. +/// For example, the then and else branches of an `if` expression are both blocks. +/// There are two cases here: +/// 1. If there is a preceding newline, then the block must be indented and is allowed to have definitions. +/// 2. If there is no preceding newline, then the block must consist of a single expression (no definitions). fn parse_block<'a, E>( options: ExprParseOptions, arena: &'a Bump, @@ -2735,6 +2778,9 @@ where ) } +/// Parse a block of statements, and process that into an Expr. +/// Assumes the caller has already parsed the optional first "space" (newline), +/// and decided whether to allow definitions. #[allow(clippy::too_many_arguments)] fn parse_block_inner<'a, E>( options: ExprParseOptions, @@ -2797,6 +2843,15 @@ where } } +/// Parse a sequence of statements, which we'll later process into an expression. +/// Statements can include: +/// - assignments +/// - type annotations +/// - expressions +/// - [multi]backpassing +/// +/// This function doesn't care about whether the order of those statements makes any sense. +/// e.g. it will happily parse two expressions in a row, or backpassing with nothing following it. fn parse_stmt_seq<'a, E: SpaceProblem + 'a>( arena: &'a Bump, mut state: State<'a>, @@ -2868,6 +2923,7 @@ fn parse_stmt_seq<'a, E: SpaceProblem + 'a>( Ok((MadeProgress, stmts, state)) } +/// Check if the current byte is a terminator for a sequence of statements fn at_terminator(state: &State<'_>) -> bool { matches!( state.bytes().first(), @@ -2875,6 +2931,8 @@ fn at_terminator(state: &State<'_>) -> bool { ) } +/// Convert a sequence of statements into a `Expr::Defs` expression +/// (which is itself a Defs struct and final expr) fn stmts_to_expr<'a>( stmts: &[SpacesBefore<'a, Loc>>], arena: &'a Bump, @@ -2940,6 +2998,9 @@ fn stmts_to_expr<'a>( } } +/// Convert a sequence of `Stmt` into a Defs and an optional final expression. +/// Future refactoring opportunity: push this logic directly into where we're +/// parsing the statements. fn stmts_to_defs<'a>( stmts: &[SpacesBefore<'a, Loc>>], mut defs: Defs<'a>, @@ -3133,6 +3194,7 @@ fn stmts_to_defs<'a>( Ok((defs, last_expr)) } +/// Given a type alias and a value definition, join them into a AnnotatedBody pub fn join_alias_to_body<'a>( arena: &'a Bump, header: TypeHeader<'a>, diff --git a/crates/compiler/test_syntax/src/bin/minimize.rs b/crates/compiler/test_syntax/src/bin/minimize.rs index 404a45b5a6..bac6e536c6 100644 --- a/crates/compiler/test_syntax/src/bin/minimize.rs +++ b/crates/compiler/test_syntax/src/bin/minimize.rs @@ -1,3 +1,15 @@ +//! Generate a minimized version of a given input, by removing parts of it. +//! This is useful for debugging, when you have a large input that causes a failure, +//! and you want to find the smallest input that still causes the failure. +//! +//! Typical usage: +//! `cargo run --release --bin minimize -- full ` +//! +//! This tool will churn on that for a while, and eventually print out a minimized version +//! of the input that still triggers the bug. +//! +//! Note that `--release` is important, as this tool is very slow in debug mode. + use test_syntax::{minimize::print_minimizations, test_helpers::InputKind}; fn main() { diff --git a/crates/compiler/test_syntax/src/minimize.rs b/crates/compiler/test_syntax/src/minimize.rs index d8fd4647d4..946b31147e 100644 --- a/crates/compiler/test_syntax/src/minimize.rs +++ b/crates/compiler/test_syntax/src/minimize.rs @@ -1,3 +1,10 @@ +//! Generate a minimized version of a given input, by removing parts of it. +//! This is useful for debugging, when you have a large input that causes a failure, +//! and you want to find the smallest input that still causes the failure. +//! +//! Most users will want to use the binary instead of this module directly. +//! e.g. `cargo run --release --bin minimize -- full ` + use crate::test_helpers::{Input, InputKind}; use bumpalo::Bump; use roc_parse::{ast::Malformed, remove_spaces::RemoveSpaces}; @@ -79,7 +86,7 @@ fn round_trip_once(input: Input<'_>) -> Option { "Initial parse failed: {:?}", e.remove_spaces(&arena) )) - } // todo: strip pos info, use the error + } }; if actual.is_malformed() { diff --git a/crates/repl_test/src/tests.rs b/crates/repl_test/src/tests.rs index 5aae4e2e1d..7af99812c7 100644 --- a/crates/repl_test/src/tests.rs +++ b/crates/repl_test/src/tests.rs @@ -1133,7 +1133,7 @@ fn parse_problem() { 4│ add m n = m + n ^^^ - Looks like you are trying to define a function. In roc, functions are + Looks like you are trying to define a function. In Roc, functions are always written as a lambda, like increment = \n -> n + 1. "# ), diff --git a/crates/reporting/src/error/parse.rs b/crates/reporting/src/error/parse.rs index e0a53ea3b0..c37482cfb6 100644 --- a/crates/reporting/src/error/parse.rs +++ b/crates/reporting/src/error/parse.rs @@ -203,7 +203,7 @@ fn to_expr_report<'a>( alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), alloc.concat([ alloc.reflow("Looks like you are trying to define a function. "), - alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.reflow("In Roc, functions are always written as a lambda, like "), alloc.parser_suggestion("increment = \\n -> n + 1"), alloc.reflow("."), ]), @@ -257,7 +257,7 @@ fn to_expr_report<'a>( Context::InDef(_pos) => { vec![alloc.stack([ alloc.reflow("Looks like you are trying to define a function. "), - alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.reflow("In Roc, functions are always written as a lambda, like "), alloc .parser_suggestion("increment = \\n -> n + 1") .indent(4), @@ -509,7 +509,7 @@ fn to_expr_report<'a>( alloc.region_with_subregion(lines.convert_region(surroundings), region, severity), alloc.concat([ alloc.reflow("Looks like you are trying to define a function. "), - alloc.reflow("In roc, functions are always written as a lambda, like "), + alloc.reflow("In Roc, functions are always written as a lambda, like "), alloc.parser_suggestion("increment = \\n -> n + 1"), alloc.reflow("."), ]), @@ -1541,7 +1541,7 @@ fn to_import_report<'a>( alloc.concat([ alloc.reflow("I was expecting to see the "), alloc.keyword("as"), - alloc.reflow(" keyword, like:"), + alloc.reflow(" keyword next, like:"), ]), alloc .parser_suggestion("import svg.Path as SvgPath")