forked from ManasJayanth/flow
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjsx_parser.ml
325 lines (305 loc) · 11.7 KB
/
jsx_parser.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
(**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open Token
open Parser_env
open Ast
module Error = Parse_error
module JSX (Parse: Parser_common.PARSER) = struct
let spread_attribute env =
Eat.push_lex_mode env Lex_mode.NORMAL;
let start_loc = Peek.loc env in
Expect.token env T_LCURLY;
Expect.token env T_ELLIPSIS;
let argument = Parse.assignment env in
let end_loc = Peek.loc env in
Expect.token env T_RCURLY;
Eat.pop_lex_mode env;
Loc.btwn start_loc end_loc, JSX.SpreadAttribute.({
argument;
})
let expression_container' env start_loc =
let expression = if Peek.token env = T_RCURLY
then
let empty_loc = Loc.btwn_exclusive start_loc (Peek.loc env) in
JSX.ExpressionContainer.EmptyExpression empty_loc
else JSX.ExpressionContainer.Expression (Parse.expression env) in
let end_loc = Peek.loc env in
Expect.token env T_RCURLY;
Eat.pop_lex_mode env;
Loc.btwn start_loc end_loc, JSX.ExpressionContainer.({
expression;
})
let expression_container env =
Eat.push_lex_mode env Lex_mode.NORMAL;
let start_loc = Peek.loc env in
Expect.token env T_LCURLY;
expression_container' env start_loc
let expression_container_or_spread_child env =
Eat.push_lex_mode env Lex_mode.NORMAL;
let start_loc = Peek.loc env in
Expect.token env T_LCURLY;
match Peek.token env with
| T_ELLIPSIS ->
Expect.token env T_ELLIPSIS;
let expr = Parse.assignment env in
let end_loc = Peek.loc env in
Expect.token env T_RCURLY;
Eat.pop_lex_mode env;
Loc.btwn start_loc end_loc, JSX.SpreadChild expr
| _ ->
let expression_container = expression_container' env start_loc in
fst expression_container, JSX.ExpressionContainer (snd expression_container)
let identifier env =
let loc = Peek.loc env in
let name = match Peek.token env with
| T_JSX_IDENTIFIER { raw } -> raw
| _ -> error_unexpected env; ""
in
Eat.token env;
loc, JSX.Identifier.({ name; })
let name =
let rec member_expression env member =
match Peek.token env with
| T_PERIOD ->
let _object = JSX.MemberExpression.MemberExpression member in
Expect.token env T_PERIOD;
let property = identifier env in
let loc = Loc.btwn (fst member) (fst property) in
let member = loc, JSX.MemberExpression.({
_object;
property;
}) in
member_expression env member
| _ -> member
in fun env ->
let name = identifier env in
match Peek.token env with
| T_COLON ->
let namespace = name in
Expect.token env T_COLON;
let name = identifier env in
let loc = Loc.btwn (fst namespace) (fst name) in
JSX.NamespacedName (loc, JSX.NamespacedName.({
namespace;
name;
}))
| T_PERIOD ->
let _object = JSX.MemberExpression.Identifier name in
Expect.token env T_PERIOD;
let property = identifier env in
let loc = Loc.btwn (fst name) (fst property) in
let member = loc, JSX.MemberExpression.({
_object;
property;
}) in
JSX.MemberExpression (member_expression env member)
| _ -> JSX.Identifier name
let attribute env =
let start_loc = Peek.loc env in
let name = identifier env in
let end_loc, name =
if Peek.token env = T_COLON
then begin
Expect.token env T_COLON;
let namespace = name in
let name = identifier env in
let loc = Loc.btwn (fst namespace) (fst name) in
loc, JSX.Attribute.NamespacedName (loc, JSX.NamespacedName.({
namespace;
name;
}))
end else fst name, JSX.Attribute.Identifier name in
let end_loc, value =
if Peek.token env = T_ASSIGN
then begin
Expect.token env T_ASSIGN;
match Peek.token env with
| T_LCURLY ->
let loc, expression_container = expression_container env in
begin
let open JSX.ExpressionContainer in
match expression_container.expression with
| EmptyExpression _ ->
error_at env (loc, Error.JSXAttributeValueEmptyExpression);
| _ -> ()
end;
loc, Some (JSX.Attribute.ExpressionContainer (loc, expression_container))
| T_JSX_TEXT (loc, value, raw) as token ->
Expect.token env token;
let value = Ast.Literal.String value in
loc, Some (JSX.Attribute.Literal (loc, { Ast.Literal.value; raw;}))
| _ ->
error env Error.InvalidJSXAttributeValue;
let loc = Peek.loc env in
let raw = "" in
let value = Ast.Literal.String "" in
loc, Some (JSX.Attribute.Literal (loc, { Ast.Literal.value; raw;}))
end else end_loc, None in
Loc.btwn start_loc end_loc, JSX.Attribute.({
name;
value;
})
let opening_element_without_lt =
let rec attributes env acc =
match Peek.token env with
| T_EOF
| T_DIV
| T_GREATER_THAN -> List.rev acc
| T_LCURLY ->
let attribute = JSX.Opening.SpreadAttribute (spread_attribute env) in
attributes env (attribute::acc)
| _ ->
let attribute = JSX.Opening.Attribute (attribute env) in
attributes env (attribute::acc)
in fun env start_loc ->
let (name, attributes, selfClosing) = match Peek.token env with
| T_GREATER_THAN ->
(None, [], false)
| _ ->
let name = Some (name env) in
let attributes = attributes env [] in
let selfClosing = Peek.token env = T_DIV in
(name, attributes, selfClosing) in
if selfClosing then Expect.token env T_DIV;
let end_loc = Peek.loc env in
Expect.token env T_GREATER_THAN;
Eat.pop_lex_mode env;
match name with
| Some name ->
Loc.btwn start_loc end_loc, `Element JSX.Opening.({
name;
selfClosing;
attributes;
})
| None ->
Loc.btwn start_loc end_loc, `Fragment
let closing_element_without_lt env start_loc =
Expect.token env T_DIV;
let name = match Peek.token env with
| T_GREATER_THAN -> None
| _ -> Some (name env) in
let end_loc = Peek.loc env in
Expect.token env T_GREATER_THAN;
(* We double pop to avoid going back to childmode and re-lexing the
* lookahead *)
Eat.double_pop_lex_mode env;
match name with
| Some name ->
Loc.btwn start_loc end_loc, `Element JSX.Closing.({
name;
})
| None ->
Loc.btwn start_loc end_loc, `Fragment
type element_or_closing =
| Closing of Loc.t JSX.Closing.t
| ClosingFragment of Loc.t
| ChildElement of (Loc.t * Loc.t JSX.element)
| ChildFragment of (Loc.t * Loc.t JSX.fragment)
let rec child env =
match Peek.token env with
| T_LCURLY -> expression_container_or_spread_child env
| T_JSX_TEXT (loc, value, raw) as token ->
Expect.token env token;
loc, JSX.Text { JSX.Text.value; raw; }
| _ ->
(match element_or_fragment env with
| (loc, `Element element) -> loc, JSX.Element element
| (loc, `Fragment fragment) -> loc, JSX.Fragment fragment)
and element_without_lt =
let element_or_closing env =
Eat.push_lex_mode env Lex_mode.JSX_TAG;
let start_loc = Peek.loc env in
Expect.token env T_LESS_THAN;
match Peek.token env with
| T_EOF
| T_DIV -> (match closing_element_without_lt env start_loc with
| (loc, `Element ec) -> Closing (loc, ec)
| (loc, `Fragment) -> ClosingFragment loc)
| _ -> (match element_without_lt env start_loc with
| (loc, `Element e) -> ChildElement (loc, e)
| (loc, `Fragment f) -> ChildFragment (loc, f))
in let rec children_and_closing env acc =
match Peek.token env with
| T_LESS_THAN -> (
match element_or_closing env with
| Closing closingElement ->
List.rev acc, `Element closingElement
| ClosingFragment closingFragment ->
List.rev acc, `Fragment closingFragment
| ChildElement element ->
let element = fst element, JSX.Element (snd element) in
children_and_closing env (element::acc)
| ChildFragment fragment ->
let fragment = fst fragment, JSX.Fragment (snd fragment) in
children_and_closing env (fragment::acc))
| T_EOF ->
error_unexpected env;
List.rev acc, `None
| _ ->
children_and_closing env ((child env)::acc)
in let rec normalize name = JSX.(match name with
| Identifier (_, { Identifier.name }) -> name
| NamespacedName (_, { NamespacedName.namespace; name; }) ->
(snd namespace).Identifier.name ^ ":" ^ (snd name).Identifier.name
| MemberExpression (_, { MemberExpression._object; property; }) ->
let _object = match _object with
| MemberExpression.Identifier (_, {Identifier.name=id; _;}) -> id
| MemberExpression.MemberExpression e ->
normalize (JSX.MemberExpression e) in
_object ^ "." ^ (snd property).Identifier.name
)
in fun env start_loc ->
let openingElement = opening_element_without_lt env start_loc in
let children, closingElement =
let selfClosing = match snd openingElement with
| `Element e -> e.JSX.Opening.selfClosing
| `Fragment -> false in
if selfClosing
then [], `None
else begin
Eat.push_lex_mode env Lex_mode.JSX_CHILD;
let ret = children_and_closing env [] in
ret
end in
let end_loc = match closingElement with
| `Element (loc, { JSX.Closing.name }) ->
(match snd openingElement with
| `Element e ->
let opening_name = normalize e.JSX.Opening.name in
if normalize name <> opening_name
then error env (Error.ExpectedJSXClosingTag opening_name)
| `Fragment -> error env (Error.ExpectedJSXClosingTag "JSX fragment"));
loc
| `Fragment loc ->
(match snd openingElement with
| `Element e -> error env (Error.ExpectedJSXClosingTag (normalize e.JSX.Opening.name))
| _ -> ());
loc
| _ -> fst openingElement in
match snd openingElement with
| `Element e ->
Loc.btwn (fst openingElement) end_loc, `Element JSX.({
openingElement = (fst openingElement, e);
closingElement = (match closingElement with
| `Element e -> Some e
| _ -> None);
children;
})
| `Fragment ->
Loc.btwn (fst openingElement) end_loc, `Fragment JSX.({
frag_openingElement = fst openingElement;
frag_closingElement = (match closingElement with
| `Fragment loc -> Some loc
| _ -> None);
frag_children = children;
})
and element_or_fragment env =
let start_loc = Peek.loc env in
Eat.push_lex_mode env Lex_mode.JSX_TAG;
Expect.token env T_LESS_THAN;
element_without_lt env start_loc
end