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

F# pipeline operator #7898

Closed
wants to merge 13 commits into from
1 change: 1 addition & 0 deletions src/commands/astCommand.ml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ let main include_tokens pretty check debug pattern file_type_opt use_strict path
esproposal_export_star_as = true;
esproposal_optional_chaining = true;
esproposal_nullish_coalescing = true;
esproposal_fsharp_pipeline_operator = true;
types = true;
use_strict;
}) in
Expand Down
1 change: 1 addition & 0 deletions src/commands/commandUtils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ let make_options ~flowconfig_name ~flowconfig ~lazy_mode ~root (options_flags: O
FlowConfig.esproposal_class_instance_fields flowconfig;
opt_esproposal_optional_chaining = FlowConfig.esproposal_optional_chaining flowconfig;
opt_esproposal_nullish_coalescing = FlowConfig.esproposal_nullish_coalescing flowconfig;
opt_esproposal_fsharp_pipeline_operator = FlowConfig.esproposal_fsharp_pipeline_operator flowconfig;
opt_max_header_tokens = FlowConfig.max_header_tokens flowconfig;
opt_haste_module_ref_prefix = FlowConfig.haste_module_ref_prefix flowconfig;
opt_haste_name_reducers = FlowConfig.haste_name_reducers flowconfig;
Expand Down
8 changes: 8 additions & 0 deletions src/commands/config/flowConfig.ml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ module Opts = struct
esproposal_export_star_as: Options.esproposal_feature_mode;
esproposal_nullish_coalescing: Options.esproposal_feature_mode;
esproposal_optional_chaining: Options.esproposal_feature_mode;
esproposal_fsharp_pipeline_operator: Options.esproposal_feature_mode;
facebook_fbs: string option;
facebook_fbt: string option;
file_watcher: Options.file_watcher option;
Expand Down Expand Up @@ -148,6 +149,7 @@ module Opts = struct
esproposal_export_star_as = Options.ESPROPOSAL_WARN;
esproposal_nullish_coalescing = Options.ESPROPOSAL_WARN;
esproposal_optional_chaining = Options.ESPROPOSAL_WARN;
esproposal_fsharp_pipeline_operator = Options.ESPROPOSAL_WARN;
facebook_fbs = None;
facebook_fbt = None;
file_watcher = None;
Expand Down Expand Up @@ -366,6 +368,11 @@ module Opts = struct
~allow_enable:true
(fun opts v -> Ok { opts with esproposal_nullish_coalescing = v });

"esproposal.fsharp_pipeline_operator",
esproposal_feature_flag
~allow_enable:true
(fun opts v -> Ok { opts with esproposal_fsharp_pipeline_operator = v });

"facebook.fbs",
string (fun opts v -> Ok { opts with facebook_fbs = Some v });

Expand Down Expand Up @@ -1029,6 +1036,7 @@ let esproposal_decorators c = c.options.Opts.esproposal_decorators
let esproposal_export_star_as c = c.options.Opts.esproposal_export_star_as
let esproposal_optional_chaining c = c.options.Opts.esproposal_optional_chaining
let esproposal_nullish_coalescing c = c.options.Opts.esproposal_nullish_coalescing
let esproposal_fsharp_pipeline_operator c = c.options.Opts.esproposal_fsharp_pipeline_operator
let file_watcher c = c.options.Opts.file_watcher
let facebook_fbs c = c.options.Opts.facebook_fbs
let facebook_fbt c = c.options.Opts.facebook_fbt
Expand Down
1 change: 1 addition & 0 deletions src/commands/config/flowConfig.mli
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ val esproposal_decorators: config -> Options.esproposal_feature_mode
val esproposal_export_star_as: config -> Options.esproposal_feature_mode
val esproposal_nullish_coalescing: config -> Options.esproposal_feature_mode
val esproposal_optional_chaining: config -> Options.esproposal_feature_mode
val esproposal_fsharp_pipeline_operator: config -> Options.esproposal_feature_mode
val facebook_fbs: config -> string option
val facebook_fbt: config -> string option
val file_watcher: config -> Options.file_watcher option
Expand Down
2 changes: 2 additions & 0 deletions src/common/options.ml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ type t = {
opt_esproposal_export_star_as: esproposal_feature_mode;
opt_esproposal_optional_chaining: esproposal_feature_mode;
opt_esproposal_nullish_coalescing: esproposal_feature_mode;
opt_esproposal_fsharp_pipeline_operator: esproposal_feature_mode;
opt_facebook_fbs: string option;
opt_facebook_fbt: string option;
opt_flowconfig_name: string;
Expand Down Expand Up @@ -134,6 +135,7 @@ let esproposal_decorators opts = opts.opt_esproposal_decorators
let esproposal_export_star_as opts = opts.opt_esproposal_export_star_as
let esproposal_optional_chaining opts = opts.opt_esproposal_optional_chaining
let esproposal_nullish_coalescing opts = opts.opt_esproposal_nullish_coalescing
let esproposal_fsharp_pipeline_operator opts = opts.opt_esproposal_fsharp_pipeline_operator
let haste_module_ref_prefix opts = opts.opt_haste_module_ref_prefix
let haste_name_reducers opts = opts.opt_haste_name_reducers
let haste_paths_blacklist opts = opts.opt_haste_paths_blacklist
Expand Down
25 changes: 13 additions & 12 deletions src/common/reason.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1121,18 +1121,19 @@ and code_desc_of_operation = Ast.Expression.(
in
let should_flatten = Binary.(
let precedence = function
| `Logical Logical.Or -> 0
| `Logical Logical.NullishCoalesce -> 0
| `Logical Logical.And -> 1
| `Binary BitOr -> 2
| `Binary Xor -> 3
| `Binary BitAnd -> 4
| `Binary (Equal | NotEqual | StrictEqual | StrictNotEqual) -> 5
| `Binary (LessThan | LessThanEqual | GreaterThan | GreaterThanEqual | In | Instanceof) -> 6
| `Binary (LShift | RShift | RShift3) -> 7
| `Binary (Plus | Minus) -> 8
| `Binary (Mult | Div | Mod) -> 9
| `Binary Exp -> 10
| `Binary Pipeline -> 0
| `Logical Logical.Or -> 1
| `Logical Logical.NullishCoalesce -> 1
| `Logical Logical.And -> 2
| `Binary BitOr -> 3
| `Binary Xor -> 4
| `Binary BitAnd -> 5
| `Binary (Equal | NotEqual | StrictEqual | StrictNotEqual) -> 6
| `Binary (LessThan | LessThanEqual | GreaterThan | GreaterThanEqual | In | Instanceof) -> 7
| `Binary (LShift | RShift | RShift3) -> 8
| `Binary (Plus | Minus) -> 9
| `Binary (Mult | Div | Mod) -> 10
| `Binary Exp -> 11
in
let equality = function
| `Binary (Equal | NotEqual | StrictEqual | StrictNotEqual) -> true
Expand Down
2 changes: 2 additions & 0 deletions src/flow_dot_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ let parse_content file content =
esproposal_export_star_as = true;
esproposal_optional_chaining = true;
esproposal_nullish_coalescing = true;
esproposal_fsharp_pipeline_operator = true;
types = true;
use_strict = false;
}) in
Expand Down Expand Up @@ -153,6 +154,7 @@ let stub_metadata ~root ~checked = { Context.
esproposal_export_star_as = Options.ESPROPOSAL_ENABLE;
esproposal_optional_chaining = Options.ESPROPOSAL_ENABLE;
esproposal_nullish_coalescing = Options.ESPROPOSAL_ENABLE;
esproposal_fsharp_pipeline_operator = Options.ESPROPOSAL_ENABLE;
facebook_fbs = None;
facebook_fbt = None;
haste_module_ref_prefix = None;
Expand Down
1 change: 1 addition & 0 deletions src/lsp/flowLsp.ml
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,7 @@ let parse_and_cache flowconfig_name (state: state) (uri: string)
esproposal_export_star_as = true;
esproposal_optional_chaining = true;
esproposal_nullish_coalescing = true;
esproposal_fsharp_pipeline_operator = true;
types = true;
use_strict;
}) in
Expand Down
85 changes: 82 additions & 3 deletions src/parser/expression_parser.ml
Original file line number Diff line number Diff line change
Expand Up @@ -250,13 +250,77 @@ module Expression
if op <> None then Eat.token env;
op

and pipeline_cover =
let open Expression in
let make_binary env left right operator loc =
let left = as_expression env left in
let right = as_expression env right in
Cover_expr (loc, Binary {
Binary.operator;
left;
right;
})
in let rec pipeline_expr env left lloc =
let options = parse_options env in
match Peek.token env with
| T_PIPELINE ->
if not options.esproposal_fsharp_pipeline_operator
then error env Parse_error.FSharpPipelineOperatorDisabled;

Expect.token env T_PIPELINE;
let env' = env |> with_allow_solo_await true in
let rloc, right = with_loc pipeline_body_cover env' in
let loc = Loc.btwn lloc rloc in
pipeline_expr env (make_binary env left right Binary.Pipeline loc) loc
| _ -> left
and error_callback _ = function
(* Don't rollback on these errors. *)
| Error.StrictReservedWord -> ()
(* Everything else causes a rollback *)
| _ -> raise Try.Rollback
and try_logical_cover_but_not_arrow_function env =
let env = env |> with_error_callback error_callback in
let ret = logical_cover env in
match Peek.token env with
| T_ARROW -> (* x => 123 *)
raise Try.Rollback
| T_COLON when last_token env = Some T_RPAREN -> (* (x): number => 123 *)
raise Try.Rollback
(* async x => 123 -- and we've already parsed async as an identifier
* expression *)
| _ when Peek.is_identifier env -> begin match ret with
| Cover_expr (_, Expression.Identifier (_, { Identifier.name= "async"; comments= _ }))
when not (Peek.is_line_terminator env) ->
raise Try.Rollback
| _ -> ret
end
| _ -> ret
and pipeline_body_cover env =
match Peek.token env, Peek.is_identifier env with
| T_LPAREN, _
| T_LESS_THAN, _
| _, true ->
(match Try.to_parse env try_logical_cover_but_not_arrow_function with
| Try.ParsedSuccessfully expr -> expr
| Try.FailedToParse ->
(match Try.to_parse env try_arrow_function with
| Try.ParsedSuccessfully expr -> expr
| Try.FailedToParse ->
logical_cover env
)
)
| _ -> logical_cover env
in fun env ->
let loc, left = with_loc pipeline_body_cover env in
pipeline_expr env left loc

(* ConditionalExpression :
* LogicalExpression
* LogicalExpression ? AssignmentExpression : AssignmentExpression
*)
and conditional_cover env =
let start_loc = Peek.loc env in
let expr = logical_cover env in
let expr = pipeline_cover env in
if Peek.token env = T_PLING
then begin
Expect.token env T_PLING;
Expand All @@ -267,7 +331,9 @@ module Expression
let end_loc, alternate = with_loc assignment env in
let loc = Loc.btwn start_loc end_loc in
Cover_expr (loc, Expression.(Conditional { Conditional.
test = as_expression env expr;
test = as_expression
env
expr;
consequent;
alternate;
}))
Expand Down Expand Up @@ -455,7 +521,20 @@ module Expression
end
| Some operator ->
Eat.token env;
let end_loc, argument = with_loc unary env in
let null (_env : Parser_env.env) : (Loc.t, Loc.t) Flow_ast.Expression.t =
Peek.loc env, Expression.Literal {
Literal.value = Literal.Null;
comments = None;
raw = "null";
}
in
let end_loc, argument = with_loc (
if (allow_solo_await env) then
null
else
unary
) env
in
let loc = Loc.btwn begin_loc end_loc in
Expression.(match operator, argument with
| Unary.Delete, (_, Identifier _) ->
Expand Down
1 change: 1 addition & 0 deletions src/parser/flow_ast.ml
Original file line number Diff line number Diff line change
Expand Up @@ -863,6 +863,7 @@ and Expression : sig
| BitAnd
| In
| Instanceof
| Pipeline

and ('M, 'T) t = {
operator: operator;
Expand Down
1 change: 1 addition & 0 deletions src/parser/flow_ast_utils.ml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ let string_of_binary_operator op =
| BitAnd -> "&"
| In -> "in"
| Instanceof -> "instanceof"
| Pipeline -> "|>"

module ExpressionSort = struct
type t =
Expand Down
1 change: 1 addition & 0 deletions src/parser/flow_lexer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,7 @@ let token (env: Lex_env.t) lexbuf : result =
| "?" -> Token (env, T_PLING)
| "&&" -> Token (env, T_AND)
| "||" -> Token (env, T_OR)
| "|>" -> Token (env, T_PIPELINE)
| "===" -> Token (env, T_STRICT_EQUAL)
| "!==" -> Token (env, T_STRICT_NOT_EQUAL)
| "<=" -> Token (env, T_LESS_THAN_EQUAL)
Expand Down
5 changes: 5 additions & 0 deletions src/parser/parse_error.ml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ type t =
| OptionalChainNew
| OptionalChainTemplate
| NullishCoalescingDisabled
| FSharpPipelineOperatorDisabled
| WhitespaceInPrivateName

exception Error of (Loc.t * t) list
Expand Down Expand Up @@ -377,5 +378,9 @@ module PP =
use the nullish coalescing operator (`??`). Nullish coalescing is an active early-stage \
feature proposal which may change and is not enabled by default. To enable support in \
the parser, use the `esproposal_nullish_coalescing` option."
| FSharpPipelineOperatorDisabled -> "The F# pipeline operator plugin must be enabled in order to \
use the pipeline operator (`|>`). Pipeline operator is an active early-stage \
feature proposal which may change and is not enabled by default. To enable support in \
the parser, use the `esproposal_fsharp_pipeline_operator` option."
| WhitespaceInPrivateName -> "Unexpected whitespace between `#` and identifier"
end
7 changes: 7 additions & 0 deletions src/parser/parser_env.ml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ type parse_options = {
esproposal_export_star_as: bool;
esproposal_optional_chaining: bool;
esproposal_nullish_coalescing: bool;
esproposal_fsharp_pipeline_operator: bool;
types: bool;
use_strict: bool;
}
Expand All @@ -156,6 +157,7 @@ let default_parse_options = {
esproposal_export_star_as = false;
esproposal_optional_chaining = false;
esproposal_nullish_coalescing = false;
esproposal_fsharp_pipeline_operator = false;
types = true;
use_strict = false;
}
Expand Down Expand Up @@ -186,6 +188,7 @@ type env = {
allow_await : bool;
allow_directive : bool;
allow_super : allowed_super;
allow_solo_await : bool;
error_callback : (env -> Error.t -> unit) option;
lex_mode_stack : Lex_mode.t list ref;
(* lex_env is the lex_env after the single lookahead has been lexed *)
Expand Down Expand Up @@ -238,6 +241,7 @@ let init_env ?(token_sink=None) ?(parse_options=None) source content =
allow_await = false;
allow_directive = false;
allow_super = No_super;
allow_solo_await = false;
error_callback = None;
lex_mode_stack = ref [Lex_mode.NORMAL];
lex_env = ref lex_env;
Expand All @@ -262,6 +266,7 @@ let allow_yield env = env.allow_yield
let allow_await env = env.allow_await
let allow_directive env = env.allow_directive
let allow_super env = env.allow_super
let allow_solo_await env = env.allow_solo_await
let no_in env = env.no_in
let no_call env = env.no_call
let no_let env = env.no_let
Expand Down Expand Up @@ -338,6 +343,7 @@ let with_allow_yield allow_yield env = { env with allow_yield }
let with_allow_await allow_await env = { env with allow_await }
let with_allow_directive allow_directive env = { env with allow_directive }
let with_allow_super allow_super env = { env with allow_super }
let with_allow_solo_await allow_solo_await env = { env with allow_solo_await }
let with_no_let no_let env = { env with no_let }
let with_in_loop in_loop env = { env with in_loop }
let with_no_in no_in env = { env with no_in }
Expand Down Expand Up @@ -622,6 +628,7 @@ module Peek = struct
| T_COLON
| T_OR
| T_AND
| T_PIPELINE
| T_BIT_OR
| T_BIT_XOR
| T_BIT_AND
Expand Down
3 changes: 3 additions & 0 deletions src/parser/parser_env.mli
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type parse_options = {
esproposal_export_star_as: bool;
esproposal_optional_chaining: bool;
esproposal_nullish_coalescing: bool;
esproposal_fsharp_pipeline_operator: bool;
types: bool;
use_strict: bool;
}
Expand Down Expand Up @@ -70,6 +71,7 @@ val allow_yield : env -> bool
val allow_await: env -> bool
val allow_directive : env -> bool
val allow_super : env -> allowed_super
val allow_solo_await : env -> bool
val no_in : env -> bool
val no_call : env -> bool
val no_let : env -> bool
Expand Down Expand Up @@ -104,6 +106,7 @@ val with_allow_yield : bool -> env -> env
val with_allow_await : bool -> env -> env
val with_allow_directive : bool -> env -> env
val with_allow_super : allowed_super -> env -> env
val with_allow_solo_await : bool -> env -> env
val with_no_let : bool -> env -> env
val with_in_loop : bool -> env -> env
val with_no_in : bool -> env -> env
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
x => x |> [y => y + 1 |> z => z * 2]