Support (and default to) parenthesis for block expressions

This changes allow to use parenthesis `(` `)` to encapsulate
  expressions in addition to braces `{` `}` used to define blocks.

  The main use-case is for arithmetic and boolean expressions for which
  developers are used to using parenthesis. For example:

  ```
  { 14 + 42 } * 1337
  ```

  can now be written as:

  ```
  ( 14 + 42 ) * 1337
  ```

  This may sound straightforward at first but wasn't necessarily trivial
  in Aiken given that (a) everything is an expression, (b) whitespaces
  do not generally matter and (c) there's no symbol indicating the end
  of a 'statement' (because there's no statement).

  Thus, we have to properly disambiguate between:

  ```
  let foo = bar(14 + 42)
  ```

  and

  ```
  let foo = bar
  (14 + 42)
  ```

  Before this commit, the latter would be interpreted as a function call
  and would lead to a somewhat puzzling error. Now, the newline serves
  as a delimiting symbol. The trade-off being that for a function call,
  the left parenthesis has to be on the same line as the function name
  identifier -- which is a fair trade off. So this is still allowed:

  ```
  let foo = bar(
    14 + 42
  )
  ```

  As there's very little ambiguity about it.

  This fixes #236 and would seemingly allow us to get rid of the leading
  `#` in front of tuples.
This commit is contained in:
KtorZ 2023-01-14 10:39:25 +01:00 committed by Lucas
parent b791131fd0
commit 2d99c07dd3
5 changed files with 408 additions and 104 deletions

View File

@ -1008,11 +1008,11 @@ impl<'comments> Formatter<'comments> {
pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> { pub fn operator_side<'a>(&mut self, doc: Document<'a>, op: u8, side: u8) -> Document<'a> {
if op > side { if op > side {
break_("{", "{ ") break_("(", "( ")
.append(doc) .append(doc)
.nest(INDENT) .nest(INDENT)
.append(break_("", " ")) .append(break_("", " "))
.append("}") .append(")")
.group() .group()
} else { } else {
doc doc

View File

@ -30,25 +30,39 @@ pub fn module(
let mut extra = ModuleExtra::new(); let mut extra = ModuleExtra::new();
let tokens = tokens.into_iter().filter(|(token, span)| match token { let mut previous_is_newline = false;
Token::ModuleComment => {
extra.module_comments.push(*span); let tokens = tokens.into_iter().filter_map(|(token, ref span)| {
false let current_is_newline = token == Token::NewLine;
} let result = match token {
Token::DocComment => { Token::ModuleComment => {
extra.doc_comments.push(*span); extra.module_comments.push(*span);
false None
} }
Token::Comment => { Token::DocComment => {
extra.comments.push(*span); extra.doc_comments.push(*span);
false None
} }
Token::EmptyLine => { Token::Comment => {
extra.empty_lines.push(span.start); extra.comments.push(*span);
false None
} }
Token::NewLine => false, Token::EmptyLine => {
_ => true, 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))?; let definitions = module_parser().parse(chumsky::Stream::from_iter(span(len), tokens))?;
@ -942,9 +956,15 @@ pub fn expr_parser(
tail, tail,
}); });
let block_parser = seq_r let block_parser = choice((
.clone() seq_r
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)); .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) let anon_fn_parser = just(Token::Fn)
.ignore_then( .ignore_then(

View File

@ -10,12 +10,13 @@ pub enum Token {
Int { value: String }, Int { value: String },
String { value: String }, String { value: String },
// Groupings // Groupings
LeftParen, // ( NewLineLeftParen, // ↳(
RightParen, // ) LeftParen, // (
LeftSquare, // [ RightParen, // )
RightSquare, // } LeftSquare, // [
LeftBrace, // { RightSquare, // }
RightBrace, // } LeftBrace, // {
RightBrace, // }
// Int Operators // Int Operators
Plus, Plus,
Minus, Minus,
@ -95,6 +96,7 @@ impl fmt::Display for Token {
Token::DiscardName { name } => name, Token::DiscardName { name } => name,
Token::Int { value } => value, Token::Int { value } => value,
Token::String { value } => value, Token::String { value } => value,
Token::NewLineLeftParen => "↳(",
Token::LeftParen => "(", Token::LeftParen => "(",
Token::RightParen => ")", Token::RightParen => ")",
Token::LeftSquare => "[", Token::LeftSquare => "[",

View File

@ -231,3 +231,28 @@ fn test_negate() {
assert_fmt(src, expected) 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);
}

View File

@ -7,7 +7,7 @@ use crate::{
expr, parser, expr, parser,
}; };
fn assert_definition(code: &str, definition: ast::UntypedDefinition) { fn assert_definitions(code: &str, definitions: Vec<ast::UntypedDefinition>) {
let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap(); let (module, _extra) = parser::module(code, ast::ModuleKind::Validator).unwrap();
assert_eq!( assert_eq!(
@ -17,7 +17,7 @@ fn assert_definition(code: &str, definition: ast::UntypedDefinition) {
kind: ast::ModuleKind::Validator, kind: ast::ModuleKind::Validator,
name: "".to_string(), name: "".to_string(),
type_info: (), type_info: (),
definitions: vec![definition] definitions,
} }
) )
} }
@ -28,15 +28,15 @@ fn import() {
use std/list use std/list
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Use(Use { vec![ast::UntypedDefinition::Use(Use {
location: Span::new((), 0..12), location: Span::new((), 0..12),
module: vec!["std".to_string(), "list".to_string()], module: vec!["std".to_string(), "list".to_string()],
as_name: None, as_name: None,
unqualified: vec![], unqualified: vec![],
package: (), package: (),
}), })],
) )
} }
@ -46,9 +46,9 @@ fn unqualified_imports() {
use std/address.{Address as A, thing as w} use std/address.{Address as A, thing as w}
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Use(Use { vec![ast::UntypedDefinition::Use(Use {
location: Span::new((), 0..42), location: Span::new((), 0..42),
module: vec!["std".to_string(), "address".to_string()], module: vec!["std".to_string(), "address".to_string()],
as_name: None, as_name: None,
@ -67,7 +67,7 @@ fn unqualified_imports() {
}, },
], ],
package: (), package: (),
}), })],
) )
} }
@ -77,15 +77,15 @@ fn import_alias() {
use std/tx as t use std/tx as t
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Use(Use { vec![ast::UntypedDefinition::Use(Use {
location: Span::new((), 0..15), location: Span::new((), 0..15),
module: vec!["std".to_string(), "tx".to_string()], module: vec!["std".to_string(), "tx".to_string()],
as_name: Some("t".to_string()), as_name: Some("t".to_string()),
unqualified: vec![], unqualified: vec![],
package: (), package: (),
}), })],
) )
} }
@ -99,9 +99,9 @@ fn custom_type() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::DataType(DataType { vec![ast::UntypedDefinition::DataType(DataType {
constructors: vec![ constructors: vec![
ast::RecordConstructor { ast::RecordConstructor {
location: Span::new((), 19..31), location: Span::new((), 19..31),
@ -180,7 +180,7 @@ fn custom_type() {
parameters: vec!["a".to_string()], parameters: vec!["a".to_string()],
public: false, public: false,
typed_parameters: vec![], typed_parameters: vec![],
}), })],
) )
} }
@ -192,9 +192,9 @@ fn opaque_type() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::DataType(DataType { vec![ast::UntypedDefinition::DataType(DataType {
constructors: vec![ast::RecordConstructor { constructors: vec![ast::RecordConstructor {
location: Span::new((), 21..35), location: Span::new((), 21..35),
name: "User".to_string(), name: "User".to_string(),
@ -218,7 +218,7 @@ fn opaque_type() {
parameters: vec![], parameters: vec![],
public: true, public: true,
typed_parameters: vec![], typed_parameters: vec![],
}), })],
) )
} }
@ -228,9 +228,9 @@ fn type_alias() {
type Thing = Option<Int> type Thing = Option<Int>
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::TypeAlias(TypeAlias { vec![ast::UntypedDefinition::TypeAlias(TypeAlias {
alias: "Thing".to_string(), alias: "Thing".to_string(),
annotation: ast::Annotation::Constructor { annotation: ast::Annotation::Constructor {
location: Span::new((), 13..24), location: Span::new((), 13..24),
@ -248,7 +248,7 @@ fn type_alias() {
parameters: vec![], parameters: vec![],
public: false, public: false,
tipo: (), tipo: (),
}), })],
) )
} }
@ -258,9 +258,9 @@ fn pub_type_alias() {
pub type Me = Option<String> pub type Me = Option<String>
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::TypeAlias(TypeAlias { vec![ast::UntypedDefinition::TypeAlias(TypeAlias {
alias: "Me".to_string(), alias: "Me".to_string(),
annotation: ast::Annotation::Constructor { annotation: ast::Annotation::Constructor {
location: Span::new((), 14..28), location: Span::new((), 14..28),
@ -278,7 +278,7 @@ fn pub_type_alias() {
parameters: vec![], parameters: vec![],
public: true, public: true,
tipo: (), tipo: (),
}), })],
) )
} }
@ -288,9 +288,9 @@ fn empty_function() {
pub fn run() {} pub fn run() {}
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Todo { body: expr::UntypedExpr::Todo {
kind: ast::TodoKind::EmptyFunction, kind: ast::TodoKind::EmptyFunction,
@ -304,7 +304,7 @@ fn empty_function() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 14, end_position: 14,
}), })],
) )
} }
@ -316,9 +316,9 @@ fn plus_binop() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ast::Arg { arguments: vec![ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
label: "a".to_string(), label: "a".to_string(),
@ -353,7 +353,7 @@ fn plus_binop() {
}), }),
return_type: (), return_type: (),
end_position: 35, end_position: 35,
}), })],
) )
} }
@ -367,9 +367,9 @@ fn pipeline() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ast::Arg { arguments: vec![ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
name: "a".to_string(), name: "a".to_string(),
@ -416,7 +416,7 @@ fn pipeline() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 63, end_position: 63,
}), })],
) )
} }
@ -436,9 +436,9 @@ fn if_expression() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::If { body: expr::UntypedExpr::If {
location: Span::new((), 13..106), location: Span::new((), 13..106),
@ -513,7 +513,7 @@ fn if_expression() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 107, end_position: 107,
}), })],
) )
} }
@ -534,9 +534,9 @@ fn let_bindings() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ast::Arg { arguments: vec![ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
label: "a".to_string(), label: "a".to_string(),
@ -641,7 +641,7 @@ fn let_bindings() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 122, end_position: 122,
}), })],
) )
} }
@ -659,9 +659,9 @@ fn block() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ast::Arg { arguments: vec![ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
label: "a".to_string(), label: "a".to_string(),
@ -732,7 +732,7 @@ fn block() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 67, end_position: 67,
}), })],
) )
} }
@ -753,9 +753,9 @@ fn when() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ast::Arg { arguments: vec![ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
label: "a".to_string(), label: "a".to_string(),
@ -878,7 +878,7 @@ fn when() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 139, end_position: 139,
}), })],
) )
} }
@ -892,9 +892,9 @@ fn anonymous_function() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Sequence { body: expr::UntypedExpr::Sequence {
location: Span::new((), 25..83), location: Span::new((), 25..83),
@ -971,7 +971,7 @@ fn anonymous_function() {
}), }),
return_type: (), return_type: (),
end_position: 84, end_position: 84,
}), })],
) )
} }
@ -983,9 +983,9 @@ fn field_access() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ast::Arg { arguments: vec![ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
label: "user".to_string(), label: "user".to_string(),
@ -1016,7 +1016,7 @@ fn field_access() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 34, end_position: 34,
}), })],
) )
} }
@ -1032,9 +1032,9 @@ fn call() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Sequence { body: expr::UntypedExpr::Sequence {
location: Span::new((), 15..108), location: Span::new((), 15..108),
@ -1177,7 +1177,7 @@ fn call() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 109, end_position: 109,
}), })],
) )
} }
@ -1189,9 +1189,9 @@ fn record_update() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![ arguments: vec![
ast::Arg { ast::Arg {
arg_name: ast::ArgName::Named { arg_name: ast::ArgName::Named {
@ -1268,7 +1268,7 @@ fn record_update() {
}), }),
return_type: (), return_type: (),
end_position: 89, end_position: 89,
}), })],
) )
} }
@ -1280,9 +1280,9 @@ fn record_create_labeled() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(ast::Function { vec![ast::UntypedDefinition::Fn(ast::Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Call { body: expr::UntypedExpr::Call {
arguments: vec![ arguments: vec![
@ -1324,7 +1324,7 @@ fn record_create_labeled() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 54, end_position: 54,
}), })],
) )
} }
@ -1336,9 +1336,9 @@ fn record_create_labeled_with_field_access() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(ast::Function { vec![ast::UntypedDefinition::Fn(ast::Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Call { body: expr::UntypedExpr::Call {
arguments: vec![ arguments: vec![
@ -1384,7 +1384,7 @@ fn record_create_labeled_with_field_access() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 66, end_position: 66,
}), })],
) )
} }
@ -1396,9 +1396,9 @@ fn record_create_unlabeled() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(ast::Function { vec![ast::UntypedDefinition::Fn(ast::Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Call { body: expr::UntypedExpr::Call {
arguments: vec![ arguments: vec![
@ -1436,7 +1436,7 @@ fn record_create_unlabeled() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 40, end_position: 40,
}), })],
) )
} }
@ -1449,9 +1449,9 @@ fn parse_tuple() {
} }
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::Fn(Function { vec![ast::UntypedDefinition::Fn(Function {
arguments: vec![], arguments: vec![],
body: expr::UntypedExpr::Sequence { body: expr::UntypedExpr::Sequence {
location: Span::new((), 13..86), location: Span::new((), 13..86),
@ -1539,7 +1539,7 @@ fn parse_tuple() {
return_annotation: None, return_annotation: None,
return_type: (), return_type: (),
end_position: 87, end_position: 87,
}), })],
) )
} }
@ -1549,9 +1549,9 @@ fn plain_bytearray_literals() {
pub const my_policy_id = #[0, 170, 255] pub const my_policy_id = #[0, 170, 255]
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::ModuleConstant(ModuleConstant { vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant {
doc: None, doc: None,
location: Span::new((), 0..39), location: Span::new((), 0..39),
public: true, public: true,
@ -1562,7 +1562,7 @@ fn plain_bytearray_literals() {
bytes: vec![0, 170, 255], bytes: vec![0, 170, 255],
}), }),
tipo: (), tipo: (),
}), })],
) )
} }
@ -1572,9 +1572,9 @@ fn base16_bytearray_literals() {
pub const my_policy_id = #"00aaff" pub const my_policy_id = #"00aaff"
"#}; "#};
assert_definition( assert_definitions(
code, code,
ast::UntypedDefinition::ModuleConstant(ModuleConstant { vec![ast::UntypedDefinition::ModuleConstant(ModuleConstant {
doc: None, doc: None,
location: Span::new((), 0..34), location: Span::new((), 0..34),
public: true, public: true,
@ -1585,6 +1585,263 @@ fn base16_bytearray_literals() {
bytes: vec![0, 170, 255], bytes: vec![0, 170, 255],
}), }),
tipo: (), 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,
}),
],
) )
} }