2 * Copyright (c) 2015, Facebook, Inc.
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the "hack" directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
11 (* generated by ocamltarzan *)
21 =============================================================================
22 Unparsing is fairly complicated, especially with a language like php which has
23 many nuances. Instead of implementing the entire unparser at one go, I prefer to
24 implement it piecemeal as I come across constructs I care about for task at hand.
26 At the same time I do not want to depend on wildcards to identify what's not been
27 unparsed and I want the error message when I cannot unparse something to be clear.
29 To that extend I'm using ocamltarzan to generate the scaffold for the unparser,
30 it recursively unparses each element of every construct, but also raises TODO
31 by default until the particular construct is implemented.
33 Here are some helper functions for the same.
34 =================================================================================
37 These are todo constructs for which we will choose not to fail but to
38 instead return the default which is known to be incorrect or incomplete
40 let ok_todos = set_of_list
[
45 The first argument is the error message to raise and the second argument is
46 the autogenerated code that is not useful. The reason we retain the autogenerated
47 code is to not have to regenerate it later, and at the same time to not have it
48 return unused warnings.
50 let u_todo msg pregen_fn
=
51 if SSet.mem msg
ok_todos
53 else raise
(Todo
("unsupported construct in Unparser: " ^ msg
))
56 This is a more nuanced variation of u_todo. It accepts a list of
57 (cond, msg, pregen_fn), in each case, if the condition is true, we call
58 u_todo with the msg and pregen_fn, but when its false we fall through.
59 This makes it easier to implement just parts of the unparser for complex constructs
60 (like class definitions)
62 let u_todo_conds todos else_fn
=
63 match (List.find ~f
:(fun (cond
, _
, _
) -> cond
) todos
) with
64 | Some
(_
, msg
, fn
) -> u_todo msg fn
68 env is something that is used while unparsing so that we can handle special issues
69 where the code that is unparsed depends implicitly on the structure of some
70 condition of the ancestors in the AST rather than just the current node.
72 Examples are modes and namespaces. Currently I've introduced mode as an example.
75 mutable mode
: FileInfo.mode
79 functions and methods have a lot in common, but their own peculiarities also.
80 This captures all the common stuff to prevent having to repeat it while
84 fc_tparams
: tparam list
;
88 fc_params
: fun_param list
;
90 fc_user_attributes
: user_attribute list
;
91 fc_fun_kind
: fun_kind
;
94 (* for expressions that contain sub-expressions, this identifies
95 if the sub-expression needs to be parenthesised when it is inside another expression
97 type shouldParens
= YES
| NO
| TODO
of string
99 let is_associative = function
100 | Plus
| Star
| AMpamp
| BArbar
| Dot
101 | Amp
| Bar
| Xor
-> true
102 | Minus
| Slash
| Lt
| Lte
| Gt
| Gte
| Starstar
103 | Eqeq
| EQeqeq
| Diff
| Diff2
| Ltlt
104 | Gtgt
| Percent
| Eq _
-> false
106 module Unparse
= struct
108 unparsers for simple and predefined types.
110 let u_of_list_spc u_of_elem elems
=
111 List.map ~f
:u_of_elem elems
|>
114 let u_of_list_comma u_of_elem elems
=
115 List.map ~f
:u_of_elem elems
|>
116 fun x
-> StrCommaList x
118 let u_of_list_parens_comma u_of_elem elems
=
119 List.map ~f
:u_of_elem elems
|>
120 fun x
-> StrParens
(StrCommaList x
)
122 let u_of_list_braces_spc u_of_elem elems
=
123 List.map ~f
:u_of_elem elems
|>
124 fun x
-> StrBraces
(StrWords x
)
126 let u_of_bool b
= Str
(if b
then "true" else "false")
128 let u_of_float f
= Str
(string_of_float f
)
130 let u_of_option f
= function
134 let u_of_string str
= Str str
137 unparser for the AST. Autogenerated with ocamltarzan
140 let dummy_unparse_fn = fun () -> StrEmpty
144 | FileInfo.PhpFile
-> Str
"<?php"
145 | FileInfo.HhFile
-> Str
"<?hh"
147 let u_pos_t _
= StrEmpty
152 let u_pstring (_pos
, s
) =
155 let u_var_name (_pos
, s
) =
158 let u_of_smap _ _
= u_todo "smap" (fun () -> StrEmpty
)
161 (* FIXME: Don't use the default popt *)
162 if (ns
= Namespace_env.empty
ParserOptions.default
) then true else false
164 let rec u_program v
= u_of_list_spc u_def v
165 and u_in_mode _ f
= u_todo "mode" f
168 | Fun fun_
-> u_fun_ fun_
169 | Class v2
-> u_class_ v2
170 | Stmt stmt
-> u_stmt stmt
174 let v1 = Str
"Typedef" and v2
= u_typedef v2
in StrWords
[ v1; v2
])
175 | Constant v2
-> u_gconst v2
176 | Namespace
(id
, program
) ->
177 let strProgram = u_program program
178 in StrWords
[Str
"namespace"; u_id id
; StrBraces
strProgram]
179 | NamespaceUse uses
->
180 let u_use (kind
, (p1
, ns
), (p2
, name
)) =
181 let ns_end = List.last_exn
(R.split
(R.regexp
"\\\\") ns
) in
182 let qualifier = if ns_end <> name
183 then [Str
"as"; u_id (p2
, name
)]
185 let id_and_qualifier = u_id (p1
, ns
) :: qualifier in
186 let kind_id_and_qualifier = match kind
with
187 | NSClass
-> id_and_qualifier
188 | NSFun
-> Str
"function" :: id_and_qualifier
189 | NSConst
-> Str
"const" :: id_and_qualifier in
190 StrStatement
(Str
"use" :: kind_id_and_qualifier) in
191 u_of_list_spc u_use uses
194 t_id
= (pos
, _
) as v_t_id
;
195 t_tparams
= v_t_tparams
;
196 t_constraint
= v_t_constraint
;
198 t_user_attributes
= v_t_user_attributes
;
199 t_namespace
= v_t_namespace
;
202 invariant
(is_empty_ns v_t_namespace
)
203 (pos
, "Namespaces are expected to not be elaborated");
206 u_in_mode v_t_mode
(fun () ->
208 (v_t_user_attributes
<> [], "t_user_attributes",
209 (fun () -> u_of_smap u_user_attribute v_t_user_attributes
)) ;
211 let v_t_id = u_id v_t_id in
212 let v_t_tparams = u_of_list_spc u_tparam
v_t_tparams in
213 let v_t_constraint = u_tconstraint
v_t_constraint in
214 let v_t_kind = u_typedef_kind
v_t_kind in
215 StrWords
[v_t_id; v_t_tparams; v_t_constraint; v_t_kind])))
218 cst_mode
= v_cst_mode
;
219 cst_kind
= v_cst_kind
;
220 cst_name
= (pos
, _
) as v_cst_name
;
221 cst_type
= v_cst_type
;
222 cst_value
= v_cst_value
;
223 cst_namespace
= v_cst_namespace
225 u_in_mode v_cst_mode
(fun () ->
226 invariant
(is_empty_ns v_cst_namespace
)
227 (pos
, "Namespaces are expected to not be elaborated");
228 let v_cst_value = u_expr
v_cst_value in
229 match v_cst_kind
with
231 let v_cst_name = u_id v_cst_name in
232 let v_cst_type = u_of_option u_hint
v_cst_type in
241 invariant
(v_cst_type = None
) (pos
, "Constants using the define " ^
242 "syntax cannot use type hints");
245 StrParens
(StrCommaList
[
246 u_expr_
(String
v_cst_name);
252 | Covariant
-> u_todo "Covariant" (fun () -> StrEmpty
)
253 | Contravariant
-> u_todo "Contravariant" (fun () -> StrEmpty
)
254 | Invariant
-> u_todo "Invariant" (fun () -> StrEmpty
)
255 and u_constraint_kind
=
257 | Constraint_as
-> u_todo "as" (fun () -> StrEmpty
)
258 | Constraint_eq
-> u_todo "=" (fun () -> StrEmpty
)
259 | Constraint_super
-> u_todo "super" (fun () -> StrEmpty
)
260 and u_tparam
(v2
, v3
, v4
) =
263 let v1 = Str
"tparam"
264 and v2
= u_variance v2
266 and v4
= u_of_list_spc (fun (ck
, h
) ->
267 StrWords
[u_constraint_kind ck
; u_hint h
]) v4
268 in StrWords
[ v1; v2
; v3
; v4
])
269 and u_tconstraint v
= u_of_option u_hint v
275 let v1 = Str
"Alias" and v2
= u_hint v2
in StrWords
[ v1; v2
])
279 let v1 = Str
"NewType" and v2
= u_hint v2
in StrWords
[ v1; v2
])
283 c_user_attributes
= v_c_user_attributes
;
286 c_is_xhp
= v_c_is_xhp
;
287 c_name
= (pos
, _
) as v_c_name
;
288 c_tparams
= v_c_tparams
;
289 c_extends
= v_c_extends
;
290 c_implements
= v_c_implements
;
292 c_namespace
= v_c_namespace
;
296 u_in_mode v_c_mode
(fun () ->
297 invariant
(List.length v_c_extends
<= 1 || v_c_kind
= Cinterface
)
298 (pos
, "Multiple inheritance is not supported.");
299 invariant
(is_empty_ns v_c_namespace
)
300 (pos
, "Namespaces are expected to not be elaborated");
302 (v_c_is_xhp
, "c_is_xhp", (fun () -> u_of_bool v_c_is_xhp
)) ;
303 (not
(List.is_empty v_c_tparams
), "c_tparams", (fun () -> u_of_list_spc u_tparam v_c_tparams
)) ;
304 (v_c_user_attributes
<> [], "c_user_attributes",
305 (fun () -> u_of_smap u_user_attribute v_c_user_attributes
)) ;
306 (Option.is_some v_c_enum
, "c_enum", (fun () -> u_of_option u_enum_ v_c_enum
))
308 let u_elt = u_class_elt v_c_kind
in
309 let str_c_final = if v_c_final
then Str
"final" else StrEmpty
in
310 let str_c_kind = u_class_kind v_c_kind
in
311 let str_c_name = u_id v_c_name
in
312 let str_c_body = u_of_list_braces_spc u_elt v_c_body
in
313 let str_c_extends = u_extends v_c_extends
in
314 let str_c_implements = u_implements v_c_implements
in
323 and u_extends
= function
325 | hints
-> StrWords
[Str
"extends"; u_of_list_comma u_hint hints
]
326 and u_implements
= function
328 | implements
-> StrWords
[Str
"implements"; u_of_list_comma u_hint implements
]
329 and u_enum_
{ e_base
= v_e_base
; e_constraint
= v_e_constraint
} =
332 let v_e_base = u_hint
v_e_base in
333 let v_e_constraint = u_of_option u_hint
v_e_constraint
334 in StrWords
[ v_e_base; v_e_constraint ])
335 and u_user_attribute v
= u_of_list_spc u_expr v
338 | Cabstract
-> StrWords
[Str
"abstract"; Str
"class"]
339 | Cnormal
-> Str
"class"
340 | Cinterface
-> Str
"interface"
341 | Ctrait
-> Str
"trait"
342 | Cenum
-> u_todo "Cenum" (fun () -> StrEmpty
)
343 and u_trait_req_kind
=
345 | MustExtend
-> Str
"extends"
346 | MustImplement
-> Str
"implements"
347 and u_class_elt kind
=
349 | Const
(hOption
, decls
) ->
350 let hOptionStr = u_of_option u_hint hOption
351 and declsStr
= u_of_list_comma (fun (id
, expr
) ->
352 StrWords
[ u_id id
; Str
"="; u_expr expr
]) decls
353 in StrStatement
[ Str
"const" ; hOptionStr; declsStr
]
354 | AbsConst
(hOption
, name
) ->
355 let hOptionStr = u_of_option u_hint hOption
356 and nameStr
= u_id name
357 in StrStatement
[ Str
"abstract const" ; hOptionStr; nameStr
]
361 let v1 = Str
"Attributes"
362 and v2
= u_of_list_spc u_class_attr v2
363 in StrWords
[ v1; v2
])
365 StrStatement
[Str
"use"; u_hint hint
]
367 StrStatement
[Str
"attribute"; u_hint hint
]
368 | ClassTraitRequire
(trait_req_kind
, hint
) ->
371 u_trait_req_kind trait_req_kind
;
374 | ClassVars
(kinds
, hintOption
, classVars
) ->
375 let kindStr = u_of_list_spc u_kind kinds
376 and hintStr
= u_of_option u_hint hintOption
377 and varStr
= u_of_list_comma u_class_var classVars
in
378 StrStatement
[kindStr; hintStr
; varStr
]
380 u_todo "XhpAttr" (fun () -> StrEmpty
)
382 u_todo "XhpCategory" (fun () -> StrEmpty
)
383 | Method m
-> u_method_ kind m
384 | TypeConst _
-> u_todo "TypeConst" (fun () -> StrEmpty
)
391 let v1 = Str
"CA_name" and v2
= u_id v2
in StrWords
[ v1; v2
])
395 let v1 = Str
"CA_field"
396 and v2
= u_ca_field v2
397 in StrWords
[ v1; v2
])
402 ca_value
= v_ca_value
;
403 ca_required
= v_ca_required
407 let v_ca_type = u_ca_type
v_ca_type in
408 let v_ca_id = u_id v_ca_id in
409 let v_ca_value = u_of_option u_expr
v_ca_value in
410 let v_ca_required = u_of_bool v_ca_required
411 in StrWords
[ v_ca_type; v_ca_id; v_ca_value; v_ca_required ])
417 let v1 = Str
"CA_hint" and v2
= u_hint v2
in StrWords
[ v1; v2
])
421 let v1 = Str
"CA_enum"
422 and v2
= u_of_list_spc u_of_string v2
423 in StrWords
[ v1; v2
])
425 let s = match kind
with
428 | Abstract
-> "abstract"
429 | Private
-> "private"
431 | Protected
-> "protected" in
433 and u_class_var
(_
, id
, exprOpt
) =
434 let exprStr = match exprOpt
with
436 | Some expr
-> StrWords
[Str
"=" ; u_expr expr
] in
437 StrWords
[u_var_name id
; exprStr]
439 u_method_ class_kind
{
452 let str_m_kind = u_of_list_spc u_kind m_kind
454 fc_tparams
= m_tparams
;
456 fc_ret_by_ref
= m_ret_by_ref
;
458 fc_params
= m_params
;
460 fc_user_attributes
= m_user_attributes
;
461 fc_fun_kind
= m_fun_kind
;
463 and is_abstract
= class_kind
= Cinterface
|| List.mem m_kind Abstract
in
464 StrWords
[str_m_kind; u_fun_common v_f_common StrEmpty
u_id is_abstract
]
467 param_hint
= v_param_hint
;
468 param_is_reference
= v_param_is_reference
;
469 param_is_variadic
= v_param_is_variadic
;
470 param_id
= v_param_id
;
471 param_expr
= v_param_expr
;
472 param_modifier
= v_param_modifier
;
473 param_user_attributes
= v_param_user_attributes
476 (v_param_user_attributes
<> [], "param_user_attributes",
477 fun () -> u_of_smap u_user_attribute v_param_user_attributes
);
479 let str_param_mod = u_of_option u_kind v_param_modifier
480 and str_param_hint
= u_of_option u_hint v_param_hint
481 and str_param_id
= u_id v_param_id
in
482 let str_param_id = if v_param_is_reference
483 then StrList
[Str
"&"; str_param_id]
486 match (v_param_is_variadic
, str_param_id) with
487 | (true, Str
"...") (* hack variadic *) | (false, _
) -> str_param_id
488 | (true, _
) -> StrList
[Str
"..."; str_param_id] in
489 let str_param_expr = match v_param_expr
with
491 | Some e
-> StrWords
[Str
"="; u_expr e
] in
492 StrWords
[ str_param_mod; str_param_hint
; str_param_id; str_param_expr]
494 (* We have to treat the use clause specially because its in the middle of
495 a function, but only used by lambdas.
497 and u_fun_name name_parser id
=
499 | Str
";anonymous" -> StrEmpty
500 | _
-> name_parser id
510 } useStr u_of_name abstract
=
512 fc_user_attributes
<> [],
514 (fun () -> u_of_smap u_user_attribute fc_user_attributes
)
516 let str_tparams = match fc_tparams
with
518 | _
-> u_todo "fc_tparams" (fun () -> StrList
[Str
"<"; u_of_list_comma u_tparam fc_tparams
; Str
">"])
519 and str_params
= u_of_list_parens_comma u_fun_param fc_params
520 and str_body
= if abstract
then StrSemi
else u_block fc_body
521 and str_ret
= match fc_ret
with
523 | Some r
-> StrWords
[Str
":"; u_hint r
]
524 and str_fun_kind
= u_fun_kind fc_fun_kind
525 and str_name
= u_fun_name u_of_name fc_name
in
526 let str_name = if fc_ret_by_ref
527 then StrList
[Str
"&"; str_name]
546 f_name
= (pos
, _
) as f_name
;
554 u_in_mode f_mode
begin fun () ->
555 invariant
(is_empty_ns f_namespace
)
556 (pos
, "Namespaces are expected to not be elaborated");
558 fc_tparams
= f_tparams
;
560 fc_ret_by_ref
= f_ret_by_ref
;
562 fc_params
= f_params
;
564 fc_user_attributes
= f_user_attributes
;
565 fc_fun_kind
= f_fun_kind
;
566 } useStr
(u_id) false
569 u_fun_ fun_
= u_fun_with_use fun_ StrEmpty
572 | FAsync
| FAsyncGenerator
-> Str
"async"
573 | FSync
| FGenerator
-> StrEmpty
574 and u_hint
(v2
, v3
) = StrList
[u_pos_t v2
; u_hint_ v3
]
577 | Hoption hint
-> StrList
[Str
"?"; u_hint hint
]
578 | Hfun
((v2
, v3
, v4
)) ->
582 and v2
= u_of_list_spc u_hint v2
583 and v3
= u_of_bool v3
585 in StrWords
[ v1; v2
; v3
; v4
])
589 let v1 = Str
"Htuple"
590 and v2
= u_of_list_spc u_hint v2
591 in StrWords
[ v1; v2
])
594 (List.length v3
<> 0, "Happly", (fun () -> u_of_list_spc u_hint v3
))
595 ] (fun () -> u_id v2
)
599 let v1 = Str
"Hshape"
600 and v2
= u_of_list_spc u_shape_field v2
601 in StrWords
[ v1; v2
])
602 | Haccess _
-> u_todo "Haccess" (fun () -> StrEmpty
)
603 and u_shape_field_optional
=
605 | true -> [Str
"optional"]
607 and u_shape_field_name
=
612 let v1 = Str
"SFlit" and v2
= u_pstring v2
in StrWords
[ v1; v2
])
613 | SFclass_const
((v2
, v3
)) ->
614 u_todo "SFclass_const"
616 let v1 = Str
"SFclass_const"
618 and v3
= u_pstring v3
619 in StrWords
[ v1; v2
; v3
])
620 and u_shape_field
{ sf_optional
; sf_name
; sf_hint
} =
625 :: u_shape_field_optional sf_optional
626 @ [u_shape_field_name sf_name
; u_hint sf_hint
] in
629 let stmtStr = match stmt
with
630 | Unsafe
-> StrComment
"UNSAFE"
631 | Fallthrough
-> StrComment
"FALLTHROUGH"
632 | Expr expr
-> u_expr expr
633 | Block stmts
-> u_block stmts
634 | Break _pos
-> Str
"break"
635 | Continue _pos
-> Str
"continue"
636 | Throw expr
-> StrWords
[Str
"throw"; u_expr expr
]
637 | Return
(_pos
, exprOpt
) ->
638 let exprStr = u_of_option u_expr exprOpt
in
639 StrWords
[ Str
"return"; exprStr]
640 | Static_var exprList
->
641 StrWords
[Str
"static"; u_of_list_comma u_expr exprList
;]
642 | If
(cond
, thenExpr
, elseExpr
) ->
643 let condStr = StrParens
(u_expr cond
)
644 and thenStr
= u_block thenExpr
645 and elseStr
= match elseExpr
with
647 | _
-> StrWords
[Str
"else"; u_block elseExpr
]
648 in StrWords
[ Str
"if"; condStr; thenStr
; elseStr
]
649 | Do
(block
, expr
) ->
654 StrParens
(u_expr expr
)
656 | While
(expr
, block
) ->
659 StrParens
(u_expr expr
);
662 | For
(initExpr
, testExpr
, loopExpr
, block
) ->
665 StrParens
(StrSemiList
[
672 | Switch
(expr
, cases
) ->
675 StrParens
(u_expr expr
);
676 u_of_list_braces_spc u_case cases
;
678 | Foreach
(expr
, _posOption
, as_expr
, block
) ->
681 StrParens
(StrWords
[
687 | Try
(try_block
, catches
, finally_block
) ->
688 dn
(string_of_int
(List.length finally_block
));
689 let strFinally = match finally_block
with
690 | [] | [Noop
] -> StrEmpty
691 | _
-> StrWords
[Str
"finally"; u_block finally_block
] in
692 StrWords
[Str
"try"; u_block try_block
; u_of_list_spc u_catch catches
; strFinally]
693 | Noop
-> StrEmpty
in
704 | Fallthrough
-> stmtStr
711 | Static_var _
-> StrStatement
[stmtStr]
713 and u_catch
(hint
, var
, block
) =
716 StrParens
(u_of_list_spc u_id [hint
; var
;]);
722 | As_v expr
-> StrWords
[Str
"as"; u_expr expr
]
723 | As_kv
(kExpr
, vExpr
) -> StrWords
[Str
"as"; u_expr kExpr
; Str
"=>"; u_expr vExpr
]
724 and u_block v
= match v
with
725 | [Block v
] -> u_block v
726 | _
-> u_of_list_braces_spc u_stmt v
727 and u_naked_block v
= u_of_list_spc u_stmt v
729 unparses an expression nested inside another expression.
730 introduces an additional set of parens if necessary.
732 and u_expr_nested
(_pos
, expr_
) =
733 let res = u_expr_ expr_
in
734 let todo_with s = u_todo ("Parens " ^
s) (fun () -> res) in
741 | Id_type_arguments _
754 | Unsafeexpr _
-> res
771 | Await _
-> StrParens
res
772 | Shape _
-> todo_with "shape"
773 | Xml _
-> todo_with "xml"
774 | Dollardollar
-> todo_with "Dollardollar"
775 and u_expr
(_pos
, expr_
) = u_expr_ expr_
778 | Array v2
-> StrList
[Str
"array"; u_of_list_parens_comma u_afield v2
]
789 and v2
= u_shape_field_name v2
791 in StrWords
[ v1; v2
; v3
]))
793 in StrWords
[ v1; v2
])
795 u_todo "Dollardollar"
798 | Collection
(id
, afields
) ->
800 and fieldStr
= StrBraces
(u_of_list_comma u_afield afields
) in
801 StrWords
[idStr; fieldStr
;]
804 | False
-> Str
"false"
806 | Id_type_arguments
(id
, hintExprs
) ->
807 let hintStr = StrAngles
(StrCommaList
(List.map ~f
:u_hint hintExprs
)) in
808 StrList
[u_id id
; hintStr]
809 | Lvar lvar
-> u_id lvar
810 | Lvarvar
(n
, lvar
) -> begin
811 let p, var_id
= lvar
in
812 u_id (p, (String.make n '$'
) ^ var_id
)
814 | Clone expr
-> StrWords
[Str
"clone"; u_expr_nested expr
]
815 | Obj_get
(objExpr
, itemExpr
, null_flavor
) ->
816 let objStr = u_expr_nested objExpr
817 and itemStr
= u_expr itemExpr
818 and operString
= match null_flavor
with
819 | OG_nullthrows
-> "->"
820 | OG_nullsafe
-> "?->"
821 in StrList
[ objStr; Str operString
; itemStr
]
822 | Array_get
(exprArr
, exprKey
) ->
823 let exprArrStr = u_expr_nested exprArr
824 and exprKeyStr
= u_of_option u_expr exprKey
in
825 StrList
[exprArrStr; Str
"["; exprKeyStr
; Str
"]"]
826 | Class_get
(hintExpr
, getExpr
) ->
827 let hintStr = u_id hintExpr
828 and getStr
= u_pstring getExpr
in
829 StrList
[hintStr; Str
"::"; getStr
]
830 | Class_const
(hintExpr
, constExpr
) ->
831 let hintStr = u_id hintExpr
832 and constStr
= u_pstring constExpr
in
833 StrList
[hintStr; Str
"::"; constStr
]
834 | Call
(funExpr
, paramExprs
, unpackParamExprs
) ->
835 let funStr = u_expr_nested funExpr
in
836 let paramStr = match funStr with
837 | Str
"echo" when List.length paramExprs
> 1 ->
838 if unpackParamExprs
<> [] then
839 u_todo "echo with ... ?" (fun () -> StrEmpty
)
840 else StrList
[StrBlank
; u_of_list_comma u_expr paramExprs
]
843 (List.map ~f
:u_expr paramExprs
) @
844 (List.map ~f
:(fun e
-> StrList
[Str
"..." ; u_expr e
]) unpackParamExprs
) in
845 StrParens
(StrCommaList
listExprs)
846 in StrList
[ funStr; paramStr ]
847 | Int i
-> u_pstring i
848 | Float f
-> u_pstring f
850 StrList
[Str
"\""; u_pstring (p, Php_escaping.escape
s); Str
"\""]
852 (* build the string back by concatenating the parts *)
853 List.map ~f
:u_expr elems
|>
854 fun els
-> StrList
(List.intersperse ~sep
:(Str
".") els
)
856 StrWords
[Str
"yield"; u_afield afield
]
857 | Yield_break
-> u_todo "Yield_break" (fun () -> StrEmpty
)
859 StrWords
[Str
"await"; u_expr v2
]
861 StrList
[Str
"list"; u_of_list_parens_comma u_expr exprs
]
863 u_of_list_comma u_expr exprs
864 | Cast
(hint
, expr
) ->
865 StrList
[StrParens
(u_hint hint
); u_expr_nested expr
];
866 | Unop
(uop
, expr
) -> u_uop expr uop
867 | Binop
(bop
, e1
, e2
) -> u_bop e1 e2 bop
868 (** The pipe ID is only used for typechecking phase. *)
869 | Pipe
(e1
, e2
) -> u_pipe e1 e2
870 | Eif
(condExpr
, trueExprOption
, falseExpr
) ->
872 u_expr_nested condExpr
;
874 u_of_option u_expr_nested trueExprOption
;
876 u_expr_nested falseExpr
;
878 | NullCoalesce
(trueExpr
, falseExpr
) ->
880 u_expr_nested trueExpr
;
882 u_expr_nested falseExpr
;
884 | InstanceOf
(instExpr
, hintExpr
) ->
886 u_expr_nested instExpr
;
888 u_expr_nested hintExpr
;
890 | New
(klass
, paramExprs
, unpackParamExprs
) ->
891 let klassStr = u_expr klass
in
893 (List.map ~f
:u_expr paramExprs
) @
894 (List.map ~f
:(fun e
-> StrList
[Str
"..." ; u_expr e
]) unpackParamExprs
) in
895 let paramStr = StrParens
(StrCommaList
listExprs) in
896 StrList
[ Str
"new"; StrBlank
; klassStr; paramStr]
897 | Efun
(fun_
, uselist
) ->
898 let useStr = match uselist
with
900 | _
-> StrList
[Str
"use"; u_of_list_parens_comma u_use uselist
] in
901 u_fun_with_use fun_
useStr
905 let v1 = Str
"Lfun" and v2
= u_fun_ v2
in StrWords
[ v1; v2
])
906 | Xml
((v2
, v3
, v4
)) ->
919 in StrWords
[ v1; v2
; v3
]))
921 and v4
= u_of_list_spc u_expr v4
922 in StrWords
[ v1; v2
; v3
; v4
])
924 StrWords
[Str
"/* UNSAFE_EXPR */"; u_expr expr
]
925 | Import
(flavor
, expr
) ->
926 StrWords
[u_import_flavor flavor
; StrParens
(u_expr expr
)]
927 and u_import_flavor
=
929 | Require
-> Str
"require"
930 | Include
-> Str
"include"
931 | RequireOnce
-> Str
"require_once"
932 | IncludeOnce
-> Str
"include_once"
933 and u_use (id
, is_ref
) =
935 and str_ref
= if is_ref
then Str
"&" else StrBlank
936 in StrList
[str_ref
; str_id]
939 | AFvalue v2
-> u_expr v2
940 | AFkvalue
(kExpr
, vExpr
) -> StrWords
[u_expr kExpr
; Str
"=>"; u_expr vExpr
]
941 and u_bop e1 e2 bop
=
942 let bop_str = function
946 | Starstar
-> Str
"**"
949 | EQeqeq
-> Str
"==="
965 | _
-> raise Impossible
in
969 StrList
[u_of_option bop_str b
; Str
"="];
973 let e1 = u_expr_in_bop bop
e1 in
974 let e2 = u_expr_in_bop bop
e2 in
975 let s = bop_str bop
in
977 and u_expr_in_bop bop expr
=
979 | (_
, Binop
(b
,_
,_
)) when bop
= b
&& is_associative bop
-> u_expr expr
980 | _
-> u_expr_nested expr
982 StrWords
[u_expr
e1; Str
"|>"; u_expr
e2]
984 let prefix_with s = StrList
[Str
s; u_expr_nested expr
] in
986 | Upincr
-> StrList
[u_expr_nested expr
; Str
"++"]
987 | Updecr
-> StrList
[u_expr_nested expr
; Str
"--"]
988 | Utild
-> prefix_with "~"
989 | Unot
-> prefix_with "!"
990 | Uplus
-> prefix_with "+"
991 | Uminus
-> prefix_with "-"
992 | Uincr
-> prefix_with "++"
993 | Udecr
-> prefix_with "--"
994 | Uref
-> prefix_with "&"
996 let case_expr name block
=
997 StrWords
[name
; Str
":"; u_naked_block block
;] in
1000 case_expr (Str
"default") block
1001 | Case
(expr
, block
) ->
1002 case_expr (StrWords
[Str
"case"; u_expr expr
;]) block
1003 let unparser _env
= u_program
1008 let unparse_internal program
=
1010 we feed the output of the unparser to the hack formatter which only
1011 accepts files with hh prefix, so we have to fake it.
1014 u_file_type FileInfo.HhFile
;
1015 unparser {mode
= FileInfo.Mdecl
} program
1019 FileInfo.file_type
-> Path.t
-> program
-> string =
1020 fun filetype file program
->
1021 unparse_internal program
|>
1025 let modes = [Some
FileInfo.Mstrict
; Some
FileInfo.Mpartial
] in
1026 let formatted = Format_hack.program
modes file ~no_trailing_commas
:true s in
1027 let s'
= match formatted with
1028 | Format_hack.Disabled_mode
-> raise Impossible
1029 | Format_hack.Internal_error
-> raise
(FormatterError
"")
1030 | Format_hack.Success
s'
-> s'
1031 | Format_hack.Parsing_error error
-> raise
(FormatterError
1032 ("parsing error \n" ^
(Errors.to_string
(Errors.to_absolute
1033 (List.hd_exn error
))))) in
1035 | FileInfo.HhFile
-> s'
1036 | FileInfo.PhpFile
-> let r = R.regexp
"<\\?hh" in
1037 R.replace_first
r "<?php" s'