2 * Copyright (c) 2014, 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.
13 module SMap
= Utils.SMap
16 (*****************************************************************************)
18 (*****************************************************************************)
24 errors
: (Pos.t
* string) list
ref;
34 type parser_return
= {
35 (* True if we are dealing with a hack file *)
37 comments
: (Pos.t
* string) list
;
41 (*****************************************************************************)
42 (* Lexer (with backtracking) *)
43 (*****************************************************************************)
46 (* no need to save refill_buff because it's constant *)
51 lex_last_action
: int;
52 lex_eof_reached
: bool;
54 lex_start_p
: Lexing.position
;
55 lex_curr_p
: Lexing.position
;
58 let save_lexbuf_state (lb
: Lexing.lexbuf
) : saved_lb
=
60 lex_abs_pos
= lb
.Lexing.lex_abs_pos
;
61 lex_start_pos
= lb
.Lexing.lex_start_pos
;
62 lex_curr_pos
= lb
.Lexing.lex_curr_pos
;
63 lex_last_pos
= lb
.Lexing.lex_last_pos
;
64 lex_last_action
= lb
.Lexing.lex_last_action
;
65 lex_eof_reached
= lb
.Lexing.lex_eof_reached
;
66 lex_mem
= lb
.Lexing.lex_mem
;
67 lex_start_p
= lb
.Lexing.lex_start_p
;
68 lex_curr_p
= lb
.Lexing.lex_curr_p
;
71 let restore_lexbuf_state (lb
: Lexing.lexbuf
) (saved
: saved_lb
) : unit =
73 lb
.Lexing.lex_abs_pos
<- saved
.lex_abs_pos
;
74 lb
.Lexing.lex_start_pos
<- saved
.lex_start_pos
;
75 lb
.Lexing.lex_curr_pos
<- saved
.lex_curr_pos
;
76 lb
.Lexing.lex_last_pos
<- saved
.lex_last_pos
;
77 lb
.Lexing.lex_last_action
<- saved
.lex_last_action
;
78 lb
.Lexing.lex_eof_reached
<- saved
.lex_eof_reached
;
79 lb
.Lexing.lex_mem
<- saved
.lex_mem
;
80 lb
.Lexing.lex_start_p
<- saved
.lex_start_p
;
81 lb
.Lexing.lex_curr_p
<- saved
.lex_curr_p
;
85 * Call a function with a forked lexing environment, and return its
88 let look_ahead (env
: env
) (f
: env
-> 'a
) : 'a
=
89 let saved = save_lexbuf_state env
.lb
in
91 restore_lexbuf_state env
.lb
saved;
95 * Conditionally parse, saving lexer state in case we need to backtrack.
96 * The function parameter returns any optional type. If it's None, pop
97 * lexer state on the way out.
99 * Note that you shouldn't add any errors to the environment before
100 * you've committed to returning Some something. The error state is not
103 let try_parse (env
: env
) (f
: env
-> 'a
option) : 'a
option =
104 let saved = save_lexbuf_state env
.lb
in
107 | None
-> (restore_lexbuf_state env
.lb
saved; None
)
109 (* Return the next token without updating lexer state *)
111 let saved = save_lexbuf_state env
.lb
in
112 let ret = L.token env
.lb
in
113 restore_lexbuf_state env
.lb
saved;
116 (* Drop the next token unconditionally *)
117 let drop (env
: env
) : unit = match L.token env
.lb
with _
-> ()
119 let btw (p1
, _
) (p2
, _
) = Pos.btw p1 p2
121 let is_hh_file = ref false
123 (*****************************************************************************)
125 (*****************************************************************************)
127 let error_at env pos msg
=
128 env
.errors
:= (pos
, msg
) :: !(env
.errors
)
131 error_at env
(Pos.make env
.lb
) msg
133 let error_continue env
=
135 "Yeah...we're not going to support continue/break N. \
136 It makes static analysis tricky and it's not really essential"
138 let error_expect env expect
=
139 let pos = Pos.make env
.lb
in
141 env
.errors
:= (pos, "Expected "^expect
) :: !(env
.errors
)
144 if L.token env
.lb
= x
146 else error_expect env
(L.token_to_string x
)
148 let expect_word env name
=
149 let tok = L.token env
.lb
in
150 let value = Lexing.lexeme env
.lb
in
151 if tok <> Tword
|| value <> name
152 then error_expect env
("Was expecting: '"^name^
"' (not '"^
value^
"')");
155 (*****************************************************************************)
156 (* Modifiers checks (public private, final abstract etc ...) *)
157 (*****************************************************************************)
159 let rec check_modifiers env
pos abstract final
= function
161 | Final
:: _
when abstract
->
162 error_at env
pos "Parse error. Cannot mix final and abstract"
163 | Abstract
:: _
when final
->
164 error_at env
pos "Parse error. Cannot mix final and abstract"
165 | Final
:: rl
-> check_modifiers env
pos abstract
true rl
166 | Abstract
:: rl
-> check_modifiers env
pos true final rl
167 | _
:: rl
-> check_modifiers env
pos abstract final rl
169 let check_visibility env
pos l
=
170 if List.exists
begin function
171 | Private
| Public
| Protected
| Static
-> true
175 else error_at env
pos
176 "Parse error. You are missing public, private or protected."
178 let rec check_mix_visibility env
pos last_vis
= function
180 | (Private
| Public
| Protected
as vis
) :: rl
->
182 | Some vis2
when vis
<> vis2
->
184 "Parse error. Cannot mix different visibilities."
186 check_mix_visibility env
pos (Some vis
) rl
188 | _
:: rl
-> check_mix_visibility env
pos last_vis rl
190 let rec check_duplicates env
pos = function
192 | Private
:: rl
-> check_duplicates env
pos rl
193 | x
:: (y
:: _
) when x
= y
->
194 error_at env
pos "Parse error. Duplicate modifier"
195 | _
:: rl
-> check_duplicates env
pos rl
197 let check_modifiers env
pos l
=
198 check_visibility env
pos l
;
199 check_modifiers env
pos false false l
;
200 check_duplicates env
pos (List.sort compare l
);
201 check_mix_visibility env
pos None l
;
204 let check_not_final env
pos modifiers
=
205 if List.exists
(function Final
-> true | _
-> false) modifiers
206 then error_at env
pos "class variable cannot be final";
209 let check_toplevel env
pos =
210 if env
.mode
= Ast.Mstrict
211 then error_at env
pos "Remove all toplevel statements except for requires"
213 (*****************************************************************************)
214 (* Check expressions. *)
215 (*****************************************************************************)
217 let rec check_lvalue env
= function
218 | _
, (Lvar _
| Obj_get _
| Array_get _
| Class_get _
) -> ()
219 | pos, Call
((_
, Id
(_
, "tuple")), _
) ->
221 "Tuple cannot be used as an lvalue. Maybe you meant List?"
222 | _
, List el
-> List.iter
(check_lvalue env
) el
223 | pos, (Array _
| Shape _
| Collection _
224 | Null
| True
| False
| Id _
| Clone _
225 | Class_const _
| Call _
| Int _
| Float _
226 | String _
| String2 _
| Yield _
| Yield_break
227 | Await _
| Expr_list _
| Cast _
| Unop _
|
228 Binop _
| Eif _
| InstanceOf _
| New _
| Efun _
| Lfun _
| Xml _
) ->
229 error_at env
pos "Invalid lvalue"
231 (*****************************************************************************)
232 (* Operator priorities.
234 * It is annoying to deal with priorities by hand (although it's possible).
235 * This list mimics what would typically look like yacc rules, defining
236 * the operators priorities (from low to high), and associativity (left, right
239 * The priorities are then used by the "reducer" to auto-magically parse
240 * expressions in the right order (left, right, non-assoc) and with the right
241 * priority. Checkout the function "reduce" for more details.
243 (*****************************************************************************)
246 | Left
(* a <op> b <op> c = ((a <op> b) <op> c) *)
247 | Right
(* a <op> b <op> c = (a <op> (b <op> c)) *)
248 | NonAssoc
(* a <op> b <op> c = error *)
251 (* Lowest priority *)
252 (Left
, [Tinclude
; Tinclude_once
; Teval
; Trequire
; Trequire_once
]);
255 (Left
, [Tqm
; Tcolon
]);
261 (NonAssoc
, [Teqeq
; Tdiff
; Teqeqeq
; Tdiff2
]);
262 (NonAssoc
, [Tlt
; Tlte
; Tgt
; Tgte
]);
263 (Left
, [Tltlt
; Tgtgt
]);
264 (Left
, [Tplus
; Tminus
; Tdot
]);
265 (Left
, [Tstar
; Tslash
; Tpercent
]);
267 (NonAssoc
, [Tinstanceof
]);
268 (Right
, [Ttild
; Tincr
; Tdecr
; Tcast
]);
269 (Right
, [Tat
; Tref
]);
270 (NonAssoc
, [Tyield
]);
271 (NonAssoc
, [Tawait
]);
273 (NonAssoc
, [Tnew
; Tclone
]);
275 (Right
, [Teq
; Tpluseq
; Tminuseq
; Tstareq
;
276 Tslasheq
; Tdoteq
; Tpercenteq
;
277 Tampeq
; Tbareq
; Txoreq
; Tlshifteq
; Trshifteq
]);
284 (* Highest priority *)
288 (* Creating the table of assocs/priorities at initialization time. *)
289 let ptable = Hashtbl.create
23 in
290 (* Lowest priority = 0 *)
291 let priority = ref 0 in
292 List.iter
begin fun (assoc
, tokl
) ->
293 List.iter
begin fun token
->
294 (* Associates operator => (associativity, priority) *)
295 Hashtbl.add
ptable token
(assoc
, !priority)
297 (* This is a bit subtle:
299 * The difference in priority between 2 lines should be 2, not 1.
301 * It's because of a trick we use in the reducer.
302 * For something to be left-associative, we just pretend
303 * that the right hand side expression has a higher priority.
307 * reduce (e1 = 1) "2 + 3" // priority = 0
308 * reduce (e1 = 1) (expr "2 + 3" with priority+1)
309 * reduce (e1 = 1) (2, "+ 3") <--- this is where the trick is:
310 * because we made the priority higher
311 * the reducer stops when it sees the
314 priority := !priority + 2
317 assert (Hashtbl.mem
ptable tok);
318 Hashtbl.find
ptable tok
320 let with_priority env op f
=
321 let _, prio
= get_priority op
in
322 let env = { env with priority = prio
} in
325 let with_base_priority env f
=
326 let env = { env with priority = 0 } in
329 (*****************************************************************************)
331 (*****************************************************************************)
334 match L.token
env.lb
with
335 | Tamp
when env.mode
= Ast.Mstrict
->
336 error env "Don't use references!"
342 (*****************************************************************************)
344 (*****************************************************************************)
348 match L.token
env.lb
with
350 let pos = Pos.make
env.lb
in
351 let name = Lexing.lexeme
env.lb
in
354 (match L.xhpname
env.lb
with
356 Pos.make
env.lb
, ":"^
Lexing.lexeme
env.lb
358 error_expect env "identifier";
359 Pos.make
env.lb
, "*Unknown*"
362 error_expect env "identifier";
363 Pos.make
env.lb
, "*Unknown*"
367 match L.token
env.lb
with
369 Pos.make
env.lb
, Lexing.lexeme
env.lb
371 error_expect env "variable";
372 Pos.make
env.lb
, "$_"
375 let ref_variable env =
382 let is_variadic = match L.token
env.lb
with
384 | _ -> L.back
env.lb
; false
386 let var = variable env in
389 (*****************************************************************************)
391 (*****************************************************************************)
393 let rec program content
=
395 L.comment_list
:= [];
396 let lb = Lexing.from_string content
in
397 let env = init_env lb in
398 let ast = header
env in
399 let comments = !L.comment_list
in
400 L.comment_list
:= [];
401 if !(env.errors
) <> []
402 then Errors.parsing_error
(List.hd
(List.rev
!(env.errors
)));
403 let is_hh_file = !is_hh_file in
404 let ast = Namespaces.elaborate_defs
ast in
405 {is_hh_file; comments; ast}
407 (*****************************************************************************)
408 (* Hack headers (strict, decl, partial) *)
409 (*****************************************************************************)
412 let file_type, head
= get_header
env in
413 match file_type, head
with
415 | _, Some
Ast.Mdecl
->
416 let env = { env with mode
= Ast.Mdecl
} in
417 let attr = SMap.empty
in
418 let result = ignore_toplevel ~
attr [] env (fun x
-> x
= Teof
) in
420 if head
= Some
Ast.Mdecl
then is_hh_file := true;
423 let result = toplevel
[] { env with mode
= mode
} (fun x
-> x
= Teof
) in
431 match L.header
env.lb with
432 | `
error -> Ast.HhFile
, None
433 | `default_mode
-> Ast.HhFile
, Some
Ast.Mpartial
434 | `php_decl_mode
-> Ast.PhpFile
, Some
Ast.Mdecl
435 | `php_mode
-> Ast.PhpFile
, None
437 let _token = L.token
env.lb in
438 (match Lexing.lexeme
env.lb with
439 | "strict" when !(Ide.is_ide_mode
) -> Ast.HhFile
, Some
Ast.Mpartial
440 | "strict" -> Ast.HhFile
, Some
Ast.Mstrict
441 | ("decl"|"only-headers") -> Ast.HhFile
, Some
Ast.Mdecl
442 | "partial" -> Ast.HhFile
, Some
Ast.Mpartial
445 "Incorrect comment; possible values include strict, decl, partial or empty";
446 Ast.HhFile
, Some
Ast.Mdecl
449 (*****************************************************************************)
451 (*****************************************************************************)
453 and ignore_toplevel ~
attr acc
env terminate
=
454 match L.token
env.lb with
455 | x
when terminate x
->
459 (* Parsing attribute << .. >> *)
460 let attr = attribute_remain
env SMap.empty
in
461 ignore_toplevel ~
attr acc
env terminate
463 let acc = ignore_toplevel ~
attr acc env terminate
in
464 ignore_toplevel ~
attr acc env terminate
466 let pos = Pos.make
env.lb in
467 let abs_pos = env.lb.Lexing.lex_curr_pos
in
468 ignore
(expr_string
env pos abs_pos);
469 ignore_toplevel ~
attr acc env terminate
471 let pos = Pos.make
env.lb in
472 ignore
(expr_encapsed
env pos);
473 ignore_toplevel ~
attr acc env terminate
475 ignore
(expr_heredoc
env);
476 ignore_toplevel ~
attr acc env terminate
477 | Tlt
when is_xhp
env ->
479 ignore_toplevel ~
attr acc env terminate
481 (match Lexing.lexeme
env.lb with
483 (match L.token
env.lb with
486 let def = toplevel_word ~
attr env "function" in
487 ignore_toplevel ~
attr:SMap.empty
(def @ acc) env terminate
488 (* function &foo(...), we still want them in decl mode *)
490 (match L.token
env.lb with
493 let def = toplevel_word ~
attr env "function" in
494 ignore_toplevel ~
attr:SMap.empty
(def @ acc) env terminate
496 ignore_toplevel ~
attr acc env terminate
499 ignore_toplevel ~
attr acc env terminate
501 | "abstract" | "final"
502 | "class"| "trait" | "interface"
504 | "async" | "newtype"| "type"| "const" ->
505 (* Parsing toplevel declarations (class, function etc ...) *)
506 let def = toplevel_word ~
attr env (Lexing.lexeme
env.lb) in
507 ignore_toplevel ~
attr:SMap.empty
(def @ acc) env terminate
508 | _ -> ignore_toplevel ~
attr acc env terminate
511 error env "Hack does not allow the closing ?> tag";
513 | _ -> ignore_toplevel ~
attr acc env terminate
515 (*****************************************************************************)
516 (* Toplevel statements. *)
517 (*****************************************************************************)
519 and toplevel
acc env terminate
=
520 match L.token
env.lb with
521 | x
when terminate x
->
525 (* Ignore extra semicolons at toplevel (important so we don't yell about
526 * them in strict mode). *)
527 toplevel
acc env terminate
529 (* Parsing attribute << .. >> *)
530 let attr = attribute_remain
env SMap.empty
in
531 let _ = L.token
env.lb in
532 let def = toplevel_word ~
attr env (Lexing.lexeme
env.lb) in
533 toplevel
(def @ acc) env terminate
535 (* Parsing toplevel declarations (class, function etc ...) *)
536 let attr = SMap.empty
in
537 let def = toplevel_word ~
attr env (Lexing.lexeme
env.lb) in
538 toplevel
(def @ acc) env terminate
540 error env "Hack does not allow the closing ?> tag";
543 (* All the other statements. *)
544 let pos = Pos.make
env.lb in
546 let error_state = !(env.errors
) in
547 let stmt = Stmt
(statement
env) in
548 check_toplevel env pos;
549 if error_state != !(env.errors
)
550 then ignore_toplevel ~
attr:SMap.empty
(stmt :: acc) env terminate
551 else toplevel
(stmt :: acc) env terminate
553 and toplevel_word ~
attr env = function
555 expect_word env "class";
556 let class_ = class_ ~
attr ~final
:false ~kind
:Cabstract
env in
559 expect_word env "class";
560 let class_ = class_ ~
attr ~final
:true ~kind
:Cnormal
env in
563 let class_ = class_ ~
attr ~final
:false ~kind
:Cnormal
env in
566 let class_ = class_ ~
attr ~final
:false ~kind
:Ctrait
env in
569 let class_ = class_ ~
attr ~final
:false ~kind
:Cinterface
env in
572 expect_word env "function";
573 let fun_ = fun_ ~
attr ~sync
:FAsync
env in
576 let fun_ = fun_ ~
attr ~sync
:FSync
env in
579 let id, tparaml
, tconstraint
, typedef
= typedef
env in
583 t_constraint
= tconstraint
;
584 t_kind
= NewType typedef
;
585 t_namespace
= Namespace_env.empty
;
589 let id, tparaml
, tconstraint
, typedef
= typedef
env in
593 t_constraint
= tconstraint
;
594 t_kind
= Alias typedef
;
595 t_namespace
= Namespace_env.empty
;
599 let id, body
= namespace
env in
600 (* Check for an empty name and omit the Namespace wrapper *)
603 | _ -> [Namespace
(id, body
)])
605 let usel = namespace_use_list
env [] in
608 let consts = class_const_def
env in
611 List.map
(fun (x
, y
) -> Constant
{
613 cst_kind
= Cst_const
;
617 cst_namespace
= Namespace_env.empty
;
620 | "require" | "require_once" ->
625 let pos = Pos.make
env.lb in
627 let stmt = statement
env in
628 check_toplevel env pos;
629 [define_or_stmt
env stmt]
631 and define_or_stmt
env = function
632 | Expr
(_, Call
((_, Id
(_, "define")), [(_, String
name); value])) ->
635 cst_kind
= Cst_define
;
639 cst_namespace
= Namespace_env.empty
;
644 (*****************************************************************************)
645 (* Attributes: <<_>> *)
646 (*****************************************************************************)
650 let acc = SMap.empty
in
651 if look_ahead env (fun env -> L.token
env.lb = Tltlt
)
654 attribute_remain
env acc;
659 and attribute_remain
env acc =
660 match L.token
env.lb with
662 let attr_name = Lexing.lexeme
env.lb in
663 let acc = attribute_parameter
attr_name acc env in
664 attribute_list_remain
acc env
666 error_expect env "attribute name";
669 (* empty | (parameter_list) *)
670 and attribute_parameter
attr_name acc env =
671 match L.token
env.lb with
673 let el = expr_list_remain
env in
674 SMap.add
attr_name el acc
676 let acc = SMap.add
attr_name [] acc in
681 and attribute_list_remain
acc env =
682 match L.token
env.lb with
684 | Tcomma
-> attribute_remain
env acc
686 error_expect env ">>";
689 (*****************************************************************************)
691 (*****************************************************************************)
693 and fun_ ~
attr ~sync
env =
695 let name = identifier env in
696 let tparams = class_params
env in
697 let params = parameter_list
env in
698 let ret = hint_return_opt
env in
699 let body = function_body
env in
705 f_user_attributes
= attr;
709 f_namespace
= Namespace_env.empty
;
712 (*****************************************************************************)
714 (*****************************************************************************)
716 and class_ ~
attr ~final ~kind
env =
717 let cname = identifier env in
718 let is_xhp = (snd
cname).[0] = '
:'
in
719 let tparams = class_params
env in
720 let cextends = class_extends
env in
721 let cimplements = class_implements
env in
722 let cbody = class_body
env in
728 c_implements
= cimplements;
730 c_user_attributes
= attr;
732 c_extends
= cextends;
734 c_namespace
= Namespace_env.empty
;
737 class_implicit_fields
result
739 (*****************************************************************************)
740 (* Extends/Implements *)
741 (*****************************************************************************)
743 and class_extends
env =
744 match L.token
env.lb with
746 (match Lexing.lexeme
env.lb with
747 | "extends" -> class_extends_list
env
748 | "implements" -> L.back
env.lb; []
749 | _ -> error env "Expected: extends"; []
755 error_expect env "{";
758 and class_implements
env =
759 match L.token
env.lb with
761 (match Lexing.lexeme
env.lb with
762 | "implements" -> class_extends_list
env
763 | "extends" -> L.back
env.lb; []
764 | _ -> error env "Expected: implements"; []
770 error_expect env "{";
773 and class_extends_list
env =
774 let error_state = !(env.errors
) in
775 let c = class_hint
env in
776 match L.token
env.lb with
780 if !(env.errors
) != error_state
782 else c :: class_extends_list
env
784 (match Lexing.lexeme
env.lb with
785 | "implements" | "extends" -> L.back
env.lb; [c]
786 | _ -> error_expect env "{"; []
788 | _ -> error_expect env "{"; []
790 (*****************************************************************************)
791 (* Class parameters class A<T as X ..> *)
792 (*****************************************************************************)
794 and class_params
env =
795 match L.token
env.lb with
796 | Tlt
-> class_param_list
env
797 | _ -> L.back
env.lb; []
799 and class_param_list
env =
800 let error_state = !(env.errors
) in
801 let cst = class_param
env in
802 match L.gt_or_comma
env.lb with
806 if !(env.errors
) != error_state
808 else cst :: class_param_list_remain
env
810 error_expect env ">";
813 and class_param_list_remain
env =
814 match L.gt_or_comma
env.lb with
818 let error_state = !(env.errors
) in
819 let cst = class_param
env in
820 match L.gt_or_comma
env.lb with
824 if !(env.errors
) != error_state
826 else cst :: class_param_list_remain
env
827 | _ -> error_expect env ">"; [cst]
829 and class_param
env =
830 match L.token
env.lb with
832 let parameter_name = Pos.make
env.lb, Lexing.lexeme
env.lb in
833 let parameter_constraint = class_parameter_constraint
env in
834 parameter_name, parameter_constraint
836 error_expect env "type parameter";
837 let parameter_name = Pos.make
env.lb, "T*unknown*" in
841 and class_parameter_constraint
env =
842 match L.token
env.lb with
843 | Tword
when Lexing.lexeme
env.lb = "as" ->
845 | _ -> L.back
env.lb; None
847 (*****************************************************************************)
848 (* Class hints (A<T> etc ...) *)
849 (*****************************************************************************)
852 let pname = identifier env in
853 class_hint_with_name
env pname
855 and class_hint_with_name
env pname =
856 let params = class_hint_params
env in
857 (fst
pname), Happly
(pname, params)
859 and class_hint_params
env =
860 match L.token
env.lb with
861 | Tlt
-> class_hint_param_list
env
862 | _ -> L.back
env.lb; []
864 and class_hint_param_list
env =
865 let error_state = !(env.errors
) in
867 match L.gt_or_comma
env.lb with
871 if !(env.errors
) != error_state
873 else h :: class_hint_param_list_remain
env
875 error_expect env ">"; [h]
877 and class_hint_param_list_remain
env =
878 match L.gt_or_comma
env.lb with
882 let error_state = !(env.errors
) in
884 match L.gt_or_comma
env.lb with
888 if !(env.errors
) != error_state
890 else h :: class_hint_param_list_remain
env
891 | _ -> error_expect env ">"; [h]
893 (*****************************************************************************)
894 (* Type hints: int, ?int, A<T>, array<...> etc ... *)
895 (*****************************************************************************)
898 match L.token
env.lb with
901 let start = Pos.make
env.lb in
903 Pos.btw start (fst
e), Hoption
e
904 (* A<_> | function(_):_ *)
906 let pos = Pos.make
env.lb in
907 let word = Lexing.lexeme
env.lb in
908 hint_word
env pos word
912 let cname = identifier env in
913 class_hint_with_name
env cname
916 let start_pos = Pos.make
env.lb in
917 hint_paren
start_pos env
920 let start = Pos.make
env.lb in
922 Pos.btw start (fst
h), snd
h
924 error_expect env "type";
925 let pos = Pos.make
env.lb in
926 pos, Happly
((pos, "*Unknown*"), [])
928 and hint_word
env pos word =
932 hint_function
pos env
935 class_hint_with_name
env (pos, name)
938 and hint_paren
start env =
939 let hintl = hint_list
env in
940 let end_ = Pos.make
env.lb in
941 let pos = Pos.btw start end_ in
944 | [_, Hfun
_ as h] -> pos, snd
h
946 error_at env pos "Tuples of one element are not allowed";
947 pos, Happly
((pos, "*Unkown*"), [])
948 | hl
-> pos, Htuple hl
951 let error_state = !(env.errors
) in
953 match L.token
env.lb with
957 if !(env.errors
) != error_state
959 else h :: hint_list_remain
env
961 error_expect env ">"; [h]
963 and hint_list_remain
env =
964 match L.token
env.lb with
968 let error_state = !(env.errors
) in
970 match L.token
env.lb with
974 if !(env.errors
) != error_state
976 else h :: hint_list_remain
env
978 error_expect env ">"; [h]
980 (*****************************************************************************)
981 (* Function hint (function(_): _) *)
982 (*****************************************************************************)
985 and hint_function
start env =
987 let params, has_dots
= hint_function_params
env in
988 let ret = hint_return
env in
989 Pos.btw start (fst
ret), Hfun
(params, has_dots
, ret)
991 (* (parameter_1, .., parameter_n) *)
992 and hint_function_params
env =
993 match L.token
env.lb with
997 hint_function_params_close
env;
1001 hint_function_params_remain
env
1004 and hint_function_params_close
env =
1005 match L.token
env.lb with
1011 error_expect env ")";
1014 (* _, parameter_list | _) | ...) | ...,) *)
1015 and hint_function_params_remain
env =
1016 let error_state = !(env.errors
) in
1018 match L.token
env.lb with
1020 if !(env.errors
) != error_state
1023 let hl, has_dots
= hint_function_params
env in
1028 hint_function_params_close
env;
1031 error_expect env ")";
1035 and hint_return
env =
1039 and hint_return_opt
env =
1040 match L.token
env.lb with
1041 | Tcolon
-> Some
(hint
env)
1042 | _ -> L.back
env.lb; None
1044 (*****************************************************************************)
1045 (* Class statements *)
1046 (*****************************************************************************)
1049 and class_body
env =
1050 let error_state = !(env.errors
) in
1052 if error_state != !(env.errors
)
1053 then L.look_for_open_cb
env.lb;
1056 and class_defs
env =
1057 match L.token
env.lb with
1061 (* xhp_format | const | use *)
1063 let word = Lexing.lexeme
env.lb in
1064 class_toplevel_word
env word
1066 (* variable | method *)
1068 let error_state = !(env.errors
) in
1069 let m = class_member_def
env in
1070 if !(env.errors
) != error_state
1072 else m :: class_defs
env
1074 error_expect env "class member";
1075 let start = Pos.make
env.lb in
1076 look_for_next_method
start env;
1077 let _ = L.token
env.lb in
1078 let word = Lexing.lexeme
env.lb in
1079 class_toplevel_word
env word
1081 and class_toplevel_word
env word =
1083 | "category" | "children" | "attribute" ->
1087 let error_state = !(env.errors
) in
1088 let def = class_const_def
env in
1089 if !(env.errors
) != error_state
1091 else def :: class_defs
env
1093 let traitl = class_use_list
env in
1094 traitl @ class_defs
env
1096 let traitl = trait_require
env in
1097 traitl @ class_defs
env
1098 | "abstract" | "public" | "protected" | "private" | "final" | "static" ->
1099 (* variable | method *)
1101 let start = Pos.make
env.lb in
1102 let error_state = !(env.errors
) in
1103 let m = class_member_def
env in
1104 if !(env.errors
) != error_state
1105 then look_for_next_method
start env;
1108 error_expect env "modifier";
1111 and look_for_next_method previous_pos
env =
1112 match L.token
env.lb with
1116 (match Lexing.lexeme
env.lb with
1117 | "abstract" | "public" | "protected"
1118 | "private" | "final" | "static" ->
1119 let pos = Pos.make
env.lb in
1120 if Pos.compare
pos previous_pos
= 0
1121 then (* we are stuck in a circle *)
1122 look_for_next_method
pos env
1125 | _ -> look_for_next_method previous_pos
env
1127 | _ -> look_for_next_method previous_pos
env
1129 (*****************************************************************************)
1130 (* Use (for traits) *)
1131 (*****************************************************************************)
1133 and class_use_list
env =
1134 let error_state = !(env.errors
) in
1135 let cst = ClassUse
(class_hint
env) in
1136 match L.token
env.lb with
1140 if !(env.errors
) != error_state
1142 else cst :: class_use_list_remain
env
1144 error_expect env ";"; [cst]
1146 and class_use_list_remain
env =
1147 match L.token
env.lb with
1151 let error_state = !(env.errors
) in
1152 let cst = ClassUse
(class_hint
env) in
1153 match L.token
env.lb with
1157 if !(env.errors
) != error_state
1159 else cst :: class_use_list_remain
env
1160 | _ -> error_expect env ";"; [cst]
1162 and trait_require
env =
1163 match L.token
env.lb with
1165 let req_type = Lexing.lexeme
env.lb in
1166 let ret = (match req_type with
1167 | "implements" -> [ClassTraitRequire
(MustImplement
, class_hint
env)]
1168 | "extends" -> [ClassTraitRequire
(MustExtend
, class_hint
env)]
1169 | _ -> error env "Expected: implements or extends"; []
1171 (match L.token
env.lb with
1173 | _ -> error_expect env ";"; [])
1174 | _ -> error env "Expected: implements or extends"; []
1176 (*****************************************************************************)
1177 (* Class xhp_fromat *)
1179 * within a class body -->
1184 (*****************************************************************************)
1186 and xhp_format
env =
1187 match L.token
env.lb with
1190 let pos = Pos.make
env.lb in
1191 let abs_pos = env.lb.Lexing.lex_curr_pos
in
1192 ignore
(expr_string
env pos abs_pos);
1195 let pos = Pos.make
env.lb in
1196 ignore
(expr_encapsed
env pos);
1201 (*****************************************************************************)
1202 (* Class constants *)
1204 * within a class body -->
1207 (*****************************************************************************)
1209 (* const_hint const_name1 = value1, ..., const_name_n = value_n; *)
1210 and class_const_def
env =
1211 let h = class_const_hint
env in
1212 let consts = class_const_list
env in
1215 (* const _ X = ...; *)
1216 and class_const_hint
env =
1217 if class_const_has_hint
env
1218 then Some
(hint
env)
1221 (* Determines if there is a type-hint by looking ahead. *)
1222 and class_const_has_hint
env =
1223 look_ahead env begin fun env ->
1224 match L.token
env.lb with
1225 (* const_name = ... | hint_name const_name = ... *)
1227 (* If we see 'name =', there is no type hint *)
1228 L.token
env.lb <> Teq
1232 and class_const_list
env =
1233 let error_state = !(env.errors
) in
1234 let cst = class_const
env in
1235 match L.token
env.lb with
1239 if !(env.errors
) != error_state
1241 else cst :: class_const_list_remain
env
1243 error_expect env ";"; [cst]
1245 and class_const_list_remain
env =
1246 match L.token
env.lb with
1250 let error_state = !(env.errors
) in
1251 let cst = class_const
env in
1252 match L.token
env.lb with
1256 if !(env.errors
) != error_state
1258 else cst :: class_const_list_remain
env
1260 error_expect env ";"; [cst]
1262 (* const_name = const_value *)
1263 and class_const
env =
1264 let id = identifier env in
1269 (*****************************************************************************)
1271 (*****************************************************************************)
1273 and mandatory_modifier_list
env =
1274 match L.token
env.lb with
1276 let word = Lexing.lexeme
env.lb in
1277 (match modifier_word
env word with
1278 | None
-> error_expect env "modifier"; []
1279 | Some v
-> v
:: optional_modifier_list
env
1282 error_expect env "modifier"; []
1284 and optional_modifier_list
env =
1285 match L.token
env.lb with
1287 let word = Lexing.lexeme
env.lb in
1288 (match modifier_word
env word with
1289 | None
-> L.back
env.lb; []
1290 | Some v
-> v
:: optional_modifier_list
env
1295 and modifier_word
env = function
1296 | "final" -> Some Final
1297 | "static" -> Some Static
1298 | "abstract" -> Some Abstract
1299 | "private" -> Some Private
1300 | "public" -> Some Public
1301 | "protected" -> Some Protected
1304 (*****************************************************************************)
1305 (* Class variables/methods. *)
1307 * within a class body -->
1308 * modifier_list ...;
1310 (*****************************************************************************)
1312 and class_member_def
env =
1313 let attrs = attribute
env in
1314 let modifier_start = Pos.make
env.lb in
1315 let modifiers = mandatory_modifier_list
env in
1316 let modifier_end = Pos.make
env.lb in
1317 let modifier_pos = Pos.btw modifier_start modifier_end in
1318 check_modifiers env modifier_pos modifiers;
1319 match L.token
env.lb with
1320 (* modifier_list $_ *)
1323 check_not_final env modifier_pos modifiers;
1324 let cvars = class_var_list
env in
1325 ClassVars
(modifiers, None
, cvars)
1327 let word = Lexing.lexeme
env.lb in
1328 class_member_word
env ~
modifiers ~
attrs word
1331 check_not_final env modifier_pos modifiers;
1333 let cvars = class_var_list
env in
1334 ClassVars
(modifiers, Some
h, cvars)
1336 (*****************************************************************************)
1337 (* Class variables *)
1339 * within a class body -->
1341 * modifier_list hint $x;
1343 (*****************************************************************************)
1345 and class_var_list
env =
1346 let error_state = !(env.errors
) in
1347 let cvar = class_var
env in
1348 if !(env.errors
) != error_state
1350 else cvar :: class_var_list_remain
env
1352 and class_var_list_remain
env =
1353 match L.token
env.lb with
1357 (match L.token
env.lb with
1362 let error_state = !(env.errors
) in
1363 let var = class_var
env in
1364 if !(env.errors
) != error_state
1366 else var :: class_var_list_remain
env
1368 | _ -> error_expect env ";"; []
1371 let pos, name = variable env in
1372 let name = class_var_name
name in
1373 let default = parameter_default
env in
1374 (pos, name), default
1376 and class_var_name
name =
1377 String.sub
name 1 (String.length
name - 1)
1379 (*****************************************************************************)
1382 * within a class body -->
1383 * modifier_list async function ...
1384 * modifier_list function ...
1386 (*****************************************************************************)
1388 and class_member_word
env ~
attrs ~
modifiers = function
1390 expect_word env "function";
1392 let fun_name = identifier env in
1393 let method_ = method_ env ~
modifiers ~
attrs ~sync
:FAsync
fun_name in
1397 let fun_name = identifier env in
1398 let method_ = method_ env ~
modifiers ~
attrs ~sync
:FSync
fun_name in
1403 let cvars = class_var_list
env in
1404 ClassVars
(modifiers, Some
h, cvars)
1406 and method_ env ~
modifiers ~
attrs ~sync
pname =
1407 let pos, name = pname in
1408 let tparams = class_params
env in
1409 let params = parameter_list
env in
1410 let ret = hint_return_opt
env in
1411 let body = function_body
env in
1412 let ret = method_implicit_return
env pname ret in
1413 if name = "__destruct" && params <> []
1414 then error_at env pos "Destructor must not have any parameters.";
1416 m_tparams
= tparams;
1421 m_user_attributes
= attrs;
1425 (*****************************************************************************)
1426 (* Constructor/Destructors special cases. *)
1427 (*****************************************************************************)
1429 and method_implicit_return
env (pos, name) ret =
1430 match name, ret with
1431 | ("__construct" | "__destruct"), None
->
1432 Some
(pos, Happly
((pos, "void"), []))
1433 | _, Some
(_, Happly
((_, "void"), [])) -> ret
1434 | "__construct", Some
_ ->
1435 error_at env pos "Constructor return type must be void or elided.";
1437 | "__destruct", Some
_ ->
1438 error_at env pos "Destructor return type must be void or elided.";
1442 (*****************************************************************************)
1443 (* Implicit class fields __construct(public int $x). *)
1444 (*****************************************************************************)
1446 and class_implicit_fields
class_ =
1447 let class_body = method_implicit_fields
class_.c_body
in
1448 { class_ with c_body
= class_body }
1450 and method_implicit_fields members
=
1453 | Method
({ m_name
= _, "__construct"; _ } as m) :: rl
->
1454 let fields, assigns
= param_implicit_fields
m.m_params
in
1455 fields @ Method
{ m with m_body
= assigns
@ m.m_body
} :: rl
1457 x
:: method_implicit_fields rl
1459 and param_implicit_fields
params =
1462 | { param_modifier
= Some vis
; _ } as p
:: rl
->
1463 let member, stmt = param_implicit_field vis p
in
1464 let members, assigns
= param_implicit_fields rl
in
1465 member :: members, stmt :: assigns
1467 param_implicit_fields rl
1469 and param_implicit_field vis p
=
1470 (* Building the implicit field (for example: private int $x;) *)
1471 let pos, name = p
.param_id
in
1472 let cvname = pos, class_var_name
name in
1473 let member = ClassVars
([vis
], p
.param_hint
, [cvname, None
]) in
1474 (* Building the implicit assignment (for example: $this->x = $x;) *)
1475 let this = pos, "$this" in
1477 Expr
(pos, Binop
(Eq None
, (pos, Obj_get
((pos, Lvar
this),
1479 (pos, Lvar p
.param_id
)))
1483 (*****************************************************************************)
1484 (* Function/Method bodies. *)
1485 (*****************************************************************************)
1487 and function_body
env =
1488 match L.token
env.lb with
1491 (match env.mode
with
1494 (* This is a hack for the type-checker to make a distinction
1495 * Between function foo(); and function foo() {}
1499 (match statement_list
env with
1503 | _ -> error_expect env "{"; []
1505 and ignore_body
env =
1506 match L.token
env.lb with
1507 | Tlcb
-> ignore_body
env; ignore_body
env
1510 let pos = Pos.make
env.lb in
1511 let abs_pos = env.lb.Lexing.lex_curr_pos
in
1512 ignore
(expr_string
env pos abs_pos);
1515 let pos = Pos.make
env.lb in
1516 ignore
(expr_encapsed
env pos);
1519 ignore
(expr_heredoc
env);
1521 | Tlt
when is_xhp env ->
1524 | Teof
-> error_expect env "}"; ()
1525 | _ -> ignore_body
env
1527 (*****************************************************************************)
1529 (*****************************************************************************)
1531 and statement_list
env =
1532 match L.token
env.lb with
1535 let block = statement_list
env in
1536 Block
block :: statement_list
env
1540 error_expect env "}";
1544 let error_state = !(env.errors
) in
1545 let stmt = statement
env in
1546 if !(env.errors
) != error_state
1547 then L.next_newline_or_close_cb
env.lb;
1548 stmt :: statement_list
env
1551 match L.token
env.lb with
1553 let word = Lexing.lexeme
env.lb in
1554 let stmt = statement_word
env word in
1557 Block
(statement_list
env)
1570 and statement_word
env = function
1571 | "break" -> statement_break
env
1572 | "continue" -> statement_continue
env
1573 | "throw" -> statement_throw
env
1574 | "return" -> statement_return
env
1575 | "static" -> statement_static
env
1576 | "print" -> statement_echo
env
1577 | "echo" -> statement_echo
env
1578 | "if" -> statement_if
env
1579 | "do" -> statement_do
env
1580 | "while" -> statement_while
env
1581 | "for" -> statement_for
env
1582 | "switch" -> statement_switch
env
1583 | "foreach" -> statement_foreach
env
1584 | "try" -> statement_try
env
1585 | "function" | "class" | "trait" | "interface" | "const"
1586 | "async" | "abstract" | "final" ->
1588 "Parse error: declarations are not supported outside global scope";
1589 ignore
(ignore_toplevel
SMap.empty
[] env (fun _ -> true));
1597 (*****************************************************************************)
1598 (* Break statement *)
1599 (*****************************************************************************)
1601 and statement_break
env =
1605 (*****************************************************************************)
1606 (* Continue statement *)
1607 (*****************************************************************************)
1609 and statement_continue
env =
1613 and check_continue
env =
1614 match L.token
env.lb with
1616 | Tint
-> error_continue env
1617 | _ -> error_expect env ";"
1619 (*****************************************************************************)
1620 (* Throw statement *)
1621 (*****************************************************************************)
1623 and statement_throw
env =
1628 (*****************************************************************************)
1629 (* Return statement *)
1630 (*****************************************************************************)
1632 and statement_return
env =
1633 let pos = Pos.make
env.lb in
1634 let value = return_value
env in
1637 and return_value
env =
1638 match L.token
env.lb with
1646 (*****************************************************************************)
1647 (* Static variables *)
1648 (*****************************************************************************)
1650 and statement_static
env =
1651 let pos = Pos.make
env.lb in
1652 match L.token
env.lb with
1655 let el = static_var_list
env in
1659 let id = pos, Id
(pos, "static") in
1660 let e = expr_remain
env id in
1663 and static_var_list
env =
1664 let error_state = !(env.errors
) in
1665 let cst = static_var
env in
1666 match L.token
env.lb with
1670 if !(env.errors
) != error_state
1672 else cst :: static_var_list_remain
env
1673 | _ -> error_expect env ";"; [cst]
1675 and static_var_list_remain
env =
1676 match L.token
env.lb with
1680 let error_state = !(env.errors
) in
1681 let cst = static_var
env in
1682 match L.token
env.lb with
1686 if !(env.errors
) != error_state
1688 else cst :: static_var_list_remain
env
1690 error_expect env ";"; [cst]
1692 and static_var
env =
1695 (*****************************************************************************)
1696 (* Switch statement *)
1697 (*****************************************************************************)
1699 and statement_switch
env =
1700 let e = paren_expr
env in
1702 let casel = switch_body
env in
1705 (* switch(...) { _ } *)
1706 and switch_body
env =
1707 match L.token
env.lb with
1711 let word = Lexing.lexeme
env.lb in
1712 switch_body_word
env word
1714 error_expect env "}";
1717 and switch_body_word
env = function
1721 let stl = case_body
env in
1722 Case
(e, stl) :: switch_body
env
1725 let stl = case_body
env in
1726 Default
stl :: switch_body
env
1727 | _ -> error_expect env "case"; []
1729 (* switch(...) { case/default: _ } *)
1731 match L.token
env.lb with
1733 (match Lexing.lexeme
env.lb with
1734 | "case" | "default" -> L.back
env.lb; []
1737 let error_state = !(env.errors
) in
1738 let st = statement
env in
1739 if !(env.errors
) != error_state
1741 else st :: case_body
env
1747 let error_state = !(env.errors
) in
1748 let st = statement
env in
1749 if !(env.errors
) != error_state
1751 else st :: case_body
env
1753 (*****************************************************************************)
1755 (*****************************************************************************)
1757 and statement_if
env =
1758 let e = paren_expr
env in
1759 let st1 = statement
env in
1760 let st2 = statement_else
env in
1761 If
(e, [st1], [st2])
1763 and statement_else
env =
1764 match L.token
env.lb with
1766 (match Lexing.lexeme
env.lb with
1767 | "else" -> statement
env
1768 | "elseif" -> statement_if
env
1769 | _ -> L.back
env.lb; Noop
1771 | _ -> L.back
env.lb; Noop
1773 (*****************************************************************************)
1774 (* Do/While do statement *)
1775 (*****************************************************************************)
1777 and statement_do
env =
1778 let st = statement
env in
1779 expect_word env "while";
1780 let e = paren_expr
env in
1784 and statement_while
env =
1785 let e = paren_expr
env in
1786 let st = statement
env in
1789 (*****************************************************************************)
1791 (*****************************************************************************)
1793 and statement_for
env =
1795 let start = Pos.make
env.lb in
1796 let _ = L.token
env.lb in
1797 let _ = L.back
env.lb in
1798 let last, el = for_expr
env in
1799 let e1 = Pos.btw start last, Expr_list
el in
1801 let last, el = for_expr
env in
1802 let e2 = Pos.btw start last, Expr_list
el in
1804 let last, el = for_last_expr
env in
1805 let e3 = Pos.btw start last, Expr_list
el in
1806 let st = statement
env in
1807 For
(e1, e2, e3, [st])
1810 match L.token
env.lb with
1815 let error_state = !(env.errors
) in
1817 match L.token
env.lb with
1819 Pos.make
env.lb, [e]
1820 | _ when !(env.errors
) != error_state ->
1822 Pos.make
env.lb, [e]
1824 let last, el = for_expr
env in
1827 error_expect env ";";
1828 Pos.make
env.lb, [e]
1830 and for_last_expr
env =
1831 match L.token
env.lb with
1836 let error_state = !(env.errors
) in
1838 match L.token
env.lb with
1840 Pos.make
env.lb, [e]
1841 | _ when !(env.errors
) != error_state ->
1843 Pos.make
env.lb, [e]
1845 let last, el = for_last_expr
env in
1848 error_expect env ")";
1849 Pos.make
env.lb, [e]
1851 (*****************************************************************************)
1852 (* Foreach statement *)
1853 (*****************************************************************************)
1855 and statement_foreach
env =
1858 expect_word env "as";
1859 let as_expr = foreach_as
env in
1860 let st = statement
env in
1861 Foreach
(e, as_expr, [st])
1863 and foreach_as
env =
1864 let e1 = expr
env in
1865 match L.token
env.lb with
1867 let e2 = expr
env in
1873 error_expect env ")";
1876 (*****************************************************************************)
1878 (*****************************************************************************)
1880 and statement_try
env =
1881 let st = statement
env in
1882 let cl = catch_list
env in
1883 let fin = finally
env in
1886 and catch_list
env =
1887 match L.token
env.lb with
1888 | Tword
when Lexing.lexeme
env.lb = "catch" ->
1890 let name = identifier env in
1891 let e = variable env in
1893 let st = statement
env in
1894 (name, e, [st]) :: catch_list
env
1895 | _ -> L.back
env.lb; []
1898 match L.token
env.lb with
1899 | Tword
when Lexing.lexeme
env.lb = "finally" ->
1900 let st = statement
env in
1902 | _ -> L.back
env.lb; []
1904 (*****************************************************************************)
1905 (* Echo statement *)
1906 (*****************************************************************************)
1908 and statement_echo
env =
1909 let pos = Pos.make
env.lb in
1910 let args = echo_args
env in
1911 let f = pos, Id
(pos, "echo") in
1912 Expr
(pos, Call
(f, args))
1916 match L.token
env.lb with
1922 error_expect env ";"; []
1924 (*****************************************************************************)
1925 (* Function/Method parameters *)
1926 (*****************************************************************************)
1928 and parameter_list
env =
1930 parameter_list_remain
env
1932 and parameter_list_remain
env =
1933 match L.token
env.lb with
1936 [parameter_varargs
env]
1939 let error_state = !(env.errors
) in
1940 let p = param ~variadic
:false env in
1941 match L.token
env.lb with
1945 [p ; parameter_varargs
env]
1947 if !(env.errors
) != error_state
1949 else p :: parameter_list_remain
env
1951 error_expect env ")"; [p]
1953 and parameter_varargs
env =
1954 let pos = Pos.make
env.lb in
1955 (match L.token
env.lb with
1956 | Tcomma
-> expect env Trp
; make_param_ellipsis
pos
1957 | Trp
-> make_param_ellipsis
pos;
1960 let p = param ~variadic
:true env in
1964 and make_param_ellipsis
pos =
1965 { param_hint
= None
;
1966 param_is_reference
= false;
1967 param_is_variadic
= true;
1968 param_id
= (pos, "...");
1970 param_modifier
= None
;
1971 param_user_attributes
= SMap.empty
;
1974 and param ~variadic
env =
1975 let attrs = attribute
env in
1976 let modifs = parameter_modifier
env in
1977 let h = parameter_hint
env in
1978 let variadic_after_hint, name = ref_param env in
1979 assert ((not
variadic_after_hint) || (not variadic
));
1980 let variadic = variadic || variadic_after_hint in
1981 let default = parameter_default
env in
1983 if variadic && default <> None
then
1984 let () = error env "Variadic arguments don't have default values" in
1987 if variadic_after_hint then begin
1992 param_is_reference
= false;
1993 param_is_variadic
= variadic;
1995 param_expr
= default;
1996 param_modifier
= modifs;
1997 param_user_attributes
= attrs;
2000 and parameter_modifier
env =
2001 match L.token
env.lb with
2003 (match Lexing.lexeme
env.lb with
2004 | "private" -> Some Private
2005 | "public" -> Some Public
2006 | "protected" -> Some Protected
2007 | _ -> L.back
env.lb; None
2009 | _ -> L.back
env.lb; None
2011 and parameter_hint
env =
2012 if parameter_has_hint
env
2013 then Some
(hint
env)
2016 and parameter_has_hint
env =
2017 look_ahead env begin fun env ->
2018 match L.token
env.lb with
2019 | Tellipsis
| Tamp
| Tlvar
-> false
2023 and parameter_default
env =
2024 match L.token
env.lb with
2026 let default = expr
env in
2028 | _ -> L.back
env.lb; None
2030 (*****************************************************************************)
2032 (*****************************************************************************)
2035 let e1 = expr_atomic
env in
2036 let e2 = expr_remain
env e1 in
2041 expr_list_remain
env
2043 and expr_list_remain
env =
2044 match L.token
env.lb with
2048 let error_state = !(env.errors
) in
2049 let e = expr
{ env with priority = 0 } in
2050 match L.token
env.lb with
2054 if !(env.errors
) != error_state
2056 else e :: expr_list_remain
env
2057 | _ -> error_expect env ")"; [e]
2059 and expr_remain
env e1 =
2060 match L.token
env.lb with
2062 expr_binop
env Tplus Plus
e1
2064 expr_binop
env Tminus Minus
e1
2066 expr_binop
env Tstar Star
e1
2068 expr_binop
env Tslash Slash
e1
2070 expr_assign
env Teq
(Eq None
) e1
2072 expr_assign
env Tbareq
(Eq
(Some Bar
)) e1
2074 expr_assign
env Tpluseq
(Eq
(Some Plus
)) e1
2076 expr_assign
env Tstareq
(Eq
(Some Star
)) e1
2078 expr_assign
env Tslasheq
(Eq
(Some Slash
)) e1
2080 expr_assign
env Tdoteq
(Eq
(Some Dot
)) e1
2082 expr_assign
env Tminuseq
(Eq
(Some Minus
)) e1
2084 expr_assign
env Tpercenteq
(Eq
(Some Percent
)) e1
2086 expr_assign
env Txoreq
(Eq
(Some Xor
)) e1
2088 expr_assign
env Tampeq
(Eq
(Some Amp
)) e1
2090 expr_assign
env Tlshifteq
(Eq
(Some Ltlt
)) e1
2092 expr_assign
env Trshifteq
(Eq
(Some Gtgt
)) e1
2094 expr_binop
env Teqeqeq EQeqeq
e1
2096 expr_binop
env Tgt Gt
e1
2098 expr_binop
env Tpercent Percent
e1
2100 expr_binop
env Tdot Dot
e1
2102 expr_binop
env Teqeq Eqeq
e1
2104 expr_binop
env Tampamp AMpamp
e1
2106 expr_binop
env Tbarbar BArbar
e1
2108 expr_binop
env Tdiff Diff
e1
2110 expr_binop
env Tlt Lt
e1
2112 expr_binop
env Tdiff2 Diff2
e1
2114 expr_binop
env Tgte Gte
e1
2116 expr_binop
env Tlte Lte
e1
2118 expr_binop
env Tamp Amp
e1
2120 expr_binop
env Tbar Bar
e1
2122 expr_binop
env Tltlt Ltlt
e1
2124 expr_binop
env Tgtgt Gtgt
e1
2126 expr_binop
env Txor Xor
e1
2127 | Tincr
| Tdecr
as uop
->
2128 expr_postfix_unary
env uop
e1
2136 expr_array_get
env e1
2139 | Tword
when Lexing.lexeme
env.lb = "instanceof" ->
2140 expr_instanceof
env e1
2141 | Tword
when Lexing.lexeme
env.lb = "and" ->
2142 error env ("Do not use \"and\", it has surprising precedence. "^
2143 "Use \"&&\" instead");
2144 expr_binop
env Tampamp AMpamp
e1
2145 | Tword
when Lexing.lexeme
env.lb = "or" ->
2146 error env ("Do not use \"or\", it has surprising precedence. "^
2147 "Use \"||\" instead");
2148 expr_binop
env Tbarbar BArbar
e1
2149 | Tword
when Lexing.lexeme
env.lb = "xor" ->
2150 error env ("Do not use \"xor\", it has surprising precedence. "^
2151 "Cast to bool and use \"^\" instead");
2152 expr_binop
env Txor Xor
e1
2156 (*****************************************************************************)
2157 (* Expression reducer *)
2158 (*****************************************************************************)
2160 and reduce
env e1 op make
=
2161 let e, continue
= reduce_
env e1 op make
in
2162 if continue
then expr_remain
env e else e
2164 and reduce_
env e1 op make
=
2165 let current_prio = env.priority in
2166 let assoc, prio
= get_priority op
in
2167 let env = { env with priority = prio
} in
2168 if prio
= current_prio
2172 let e = make
e1 { env with priority = env.priority + 1 } in
2173 expr_remain
env e, true
2175 let e = make
e1 env in
2178 error env "This operator is not associative, add parentheses";
2179 let e = make
e1 env in
2181 else if prio
< current_prio
2187 assert (prio
> current_prio);
2189 then make
e1 env, true
2190 else reduce_
env e1 op make
2193 (*****************************************************************************)
2194 (* lambda expressions *)
2195 (*****************************************************************************)
2197 and lambda_expr_body
: env -> block = fun env ->
2198 let (p, e1) = expr
env in
2199 [Return
(p, (Some
(p, e1)))]
2201 and lambda_body
env params ret =
2204 then function_body
env
2205 else lambda_expr_body
env
2208 f_name
= (Pos.none
, ";anonymous");
2213 f_user_attributes
= Utils.SMap.empty
;
2217 f_namespace
= Namespace_env.empty
;
2221 and make_lambda_param
: id -> fun_param
= fun var_id
->
2224 param_is_reference
= false;
2225 param_is_variadic
= false;
2228 param_modifier
= None
;
2229 param_user_attributes
= Utils.SMap.empty
;
2232 and lambda_single_arg
env var_id
=
2234 lambda_body
env [make_lambda_param var_id
] None
2236 and try_short_lambda
env =
2237 try_parse env begin fun env ->
2238 let error_state = !(env.errors
) in
2239 let param_list = parameter_list_remain
env in
2240 if !(env.errors
) != error_state then begin
2241 env.errors
:= error_state;
2244 let ret = hint_return_opt
env in
2245 if !(env.errors
) != error_state then begin
2246 env.errors
:= error_state;
2248 end else if not
(peek env = Tlambda
)
2252 Some
(lambda_body
env param_list ret)
2257 (*****************************************************************************)
2259 (*****************************************************************************)
2261 and expr_atomic ?
(allow_class
=false) env =
2262 let tok = L.token
env.lb in
2263 let pos = Pos.make
env.lb in
2266 let tok_value = Lexing.lexeme
env.lb in
2267 pos, Int
(pos, tok_value)
2269 let tok_value = Lexing.lexeme
env.lb in
2270 pos, Float
(pos, tok_value)
2272 let absolute_pos = env.lb.Lexing.lex_curr_pos
in
2273 expr_string
env pos absolute_pos
2275 expr_encapsed
env pos
2277 let tok_value = Lexing.lexeme
env.lb in
2278 let var_id = (pos, tok_value) in
2279 pos, if peek env = Tlambda
2280 then lambda_single_arg
env var_id
2284 let name = identifier env in
2286 | Tem
| Tincr
| Tdecr
| Ttild
| Tplus
| Tminus
as op
->
2287 expr_prefix_unary
env pos op
2289 with_priority env Tref expr
2291 with_priority env Tat expr
2293 let word = Lexing.lexeme
env.lb in
2294 expr_atomic_word ~allow_class
env pos word
2296 (match try_short_lambda
env with
2299 then expr_cast
env pos
2300 else with_base_priority env begin fun env ->
2303 let end_ = Pos.make
env.lb in
2304 Pos.btw pos end_, snd
e
2309 expr_short_array
env pos
2310 | Tlt
when is_xhp env ->
2315 error env ("A valid variable name starts with a letter or underscore,"^
2316 "followed by any number of letters, numbers, or underscores");
2319 error_expect env "expression";
2322 and expr_atomic_word ~allow_class
env pos = function
2323 | "class" when not allow_class
->
2324 error_expect env "expression";
2326 | "final" | "abstract" | "interface" | "trait" ->
2327 error_expect env "expression";
2342 expr_anon_async
env pos
2344 expr_anon_fun
env pos ~sync
:FSync
2345 | name when is_collection
env ->
2346 expr_collection
env pos name
2354 expr_php_list
env pos
2355 | "require" | "require_once" ->
2356 if env.mode
= Ast.Mstrict
2359 ("Parse error: require_once is supported only as a toplevel "^
2366 (*****************************************************************************)
2367 (* Expressions in parens. *)
2368 (*****************************************************************************)
2370 and paren_expr
env =
2371 with_base_priority env begin fun env ->
2378 (*****************************************************************************)
2379 (* Assignments (=, +=, -=, ...) *)
2380 (*****************************************************************************)
2382 and expr_assign
env bop ast_bop
e1 =
2383 reduce
env e1 bop
begin fun e1 env ->
2384 check_lvalue env e1;
2385 let e2 = expr
{ env with priority = 0 } in
2386 btw e1 e2, Binop
(ast_bop
, e1, e2)
2389 (*****************************************************************************)
2390 (* Binary operations (+, -, /, ...) *)
2391 (*****************************************************************************)
2393 and expr_binop
env bop ast_bop
e1 =
2394 reduce
env e1 bop
begin fun e1 env ->
2395 let e2 = expr
env in
2396 btw e1 e2, Binop
(ast_bop
, e1, e2)
2399 (*****************************************************************************)
2400 (* Object Access ($obj->method) *)
2401 (*****************************************************************************)
2403 and expr_arrow
env e1 =
2404 reduce
env e1 Tarrow
begin fun e1 env ->
2406 match L.token
env.lb with
2408 let name = Lexing.lexeme
env.lb in
2409 let pos = Pos.make
env.lb in
2411 | _ -> L.back
env.lb; expr
env
2413 btw e1 e2, Obj_get
(e1, e2)
2416 (*****************************************************************************)
2417 (* Class Access (ClassName::method_name) *)
2418 (*****************************************************************************)
2420 and expr_colcol
env e1 =
2421 reduce
env e1 Tcolcol
begin fun e1 env ->
2424 (* XYZ::class is OK ... *)
2425 expr_colcol_remain ~allow_class
:true env e1 cname
2426 | pos, Lvar
cname ->
2427 (* ... but get_class($x) should be used instead of $x::class *)
2428 expr_colcol_remain ~allow_class
:false env e1 cname
2430 error_at env pos "Expected class name";
2435 and expr_colcol_remain ~allow_class
env e1 cname =
2436 match expr_atomic
env ~allow_class
with
2438 btw e1 x
, Class_get
(cname, x
)
2440 btw e1 x
, Class_const
(cname, x
)
2442 error_at env pos "Expected identifier";
2445 (*****************************************************************************)
2446 (* Function call (foo(params)) *)
2447 (*****************************************************************************)
2449 and expr_call
env e1 =
2450 reduce
env e1 Tlp
begin fun e1 env ->
2452 let args = expr_list
env in
2453 let end_ = Pos.make
env.lb in
2454 Pos.btw (fst
e1) end_, Call
(e1, args)
2457 (*****************************************************************************)
2459 (*****************************************************************************)
2461 and is_collection
env = peek env = Tlcb
2463 and expr_collection
env pos name =
2464 if is_collection
env
2465 then build_collection
env pos name
2466 else pos, Id
(pos, name)
2468 and build_collection
env pos name =
2469 let name = pos, name in
2470 let fds = collection_field_list
env in
2471 let end_ = Pos.make
env.lb in
2472 Pos.btw pos end_, Collection
(name, fds)
2474 and collection_field_list
env =
2476 collection_field_list_remain
env
2478 and collection_field_list_remain
env =
2479 match L.token
env.lb with
2483 let error_state = !(env.errors
) in
2484 let fd = array_field
env in
2485 match L.token
env.lb with
2489 if !(env.errors
) != error_state
2491 else fd :: collection_field_list_remain
env
2493 error_expect env "}"; []
2495 (*****************************************************************************)
2497 (*****************************************************************************)
2499 and expr_instanceof
env e1 =
2500 reduce
env e1 Tinstanceof
begin fun e1 env ->
2501 let e2 = expr
env in
2502 btw e1 e2, InstanceOf
(e1, e2)
2505 (*****************************************************************************)
2507 (*****************************************************************************)
2509 and expr_yield
env start =
2510 with_priority env Tyield
begin fun env ->
2511 match L.token
env.lb with
2512 | Tword
when Lexing.lexeme
env.lb = "break" ->
2513 let end_ = Pos.make
env.lb in
2514 Pos.btw start end_, Yield_break
2518 Pos.btw start (fst
e), Yield
e
2521 and expr_await
env start =
2522 with_priority env Tawait
begin fun env ->
2524 Pos.btw start (fst
e), Await
e
2527 (*****************************************************************************)
2529 (*****************************************************************************)
2531 and expr_clone
env start =
2532 with_base_priority env begin fun env ->
2534 Pos.btw start (fst
e), Clone
e
2537 (*****************************************************************************)
2539 (*****************************************************************************)
2541 and expr_php_list
env start =
2542 let el = expr_list
env in
2543 let end_ = Pos.make
env.lb in
2544 Pos.btw start end_, List
el
2546 (*****************************************************************************)
2547 (* Anonymous functions *)
2548 (*****************************************************************************)
2550 and is_function
env =
2551 look_ahead env begin fun env ->
2552 let tok = L.token
env.lb in
2554 Lexing.lexeme
env.lb = "function"
2557 and expr_anon_async
env pos =
2560 expect_word env "function";
2561 expr_anon_fun
env pos ~sync
:FAsync
2563 else pos, Id
(pos, "async")
2565 and expr_anon_fun
env pos ~sync
=
2566 let env = { env with priority = 0 } in
2567 let params = parameter_list
env in
2568 let ret = hint_return_opt
env in
2569 let use = function_use
env in
2570 let body = function_body
env in
2572 f_name
= (Pos.none
, ";anonymous");
2577 f_user_attributes
= Utils.SMap.empty
;
2581 f_namespace
= Namespace_env.empty
;
2586 (*****************************************************************************)
2587 (* Use (for functions) *)
2588 (*****************************************************************************)
2590 and function_use
env =
2591 match L.token
env.lb with
2592 | Tword
when Lexing.lexeme
env.lb = "use" ->
2595 | _ -> L.back
env.lb; []
2598 match L.token
env.lb with
2602 let error_state = !(env.errors
) in
2603 let var = ref_variable env in
2604 match L.token
env.lb with
2606 if !(env.errors
) != error_state
2608 else var :: use_list
env
2612 error_expect env ")";
2615 (*****************************************************************************)
2616 (* New: new ClassName(...) *)
2617 (*****************************************************************************)
2619 and expr_new
env pos_start
=
2620 with_priority env Tnew
begin fun env ->
2629 if env.mode
= Ast.Mstrict
2630 then error env "Cannot use dynamic new in strict mode";
2634 error_expect env "class name";
2637 let args = expr_list
env in
2638 let pos_end = Pos.make
env.lb in
2639 Pos.btw pos_start
pos_end, New
(cname, args)
2642 (*****************************************************************************)
2643 (* Casts: (int|..|float) expr *)
2644 (*****************************************************************************)
2646 and is_cast_type
= function
2647 | "int" | "float" | "double" | "string"
2648 | "array" | "object" | "bool" | "unset" -> true
2652 look_ahead env begin fun env ->
2653 let _ = L.token
env.lb in
2654 is_cast_type
(Lexing.lexeme
env.lb) &&
2655 L.token
env.lb = Trp
2658 and expr_cast
env start_pos =
2659 with_priority env Tcast
begin fun env ->
2660 let tok = L.token
env.lb in
2661 let cast_type = Lexing.lexeme
env.lb in
2662 assert (tok = Tword
);
2663 assert (is_cast_type
cast_type);
2665 let ty = Pos.make
env.lb, cast_type in
2666 let ty = fst
ty, Happly
(ty, []) in
2668 match cast_type with
2670 | _ -> Pos.btw start_pos (fst
e), Cast
(ty, e)
2673 (*****************************************************************************)
2674 (* Unary operators $i++ etc ... *)
2675 (*****************************************************************************)
2677 and unary_priority
= function
2678 | Tplus
| Tminus
-> Tincr
2681 and expr_prefix_unary
env start op
=
2682 with_priority env (unary_priority op
) begin fun env ->
2694 Pos.btw start (fst
e), Unop
(op, e)
2697 and expr_postfix_unary
env uop
e1 =
2698 let end_ = Pos.make
env.lb in
2700 reduce
env e1 (unary_priority uop
) begin fun e1 env ->
2707 Pos.btw (fst
e1) end_, Unop
(op, e1)
2710 let x = L.token
env.lb in
2712 then expr_remain
env e
2713 else (L.back
env.lb; expr_remain
env e)
2715 (*****************************************************************************)
2716 (* If expression: _?_:_ *)
2717 (*****************************************************************************)
2719 and is_colon_if
env =
2720 look_ahead env begin fun env ->
2721 let tok = L.token
env.lb in
2723 (* At this point, we might still be dealing with an xhp identifier *)
2724 L.no_space_id
env.lb <> Tword
2727 and expr_if
env e1 =
2728 reduce
env e1 Tqm
begin fun e1 env ->
2730 then colon_if
env e1
2731 else ternary_if
env e1
2734 and ternary_if
env e1 =
2735 let e2 = expr
{ env with priority = 0 } in
2737 let e3 = expr
env in
2740 error_at env pos "You should add parentheses"
2742 Pos.btw (fst
e1) (fst
e3), Eif
(e1, Some
e2, e3)
2744 and colon_if
env e1 =
2746 let e2 = expr
env in
2747 Pos.btw (fst
e1) (fst
e2), Eif
(e1, None
, e2)
2750 (*****************************************************************************)
2752 (*****************************************************************************)
2754 and expr_string
env start abs_start
=
2755 match L.string env.lb with
2757 let pos = Pos.btw start (Pos.make
env.lb) in
2758 let len = env.lb.Lexing.lex_curr_pos
- abs_start
- 1 in
2759 let content = String.sub
env.lb.Lexing.lex_buffer abs_start
len in
2760 pos, String
(pos, content)
2762 error_at env start "string not closed";
2763 start, String
(start, "")
2766 and expr_encapsed
env start =
2767 let abs_start = env.lb.Lexing.lex_curr_pos
in
2768 let pos_start = Pos.make
env.lb in
2769 let el = encapsed_nested
pos_start env in
2770 let pos_end = Pos.make
env.lb in
2771 let pos = Pos.btw pos_start pos_end in
2772 let len = env.lb.Lexing.lex_curr_pos
- abs_start - 1 in
2773 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
2774 pos, String2
(el, (pos, content))
2776 and encapsed_nested
start env =
2777 match L.string2
env.lb with
2781 error_at env start "string not properly closed";
2783 | Tlcb
when env.mode
= Ast.Mdecl
->
2784 encapsed_nested
start env
2786 (match L.string2
env.lb with
2788 error env "{ not supported";
2790 encapsed_nested
start env
2793 let error_state = !(env.errors
) in
2794 let e = encapsed_expr
env in
2795 (match L.string2
env.lb with
2797 | _ -> error_expect env "}");
2798 if !(env.errors
) != error_state
2800 else e :: encapsed_nested
start env
2803 encapsed_nested
start env
2806 encapsed_nested
start env
2808 (match L.string2
env.lb with
2810 if env.mode
= Ast.Mstrict
2811 then error env "${ not supported";
2812 let error_state = !(env.errors
) in
2813 let result = (match L.string2
env.lb with
2815 (* The first token after ${ will lex as a word, but is actually
2816 * an lvar, so we need to fix it up. For example, "${foo}" should
2817 * be Lvar $foo, but will lex as Tdollar-Tlcb-Tword foo. *)
2818 let pos = Pos.make
env.lb in
2819 let lvar = pos, Lvar
(pos, "$" ^
Lexing.lexeme
env.lb) in
2820 encapsed_expr_reduce
pos env lvar
2822 error_expect env "variable";
2823 Pos.make
env.lb, Null
) in
2825 if !(env.errors
) != error_state
2827 else result :: encapsed_nested
start env
2830 encapsed_nested
start env
2834 let error_state = !(env.errors
) in
2835 let e = encapsed_expr
env in
2836 if !(env.errors
) != error_state
2838 else e :: encapsed_nested
start env
2839 | _ -> encapsed_nested
start env
2841 and encapsed_expr
env =
2842 match L.string2
env.lb with
2843 | Tlcb
when env.mode
= Ast.Mdecl
->
2844 Pos.make
env.lb, Null
2846 let pos = Pos.make
env.lb in
2847 let absolute_pos = env.lb.Lexing.lex_curr_pos
in
2848 expr_string
env pos absolute_pos
2850 let pos = Pos.make
env.lb in
2851 let tok_value = Lexing.lexeme
env.lb in
2852 pos, Int
(pos, tok_value)
2854 let pid = Pos.make
env.lb in
2855 let id = Lexing.lexeme
env.lb in
2858 let pos = Pos.make
env.lb in
2859 let lvar = pos, Lvar
(pos, Lexing.lexeme
env.lb) in
2860 encapsed_expr_reduce
pos env lvar
2862 error_expect env "expression";
2863 Pos.make
env.lb, Null
2865 and encapsed_expr_reduce
start env e1 =
2866 let e1, continue
= encapsed_expr_reduce_left
start env e1 in
2868 then encapsed_expr_reduce
start env e1
2871 and encapsed_expr_reduce_left
start env e1 =
2872 match L.string2
env.lb with
2875 match L.string2
env.lb with
2877 (* We need to special case this because any identifier
2878 * (including keywords) is allowed in this context.
2879 * For example: $x[function] is legal.
2881 let pid = Pos.make
env.lb in
2882 let id = Lexing.lexeme
env.lb in
2886 expr
{ env with priority = 0 }
2888 (match L.string2
env.lb with
2890 | _ -> error_expect env "]"
2892 let pos = Pos.btw start (Pos.make
env.lb) in
2893 (pos, Array_get
(e1, Some
e2)), true
2895 (match L.string2
env.lb with
2898 let e2 = encapsed_expr
env in
2899 let pos = Pos.btw start (Pos.make
env.lb) in
2900 (pos, Obj_get
(e1, e2)), true
2909 (*****************************************************************************)
2911 (*****************************************************************************)
2913 and expr_heredoc
env =
2914 let abs_start = env.lb.Lexing.lex_curr_pos
in
2915 let tag = heredoc_tag
env in
2916 heredoc_body
tag env;
2917 let len = env.lb.Lexing.lex_curr_pos
- abs_start - 1 in
2918 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
2919 fst
tag, String
(fst
tag, content)
2921 and heredoc_tag
env =
2922 match L.token
env.lb with
2924 Pos.make
env.lb, Lexing.lexeme
env.lb
2926 let pos = Pos.make
env.lb in
2927 let abs_pos = env.lb.Lexing.lex_curr_pos
in
2928 (match expr_string
env pos abs_pos with
2930 | _ -> assert false)
2932 error_expect env "heredoc or nowdoc identifier";
2933 Pos.make
env.lb, "HEREDOC"
2935 and heredoc_body
(pos, tag_value
as tag) env =
2936 match L.heredoc_token
env.lb with
2940 error_expect env tag_value
2942 heredoc_body
tag env
2944 and heredoc_end
(pos, tag_value
as tag) env =
2945 match L.heredoc_token
env.lb with
2947 let tag2 = Lexing.lexeme
env.lb in
2948 (match L.heredoc_token
env.lb with
2949 | Tnewline
when tag2 = tag_value
->
2953 | Tsc
when tag2 = tag_value
->
2957 heredoc_body
tag env
2962 heredoc_body
tag env
2965 (*****************************************************************************)
2967 (*****************************************************************************)
2969 and expr_array
env pos =
2970 let fields = array_field_list
env in
2973 and array_field_list
env =
2975 array_field_list_remain
env Trp
[]
2977 and expr_short_array
env pos =
2978 let fields = array_field_list_remain
env Trb
[] in
2981 and array_field_list_remain
env terminal
acc =
2982 match L.token
env.lb with
2983 | x when x = terminal
-> List.rev
acc
2986 let error_state = !(env.errors
) in
2987 let fd = array_field
env in
2988 let acc = fd :: acc in
2989 match L.token
env.lb with
2990 | x when x = terminal
->
2993 if !(env.errors
) != error_state
2995 else array_field_list_remain
env terminal
acc
2996 | _ -> error_expect env ")"; [fd]
2998 and array_field
env =
2999 let env = { env with priority = 0 } in
3000 let e1 = expr
env in
3001 match L.token
env.lb with
3003 let e2 = expr
env in
3009 (*****************************************************************************)
3011 (*****************************************************************************)
3013 and expr_shape
env pos =
3014 let fields = shape_field_list
env in
3017 and shape_field_list
env =
3019 shape_field_list_remain
env
3021 and shape_field_list_remain
env =
3022 match L.token
env.lb with
3026 let error_state = !(env.errors
) in
3027 let fd = shape_field
env in
3028 match L.token
env.lb with
3032 if !(env.errors
) != error_state
3034 else fd :: shape_field_list_remain
env
3035 | _ -> error_expect env ")"; [fd]
3037 and shape_field
env =
3038 let name = shape_field_name
env in
3040 let value = expr
{ env with priority = 0 } in
3043 and shape_field_name
env =
3044 let pos, e = expr
env in
3046 | String
p -> SFlit
p
3047 | Class_const
(id, ps
) -> SFclass_const
(id, ps
)
3048 | _ -> error_expect env "string literal or class constant";
3052 (*****************************************************************************)
3053 (* Array access ($my_array[]|$my_array[_]) *)
3054 (*****************************************************************************)
3056 and expr_array_get
env e1 =
3057 reduce
env e1 Tlb
begin fun e1 env ->
3058 match L.token
env.lb with
3060 let end_ = Pos.make
env.lb in
3061 Pos.btw (fst
e1) end_, Array_get
(e1, None
)
3064 let e2 = expr
{ env with priority = 0 } in
3066 let end_ = Pos.make
env.lb in
3067 Pos.btw (fst
e1) end_, Array_get
(e1, Some
e2)
3071 (*****************************************************************************)
3073 (*****************************************************************************)
3076 look_ahead env begin fun env ->
3077 let tok = L.xhpname
env.lb in
3079 let tok2 = L.xhpattr
env.lb in
3080 tok2 = Tgt
|| tok2 = Tword
||
3081 (tok2 = Tslash
&& L.xhpattr
env.lb = Tgt
)
3085 match L.xhpname
env.lb with
3087 let start = Pos.make
env.lb in
3088 let name = Lexing.lexeme
env.lb in
3089 let pname = start, ":"^
name in
3090 let attrl, closed
= xhp_attributes
env in
3091 let end_tag = Pos.make
env.lb in
3093 then Pos.btw start end_tag, Xml
(pname, attrl, [])
3095 let tag_pos = Pos.btw start end_tag in
3096 let el = xhp_body
tag_pos name env in
3097 let end_ = Pos.make
env.lb in
3098 Pos.btw start end_, Xml
(pname, attrl, el)
3100 error_expect env "xhpname";
3101 let pos = Pos.make
env.lb in
3102 pos, Xml
((pos, "xhp"), [], [])
3104 and xhp_attributes
env =
3105 match L.xhpattr
env.lb with
3107 if L.xhpattr
env.lb <> Tgt
3108 then error_expect env ">";
3113 let error_state = !(env.errors
) in
3114 let attr_name = Pos.make
env.lb, Lexing.lexeme
env.lb in
3116 let attr_value = xhp_attribute_value
env in
3117 if !(env.errors
) != error_state
3119 [attr_name, attr_value], true
3121 let rl, closed
= xhp_attributes
env in
3122 (attr_name, attr_value) :: rl, closed
3124 error_expect env ">";
3127 and xhp_attribute_value
env =
3128 match L.xhpattr
env.lb with
3129 | Tlcb
when env.mode
= Ast.Mdecl
->
3133 let result = expr
{ env with priority = 0 } in
3137 let start = Pos.make
env.lb in
3138 let abs_start = env.lb.Lexing.lex_curr_pos
in
3139 xhp_attribute_string
env start abs_start
3141 error_expect env "attribute value";
3142 let pos = Pos.make
env.lb in
3143 pos, String
(pos, "")
3145 and xhp_attribute_string
env start abs_start =
3146 match L.string2
env.lb with
3148 error_at env start "Xhp attribute not closed";
3149 start, String
(start, "")
3151 let len = env.lb.Lexing.lex_curr_pos
- abs_start - 1 in
3152 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
3153 let pos = Pos.btw start (Pos.make
env.lb) in
3154 pos, String
(pos, content)
3156 xhp_attribute_string
env start abs_start
3158 and xhp_body
pos name env =
3159 match L.xhptoken
env.lb with
3160 | Tlcb
when env.mode
= Ast.Mdecl
->
3162 xhp_body
pos name env
3164 let error_state = !(env.errors
) in
3165 let e = expr
{ env with priority = 0 } in
3167 if !(env.errors
) != error_state
3169 else e :: xhp_body
pos name env
3174 | (_, Xml
(_, _, _)) as xml
->
3175 xml
:: xhp_body
pos name env
3176 | _ -> xhp_body
pos name env)
3178 (match L.xhptoken
env.lb with
3180 let closing_tok = L.xhpname
env.lb in
3181 let closing_name = Lexing.lexeme
env.lb in
3182 if closing_tok = Txhpname
&&
3183 (L.xhptoken
env.lb = Tgt
)
3185 if closing_name = name
3188 error_expect env name;
3191 else xhp_body
pos name env
3194 xhp_body
pos name env
3197 error_at env pos "Xhp tag not closed";
3200 xhp_body
pos name env
3201 | _ -> xhp_body
pos name env
3203 (*****************************************************************************)
3205 (*****************************************************************************)
3208 let id = identifier env in
3209 let tparams = class_params
env in
3210 let tconstraint = typedef_constraint
env in
3212 let td = typedef_body
env in
3214 id, tparams, tconstraint, td
3216 and typedef_constraint
env =
3217 match L.token
env.lb with
3218 | Tword
when Lexing.lexeme
env.lb = "as" ->
3224 and typedef_body
env =
3225 match L.token
env.lb with
3226 | Tword
when Lexing.lexeme
env.lb = "shape" ->
3227 let pos = Pos.make
env.lb in
3228 pos, Hshape
(typedef_shape_field_list
env)
3229 | _ -> L.back
env.lb; hint
env
3231 and typedef_shape_field_list
env =
3233 typedef_shape_field_list_remain
env
3235 and typedef_shape_field_list_remain
env =
3236 match L.token
env.lb with
3240 let error_state = !(env.errors
) in
3241 let fd = typedef_shape_field
env in
3242 match L.token
env.lb with
3246 if !(env.errors
) != error_state
3248 else fd :: typedef_shape_field_list_remain
env
3250 error_expect env ")";
3253 and typedef_shape_field
env =
3254 let name = shape_field_name
env in
3256 let ty = hint
env in
3259 (*****************************************************************************)
3261 (*****************************************************************************)
3264 (* The safety of the recursive calls here is slightly subtle. Normally, we
3265 * check for errors when making a recursive call to make sure we don't get
3266 * stuck in a loop. Here, we actually don't need to do that, since the only
3267 * time we make a recursive call is when we see (and thus consume) a token
3268 * that we like. So every time we recurse we'll consume at least one token,
3269 * so we can't get stuck in an infinite loop. *)
3270 let tl = match env.mode
with
3271 | Ast.Mdecl
-> ignore_toplevel ~
attr:SMap.empty
3273 (* The name for a namespace is actually optional, so we need to check for
3274 * the name first. Setting the name to an empty string if there's no
3275 * identifier following the `namespace` token *)
3276 let id = match L.token
env.lb with
3277 | Tword
-> L.back
env.lb; identifier env
3278 | _ -> L.back
env.lb; Pos.make
env.lb, "" in
3279 match L.token
env.lb with
3281 let body = tl [] env (fun x -> x = Trcb
) in
3284 | Tsc
when (snd
id) = "" ->
3285 error_expect env "{";
3288 let terminate = function
3289 | Tword
-> Lexing.lexeme
env.lb = "namespace"
3292 let body = tl [] env terminate in
3295 error_expect env "{ or ;";
3298 and namespace_use_list
env acc =
3299 let p1, s1
= identifier env in
3300 let id1 = p1, if s1
.[0] = '
\\'
then s1
else "\\" ^ s1
in
3302 match L.token
env.lb with
3303 | Tword
when Lexing.lexeme
env.lb = "as" ->
3307 let str = snd
id1 in
3308 let start = try (String.rindex
str '
\\'
) + 1 with Not_found
-> 0 in
3309 let len = (String.length
str) - start in
3310 fst
id1, String.sub
str start len
3312 let acc = (id1, id2) :: acc in
3313 match L.token
env.lb with
3315 | Tcomma
-> namespace_use_list
env acc
3317 error_expect env "Namespace use list";
3320 (*****************************************************************************)
3322 (*****************************************************************************)
3324 let from_file filename
=
3325 Pos.file
:= filename
;
3326 let content = try Utils.cat filename
with _ -> "" in