diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index 1f1374fc..e5e75d81 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -647,6 +647,15 @@ impl Annotation { } } + pub fn boolean(location: Span) -> Self { + Annotation::Constructor { + name: "Bool".to_string(), + module: None, + arguments: vec![], + location, + } + } + pub fn is_logically_equal(&self, other: &Annotation) -> bool { match self { Annotation::Constructor { diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index e42c225c..32c14c29 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -404,6 +404,7 @@ impl TypedExpr { pub enum FnStyle { Plain, Capture, + BinOp(BinOp), } #[derive(Debug, Clone, PartialEq)] diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 31341e1c..338175e1 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -773,6 +773,14 @@ impl<'comments> Formatter<'comments> { .. } => self.fn_capture(body), + UntypedExpr::Fn { + fn_style: FnStyle::BinOp(op), + body, + .. + } => { + unimplemented!() + } + UntypedExpr::Fn { fn_style: FnStyle::Plain, return_annotation, diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index 806704a9..b7f08fb0 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -940,6 +940,57 @@ pub fn expr_parser( }, ); + let anon_binop_parser = select! { + Token::Greater => BinOp::GtInt, + } + .map_with_span(|name, location| { + let arguments = vec![ + ast::Arg { + arg_name: ast::ArgName::Named { + name: "left".to_string(), + label: "left".to_string(), + location, + is_validator_param: false, + }, + annotation: Some(ast::Annotation::boolean(location)), + location, + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "right".to_string(), + label: "right".to_string(), + location, + is_validator_param: false, + }, + annotation: Some(ast::Annotation::boolean(location)), + location, + tipo: (), + }, + ]; + + let body = expr::UntypedExpr::BinOp { + location, + name, + left: Box::new(expr::UntypedExpr::Var { + location, + name: "left".to_string(), + }), + right: Box::new(expr::UntypedExpr::Var { + location, + name: "right".to_string(), + }), + }; + + expr::UntypedExpr::Fn { + arguments, + body: Box::new(body), + return_annotation: Some(ast::Annotation::boolean(location)), + fn_style: expr::FnStyle::BinOp(name), + location, + } + }); + let when_clause_parser = pattern_parser() .then( just(Token::Vbar) @@ -1083,6 +1134,7 @@ pub fn expr_parser( bytearray, list_parser, anon_fn_parser, + anon_binop_parser, block_parser, when_parser, let_parser, diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 9cd9bae0..14c14e1b 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -3667,3 +3667,114 @@ fn int_parsing_numeric_underscore() { })], ) } + +#[test] +fn first_class_binop() { + use expr::UntypedExpr::*; + + let code = indoc! {r#" + fn foo() { + compare_with(a, >, b) + } + "#}; + + assert_definitions( + code, + vec![ast::Definition::Fn(Function { + arguments: vec![], + body: Call { + arguments: vec![ + ast::CallArg { + label: None, + location: Span::new((), 26..27), + value: Var { + location: Span::new((), 26..27), + name: "a".to_string(), + }, + }, + ast::CallArg { + label: None, + location: Span::new((), 29..30), + value: Fn { + location: Span::new((), 29..30), + fn_style: expr::FnStyle::BinOp(ast::BinOp::GtInt), + arguments: vec![ + ast::Arg { + arg_name: ast::ArgName::Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 29..30), + is_validator_param: false, + }, + location: Span::new((), 29..30), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 29..30), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ast::Arg { + arg_name: ast::ArgName::Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 29..30), + is_validator_param: false, + }, + location: Span::new((), 29..30), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 29..30), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 29..30), + name: ast::BinOp::GtInt, + left: Box::new(Var { + location: Span::new((), 29..30), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 29..30), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 29..30), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + ast::CallArg { + label: None, + location: Span::new((), 32..33), + value: Var { + location: Span::new((), 32..33), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 13..25), + name: "compare_with".to_string(), + }), + location: Span::new((), 13..34), + }, + doc: None, + location: Span::new((), 0..8), + name: "foo".to_string(), + public: false, + return_annotation: None, + return_type: (), + end_position: 35, + can_error: true, + })], + ); +}