Skip to content

Commit

Permalink
Introduce pipe operator |> (#371)
Browse files Browse the repository at this point in the history
* Add pipe operator

* Add tests

* Update docs and CHANGELOG
  • Loading branch information
ghallak committed Apr 12, 2022
1 parent cfcf0a8 commit 74aff54
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 6 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Compiler warnings for the follwing: shadowing, negative spends, division by zero, unused functions, unused includes, unused stateful annotations, unused variables, unused parameters, unused user-defined type, dead return value.
- The pipe operator |>
```
[1, 2, 3] |> List.first |> Option.is_some // Option.is_some(List.first([1, 2, 3]))
```
### Changed
- Error messages have been restructured (less newlines) to provide more unified errors. Also `pp_oneline/1` has been added.
### Removed
Expand Down
3 changes: 3 additions & 0 deletions docs/sophia_syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ Path ::= Id // Record field

BinOp ::= '||' | '&&' | '<' | '>' | '=<' | '>=' | '==' | '!='
| '::' | '++' | '+' | '-' | '*' | '/' | 'mod' | '^'
| '|>'
UnOp ::= '-' | '!'
```

Expand All @@ -245,6 +246,7 @@ UnOp ::= '-' | '!'
| `!` `&&` `\|\|` | logical operators
| `==` `!=` `<` `>` `=<` `>=` | comparison operators
| `::` `++` | list operators
| `\|>` | functional operators

## Operator precendences

Expand All @@ -261,3 +263,4 @@ In order of highest to lowest precedence.
| `<` `>` `=<` `>=` `==` `!=` | none
| `&&` | right
| `\|\|` | right
| `\|>` | left
7 changes: 6 additions & 1 deletion src/aeso_ast_infer_types.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1941,7 +1941,12 @@ infer_infix({'::', As}) ->
infer_infix({'++', As}) ->
ElemType = fresh_uvar(As),
ListType = {app_t, As, {id, As, "list"}, [ElemType]},
{fun_t, As, [], [ListType, ListType], ListType}.
{fun_t, As, [], [ListType, ListType], ListType};
infer_infix({'|>', As}) ->
ArgType = fresh_uvar(As),
ResType = fresh_uvar(As),
FunType = {fun_t, As, [], [ArgType], ResType},
{fun_t, As, [], [ArgType, FunType], ResType}.

infer_prefix({'!',As}) ->
Bool = {id, As, "bool"},
Expand Down
7 changes: 5 additions & 2 deletions src/aeso_ast_to_fcode.erl
Original file line number Diff line number Diff line change
Expand Up @@ -703,8 +703,11 @@ expr_to_fcode(Env, _Type, {block, _, Stmts}) ->
expr_to_fcode(Env, _Type, Expr = {app, _, {Op, _}, [_, _]}) when Op == '&&'; Op == '||' ->
Tree = expr_to_decision_tree(Env, Expr),
decision_tree_to_fcode(Tree);
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A, B]}) when is_atom(Op) ->
{op, Op, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]};
expr_to_fcode(Env, Type, {app, Ann, {Op, _}, [A, B]}) when is_atom(Op) ->
case Op of
'|>' -> expr_to_fcode(Env, Type, {app, Ann, B, [A]});
_ -> {op, Op, [expr_to_fcode(Env, A), expr_to_fcode(Env, B)]}
end;
expr_to_fcode(Env, _Type, {app, _Ann, {Op, _}, [A]}) when is_atom(Op) ->
case Op of
'-' -> {op, '-', [{lit, {int, 0}}, expr_to_fcode(Env, A)]};
Expand Down
7 changes: 4 additions & 3 deletions src/aeso_parser.erl
Original file line number Diff line number Diff line change
Expand Up @@ -308,17 +308,18 @@ expr() -> expr100().

expr100() ->
Expr100 = ?LAZY_P(expr100()),
Expr200 = ?LAZY_P(expr200()),
Expr150 = ?LAZY_P(expr150()),
choice(
[ ?RULE(lam_args(), keyword('=>'), body(), {lam, _2, _1, _3}) %% TODO: better location
, {'if', keyword('if'), parens(Expr100), Expr200, right(tok(else), Expr100)}
, ?RULE(Expr200, optional(right(tok(':'), type())),
, {'if', keyword('if'), parens(Expr100), Expr150, right(tok(else), Expr100)}
, ?RULE(Expr150, optional(right(tok(':'), type())),
case _2 of
none -> _1;
{ok, Type} -> {typed, get_ann(_1), _1, Type}
end)
]).

expr150() -> infixl(expr200(), binop('|>')).
expr200() -> infixr(expr300(), binop('||')).
expr300() -> infixr(expr400(), binop('&&')).
expr400() -> infix(expr500(), binop(['<', '>', '=<', '>=', '==', '!='])).
Expand Down
1 change: 1 addition & 0 deletions test/aeso_compiler_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ compilable_contracts() ->
"using_namespace",
"assign_patterns",
"patterns_guards",
"pipe_operator",
"test" % Custom general-purpose test file. Keep it last on the list.
].

Expand Down
18 changes: 18 additions & 0 deletions test/contracts/pipe_operator.aes
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
contract Main =
function is_negative(x : int) =
if (x < 0)
true
else
false

function inc_by_one(x : int) = x + 1
function inc_by_two(x : int) = x + 2

type state = bool

entrypoint init(x : int) = x
|> inc_by_one
|> inc_by_one
|> inc_by_two
|> ((x) => x * 5)
|> is_negative

0 comments on commit 74aff54

Please sign in to comment.