Merge pull request #7000 from smores56/try-suffix-ast-node

Combine ! and ? into single TrySuffix AST node
This commit is contained in:
Sam Mohr 2024-08-15 09:31:59 -07:00 committed by GitHub
commit a14a110293
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 921 additions and 188 deletions

View file

@ -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;

View file

@ -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::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();
@ -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())
@ -2515,7 +2518,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

View file

@ -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<first (second! 42)>`
UnwrappedDefExpr(&'a Loc<Expr<'a>>),
UnwrappedDefExpr {
loc_expr: &'a Loc<Expr<'a>>,
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<Expr<'a>>,
/// the pattern for the closure
@ -49,6 +52,9 @@ pub enum EUnwrapped<'a> {
/// the expression to replace the unwrapped
sub_new: &'a Loc<Expr<'a>>,
/// The type of the target for the suffix, e.g. a Task or Result
target: TryTarget,
},
/// Malformed use of the suffix
@ -59,12 +65,16 @@ fn init_unwrapped_err<'a>(
arena: &'a Bump,
unwrapped_expr: &'a Loc<Expr<'a>>,
maybe_def_pat: Option<&'a Loc<Pattern<'a>>>,
target: TryTarget,
) -> Result<&'a Loc<Expr<'a>>, 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 +97,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 +114,13 @@ pub fn unwrap_suffixed_expression<'a>(
) -> Result<&'a Loc<Expr<'a>>, 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 +168,22 @@ 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 +191,7 @@ pub fn unwrap_suffixed_expression<'a>(
sub_arg: new_expect,
sub_pat,
sub_new,
target,
})
}
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
@ -208,13 +230,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 +247,7 @@ pub fn unwrap_suffixed_expression_parens_help<'a>(
sub_arg,
sub_pat,
sub_new: new_parens,
target,
})
}
Err(err) => Err(err),
@ -247,13 +271,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 +302,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 +325,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 +334,14 @@ 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 +384,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 +447,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 +471,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 +500,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 +535,24 @@ 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);
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,
@ -551,7 +587,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 +614,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 +667,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 +676,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 +743,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 +778,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 +817,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 +826,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 +855,24 @@ 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 +882,7 @@ fn unwrap_low_level_dbg<'a>(
sub_arg: new_dbg,
sub_pat,
sub_new,
target,
})
}
Err(EUnwrapped::Malformed) => Err(EUnwrapped::Malformed),
@ -836,25 +894,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<Expr<'a>>,
loc_pat: &'a Loc<Pattern<'a>>,
loc_cont: &'a Loc<Expr<'a>>,
maybe_loc_ann: Option<(&'a Loc<Pattern>, &'a Loc<TypeAnnotation<'a>>)>,
target: TryTarget,
) -> &'a Loc<Expr<'a>> {
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 +934,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 +956,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 +981,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 +998,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 +1008,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,
),
))
}

View file

@ -10,7 +10,7 @@ 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,
OldRecordBuilderField, Pattern, TryTarget, WhenBranch,
};
use roc_parse::ast::{StrLiteral, StrSegment};
use roc_parse::ident::Accessor;
@ -47,7 +47,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 +520,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);

View file

@ -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)]

View file

@ -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<Expr<'a>>>),
@ -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(),

View file

@ -3,7 +3,7 @@ use crate::ast::{
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
ModuleImportParams, OldRecordBuilderField, Pattern, Spaceable, Spaced, Spaces, SpacesBefore,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
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 +169,12 @@ 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 +2182,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::OptionalFieldInRecordBuilder(_, _)
| Expr::RecordUpdate { .. }
| Expr::UnaryOp(_, _)
| Expr::TaskAwaitBang(..)
| Expr::TrySuffix { .. }
| Expr::Crash
| Expr::OldRecordBuilder(..)
| Expr::RecordBuilder { .. } => return Err(()),
@ -3749,7 +3754,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),
},
})
}

View file

@ -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

View file

@ -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::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)),

View file

@ -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,

View file

@ -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,

View file

@ -1,10 +0,0 @@
TaskAwaitBang(
TaskAwaitBang(
TaskAwaitBang(
Var {
module_name: "Stdout",
ident: "line",
},
),
),
)

View file

@ -0,0 +1,13 @@
TrySuffix {
target: Task,
expr: TrySuffix {
target: Task,
expr: TrySuffix {
target: Task,
expr: Var {
module_name: "Stdout",
ident: "line",
},
},
},
}

View file

@ -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: "",

View file

@ -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: "",

View file

@ -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(

View file

@ -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",

View file

@ -0,0 +1,13 @@
TrySuffix {
target: Result,
expr: TrySuffix {
target: Result,
expr: TrySuffix {
target: Result,
expr: Var {
module_name: "Stdout",
ident: "line",
},
},
},
}

View file

@ -0,0 +1 @@
Stdout.line???

View file

@ -0,0 +1,5 @@
main =
a? "Bar"
x = B.b? "Foo"
c? x

View file

@ -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,
],
),
),
],
}

View file

@ -0,0 +1,5 @@
main =
a? "Bar"
x= B.b? "Foo"
c? x

View file

@ -0,0 +1 @@
foo? (bar? baz) (blah stuff)

View file

@ -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,
)

View file

@ -0,0 +1 @@
foo? ( bar? baz) ( blah stuff)

View file

@ -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"

View file

@ -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?",
),
],
),
),
],
},
},
)

View file

@ -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"

View file

@ -0,0 +1,9 @@
app [main] {
cli: platform "",
}
main =
"jq --version"
|> Cmd.new
|> Cmd.status
|> Result.mapErr? UnableToCheckJQVersion

View file

@ -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,
],
),
),
],
},
},
)

View file

@ -0,0 +1,12 @@
app ""
packages {
cli: "",
}
imports []
provides [main] to cli
main =
"jq --version"
|> Cmd.new
|> Cmd.status
|> Result.mapErr? UnableToCheckJQVersion

View file

@ -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,