Skip to content

Commit feb0a0d

Browse files
gkzmeta-codesync[bot]
authored andcommitted
[flow][records] Basic parsing of record declarations
Summary: Basic parsing of record declarations (minus parsing of methods in records). Changelog: [internal] Reviewed By: SamChou19815 Differential Revision: D85729245 fbshipit-source-id: 2c2761dfe10b68e1e79381cd71a8c2476597e5c4
1 parent 0b41a37 commit feb0a0d

39 files changed

Lines changed: 835 additions & 6 deletions

packages/flow-parser/test/custom_ast_types.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,26 @@ function custom_ast_types(fork) {
326326
.field('pattern', def('MatchPattern'))
327327
.field('target', or(def('Identifier'), def('MatchBindingPattern')));
328328

329+
def('RecordDeclaration')
330+
.bases('Statement')
331+
.build('id', 'typeParameters', 'implements', 'body')
332+
.field('id', def('Identifier'))
333+
.field('typeParameters', or(def('TypeParameterDeclaration'), null))
334+
.field('implements', [def('RecordImplements')])
335+
.field('body', def('RecordBody'));
336+
337+
def('RecordImplements').bases('ClassImplements');
338+
339+
def('RecordBody')
340+
.build('body')
341+
.field('body', [or(def('RecordProperty'), def('Method'))]);
342+
343+
def('RecordProperty')
344+
.build('key', 'typeAnnotation', 'defaultValue')
345+
.field('key', def('Identifier'))
346+
.field('typeAnnotation', def('TypeAnnotation'))
347+
.field('defaultValue', or(def('Expression'), null));
348+
329349
/////////
330350
// es2018
331351
/////////

src/parser/comment_attachment.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ let statement_add_comments
575575
Labeled { s with Labeled.comments = merge_comments comments }
576576
| Match ({ Match.comments; _ } as s) ->
577577
Match { s with Match.comments = merge_comments comments }
578+
| RecordDeclaration ({ RecordDeclaration.comments; _ } as s) ->
579+
RecordDeclaration { s with RecordDeclaration.comments = merge_comments comments }
578580
| Return ({ Return.comments; _ } as s) ->
579581
Return { s with Return.comments = merge_comments comments }
580582
| Switch ({ Switch.comments; _ } as s) ->

src/parser/estree_translator.ml

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,47 @@ with type t = Impl.t = struct
178178
"SwitchStatement"
179179
loc
180180
[("discriminant", expression discriminant); ("cases", array_of_list case cases)]
181+
| (loc, RecordDeclaration { RecordDeclaration.id; tparams; implements; body; comments }) ->
182+
let record_property (loc, { RecordDeclaration.Property.key; annot; default_value; comments })
183+
=
184+
let key = identifier key in
185+
node
186+
?comments
187+
"RecordProperty"
188+
loc
189+
[
190+
("key", key);
191+
("typeAnnotation", type_annotation annot);
192+
("defaultValue", option expression default_value);
193+
]
194+
in
195+
196+
let record_element element =
197+
let open RecordDeclaration.Body in
198+
match element with
199+
| Property prop -> record_property prop
200+
| Method meth -> class_method meth
201+
in
202+
let record_body (loc, { RecordDeclaration.Body.body; comments }) =
203+
node ?comments "RecordBody" loc [("body", array_of_list record_element body)]
204+
in
205+
let record_implements = implements_helper "RecordImplements" in
206+
let implements =
207+
match implements with
208+
| Some (_, { Class.Implements.interfaces; comments = _ }) ->
209+
array_of_list record_implements interfaces
210+
| None -> array []
211+
in
212+
node
213+
?comments
214+
"RecordDeclaration"
215+
loc
216+
[
217+
("id", identifier id);
218+
("typeParameters", option type_parameter_declaration tparams);
219+
("implements", implements);
220+
("body", record_body body);
221+
]
181222
| (loc, Return { Return.argument; comments; return_out = _ }) ->
182223
node ?comments "ReturnStatement" loc [("argument", option expression argument)]
183224
| (loc, Throw { Throw.argument; comments }) ->
@@ -1247,8 +1288,9 @@ with type t = Impl.t = struct
12471288
]
12481289
and class_decorator (loc, { Class.Decorator.expression = expr; comments }) =
12491290
node ?comments "Decorator" loc [("expression", expression expr)]
1250-
and class_implements (loc, { Class.Implements.Interface.id; targs }) =
1251-
node "ClassImplements" loc [("id", identifier id); ("typeParameters", option type_args targs)]
1291+
and implements_helper node_type (loc, { Class.Implements.Interface.id; targs }) =
1292+
node node_type loc [("id", identifier id); ("typeParameters", option type_args targs)]
1293+
and class_implements node = implements_helper "ClassImplements" node
12521294
and class_body (loc, { Class.Body.body; comments }) =
12531295
node ?comments "ClassBody" loc [("body", array_of_list class_element body)]
12541296
and class_element =

src/parser/flow_ast.ml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,43 @@ and Statement : sig
10761076
[@@deriving show]
10771077
end
10781078

1079+
module RecordDeclaration : sig
1080+
module Property : sig
1081+
type ('M, 'T) t = 'T * ('M, 'T) t'
1082+
1083+
and ('M, 'T) t' = {
1084+
key: ('M, 'T) Identifier.t;
1085+
annot: ('M, 'T) Type.annotation;
1086+
default_value: ('M, 'T) Expression.t option;
1087+
comments: ('M, unit) Syntax.t option;
1088+
}
1089+
[@@deriving show]
1090+
end
1091+
1092+
module Body : sig
1093+
type ('M, 'T) t = 'M * ('M, 'T) t'
1094+
1095+
and ('M, 'T) t' = {
1096+
body: ('M, 'T) element list;
1097+
comments: ('M, unit) Syntax.t option;
1098+
}
1099+
1100+
and ('M, 'T) element =
1101+
| Method of ('M, 'T) Class.Method.t
1102+
| Property of ('M, 'T) Property.t
1103+
[@@deriving show]
1104+
end
1105+
1106+
type ('M, 'T) t = {
1107+
id: ('M, 'T) Identifier.t;
1108+
tparams: ('M, 'T) Type.TypeParams.t option;
1109+
implements: ('M, 'T) Class.Implements.t option;
1110+
body: ('M, 'T) Body.t;
1111+
comments: ('M, unit) Syntax.t option;
1112+
}
1113+
[@@deriving show]
1114+
end
1115+
10791116
module DeclareClass : sig
10801117
type ('M, 'T) t = {
10811118
id: ('M, 'T) Identifier.t;
@@ -1320,6 +1357,7 @@ and Statement : sig
13201357
| InterfaceDeclaration of ('M, 'T) Interface.t
13211358
| Labeled of ('M, 'T) Labeled.t
13221359
| Match of ('M, 'T) match_statement
1360+
| RecordDeclaration of ('M, 'T) RecordDeclaration.t
13231361
| Return of ('M, 'T) Return.t
13241362
| Switch of ('M, 'T) Switch.t
13251363
| Throw of ('M, 'T) Throw.t

src/parser/flow_ast_mapper.ml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ type type_params_context =
9292
| FunctionTypeTP
9393
| InferTP
9494
| ObjectMappedTypeTP
95+
| RecordTP
9596

9697
class ['loc] mapper =
9798
object (this)
@@ -190,6 +191,10 @@ class ['loc] mapper =
190191
| (loc, Match x) -> id_loc this#match_statement loc x stmt (fun x -> (loc, Match x))
191192
| (loc, OpaqueType otype) ->
192193
id_loc this#opaque_type loc otype stmt (fun otype -> (loc, OpaqueType otype))
194+
| (loc, RecordDeclaration record) ->
195+
id_loc this#record_declaration loc record stmt (fun record ->
196+
(loc, RecordDeclaration record)
197+
)
193198
| (loc, Return ret) -> id_loc this#return loc ret stmt (fun ret -> (loc, Return ret))
194199
| (loc, Switch switch) ->
195200
id_loc this#switch loc switch stmt (fun switch -> (loc, Switch switch))
@@ -3280,6 +3285,62 @@ class ['loc] mapper =
32803285
else
32813286
(loc, { argument = argument'; comments = comments' })
32823287

3288+
method record_declaration _loc (record : ('loc, 'loc) Ast.Statement.RecordDeclaration.t) =
3289+
let open Ast.Statement.RecordDeclaration in
3290+
let { id; tparams; implements; body; comments } = record in
3291+
let id' = this#identifier id in
3292+
let tparams' = map_opt (this#type_params ~kind:RecordTP) tparams in
3293+
let implements' = map_opt this#class_implements implements in
3294+
let body' = this#record_body body in
3295+
let comments' = this#syntax_opt comments in
3296+
if
3297+
id == id'
3298+
&& tparams == tparams'
3299+
&& implements == implements'
3300+
&& body == body'
3301+
&& comments == comments'
3302+
then
3303+
record
3304+
else
3305+
{
3306+
id = id';
3307+
tparams = tparams';
3308+
implements = implements';
3309+
body = body';
3310+
comments = comments';
3311+
}
3312+
3313+
method record_body (record_body : ('loc, 'loc) Ast.Statement.RecordDeclaration.Body.t) =
3314+
let open Ast.Statement.RecordDeclaration.Body in
3315+
let (loc, { body; comments }) = record_body in
3316+
let body' = map_list this#record_element body in
3317+
let comments' = this#syntax_opt comments in
3318+
if body == body' && comments == comments' then
3319+
record_body
3320+
else
3321+
(loc, { body = body'; comments = comments' })
3322+
3323+
method record_element (element : ('loc, 'loc) Ast.Statement.RecordDeclaration.Body.element) =
3324+
let open Ast.Statement.RecordDeclaration.Body in
3325+
match element with
3326+
| Method (loc, meth) ->
3327+
id_loc this#class_method loc meth element (fun meth -> Method (loc, meth))
3328+
| Property (loc, prop) ->
3329+
id_loc this#record_property loc prop element (fun prop -> Property (loc, prop))
3330+
3331+
method record_property _loc (prop : ('loc, 'loc) Ast.Statement.RecordDeclaration.Property.t') =
3332+
let open Ast.Statement.RecordDeclaration.Property in
3333+
let { key; annot; default_value; comments } = prop in
3334+
let key' = this#identifier key in
3335+
let annot' = this#type_annotation annot in
3336+
let default_value' = map_opt this#expression default_value in
3337+
let comments' = this#syntax_opt comments in
3338+
if key' == key && annot' == annot && default_value' == default_value && comments' == comments
3339+
then
3340+
prop
3341+
else
3342+
{ key = key'; annot = annot'; default_value = default_value'; comments = comments' }
3343+
32833344
method return _loc (stmt : ('loc, 'loc) Ast.Statement.Return.t) =
32843345
let open Ast.Statement.Return in
32853346
let { argument; comments; return_out } = stmt in

src/parser/flow_ast_utils.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ let acceptable_statement_in_declaration_context ~in_declare_namespace =
378378
| If _ -> Error "if"
379379
| Labeled _ -> Error "labeled"
380380
| Match _ -> Error "match"
381+
| RecordDeclaration _ -> Error "record declaration"
381382
| Return _ -> Error "return"
382383
| Switch _ -> Error "switch"
383384
| Throw _ -> Error "throw"
@@ -449,6 +450,7 @@ let rec is_type_only_declaration_statement (_, stmt') =
449450
| If _
450451
| Labeled _
451452
| Match _
453+
| RecordDeclaration _
452454
| Return _
453455
| Switch _
454456
| Throw _

src/parser/flow_lexer.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ let token (env : Lex_env.t) lexbuf : result =
903903
| "private" -> Token (env, T_PRIVATE)
904904
| "protected" -> Token (env, T_PROTECTED)
905905
| "public" -> Token (env, T_PUBLIC)
906+
| "record" -> Token (env, T_RECORD)
906907
| "return" -> Token (env, T_RETURN)
907908
| "static" -> Token (env, T_STATIC)
908909
| "super" -> Token (env, T_SUPER)

src/parser/flow_parser_js.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ let parse_options jsopts =
6868
components = bool_opt defaults.components jsopts "components";
6969
enums = bool_opt defaults.enums jsopts "enums";
7070
pattern_matching = bool_opt defaults.pattern_matching jsopts "pattern_matching";
71+
records = bool_opt defaults.records jsopts "records";
7172
esproposal_decorators = bool_opt defaults.esproposal_decorators jsopts "esproposal_decorators";
7273
types = bool_opt defaults.types jsopts "types";
7374
use_strict = bool_opt defaults.use_strict jsopts "use_strict";

src/parser/object_parser.ml

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,4 +1169,91 @@ module Object
11691169

11701170
let class_expression =
11711171
with_loc (fun env -> Ast.Expression.Class (_class env ~optional_id:true ~expression:true))
1172+
1173+
let record_body =
1174+
let open Statement.RecordDeclaration in
1175+
let rec elements env acc =
1176+
match Peek.token env with
1177+
| T_EOF
1178+
| T_RCURLY ->
1179+
List.rev acc
1180+
| _ ->
1181+
let leading = Peek.comments env in
1182+
let key = identifier_name env in
1183+
let (key_loc, _) = key in
1184+
(match Peek.token env with
1185+
(* TODO: records - methods parsing*)
1186+
| T_COLON ->
1187+
let prop =
1188+
with_loc
1189+
~start_loc:key_loc
1190+
(fun env ->
1191+
let annot = Type.annotation env in
1192+
let default_value =
1193+
if Eat.maybe env T_ASSIGN then
1194+
Some (Expression.assignment env)
1195+
else
1196+
None
1197+
in
1198+
(match Peek.token env with
1199+
| T_EOF
1200+
| T_RCURLY ->
1201+
()
1202+
| _ -> Expect.token env T_COMMA);
1203+
let comments = Flow_ast_utils.mk_comments_opt ~leading () in
1204+
{ Property.key; annot; default_value; comments })
1205+
env
1206+
in
1207+
elements env (Body.Property prop :: acc)
1208+
| _ ->
1209+
(* TODO: records - error *)
1210+
elements env acc)
1211+
in
1212+
with_loc (fun env ->
1213+
let leading = Peek.comments env in
1214+
if Eat.maybe env T_LCURLY then (
1215+
let body = elements env [] in
1216+
let trailing =
1217+
match Peek.token env with
1218+
| T_RCURLY
1219+
| T_EOF ->
1220+
Eat.trailing_comments env
1221+
| _ when Peek.is_line_terminator env -> Eat.comments_until_next_line env
1222+
| _ -> []
1223+
in
1224+
Expect.token env T_RCURLY;
1225+
{ Body.body; comments = Flow_ast_utils.mk_comments_opt ~leading ~trailing () }
1226+
) else (
1227+
Expect.error env T_LCURLY;
1228+
{ Body.body = []; comments = None }
1229+
)
1230+
)
1231+
1232+
let record_declaration =
1233+
with_loc (fun env ->
1234+
let leading = Peek.comments env in
1235+
Expect.token env T_RECORD;
1236+
let id = Parse.identifier env in
1237+
let tparams =
1238+
match Type.type_params env with
1239+
| None -> None
1240+
| Some tparams ->
1241+
let { remove_trailing; _ } = trailing_and_remover env in
1242+
Some
1243+
(remove_trailing tparams (fun remover tparams ->
1244+
remover#type_params ~kind:Flow_ast_mapper.RecordTP tparams
1245+
)
1246+
)
1247+
in
1248+
let implements =
1249+
if Peek.token env = T_IMPLEMENTS then
1250+
Some (class_implements_remove_trailing env (class_implements env ~attach_leading:true))
1251+
else
1252+
None
1253+
in
1254+
let body = record_body env in
1255+
let comments = Flow_ast_utils.mk_comments_opt ~leading () in
1256+
Ast.Statement.RecordDeclaration
1257+
{ Ast.Statement.RecordDeclaration.id; tparams; implements; body; comments }
1258+
)
11721259
end

src/parser/parser_common.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ module type OBJECT = sig
149149
val class_implements : env -> attach_leading:bool -> (Loc.t, Loc.t) Class.Implements.t
150150

151151
val decorator_list : env -> (Loc.t, Loc.t) Class.Decorator.t list
152+
153+
val record_declaration : env -> (Loc.t, Loc.t) Statement.t
152154
end
153155

154156
module type JSX = sig
@@ -378,6 +380,7 @@ let identifier_name_raw env =
378380
| T_OPAQUE -> "opaque"
379381
| T_ANY_TYPE -> "any"
380382
| T_MATCH -> "match"
383+
| T_RECORD -> "record"
381384
| T_MIXED_TYPE -> "mixed"
382385
| T_EMPTY_TYPE -> "empty"
383386
| T_BOOLEAN_TYPE BOOL -> "bool"

0 commit comments

Comments
 (0)