Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fast pipe sugar #1999

Merged
merged 8 commits into from
Jul 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions formatTest/typeCheckedTests/expected_output/fastPipe.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
let (|.) = (x, y) => x + y;

let a = 1;
let b = 2;
let c = 3;

/* parses as 10 < (a->b->c) */
let t1: bool = 10 < a->b->c;

type coordinate = {
x: int,
y: int,
};
let coord = {x: 1, y: 1};

/* parses as (coord.x)->a->b->c */
let t2: int = coord.x->a->b->c;

let (|.) = (x, y) => x || y;

let a = true;
let b = false;
let c = true;

/* parses as !(a->b->c) */
let t3: bool = ! a->b->c;
23 changes: 23 additions & 0 deletions formatTest/typeCheckedTests/input/fastPipe.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
let (|.) = (x, y) => x + y;

let a = 1;
let b = 2;
let c = 3;

/* parses as 10 < (a->b->c) */
let t1: bool = 10 < a->b->c;

type coordinate = {x: int, y: int};
let coord = {x: 1, y: 1};

/* parses as (coord.x)->a->b->c */
let t2: int = coord.x->a->b->c;

let (|.) = (x, y) => x || y;

let a = true;
let b = false;
let c = true;

/* parses as !(a->b->c) */
let t3: bool = !a->b->c;
40 changes: 40 additions & 0 deletions formatTest/unit_tests/expected_output/fastPipe.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
foo->f->g->h;

bar->f->g->h;

compilation
->Plugin.buildAssets
->Js.Json.stringify
->Node.Fs.writeFileAsUtf8Sync(_, path);

foo->bar->baz >>= monadicFunction |> bind;

compilation
->Plugin.buildAssets
->Js.Json.stringify
|> Cohttp_lwt_body.to_string
>|= (
body =>
Printf.sprintf(
"okokok",
uri,
meth,
headers,
body,
)
)
>>= (
body =>
Server.respond_string(~status, ~body, ())
);

x + y + foo->bar->baz;
x + y * foo->bar->baz;
x && y || foo->bar->baz;

x < foo->bar->baz;
foo !== bar->baz;
x |> y >>= foo->bar->baz;
let m = f => foo->bar->f;

obj##x->foo->bar;
44 changes: 44 additions & 0 deletions formatTest/unit_tests/input/fastPipe.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
foo |. f |. g |. h;

bar->f->g->h;

compilation
->Plugin.buildAssets
->Js.Json.stringify
->Node.Fs.writeFileAsUtf8Sync(_, path);

foo->bar->baz >>= monadicFunction |> bind;

compilation
->Plugin.buildAssets
->Js.Json.stringify
|> Cohttp_lwt_body.to_string
>|= (
body =>
Printf.sprintf(
"okokok",
uri,
meth,
headers,
body,
)
)
>>= (
body =>
Server.respond_string(
~status,
~body,
(),
)
);

x + y + foo->bar->baz;
x + y * foo->bar->baz;
x && y || foo->bar->baz;

x < foo->bar->baz;
foo !== bar->baz;
x |> y >>= foo->bar->baz;
let m = f => foo->bar->f;

obj##x->foo->bar;
1 change: 1 addition & 0 deletions src/reason-parser/jbuild
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
(wrapped false)
(modules
(lexer_warning
reason_ast
reason_syntax_util
reason_comment
reason_layout
Expand Down
14 changes: 14 additions & 0 deletions src/reason-parser/reason_ast.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
open Ast_404
open Parsetree

let processFastPipe e =
match e.pexp_desc with
| Pexp_apply(
{pexp_desc = Pexp_ident({txt = Longident.Lident("|."); loc})} as identExp,
args
) ->
let pipe = {identExp with pexp_desc =
Pexp_ident {txt = Longident.Lident("->"); loc}
} in
{e with pexp_desc = Pexp_apply(pipe, args) }
| _ -> e
1 change: 1 addition & 0 deletions src/reason-parser/reason_ast.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val processFastPipe : Ast_404.Parsetree.expression -> Ast_404.Parsetree.expression
8 changes: 7 additions & 1 deletion src/reason-parser/reason_parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,7 @@ conflicts.

(* PREFIXOP and BANG precedence *)
%nonassoc below_DOT_AND_SHARP (* practically same as below_SHARP but we convey purpose *)
%left MINUSGREATER
%nonassoc SHARP (* simple_expr/toplevel_directive *)
%left SHARPOP
%nonassoc below_DOT
Expand Down Expand Up @@ -2831,7 +2832,11 @@ mark_position_exp
mkexp_cons loc_colon (mkexp ~ghost:true ~loc (Pexp_tuple[$5;$7])) loc
}
| E as_loc(infix_operator) expr
{ mkinfix $1 $2 $3 }
{ let op = match $2.txt with
| "->" -> {$2 with txt = "|."}
| _ -> $2
in mkinfix $1 op $3
}
| as_loc(subtractive) expr %prec prec_unary
{ mkuminus $1 $2 }
| as_loc(additive) expr %prec prec_unary
Expand Down Expand Up @@ -4528,6 +4533,7 @@ val_ident:
operator swapping requires that we express that as != *)
| LESSDOTDOTGREATER { "<..>" }
| GREATER GREATER { ">>" }
| MINUSGREATER { "->" }
;

operator:
Expand Down
86 changes: 73 additions & 13 deletions src/reason-parser/reason_pprint_ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,9 @@ let isSimplePrefixToken s = match printedStringAndFixity s with
`CustomPrecedence`) the string name of a custom rule precedence declared
using %prec *)
let rules = [
[
(TokenPrecedence, (fun s -> (Left, s = "->")));
];
[
(TokenPrecedence, (fun s -> (Nonassoc, isSimplePrefixToken s)));
];
Expand Down Expand Up @@ -575,7 +578,7 @@ let rules = [
else
s.[0] == '+'
)));
(TokenPrecedence ,(fun s -> (Left, s.[0] == '-' )));
(TokenPrecedence ,(fun s -> (Left, s.[0] == '-' && s <> "->" )));
(TokenPrecedence ,(fun s -> (Left, s = "!" )));
];
[
Expand Down Expand Up @@ -2122,6 +2125,40 @@ let rec computeInfixChain = function

let equalityOperators = ["!="; "!=="; "==="; "=="; ">="; "<="; "<"; ">"]

(* Takes a list of layouts and provides beautiful printing for fast pipe.
* Prints
* [atom "foo"; atom "->"; atom "f"; atom "->"; atom "g"]
* as:
* foo->f->g
* or if line-length indicates breaking:
* foo
* ->f
* ->g
*)
let formatFastPipeChain layouts =
(* transforms [->; f; ->; g] into [->f; ->g] *)
let rec processPipePairs acc = function
| pipe::exp::xs ->
let layout = label ~break:`Never pipe exp in
processPipePairs (layout::acc) xs
| [x] -> List.rev (x::acc)
| [] -> List.rev acc
in match layouts with
| hd::tl ->
(* process head of the layout list different so all "pipe pairs"
* are nicely aligned under the first element when the layout breaks.
* foo->f->g
* becomes
* foo
* ->f
* ->g
*)
let pipes = processPipePairs [] tl in
makeList ~break:IfNeed ~inline:(true, true) (hd::pipes)
| [] ->
atom ""


(* Formats a flattened list of infixChain nodes into a list of layoutNodes
* which allow smooth line-breaking
* e.g. [LayoutNode foo; InfixToken |>; LayoutNode f; InfixToken |>; LayoutNode z]
Expand All @@ -2146,7 +2183,7 @@ let formatComputedInfixChain infixChainList =
* |> f
* |> z *)
if List.length group < 2 then
makeList ~inline:(true, true) ~sep:(Sep " ") ~break:Layout.Never group
makeList ~inline:(true, true) ~sep:(Sep " ") group
(* Basic equality operators require special formatting, we can't give it
* 'classic' infix operator formatting, otherwise we would get
* let example =
Expand All @@ -2157,8 +2194,10 @@ let formatComputedInfixChain infixChainList =
* *)
else if List.mem currentToken equalityOperators then
let hd = List.hd group in
let tl = makeList ~inline:(true, true) ~sep:(Sep " ") ~break:Layout.Never (List.tl group) in
let tl = makeList ~inline:(true, true) ~sep:(Sep " ") (List.tl group) in
makeList ~inline:(true, true) ~sep:(Sep " ") ~break:IfNeed [hd; tl]
else if currentToken = "->" then
formatFastPipeChain group
else
(* Represents `|> f` in foo |> f
* We need a label here to indent possible closing parens
Expand All @@ -2168,17 +2207,18 @@ let formatComputedInfixChain infixChainList =
* fun body =>
* Printf.sprintf
* "okokok" uri meth headers body
* ) <-- notice how this closing paren is on the same height as >|= *)
* ) <-- notice how this closing paren is on the same height as >|=
*)
label ~break:`Never ~space:true (atom currentToken) (List.nth group 1)
in
let rec print acc group currentToken l =
match l with
| x::xs -> (match x with
| InfixToken t ->
(* = or := *)
if List.mem t requireIndentFor then
let groupNode =
makeList ~inline:(true, true) ~sep:(Sep " ") ~break:Layout.Never
(group @ [atom t])
makeList ~inline:(true, true) ~sep:(Sep " ") (group @ [atom t])
in
let children =
makeList ~inline:(true, true) ~preSpace:true ~break:IfNeed
Expand All @@ -2194,14 +2234,34 @@ let formatComputedInfixChain infixChainList =
* *)
else if t = "@@" then
let groupNode =
makeList ~inline:(true, true) ~sep:(Sep " ") ~break:Layout.Never
(group @ [atom t])
makeList ~inline:(true, true) ~sep:(Sep " ") (group @ [atom t])
in
print (acc @ [groupNode]) [] t xs
(* != !== === == >= <= < > etc *)
else if List.mem t equalityOperators then
print acc (group @ [atom t]) t xs
else
print (acc @ [layout_of_group group currentToken]) [(atom t)] t xs
begin if t = "->" then
begin if (currentToken = "" || currentToken = "->") then
print acc (group@[atom t]) t xs
else
(* a + b + foo->bar->baz
* `foo` needs to be picked from the current group
* and inserted into a new one. This way `foo`
* gets the special "fast pipe chain"-printing:
* foo->bar->baz. *)
begin match List.rev group with
| hd::tl ->
let acc =
acc @ [layout_of_group (List.rev tl) currentToken]
in
print acc [hd; atom t] t xs
| [] -> print acc (group@[atom t]) t xs
end
end
else
print (acc @ [layout_of_group group currentToken]) [(atom t)] t xs
end
| Layout layoutNode -> print acc (group @ [layoutNode]) currentToken xs
)
| [] ->
Expand All @@ -2210,7 +2270,8 @@ let formatComputedInfixChain infixChainList =
else
acc @ [layout_of_group group currentToken]
in
print [] [] "" infixChainList
let l = print [] [] "" infixChainList in
makeList ~inline:(true, true) ~sep:(Sep " ") ~break:IfNeed l

(**
* [groupAndPrint] will print every item in [items] according to the function [xf].
Expand Down Expand Up @@ -3627,9 +3688,7 @@ let printer = object(self:'self)
method unparseResolvedRule = function
| LayoutNode layoutNode -> layoutNode
| InfixTree _ as infixTree ->
let infixChainList = computeInfixChain infixTree in
let l = formatComputedInfixChain infixChainList in
makeList ~inline:(true, true) ~sep:(Sep " ") ~break:IfNeed l
formatComputedInfixChain (computeInfixChain infixTree)


method unparseExprApplicationItems x =
Expand Down Expand Up @@ -3673,6 +3732,7 @@ let printer = object(self:'self)
x

method unparseExprRecurse x =
let x = Reason_ast.processFastPipe x in
let x = self#process_underscore_application x in
(* If there are any attributes, render unary like `(~-) x [@ppx]`, and infix like `(+) x y [@attr]` *)

Expand Down