diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 8ede3abe..716a1eff 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -1008,11 +1008,11 @@ impl<'comments> Formatter<'comments> { pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> { if op > side { - break_("{", "{ ") + break_("(", "( ") .append(doc) .nest(INDENT) .append(break_("", " ")) - .append("}") + .append(")") .group() } else { doc diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index b41545a3..4206104f 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -30,25 +30,39 @@ pub fn module( let mut extra = ModuleExtra::new(); - let tokens = tokens.into_iter().filter(|(token, span)| match token { - Token::ModuleComment => { - extra.module_comments.push(*span); - false - } - Token::DocComment => { - extra.doc_comments.push(*span); - false - } - Token::Comment => { - extra.comments.push(*span); - false - } - Token::EmptyLine => { - extra.empty_lines.push(span.start); - false - } - Token::NewLine => false, - _ => true, + let mut previous_is_newline = false; + + let tokens = tokens.into_iter().filter_map(|(token, ref span)| { + let current_is_newline = token == Token::NewLine; + let result = match token { + Token::ModuleComment => { + extra.module_comments.push(*span); + None + } + Token::DocComment => { + extra.doc_comments.push(*span); + None + } + Token::Comment => { + extra.comments.push(*span); + None + } + Token::EmptyLine => { + extra.empty_lines.push(span.start); + None + } + Token::NewLine => None, + Token::LeftParen => { + if previous_is_newline { + Some((Token::NewLineLeftParen, *span)) + } else { + Some((Token::LeftParen, *span)) + } + } + _ => Some((token, *span)), + }; + previous_is_newline = current_is_newline; + result }); let definitions = module_parser().parse(chumsky::Stream::from_iter(span(len), tokens))?; @@ -942,9 +956,15 @@ pub fn expr_parser( tail, }); - let block_parser = seq_r - .clone() - .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); + let block_parser = choice(( + seq_r + .clone() + .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), + seq_r.clone().delimited_by( + choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), + just(Token::RightParen), + ), + )); let anon_fn_parser = just(Token::Fn) .ignore_then( diff --git a/crates/aiken-lang/src/parser/token.rs b/crates/aiken-lang/src/parser/token.rs index 09da20bf..6e79e6a8 100644 --- a/crates/aiken-lang/src/parser/token.rs +++ b/crates/aiken-lang/src/parser/token.rs @@ -10,12 +10,13 @@ pub enum Token { Int { value: String }, String { value: String }, // Groupings - LeftParen, // ( - RightParen, // ) - LeftSquare, // [ - RightSquare, // } - LeftBrace, // { - RightBrace, // } + NewLineLeftParen, // ↳( + LeftParen, // ( + RightParen, // ) + LeftSquare, // [ + RightSquare, // } + LeftBrace, // { + RightBrace, // } // Int Operators Plus, Minus, @@ -95,6 +96,7 @@ impl fmt::Display for Token { Token::DiscardName { name } => name, Token::Int { value } => value, Token::String { value } => value, + Token::NewLineLeftParen => "↳(", Token::LeftParen => "(", Token::RightParen => ")", Token::LeftSquare => "[", diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index ad8e4637..4290cf0c 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -231,3 +231,28 @@ fn test_negate() { assert_fmt(src, expected) } + +#[test] +fn test_block_expr() { + let src = indoc! {r#" + fn foo() { + ( 14 + 42 ) * 1337 + } + + fn bar() { + { 14 + 42 } * 1337 + } + "#}; + + let expected = indoc! {r#" + fn foo() { + ( 14 + 42 ) * 1337 + } + + fn bar() { + ( 14 + 42 ) * 1337 + } + "#}; + + assert_fmt(src, expected); +} diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index a28f10bf..f605339c 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -7,7 +7,7 @@ use crate::{ expr, parser, }; -fn assert_definition(code: &str, definition: ast::UntypedDefinition) { +fn assert_definitions(code: &str, definitions: Vec) { let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap(); assert_eq!( @@ -17,7 +17,7 @@ fn assert_definition(code: &str, definition: ast::UntypedDefinition) { kind: ast::ModuleKind::Validator, name: "".to_string(), type_info: (), - definitions: vec![definition] + definitions, } ) } @@ -28,15 +28,15 @@ fn import() { use std/list "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Use(Use { + vec![ast::UntypedDefinition::Use(Use { location: Span::new((), 0..12), module: vec!["std".to_string(), "list".to_string()], as_name: None, unqualified: vec![], package: (), - }), + })], ) } @@ -46,9 +46,9 @@ fn unqualified_imports() { use std/address.{Address as A, thing as w} "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Use(Use { + vec![ast::UntypedDefinition::Use(Use { location: Span::new((), 0..42), module: vec!["std".to_string(), "address".to_string()], as_name: None, @@ -67,7 +67,7 @@ fn unqualified_imports() { }, ], package: (), - }), + })], ) } @@ -77,15 +77,15 @@ fn import_alias() { use std/tx as t "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Use(Use { + vec![ast::UntypedDefinition::Use(Use { location: Span::new((), 0..15), module: vec!["std".to_string(), "tx".to_string()], as_name: Some("t".to_string()), unqualified: vec![], package: (), - }), + })], ) } @@ -99,9 +99,9 @@ fn custom_type() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::DataType(DataType { + vec![ast::UntypedDefinition::DataType(DataType { constructors: vec![ ast::RecordConstructor { location: Span::new((), 19..31), @@ -180,7 +180,7 @@ fn custom_type() { parameters: vec!["a".to_string()], public: false, typed_parameters: vec![], - }), + })], ) } @@ -192,9 +192,9 @@ fn opaque_type() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::DataType(DataType { + vec![ast::UntypedDefinition::DataType(DataType { constructors: vec![ast::RecordConstructor { location: Span::new((), 21..35), name: "User".to_string(), @@ -218,7 +218,7 @@ fn opaque_type() { parameters: vec![], public: true, typed_parameters: vec![], - }), + })], ) } @@ -228,9 +228,9 @@ fn type_alias() { type Thing = Option "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::TypeAlias(TypeAlias { + vec![ast::UntypedDefinition::TypeAlias(TypeAlias { alias: "Thing".to_string(), annotation: ast::Annotation::Constructor { location: Span::new((), 13..24), @@ -248,7 +248,7 @@ fn type_alias() { parameters: vec![], public: false, tipo: (), - }), + })], ) } @@ -258,9 +258,9 @@ fn pub_type_alias() { pub type Me = Option "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::TypeAlias(TypeAlias { + vec![ast::UntypedDefinition::TypeAlias(TypeAlias { alias: "Me".to_string(), annotation: ast::Annotation::Constructor { location: Span::new((), 14..28), @@ -278,7 +278,7 @@ fn pub_type_alias() { parameters: vec![], public: true, tipo: (), - }), + })], ) } @@ -288,9 +288,9 @@ fn empty_function() { pub fn run() {} "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![], body: expr::UntypedExpr::Todo { kind: ast::TodoKind::EmptyFunction, @@ -304,7 +304,7 @@ fn empty_function() { return_annotation: None, return_type: (), end_position: 14, - }), + })], ) } @@ -316,9 +316,9 @@ fn plus_binop() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "a".to_string(), @@ -353,7 +353,7 @@ fn plus_binop() { }), return_type: (), end_position: 35, - }), + })], ) } @@ -367,9 +367,9 @@ fn pipeline() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { name: "a".to_string(), @@ -416,7 +416,7 @@ fn pipeline() { return_annotation: None, return_type: (), end_position: 63, - }), + })], ) } @@ -436,9 +436,9 @@ fn if_expression() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![], body: expr::UntypedExpr::If { location: Span::new((), 13..106), @@ -513,7 +513,7 @@ fn if_expression() { return_annotation: None, return_type: (), end_position: 107, - }), + })], ) } @@ -534,9 +534,9 @@ fn let_bindings() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "a".to_string(), @@ -641,7 +641,7 @@ fn let_bindings() { return_annotation: None, return_type: (), end_position: 122, - }), + })], ) } @@ -659,9 +659,9 @@ fn block() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "a".to_string(), @@ -732,7 +732,7 @@ fn block() { return_annotation: None, return_type: (), end_position: 67, - }), + })], ) } @@ -753,9 +753,9 @@ fn when() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "a".to_string(), @@ -878,7 +878,7 @@ fn when() { return_annotation: None, return_type: (), end_position: 139, - }), + })], ) } @@ -892,9 +892,9 @@ fn anonymous_function() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![], body: expr::UntypedExpr::Sequence { location: Span::new((), 25..83), @@ -971,7 +971,7 @@ fn anonymous_function() { }), return_type: (), end_position: 84, - }), + })], ) } @@ -983,9 +983,9 @@ fn field_access() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "user".to_string(), @@ -1016,7 +1016,7 @@ fn field_access() { return_annotation: None, return_type: (), end_position: 34, - }), + })], ) } @@ -1032,9 +1032,9 @@ fn call() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![], body: expr::UntypedExpr::Sequence { location: Span::new((), 15..108), @@ -1177,7 +1177,7 @@ fn call() { return_annotation: None, return_type: (), end_position: 109, - }), + })], ) } @@ -1189,9 +1189,9 @@ fn record_update() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![ ast::Arg { arg_name: ast::ArgName::Named { @@ -1268,7 +1268,7 @@ fn record_update() { }), return_type: (), end_position: 89, - }), + })], ) } @@ -1280,9 +1280,9 @@ fn record_create_labeled() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(ast::Function { + vec![ast::UntypedDefinition::Fn(ast::Function { arguments: vec![], body: expr::UntypedExpr::Call { arguments: vec![ @@ -1324,7 +1324,7 @@ fn record_create_labeled() { return_annotation: None, return_type: (), end_position: 54, - }), + })], ) } @@ -1336,9 +1336,9 @@ fn record_create_labeled_with_field_access() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(ast::Function { + vec![ast::UntypedDefinition::Fn(ast::Function { arguments: vec![], body: expr::UntypedExpr::Call { arguments: vec![ @@ -1384,7 +1384,7 @@ fn record_create_labeled_with_field_access() { return_annotation: None, return_type: (), end_position: 66, - }), + })], ) } @@ -1396,9 +1396,9 @@ fn record_create_unlabeled() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(ast::Function { + vec![ast::UntypedDefinition::Fn(ast::Function { arguments: vec![], body: expr::UntypedExpr::Call { arguments: vec![ @@ -1436,7 +1436,7 @@ fn record_create_unlabeled() { return_annotation: None, return_type: (), end_position: 40, - }), + })], ) } @@ -1449,9 +1449,9 @@ fn parse_tuple() { } "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::Fn(Function { + vec![ast::UntypedDefinition::Fn(Function { arguments: vec![], body: expr::UntypedExpr::Sequence { location: Span::new((), 13..86), @@ -1539,7 +1539,7 @@ fn parse_tuple() { return_annotation: None, return_type: (), end_position: 87, - }), + })], ) } @@ -1549,9 +1549,9 @@ fn plain_bytearray_literals() { pub const my_policy_id = #[0, 170, 255] "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::ModuleConstant(ModuleConstant { + vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant { doc: None, location: Span::new((), 0..39), public: true, @@ -1562,7 +1562,7 @@ fn plain_bytearray_literals() { bytes: vec![0, 170, 255], }), tipo: (), - }), + })], ) } @@ -1572,9 +1572,9 @@ fn base16_bytearray_literals() { pub const my_policy_id = #"00aaff" "#}; - assert_definition( + assert_definitions( code, - ast::UntypedDefinition::ModuleConstant(ModuleConstant { + vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant { doc: None, location: Span::new((), 0..34), public: true, @@ -1585,6 +1585,263 @@ fn base16_bytearray_literals() { bytes: vec![0, 170, 255], }), tipo: (), - }), + })], + ) +} + +#[test] +fn function_def() { + let code = indoc! {r#" + fn foo() {} + "#}; + + assert_definitions( + code, + vec![ast::UntypedDefinition::Fn(Function { + doc: None, + arguments: vec![], + body: expr::UntypedExpr::Todo { + kind: ast::TodoKind::EmptyFunction, + location: Span::new((), 0..11), + label: None, + }, + location: Span::new((), 0..8), + name: "foo".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 10, + })], + ) +} + +#[test] +fn function_invoke() { + let code = indoc! {r#" + fn foo() { + let a = bar(42) + } + "#}; + + assert_definitions( + code, + vec![ast::UntypedDefinition::Fn(Function { + doc: None, + arguments: vec![], + body: expr::UntypedExpr::Assignment { + location: Span::new((), 13..28), + kind: ast::AssignmentKind::Let, + annotation: None, + pattern: ast::Pattern::Var { + location: Span::new((), 17..18), + name: "a".to_string(), + }, + value: Box::new(expr::UntypedExpr::Call { + location: Span::new((), 24..28), + fun: Box::new(expr::UntypedExpr::Var { + location: Span::new((), 21..24), + name: "bar".to_string(), + }), + arguments: vec![ast::CallArg { + label: None, + location: Span::new((), 25..27), + value: expr::UntypedExpr::Int { + location: Span::new((), 25..27), + value: "42".to_string(), + }, + }], + }), + }, + location: Span::new((), 0..8), + name: "foo".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 29, + })], + ) +} + +#[test] +fn function_ambiguous_sequence() { + let code = indoc! {r#" + fn foo_1() { + let a = bar + (40) + } + + fn foo_2() { + let a = bar + {40} + } + + fn foo_3() { + let a = (40+2) + } + + fn foo_4() { + let a = bar(42) + (a + 14) * 42 + } + "#}; + + assert_definitions( + code, + vec![ + ast::UntypedDefinition::Fn(Function { + arguments: vec![], + body: expr::UntypedExpr::Sequence { + location: Span::new((), 15..32), + expressions: vec![ + expr::UntypedExpr::Assignment { + location: Span::new((), 15..26), + value: Box::new(expr::UntypedExpr::Var { + location: Span::new((), 23..26), + name: "bar".to_string(), + }), + pattern: ast::Pattern::Var { + location: Span::new((), 19..20), + name: "a".to_string(), + }, + kind: ast::AssignmentKind::Let, + annotation: None, + }, + expr::UntypedExpr::Int { + location: Span::new((), 30..32), + value: "40".to_string(), + }, + ], + }, + doc: None, + location: Span::new((), 0..10), + name: "foo_1".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 34, + }), + ast::UntypedDefinition::Fn(Function { + arguments: vec![], + body: expr::UntypedExpr::Sequence { + location: Span::new((), 52..69), + expressions: vec![ + expr::UntypedExpr::Assignment { + location: Span::new((), 52..63), + value: Box::new(expr::UntypedExpr::Var { + location: Span::new((), 60..63), + name: "bar".to_string(), + }), + pattern: ast::Pattern::Var { + location: Span::new((), 56..57), + name: "a".to_string(), + }, + kind: ast::AssignmentKind::Let, + annotation: None, + }, + expr::UntypedExpr::Int { + location: Span::new((), 67..69), + value: "40".to_string(), + }, + ], + }, + doc: None, + location: Span::new((), 37..47), + name: "foo_2".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 71, + }), + ast::UntypedDefinition::Fn(Function { + arguments: vec![], + body: expr::UntypedExpr::Assignment { + location: Span::new((), 89..103), + value: Box::new(expr::UntypedExpr::BinOp { + location: Span::new((), 98..102), + name: ast::BinOp::AddInt, + left: Box::new(expr::UntypedExpr::Int { + location: Span::new((), 98..100), + value: "40".to_string(), + }), + right: Box::new(expr::UntypedExpr::Int { + location: Span::new((), 101..102), + value: "2".to_string(), + }), + }), + pattern: ast::Pattern::Var { + location: Span::new((), 93..94), + name: "a".to_string(), + }, + kind: ast::AssignmentKind::Let, + annotation: None, + }, + doc: None, + location: Span::new((), 74..84), + name: "foo_3".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 104, + }), + ast::UntypedDefinition::Fn(Function { + arguments: vec![], + body: expr::UntypedExpr::Sequence { + location: Span::new((), 122..153), + expressions: vec![ + expr::UntypedExpr::Assignment { + location: Span::new((), 122..137), + value: Box::new(expr::UntypedExpr::Call { + arguments: vec![ast::CallArg { + label: None, + location: Span::new((), 134..136), + value: expr::UntypedExpr::Int { + location: Span::new((), 134..136), + value: "42".to_string(), + }, + }], + fun: Box::new(expr::UntypedExpr::Var { + location: Span::new((), 130..133), + name: "bar".to_string(), + }), + location: Span::new((), 133..137), + }), + pattern: ast::Pattern::Var { + location: Span::new((), 126..127), + name: "a".to_string(), + }, + kind: ast::AssignmentKind::Let, + annotation: None, + }, + expr::UntypedExpr::BinOp { + location: Span::new((), 141..153), + name: ast::BinOp::MultInt, + left: Box::new(expr::UntypedExpr::BinOp { + location: Span::new((), 141..147), + name: ast::BinOp::AddInt, + left: Box::new(expr::UntypedExpr::Var { + location: Span::new((), 141..142), + name: "a".to_string(), + }), + right: Box::new(expr::UntypedExpr::Int { + location: Span::new((), 145..147), + value: "14".to_string(), + }), + }), + right: Box::new(expr::UntypedExpr::Int { + location: Span::new((), 151..153), + value: "42".to_string(), + }), + }, + ], + }, + doc: None, + location: Span::new((), 107..117), + name: "foo_4".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 154, + }), + ], ) }