From 3bc5c1dc1218ad90af9ce377377c459a1ba96620 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 22:14:08 -0700 Subject: [PATCH 1/6] Combine ! and ? into single TrySuffix AST node --- crates/compiler/can/src/desugar.rs | 32 ++- crates/compiler/can/src/expr.rs | 4 +- crates/compiler/can/src/suffixed.rs | 200 +++++++++------- crates/compiler/fmt/src/expr.rs | 12 +- crates/compiler/module/src/called_via.rs | 4 + crates/compiler/parse/src/ast.rs | 26 ++- crates/compiler/parse/src/expr.rs | 16 +- crates/compiler/parse/src/ident.rs | 3 +- crates/compiler/parse/src/normalize.rs | 2 +- ...middle_extra_indents.moduledefs.result-ast | 14 +- .../pass/pizza_bang.moduledefs.result-ast | 21 +- .../snapshots/pass/suffixed.expr.result-ast | 10 - .../pass/suffixed_bang.expr.result-ast | 13 ++ ...ffixed.expr.roc => suffixed_bang.expr.roc} | 0 ...ng_multiple_defs.moduledefs.formatted.roc} | 0 ..._bang_multiple_defs.moduledefs.result-ast} | 21 +- ...uffixed_bang_multiple_defs.moduledefs.roc} | 0 ...> suffixed_bang_nested.expr.formatted.roc} | 0 ...t => suffixed_bang_nested.expr.result-ast} | 14 +- ...expr.roc => suffixed_bang_nested.expr.roc} | 0 ... suffixed_bang_one_def.full.formatted.roc} | 0 ... => suffixed_bang_one_def.full.result-ast} | 14 +- ...ull.roc => suffixed_bang_one_def.full.roc} | 0 ...xed_bang_optional_last.full.formatted.roc} | 0 ...ffixed_bang_optional_last.full.result-ast} | 7 +- ...c => suffixed_bang_optional_last.full.roc} | 0 .../pass/suffixed_question.expr.result-ast | 13 ++ .../snapshots/pass/suffixed_question.expr.roc | 1 + ...ion_multiple_defs.moduledefs.formatted.roc | 5 + ...estion_multiple_defs.moduledefs.result-ast | 119 ++++++++++ ...ixed_question_multiple_defs.moduledefs.roc | 5 + ...uffixed_question_nested.expr.formatted.roc | 1 + .../suffixed_question_nested.expr.result-ast | 45 ++++ .../pass/suffixed_question_nested.expr.roc | 1 + ...ffixed_question_one_def.full.formatted.roc | 17 ++ .../suffixed_question_one_def.full.result-ast | 213 ++++++++++++++++++ .../pass/suffixed_question_one_def.full.roc | 16 ++ ..._question_optional_last.full.formatted.roc | 9 + ...xed_question_optional_last.full.result-ast | 134 +++++++++++ .../suffixed_question_optional_last.full.roc | 12 + .../test_syntax/tests/test_snapshots.rs | 15 +- crates/language_server/src/analysis/tokens.rs | 2 +- 42 files changed, 859 insertions(+), 162 deletions(-) delete mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed.expr.roc => suffixed_bang.expr.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_multiple_defs.moduledefs.formatted.roc => suffixed_bang_multiple_defs.moduledefs.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_multiple_defs.moduledefs.result-ast => suffixed_bang_multiple_defs.moduledefs.result-ast} (86%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_multiple_defs.moduledefs.roc => suffixed_bang_multiple_defs.moduledefs.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_nested.expr.formatted.roc => suffixed_bang_nested.expr.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_nested.expr.result-ast => suffixed_bang_nested.expr.result-ast} (81%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_nested.expr.roc => suffixed_bang_nested.expr.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_one_def.full.formatted.roc => suffixed_bang_one_def.full.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_one_def.full.result-ast => suffixed_bang_one_def.full.result-ast} (94%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_one_def.full.roc => suffixed_bang_one_def.full.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_optional_last.full.formatted.roc => suffixed_bang_optional_last.full.formatted.roc} (100%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_optional_last.full.result-ast => suffixed_bang_optional_last.full.result-ast} (96%) rename crates/compiler/test_syntax/tests/snapshots/pass/{suffixed_optional_last.full.roc => suffixed_bang_optional_last.full.roc} (100%) create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast create mode 100644 crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index bb4affaf720..75cc844a773 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -1,6 +1,6 @@ #![allow(clippy::manual_map)] -use crate::suffixed::{apply_task_await, unwrap_suffixed_expression, EUnwrapped}; +use crate::suffixed::{apply_try_function, unwrap_suffixed_expression, EUnwrapped}; use bumpalo::collections::Vec; use bumpalo::Bump; use roc_error_macros::internal_error; @@ -220,11 +220,20 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) sub_arg, sub_pat, sub_new, + target, }) => desugar_value_def_suffixed( arena, Body( loc_pattern, - apply_task_await(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None), + apply_try_function( + arena, + loc_expr.region, + sub_arg, + sub_pat, + sub_new, + None, + target, + ), ), ), Err(..) => Body( @@ -254,6 +263,7 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) sub_arg, sub_pat, sub_new, + target, }) => desugar_value_def_suffixed( arena, AnnotatedBody { @@ -261,13 +271,14 @@ pub fn desugar_value_def_suffixed<'a>(arena: &'a Bump, value_def: ValueDef<'a>) ann_type, lines_between, body_pattern, - body_expr: apply_task_await( + body_expr: apply_try_function( arena, body_expr.region, sub_arg, sub_pat, sub_new, Some((ann_pattern, ann_type)), + target, ), }, ), @@ -371,14 +382,23 @@ pub fn desugar_expr<'a>( arena.alloc(Loc { region, value }) } - // desugar the sub_expression, but leave the TaskAwaitBang as this will + // desugar the sub_expression, but leave the TrySuffix as this will // be unwrapped later in desugar_value_def_suffixed - TaskAwaitBang(sub_expr) => { + TrySuffix { + expr: sub_expr, + target, + } => { let intermediate = arena.alloc(Loc::at(loc_expr.region, **sub_expr)); let new_sub_loc_expr = desugar_expr(arena, intermediate, src, line_info, module_path); let new_sub_expr = arena.alloc(new_sub_loc_expr.value); - arena.alloc(Loc::at(loc_expr.region, TaskAwaitBang(new_sub_expr))) + arena.alloc(Loc::at( + loc_expr.region, + TrySuffix { + expr: new_sub_expr, + target: *target, + }, + )) } RecordAccess(sub_expr, paths) => { let region = loc_expr.region; diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 86c372d7004..9dd672fdf54 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -1119,7 +1119,7 @@ pub fn canonicalize_expr<'a>( output, ) } - ast::Expr::TaskAwaitBang(..) => internal_error!("a Expr::TaskAwaitBang expression was not completely removed in desugar_value_def_suffixed"), + ast::Expr::TrySuffix { .. } => internal_error!("a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"), ast::Expr::Tag(tag) => { let variant_var = var_store.fresh(); let ext_var = var_store.fresh(); @@ -2515,7 +2515,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool { ast::Expr::TupleAccess(sub_expr, _) | ast::Expr::ParensAround(sub_expr) | ast::Expr::RecordAccess(sub_expr, _) - | ast::Expr::TaskAwaitBang(sub_expr) => is_valid_interpolation(sub_expr), + | ast::Expr::TrySuffix { expr: sub_expr, .. } => is_valid_interpolation(sub_expr), ast::Expr::Apply(loc_expr, args, _called_via) => { is_valid_interpolation(&loc_expr.value) && args diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index e70c3dfc669..251ec683ae9 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, TypeAnnotation, ValueDef, WhenBranch}; +use roc_parse::ast::{is_expr_suffixed, Pattern, TryTarget, TypeAnnotation, ValueDef, WhenBranch}; use roc_region::all::{Loc, Region}; use std::cell::Cell; @@ -34,14 +34,17 @@ pub enum EUnwrapped<'a> { /// e.g. x = first! (second! 42) /// The first unwrap will produce /// `UnwrappedDefExpr` - UnwrappedDefExpr(&'a Loc>), + UnwrappedDefExpr { + loc_expr: &'a Loc>, + target: TryTarget, + }, /// 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: #!0_arg, sub_new: #!0_arg }>` UnwrappedSubExpr { - /// the unwrapped expression argument for Task.await + /// the unwrapped expression argument for `try` functions sub_arg: &'a Loc>, /// the pattern for the closure @@ -49,6 +52,9 @@ pub enum EUnwrapped<'a> { /// the expression to replace the unwrapped sub_new: &'a Loc>, + + /// The type of the target for the suffix, e.g. a Task or Result + target: TryTarget, }, /// Malformed use of the suffix @@ -59,12 +65,13 @@ fn init_unwrapped_err<'a>( arena: &'a Bump, unwrapped_expr: &'a Loc>, maybe_def_pat: Option<&'a Loc>>, + target: TryTarget, ) -> Result<&'a Loc>, EUnwrapped<'a>> { match maybe_def_pat { Some(..) => { // we have a def pattern, so no need to generate a new pattern // as this should only be created in the first call from a def - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) } None => { // Provide an intermediate answer expression and pattern when unwrapping a @@ -87,13 +94,14 @@ fn init_unwrapped_err<'a>( sub_arg: unwrapped_expr, sub_pat, sub_new, + target, }) } } } /// Descend through the AST and unwrap each suffixed expression -/// when an expression is unwrapped, we apply a `Task.await` and +/// when an expression is unwrapped, we apply the appropriate try function and /// then descend through the AST again until there are no more suffixed /// expressions, or we hit an error pub fn unwrap_suffixed_expression<'a>( @@ -103,10 +111,13 @@ pub fn unwrap_suffixed_expression<'a>( ) -> Result<&'a Loc>, EUnwrapped<'a>> { let unwrapped_expression = { match loc_expr.value { - Expr::TaskAwaitBang(sub_expr) => { + Expr::TrySuffix { + expr: sub_expr, + target, + } => { let unwrapped_sub_expr = arena.alloc(Loc::at(loc_expr.region, *sub_expr)); - init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat) + init_unwrapped_err(arena, unwrapped_sub_expr, maybe_def_pat, target) } Expr::Defs(..) => unwrap_suffixed_expression_defs_help(arena, loc_expr, maybe_def_pat), @@ -154,15 +165,16 @@ pub fn unwrap_suffixed_expression<'a>( .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); return Ok(new_expect); } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target, }) => { let new_expect = arena .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); - Err(EUnwrapped::UnwrappedDefExpr(new_expect)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_expect, target }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, sub_pat, sub_new, + target, }) => { let new_expect = arena .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); @@ -170,6 +182,7 @@ pub fn unwrap_suffixed_expression<'a>( sub_arg: new_expect, sub_pat, sub_new, + target, }) } Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), @@ -208,13 +221,14 @@ pub fn unwrap_suffixed_expression_parens_help<'a>( )); Ok(new_parens) } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unreachable, child expressions from ParensAround should generate UnwrappedSubExpr instead"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { let new_parens = arena.alloc(Loc::at( loc_expr.region, @@ -224,6 +238,7 @@ pub fn unwrap_suffixed_expression_parens_help<'a>( sub_arg, sub_pat, sub_new: new_parens, + target, }) } Err(err) => Err(err), @@ -247,13 +262,13 @@ pub fn unwrap_suffixed_expression_closure_help<'a>( let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, unwrapped_expr))); 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, None); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_closure_loc_ret = apply_try_function(arena, loc_expr.region, sub_arg, sub_pat, sub_new, None, target); let new_closure = arena.alloc(Loc::at(loc_expr.region, Expr::Closure(closure_args, new_closure_loc_ret))); Ok(new_closure) } Err(err) => { - debug_assert!(false,"the closure Defs was malformd, got {:#?}", err); + debug_assert!(false,"the closure Defs was malformed, got {:#?}", err); Err(EUnwrapped::Malformed) } } @@ -278,22 +293,22 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( Ok(new_arg) => { *arg = new_arg; } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unreachable, unwrapped arg cannot be def expression as `None` was passed as pattern"); } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_arg, target }) => { *arg = new_arg; let new_apply = arena.alloc(Loc::at(loc_expr.region, Apply(function, local_args, called_via))); - return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply}); + return Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new: new_apply, target }); } Err(err) => return Err(err), } } // special case for when our Apply function is a suffixed Var (but not multiple suffixed) - if let Expr::TaskAwaitBang(sub_expr) = function.value { + if let Expr::TrySuffix { expr: sub_expr, target } = function.value { let unwrapped_function = arena.alloc(Loc::at( loc_expr.region, *sub_expr, @@ -301,7 +316,7 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); - return init_unwrapped_err(arena, new_apply, maybe_def_pat); + return init_unwrapped_err(arena, new_apply, maybe_def_pat, target); } // function is another expression @@ -310,15 +325,15 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(new_function, local_args, called_via))); Ok(new_apply) } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_function)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_function, target }) => { let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(unwrapped_function, local_args, called_via))); - Err(EUnwrapped::UnwrappedDefExpr(new_apply)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target }) } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => { let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via))); - - Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply}) + + Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target }) } Err(err) => Err(err) } @@ -361,21 +376,23 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { - let unwrapped_expression = apply_task_await( + let unwrapped_expression = apply_try_function( arena, sub_arg.region, sub_arg, sub_pat, sub_new, None, + target, ); let mut new_if_thens = Vec::new_in(arena); @@ -422,13 +439,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( return unwrap_suffixed_expression(arena, new_if, maybe_def_pat); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { if before.is_empty() { let mut new_if_thens = Vec::new_in(arena); @@ -445,13 +463,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( ), )); - let unwrapped_if_then = apply_task_await( + let unwrapped_if_then = apply_try_function( arena, sub_arg.region, sub_arg, sub_pat, new_if, None, + target, ); return unwrap_suffixed_expression( @@ -473,13 +492,14 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( ), )); - let after_if_then = apply_task_await( + let after_if_then = apply_try_function( arena, sub_arg.region, sub_arg, sub_pat, after_if, None, + target, ); let before_if_then = arena.alloc(Loc::at( @@ -507,16 +527,17 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( Expr::If(if_thens, unwrapped_final_else), ))); } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!("unexpected, unwrapped if-then-else Def expr should have intermediate answer as `None` was passed as pattern"); } Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, + target, }) => { let unwrapped_final_else = - apply_task_await(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None); + apply_try_function(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None, target); let new_if = arena.alloc(Loc::at( loc_expr.region, @@ -551,7 +572,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, None), + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => apply_try_function(arena, branch_loc_expr.region, sub_arg, sub_pat, sub_new, None, target), Err(..) => return Err(EUnwrapped::Malformed), }; @@ -578,12 +599,12 @@ pub fn unwrap_suffixed_expression_when_help<'a>( let new_when = arena.alloc(Loc::at(loc_expr.region, Expr::When(unwrapped_condition, branches))); Ok(new_when) } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { 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, None); + let applied_task_await = apply_try_function(arena,loc_expr.region,sub_arg,sub_pat,new_when, None, target); Ok(applied_task_await) } - Err(EUnwrapped::UnwrappedDefExpr(..)) + Err(EUnwrapped::UnwrappedDefExpr { .. }) | Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed) } @@ -631,7 +652,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( current_value_def.replace_expr(unwrapped_def); local_defs.replace_with_value_def(tag_index, current_value_def, def_expr.region); } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { let split_defs = local_defs.split_defs_around(tag_index); let before_empty = split_defs.before.is_empty(); let after_empty = split_defs.after.is_empty(); @@ -640,48 +661,60 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( // We pass None as a def pattern here because it's desugaring of the ret expression let next_expr = match unwrap_suffixed_expression(arena,loc_ret, None) { Ok(next_expr) => next_expr, - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { // 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, None) + apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target) } - Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { + 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, ann_type), maybe_def_pat); + return unwrap_suffixed_expression( + arena, + apply_try_function( + arena, + def_expr.region, + unwrapped_expr, + def_pattern, + next_expr, + ann_type, + target, + ), + 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))); 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, None) + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target) } - Err(EUnwrapped::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { + 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,ann_type), maybe_def_pat); + return unwrap_suffixed_expression(arena, apply_try_function(arena,def_expr.region,unwrapped_expr,def_pattern,next_expr, ann_type, target), 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, ann_type); + let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); 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, None); - let applied_task_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_loc_ret = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,sub_new, None, target); + let applied_task_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); 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::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { // TODO confirm this is correct with test case return Err(EUnwrapped::Malformed); } @@ -695,28 +728,28 @@ 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, ann_type); + let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); 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, None); - let applied_await = apply_task_await(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_loc_ret = apply_try_function(arena, def_expr.region, sub_arg, sub_pat, sub_new, None, target); + let applied_await = apply_try_function(arena, loc_expr.region, unwrapped_expr, def_pattern, new_loc_ret, ann_type, target); 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::UnwrappedDefExpr(..)) | Err(EUnwrapped::Malformed) => { + 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); }, }; } } - Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new }) => { + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { 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, ann_type); + let replaced_def = apply_try_function(arena,def_expr.region,sub_arg,sub_pat,new_defs_expr, ann_type, target); return unwrap_suffixed_expression(arena,replaced_def,maybe_def_pat); } Err(err) => return Err(err) @@ -730,12 +763,12 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( 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); + Err(EUnwrapped::UnwrappedSubExpr { sub_arg, sub_pat, sub_new, target }) => { + let new_loc_ret = apply_try_function(arena, loc_expr.region,sub_arg,sub_pat,sub_new, None, target); let new_defs = arena.alloc(Loc::at(loc_expr.region,Defs(arena.alloc(local_defs), new_loc_ret))); unwrap_suffixed_expression(arena, new_defs, None) } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { // TODO confirm this is correct with test case Err(EUnwrapped::Malformed) } @@ -769,6 +802,7 @@ fn unwrap_low_level_dbg<'a>( sub_arg, sub_pat, sub_new, + target, }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, @@ -777,18 +811,19 @@ fn unwrap_low_level_dbg<'a>( unwrap_suffixed_expression( arena, - apply_task_await( + apply_try_function( arena, new_dbg.region, sub_arg, sub_pat, new_dbg, None, + target, ), maybe_def_pat, ) } - Err(EUnwrapped::UnwrappedDefExpr(..)) => { + Err(EUnwrapped::UnwrappedDefExpr { .. }) => { internal_error!( "unreachable, arg of LowLevelDbg should generate UnwrappedSubExpr instead" ); @@ -805,17 +840,18 @@ fn unwrap_low_level_dbg<'a>( )); Ok(&*new_dbg) } - Err(EUnwrapped::UnwrappedDefExpr(unwrapped_expr)) => { + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, LowLevelDbg(dbg_src, arg, unwrapped_expr), )); - Err(EUnwrapped::UnwrappedDefExpr(new_dbg)) + Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_dbg, target }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, sub_pat, sub_new, + target, }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, @@ -825,6 +861,7 @@ fn unwrap_low_level_dbg<'a>( sub_arg: new_dbg, sub_pat, sub_new, + target, }) } Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed), @@ -836,25 +873,26 @@ fn unwrap_low_level_dbg<'a>( } } -/// Helper for `Task.await loc_expr \loc_pat -> loc_cont` -pub fn apply_task_await<'a>( +/// Helper for try_function loc_expr \loc_pat -> loc_cont` +pub fn apply_try_function<'a>( arena: &'a Bump, region: Region, loc_expr: &'a Loc>, loc_pat: &'a Loc>, loc_cont: &'a Loc>, maybe_loc_ann: Option<(&'a Loc, &'a Loc>)>, + target: TryTarget, ) -> &'a Loc> { - let task_await_first_arg = match maybe_loc_ann { + let try_function_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 + // try_function // ( - // #!0_expr : Task loc_type _ + // #!0_expr : Target loc_type _ // #!0_expr = loc_expr // #!0_expr // ) @@ -875,8 +913,12 @@ pub fn apply_task_await<'a>( let new_ident = arena.alloc(new_ident); // #!0_expr (pattern) - // #!0_expr : Task loc_type _ + // #!0_expr : Target loc_type _ // #!0_expr = loc_expr + let target_type_name = match target { + TryTarget::Task => "Task", + TryTarget::Result => "Result", + }; let value_def = ValueDef::AnnotatedBody { ann_pattern: arena.alloc(Loc::at( loc_ann_pat.region, @@ -893,7 +935,7 @@ pub fn apply_task_await<'a>( loc_type.region, TypeAnnotation::Apply( arena.alloc(""), - arena.alloc("Task"), + arena.alloc(target_type_name), arena.alloc([ *loc_type, Loc::at(loc_type.region, TypeAnnotation::Inferred), @@ -918,7 +960,7 @@ pub fn apply_task_await<'a>( )); // ( - // #!0_expr : Task loc_type _ + // #!0_expr : Target loc_type _ // #!0_expr = loc_expr // #!0_expr // ) @@ -935,7 +977,7 @@ pub fn apply_task_await<'a>( // loc_cont // desugar to - // Task.await loc_expr \loc_pat -> loc_cont + // try_function loc_expr \loc_pat -> loc_cont loc_expr } }; @@ -945,25 +987,29 @@ pub fn apply_task_await<'a>( // \x -> x! // \x -> x if is_matching_intermediate_answer(loc_pat, loc_cont) { - return task_await_first_arg; + return try_function_first_arg; } // \loc_pat -> loc_cont let closure = arena.alloc(Loc::at(region, Closure(arena.alloc([*loc_pat]), loc_cont))); - // Task.await task_first_arg closure + // try_function first_arg closure + let (try_function_module, try_function_ident, called_via) = match target { + TryTarget::Task => (ModuleName::TASK, "await", CalledVia::BangSuffix), + TryTarget::Result => (ModuleName::RESULT, "try", CalledVia::QuestionSuffix), + }; arena.alloc(Loc::at( region, Apply( arena.alloc(Loc { region, value: Var { - module_name: ModuleName::TASK, - ident: "await", + module_name: try_function_module, + ident: try_function_ident, }, }), - arena.alloc([task_await_first_arg, closure]), - CalledVia::BangSuffix, + arena.alloc([try_function_first_arg, closure]), + called_via, ), )) } diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index 04544ea2c6f..f61b19de4f0 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -9,8 +9,7 @@ use crate::spaces::{ use crate::Buf; use roc_module::called_via::{self, BinOp}; use roc_parse::ast::{ - is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, - OldRecordBuilderField, Pattern, WhenBranch, + is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, OldRecordBuilderField, Pattern, TryTarget, WhenBranch }; use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::ident::Accessor; @@ -47,7 +46,7 @@ impl<'a> Formattable for Expr<'a> { | OpaqueRef(_) | Crash => false, - RecordAccess(inner, _) | TupleAccess(inner, _) | TaskAwaitBang(inner) => { + RecordAccess(inner, _) | TupleAccess(inner, _) | TrySuffix { expr: inner, .. } => { inner.is_multiline() } @@ -520,9 +519,12 @@ impl<'a> Formattable for Expr<'a> { buf.push('.'); buf.push_str(key); } - TaskAwaitBang(expr) => { + TrySuffix { expr, target } => { expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent); - buf.push('!'); + match target { + TryTarget::Task => buf.push('!'), + TryTarget::Result => buf.push('?'), + } } MalformedIdent(str, _) => { buf.indent(indent); diff --git a/crates/compiler/module/src/called_via.rs b/crates/compiler/module/src/called_via.rs index 43780ba9e15..cfac8d5f2a3 100644 --- a/crates/compiler/module/src/called_via.rs +++ b/crates/compiler/module/src/called_via.rs @@ -95,6 +95,10 @@ pub enum CalledVia { /// This call is the result of desugaring a Task.await from `!` syntax /// e.g. Stdout.line! "Hello" becomes Task.await (Stdout.line "Hello") \{} -> ... BangSuffix, + + /// This call is the result of desugaring a Result.try from `?` syntax + /// e.g. Dict.get? items "key" becomes Result.try (Dict.get items "key") \item -> ... + QuestionSuffix, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 445b7c5254b..a4a29b1282d 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -392,6 +392,15 @@ pub enum StrLiteral<'a> { Block(&'a [&'a [StrSegment<'a>]]), } +/// Values that can be tried, extracting success values or "returning early" on failure +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TryTarget { + /// Tasks suffixed with ! are `Task.await`ed + Task, + /// Results suffixed with ? are `Result.try`ed + Result, +} + /// A parsed expression. This uses lifetimes extensively for two reasons: /// /// 1. It uses Bump::alloc for all allocations, which returns a reference. @@ -426,8 +435,11 @@ pub enum Expr<'a> { /// Look up exactly one field on a tuple, e.g. `(x, y).1`. TupleAccess(&'a Expr<'a>, &'a str), - /// Task await bang - i.e. the ! in `File.readUtf8! path` - TaskAwaitBang(&'a Expr<'a>), + /// Early return on failures - e.g. the ! in `File.readUtf8! path` + TrySuffix { + target: TryTarget, + expr: &'a Expr<'a>, + }, // Collection Literals List(Collection<'a, &'a Loc>>), @@ -555,9 +567,9 @@ pub fn split_loc_exprs_around<'a>( /// Checks if the bang suffix is applied only at the top level of expression pub fn is_top_level_suffixed(expr: &Expr) -> bool { - // TODO: should we check BinOps with pizza where the last expression is TaskAwaitBang? + // TODO: should we check BinOps with pizza where the last expression is TrySuffix? match expr { - Expr::TaskAwaitBang(..) => true, + Expr::TrySuffix { .. } => true, Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value), Expr::SpaceBefore(a, _) => is_top_level_suffixed(a), Expr::SpaceAfter(a, _) => is_top_level_suffixed(a), @@ -571,7 +583,7 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool { // expression without arguments, `read!` Expr::Var { .. } => false, - Expr::TaskAwaitBang(..) => true, + Expr::TrySuffix { .. } => true, // expression with arguments, `line! "Foo"` Expr::Apply(sub_loc_expr, apply_args, _) => { @@ -995,7 +1007,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> { } RecordAccess(expr, _) | TupleAccess(expr, _) - | TaskAwaitBang(expr) + | TrySuffix { expr, .. } | SpaceBefore(expr, _) | SpaceAfter(expr, _) | ParensAround(expr) => expr_stack.push(expr), @@ -2486,7 +2498,7 @@ impl<'a> Malformed for Expr<'a> { RecordAccess(inner, _) | TupleAccess(inner, _) | - TaskAwaitBang(inner) => inner.is_malformed(), + TrySuffix { expr: inner, .. } => inner.is_malformed(), List(items) => items.is_malformed(), diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index d19fc740ec7..0603a5d88b5 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,9 +1,5 @@ use crate::ast::{ - 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, + 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, TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef }; use crate::blankspace::{ loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, @@ -169,7 +165,8 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), - map(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang), + map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Task)), + map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Result)), )) } @@ -2177,7 +2174,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result return Err(()), @@ -3749,7 +3746,10 @@ fn apply_expr_access_chain<'a>( Suffix::Accessor(Accessor::TupleIndex(field)) => { Expr::TupleAccess(arena.alloc(value), field) } - Suffix::TaskAwaitBang => Expr::TaskAwaitBang(arena.alloc(value)), + Suffix::TrySuffix(target) => Expr::TrySuffix { + target, + expr: arena.alloc(value), + }, }) } diff --git a/crates/compiler/parse/src/ident.rs b/crates/compiler/parse/src/ident.rs index 8b28bb2ffa0..58a1bb3d39a 100644 --- a/crates/compiler/parse/src/ident.rs +++ b/crates/compiler/parse/src/ident.rs @@ -1,3 +1,4 @@ +use crate::ast::TryTarget; use crate::parser::Progress::{self, *}; use crate::parser::{BadInputError, EExpr, ParseResult, Parser}; use crate::state::State; @@ -377,7 +378,7 @@ impl<'a> Accessor<'a> { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Suffix<'a> { Accessor(Accessor<'a>), - TaskAwaitBang, + TrySuffix(TryTarget), } /// a `.foo` or `.1` accessor function diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 3d69dc36571..709a9e24290 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -722,7 +722,7 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.normalize(arena)), b), Expr::AccessorFunction(a) => Expr::AccessorFunction(a), Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), - Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.normalize(arena))), + Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { expr: arena.alloc(a.normalize(arena)), target }, Expr::List(a) => Expr::List(a.normalize(arena)), Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { update: arena.alloc(update.normalize(arena)), 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 index 5b346e9c0cc..aa154180b73 100644 --- 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 @@ -62,12 +62,13 @@ Defs { ), Stmt( @97-111 Apply( - @97-108 TaskAwaitBang( - Var { + @97-108 TrySuffix { + target: Task, + expr: Var { module_name: "Stdout", ident: "line", }, - ), + }, [ @110-111 Var { module_name: "", @@ -80,12 +81,13 @@ Defs { ], }, @141-150 SpaceBefore( - TaskAwaitBang( - Var { + TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "printBar", }, - ), + }, [ Newline, Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast index 45b2889f076..29e7d578060 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/pizza_bang.moduledefs.result-ast @@ -26,12 +26,13 @@ Defs { ( @11-23 SpaceAfter( Apply( - @11-19 TaskAwaitBang( - Var { + @11-19 TrySuffix { + target: Task, + expr: Var { module_name: "Arg", ident: "list", }, - ), + }, [ @21-23 Record( [], @@ -88,12 +89,13 @@ Defs { ), ( @83-98 SpaceAfter( - TaskAwaitBang( - Var { + TrySuffix { + target: Task, + expr: Var { module_name: "Task", ident: "fromResult", }, - ), + }, [ Newline, ], @@ -152,12 +154,13 @@ Defs { @174-176 Pizza, ), ], - @177-188 TaskAwaitBang( - Var { + @177-188 TrySuffix { + target: Task, + expr: Var { module_name: "Stdout", ident: "line", }, - ), + }, ), [ Newline, diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast deleted file mode 100644 index 1c715e9505e..00000000000 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.result-ast +++ /dev/null @@ -1,10 +0,0 @@ -TaskAwaitBang( - TaskAwaitBang( - TaskAwaitBang( - Var { - module_name: "Stdout", - ident: "line", - }, - ), - ), -) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast new file mode 100644 index 00000000000..f75bc40a558 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.result-ast @@ -0,0 +1,13 @@ +TrySuffix { + target: Task, + expr: TrySuffix { + target: Task, + expr: TrySuffix { + target: Task, + expr: Var { + module_name: "Stdout", + ident: "line", + }, + }, + }, +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang.expr.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.formatted.roc 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_bang_multiple_defs.moduledefs.result-ast similarity index 86% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast index aa86b9b304a..5d986547d54 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.result-ast @@ -44,12 +44,13 @@ Defs { value_defs: [ Stmt( @12-21 Apply( - @12-13 TaskAwaitBang( - Var { + @12-13 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "a", }, - ), + }, [ @16-21 Str( PlainLine( @@ -65,12 +66,13 @@ Defs { ident: "x", }, @29-39 Apply( - @29-32 TaskAwaitBang( - Var { + @29-32 TrySuffix { + target: Task, + expr: Var { module_name: "B", ident: "b", }, - ), + }, [ @34-39 Str( PlainLine( @@ -85,12 +87,13 @@ Defs { }, @45-49 SpaceBefore( Apply( - @45-46 TaskAwaitBang( - Var { + @45-46 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "c", }, - ), + }, [ @48-49 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_multiple_defs.moduledefs.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.formatted.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast similarity index 81% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast index 368792b05ca..e62e74bd7fe 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.result-ast @@ -1,19 +1,21 @@ Apply( - @0-3 TaskAwaitBang( - Var { + @0-3 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "foo", }, - ), + }, [ @9-17 ParensAround( Apply( - @9-12 TaskAwaitBang( - Var { + @9-12 TrySuffix { + target: Task, + expr: Var { module_name: "", ident: "bar", }, - ), + }, [ @14-17 Var { module_name: "", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_nested.expr.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_nested.expr.roc 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_bang_one_def.full.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.formatted.roc 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_bang_one_def.full.result-ast similarity index 94% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast index b124b30b99f..d74a5bb54f8 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.result-ast @@ -123,12 +123,13 @@ Full( @126-128 Pizza, ), ], - @129-132 TaskAwaitBang( - Var { + @129-132 TrySuffix { + target: Task, + expr: Var { module_name: "A", ident: "x", }, - ), + }, ), ), Stmt( @@ -144,12 +145,13 @@ Full( ), ], @171-205 Apply( - @171-174 TaskAwaitBang( - Var { + @171-174 TrySuffix { + target: Task, + expr: Var { module_name: "B", ident: "y", }, - ), + }, [ @185-205 SpaceBefore( Record( diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_one_def.full.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_one_def.full.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.formatted.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.formatted.roc 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_bang_optional_last.full.result-ast similarity index 96% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast index 78f12e6ffd3..31cf2f05ec5 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.result-ast @@ -108,12 +108,13 @@ Full( ), ], @167-202 Apply( - @167-178 TaskAwaitBang( - Var { + @167-178 TrySuffix { + target: Task, + expr: Var { module_name: "Task", ident: "mapErr", }, - ), + }, [ @180-202 Tag( "UnableToCheckJQVersion", diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc similarity index 100% rename from crates/compiler/test_syntax/tests/snapshots/pass/suffixed_optional_last.full.roc rename to crates/compiler/test_syntax/tests/snapshots/pass/suffixed_bang_optional_last.full.roc diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast new file mode 100644 index 00000000000..fd56db822d4 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.result-ast @@ -0,0 +1,13 @@ +TrySuffix { + target: Result, + expr: TrySuffix { + target: Result, + expr: TrySuffix { + target: Result, + expr: Var { + module_name: "Stdout", + ident: "line", + }, + }, + }, +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc new file mode 100644 index 00000000000..4382b519a6f --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question.expr.roc @@ -0,0 +1 @@ +Stdout.line??? \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc new file mode 100644 index 00000000000..62c98cbff83 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.formatted.roc @@ -0,0 +1,5 @@ +main = + a? "Bar" + x = B.b? "Foo" + + c? x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast new file mode 100644 index 00000000000..8e5f0739009 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.result-ast @@ -0,0 +1,119 @@ +Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @0-49, + ], + space_before: [ + Slice(start = 0, length = 0), + ], + space_after: [ + Slice(start = 0, length = 1), + ], + spaces: [ + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @0-4 Identifier { + ident: "main", + }, + @12-49 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @12-21, + @26-39, + ], + 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: [ + Stmt( + @12-21 Apply( + @12-13 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "a", + }, + }, + [ + @16-21 Str( + PlainLine( + "Bar", + ), + ), + ], + Space, + ), + ), + Body( + @26-27 Identifier { + ident: "x", + }, + @29-39 Apply( + @29-32 TrySuffix { + target: Result, + expr: Var { + module_name: "B", + ident: "b", + }, + }, + [ + @34-39 Str( + PlainLine( + "Foo", + ), + ), + ], + Space, + ), + ), + ], + }, + @45-49 SpaceBefore( + Apply( + @45-46 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "c", + }, + }, + [ + @48-49 Var { + module_name: "", + ident: "x", + }, + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + ], + ), + ), + ], +} diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc new file mode 100644 index 00000000000..15400112ae7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_multiple_defs.moduledefs.roc @@ -0,0 +1,5 @@ +main = + a? "Bar" + x= B.b? "Foo" + + c? x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc new file mode 100644 index 00000000000..cdf5e420252 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.formatted.roc @@ -0,0 +1 @@ +foo? (bar? baz) (blah stuff) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast new file mode 100644 index 00000000000..a6eaa3d4d30 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.result-ast @@ -0,0 +1,45 @@ +Apply( + @0-3 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "foo", + }, + }, + [ + @9-17 ParensAround( + Apply( + @9-12 TrySuffix { + target: Result, + expr: Var { + module_name: "", + ident: "bar", + }, + }, + [ + @14-17 Var { + module_name: "", + ident: "baz", + }, + ], + Space, + ), + ), + @22-32 ParensAround( + Apply( + @22-26 Var { + module_name: "", + ident: "blah", + }, + [ + @27-32 Var { + module_name: "", + ident: "stuff", + }, + ], + Space, + ), + ), + ], + Space, +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc new file mode 100644 index 00000000000..d25e7f53910 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_nested.expr.roc @@ -0,0 +1 @@ +foo? ( bar? baz) ( blah stuff) \ No newline at end of file diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc new file mode 100644 index 00000000000..e0bc1818e9b --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.formatted.roc @@ -0,0 +1,17 @@ +app [main] { + cli: "../basic-cli/platform/main.roc", +} + +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_question_one_def.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast new file mode 100644 index 00000000000..da9d4a5b6a7 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.result-ast @@ -0,0 +1,213 @@ +Full( + FullAst { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [], + provides: [ + @5-9 ExposedName( + "main", + ), + ], + before_packages: [], + packages: @11-55 Collection { + items: [ + @15-52 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: None, + package_name: @20-52 PackageName( + "../basic-cli/platform/main.roc", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + old_imports: None, + old_provides_to_new_package: None, + }, + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @57-74, + @76-220, + ], + space_before: [ + Slice(start = 0, length = 2), + Slice(start = 2, length = 2), + ], + space_after: [ + Slice(start = 2, length = 0), + Slice(start = 4, length = 2), + ], + spaces: [ + Newline, + Newline, + Newline, + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + ModuleImport( + ModuleImport { + before_name: [], + name: @64-74 ImportedModuleName { + package: Some( + "cli", + ), + name: ModuleName( + "Stdout", + ), + }, + params: None, + alias: None, + exposed: None, + }, + ), + Body( + @76-80 Identifier { + ident: "main", + }, + @120-220 SpaceBefore( + Defs( + Defs { + tags: [ + Index(2147483648), + Index(2147483649), + ], + regions: [ + @120-133, + @162-205, + ], + space_before: [ + Slice(start = 0, length = 0), + Slice(start = 0, length = 3), + ], + space_after: [ + Slice(start = 0, length = 0), + Slice(start = 3, length = 0), + ], + spaces: [ + Newline, + Newline, + LineComment( + " what about this?", + ), + ], + type_defs: [], + value_defs: [ + Stmt( + @120-133 BinOps( + [ + ( + @120-125 Str( + PlainLine( + "Foo", + ), + ), + @126-128 Pizza, + ), + ], + @129-132 TrySuffix { + target: Result, + expr: Var { + module_name: "A", + ident: "x", + }, + }, + ), + ), + Stmt( + @162-205 BinOps( + [ + ( + @162-167 Str( + PlainLine( + "Bar", + ), + ), + @168-170 Pizza, + ), + ], + @171-205 Apply( + @171-174 TrySuffix { + target: Result, + expr: Var { + module_name: "B", + ident: "y", + }, + }, + [ + @185-205 SpaceBefore( + Record( + [ + @187-203 RequiredValue( + @187-193 "config", + [], + @195-203 Str( + PlainLine( + "config", + ), + ), + ), + ], + ), + [ + Newline, + ], + ), + ], + Space, + ), + ), + ), + ], + }, + @211-220 SpaceBefore( + Apply( + @211-214 Var { + module_name: "C", + ident: "z", + }, + [ + @215-220 Str( + PlainLine( + "Bar", + ), + ), + ], + Space, + ), + [ + Newline, + Newline, + ], + ), + ), + [ + Newline, + LineComment( + " is this a valid statement?", + ), + ], + ), + ), + ], + }, + }, +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc new file mode 100644 index 00000000000..177c84c4361 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_one_def.full.roc @@ -0,0 +1,16 @@ +app [main] { + cli: "../basic-cli/platform/main.roc", +} + +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_question_optional_last.full.formatted.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc new file mode 100644 index 00000000000..65fc305940a --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.formatted.roc @@ -0,0 +1,9 @@ +app [main] { + cli: platform "", +} + +main = + "jq --version" + |> Cmd.new + |> Cmd.status + |> Result.mapErr? UnableToCheckJQVersion diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast new file mode 100644 index 00000000000..9bef33589c9 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.result-ast @@ -0,0 +1,134 @@ +Full( + FullAst { + header: SpacesBefore { + before: [], + item: App( + AppHeader { + before_provides: [ + Newline, + ], + provides: [ + @74-78 ExposedName( + "main", + ), + ], + before_packages: [ + Newline, + ], + packages: @6-44 Collection { + items: [ + @30-37 SpaceBefore( + PackageEntry { + shorthand: "cli", + spaces_after_shorthand: [], + platform_marker: Some( + [], + ), + package_name: @35-37 PackageName( + "", + ), + }, + [ + Newline, + ], + ), + ], + final_comments: [ + Newline, + ], + }, + old_imports: None, + old_provides_to_new_package: None, + }, + ), + }, + defs: Defs { + tags: [ + Index(2147483648), + ], + regions: [ + @88-204, + ], + space_before: [ + Slice(start = 0, length = 2), + ], + space_after: [ + Slice(start = 2, length = 1), + ], + spaces: [ + Newline, + Newline, + Newline, + ], + type_defs: [], + value_defs: [ + Body( + @88-92 Identifier { + ident: "main", + }, + @100-204 SpaceBefore( + BinOps( + [ + ( + @100-114 SpaceAfter( + Str( + PlainLine( + "jq --version", + ), + ), + [ + Newline, + ], + ), + @123-125 Pizza, + ), + ( + @126-133 SpaceAfter( + Var { + module_name: "Cmd", + ident: "new", + }, + [ + Newline, + ], + ), + @142-144 Pizza, + ), + ( + @145-155 SpaceAfter( + Var { + module_name: "Cmd", + ident: "status", + }, + [ + Newline, + ], + ), + @164-166 Pizza, + ), + ], + @167-204 Apply( + @167-180 TrySuffix { + target: Result, + expr: Var { + module_name: "Result", + ident: "mapErr", + }, + }, + [ + @182-204 Tag( + "UnableToCheckJQVersion", + ), + ], + Space, + ), + ), + [ + Newline, + ], + ), + ), + ], + }, + }, +) diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc new file mode 100644 index 00000000000..a2b949f35e0 --- /dev/null +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_question_optional_last.full.roc @@ -0,0 +1,12 @@ +app "" + packages { + cli: "", + } + imports [] + provides [main] to cli + +main = + "jq --version" + |> Cmd.new + |> Cmd.status + |> Result.mapErr? UnableToCheckJQVersion diff --git a/crates/compiler/test_syntax/tests/test_snapshots.rs b/crates/compiler/test_syntax/tests/test_snapshots.rs index 44fb528d4cb..0ad5e2bb65f 100644 --- a/crates/compiler/test_syntax/tests/test_snapshots.rs +++ b/crates/compiler/test_syntax/tests/test_snapshots.rs @@ -469,11 +469,16 @@ mod test_snapshots { pass/string_without_escape.expr, pass/sub_var_with_spaces.expr, pass/sub_with_spaces.expr, - pass/suffixed.expr, - pass/suffixed_multiple_defs.moduledefs, - pass/suffixed_nested.expr, - pass/suffixed_one_def.full, - pass/suffixed_optional_last.full, + pass/suffixed_bang.expr, + pass/suffixed_bang_multiple_defs.moduledefs, + pass/suffixed_bang_nested.expr, + pass/suffixed_bang_one_def.full, + pass/suffixed_bang_optional_last.full, + pass/suffixed_question.expr, + pass/suffixed_question_multiple_defs.moduledefs, + pass/suffixed_question_nested.expr, + pass/suffixed_question_one_def.full, + pass/suffixed_question_optional_last.full, pass/tag_pattern.expr, pass/ten_times_eleven.expr, pass/three_arg_closure.expr, diff --git a/crates/language_server/src/analysis/tokens.rs b/crates/language_server/src/analysis/tokens.rs index 394b028bd91..18514583258 100644 --- a/crates/language_server/src/analysis/tokens.rs +++ b/crates/language_server/src/analysis/tokens.rs @@ -667,7 +667,7 @@ impl IterTokens for Loc> { Expr::RecordAccess(rcd, _field) => Loc::at(region, *rcd).iter_tokens(arena), Expr::AccessorFunction(accessor) => Loc::at(region, accessor).iter_tokens(arena), Expr::TupleAccess(tup, _field) => Loc::at(region, *tup).iter_tokens(arena), - Expr::TaskAwaitBang(inner) => Loc::at(region, *inner).iter_tokens(arena), + Expr::TrySuffix { expr: inner, .. } => Loc::at(region, *inner).iter_tokens(arena), Expr::List(lst) => lst.iter_tokens(arena), Expr::RecordUpdate { update, fields } => (update.iter_tokens(arena).into_iter()) .chain(fields.iter().flat_map(|f| f.iter_tokens(arena))) From 4870f56a1823b7fac0764134ba4579216b71d24a Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 22:21:53 -0700 Subject: [PATCH 2/6] Update args.roc test to use ? sugar --- crates/cli/tests/cli/parse-args.roc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/crates/cli/tests/cli/parse-args.roc b/crates/cli/tests/cli/parse-args.roc index 556367b1e17..c9bb274f99c 100644 --- a/crates/cli/tests/cli/parse-args.roc +++ b/crates/cli/tests/cli/parse-args.roc @@ -58,8 +58,7 @@ cliMap : ArgParser a, (a -> b) -> ArgParser b cliMap = \{ params, parser }, mapper -> { params, parser: \args -> - (data, afterData) <- parser args - |> Result.try + (data, afterData) = parser? args Ok (mapper data, afterData), } @@ -68,10 +67,8 @@ cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c cliBuild = \firstWeaver, secondWeaver, combine -> allParams = List.concat firstWeaver.params secondWeaver.params combinedParser = \args -> - (firstValue, afterFirst) <- firstWeaver.parser args - |> Result.try - (secondValue, afterSecond) <- secondWeaver.parser afterFirst - |> Result.try + (firstValue, afterFirst) = firstWeaver.parser? args + (secondValue, afterSecond) = secondWeaver.parser? afterFirst Ok (combine firstValue secondValue, afterSecond) From 0fd0cc11aa35a3928bdbe73c7485466241e81cf4 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 22:26:32 -0700 Subject: [PATCH 3/6] Format code --- crates/compiler/can/src/expr.rs | 13 ++++---- crates/compiler/can/src/suffixed.rs | 41 +++++++++++++++++++------- crates/compiler/fmt/src/expr.rs | 3 +- crates/compiler/parse/src/expr.rs | 14 +++++++-- crates/compiler/parse/src/normalize.rs | 5 +++- 5 files changed, 56 insertions(+), 20 deletions(-) diff --git a/crates/compiler/can/src/expr.rs b/crates/compiler/can/src/expr.rs index 9dd672fdf54..cceb62d910f 100644 --- a/crates/compiler/can/src/expr.rs +++ b/crates/compiler/can/src/expr.rs @@ -648,9 +648,7 @@ pub fn canonicalize_expr<'a>( (answer, Output::default()) } - ast::Expr::Record(fields) => { - canonicalize_record(env, var_store, scope, region, *fields) - } + ast::Expr::Record(fields) => canonicalize_record(env, var_store, scope, region, *fields), ast::Expr::RecordUpdate { fields, update: loc_update, @@ -1119,7 +1117,9 @@ pub fn canonicalize_expr<'a>( output, ) } - ast::Expr::TrySuffix { .. } => internal_error!("a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed"), + ast::Expr::TrySuffix { .. } => internal_error!( + "a Expr::TrySuffix expression was not completely removed in desugar_value_def_suffixed" + ), ast::Expr::Tag(tag) => { let variant_var = var_store.fresh(); let ext_var = var_store.fresh(); @@ -1371,7 +1371,10 @@ pub fn canonicalize_expr<'a>( use roc_problem::can::RuntimeError::*; let sub_region = Region::span_across(&loc_name.region, &loc_value.region); - let problem = OptionalFieldInRecordBuilder {record: region, field: sub_region }; + let problem = OptionalFieldInRecordBuilder { + record: region, + field: sub_region, + }; env.problem(Problem::RuntimeError(problem.clone())); (RuntimeError(problem), Output::default()) diff --git a/crates/compiler/can/src/suffixed.rs b/crates/compiler/can/src/suffixed.rs index 251ec683ae9..81a717fcb65 100644 --- a/crates/compiler/can/src/suffixed.rs +++ b/crates/compiler/can/src/suffixed.rs @@ -71,7 +71,10 @@ fn init_unwrapped_err<'a>( Some(..) => { // we have a def pattern, so no need to generate a new pattern // as this should only be created in the first call from a def - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: unwrapped_expr, + target, + }) } None => { // Provide an intermediate answer expression and pattern when unwrapping a @@ -165,10 +168,16 @@ pub fn unwrap_suffixed_expression<'a>( .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); return Ok(new_expect); } - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target, }) => { + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: unwrapped_expr, + target, + }) => { let new_expect = arena .alloc(Loc::at(loc_expr.region, Expect(condition, unwrapped_expr))); - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_expect, target }) + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: new_expect, + target, + }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, @@ -330,9 +339,8 @@ pub fn unwrap_suffixed_expression_apply_help<'a>( Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_apply, target }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new, target }) => { - let new_apply = arena.alloc(Loc::at(loc_expr.region, Expr::Apply(sub_new, local_args, called_via))); - + Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_function, sub_pat, sub_new:new_apply, target }) } Err(err) => Err(err) @@ -536,8 +544,15 @@ pub fn unwrap_suffixed_expression_if_then_else_help<'a>( sub_new, target, }) => { - let unwrapped_final_else = - apply_try_function(arena, sub_arg.region, sub_arg, sub_pat, sub_new, None, target); + let unwrapped_final_else = apply_try_function( + arena, + sub_arg.region, + sub_arg, + sub_pat, + sub_new, + None, + target, + ); let new_if = arena.alloc(Loc::at( loc_expr.region, @@ -673,7 +688,7 @@ pub fn unwrap_suffixed_expression_defs_help<'a>( return unwrap_suffixed_expression( arena, apply_try_function( - arena, + arena, def_expr.region, unwrapped_expr, def_pattern, @@ -840,12 +855,18 @@ fn unwrap_low_level_dbg<'a>( )); Ok(&*new_dbg) } - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: unwrapped_expr, target }) => { + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: unwrapped_expr, + target, + }) => { let new_dbg = arena.alloc(Loc::at( loc_expr.region, LowLevelDbg(dbg_src, arg, unwrapped_expr), )); - Err(EUnwrapped::UnwrappedDefExpr { loc_expr: new_dbg, target }) + Err(EUnwrapped::UnwrappedDefExpr { + loc_expr: new_dbg, + target, + }) } Err(EUnwrapped::UnwrappedSubExpr { sub_arg: unwrapped_expr, diff --git a/crates/compiler/fmt/src/expr.rs b/crates/compiler/fmt/src/expr.rs index f61b19de4f0..645b258a617 100644 --- a/crates/compiler/fmt/src/expr.rs +++ b/crates/compiler/fmt/src/expr.rs @@ -9,7 +9,8 @@ use crate::spaces::{ use crate::Buf; use roc_module::called_via::{self, BinOp}; use roc_parse::ast::{ - is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, OldRecordBuilderField, Pattern, TryTarget, WhenBranch + is_expr_suffixed, AssignedField, Base, Collection, CommentOrNewline, Expr, ExtractSpaces, + OldRecordBuilderField, Pattern, TryTarget, WhenBranch, }; use roc_parse::ast::{StrLiteral, StrSegment}; use roc_parse::ident::Accessor; diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 0603a5d88b5..26b8c85dbdc 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -1,5 +1,9 @@ use crate::ast::{ - 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, TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef + 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, + TryTarget, TypeAnnotation, TypeDef, TypeHeader, ValueDef, }; use crate::blankspace::{ loc_space0_e, require_newline_or_eof, space0_after_e, space0_around_ee, space0_before_e, @@ -165,8 +169,12 @@ fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr ) ) ), - map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Task)), - map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix(TryTarget::Result)), + map(byte(b'!', EExpr::Access), |_| Suffix::TrySuffix( + TryTarget::Task + )), + map(byte(b'?', EExpr::Access), |_| Suffix::TrySuffix( + TryTarget::Result + )), )) } diff --git a/crates/compiler/parse/src/normalize.rs b/crates/compiler/parse/src/normalize.rs index 709a9e24290..4f37a1e18ee 100644 --- a/crates/compiler/parse/src/normalize.rs +++ b/crates/compiler/parse/src/normalize.rs @@ -722,7 +722,10 @@ impl<'a> Normalize<'a> for Expr<'a> { Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.normalize(arena)), b), Expr::AccessorFunction(a) => Expr::AccessorFunction(a), Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.normalize(arena)), b), - Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { expr: arena.alloc(a.normalize(arena)), target }, + Expr::TrySuffix { expr: a, target } => Expr::TrySuffix { + expr: arena.alloc(a.normalize(arena)), + target, + }, Expr::List(a) => Expr::List(a.normalize(arena)), Expr::RecordUpdate { update, fields } => Expr::RecordUpdate { update: arena.alloc(update.normalize(arena)), From 0733863a81fcf399a2fbe27d15c73183594de455 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 23:23:45 -0700 Subject: [PATCH 4/6] Prefer false-interpreter for ? desugaring example --- crates/cli/tests/cli/parse-args.roc | 11 ++++-- examples/cli/false-interpreter/False.roc | 48 ++++++++++++------------ 2 files changed, 31 insertions(+), 28 deletions(-) diff --git a/crates/cli/tests/cli/parse-args.roc b/crates/cli/tests/cli/parse-args.roc index c9bb274f99c..145a2e36e47 100644 --- a/crates/cli/tests/cli/parse-args.roc +++ b/crates/cli/tests/cli/parse-args.roc @@ -12,7 +12,7 @@ main = file, count: numParam { name: "count" }, doubled: numParam { name: "doubled" } - |> cliMap \d -> d * 2, + |> cliMap \d -> d * 2, } args = ["parse-args", "file.txt", "5", "7"] @@ -58,7 +58,8 @@ cliMap : ArgParser a, (a -> b) -> ArgParser b cliMap = \{ params, parser }, mapper -> { params, parser: \args -> - (data, afterData) = parser? args + (data, afterData) <- parser args + |> Result.try Ok (mapper data, afterData), } @@ -67,8 +68,10 @@ cliBuild : ArgParser a, ArgParser b, (a, b -> c) -> ArgParser c cliBuild = \firstWeaver, secondWeaver, combine -> allParams = List.concat firstWeaver.params secondWeaver.params combinedParser = \args -> - (firstValue, afterFirst) = firstWeaver.parser? args - (secondValue, afterSecond) = secondWeaver.parser? afterFirst + (firstValue, afterFirst) <- firstWeaver.parser args + |> Result.try + (secondValue, afterSecond) <- secondWeaver.parser afterFirst + |> Result.try Ok (combine firstValue secondValue, afterSecond) diff --git a/examples/cli/false-interpreter/False.roc b/examples/cli/false-interpreter/False.roc index 4c59516c1db..7300abff728 100644 --- a/examples/cli/false-interpreter/False.roc +++ b/examples/cli/false-interpreter/False.roc @@ -242,13 +242,13 @@ interpretCtxLoop = \ctx -> when result is Ok (T 0xB8 newCtx) -> result2 = - (T popCtx index) <- Result.try (popNumber newCtx) + (T popCtx index) = popNumber? newCtx # I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers. size = List.len popCtx.stack - 1 offset = Num.intCast size - index if offset >= 0 then - stackVal <- Result.try (List.get popCtx.stack (Num.intCast offset)) + stackVal = List.get? popCtx.stack (Num.intCast offset) Ok (Context.pushStack popCtx stackVal) else Err OutOfBounds @@ -292,7 +292,7 @@ stepExecCtx = \ctx, char -> # `!` execute lambda Task.fromResult ( - (T popCtx bytes) <- Result.try (popLambda ctx) + (T popCtx bytes) = popLambda? ctx Ok { popCtx & scopes: List.append popCtx.scopes { data: None, buf: bytes, index: 0, whileInfo: None } } ) @@ -300,8 +300,8 @@ stepExecCtx = \ctx, char -> # `?` if Task.fromResult ( - (T popCtx1 bytes) <- Result.try (popLambda ctx) - (T popCtx2 n1) <- Result.try (popNumber popCtx1) + (T popCtx1 bytes) = popLambda? ctx + (T popCtx2 n1) = popNumber? popCtx1 if n1 == 0 then Ok popCtx2 else @@ -312,8 +312,8 @@ stepExecCtx = \ctx, char -> # `#` while Task.fromResult ( - (T popCtx1 body) <- Result.try (popLambda ctx) - (T popCtx2 cond) <- Result.try (popLambda popCtx1) + (T popCtx1 body) = popLambda? ctx + (T popCtx2 cond) = popLambda? popCtx1 last = (List.len popCtx2.scopes - 1) when List.get popCtx2.scopes last is @@ -346,8 +346,8 @@ stepExecCtx = \ctx, char -> 0x5C -> # `\` swap result2 = - (T popCtx1 n1) <- Result.try (Context.popStack ctx) - (T popCtx2 n2) <- Result.try (Context.popStack popCtx1) + (T popCtx1 n1) = Context.popStack? ctx + (T popCtx2 n2) = Context.popStack? popCtx1 Ok (Context.pushStack (Context.pushStack popCtx2 n1) n2) when result2 is @@ -361,9 +361,9 @@ stepExecCtx = \ctx, char -> 0x40 -> # `@` rot result2 = - (T popCtx1 n1) <- Result.try (Context.popStack ctx) - (T popCtx2 n2) <- Result.try (Context.popStack popCtx1) - (T popCtx3 n3) <- Result.try (Context.popStack popCtx2) + (T popCtx1 n1) = Context.popStack? ctx + (T popCtx2 n2) = Context.popStack? popCtx1 + (T popCtx3 n3) = Context.popStack? popCtx2 Ok (Context.pushStack (Context.pushStack (Context.pushStack popCtx3 n2) n1) n3) when result2 is @@ -384,13 +384,13 @@ stepExecCtx = \ctx, char -> # `O` also treat this as pick for easier script writing Task.fromResult ( - (T popCtx index) <- Result.try (popNumber ctx) + (T popCtx index) = popNumber? ctx # I think Num.abs is too restrictive, it should be able to produce a natural number, but it seem to be restricted to signed numbers. size = List.len popCtx.stack - 1 offset = Num.intCast size - index if offset >= 0 then - stackVal <- Result.try (List.get popCtx.stack (Num.intCast offset)) + stackVal = List.get? popCtx.stack (Num.intCast offset) Ok (Context.pushStack popCtx stackVal) else Err OutOfBounds @@ -422,9 +422,9 @@ stepExecCtx = \ctx, char -> # Due to possible division by zero error, this must be handled specially. Task.fromResult ( - (T popCtx1 numR) <- Result.try (popNumber ctx) - (T popCtx2 numL) <- Result.try (popNumber popCtx1) - res <- Result.try (Num.divTruncChecked numL numR) + (T popCtx1 numR) = popNumber? ctx + (T popCtx2 numL) = popNumber? popCtx1 + res = Num.divTruncChecked? numL numR Ok (Context.pushStack popCtx2 (Number res)) ) @@ -504,9 +504,9 @@ stepExecCtx = \ctx, char -> # `:` store to variable Task.fromResult ( - (T popCtx1 var) <- Result.try (popVariable ctx) + (T popCtx1 var) = popVariable? ctx # The Result.mapErr on the next line maps from EmptyStack in Context.roc to the full InterpreterErrors union here. - (T popCtx2 n1) <- Result.try (Result.mapErr (Context.popStack popCtx1) (\EmptyStack -> EmptyStack)) + (T popCtx2 n1) = Result.mapErr? (Context.popStack popCtx1) (\EmptyStack -> EmptyStack) Ok { popCtx2 & vars: List.set popCtx2.vars (Variable.toIndex var) n1 } ) @@ -514,8 +514,8 @@ stepExecCtx = \ctx, char -> # `;` load from variable Task.fromResult ( - (T popCtx var) <- Result.try (popVariable ctx) - elem <- Result.try (List.get popCtx.vars (Variable.toIndex var)) + (T popCtx var) = popVariable? ctx + elem = List.get? popCtx.vars (Variable.toIndex var) Ok (Context.pushStack popCtx elem) ) @@ -551,13 +551,13 @@ stepExecCtx = \ctx, char -> unaryOp : Context, (I32 -> I32) -> Result Context InterpreterErrors unaryOp = \ctx, op -> - (T popCtx num) <- Result.try (popNumber ctx) + (T popCtx num) = popNumber? ctx Ok (Context.pushStack popCtx (Number (op num))) binaryOp : Context, (I32, I32 -> I32) -> Result Context InterpreterErrors binaryOp = \ctx, op -> - (T popCtx1 numR) <- Result.try (popNumber ctx) - (T popCtx2 numL) <- Result.try (popNumber popCtx1) + (T popCtx1 numR) = popNumber? ctx + (T popCtx2 numL) = popNumber? popCtx1 Ok (Context.pushStack popCtx2 (Number (op numL numR))) popNumber : Context -> Result [T Context I32] InterpreterErrors From fc5a53a08aec8e8d0f8680ed4d42a8907f5b6052 Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Wed, 14 Aug 2024 23:26:42 -0700 Subject: [PATCH 5/6] Enable false-interpreter test --- crates/cli/tests/cli_run.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 25237266f28..7eb215a554a 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -892,7 +892,6 @@ mod cli_run { } #[test] - #[cfg_attr(any(target_os = "windows", target_os = "linux"), ignore = "Segfault")] fn false_interpreter() { test_roc_app( "examples/cli/false-interpreter", From fdacdbe5efd414d0e4949674eb8b00f60d44df3f Mon Sep 17 00:00:00 2001 From: Sam Mohr Date: Thu, 15 Aug 2024 07:55:02 -0700 Subject: [PATCH 6/6] Re-ignore false interpreter test on Windows --- crates/cli/tests/cli_run.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cli/tests/cli_run.rs b/crates/cli/tests/cli_run.rs index 7eb215a554a..9904498c1e2 100644 --- a/crates/cli/tests/cli_run.rs +++ b/crates/cli/tests/cli_run.rs @@ -892,6 +892,7 @@ mod cli_run { } #[test] + #[cfg_attr(target_os = "windows", ignore = "Segfault")] fn false_interpreter() { test_roc_app( "examples/cli/false-interpreter",