diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index e6c89fea56..15bd3294f0 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -2880,7 +2880,7 @@ fn to_pending_value_def<'a>( preceding_comment: *preceding_comment, }), - Stmt(_) => todo!(), + Stmt(_) => internal_error!("a Stmt was not desugared correctly, should have been converted to a Body(...) in desguar"), } } diff --git a/crates/compiler/can/src/desugar.rs b/crates/compiler/can/src/desugar.rs index 6485224ae9..50902d6aeb 100644 --- a/crates/compiler/can/src/desugar.rs +++ b/crates/compiler/can/src/desugar.rs @@ -133,8 +133,14 @@ fn desugar_value_def<'a>( } } - // We'll need to desugar into `Body({}=, ...)` - Stmt(_) => todo!(), + // Desugar into `Body({}=, ...)` + Stmt(loc_expr) => ValueDef::Body( + arena.alloc(Loc::at( + loc_expr.region, + Pattern::RecordDestructure(Collection::empty()), + )), + desugar_expr(arena, loc_expr, src, line_info, module_path), + ), } } @@ -169,11 +175,8 @@ fn desugar_defs_node_suffixed<'a>( ); // Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await - let (suffixed_sub_apply_loc, pattern) = unwrap_suffixed_def_and_pattern( - arena, - loc_expr.region, - defs.value_defs[0], - ); + let (suffixed_sub_loc_expr, pattern) = + unwrap_suffixed_value_def(arena, defs.value_defs[0]); // Create Closure for the result of the recursion, // use the pattern from our Suffixed Def as closure argument @@ -182,8 +185,7 @@ fn desugar_defs_node_suffixed<'a>( // Apply arguments to Task.await, first is the unwrapped Suffix expr second is the Closure let mut task_await_apply_args: Vec<&'a Loc>> = Vec::new_in(arena); - task_await_apply_args - .push(arena.alloc(Loc::at(loc_expr.region, suffixed_sub_apply_loc))); + task_await_apply_args.push(suffixed_sub_loc_expr); task_await_apply_args .push(arena.alloc(Loc::at(loc_expr.region, closure_expr))); @@ -213,11 +215,8 @@ fn desugar_defs_node_suffixed<'a>( ); // Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await - let (suffixed_sub_apply_loc, pattern) = unwrap_suffixed_def_and_pattern( - arena, - loc_expr.region, - defs.value_defs[0], - ); + let (suffixed_sub_loc_expr, pattern) = + unwrap_suffixed_value_def(arena, defs.value_defs[0]); // Get a mutable copy of the defs let mut copied_defs = defs.clone(); @@ -241,8 +240,7 @@ fn desugar_defs_node_suffixed<'a>( // Apply arguments to Task.await, first is the unwrapped Suffix expr second is the Closure let mut task_await_apply_args: Vec<&'a Loc>> = Vec::new_in(arena); - task_await_apply_args - .push(arena.alloc(Loc::at(loc_expr.region, suffixed_sub_apply_loc))); + task_await_apply_args.push(suffixed_sub_loc_expr); task_await_apply_args .push(arena.alloc(Loc::at(loc_expr.region, closure_expr))); @@ -289,11 +287,8 @@ fn desugar_defs_node_suffixed<'a>( }; // Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await - let (suffixed_sub_apply_loc, pattern) = unwrap_suffixed_def_and_pattern( - arena, - loc_expr.region, - defs.value_defs[value_index], - ); + let (suffixed_sub_loc_expr, pattern) = + unwrap_suffixed_value_def(arena, defs.value_defs[value_index]); // Create Closure for the result of the recursion, // use the pattern from our Suffixed Def as closure argument @@ -303,8 +298,7 @@ fn desugar_defs_node_suffixed<'a>( // Apply arguments to Task.await, first is the unwrapped Suffix expr second is the Closure let mut task_await_apply_args: Vec<&'a Loc>> = Vec::new_in(arena); - task_await_apply_args - .push(arena.alloc(Loc::at(loc_expr.region, suffixed_sub_apply_loc))); + task_await_apply_args.push(suffixed_sub_loc_expr); task_await_apply_args .push(arena.alloc(Loc::at(loc_expr.region, closure_expr))); @@ -338,40 +332,76 @@ fn desugar_defs_node_suffixed<'a>( } } -// Unwrap Suffixed def within Apply, and the pattern so we can use in the call to Task.await -fn unwrap_suffixed_def_and_pattern<'a>( +// Unwrap suffixed value_def so we can use in a call to Task.await +fn unwrap_suffixed_value_def<'a>( arena: &'a Bump, - region: Region, value_def: ValueDef<'a>, ) -> ( - roc_parse::ast::Expr<'a>, + &'a Loc>, &'a Loc>, ) { - todo!() - // match value_def { - // ValueDef::Body(pattern, suffixed_expression) => match suffixed_expression.value { - // // The Suffixed has arguments applied e.g. `Stdout.line! "Hello World"` - // Apply(sub_loc, suffixed_args, called_via) => match sub_loc.value { - // Suffixed(sub_expr) => ( - // Apply( - // arena.alloc(Loc::at(region, *sub_expr)), - // suffixed_args, - // called_via, - // ), - // pattern, - // ), - // _ => unreachable!("should have a suffixed Apply inside Body def"), - // }, - // // The Suffixed has NIL arguments applied e.g. `Stdin.line!` - // Suffixed(sub_expr) => (*sub_expr, pattern), - // _ => { - // unreachable!("should have a suffixed Apply inside Body def") - // } - // }, - // _ => unreachable!("should have a suffixed Body def"), - // } + match value_def { + ValueDef::Stmt(_) => { + internal_error!("this should have been desugared elswhere...") + } + ValueDef::Body(loc_pattern, loc_expr) => { + (unwrap_suffixed_loc_expr(arena, loc_expr), loc_pattern) + } + _ => unreachable!("should have a suffixed Body value_def"), + } } +fn unwrap_suffixed_loc_expr<'a>( + arena: &'a Bump, + loc_expr: &Loc>, +) -> &'a Loc> { + match loc_expr.value { + // Arguments applied e.g. `Stdout.line! "Hello World"` + Apply( + Loc { + value: + Var { + suffixed, + module_name, + ident, + }, + .. + }, + args, + called_via, + ) if suffixed > &0 => arena.alloc(Loc::at( + loc_expr.region, + Apply( + arena.alloc(Loc::at( + loc_expr.region, + Var { + module_name, + ident, + suffixed: 0, + }, + )), + args, + called_via, + ), + )), + // NIL arguments applied e.g. `Stdin.line!` + Var { + suffixed, + module_name, + ident, + } if suffixed > 0 => arena.alloc(Loc::at( + loc_expr.region, + Var { + module_name, + ident, + suffixed: 0, + }, + )), + _ => { + unreachable!("should have a suffixed Var inside a Body value_def") + } + } +} /// Reorder the expression tree based on operator precedence and associativity rules, /// then replace the BinOp nodes with Apply nodes. Also drop SpaceBefore and SpaceAfter nodes. pub fn desugar_expr<'a>( @@ -595,13 +625,13 @@ pub fn desugar_expr<'a>( desugar_defs_node_values(arena, &mut defs, src, line_info, module_path); let loc_ret = desugar_expr(arena, loc_ret, src, line_info, module_path); - // Desugar any Suffixed nodes - // desugar_defs_node_suffixed( - // arena, - // arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret))), - // ) + // Desugar any suffixed nodes, such as `foo = bar!` + desugar_defs_node_suffixed( + arena, + arena.alloc(Loc::at(loc_expr.region, Defs(arena.alloc(defs), loc_ret))), + ) - arena.alloc(Loc::at(loc_ret.region, Defs(arena.alloc(defs), loc_ret))) + // arena.alloc(Loc::at(loc_ret.region, Defs(arena.alloc(defs), loc_ret))) } Apply(loc_fn, loc_args, called_via) => { let mut desugared_args = Vec::with_capacity_in(loc_args.len(), arena); diff --git a/crates/compiler/load_internal/src/docs.rs b/crates/compiler/load_internal/src/docs.rs index 22c96b6119..5b79d174dd 100644 --- a/crates/compiler/load_internal/src/docs.rs +++ b/crates/compiler/load_internal/src/docs.rs @@ -291,7 +291,25 @@ fn generate_entry_docs( // Don't generate docs for `expect-fx`s } - ValueDef::Stmt(_) => todo!(), + ValueDef::Stmt(loc_expr) => { + // TODO is this right for suffixed?? + if let roc_parse::ast::Expr::Var { + ident: identifier, .. + } = loc_expr.value + { + // Check if this module exposes the def + if let Some(ident_id) = ident_ids.get_id(identifier) { + let doc_def = DocDef { + name: identifier.to_string(), + type_annotation: TypeAnnotation::NoTypeAnn, + type_vars: Vec::new(), + symbol: Symbol::new(home, ident_id), + docs, + }; + doc_entries.push(DocEntry::DocDef(doc_def)); + } + } + } }, Ok(type_index) => match &defs.type_defs[type_index.index()] { diff --git a/crates/compiler/parse/src/ast.rs b/crates/compiler/parse/src/ast.rs index 5d8ab00bfc..67061f4d0e 100644 --- a/crates/compiler/parse/src/ast.rs +++ b/crates/compiler/parse/src/ast.rs @@ -346,20 +346,20 @@ pub enum Expr<'a> { UnappliedRecordBuilder(&'a Loc>), } -pub fn is_loc_expr_suffixed(loc_expr: Loc) -> bool { +pub fn is_loc_expr_suffixed(loc_expr: &Loc) -> bool { match loc_expr.value.extract_spaces().item { // expression without arguments, `read!` Expr::Var { suffixed, .. } => suffixed > 0, // expression with arguments, `line! "Foo"` - Expr::Apply(sub_loc_expr, _, _) => is_loc_expr_suffixed(*sub_loc_expr), + Expr::Apply(sub_loc_expr, _, _) => is_loc_expr_suffixed(sub_loc_expr), // expression in a pipeline, `"hi" |> say!` Expr::BinOps(chain, sub_loc_expr) => { let sum: isize = chain - .into_iter() + .iter() .map(|(chain_loc_expr, _)| -> isize { - if is_loc_expr_suffixed(*chain_loc_expr) { + if is_loc_expr_suffixed(chain_loc_expr) { 1 } else { 0 @@ -367,7 +367,7 @@ pub fn is_loc_expr_suffixed(loc_expr: Loc) -> bool { }) .sum(); - is_loc_expr_suffixed(*sub_loc_expr) || sum > 0 + is_loc_expr_suffixed(sub_loc_expr) || sum > 0 } _ => false, @@ -544,13 +544,13 @@ impl<'a> Defs<'a> { .. }, loc_expr, - ) if collection.is_empty() && is_loc_expr_suffixed(*loc_expr) => { + ) if collection.is_empty() && is_loc_expr_suffixed(loc_expr) => { let mut new_defs = self.clone(); new_defs.remove_value_def(tag_index); return Some((new_defs, loc_expr)); } - ValueDef::Stmt(loc_expr) if is_loc_expr_suffixed(*loc_expr) => { + ValueDef::Stmt(loc_expr) if is_loc_expr_suffixed(loc_expr) => { let mut new_defs = self.clone(); new_defs.remove_value_def(tag_index); @@ -676,7 +676,7 @@ impl<'a> Defs<'a> { // a definition with a suffixed expression e.g. `args = Arg.list!` if let ValueDef::Body(_, loc_expr) = &self.value_defs[index] { - if is_loc_expr_suffixed(**loc_expr) { + if is_loc_expr_suffixed(loc_expr) { return Some((tag_index, index)); } } diff --git a/crates/compiler/parse/src/expr.rs b/crates/compiler/parse/src/expr.rs index 58cadf2b14..835fa2eba5 100644 --- a/crates/compiler/parse/src/expr.rs +++ b/crates/compiler/parse/src/expr.rs @@ -327,7 +327,7 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a let initial_state = state.clone(); let end = state.pos(); - let new_indent = if is_loc_expr_suffixed(expr) { + let new_indent = if is_loc_expr_suffixed(&expr) { min_indent + 1 } else { min_indent @@ -404,7 +404,7 @@ impl<'a> ExprState<'a> { } else if !self.expr.value.is_tag() && !self.expr.value.is_opaque() && !self.arguments.is_empty() - && !is_loc_expr_suffixed(self.expr) + && !is_loc_expr_suffixed(&self.expr) { let region = Region::across_all(self.arguments.iter().map(|v| &v.region)); @@ -631,7 +631,7 @@ pub fn parse_single_def<'a>( |_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) }, ) { Ok((_, Some(single_def), state)) => match single_def.type_or_value { - Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(*loc_expr) => { + Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(loc_expr) => { Ok((MadeProgress, Some(single_def), state)) } _ => Ok((NoProgress, None, initial)), @@ -712,21 +712,26 @@ pub fn parse_single_def<'a>( let operator_result = operator().parse(arena, state.clone(), min_indent); if let Ok((_, BinOp::Assignment, operator_result_state)) = operator_result { - return parse_single_def_assignment( + let result = parse_single_def_assignment( options, // to support statements we have to increase the indent here so that we can parse a child def // within a def and still continue to parse the final expresison for this def // e.g. // main = // Stdout.line! "Bar" - // a = Stdout.line! "Foo" + // a=Stdout.line! "Foo" // Task.ok {} - min_indent + 1, + // any less than +4 and this doesn't work reliably + operator_result_state.line_indent() + 1, arena, - operator_result_state, + operator_result_state.clone(), loc_pattern, spaces_before_current, ); + + // dbg!("parse_single_def", &loc_pattern, min_indent, operator_result_state.clone(), &result); + + return result; }; if let Ok((_, BinOp::IsAliasType, state)) = operator_result { @@ -893,7 +898,7 @@ pub fn parse_single_def<'a>( |_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) }, ) { Ok((_, Some(single_def), state)) => match single_def.type_or_value { - Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(*loc_expr) => { + Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(loc_expr) => { Ok((MadeProgress, Some(single_def), state)) } _ => Ok((NoProgress, None, initial)), @@ -923,7 +928,7 @@ pub fn parse_single_def_assignment<'a>( // If the expression is actually a suffixed statement, then we need to continue // to parse the rest of the expression - if crate::ast::is_loc_expr_suffixed(loc_def_expr) { + if crate::ast::is_loc_expr_suffixed(&loc_def_expr) { let mut defs = Defs::default(); // Take the suffixed value and make it a e.g. Body(`{}=`, Apply(Var(...))) // we will keep the pattern `loc_pattern` for the new Defs @@ -941,7 +946,9 @@ pub fn parse_single_def_assignment<'a>( loc_def_expr = Loc::at(region, expr); state = new_state; } - Err(err) => {} + Err(_) => { + // unable to parse more definitions, continue + } } }; @@ -956,80 +963,6 @@ pub fn parse_single_def_assignment<'a>( }), state, )) - - // If there is only 1 def then extract the value and replace the Defs node - // let loc_return = if defs.value_defs.len() > 1 { - // match defs.last_value_suffixed() { - // None => internal_error!("expected a value_def, found nothing"), - // Some((new_defs, value_def)) => { - // if new_defs. - // } - // } - // }; - - // let parse_defs_expr_result = dbg!(parse_defs_expr( - // options, - // min_indent + 1, - // defs.clone(), - // arena, - // state_after_def_expr.clone(), - // )); - - // if let Err((err_progress, EExpr::DefMissingFinalExpr2(expr2, pos))) = - // parse_defs_expr_result - // { - // let defs_copy = defs.clone(); - - // // Try to de-sugar `!` a suffix if it is the final expression - // if let Some(ValueDef::Body(body_loc_pattern, loc_expr)) = - // defs_copy.value_defs.last() - // { - // if let Pattern::RecordDestructure(record) = - // body_loc_pattern.extract_spaces().item - // { - // if record.is_empty() && is_loc_expr_suffixed(**loc_expr) { - // // remove the last value_def and extract the Suffixed value - // // to be the return expression ret_loc - // let mut new_defs_copy = defs_copy.clone(); - - // dbg!(&new_defs_copy); - // new_defs_copy.remove_value_def( - // new_defs_copy.tags.len().saturating_sub(1), - // ); - // dbg!(&new_defs_copy); - - // let loc_ret = if new_defs_copy.len() <= 1 { - // extract_suffixed_expr(arena, **loc_expr) - // } else { - // arena.alloc(Loc::at( - // region, - // Expr::Defs( - // arena.alloc(new_defs_copy), - // extract_suffixed_expr(arena, **loc_expr), - // ), - // )) - // }; - - // return Ok(( - // MadeProgress, - // Some(SingleDef { - // type_or_value: Either::Second(ValueDef::Body( - // arena.alloc(loc_pattern), - // loc_ret, - // )), - // region, - // spaces_before: spaces_before_current, - // }), - // state_after_def_expr.clone(), - // )); - // } - // } - // } - - // return Err((err_progress, EExpr::DefMissingFinalExpr2(expr2, pos))); - // } else if let Ok((progress, expr, state_after_parse_defs_expr)) = - // parse_defs_expr_result - // { } /// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg` @@ -1246,7 +1179,7 @@ fn parse_defs_expr<'a>( arena: &'a Bump, state: State<'a>, ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { - match dbg!(parse_defs_end(options, min_indent, defs, arena, state)) { + match parse_defs_end(options, min_indent, defs, arena, state) { Err(bad) => Err(bad), Ok((_, def_state, state)) => { // this is no def, because there is no `=` or `:`; parse as an expr @@ -1817,7 +1750,7 @@ fn parse_expr_operator<'a>( // ``` // if we don't do this then we do not know where the statement ends // and the next expressions starts - let new_indent = if is_loc_expr_suffixed(new_expr) { + let new_indent = if is_loc_expr_suffixed(&new_expr) { min_indent + 1 } else { min_indent 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_multiple_defs.moduledefs.formatted.roc index 21dbd17c19..3365e68d38 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.formatted.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.formatted.roc @@ -1,7 +1,5 @@ main = - A.x! "Bar" -ab = + a! "Bar" + x = B.b! "Foo" - B.y! "Foo" - - C.z out + c! x diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast index cc2e0befaf..bc84fe4522 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.result-ast @@ -1,23 +1,17 @@ Defs { tags: [ Index(2147483648), - Index(2147483649), ], regions: [ - @0-22, - @27-40, + @0-20, ], 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, ], + spaces: [], type_defs: [], value_defs: [ Body( @@ -25,44 +19,22 @@ Defs { ident: "main", suffixed: 0, }, - @12-22 SpaceBefore( - Apply( - @12-16 Var { - module_name: "A", - ident: "x", - suffixed: 1, - }, - [ - @17-22 Str( - PlainLine( - "Bar", - ), - ), - ], - Space, - ), - [ - Newline, - ], - ), - ), - Body( - @27-29 Identifier { - ident: "ab", - suffixed: 0, - }, - @27-40 Defs( + @0-20 Defs( Defs { tags: [ Index(2147483648), + Index(2147483649), ], regions: [ - @27-40, + @0-20, + @25-37, ], 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: [ @@ -71,14 +43,40 @@ Defs { type_defs: [], value_defs: [ Stmt( - @30-40 Apply( - @30-34 Var { + @12-20 SpaceBefore( + Apply( + @12-14 Var { + module_name: "", + ident: "a", + suffixed: 1, + }, + [ + @15-20 Str( + PlainLine( + "Bar", + ), + ), + ], + Space, + ), + [ + Newline, + ], + ), + ), + Body( + @25-26 Identifier { + ident: "x", + suffixed: 0, + }, + @27-37 Apply( + @27-31 Var { module_name: "B", - ident: "y", + ident: "b", suffixed: 1, }, [ - @35-40 Str( + @32-37 Str( PlainLine( "Foo", ), @@ -89,17 +87,17 @@ Defs { ), ], }, - @46-53 SpaceBefore( + @43-47 SpaceBefore( Apply( - @46-49 Var { - module_name: "C", - ident: "z", - suffixed: 0, + @43-45 Var { + module_name: "", + ident: "c", + suffixed: 1, }, [ - @50-53 Var { + @46-47 Var { module_name: "", - ident: "out", + ident: "x", suffixed: 0, }, ], diff --git a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc index da9960c3ec..ae5885e0e7 100644 --- a/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc +++ b/crates/compiler/test_syntax/tests/snapshots/pass/suffixed_multiple_defs.moduledefs.roc @@ -1,5 +1,5 @@ main = - A.x! "Bar" - ab=B.y! "Foo" + a! "Bar" + x=B.b! "Foo" - C.z out \ No newline at end of file + c! x \ No newline at end of file