From ba911d48ea86a764e38281d6d1a36cd6af9139b0 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Jun 2023 07:26:46 +0200 Subject: [PATCH 1/6] Refactor 'is_capture' field on function expressions. Refactored into an enum to make it easier to extend with a new variant to support binary operators. --- crates/aiken-lang/src/expr.rs | 9 +++++++- crates/aiken-lang/src/format.rs | 7 +++--- crates/aiken-lang/src/parser.rs | 4 ++-- crates/aiken-lang/src/tests/parser.rs | 6 ++--- crates/aiken-lang/src/tipo/expr.rs | 33 +++++++++++++++++---------- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/crates/aiken-lang/src/expr.rs b/crates/aiken-lang/src/expr.rs index d669f171..e42c225c 100644 --- a/crates/aiken-lang/src/expr.rs +++ b/crates/aiken-lang/src/expr.rs @@ -399,6 +399,13 @@ impl TypedExpr { } } +// Represent how a function was written so that we can format it back. +#[derive(Debug, Clone, PartialEq, Copy)] +pub enum FnStyle { + Plain, + Capture, +} + #[derive(Debug, Clone, PartialEq)] pub enum UntypedExpr { Int { @@ -424,7 +431,7 @@ pub enum UntypedExpr { Fn { location: Span, - is_capture: bool, + fn_style: FnStyle, arguments: Vec>, body: Box, return_annotation: Option, diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 7bd755aa..31341e1c 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -8,7 +8,7 @@ use crate::{ Use, Validator, CAPTURE_VARIABLE, }, docvec, - expr::{UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR}, + expr::{FnStyle, UntypedExpr, DEFAULT_ERROR_STR, DEFAULT_TODO_STR}, parser::{ extra::{Comment, ModuleExtra}, token::Base, @@ -768,12 +768,13 @@ impl<'comments> Formatter<'comments> { UntypedExpr::UnOp { value, op, .. } => self.un_op(value, op), UntypedExpr::Fn { - is_capture: true, + fn_style: FnStyle::Capture, body, .. } => self.fn_capture(body), UntypedExpr::Fn { + fn_style: FnStyle::Plain, return_annotation, arguments: args, body, @@ -1093,7 +1094,7 @@ impl<'comments> Formatter<'comments> { let comments = self.pop_comments(expr.location().start); let doc = match expr { UntypedExpr::Fn { - is_capture: true, + fn_style: FnStyle::Capture, body, .. } => self.pipe_capture_right_hand_side(body), diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index b24a738c..806704a9 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -935,7 +935,7 @@ pub fn expr_parser( arguments, body: Box::new(body), location: span, - is_capture: false, + fn_style: expr::FnStyle::Plain, return_annotation, }, ); @@ -1205,7 +1205,7 @@ pub fn expr_parser( } else { expr::UntypedExpr::Fn { location: call.location(), - is_capture: true, + fn_style: expr::FnStyle::Capture, arguments: holes, body: Box::new(call), return_annotation: None, diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 19479451..9cd9bae0 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -1369,7 +1369,7 @@ fn anonymous_function() { location: Span::new((), 25..67), value: Box::new(expr::UntypedExpr::Fn { location: Span::new((), 39..67), - is_capture: false, + fn_style: expr::FnStyle::Plain, arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "a".to_string(), @@ -1547,7 +1547,7 @@ fn call() { location: Span::new((), 37..82), value: Box::new(expr::UntypedExpr::Fn { location: Span::new((), 53..82), - is_capture: true, + fn_style: expr::FnStyle::Capture, arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "_capture__0".to_string(), @@ -1574,7 +1574,7 @@ fn call() { location: Span::new((), 65..81), value: expr::UntypedExpr::Fn { location: Span::new((), 65..81), - is_capture: false, + fn_style: expr::FnStyle::Plain, arguments: vec![ast::Arg { arg_name: ast::ArgName::Named { label: "y".to_string(), diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index aa613e71..f4a39b06 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -12,7 +12,7 @@ use crate::{ UntypedRecordUpdateArg, }, builtins::{bool, byte_array, function, int, list, string, tuple}, - expr::{TypedExpr, UntypedExpr}, + expr::{FnStyle, TypedExpr, UntypedExpr}, format, tipo::fields::FieldMap, }; @@ -220,12 +220,19 @@ impl<'a, 'b> ExprTyper<'a, 'b> { UntypedExpr::Fn { location, - is_capture, + fn_style, arguments: args, body, return_annotation, .. - } => self.infer_fn(args, &[], *body, is_capture, return_annotation, location), + } => self.infer_fn( + args, + &[], + *body, + fn_style == FnStyle::Capture, + return_annotation, + location, + ), UntypedExpr::If { location, @@ -1011,17 +1018,19 @@ impl<'a, 'b> ExprTyper<'a, 'b> { body, return_annotation, location, - is_capture: false, + fn_style, .. }, - ) if expected_arguments.len() == arguments.len() => self.infer_fn( - arguments, - expected_arguments, - *body, - false, - return_annotation, - location, - ), + ) if fn_style != FnStyle::Capture && expected_arguments.len() == arguments.len() => { + self.infer_fn( + arguments, + expected_arguments, + *body, + false, + return_annotation, + location, + ) + } // Otherwise just perform normal type inference. (_, value) => self.infer(value), From ec942302949b41b83f99f60ce9d93633b67cf8f7 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Jun 2023 07:36:11 +0200 Subject: [PATCH 2/6] Extend parser to accept anonymous binop as expressions. This is simply a syntactic sugar which desugarize to a function call with two arguments mapped to the specified binary operator. Only works for '>' at this stage as a PoC, extending to all binop in the next commit. --- crates/aiken-lang/src/ast.rs | 9 +++ crates/aiken-lang/src/expr.rs | 1 + crates/aiken-lang/src/format.rs | 8 ++ crates/aiken-lang/src/parser.rs | 52 ++++++++++++ crates/aiken-lang/src/tests/parser.rs | 111 ++++++++++++++++++++++++++ 5 files changed, 181 insertions(+) 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, + })], + ); +} From d0b4c1c3b52c997cf498dbfd66a30a8620b396c9 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Jun 2023 07:57:30 +0200 Subject: [PATCH 3/6] Add remaining boolean comparison operator to anon binop parser. Nothing to see here as they all have the same signature. Implementing arithmetic bin-operators and boolean logic operators will require some more logic. --- crates/aiken-lang/src/parser.rs | 5 + crates/aiken-lang/src/tests/parser.rs | 576 ++++++++++++++++++++++---- 2 files changed, 511 insertions(+), 70 deletions(-) diff --git a/crates/aiken-lang/src/parser.rs b/crates/aiken-lang/src/parser.rs index b7f08fb0..9129bbf8 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -941,7 +941,12 @@ pub fn expr_parser( ); let anon_binop_parser = select! { + Token::EqualEqual => BinOp::Eq, + Token::NotEqual => BinOp::NotEq, + Token::Less => BinOp::LtInt, + Token::LessEqual => BinOp::LtEqInt, Token::Greater => BinOp::GtInt, + Token::GreaterEqual => BinOp::GtEqInt, } .map_with_span(|name, location| { let arguments = vec![ diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 14c14e1b..14bc6047 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -3670,11 +3670,17 @@ fn int_parsing_numeric_underscore() { #[test] fn first_class_binop() { + use ast::{Arg, ArgName::*, BinOp::*, CallArg}; use expr::UntypedExpr::*; let code = indoc! {r#" fn foo() { compare_with(a, >, b) + compare_with(a, >=, b) + compare_with(a, <, b) + compare_with(a, <=, b) + compare_with(a, ==, b) + compare_with(a, !=, b) } "#}; @@ -3682,90 +3688,520 @@ fn first_class_binop() { 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, - }, + body: Sequence { + location: Span::new((), 13..158), + expressions: vec![ + 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), - annotation: Some(ast::Annotation::Constructor { + fn_style: expr::FnStyle::BinOp(ast::BinOp::GtInt), + arguments: vec![ + Arg { + arg_name: 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: (), + }, + Arg { + arg_name: 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: 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![], }), - 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), + }, + 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), + }, + Call { + arguments: vec![ + ast::CallArg { + label: None, + location: Span::new((), 50..51), + value: Var { + location: Span::new((), 50..51), + name: "a".to_string(), + }, + }, + ast::CallArg { + label: None, + location: Span::new((), 53..55), + value: Fn { + location: Span::new((), 53..55), + fn_style: expr::FnStyle::BinOp(GtEqInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 53..55), + is_validator_param: false, + }, + location: Span::new((), 53..55), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 53..55), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 53..55), + is_validator_param: false, + }, + location: Span::new((), 53..55), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 53..55), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 53..55), + name: GtEqInt, + left: Box::new(Var { + location: Span::new((), 53..55), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 53..55), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 53..55), 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((), 57..58), + value: Var { + location: Span::new((), 57..58), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 37..49), + name: "compare_with".to_string(), + }), + location: Span::new((), 37..59), }, - ast::CallArg { - label: None, - location: Span::new((), 32..33), - value: Var { - location: Span::new((), 32..33), - name: "b".to_string(), - }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 75..76), + value: Var { + location: Span::new((), 75..76), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 78..79), + value: Fn { + location: Span::new((), 78..79), + fn_style: expr::FnStyle::BinOp(LtInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 78..79), + is_validator_param: false, + }, + location: Span::new((), 78..79), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 78..79), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 78..79), + is_validator_param: false, + }, + location: Span::new((), 78..79), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 78..79), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 78..79), + name: LtInt, + left: Box::new(Var { + location: Span::new((), 78..79), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 78..79), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 78..79), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 81..82), + value: Var { + location: Span::new((), 81..82), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 62..74), + name: "compare_with".to_string(), + }), + location: Span::new((), 62..83), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 99..100), + value: Var { + location: Span::new((), 99..100), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 102..104), + value: Fn { + location: Span::new((), 102..104), + fn_style: expr::FnStyle::BinOp(LtEqInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 102..104), + is_validator_param: false, + }, + location: Span::new((), 102..104), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 102..104), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 102..104), + is_validator_param: false, + }, + location: Span::new((), 102..104), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 102..104), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 102..104), + name: LtEqInt, + left: Box::new(Var { + location: Span::new((), 102..104), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 102..104), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 102..104), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 106..107), + value: Var { + location: Span::new((), 106..107), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 86..98), + name: "compare_with".to_string(), + }), + location: Span::new((), 86..108), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 124..125), + value: Var { + location: Span::new((), 124..125), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 127..129), + value: Fn { + location: Span::new((), 127..129), + fn_style: expr::FnStyle::BinOp(Eq), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 127..129), + is_validator_param: false, + }, + location: Span::new((), 127..129), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 127..129), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 127..129), + is_validator_param: false, + }, + location: Span::new((), 127..129), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 127..129), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 127..129), + name: Eq, + left: Box::new(Var { + location: Span::new((), 127..129), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 127..129), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 127..129), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 131..132), + value: Var { + location: Span::new((), 131..132), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 111..123), + name: "compare_with".to_string(), + }), + location: Span::new((), 111..133), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 149..150), + value: Var { + location: Span::new((), 149..150), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 152..154), + value: Fn { + location: Span::new((), 152..154), + fn_style: expr::FnStyle::BinOp(NotEq), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 152..154), + is_validator_param: false, + }, + location: Span::new((), 152..154), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 152..154), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 152..154), + is_validator_param: false, + }, + location: Span::new((), 152..154), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 152..154), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 152..154), + name: NotEq, + left: Box::new(Var { + location: Span::new((), 152..154), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 152..154), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 152..154), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 156..157), + value: Var { + location: Span::new((), 156..157), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 136..148), + name: "compare_with".to_string(), + }), + location: Span::new((), 136..158), }, ], - 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), @@ -3773,7 +4209,7 @@ fn first_class_binop() { public: false, return_annotation: None, return_type: (), - end_position: 35, + end_position: 159, can_error: true, })], ); From 91f03abb7b15229ca43bb6913121318ee2bf5ace Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Jun 2023 08:35:22 +0200 Subject: [PATCH 4/6] Support all binary operator in the anonymous binop parser. --- crates/aiken-lang/src/ast.rs | 9 + crates/aiken-lang/src/parser.rs | 45 +- crates/aiken-lang/src/tests/parser.rs | 650 ++++++++++++++++++++++++-- 3 files changed, 666 insertions(+), 38 deletions(-) diff --git a/crates/aiken-lang/src/ast.rs b/crates/aiken-lang/src/ast.rs index e5e75d81..1568f425 100644 --- a/crates/aiken-lang/src/ast.rs +++ b/crates/aiken-lang/src/ast.rs @@ -656,6 +656,15 @@ impl Annotation { } } + pub fn int(location: Span) -> Self { + Annotation::Constructor { + name: "Int".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/parser.rs b/crates/aiken-lang/src/parser.rs index 9129bbf8..581cf435 100644 --- a/crates/aiken-lang/src/parser.rs +++ b/crates/aiken-lang/src/parser.rs @@ -947,8 +947,32 @@ pub fn expr_parser( Token::LessEqual => BinOp::LtEqInt, Token::Greater => BinOp::GtInt, Token::GreaterEqual => BinOp::GtEqInt, + Token::VbarVbar => BinOp::Or, + Token::AmperAmper => BinOp::And, + Token::Plus => BinOp::AddInt, + Token::Minus => BinOp::SubInt, + Token::Slash => BinOp::DivInt, + Token::Star => BinOp::MultInt, + Token::Percent => BinOp::ModInt, } .map_with_span(|name, location| { + use BinOp::*; + + let arg_annotation = match name { + Or | And => Some(ast::Annotation::boolean(location)), + Eq | NotEq => None, + LtInt | LtEqInt | GtInt | GtEqInt | AddInt | SubInt | MultInt | DivInt | ModInt => { + Some(ast::Annotation::int(location)) + } + }; + + let return_annotation = match name { + Or | And | Eq | NotEq | LtInt | LtEqInt | GtInt | GtEqInt => { + Some(ast::Annotation::boolean(location)) + } + AddInt | SubInt | MultInt | DivInt | ModInt => Some(ast::Annotation::int(location)), + }; + let arguments = vec![ ast::Arg { arg_name: ast::ArgName::Named { @@ -957,7 +981,7 @@ pub fn expr_parser( location, is_validator_param: false, }, - annotation: Some(ast::Annotation::boolean(location)), + annotation: arg_annotation.clone(), location, tipo: (), }, @@ -968,7 +992,7 @@ pub fn expr_parser( location, is_validator_param: false, }, - annotation: Some(ast::Annotation::boolean(location)), + annotation: arg_annotation, location, tipo: (), }, @@ -990,7 +1014,7 @@ pub fn expr_parser( expr::UntypedExpr::Fn { arguments, body: Box::new(body), - return_annotation: Some(ast::Annotation::boolean(location)), + return_annotation, fn_style: expr::FnStyle::BinOp(name), location, } @@ -1296,7 +1320,20 @@ pub fn expr_parser( // Negate let op = choice(( just(Token::Bang).to(UnOp::Not), - just(Token::Minus).to(UnOp::Negate), + just(Token::Minus) + // NOTE: Prevent conflict with usage for '-' as a standalone binary op. + // This will make '-' parse when used as standalone binop in a function call. + // For example: + // + // foo(a, -, b) + // + // but it'll fail in a let-binding: + // + // let foo = - + // + // which seems acceptable. + .then_ignore(just(Token::Comma).not().rewind()) + .to(UnOp::Negate), )); let unary = op diff --git a/crates/aiken-lang/src/tests/parser.rs b/crates/aiken-lang/src/tests/parser.rs index 14bc6047..3e4a0cab 100644 --- a/crates/aiken-lang/src/tests/parser.rs +++ b/crates/aiken-lang/src/tests/parser.rs @@ -3681,6 +3681,13 @@ fn first_class_binop() { compare_with(a, <=, b) compare_with(a, ==, b) compare_with(a, !=, b) + combine_with(a, &&, b) + combine_with(a, ||, b) + compute_with(a, +, b) + compute_with(a, -, b) + compute_with(a, /, b) + compute_with(a, *, b) + compute_with(a, %, b) } "#}; @@ -3689,7 +3696,7 @@ fn first_class_binop() { vec![ast::Definition::Fn(Function { arguments: vec![], body: Sequence { - location: Span::new((), 13..158), + location: Span::new((), 13..328), expressions: vec![ Call { arguments: vec![ @@ -3719,7 +3726,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 29..30), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3735,7 +3742,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 29..30), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3804,7 +3811,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 53..55), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3820,7 +3827,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 53..55), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3889,7 +3896,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 78..79), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3905,7 +3912,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 78..79), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3974,7 +3981,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 102..104), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -3990,7 +3997,7 @@ fn first_class_binop() { annotation: Some(ast::Annotation::Constructor { location: Span::new((), 102..104), module: None, - name: "Bool".to_string(), + name: "Int".to_string(), arguments: vec![], }), tipo: (), @@ -4056,12 +4063,7 @@ fn first_class_binop() { is_validator_param: false, }, location: Span::new((), 127..129), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 127..129), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), + annotation: None, tipo: (), }, Arg { @@ -4072,12 +4074,7 @@ fn first_class_binop() { is_validator_param: false, }, location: Span::new((), 127..129), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 127..129), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), + annotation: None, tipo: (), }, ], @@ -4141,12 +4138,7 @@ fn first_class_binop() { is_validator_param: false, }, location: Span::new((), 152..154), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 152..154), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), + annotation: None, tipo: (), }, Arg { @@ -4157,12 +4149,7 @@ fn first_class_binop() { is_validator_param: false, }, location: Span::new((), 152..154), - annotation: Some(ast::Annotation::Constructor { - location: Span::new((), 152..154), - module: None, - name: "Bool".to_string(), - arguments: vec![], - }), + annotation: None, tipo: (), }, ], @@ -4201,6 +4188,601 @@ fn first_class_binop() { }), location: Span::new((), 136..158), }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 174..175), + value: Var { + location: Span::new((), 174..175), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 177..179), + value: Fn { + location: Span::new((), 177..179), + fn_style: expr::FnStyle::BinOp(And), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 177..179), + is_validator_param: false, + }, + location: Span::new((), 177..179), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 177..179), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 177..179), + is_validator_param: false, + }, + location: Span::new((), 177..179), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 177..179), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 177..179), + name: And, + left: Box::new(Var { + location: Span::new((), 177..179), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 177..179), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 177..179), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 181..182), + value: Var { + location: Span::new((), 181..182), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 161..173), + name: "combine_with".to_string(), + }), + location: Span::new((), 161..183), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 199..200), + value: Var { + location: Span::new((), 199..200), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 202..204), + value: Fn { + location: Span::new((), 202..204), + fn_style: expr::FnStyle::BinOp(Or), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 202..204), + is_validator_param: false, + }, + location: Span::new((), 202..204), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 202..204), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 202..204), + is_validator_param: false, + }, + location: Span::new((), 202..204), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 202..204), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 202..204), + name: Or, + left: Box::new(Var { + location: Span::new((), 202..204), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 202..204), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 202..204), + module: None, + name: "Bool".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 206..207), + value: Var { + location: Span::new((), 206..207), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 186..198), + name: "combine_with".to_string(), + }), + location: Span::new((), 186..208), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 224..225), + value: Var { + location: Span::new((), 224..225), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 227..228), + value: Fn { + location: Span::new((), 227..228), + fn_style: expr::FnStyle::BinOp(AddInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 227..228), + is_validator_param: false, + }, + location: Span::new((), 227..228), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 227..228), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 227..228), + is_validator_param: false, + }, + location: Span::new((), 227..228), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 227..228), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 227..228), + name: AddInt, + left: Box::new(Var { + location: Span::new((), 227..228), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 227..228), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 227..228), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 230..231), + value: Var { + location: Span::new((), 230..231), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 211..223), + name: "compute_with".to_string(), + }), + location: Span::new((), 211..232), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 248..249), + value: Var { + location: Span::new((), 248..249), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 251..252), + value: Fn { + location: Span::new((), 251..252), + fn_style: expr::FnStyle::BinOp(SubInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 251..252), + is_validator_param: false, + }, + location: Span::new((), 251..252), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 251..252), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 251..252), + is_validator_param: false, + }, + location: Span::new((), 251..252), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 251..252), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 251..252), + name: SubInt, + left: Box::new(Var { + location: Span::new((), 251..252), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 251..252), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 251..252), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 254..255), + value: Var { + location: Span::new((), 254..255), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 235..247), + name: "compute_with".to_string(), + }), + location: Span::new((), 235..256), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 272..273), + value: Var { + location: Span::new((), 272..273), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 275..276), + value: Fn { + location: Span::new((), 275..276), + fn_style: expr::FnStyle::BinOp(DivInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 275..276), + is_validator_param: false, + }, + location: Span::new((), 275..276), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 275..276), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 275..276), + is_validator_param: false, + }, + location: Span::new((), 275..276), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 275..276), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 275..276), + name: DivInt, + left: Box::new(Var { + location: Span::new((), 275..276), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 275..276), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 275..276), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 278..279), + value: Var { + location: Span::new((), 278..279), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 259..271), + name: "compute_with".to_string(), + }), + location: Span::new((), 259..280), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 296..297), + value: Var { + location: Span::new((), 296..297), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 299..300), + value: Fn { + location: Span::new((), 299..300), + fn_style: expr::FnStyle::BinOp(MultInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 299..300), + is_validator_param: false, + }, + location: Span::new((), 299..300), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 299..300), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 299..300), + is_validator_param: false, + }, + location: Span::new((), 299..300), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 299..300), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 299..300), + name: MultInt, + left: Box::new(Var { + location: Span::new((), 299..300), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 299..300), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 299..300), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 302..303), + value: Var { + location: Span::new((), 302..303), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 283..295), + name: "compute_with".to_string(), + }), + location: Span::new((), 283..304), + }, + Call { + arguments: vec![ + CallArg { + label: None, + location: Span::new((), 320..321), + value: Var { + location: Span::new((), 320..321), + name: "a".to_string(), + }, + }, + CallArg { + label: None, + location: Span::new((), 323..324), + value: Fn { + location: Span::new((), 323..324), + fn_style: expr::FnStyle::BinOp(ModInt), + arguments: vec![ + Arg { + arg_name: Named { + name: "left".to_string(), + label: "left".to_string(), + location: Span::new((), 323..324), + is_validator_param: false, + }, + location: Span::new((), 323..324), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 323..324), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + Arg { + arg_name: Named { + name: "right".to_string(), + label: "right".to_string(), + location: Span::new((), 323..324), + is_validator_param: false, + }, + location: Span::new((), 323..324), + annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 323..324), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + tipo: (), + }, + ], + body: Box::new(BinOp { + location: Span::new((), 323..324), + name: ModInt, + left: Box::new(Var { + location: Span::new((), 323..324), + name: "left".to_string(), + }), + right: Box::new(Var { + location: Span::new((), 323..324), + name: "right".to_string(), + }), + }), + return_annotation: Some(ast::Annotation::Constructor { + location: Span::new((), 323..324), + module: None, + name: "Int".to_string(), + arguments: vec![], + }), + }, + }, + CallArg { + label: None, + location: Span::new((), 326..327), + value: Var { + location: Span::new((), 326..327), + name: "b".to_string(), + }, + }, + ], + fun: Box::new(Var { + location: Span::new((), 307..319), + name: "compute_with".to_string(), + }), + location: Span::new((), 307..328), + }, ], }, doc: None, @@ -4209,7 +4791,7 @@ fn first_class_binop() { public: false, return_annotation: None, return_type: (), - end_position: 159, + end_position: 329, can_error: true, })], ); From 4252ee637398da386c74240af614997d9d36c97d Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Jun 2023 08:44:59 +0200 Subject: [PATCH 5/6] Implement formatter for anon binop. --- crates/aiken-lang/src/format.rs | 33 +++++++++++++-------------- crates/aiken-lang/src/tests/format.rs | 23 +++++++++++++++++++ 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/crates/aiken-lang/src/format.rs b/crates/aiken-lang/src/format.rs index 338175e1..e345875c 100644 --- a/crates/aiken-lang/src/format.rs +++ b/crates/aiken-lang/src/format.rs @@ -775,11 +775,8 @@ impl<'comments> Formatter<'comments> { UntypedExpr::Fn { fn_style: FnStyle::BinOp(op), - body, .. - } => { - unimplemented!() - } + } => op.to_doc(), UntypedExpr::Fn { fn_style: FnStyle::Plain, @@ -1070,7 +1067,9 @@ impl<'comments> Formatter<'comments> { let right = self.expr(right); self.operator_side(left, precedence, left_precedence) + .append(" ") .append(name) + .append(" ") .append(self.operator_side(right, precedence, right_precedence - 1)) } @@ -1726,19 +1725,19 @@ impl<'a> Documentable<'a> for &'a UnqualifiedImport { impl<'a> Documentable<'a> for &'a BinOp { fn to_doc(self) -> Document<'a> { match self { - BinOp::And => " && ", - BinOp::Or => " || ", - BinOp::LtInt => " < ", - BinOp::LtEqInt => " <= ", - BinOp::Eq => " == ", - BinOp::NotEq => " != ", - BinOp::GtEqInt => " >= ", - BinOp::GtInt => " > ", - BinOp::AddInt => " + ", - BinOp::SubInt => " - ", - BinOp::MultInt => " * ", - BinOp::DivInt => " / ", - BinOp::ModInt => " % ", + BinOp::And => "&&", + BinOp::Or => "||", + BinOp::LtInt => "<", + BinOp::LtEqInt => "<=", + BinOp::Eq => "==", + BinOp::NotEq => "!=", + BinOp::GtEqInt => ">=", + BinOp::GtInt => ">", + BinOp::AddInt => "+", + BinOp::SubInt => "-", + BinOp::MultInt => "*", + BinOp::DivInt => "/", + BinOp::ModInt => "%", } .to_doc() } diff --git a/crates/aiken-lang/src/tests/format.rs b/crates/aiken-lang/src/tests/format.rs index 9154e290..79ca88d4 100644 --- a/crates/aiken-lang/src/tests/format.rs +++ b/crates/aiken-lang/src/tests/format.rs @@ -864,3 +864,26 @@ fn hex_and_numeric_underscore() { assert_fmt(src, src); } + +#[test] +fn first_class_binop() { + let src = indoc! { r#" + fn foo() { + compare_with(a, >, b) + compare_with(a, >=, b) + compare_with(a, <, b) + compare_with(a, <=, b) + compare_with(a, ==, b) + compare_with(a, !=, b) + combine_with(a, &&, b) + combine_with(a, ||, b) + compute_with(a, +, b) + compute_with(a, -, b) + compute_with(a, /, b) + compute_with(a, *, b) + compute_with(a, %, b) + } + "#}; + + assert_fmt(src, src); +} From 41b2bf1c7e526b1ba91daafaac359562a7425aba Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sat, 17 Jun 2023 17:21:43 +0200 Subject: [PATCH 6/6] Fill-in CHANGELOG. --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8507849..85540b5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## v1.0.11-alpha - unreleased + +### Added + +- **aiken-lang**: Binary operator are now treated like first-class citizen in + expressions. In particular, they can be used as function arguments directly: + + ``` + compare_with(a, >=, b) == compare_with(a, fn(l, r) { l >= r }, b) + ``` + ## v1.0.10-alpha - 2023-06-13 ### Added