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.
17 (*****************************************************************************)
19 (*****************************************************************************)
22 file
: Relative_path.t
;
26 errors
: (Pos.t
* string) list
ref;
27 in_generator
: bool ref;
28 popt
: ParserOptions.t
;
32 let init_env file lb popt quick
= {
34 mode
= FileInfo.Mpartial
;
38 in_generator
= ref false;
43 type parser_return
= {
44 file_mode
: FileInfo.mode
option; (* None if PHP *)
45 comments
: (Pos.t
* comment
) list
;
50 (*****************************************************************************)
51 (* Lexer (with backtracking) *)
52 (*****************************************************************************)
55 (* no need to save refill_buff because it's constant *)
60 lex_last_action
: int;
61 lex_eof_reached
: bool;
63 lex_start_p
: Lexing.position
;
64 lex_curr_p
: Lexing.position
;
67 let save_lexbuf_state (lb
: Lexing.lexbuf
) : saved_lb
=
69 lex_abs_pos
= lb
.Lexing.lex_abs_pos
;
70 lex_start_pos
= lb
.Lexing.lex_start_pos
;
71 lex_curr_pos
= lb
.Lexing.lex_curr_pos
;
72 lex_last_pos
= lb
.Lexing.lex_last_pos
;
73 lex_last_action
= lb
.Lexing.lex_last_action
;
74 lex_eof_reached
= lb
.Lexing.lex_eof_reached
;
75 lex_mem
= lb
.Lexing.lex_mem
;
76 lex_start_p
= lb
.Lexing.lex_start_p
;
77 lex_curr_p
= lb
.Lexing.lex_curr_p
;
80 let restore_lexbuf_state (lb
: Lexing.lexbuf
) (saved
: saved_lb
) : unit =
82 lb
.Lexing.lex_abs_pos
<- saved
.lex_abs_pos
;
83 lb
.Lexing.lex_start_pos
<- saved
.lex_start_pos
;
84 lb
.Lexing.lex_curr_pos
<- saved
.lex_curr_pos
;
85 lb
.Lexing.lex_last_pos
<- saved
.lex_last_pos
;
86 lb
.Lexing.lex_last_action
<- saved
.lex_last_action
;
87 lb
.Lexing.lex_eof_reached
<- saved
.lex_eof_reached
;
88 lb
.Lexing.lex_mem
<- saved
.lex_mem
;
89 lb
.Lexing.lex_start_p
<- saved
.lex_start_p
;
90 lb
.Lexing.lex_curr_p
<- saved
.lex_curr_p
;
94 * Call a function with a forked lexing environment, and return its
97 let look_ahead (env
: env
) (f
: env
-> 'a
) : 'a
=
98 let saved = save_lexbuf_state env
.lb
in
100 restore_lexbuf_state env
.lb
saved;
104 * Conditionally parse, saving lexer state in case we need to backtrack.
105 * The function parameter returns any optional type. If it's None, pop
106 * lexer state on the way out.
108 * Note that you shouldn't add any errors to the environment before
109 * you've committed to returning Some something. The error state is not
112 let try_parse (env
: env
) (f
: env
-> 'a
option) : 'a
option =
113 let saved = save_lexbuf_state env
.lb
in
116 | None
-> (restore_lexbuf_state env
.lb
saved; None
)
118 (* Same as try_parse, but returns None if parsing produced errors. *)
119 let try_parse_with_errors (env
: env
) (f
: env
-> 'a
) : 'a
option =
121 let error_state = !(env
.errors
) in
122 let result = f env
in
123 if !(env
.errors
) == error_state then
127 env
.errors
:= error_state;
132 (* Return the next token without updating lexer state *)
134 let saved = save_lexbuf_state env
.lb
in
135 let ret = L.token env
.file env
.lb
in
136 restore_lexbuf_state env
.lb
saved;
139 (* Checks if the next token matches a given word without updating lexer state*)
140 let peek_check_word env
word =
141 let saved = save_lexbuf_state env
.lb
in
142 let ret = L.token env
.file env
.lb
= Tword
&& Lexing.lexeme env
.lb
= word in
143 restore_lexbuf_state env
.lb
saved;
146 (* Drop the next token unconditionally *)
147 let drop (env
: env
) : unit = match L.token env
.file env
.lb
with _
-> ()
149 let btw (p1
, _
) (p2
, _
) = Pos.btw p1 p2
151 (*****************************************************************************)
153 (*****************************************************************************)
155 let error_at env pos msg
=
156 env
.errors
:= (pos
, msg
) :: !(env
.errors
)
159 error_at env
(Pos.make env
.file env
.lb
) msg
161 let error_continue env
=
163 "Yeah...we're not going to support continue/break N. \
164 It makes static analysis tricky and it's not really essential"
166 let error_back env msg
=
167 let pos = Pos.make env
.file env
.lb
in
172 let error_expect env expect
=
173 error_back env
("Expected "^expect
)
176 if L.token env
.file env
.lb
= x
178 else error_expect env
(L.token_to_string x
)
180 let expect_word env name
=
181 let tok = L.token env
.file env
.lb
in
182 let value = Lexing.lexeme env
.lb
in
183 if tok <> Tword
|| value <> name
184 then error_expect env
("'"^name^
"' (not '"^
value^
"')");
187 (*****************************************************************************)
188 (* Modifiers checks (public private, final abstract etc ...) *)
189 (*****************************************************************************)
191 let rec check_modifiers env
pos abstract final
= function
193 | Final
:: _
when abstract
->
194 error_at env
pos "Parse error. Cannot mix final and abstract"
195 | Abstract
:: _
when final
->
196 error_at env
pos "Parse error. Cannot mix final and abstract"
197 | Final
:: rl
-> check_modifiers env
pos abstract
true rl
198 | Abstract
:: rl
-> check_modifiers env
pos true final rl
199 | _
:: rl
-> check_modifiers env
pos abstract final rl
201 let check_visibility env
pos l
=
202 if List.exists l
begin function
203 | Private
| Public
| Protected
| Static
-> true
207 else error_at env
pos
208 "Parse error. You are missing public, private or protected."
210 let rec check_mix_visibility env
pos last_vis
= function
212 | (Private
| Public
| Protected
as vis
) :: rl
->
214 | Some vis2
when vis
<> vis2
->
216 "Parse error. Cannot mix different visibilities."
218 check_mix_visibility env
pos (Some vis
) rl
220 | _
:: rl
-> check_mix_visibility env
pos last_vis rl
222 let rec check_duplicates env
pos = function
224 | Private
:: rl
-> check_duplicates env
pos rl
225 | x
:: (y
:: _
) when x
= y
->
226 error_at env
pos "Parse error. Duplicate modifier"
227 | _
:: rl
-> check_duplicates env
pos rl
229 let check_modifiers env
pos l
=
230 check_visibility env
pos l
;
231 check_modifiers env
pos false false l
;
232 check_duplicates env
pos (List.sort compare l
);
233 check_mix_visibility env
pos None l
;
236 let check_not_final env
pos modifiers
=
237 if List.exists modifiers
(function Final
-> true | _
-> false)
238 then error_at env
pos "class variable cannot be final";
241 let check_toplevel env
pos =
242 if env
.mode
= FileInfo.Mstrict
243 then error_at env
pos "Remove all toplevel statements except for requires"
245 (*****************************************************************************)
246 (* Check expressions. *)
247 (*****************************************************************************)
249 let rec check_lvalue env
= function
250 | pos, Obj_get
(_
, (_
, Id
(_
, name
)), OG_nullsafe
) ->
251 error_at env
pos "?-> syntax is not supported for lvalues"
252 | pos, Obj_get
(_
, (_
, Id
(_
, name
)), _
) when name
.[0] = '
:'
->
253 error_at env
pos "->: syntax is not supported for lvalues"
254 | pos, Array_get
((_
, Class_const _
), _
) ->
255 error_at env
pos "Array-like class consts are not valid lvalues"
256 | _
, (Lvar _
| Lvarvar _
| Obj_get _
| Array_get _
| Class_get _
|
257 Unsafeexpr _
| Omitted
| BracedExpr _
) -> ()
258 | pos, Call
((_
, Id
(_
, "tuple")), _
, _
) ->
260 "Tuple cannot be used as an lvalue. Maybe you meant List?"
261 | _
, List el
-> List.iter el
(check_lvalue env
)
262 | pos, (Array _
| Darray _
| Varray _
| Shape _
| Collection _
263 | Null
| True
| False
| Id _
| Clone _
| Id_type_arguments _
264 | Class_const _
| Call _
| Int _
| Float _
265 | String _
| String2 _
| Yield _
| Yield_break
266 | Await _
| Expr_list _
| Cast _
| Unop _
267 | Binop _
| Eif _
| NullCoalesce _
| InstanceOf _
| New _
| Efun _
| Lfun _
268 | Xml _
| Import _
| Pipe _
) ->
269 error_at env
pos "Invalid lvalue"
271 (* The bound variable of a foreach can be a reference (but not inside
272 a list expression. *)
273 let check_foreach_lvalue env
= function
274 | (_
, Unop
(Uref
, e
)) | e
-> check_lvalue env e
276 (*****************************************************************************)
277 (* Operator priorities.
279 * It is annoying to deal with priorities by hand (although it's possible).
280 * This list mimics what would typically look like yacc rules, defining
281 * the operators priorities (from low to high), and associativity (left, right
284 * The priorities are then used by the "reducer" to auto-magically parse
285 * expressions in the right order (left, right, non-assoc) and with the right
286 * priority. Checkout the function "reduce" for more details.
288 (*****************************************************************************)
291 | Left
(* a <op> b <op> c = ((a <op> b) <op> c) *)
292 | Right
(* a <op> b <op> c = (a <op> (b <op> c)) *)
293 | NonAssoc
(* a <op> b <op> c = error *)
296 (* Lowest priority *)
297 (NonAssoc
, [Tyield
]);
298 (NonAssoc
, [Tawait
]);
299 (Left
, [Timport
; Teval
;]);
303 (Left
, [Tqm
; Tcolon
]);
310 (NonAssoc
, [Teqeq
; Tdiff
; Teqeqeq
; Tdiff2
]);
311 (NonAssoc
, [Tlt
; Tlte
; Tgt
; Tgte
; Tcmp
]);
312 (Left
, [Tltlt
; Tgtgt
]);
313 (Left
, [Tplus
; Tminus
; Tdot
]);
314 (Left
, [Tstar
; Tslash
; Tpercent
]);
316 (NonAssoc
, [Tinstanceof
]);
317 (Right
, [Ttild
; Tincr
; Tdecr
; Tcast
]);
318 (Right
, [Tstarstar
]);
319 (Right
, [Tat
; Tref
]);
321 (NonAssoc
, [Tnew
; Tclone
]);
323 (Right
, [Teq
; Tpluseq
; Tminuseq
; Tstareq
; Tstarstareq
;
324 Tslasheq
; Tdoteq
; Tpercenteq
;
325 Tampeq
; Tbareq
; Txoreq
; Tlshifteq
; Trshifteq
]);
326 (Left
, [Tarrow
; Tnsarrow
]);
332 (* Highest priority *)
336 (* Creating the table of assocs/priorities at initialization time. *)
337 let ptable = Hashtbl.create
23 in
338 (* Lowest priority = 0 *)
339 let priority = ref 0 in
340 List.iter
priorities begin fun (assoc
, tokl
) ->
341 List.iter tokl
begin fun token
->
342 (* Associates operator => (associativity, priority) *)
343 Hashtbl.add
ptable token
(assoc
, !priority)
345 (* This is a bit subtle:
347 * The difference in priority between 2 lines should be 2, not 1.
349 * It's because of a trick we use in the reducer.
350 * For something to be left-associative, we just pretend
351 * that the right hand side expression has a higher priority.
355 * reduce (e1 = 1) "2 + 3" // priority = 0
356 * reduce (e1 = 1) (expr "2 + 3" with priority+1)
357 * reduce (e1 = 1) (2, "+ 3") <--- this is where the trick is:
358 * because we made the priority higher
359 * the reducer stops when it sees the
362 priority := !priority + 2
365 assert (Hashtbl.mem
ptable tok);
366 Hashtbl.find
ptable tok
368 let with_priority env op f
=
369 let _, prio
= get_priority op
in
370 let env = { env with priority = prio
} in
373 let with_base_priority env f
=
374 let env = { env with priority = 0 } in
377 (*****************************************************************************)
379 (*****************************************************************************)
382 match L.token
env.file
env.lb
with
383 | Tamp
when env.mode
= FileInfo.Mstrict
->
384 error env "Don't use references!";
392 (*****************************************************************************)
394 (*****************************************************************************)
396 let xhp_identifier env =
397 (match L.xhpname
env.file
env.lb
with
399 Pos.make
env.file
env.lb
, ":"^
Lexing.lexeme
env.lb
401 error_expect env "identifier";
402 Pos.make
env.file
env.lb
, "*Unknown*"
407 match L.token
env.file
env.lb
with
409 let pos = Pos.make
env.file
env.lb
in
410 let name = Lexing.lexeme
env.lb
in
413 let start = Pos.make
env.file
env.lb
in
414 let end_, name = xhp_identifier env in
415 Pos.btw start end_, name
417 error_expect env "identifier";
418 Pos.make
env.file
env.lb
, "*Unknown*"
422 match L.token
env.file
env.lb
with
424 Pos.make
env.file
env.lb
, Lexing.lexeme
env.lb
426 error_expect env "variable";
427 Pos.make
env.file
env.lb
, "$_" (* SpecialIdents.placeholder *)
430 let ref_variable env =
431 let is_ref = ref_opt env in
432 (variable env, is_ref)
434 let ellipsis_opt env =
435 match L.token
env.file
env.lb
with
437 | _ -> L.back
env.lb
; false
439 (*****************************************************************************)
441 (*****************************************************************************)
444 ?
(quick
= false) (* Quick parsing of only declarations *)
445 ?
(elaborate_namespaces
= true)
446 ?
(include_line_comments
= false)
447 ?
(keep_errors
= true)
449 L.include_line_comments
:= include_line_comments
;
450 L.comment_list
:= [];
451 L.fixmes
:= IMap.empty
;
452 let lb = Lexing.from_string content
in
453 let env = init_env file
lb popt quick
in
454 let ast, file_mode
= header
env in
455 let comments = !L.comment_list
in
456 let fixmes = !L.fixmes in
457 L.comment_list
:= [];
458 L.fixmes := IMap.empty
;
459 if keep_errors
then begin
460 Fixmes.HH_FIXMES.add
env.file
fixmes;
461 Option.iter
(List.last
!(env.errors
)) Errors.parsing_error
463 let ast = if elaborate_namespaces
464 then Namespaces.elaborate_defs
env.popt
ast
466 {file_mode
; comments; ast; content
}
468 and program_with_default_popt
469 ?
(elaborate_namespaces
= true)
470 ?
(include_line_comments
= false)
471 ?
(keep_errors
= true)
474 ~elaborate_namespaces
475 ~include_line_comments
477 ParserOptions.default
481 (*****************************************************************************)
482 (* Hack headers (strict, decl, partial) *)
483 (*****************************************************************************)
486 let file_type, head
= get_header
env in
487 match file_type, head
with
488 | FileInfo.PhpFile
, _
489 | _, Some
FileInfo.Mdecl
->
490 let env = { env with mode
= FileInfo.Mdecl
} in
492 let result = ignore_toplevel None ~
attr [] env (fun x
-> x
= Teof
) in
496 let result = toplevel
[] { env with mode
= mode
} (fun x
-> x
= Teof
) in
503 match L.header
env.file
env.lb with
504 | `
error -> FileInfo.HhFile
, None
505 | `default_mode
-> FileInfo.HhFile
, Some
FileInfo.Mpartial
506 | `php_decl_mode
-> FileInfo.PhpFile
, Some
FileInfo.Mdecl
507 | `php_mode
-> FileInfo.PhpFile
, None
509 let _token = L.token
env.file
env.lb in
510 (match Lexing.lexeme
env.lb with
511 | "strict" when !(Ide.is_ide_mode
) ->
512 FileInfo.HhFile
, Some
FileInfo.Mpartial
513 | "strict" -> FileInfo.HhFile
, Some
FileInfo.Mstrict
514 | ("decl"|"only-headers") -> FileInfo.HhFile
, Some
FileInfo.Mdecl
515 | "partial" -> FileInfo.HhFile
, Some
FileInfo.Mpartial
518 "Incorrect comment; possible values include strict, decl, partial or empty";
519 FileInfo.HhFile
, Some
FileInfo.Mdecl
522 (*****************************************************************************)
524 (*****************************************************************************)
526 and ignore_toplevel attr_start ~
attr acc
env terminate
=
527 match L.token
env.file
env.lb with
528 | x
when terminate x
|| x
= Teof
->
532 (* Parsing attribute << .. >> *)
533 (* TODO: error for repeated attribute list *)
534 let attr_start = Some
(Pos.make
env.file
env.lb) in
535 let attr = attribute_remain
env in
536 ignore_toplevel
attr_start ~
attr acc
env terminate
538 let acc = ignore_toplevel
attr_start ~
attr acc env terminate
in
539 ignore_toplevel
attr_start ~
attr acc env terminate
541 let pos = Pos.make
env.file
env.lb in
542 let abs_pos = env.lb.Lexing.lex_curr_pos
in
543 ignore
(expr_string
env pos abs_pos);
544 ignore_toplevel
attr_start ~
attr acc env terminate
546 let pos = Pos.make
env.file
env.lb in
547 ignore
(expr_encapsed
env pos);
548 ignore_toplevel
attr_start ~
attr acc env terminate
550 ignore
(expr_heredoc
env);
551 ignore_toplevel
attr_start ~
attr acc env terminate
552 | Tlt
when is_xhp
env ->
554 ignore_toplevel
attr_start ~
attr acc env terminate
556 (match Lexing.lexeme
env.lb with
558 let def_start = Option.value attr_start
559 ~default
:(Pos.make
env.file
env.lb) in
560 (match L.token
env.file
env.lb with
563 let def = toplevel_word
def_start ~
attr env "function" in
564 ignore_toplevel None ~
attr:[] (def @ acc) env terminate
565 (* function &foo(...), we still want them in decl mode *)
567 (match L.token
env.file
env.lb with
570 let def = toplevel_word
def_start ~
attr env "function" in
571 ignore_toplevel None ~
attr:[] (def @ acc) env terminate
573 ignore_toplevel
attr_start ~
attr acc env terminate
576 ignore_toplevel
attr_start ~
attr acc env terminate
579 | "abstract" | "final"
580 | "class"| "trait" | "interface"
582 | "async" | "newtype" | "type"| "const" | "enum" ->
583 (* Parsing toplevel declarations (class, function etc ...) *)
584 let def_start = Option.value attr_start
585 ~default
:(Pos.make
env.file
env.lb) in
586 let def = toplevel_word
def_start ~
attr env (Lexing.lexeme
env.lb) in
587 ignore_toplevel None ~
attr:[] (def @ acc) env terminate
589 (* Ignore use statements in decl files *)
590 let error_state = !(env.errors
) in
591 ignore
(namespace_use
env);
592 env.errors
:= error_state;
593 ignore_toplevel
attr_start ~
attr (acc) env terminate
596 ignore_statement
env;
597 ignore_toplevel
attr_start ~
attr (acc) env terminate
601 error env "Hack does not allow the closing ?> tag";
604 (* All the other statements. *)
605 ignore_toplevel None ~
attr:[] acc env terminate
607 (*****************************************************************************)
608 (* Toplevel statements. *)
609 (*****************************************************************************)
611 and toplevel
acc env terminate
=
612 match L.token
env.file
env.lb with
613 | x
when terminate x
|| x
= Teof
->
617 (* Ignore extra semicolons at toplevel (important so we don't yell about
618 * them in strict mode). *)
619 toplevel
acc env terminate
621 (* Parsing attribute << .. >> *)
622 let attr_start = Pos.make
env.file
env.lb in
623 let attr = attribute_remain
env in
624 let _ = L.token
env.file
env.lb in
625 let def = toplevel_word
attr_start ~
attr env (Lexing.lexeme
env.lb) in
626 toplevel
(def @ acc) env terminate
628 (* Parsing toplevel declarations (class, function etc ...) *)
629 let def_start = Pos.make
env.file
env.lb in
630 let def = toplevel_word
def_start ~
attr:[] env (Lexing.lexeme
env.lb) in
631 toplevel
(def @ acc) env terminate
633 error env "Hack does not allow the closing ?> tag";
636 (* All the other statements. *)
637 let pos = Pos.make
env.file
env.lb in
639 let error_state = !(env.errors
) in
640 let stmt = Stmt
(statement
env) in
641 check_toplevel env pos;
642 if error_state != !(env.errors
)
643 then ignore_toplevel None ~
attr:[] (stmt :: acc) env terminate
644 else toplevel
(stmt :: acc) env terminate
646 and toplevel_word
def_start ~
attr env = function
647 | ("abstract" | "final") as attr_
->
648 (* parse all the class attributes until we see a non-attribute *)
649 let is_attribute attribute_
=
650 (attribute_
= "final" || attribute_
= "abstract") in
651 let rec parse_attributes attr_accum
=
652 (match L.token
env.file
env.lb with
653 | Tword
when is_attribute (Lexing.lexeme
env.lb) ->
654 let tok = Lexing.lexeme
env.lb in
655 if List.exists attr_accum
(fun x
-> x
= tok) then
656 error env ("Cannot specify attribute '" ^
tok ^
657 "' multiple times.");
658 parse_attributes (attr_accum
@ [tok])
659 | _ -> begin L.back
env.lb; attr_accum
end
661 let attributes = parse_attributes [attr_
] in
662 let final = List.exists
attributes (fun x
-> x
= "final") in
663 let abstract = List.exists
attributes (fun x
-> x
= "abstract") in
664 expect_word env "class";
665 let kind = if abstract then Cabstract
else Cnormal
in
666 let class_ = class_ def_start ~
attr ~
final:final ~
kind:kind env in
669 let class_ = class_ def_start ~
attr ~
final:false ~
kind:Cnormal
env in
672 let class_ = class_ def_start ~
attr ~
final:false ~
kind:Ctrait
env in
675 let class_ = class_ def_start ~
attr ~
final:false ~
kind:Cinterface
env in
678 let class_ = enum_
def_start ~
attr env in
681 expect_word env "function";
682 let fun_ = fun_ def_start ~
attr ~sync
:FDeclAsync
env in
685 let fun_ = fun_ def_start ~
attr ~sync
:FDeclSync
env in
688 let typedef_ = typedef ~
attr ~is_abstract
:true env in
691 let typedef_ = typedef ~
attr ~is_abstract
:false env in
694 let id, body
= namespace
env in
695 [Namespace
(id, body
)]
697 let usel = namespace_use
env in
700 let consts = class_const_def
env in
703 List.map cstl
begin fun (x
, y
) -> Constant
{
705 cst_kind
= Cst_const
;
709 cst_namespace
= Namespace_env.empty
env.popt
;
712 | r
when is_import r
->
713 let pos = Pos.make
env.file
env.lb in
714 let e = expr_import r
env pos in
718 let pos = Pos.make
env.file
env.lb in
720 let stmt = statement
env in
721 check_toplevel env pos;
722 [define_or_stmt
env stmt]
724 and define_or_stmt
env = function
725 | Expr
(_, Call
((_, Id
(_, "define")), [(_, String
name); value], [])) ->
728 cst_kind
= Cst_define
;
732 cst_namespace
= Namespace_env.empty
env.popt
;
737 (*****************************************************************************)
738 (* Attributes: <<_>> *)
739 (*****************************************************************************)
743 if look_ahead env (fun env -> L.token
env.file
env.lb = Tltlt
)
746 attribute_remain
env;
751 and attribute_remain
env =
752 match L.token
env.file
env.lb with
754 let pos = Pos.make
env.file
env.lb in
755 let ua_name = pos, Lexing.lexeme
env.lb in
756 let ua_params = attribute_parameters
env in
757 let attr = { ua_name; ua_params } in
758 attr :: attribute_list_remain
env
760 error_expect env "attribute name";
763 (* empty | (parameter_list) *)
764 and attribute_parameters
env =
765 match L.token
env.file
env.lb with
766 | Tlp
-> expr_list_remain
env
767 | _ -> L.back
env.lb; []
770 and attribute_list_remain
env =
771 match L.token
env.file
env.lb with
773 | Tcomma
-> attribute_remain
env
775 error_expect env ">>";
778 (*****************************************************************************)
780 (*****************************************************************************)
782 and fun_ fun_start ~
attr ~
(sync
:fun_decl_kind
) env =
783 let is_ref = ref_opt env in
784 if is_ref && sync
= FDeclAsync
785 then error env ("Asynchronous function cannot return reference");
786 let name = identifier env in
787 let tparams = class_params
env in
788 let params = parameter_list
env in
789 let ret = hint_return_opt
env in
790 let constrs = where_clause
env in
791 let is_generator, body_stmts
= function_body
env in
792 let fun_end = Pos.make
env.file
env.lb in
797 f_ret_by_ref
= is_ref;
800 f_user_attributes
= attr;
801 f_fun_kind
= fun_kind sync
is_generator;
803 f_namespace
= Namespace_env.empty
env.popt
;
804 f_span
= Pos.btw fun_start
fun_end;
805 f_doc_comment
= None
;
809 (*****************************************************************************)
811 (*****************************************************************************)
813 and class_ class_start ~
attr ~
final ~
kind env =
814 let cname = identifier env in
815 let is_xhp = (snd
cname).[0] = '
:'
in
816 let tparams = class_params
env in
818 if kind = Ctrait
then []
819 else class_extends ~single
:(kind <> Cinterface
) env in
820 let cimplements = class_implements
kind env in
821 let cbody = class_body
env in
822 let class_end = Pos.make
env.file
env.lb in
823 let span = Pos.btw class_start
class_end in
829 c_implements
= cimplements;
831 c_user_attributes
= attr;
833 c_extends
= cextends;
835 c_namespace
= Namespace_env.empty
env.popt
;
838 c_doc_comment
= None
;
841 class_implicit_fields
result
843 (*****************************************************************************)
845 (*****************************************************************************)
847 and enum_base_ty
env =
852 and enum_ class_start ~
attr env =
853 let cname = identifier env in
854 let basety = enum_base_ty
env in
855 let constraint_ = typedef_constraint
env in
856 let cbody = enum_body
env in
857 let class_end = Pos.make
env.file
env.lb in
858 let span = Pos.btw class_start
class_end in
866 c_user_attributes
= attr;
870 c_namespace
= Namespace_env.empty
env.popt
;
873 e_constraint
= constraint_;
876 c_doc_comment
= None
;
893 let const = class_const
env in
894 let elem = Const
(None
, [const]) in
896 let rest = enum_defs
env in
899 error_expect env "enum const declaration";
903 (*****************************************************************************)
904 (* Extends/Implements *)
905 (*****************************************************************************)
907 and class_extends ~single
env =
908 match L.token
env.file
env.lb with
910 (match Lexing.lexeme
env.lb with
911 | "extends" -> if single
then [class_hint
env] else class_extends_list
env
912 | "implements" -> L.back
env.lb; []
913 | s
-> error env ("Expected: extends; Got: "^s
); []
919 error_expect env "{";
922 and class_implements
kind env =
923 match L.token
env.file
env.lb with
925 (match Lexing.lexeme
env.lb with
927 let impl = class_extends_list
env in
928 if kind = Cinterface
then begin
929 error env "Expected: extends; Got implements"; []
932 | "extends" -> L.back
env.lb; []
933 | s
-> error env ("Expected: implements; Got: "^s
); []
939 error_expect env "{";
942 and class_extends_list
env =
943 let error_state = !(env.errors
) in
944 let c = class_hint
env in
945 match L.token
env.file
env.lb with
949 if !(env.errors
) != error_state
951 else c :: class_extends_list
env
953 (match Lexing.lexeme
env.lb with
954 | "implements" | "extends" -> L.back
env.lb; [c]
955 | _ -> error_expect env "{"; []
957 | _ -> error_expect env "{"; []
959 (*****************************************************************************)
960 (* Class parameters class A<T as X ..> *)
961 (*****************************************************************************)
963 and class_params
env =
964 match L.token
env.file
env.lb with
965 | Tlt
-> class_param_list
env
966 | _ -> L.back
env.lb; []
968 and class_param_list
env =
969 let error_state = !(env.errors
) in
970 let cst = class_param
env in
971 match L.gt_or_comma
env.file
env.lb with
975 if !(env.errors
) != error_state
977 else cst :: class_param_list_remain
env
979 error_expect env ">";
982 and class_param_list_remain
env =
983 match L.gt_or_comma
env.file
env.lb with
987 let error_state = !(env.errors
) in
988 let cst = class_param
env in
989 match L.gt_or_comma
env.file
env.lb with
993 if !(env.errors
) != error_state
995 else cst :: class_param_list_remain
env
996 | _ -> error_expect env ">"; [cst]
998 and class_param
env =
999 match L.token
env.file
env.lb with
1001 if L.token
env.file
env.lb <> Tword
1002 then class_param_error
env
1004 let parameter_name, parameter_constraint
= class_param_name
env in
1005 Covariant
, parameter_name, parameter_constraint
1007 if L.token
env.file
env.lb <> Tword
1008 then class_param_error
env
1010 let parameter_name, parameter_constraint
= class_param_name
env in
1011 Contravariant
, parameter_name, parameter_constraint
1013 let parameter_name, parameter_constraint
= class_param_name
env in
1014 let variance = Invariant
in
1015 variance, parameter_name, parameter_constraint
1017 class_param_error
env
1019 and class_param_error
env =
1020 error_expect env "type parameter";
1021 let parameter_name = Pos.make
env.file
env.lb, "T*unknown*" in
1022 Invariant
, parameter_name, []
1024 and class_param_name
env =
1025 let parameter_name = Pos.make
env.file
env.lb, Lexing.lexeme
env.lb in
1026 let parameter_constraints = class_parameter_constraint_list
env in
1027 parameter_name, parameter_constraints
1029 and class_parameter_constraint_list
env =
1030 match L.token
env.file
env.lb with
1031 | Tword
when Lexing.lexeme
env.lb = "as" ->
1033 (Constraint_as
, h) :: class_parameter_constraint_list
env
1034 | Tword
when Lexing.lexeme
env.lb = "super" ->
1036 (Constraint_super
, h) :: class_parameter_constraint_list
env
1037 | _ -> L.back
env.lb; []
1039 (*****************************************************************************)
1040 (* Class hints (A<T> etc ...) *)
1041 (*****************************************************************************)
1043 and class_hint
env =
1044 let pname = identifier env in
1045 class_hint_with_name
env pname
1047 and class_hint_with_name
env pname =
1048 let params = class_hint_params
env in
1049 (fst
pname), Happly
(pname, params)
1051 and class_hint_params
env =
1052 match L.token
env.file
env.lb with
1053 | Tlt
-> class_hint_param_list
env
1054 | _ -> L.back
env.lb; []
1056 and class_hint_param_list
env =
1057 let error_state = !(env.errors
) in
1059 match L.gt_or_comma
env.file
env.lb with
1063 if !(env.errors
) != error_state
1065 else h :: class_hint_param_list_remain
env
1067 error_expect env ">"; [h]
1069 and class_hint_param_list_remain
env =
1070 match L.gt_or_comma
env.file
env.lb with
1074 let error_state = !(env.errors
) in
1076 match L.gt_or_comma
env.file
env.lb with
1080 if !(env.errors
) != error_state
1082 else h :: class_hint_param_list_remain
env
1083 | _ -> error_expect env ">"; [h]
1085 (*****************************************************************************)
1086 (* Type hints: int, ?int, A<T>, array<...> etc ... *)
1087 (*****************************************************************************)
1090 match L.token
env.file
env.lb with
1093 let start = Pos.make
env.file
env.lb in
1095 Pos.btw start (fst
e), Hoption
e
1096 (* A<_> *)(* :XHPNAME *)
1097 | Tword
when Lexing.lexeme
env.lb = "shape" ->
1098 let pos = Pos.make
env.file
env.lb in
1099 pos, Hshape
(hint_shape_info
env pos)
1100 | Tword
| Tcolon
when Lexing.lexeme
env.lb <> "function" ->
1102 hint_apply_or_access
env []
1104 let h = hint_function
env in
1105 error_at env (fst
h) "Function hints must be parenthesized";
1107 (* (_) | (function(_): _) *)
1109 let start_pos = Pos.make
env.file
env.lb in
1110 hint_paren
start_pos env
1113 let start = Pos.make
env.file
env.lb in
1115 Pos.btw start (fst
h), Hsoft
h
1117 error_expect env "type";
1118 let pos = Pos.make
env.file
env.lb in
1119 pos, Happly
((pos, "*Unknown*"), [])
1121 and hint_apply_or_access
env id_list
=
1122 match L.token
env.file
env.lb with
1126 hint_apply_or_access_remainder
env (identifier env :: id_list
)
1128 error_expect env "identifier";
1129 let pos = Pos.make
env.file
env.lb in
1130 pos, Happly
((pos, "*Unknown*"), [])
1132 and hint_apply_or_access_remainder
env id_list
=
1133 match L.token
env.file
env.lb with
1135 | Tcolcol
-> hint_apply_or_access
env id_list
1139 begin match List.rev id_list
with
1141 let params = class_hint_params
env in
1142 fst
id, Happly
(id, params)
1143 | id1
:: id2
:: ids
->
1144 let pos1 = fst id1
in
1145 let pos2 = List.fold_left ids
1147 ~f
:(fun _acc
(p
, _) -> p
) in
1148 Pos.btw pos1 pos2, Haccess
(id1
, id2
, ids
)
1150 error_expect env "identifier";
1151 let pos = Pos.make
env.file
env.lb in
1152 pos, Happly
((pos, "*Unknown*"), [])
1155 (* (_) | (function(_): _) *)
1156 and hint_paren
start env =
1157 match L.token
env.file
env.lb with
1158 | Tword
when Lexing.lexeme
env.lb = "function" ->
1159 let h = hint_function
env in
1160 if L.token
env.file
env.lb <> Trp
1161 then error_at env (fst
h) "Function hints must be parenthesized";
1162 Pos.btw start (Pos.make
env.file
env.lb), (snd
h)
1165 let hintl = hint_list
env in
1166 let end_ = Pos.make
env.file
env.lb in
1167 let pos = Pos.btw start end_ in
1169 | [] -> assert false
1171 error_at env pos "Tuples of one element are not allowed";
1172 pos, Happly
((pos, "*Unknown*"), [])
1173 | hl
-> pos, Htuple hl
1176 let error_state = !(env.errors
) in
1178 match L.token
env.file
env.lb with
1182 if !(env.errors
) != error_state
1184 else h :: hint_list_remain
env
1186 error_expect env ">"; [h]
1188 and hint_list_remain
env =
1189 match L.token
env.file
env.lb with
1193 let error_state = !(env.errors
) in
1195 match L.token
env.file
env.lb with
1199 if !(env.errors
) != error_state
1201 else h :: hint_list_remain
env
1203 error_expect env ">"; [h]
1205 (*****************************************************************************)
1206 (* Function hint (function(_): _) *)
1207 (*****************************************************************************)
1209 (* function(_): _ *)
1210 and hint_function
env =
1211 let start = Pos.make
env.file
env.lb in
1213 let params, has_dots
= hint_function_params
env in
1214 let ret = hint_return
env in
1215 Pos.btw start (fst
ret), Hfun
(params, has_dots
, ret)
1217 (* (parameter_1, .., parameter_n) *)
1218 and hint_function_params
env =
1219 match L.token
env.file
env.lb with
1223 hint_function_params_close
env;
1227 hint_function_params_remain
env
1230 and hint_function_params_close
env =
1231 match L.token
env.file
env.lb with
1237 error_expect env ")";
1240 (* _, parameter_list | _) | ...) | ...,) *)
1241 and hint_function_params_remain
env =
1242 let error_state = !(env.errors
) in
1244 match L.token
env.file
env.lb with
1246 if !(env.errors
) != error_state
1249 let hl, has_dots
= hint_function_params
env in
1254 hint_function_params_close
env;
1257 error_expect env ")";
1260 and xhp_enum_decl_list
env =
1261 match L.token
env.file
env.lb with
1263 | _ -> L.back
env.lb; xhp_enum_decl_list_remain
env
1265 and xhp_enum_decl_list_remain
env =
1266 let error_state = !(env.errors
) in
1267 let v = xhp_enum_decl_value
env in
1268 match L.token
env.file
env.lb with
1272 if !(env.errors
) != error_state
1274 else v :: xhp_enum_decl_list
env
1276 error_expect env "}"; [v]
1278 and xhp_enum_decl_value
env =
1279 let tok = L.token
env.file
env.lb in
1280 let pos = Pos.make
env.file
env.lb in
1283 let tok_value = Lexing.lexeme
env.lb in
1284 pos, Int
(pos, tok_value)
1286 let absolute_pos = env.lb.Lexing.lex_curr_pos
in
1287 expr_string
env pos absolute_pos
1289 expr_encapsed
env pos
1291 error_expect env "integer literal or string literal";
1295 and hint_return
env =
1299 and hint_return_opt
env =
1300 match L.token
env.file
env.lb with
1301 | Tcolon
-> Some
(hint
env)
1302 | _ -> L.back
env.lb; None
1304 (*****************************************************************************)
1305 (* Class statements *)
1306 (*****************************************************************************)
1309 and class_body
env =
1310 let error_state = !(env.errors
) in
1312 if error_state != !(env.errors
)
1313 then L.look_for_open_cb
env.lb;
1316 and class_defs
env =
1317 match L.token
env.file
env.lb with
1321 (* xhp_format | const | use *)
1323 let word = Lexing.lexeme
env.lb in
1324 class_toplevel_word
env word
1326 (* variable | method | type const*)
1328 let error_state = !(env.errors
) in
1329 let m = class_member_def
env in
1330 if !(env.errors
) != error_state
1332 else m :: class_defs
env
1334 error_expect env "class member";
1335 let start = Pos.make
env.file
env.lb in
1336 look_for_next_method
start env;
1337 let _ = L.token
env.file
env.lb in
1338 let word = Lexing.lexeme
env.lb in
1339 class_toplevel_word
env word
1341 and class_toplevel_word
env word =
1347 let cat = XhpCategory
(xhp_category_list
env) in
1348 cat :: class_defs
env
1350 let error_state = !(env.errors
) in
1351 let def_start = Pos.make
env.file
env.lb in (* TODO *)
1353 match try_typeconst_def
def_start env ~is_abstract
:false with
1354 | Some tconst
-> tconst
1355 | None
-> class_const_def
env
1357 if !(env.errors
) != error_state
1359 else def :: class_defs
env
1361 let traitl = class_use_list
env in
1362 traitl @ class_defs
env
1364 let traitl = trait_require
env in
1365 traitl @ class_defs
env
1367 let start = Pos.make
env.file
env.lb in
1368 let error_state = !(env.errors
) in
1369 let m = xhp_attr_list
env in
1370 if !(env.errors
) != error_state
1371 then look_for_next_method
start env;
1374 let def_start = Pos.make
env.file
env.lb in (* TODO *)
1375 (match try_abstract_const
def_start env with
1376 | Some ac
-> ac
:: class_defs
env
1377 | None
-> on_class_member_word
env)
1378 | "public" | "protected" | "private" | "final" | "static" ->
1379 on_class_member_word
env
1381 error_expect env "modifier";
1384 and on_class_member_word
env =
1385 (* variable | method | type const*)
1387 let start = Pos.make
env.file
env.lb in
1388 let error_state = !(env.errors
) in
1389 let m = class_member_def
env in
1390 if !(env.errors
) != error_state
1391 then look_for_next_method
start env;
1394 and look_for_next_method previous_pos
env =
1395 match L.token
env.file
env.lb with
1399 (match Lexing.lexeme
env.lb with
1400 | "abstract"| "public" | "protected"
1401 | "private" | "final" | "static" when not
(peek_check_word env "const") ->
1402 let pos = Pos.make
env.file
env.lb in
1403 if Pos.compare
pos previous_pos
= 0
1404 then (* we are stuck in a circle *)
1405 look_for_next_method
pos env
1408 | _ -> look_for_next_method previous_pos
env
1410 | _ -> look_for_next_method previous_pos
env
1412 (*****************************************************************************)
1413 (* Use (for traits) *)
1414 (*****************************************************************************)
1416 and class_use_list
env =
1417 let error_state = !(env.errors
) in
1418 let cst = ClassUse
(class_hint
env) in
1420 match L.token
env.file
env.lb with
1424 if !(env.errors
) != error_state
1426 else cst :: class_use_list
env
1428 error_expect env ";"; [cst]
1430 and trait_require
env =
1431 match L.token
env.file
env.lb with
1433 let req_type = Lexing.lexeme
env.lb in
1434 let ret = (match req_type with
1435 | "implements" -> [ClassTraitRequire
(MustImplement
, class_hint
env)]
1436 | "extends" -> [ClassTraitRequire
(MustExtend
, class_hint
env)]
1437 | _ -> error env "Expected: implements or extends"; []
1439 (match L.token
env.file
env.lb with
1441 | _ -> error_expect env ";"; [])
1442 | _ -> error env "Expected: implements or extends"; []
1444 (*****************************************************************************)
1445 (* Class xhp_format *)
1447 * within a class body -->
1450 (*****************************************************************************)
1452 and xhp_format
env =
1453 match L.token
env.file
env.lb with
1456 error_expect env "end of XHP children declaration";
1459 let pos = Pos.make
env.file
env.lb in
1460 let abs_pos = env.lb.Lexing.lex_curr_pos
in
1461 ignore
(expr_string
env pos abs_pos);
1464 let pos = Pos.make
env.file
env.lb in
1465 ignore
(expr_encapsed
env pos);
1470 (*****************************************************************************)
1471 (* Class constants *)
1473 * within a class body -->
1476 (*****************************************************************************)
1478 (* Is "abstract" followed by "const"?
1479 abstract const _ X; *)
1480 and try_abstract_const
def_start env =
1481 try_parse env begin fun env ->
1482 match L.token
env.file
env.lb with
1483 | Tword
when Lexing.lexeme
env.lb = "const" ->
1484 (match try_typeconst_def
def_start env ~is_abstract
:true with
1485 | Some tconst
-> Some tconst
1487 let h = class_const_hint
env in
1488 let id = identifier env in
1490 Some
(AbsConst
(h, id))
1495 and try_typeconst_def
def_start env ~is_abstract
=
1496 try_parse env begin fun env ->
1497 match L.token
env.file
env.lb with
1498 | Tword
when Lexing.lexeme
env.lb = "type" && (peek env) = Tword
->
1499 Some
(TypeConst
(typeconst_def
def_start env ~is_abstract
))
1503 (* const_hint const_name1 = value1, ..., const_name_n = value_n; *)
1504 and class_const_def
env =
1505 let h = class_const_hint
env in
1506 let consts = class_const_list
env in
1509 (* const _ X = ...; *)
1510 and class_const_hint
env =
1511 if class_const_has_hint
env
1512 then Some
(hint
env)
1515 (* Determines if there is a type-hint by looking ahead. *)
1516 and class_const_has_hint
env =
1517 look_ahead env begin fun env ->
1518 match L.token
env.file
env.lb with
1519 (* const_name = ... | hint_name const_name = ... *)
1521 (* If we see 'name =' or 'name;', there is no type hint *)
1522 let tok = L.token
env.file
env.lb in
1523 (tok <> Teq
&& tok <> Tsc
)
1527 and class_const_list
env =
1528 let error_state = !(env.errors
) in
1529 let cst = class_const
env in
1530 match L.token
env.file
env.lb with
1534 if !(env.errors
) != error_state
1536 else cst :: class_const_list_remain
env
1538 error_expect env ";"; [cst]
1540 and class_const_list_remain
env =
1541 match L.token
env.file
env.lb with
1545 let error_state = !(env.errors
) in
1546 let cst = class_const
env in
1547 match L.token
env.file
env.lb with
1551 if !(env.errors
) != error_state
1553 else cst :: class_const_list_remain
env
1555 error_expect env ";"; [cst]
1557 (* const_name = const_value *)
1558 and class_const
env =
1559 let id = identifier env in
1564 (*****************************************************************************)
1566 (*****************************************************************************)
1568 and mandatory_modifier_list
env =
1569 match L.token
env.file
env.lb with
1571 let word = Lexing.lexeme
env.lb in
1572 (match modifier_word
env word with
1573 | None
-> error_expect env "modifier"; []
1574 | Some
v -> v :: optional_modifier_list
env
1577 error_expect env "modifier"; []
1579 and optional_modifier_list
env =
1580 match L.token
env.file
env.lb with
1582 let word = Lexing.lexeme
env.lb in
1583 (match modifier_word
env word with
1584 | None
-> L.back
env.lb; []
1585 | Some
v -> v :: optional_modifier_list
env
1590 and modifier_word
env = function
1591 | "final" -> Some Final
1593 (* We need to look ahead to make sure we are not looking at a type access
1596 * public static $x; // a static var
1597 * public static::T $y; // an instance var with type static::T
1600 if peek env = Tcolcol
1603 | "abstract" -> Some Abstract
1604 | "private" -> Some Private
1605 | "public" -> Some Public
1606 | "protected" -> Some Protected
1609 (*****************************************************************************)
1610 (* Class variables/methods. *)
1612 * within a class body -->
1613 * modifier_list ...;
1615 (*****************************************************************************)
1617 and class_member_def
env =
1618 let member_start = Pos.make
env.file
env.lb in
1619 let attrs = attribute
env in
1620 let modifier_start = Pos.make
env.file
env.lb in
1621 let modifiers = mandatory_modifier_list
env in
1622 let modifier_end = Pos.make
env.file
env.lb in
1623 let modifier_pos = Pos.btw modifier_start modifier_end in
1624 check_modifiers env modifier_pos modifiers;
1625 match L.token
env.file
env.lb with
1626 (* modifier_list $_ *)
1629 check_not_final env modifier_pos modifiers;
1630 let cvars = class_var_list
env in
1631 ClassVars
(modifiers, None
, cvars)
1633 let word = Lexing.lexeme
env.lb in
1634 class_member_word
env member_start ~
modifiers ~
attrs word
1637 check_visibility env modifier_pos modifiers;
1638 check_not_final env modifier_pos modifiers;
1640 let cvars = class_var_list
env in
1641 ClassVars
(modifiers, Some
h, cvars)
1643 (*****************************************************************************)
1644 (* Class variables *)
1646 * within a class body -->
1648 * modifier_list hint $x;
1650 (*****************************************************************************)
1652 and class_var_list
env =
1653 let error_state = !(env.errors
) in
1654 let cvar = class_var
env in
1655 if !(env.errors
) != error_state
1657 else cvar :: class_var_list_remain
env
1659 and class_var_list_remain
env =
1660 match L.token
env.file
env.lb with
1664 (match L.token
env.file
env.lb with
1669 let error_state = !(env.errors
) in
1670 let var = class_var
env in
1671 if !(env.errors
) != error_state
1673 else var :: class_var_list_remain
env
1675 | _ -> error_expect env ";"; []
1678 let pos, name = variable env in
1679 let name = class_var_name
name in
1680 let default = parameter_default
env in
1681 let end_pos = match default with
1685 let span = Pos.btw pos end_pos in
1686 span, (pos, name), default
1688 and class_var_name
name =
1689 String.sub
name 1 (String.length
name - 1)
1692 let maybe_use, maybe_enum
= (match L.token
env.file
env.lb with
1694 L.back
env.lb; (try_xhp_attr_use
env, None
)
1695 | Tword
when Lexing.lexeme
env.lb = "enum" ->
1696 L.back
env.lb; (None
, try_xhp_enum_hint
env)
1698 L.back
env.lb; (None
, None
)) in
1699 match maybe_use with
1703 let h = (match maybe_enum
with
1705 | None
-> Some
(hint
env)) in
1706 let (pos_start
, _) as ident
= xhp_identifier env in
1707 let default = parameter_default
env in
1708 let pos_end = match default with
1712 let span = Pos.btw pos_start
pos_end in
1713 let is_required = (match L.token
env.file
env.lb with
1715 | _ -> L.back
env.lb; false) in
1716 XhpAttr
(h, (span, ident
, default), is_required, maybe_enum
)
1719 and xhp_attr_list
env =
1720 let error_state = !(env.errors
) in
1721 let a = xhp_attr
env in
1722 if !(env.errors
) != error_state
1724 else [a] @ xhp_attr_list_remain
env
1726 and xhp_attr_list_remain
env =
1727 match L.token
env.file
env.lb with
1731 (match L.token
env.file
env.lb with
1737 | _ -> error_expect env ";"; []
1739 and xhp_category
env =
1740 expect env Tpercent
;
1741 let token = L.xhpname
env.file
env.lb in
1742 let ret = Pos.make
env.file
env.lb, Lexing.lexeme
env.lb in
1743 (match token with | Txhpname
-> ()
1744 | _ -> error_expect env "xhp name");
1747 and xhp_category_list_remain
env =
1748 match L.token env.file
env.lb with
1752 (match L.token env.file
env.lb with
1757 xhp_category_list
env)
1758 | _ -> error_expect env ";"; []
1760 and xhp_category_list
env =
1761 let error_state = !(env.errors
) in
1762 let a = xhp_category
env in
1763 if !(env.errors
) != error_state
1765 else [a] @ xhp_category_list_remain
env
1768 (*****************************************************************************)
1771 * within a class body -->
1772 * modifier_list async function ...
1773 * modifier_list function ...
1775 (*****************************************************************************)
1777 and class_member_word
env member_start ~
attrs ~
modifiers = function
1779 expect_word env "function";
1780 let is_ref = ref_opt env in
1782 then error env ("Asynchronous function cannot return reference");
1783 let fun_name = identifier env in
1784 let method_ = method_ env member_start
1785 ~
modifiers ~
attrs ~sync
:FDeclAsync
is_ref fun_name in
1788 let is_ref = ref_opt env in
1789 let fun_name = identifier env in
1790 let method_ = method_ env member_start
1791 ~
modifiers ~
attrs ~sync
:FDeclSync
is_ref fun_name in
1797 match L.token env.file
env.lb with
1798 | Tword
when Lexing.lexeme
env.lb = "function" ->
1799 error env ("Expected variable. "^
1800 "Perhaps you meant 'function (...): return-type'?");
1802 | _ -> L.back
env.lb; class_var_list
env
1803 in ClassVars
(modifiers, Some
h, cvars)
1805 and typeconst_def
def_start env ~is_abstract
=
1806 let pname = identifier env in
1807 let constr = typedef_constraint
env in
1808 let type_ = match L.token env.file
env.lb with
1809 | Teq
-> Some
(hint
env)
1810 | _ -> L.back
env.lb; None
1812 let end_ = Pos.make
env.file
env.lb in
1813 let span = Pos.btw def_start end_ in
1815 { tconst_abstract
= is_abstract
;
1816 tconst_name
= pname;
1817 tconst_constraint
= constr;
1818 tconst_type
= type_;
1822 and method_ env method_start ~
modifiers ~
attrs ~
(sync
:fun_decl_kind
)
1824 let pos, name = pname in
1825 let tparams = class_params
env in
1826 let params = parameter_list
env in
1827 let ret = hint_return_opt
env in
1828 let constrs = where_clause
env in
1829 let is_generator, body_stmts
= function_body
env in
1830 let method_end = Pos.make
env.file
env.lb in
1831 let ret = method_implicit_return
env pname ret in
1832 if name = "__destruct" && params <> []
1833 then error_at env pos "Destructor must not have any parameters.";
1835 m_tparams
= tparams;
1838 m_constrs
= constrs;
1839 m_ret_by_ref
= is_ref;
1840 m_body
= body_stmts
;
1842 m_user_attributes
= attrs;
1843 m_fun_kind
= fun_kind sync
is_generator;
1844 m_span
= Pos.btw method_start
method_end;
1845 m_doc_comment
= None
;
1848 (*****************************************************************************)
1849 (* Constructor/Destructors special cases. *)
1850 (*****************************************************************************)
1852 and method_implicit_return
env (pos, name) ret =
1853 match name, ret with
1854 | ("__construct" | "__destruct"), None
-> ret
1855 | _, Some
(_, Happly
((_, "void"), [])) -> ret
1856 | "__construct", Some
_ ->
1857 error_at env pos "Constructor return type must be void or elided.";
1859 | "__destruct", Some
_ ->
1860 error_at env pos "Destructor return type must be void or elided.";
1864 (*****************************************************************************)
1865 (* Implicit class fields __construct(public int $x). *)
1866 (*****************************************************************************)
1868 and class_implicit_fields
class_ =
1869 let class_body = method_implicit_fields
class_.c_body
in
1870 { class_ with c_body
= class_body }
1872 and method_implicit_fields members
=
1875 | Method
({ m_name
= _, "__construct"; _ } as m) :: rl
->
1876 let fields, assigns
= param_implicit_fields
m.m_params
in
1877 fields @ Method
{ m with m_body
= assigns
@ m.m_body
} :: rl
1879 x
:: method_implicit_fields rl
1881 and param_implicit_fields
params =
1884 | { param_modifier
= Some vis
; _ } as p
:: rl
->
1885 let member, stmt = param_implicit_field vis p
in
1886 let members, assigns
= param_implicit_fields rl
in
1887 member :: members, stmt :: assigns
1889 param_implicit_fields rl
1891 and param_implicit_field vis p
=
1892 (* Building the implicit field (for example: private int $x;) *)
1893 let pos, name = p
.param_id
in
1894 let cvname = pos, class_var_name
name in
1895 let span = match p
.param_expr
with
1896 | Some
(pos_end, _) -> Pos.btw pos pos_end
1899 let member = ClassVars
([vis
], p
.param_hint
, [span, cvname, None
]) in
1900 (* Building the implicit assignment (for example: $this->x = $x;) *)
1901 let this = pos, "$this" in
1903 Expr
(pos, Binop
(Eq None
, (pos, Obj_get
((pos, Lvar
this),
1906 (pos, Lvar p
.param_id
)))
1910 (*****************************************************************************)
1911 (* Function/Method bodies. *)
1912 (*****************************************************************************)
1914 and function_body
env =
1915 match L.token env.file
env.lb with
1919 let previous_in_generator = !(env.in_generator
) in
1920 env.in_generator
:= false;
1921 let statements = (match env.mode
with
1924 (* This is a hack for the type-checker to make a distinction
1925 * Between function foo(); and function foo() {}
1929 (match statement_list
env with
1931 | _ when env.quick
-> [Noop
]
1934 let in_generator = !(env.in_generator) in
1935 env.in_generator := previous_in_generator ;
1936 in_generator, statements
1938 error_expect env "{";
1941 and fun_kind sync
(has_yield
:bool) =
1942 match sync
, has_yield
with
1943 | FDeclAsync
, true -> FAsyncGenerator
1944 | FDeclSync
, true -> FGenerator
1945 | FDeclAsync
, false -> FAsync
1946 | FDeclSync
, false -> FSync
1948 and ignore_body
env =
1949 match L.token env.file
env.lb with
1950 | Tlcb
-> ignore_body
env; ignore_body
env
1953 let pos = Pos.make
env.file
env.lb in
1954 let abs_pos = env.lb.Lexing.lex_curr_pos
in
1955 ignore
(expr_string
env pos abs_pos);
1958 let pos = Pos.make
env.file
env.lb in
1959 ignore
(expr_encapsed
env pos);
1962 ignore
(expr_heredoc
env);
1964 | Tword
when (Lexing.lexeme
env.lb) = "yield" ->
1965 env.in_generator := true;
1967 | Tword
when (Lexing.lexeme
env.lb) = "function" && peek env = Tlp
->
1968 (* this covers the async case as well *)
1969 let pos = Pos.make
env.file
env.lb in
1970 with_ignored_yield
env
1971 (fun () -> ignore
(expr_anon_fun
env pos ~sync
:FDeclSync
));
1974 with_ignored_yield
env
1975 (fun () -> ignore
(try_short_lambda
env));
1977 | Tlt
when is_xhp env ->
1980 | Teof
-> error_expect env "}"; ()
1982 ignore
(L.comment
(Buffer.create
256) env.file
env.lb);
1984 | _ -> ignore_body
env
1986 and with_ignored_yield
env fn
=
1987 let previous_in_generator = !(env.in_generator) in
1989 env.in_generator := previous_in_generator; ()
1991 (*****************************************************************************)
1993 (*****************************************************************************)
1995 and statement_list
env =
1996 match L.token env.file
env.lb with
1999 let block = statement_list
env in
2000 Block
block :: statement_list
env
2004 error_expect env "}";
2008 let error_state = !(env.errors
) in
2009 let stmt = statement
env in
2010 if !(env.errors
) != error_state
2011 then L.next_newline_or_close_cb
env.lb;
2012 stmt :: statement_list
env
2015 match L.token env.file
env.lb with
2017 let word = Lexing.lexeme
env.lb in
2018 let stmt = statement_word
env word in
2021 Block
(statement_list
env)
2033 and ignore_statement
env =
2034 (* Parse and ignore statements *)
2035 let error_state = !(env.errors
) in
2036 (* Any parsing error that occurs inside the statement should not
2037 raise errors in decl mode(or when there's already a parse error).
2038 For example, hack accepts:
2043 ignore
(statement
env);
2044 env.errors
:= error_state
2046 and parse_expr
env =
2052 and statement_word
env = function
2053 | "break" -> statement_break
env
2054 | "continue" -> statement_continue
env
2055 | "throw" -> statement_throw
env
2056 | "return" -> statement_return
env
2057 | "static" -> statement_static
env
2058 | "print" -> statement_echo
env
2059 | "echo" -> statement_echo
env
2060 | "if" -> statement_if
env
2061 | "do" -> statement_do
env
2062 | "while" -> statement_while
env
2063 | "for" -> statement_for
env
2064 | "switch" -> statement_switch
env
2065 | "foreach" -> statement_foreach
env
2066 | "try" -> statement_try
env
2067 | "goto" -> statement_goto
env
2068 | "function" | "class" | "trait" | "interface" | "const"
2069 | "async" | "abstract" | "final" ->
2071 "Parse error: declarations are not supported outside global scope";
2072 ignore
(ignore_toplevel None ~
attr:[] [] env (fun _ -> true));
2074 | x
when peek env = Tcolon
->
2075 (* Unfortunately, some XHP elements allow for expressions to look like goto
2076 * label declarations. For example,
2078 * await :intern:roadmap:project::genUpdateOnClient($project);
2080 * Looks like it is declaring a label whose name is await. To preserve
2081 * compatibility with PHP while working around this issue, we use the
2082 * following heuristic:
2084 * When we encounter a statement that is a word followed by a colon:
2085 * 1) Attempt to parse it as an expression. If there are no additional
2086 * errors, the result is used as the expression.
2087 * 2) If there are additional errors, then revert the error and lexer
2088 * state, and parse the statement as a goto label.
2091 match try_parse_with_errors env parse_expr
with
2093 | None
-> statement_goto_label
env x
2095 | x
-> parse_expr
env
2097 (*****************************************************************************)
2098 (* Break statement *)
2099 (*****************************************************************************)
2101 and statement_break
env =
2102 let stmt = Break
(Pos.make
env.file
env.lb, None
) in
2106 (*****************************************************************************)
2107 (* Continue statement *)
2108 (*****************************************************************************)
2110 and statement_continue
env =
2111 let stmt = Continue
(Pos.make
env.file
env.lb, None
) in
2115 and check_continue
env =
2116 match L.token env.file
env.lb with
2118 | Tint
-> error_continue env
2119 | _ -> error_expect env ";"
2121 (*****************************************************************************)
2122 (* Throw statement *)
2123 (*****************************************************************************)
2125 and statement_throw
env =
2130 (*****************************************************************************)
2131 (* Return statement *)
2132 (*****************************************************************************)
2134 and statement_return
env =
2135 let pos = Pos.make
env.file
env.lb in
2136 let value = return_value
env in
2139 and return_value
env =
2140 match L.token env.file
env.lb with
2148 (*****************************************************************************)
2149 (* Goto statement *)
2150 (*****************************************************************************)
2152 and statement_goto_label
env label
=
2153 let pos = Pos.make
env.file
env.lb in
2155 TypecheckerOptions.experimental_feature_enabled
2157 TypecheckerOptions.experimental_goto
in
2158 if not
goto_allowed then error env "goto is not supported.";
2160 GotoLabel
(pos, label
)
2162 and statement_goto
env =
2163 let pos = Pos.make
env.file
env.lb in
2165 TypecheckerOptions.experimental_feature_enabled
2167 TypecheckerOptions.experimental_goto
in
2168 if not
goto_allowed then error env "goto labels are not supported.";
2169 match L.token env.file
env.lb with
2171 let word = Lexing.lexeme
env.lb in
2175 error env "goto must use a label.";
2178 (*****************************************************************************)
2179 (* Static variables *)
2180 (*****************************************************************************)
2182 and statement_static
env =
2183 let pos = Pos.make
env.file
env.lb in
2184 match L.token env.file
env.lb with
2187 let el = static_var_list
env in
2191 let id = pos, Id
(pos, "static") in
2192 let e = expr_remain
env id in
2195 and static_var_list
env =
2196 let error_state = !(env.errors
) in
2197 let cst = static_var
env in
2198 match L.token env.file
env.lb with
2202 if !(env.errors
) != error_state
2204 else cst :: static_var_list_remain
env
2205 | _ -> error_expect env ";"; [cst]
2207 and static_var_list_remain
env =
2208 match L.token env.file
env.lb with
2212 let error_state = !(env.errors
) in
2213 let cst = static_var
env in
2214 match L.token env.file
env.lb with
2218 if !(env.errors
) != error_state
2220 else cst :: static_var_list_remain
env
2222 error_expect env ";"; [cst]
2224 and static_var
env =
2227 (*****************************************************************************)
2228 (* Switch statement *)
2229 (*****************************************************************************)
2231 and statement_switch
env =
2232 let e = paren_expr
env in
2234 let casel = switch_body
env in
2237 (* switch(...) { _ } *)
2238 and switch_body
env =
2239 match L.token env.file
env.lb with
2243 let word = Lexing.lexeme
env.lb in
2244 switch_body_word
env word
2246 error_expect env "}";
2249 and switch_body_word
env = function
2253 let stl = case_body
env in
2254 Case
(e, stl) :: switch_body
env
2257 let stl = case_body
env in
2258 Default
stl :: switch_body
env
2259 | _ -> error_expect env "case"; []
2261 (* switch(...) { case/default: _ } *)
2263 match L.token env.file
env.lb with
2265 (match Lexing.lexeme
env.lb with
2266 | "case" | "default" -> L.back
env.lb; []
2269 let error_state = !(env.errors
) in
2270 let st = statement
env in
2271 if !(env.errors
) != error_state
2273 else st :: case_body
env
2279 let error_state = !(env.errors
) in
2280 let st = statement
env in
2281 if !(env.errors
) != error_state
2283 else st :: case_body
env
2285 (*****************************************************************************)
2287 (*****************************************************************************)
2289 and statement_if
env =
2290 let e = paren_expr
env in
2291 let st1 = statement
env in
2292 let st2 = statement_else
env in
2293 If
(e, [st1], [st2])
2295 and statement_else
env =
2296 match L.token env.file
env.lb with
2298 (match Lexing.lexeme
env.lb with
2299 | "else" -> statement
env
2300 | "elseif" -> statement_if
env
2301 | _ -> L.back
env.lb; Noop
2303 | _ -> L.back
env.lb; Noop
2305 (*****************************************************************************)
2306 (* Do/While do statement *)
2307 (*****************************************************************************)
2309 and statement_do
env =
2310 let st = statement
env in
2311 expect_word env "while";
2312 let e = paren_expr
env in
2316 and statement_while
env =
2317 let e = paren_expr
env in
2318 let st = statement
env in
2321 (*****************************************************************************)
2323 (*****************************************************************************)
2325 and statement_for
env =
2327 let start = Pos.make
env.file
env.lb in
2328 let _ = L.token env.file
env.lb in
2329 let _ = L.back
env.lb in
2330 let last, el = for_expr
env in
2331 let e1 = Pos.btw start last, Expr_list
el in
2333 let last, el = for_expr
env in
2334 let e2 = Pos.btw start last, Expr_list
el in
2336 let last, el = for_last_expr
env in
2337 let e3 = Pos.btw start last, Expr_list
el in
2338 let st = statement
env in
2339 For
(e1, e2, e3, [st])
2342 match L.token env.file
env.lb with
2344 Pos.make
env.file
env.lb, []
2347 let error_state = !(env.errors
) in
2349 match L.token env.file
env.lb with
2351 Pos.make
env.file
env.lb, [e]
2352 | _ when !(env.errors
) != error_state ->
2354 Pos.make
env.file
env.lb, [e]
2356 let last, el = for_expr
env in
2359 error_expect env ";";
2360 Pos.make
env.file
env.lb, [e]
2362 and for_last_expr
env =
2363 match L.token env.file
env.lb with
2365 Pos.make
env.file
env.lb, []
2368 let error_state = !(env.errors
) in
2370 match L.token env.file
env.lb with
2372 Pos.make
env.file
env.lb, [e]
2373 | _ when !(env.errors
) != error_state ->
2375 Pos.make
env.file
env.lb, [e]
2377 let last, el = for_last_expr
env in
2380 error_expect env ")";
2381 Pos.make
env.file
env.lb, [e]
2383 (*****************************************************************************)
2384 (* Foreach statement *)
2385 (*****************************************************************************)
2387 and statement_foreach
env =
2391 match L.token env.file
env.lb with
2392 | Tword
when Lexing.lexeme
env.lb = "await" -> Some
(Pos.make
env.file
env.lb)
2393 | _ -> L.back
env.lb; None
in
2394 expect_word env "as";
2395 let as_expr = foreach_as
env in
2396 let st = statement
env in
2397 Foreach
(e, await, as_expr, [st])
2399 and foreach_as
env =
2400 let e1 = expr
env in
2401 match L.token env.file
env.lb with
2403 let e2 = expr
env in
2404 check_foreach_lvalue env e2;
2408 check_foreach_lvalue env e1;
2411 error_expect env ")";
2414 (*****************************************************************************)
2416 (*****************************************************************************)
2418 and statement_try
env =
2419 let st = statement
env in
2420 let cl = catch_list
env in
2421 let fin = finally
env in
2422 (* At least one catch or finally block must be provided after every try *)
2424 | [], [] -> error_expect env "catch or finally"; Try
([st], [], [])
2425 | _ -> Try
([st], cl, fin)
2427 and catch_list
env =
2428 match L.token env.file
env.lb with
2429 | Tword
when Lexing.lexeme
env.lb = "catch" ->
2431 let name = identifier env in
2432 let e = variable env in
2434 let st = statement
env in
2435 (name, e, [st]) :: catch_list
env
2436 | _ -> L.back
env.lb; []
2439 match L.token env.file
env.lb with
2440 | Tword
when Lexing.lexeme
env.lb = "finally" ->
2441 let st = statement
env in
2443 | _ -> L.back
env.lb; []
2445 (*****************************************************************************)
2446 (* Echo statement *)
2447 (*****************************************************************************)
2449 and statement_echo
env =
2450 let pos = Pos.make
env.file
env.lb in
2451 let args = echo_args
env in
2452 let f = pos, Id
(pos, "echo") in
2453 Expr
(pos, Call
(f, args, []))
2457 match L.token env.file
env.lb with
2463 error_expect env ";"; []
2465 (*****************************************************************************)
2466 (* Function/Method parameters *)
2467 (*****************************************************************************)
2469 and parameter_list
env =
2470 (* A parameter list follows one of these five patterns:
2473 ( normal-parameters )
2474 ( normal-parameters , )
2475 ( variadic-parameter )
2476 ( normal-parameters , variadic-parameter )
2478 A variadic parameter follows one of these two patterns:
2481 attributes-opt modifiers-opt typehint-opt ... $variable
2484 * A variadic parameter is never followed by a comma
2485 * A variadic parameter with a type must also have a variable.
2489 parameter_list_remain
env
2491 and parameter_list_remain
env =
2492 (* We have either just parsed the left paren that opens a parameter list,
2493 or a normal parameter -- possibly ending in a comma. *)
2494 match L.token env.file
env.lb with
2497 [parameter_varargs
env]
2500 let error_state = !(env.errors
) in
2501 let p = param
env in
2502 match L.token env.file
env.lb with
2506 if !(env.errors
) != error_state
2508 else p :: parameter_list_remain
env
2510 error_expect env ")"; [p]
2512 and parameter_default_with_variadic is_variadic
env =
2513 let default = parameter_default
env in
2514 if default <> None
&& is_variadic
then begin
2515 error env "A variadic parameter cannot have a default value."
2517 if is_variadic
then None
else default
2519 and parameter_varargs
env =
2520 (* We were looking for a parameter; we got "...". We are now expecting
2521 an optional variable followed immediately by a right paren.
2522 ... $x = whatever is an error. *)
2523 let pos = Pos.make
env.file
env.lb in
2524 (match L.token env.file
env.lb with
2525 | Trp
-> make_param_ellipsis
(pos, "...");
2528 let param_id = variable env in
2529 let _ = parameter_default_with_variadic
true env in
2531 make_param_ellipsis
param_id
2534 and make_param_ellipsis
param_id =
2535 { param_hint
= None
;
2536 param_is_reference
= false;
2537 param_is_variadic
= true;
2540 param_modifier
= None
;
2541 param_user_attributes
= [];
2545 (* We have a parameter that does not start with ... so it is of one of
2548 attributes-opt modifiers-opt typehint-opt ref-opt $name default-opt
2549 attributes-opt modifiers-opt typehint-opt ... $name
2551 let param_user_attributes = attribute
env in
2552 let param_modifier = parameter_modifier
env in
2553 let param_hint = parameter_hint
env in
2554 let param_is_reference = ref_opt env in
2555 let param_is_variadic = ellipsis_opt env in
2556 if param_is_reference && param_is_variadic then begin
2557 error env "A variadic parameter may not be passed by reference."
2559 let param_id = variable env in
2560 let param_expr = parameter_default_with_variadic
param_is_variadic env in
2561 if param_is_variadic then begin
2571 param_user_attributes;
2574 and parameter_modifier
env =
2575 match L.token env.file
env.lb with
2577 (match Lexing.lexeme
env.lb with
2578 | "private" -> Some Private
2579 | "public" -> Some Public
2580 | "protected" -> Some Protected
2581 | _ -> L.back
env.lb; None
2583 | _ -> L.back
env.lb; None
2585 and parameter_hint
env =
2586 if parameter_has_hint
env
2587 then Some
(hint
env)
2590 and parameter_has_hint
env =
2591 look_ahead env begin fun env ->
2592 match L.token env.file
env.lb with
2593 | Tellipsis
| Tamp
| Tlvar
-> false
2597 and parameter_default
env =
2598 match L.token env.file
env.lb with
2600 let default = expr
env in
2602 | _ -> L.back
env.lb; None
2604 (*****************************************************************************)
2605 (* Method where-clause (type constraints) *)
2606 (*****************************************************************************)
2608 and where_clause
env =
2609 match L.token env.file
env.lb with
2610 | Tword
when Lexing.lexeme
env.lb = "where" -> where_clause_constraints
env
2611 | _ -> L.back
env.lb; []
2613 and where_clause_constraints
env =
2614 if peek env = Tlcb
|| peek env = Tsc
then [] else
2615 let error_state = !(env.errors
) in
2616 let t1 = hint
env in
2617 match option_constraint_operator
env with
2620 let t2 = hint
env in
2621 let constr = (t1, c, t2) in
2622 match L.token env.file
env.lb with
2624 if !(env.errors
) != error_state
2626 else constr :: where_clause_constraints
env
2627 | Tlcb
| Tsc
-> L.back
env.lb; [constr]
2628 | _ -> error_expect env ", or { or ;"; [constr]
2630 and option_constraint_operator
env =
2631 match L.token env.file
env.lb with
2632 | Tword
when Lexing.lexeme
env.lb = "as" -> Some Constraint_as
2633 | Teq
-> Some Constraint_eq
2634 | Tword
when Lexing.lexeme
env.lb = "super" -> Some Constraint_super
2635 | _ -> error_expect env "type constraint operator (as, super or =)"; None
2637 (*****************************************************************************)
2639 (*****************************************************************************)
2642 let e1 = expr_atomic ~allow_class
:false ~class_const
:false env in
2643 let e2 = expr_remain
env e1 in
2648 expr_list_remain
env
2650 and expr_list_remain
env =
2651 match L.token env.file
env.lb with
2655 let error_state = !(env.errors
) in
2656 let e = expr
{ env with priority = 0 } in
2657 match L.token env.file
env.lb with
2661 if !(env.errors
) != error_state
2663 else e :: expr_list_remain
env
2664 | _ -> error_expect env ")"; [e]
2666 and expr_remain
env e1 =
2667 match L.token env.file
env.lb with
2669 expr_binop
env Tplus Plus
e1
2671 expr_binop
env Tminus Minus
e1
2673 expr_binop
env Tstar Star
e1
2675 expr_binop
env Tstarstar Starstar
e1
2677 expr_binop
env Tslash Slash
e1
2679 expr_assign
env Teq
(Eq None
) e1
2681 expr_assign
env Tbareq
(Eq
(Some Bar
)) e1
2683 expr_assign
env Tpluseq
(Eq
(Some Plus
)) e1
2685 expr_assign
env Tstarstareq
(Eq
(Some Starstar
)) e1
2687 expr_assign
env Tstareq
(Eq
(Some Star
)) e1
2689 expr_assign
env Tslasheq
(Eq
(Some Slash
)) e1
2691 expr_assign
env Tdoteq
(Eq
(Some Dot
)) e1
2693 expr_assign
env Tminuseq
(Eq
(Some Minus
)) e1
2695 expr_assign
env Tpercenteq
(Eq
(Some Percent
)) e1
2697 expr_assign
env Txoreq
(Eq
(Some Xor
)) e1
2699 expr_assign
env Tampeq
(Eq
(Some Amp
)) e1
2701 expr_assign
env Tlshifteq
(Eq
(Some Ltlt
)) e1
2703 expr_assign
env Trshifteq
(Eq
(Some Gtgt
)) e1
2705 expr_binop
env Teqeqeq EQeqeq
e1
2707 expr_binop
env Tgt Gt
e1
2709 expr_binop
env Tpercent Percent
e1
2711 expr_binop
env Tdot Dot
e1
2713 expr_binop
env Teqeq Eqeq
e1
2715 expr_binop
env Tampamp AMpamp
e1
2717 expr_binop
env Tbarbar BArbar
e1
2719 expr_binop
env Tdiff Diff
e1
2721 expr_binop
env Tlt Lt
e1
2723 expr_binop
env Tdiff2 Diff2
e1
2725 expr_binop
env Tgte Gte
e1
2727 expr_binop
env Tlte Lte
e1
2729 expr_binop
env Tcmp Cmp
e1
2731 expr_binop
env Tamp Amp
e1
2733 expr_binop
env Tbar Bar
e1
2735 expr_binop
env Tltlt Ltlt
e1
2737 expr_binop
env Tgtgt Gtgt
e1
2739 expr_binop
env Txor Xor
e1
2741 expr_pipe
env e1 Tpipe
2742 | Tincr
| Tdecr
as uop
->
2743 expr_postfix_unary
env uop
e1
2744 | Tarrow
| Tnsarrow
as tok ->
2745 expr_arrow
env e1 tok
2751 expr_array_get
env e1
2753 error env "Do not use { to subscript, use [";
2754 expr_array_get
env e1
2758 expr_null_coalesce
env e1
2759 | Tword
when Lexing.lexeme
env.lb = "instanceof" ->
2760 expr_instanceof
env e1
2761 | Tword
when Lexing.lexeme
env.lb = "and" ->
2762 error env ("Do not use \"and\", it has surprising precedence. "^
2763 "Use \"&&\" instead");
2764 expr_binop
env Tampamp AMpamp
e1
2765 | Tword
when Lexing.lexeme
env.lb = "or" ->
2766 error env ("Do not use \"or\", it has surprising precedence. "^
2767 "Use \"||\" instead");
2768 expr_binop
env Tbarbar BArbar
e1
2769 | Tword
when Lexing.lexeme
env.lb = "xor" ->
2770 error env ("Do not use \"xor\", it has surprising precedence. "^
2771 "Cast to bool and use \"^\" instead");
2772 expr_binop
env Txor Xor
e1
2776 (*****************************************************************************)
2777 (* Expression reducer *)
2778 (*****************************************************************************)
2780 and reduce
env e1 op make
=
2781 let e, continue
= reduce_
env e1 op make
in
2782 if continue
then expr_remain
env e else e
2784 and reduce_
env e1 op make
=
2785 let current_prio = env.priority in
2786 let assoc, prio
= get_priority op
in
2787 let env = { env with priority = prio
} in
2788 if prio
= current_prio
2792 let e = make
e1 { env with priority = env.priority + 1 } in
2793 expr_remain
env e, true
2795 let e = make
e1 env in
2798 error env "This operator is not associative, add parentheses";
2799 let e = make
e1 env in
2801 else if prio
< current_prio
2807 assert (prio
> current_prio);
2809 then make
e1 env, true
2810 else reduce_
env e1 op make
2813 (*****************************************************************************)
2814 (* lambda expressions *)
2815 (*****************************************************************************)
2817 and lambda_expr_body
: env -> block = fun env ->
2818 (* check for an async block *)
2819 let tok = L.token env.file
env.lb in
2820 let value = Lexing.lexeme
env.lb in
2822 if tok <> Tword
|| value <> "async"
2825 (* in sync lambda: just transform into an async lambda *)
2826 (* in async lambda: be explicit about the Awaitable<Awaitable<X>> return
2827 * type with a return statement *)
2828 error_back env "Unexpected use of async {...} as lambda expression"
2831 let (p, e1) = expr
env in
2832 [Return
(p, (Some
(p, e1)))]
2834 and lambda_body ~sync
env params ret =
2835 let is_generator, body_stmts
=
2838 * ==> { ...function body }
2839 * We reset the priority for parsing the function body.
2841 * See test ternary_within_lambda_block_within_ternary.php
2843 then with_base_priority env function_body
2846 * We keep the current priority so that possible priority ambiguities
2847 * give rise to errors and must be resolved with parens.
2849 * See test ternary_within_lambda_within_ternary.php
2851 * NB: as of Apr 2015, a lambda expression body can't contain a yield
2853 else false, (lambda_expr_body
env))
2855 let f_fun_kind = fun_kind sync
is_generator in
2857 f_name
= (Pos.none
, ";anonymous");
2862 f_ret_by_ref
= false;
2863 f_body
= body_stmts
;
2864 f_user_attributes
= [];
2867 f_namespace
= Namespace_env.empty
env.popt
;
2868 f_span
= Pos.none
; (* We only care about span of "real" functions *)
2869 f_doc_comment
= None
;
2874 and make_lambda_param
: id -> fun_param
= fun var_id
->
2877 param_is_reference = false;
2878 param_is_variadic = false;
2881 param_modifier = None
;
2882 param_user_attributes = [];
2885 and lambda_single_arg ~
(sync
:fun_decl_kind
) env var_id
: expr_
=
2887 lambda_body ~sync
env [make_lambda_param var_id
] None
2889 and try_short_lambda
env =
2890 try_parse env begin fun env ->
2891 let error_state = !(env.errors
) in
2892 let param_list = parameter_list_remain
env in
2893 if !(env.errors
) != error_state then begin
2894 env.errors
:= error_state;
2897 let ret = hint_return_opt
env in
2898 if !(env.errors
) != error_state then begin
2899 env.errors
:= error_state;
2901 end else if not
(peek env = Tlambda
)
2905 Some
(lambda_body ~sync
:FDeclSync
env param_list ret)
2910 and try_xhp_attr_use
env =
2911 try_parse env begin fun env ->
2912 match L.token env.file
env.lb with
2914 (match L.xhpname
env.file
env.lb with
2916 let name = (Pos.make
env.file
env.lb, ":"^
Lexing.lexeme
env.lb) in
2917 (match L.token env.file
env.lb with
2920 Some
(XhpAttrUse
(class_hint_with_name
env name))
2928 and try_xhp_enum_hint
env =
2929 try_parse env begin fun env ->
2930 match L.token env.file
env.lb with
2931 | Tword
when Lexing.lexeme
env.lb = "enum" ->
2932 let pos = Pos.make
env.file
env.lb in
2934 let items = xhp_enum_decl_list
env in
2939 (*****************************************************************************)
2941 (*****************************************************************************)
2943 and expr_atomic ~allow_class ~class_const
env =
2944 let tok = L.token env.file
env.lb in
2945 let pos = Pos.make
env.file
env.lb in
2948 let tok_value = Lexing.lexeme
env.lb in
2949 pos, Int
(pos, tok_value)
2951 let tok_value = Lexing.lexeme
env.lb in
2952 pos, Float
(pos, tok_value)
2954 let absolute_pos = env.lb.Lexing.lex_curr_pos
in
2955 expr_string
env pos absolute_pos
2957 expr_encapsed
env pos
2959 let tok_value = Lexing.lexeme
env.lb in
2960 let dollars, var_id
= strip_variablevariable
0 tok_value in
2961 pos, if peek env = Tlambda
2962 then lambda_single_arg ~sync
:FDeclSync
env (pos, var_id
)
2963 else if dollars < 1 then
2965 else if env.mode
= FileInfo.Mdecl
then
2966 Lvarvar
(dollars, (pos, var_id
))
2968 error_at env pos ("A valid variable name starts with a " ^
2969 "letter or underscore, followed by any number of letters, " ^
2970 "numbers, or underscores");
2971 Lvarvar
(dollars, (pos, var_id
))
2975 let name = identifier env in
2977 | Tem
| Tincr
| Tdecr
| Ttild
| Tplus
| Tminus
| Tamp
as op
->
2978 expr_prefix_unary
env pos op
2980 with_priority env Tat expr
2982 let word = Lexing.lexeme
env.lb in
2983 expr_atomic_word ~allow_class ~class_const
env pos word
2985 (match try_short_lambda
env with
2988 then expr_cast
env pos
2989 else with_base_priority env begin fun env ->
2992 let end_ = Pos.make
env.file
env.lb in
2993 Pos.btw pos end_, snd
e
2998 expr_short_array
env pos
2999 | Tlt
when is_xhp env ->
3004 error env ("A valid variable name starts with a letter or underscore,"^
3005 "followed by any number of letters, numbers, or underscores");
3008 pos, Lvar
(pos, "$$")
3010 (* Consume the rest of the comment. *)
3011 ignore
(L.comment
(Buffer.create
256) env.file
env.lb);
3013 let end_ = Pos.make
env.file
env.lb in
3014 Pos.btw pos end_, Unsafeexpr
e
3016 error_expect env "expression";
3019 and expr_atomic_word ~allow_class ~class_const
env pos = function
3020 | "class" when not allow_class
->
3021 error_expect env "expression";
3023 | "final" | "abstract" | "interface" | "trait" ->
3024 error_expect env "expression";
3026 | "true" when not class_const
->
3028 | "false" when not class_const
->
3030 | "null" when not class_const
->
3034 | "darray" when peek env = Tlb
->
3036 | "varray" when peek env = Tlb
->
3043 expr_anon_async
env pos
3045 expr_anon_fun
env pos ~sync
:FDeclSync
3046 | name when is_collection
env name ->
3047 expr_collection
env pos name
3051 env.in_generator := true;
3056 expr_php_list
env pos
3057 | r
when is_import r
->
3058 if env.mode
= FileInfo.Mstrict
3061 ("Parse error: "^r^
" is supported only as a toplevel "^
3063 expr_import r
env pos
3064 | x
when not class_const
&& String.lowercase x
= "true" ->
3065 Lint.lowercase_constant
pos x
;
3067 | x
when not class_const
&& String.lowercase x
= "false" ->
3068 Lint.lowercase_constant
pos x
;
3070 | x
when not class_const
&& String.lowercase x
= "null" ->
3071 Lint.lowercase_constant
pos x
;
3073 | x
when String.lowercase x
= "array" ->
3078 and strip_variablevariable
(dollars: int) token =
3079 if (token.[0] = '$'
) && (token.[1] = '$'
) then
3080 strip_variablevariable
(dollars + 1)
3081 (String.sub
token 1 ((String.length
token) - 1))
3085 (*****************************************************************************)
3086 (* Expressions in parens. *)
3087 (*****************************************************************************)
3089 and paren_expr
env =
3090 with_base_priority env begin fun env ->
3097 (*****************************************************************************)
3098 (* Assignments (=, +=, -=, ...) *)
3099 (*****************************************************************************)
3101 and expr_assign
env bop ast_bop
e1 =
3102 reduce
env e1 bop
begin fun e1 env ->
3103 check_lvalue env e1;
3104 let e2 = expr
{ env with priority = 0 } in
3105 btw e1 e2, Binop
(ast_bop
, e1, e2)
3108 (*****************************************************************************)
3109 (* Binary operations (+, -, /, ...) *)
3110 (*****************************************************************************)
3112 and expr_binop
env bop ast_bop
e1 =
3113 reduce
env e1 bop
begin fun e1 env ->
3114 let e2 = expr
env in
3115 btw e1 e2, Binop
(ast_bop
, e1, e2)
3118 (*****************************************************************************)
3119 (* Pipe operator |> *)
3120 (*****************************************************************************)
3122 and expr_pipe
env e1 tok =
3123 reduce
env e1 tok begin fun e1 env ->
3124 let e2 = expr
env in
3125 let pos = btw e1 e2 in
3129 (*****************************************************************************)
3130 (* Object Access ($obj->method) *)
3131 (*****************************************************************************)
3133 and expr_arrow
env e1 tok =
3134 reduce
env e1 tok begin fun e1 env ->
3136 let saved = save_lexbuf_state env.lb in
3137 match L.varname
env.lb with
3139 let name = Lexing.lexeme
env.lb in
3140 let pos = Pos.make
env.file
env.lb in
3143 restore_lexbuf_state env.lb saved;
3146 btw e1 e2, (match tok with
3147 | Tarrow
-> Obj_get
(e1, e2, OG_nullthrows
)
3148 | Tnsarrow
-> Obj_get
(e1, e2, OG_nullsafe
)
3149 | _ -> assert false)
3152 (*****************************************************************************)
3153 (* Class Access (ClassName::method_name) *)
3154 (*****************************************************************************)
3156 and expr_colcol
env e1 =
3157 reduce
env e1 Tcolcol
begin fun e1 env ->
3160 (* XYZ::class is OK ... *)
3161 expr_colcol_remain ~allow_class
:true env e1 cname
3163 (* ... but get_class($x) should be used instead of $x::class ... *)
3164 expr_colcol_remain ~allow_class
:false env e1 cname
3166 error_at env pos "Expected class name";
3171 and expr_colcol_remain ~allow_class
env e1 cname =
3172 match expr_atomic
env ~allow_class ~class_const
:true with
3173 | (_, Lvar x
) as p ->
3174 btw e1 x
, Class_get
(cname, p)
3176 btw e1 x
, Class_const
(cname, x
)
3178 error_at env pos "Expected identifier";
3181 (*****************************************************************************)
3182 (* Function call (f(params)) *)
3183 (*****************************************************************************)
3185 and expr_call
env e1 =
3186 reduce
env e1 Tlp
begin fun e1 env ->
3188 let args1, args2
= expr_call_list
env in
3189 let end_ = Pos.make
env.file
env.lb in
3190 Pos.btw (fst
e1) end_, Call
(e1, args1, args2
)
3193 (* An expr_call_list is the same as an expr_list except for the possibility
3194 * of ParamUnpack (aka splat) calls of the form:
3197 and expr_call_list
env =
3199 expr_call_list_remain
env
3201 and expr_call_list_remain
env =
3202 match L.token env.file
env.lb with
3204 | Tellipsis
-> (* f($x, $y, << ...$args >> ) *)
3205 let unpack_e = expr
{ env with priority = 0 } in
3206 check_call_time_reference
unpack_e;
3207 (* no regular params after an unpack *)
3208 (match L.token env.file
env.lb with
3212 | Trp
-> [], [unpack_e]
3213 | _ -> error_expect env ")"; [], [unpack_e])
3216 let error_state = !(env.errors
) in
3217 let e = expr
{ env with priority = 0 } in
3218 check_call_time_reference
e;
3219 match L.token env.file
env.lb with
3222 if !(env.errors
) != error_state
3225 let reg, unpack
= expr_call_list_remain
env
3228 | _ -> error_expect env ")"; [e], []
3230 and check_call_time_reference
= function
3231 | p, Unop
(Uref
, _) -> Errors.call_time_pass_by_reference
p
3234 (*****************************************************************************)
3236 (*****************************************************************************)
3238 and is_collection
env name =
3239 (peek env = Tlcb
) ||
3240 (name = "dict" && peek env = Tlb
) ||
3241 (name = "keyset" && peek env = Tlb
) ||
3242 (name = "vec" && peek env = Tlb
)
3244 and expr_collection
env pos name =
3245 let sentinels = match name with
3246 | x
when x
= "dict" || x
= "keyset" || x
= "vec" -> (Tlb
, Trb
)
3249 let fds = collection_field_list
env sentinels in
3250 let end_ = Pos.make
env.file
env.lb in
3251 Pos.btw pos end_, Collection
((pos, name), fds)
3253 and collection_field_list
env (start_sentinel
, end_sentinel
) =
3254 expect env start_sentinel
;
3255 collection_field_list_remain
env end_sentinel
3257 and collection_field_list_remain
env end_sentinel
=
3258 match L.token env.file
env.lb with
3259 | x
when x
= end_sentinel
-> []
3262 let error_state = !(env.errors
) in
3263 let fd = array_field
env in
3264 match L.token env.file
env.lb with
3265 | x
when x
= end_sentinel
->
3268 if !(env.errors
) != error_state
3270 else fd :: collection_field_list_remain
env end_sentinel
3272 error_expect env (L.token_to_string end_sentinel
); []
3274 (*****************************************************************************)
3275 (* Imports - require/include/require_once/include_once *)
3276 (*****************************************************************************)
3279 List.mem
["require"; "require_once"; "include"; "include_once"] r
3281 and expr_import r
env start =
3282 let flavor = match r
with
3283 | "require" -> Require
3284 | "include" -> Include
3285 | "require_once" -> RequireOnce
3286 | "include_once" -> IncludeOnce
3287 (* We just checked for this very condition *)
3288 | _ -> assert false in
3289 (* all the import statements have the same priority *)
3290 with_priority env Timport
begin fun env ->
3292 Pos.btw start (fst
e), Import
(flavor, e)
3295 (*****************************************************************************)
3297 (*****************************************************************************)
3299 and expr_instanceof
env e1 =
3300 reduce
env e1 Tinstanceof
begin fun e1 env ->
3301 let e2 = expr
env in
3302 btw e1 e2, InstanceOf
(e1, e2)
3305 (*****************************************************************************)
3307 (*****************************************************************************)
3309 and expr_yield
env start =
3310 with_priority env Tyield
begin fun env ->
3311 match L.token env.file
env.lb with
3312 | Tword
when Lexing.lexeme
env.lb = "break" ->
3313 let end_ = Pos.make
env.file
env.lb in
3314 Pos.btw start end_, Yield_break
3317 let af = array_field
env in
3321 and expr_await
env start =
3322 with_priority env Tawait
begin fun env ->
3324 Pos.btw start (fst
e), Await
e
3327 (*****************************************************************************)
3329 (*****************************************************************************)
3331 and expr_clone
env start =
3332 with_base_priority env begin fun env ->
3334 Pos.btw start (fst
e), Clone
e
3337 (*****************************************************************************)
3339 (*****************************************************************************)
3341 and expr_php_list
env start =
3342 let el = expr_list
env in
3343 let end_ = Pos.make
env.file
env.lb in
3344 Pos.btw start end_, List
el
3346 (*****************************************************************************)
3347 (* Anonymous functions *)
3348 (*****************************************************************************)
3350 and expr_anon_async
env pos =
3351 match L.token env.file
env.lb with
3352 | Tword
when Lexing.lexeme
env.lb = "function" ->
3353 expr_anon_fun
env pos ~sync
:FDeclAsync
3355 let var_pos = Pos.make
env.file
env.lb in
3356 pos, lambda_single_arg ~sync
:FDeclAsync
env (var_pos, Lexing.lexeme
env.lb)
3358 let param_list = parameter_list_remain
env in
3359 let ret = hint_return_opt
env in
3361 pos, lambda_body ~sync
:FDeclAsync
env param_list ret
3362 | Tlcb
-> (* async { ... } *)
3364 let lambda = pos, lambda_body ~sync
:FDeclAsync
env [] None
in
3365 pos, Call
(lambda, [], [])
3368 pos, Id
(pos, "async")
3370 and expr_anon_fun
env pos ~
(sync
:fun_decl_kind
) =
3371 let env = { env with priority = 0 } in
3372 let params = parameter_list
env in
3373 let ret = hint_return_opt
env in
3374 let use = function_use
env in
3375 let is_generator, body_stmts
= function_body
env in
3377 f_name
= (Pos.none
, ";anonymous");
3382 f_ret_by_ref
= false;
3383 f_body
= body_stmts
;
3384 f_user_attributes
= [];
3385 f_fun_kind = fun_kind sync
is_generator;
3387 f_namespace
= Namespace_env.empty
env.popt
;
3388 f_span
= Pos.none
; (* We only care about span of "real" functions *)
3389 f_doc_comment
= None
;
3395 (*****************************************************************************)
3396 (* Use (for functions) *)
3397 (*****************************************************************************)
3399 and function_use
env =
3400 match L.token env.file
env.lb with
3401 | Tword
when Lexing.lexeme
env.lb = "use" ->
3404 | _ -> L.back
env.lb; []
3407 match L.token env.file
env.lb with
3411 let error_state = !(env.errors
) in
3412 let var = ref_variable env in
3413 match L.token env.file
env.lb with
3415 if !(env.errors
) != error_state
3417 else var :: use_list
env
3421 error_expect env ")";
3424 (*****************************************************************************)
3425 (* New: new ClassName(...) *)
3426 (*****************************************************************************)
3428 and expr_new
env pos_start
=
3429 with_priority env Tnew
begin fun env ->
3434 let typeargs = class_hint_params
env in
3437 else (p, Id_type_arguments
(id, typeargs))
3445 error_expect env "class name";
3448 let args1, args2
= expr_call_list
env in
3449 let pos_end = Pos.make
env.file
env.lb in
3450 Pos.btw pos_start
pos_end, New
(cname, args1, args2
)
3453 (*****************************************************************************)
3454 (* Casts: (int|..|float) expr *)
3455 (*****************************************************************************)
3457 and is_cast_type
= function
3458 | "int" | "float" | "double" | "string"
3459 | "array" | "object" | "bool" | "unset" -> true
3462 (* (int), (float), etc are considered cast tokens by HHVM, so we will always
3463 * interpret them as casts. I.e. (object) >> 1 is a parse error because it is
3464 * trying to cast the malformed expression `>> 1` to an object. On the other
3465 * hand, (x) >> 1 is parsed like `x >> 1`, because (x) is not a cast token. *)
3467 look_ahead env begin fun env ->
3468 L.token env.file
env.lb = Tword
&&
3469 let cast_name = Lexing.lexeme
env.lb in
3470 L.token env.file
env.lb = Trp
&& begin
3471 is_cast_type
cast_name ||
3472 match L.token env.file
env.lb with
3473 (* We cannot be making a cast if the next token is a binary / ternary
3474 * operator, or if it's the end of a statement (i.e. a semicolon.) *)
3475 | Tqm
| Tsc
| Tstar
| Tslash
| Txor
| Tpercent
| Tlt
| Tgt
| Tltlt
| Tgtgt
3476 | Tlb
| Trb
| Tdot
| Tlambda
| Trp
-> false
3481 and expr_cast
env start_pos =
3482 with_priority env Tcast
begin fun env ->
3483 let tok = L.token env.file
env.lb in
3484 let cast_type = Lexing.lexeme
env.lb in
3485 assert (tok = Tword
);
3486 let p = Pos.make
env.file
env.lb in
3488 let ty = p, Happly
((p, cast_type), []) in
3490 Pos.btw start_pos (fst
e), Cast
(ty, e)
3493 (*****************************************************************************)
3494 (* Unary operators $i++ etc ... *)
3495 (*****************************************************************************)
3497 and unary_priority
= function
3498 | Tplus
| Tminus
-> Tincr
3502 and expr_prefix_unary
env start op
=
3503 with_priority env (unary_priority op
) begin fun env ->
3508 | Tincr
-> (check_lvalue env e; Uincr
)
3509 | Tdecr
-> (check_lvalue env e; Udecr
)
3516 Pos.btw start (fst
e), Unop
(op, e)
3519 and expr_postfix_unary
env uop
e1 =
3520 let end_ = Pos.make
env.file
env.lb in
3522 check_lvalue env e1;
3528 let e = Pos.btw (fst
e1) end_, Unop
(op, e1) in
3531 (*****************************************************************************)
3532 (* If expression: _?_:_ *)
3533 (*****************************************************************************)
3535 and is_colon_if
env =
3536 look_ahead env begin fun env ->
3537 let tok = L.token env.file
env.lb in
3539 (* At this point, we might still be dealing with an xhp identifier *)
3540 L.no_space_id
env.lb <> Tword
3543 and expr_if
env e1 =
3544 reduce
env e1 Tqm
begin fun e1 env ->
3546 then colon_if
env e1
3547 else ternary_if
env e1
3550 and ternary_if
env e1 =
3551 let e2 = expr
{ env with priority = 0 } in
3553 let e3 = expr
env in
3556 error_at env pos "You should add parentheses"
3558 Pos.btw (fst
e1) (fst
e3), Eif
(e1, Some
e2, e3)
3560 and colon_if
env e1 =
3562 let e2 = expr
env in
3563 Pos.btw (fst
e1) (fst
e2), Eif
(e1, None
, e2)
3565 (*****************************************************************************)
3566 (* Null coalesce expression: _??_ *)
3567 (*****************************************************************************)
3569 and expr_null_coalesce
env e1 =
3570 reduce
env e1 Tqmqm
begin fun e1 env ->
3571 let e2 = expr
env in
3572 btw e1 e2, NullCoalesce
(e1, e2)
3575 (*****************************************************************************)
3577 (*****************************************************************************)
3579 and make_string
env pos content f_unescape
=
3581 try f_unescape content
with
3582 | Php_escaping.Invalid_string
error -> error_at env pos error; ""
3583 in String
(pos, unescaped)
3585 and expr_string
env start abs_start
=
3586 match L.string env.file
env.lb with
3588 let pos = Pos.btw start (Pos.make
env.file
env.lb) in
3589 let len = env.lb.Lexing.lex_curr_pos
- abs_start
- 1 in
3590 let content = String.sub
env.lb.Lexing.lex_buffer abs_start
len in
3591 pos, make_string
env pos content Php_escaping.unescape_single
3593 error_at env start "string not closed";
3594 start, String
(start, "")
3597 and expr_encapsed
env start =
3598 let pos_start = Pos.make
env.file
env.lb in
3599 let el = encapsed_nested
pos_start env in
3600 let pos_end = Pos.make
env.file
env.lb in
3601 let pos = Pos.btw pos_start pos_end in
3602 (* Represent purely literal strings as just String *)
3604 | [] -> pos, String
(pos, "")
3605 | [_, String
(_, s
)] -> pos, String
(pos, s
)
3606 | el -> pos, String2
el
3608 and encapsed_nested
start env =
3609 let abs_start = env.lb.Lexing.lex_curr_pos
in
3610 (* Advance the lexer so we can get a start position that doesn't
3611 * include the opening quote or the last bit of the expression or
3612 * whatever. Then rewind it. *)
3613 let frag_start = look_ahead env (fun env ->
3614 let _ = L.string2
env.file
env.lb in
3615 Pos.make
env.file
env.lb
3617 encapsed_nested_inner
start (frag_start, abs_start) env
3619 and encapsed_text
env (start, abs_start) (stop
, abs_stop
) =
3620 let len = abs_stop
- abs_start in
3621 if len = 0 then [] else
3622 let pos = Pos.btw start stop
in
3623 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
3624 [pos, make_string
env pos content Php_escaping.unescape_double
]
3626 and encapsed_nested_inner
start frag
env =
3627 let cur_pos = Pos.make
env.file
env.lb, env.lb.Lexing.lex_curr_pos
in
3628 (* Get any literal string part that occurs before this point *)
3629 let get_text () = encapsed_text
env frag
cur_pos in
3631 (* We need to save the lexbuf here because L.string2 can match across
3632 * newlines, changing the line number in the process. Thus, L.back will
3633 * not restore us to a valid state; we need restore_lexbuf_state for that.
3635 let saved = save_lexbuf_state env.lb in
3636 match L.string2
env.file
env.lb with
3640 error_at env start "string not properly closed";
3642 | Tlcb
when env.mode
= FileInfo.Mdecl
->
3643 encapsed_nested
start env
3645 let saved = save_lexbuf_state env.lb in
3646 (match L.string2
env.file
env.lb with
3648 error env "{ not supported";
3649 restore_lexbuf_state env.lb saved;
3650 encapsed_nested
start env
3652 restore_lexbuf_state env.lb saved;
3653 let error_state = !(env.errors
) in
3655 (match L.string2
env.file
env.lb with
3657 | _ -> error_expect env "}");
3658 if !(env.errors
) != error_state
3660 else get_text () @ e :: encapsed_nested
start env
3662 restore_lexbuf_state env.lb saved;
3663 encapsed_nested_inner
start frag
env
3666 let saved = save_lexbuf_state env.lb in
3667 (match L.string2
env.file
env.lb with
3669 if env.mode
= FileInfo.Mstrict
3670 then error env "${ not supported";
3671 let error_state = !(env.errors
) in
3672 let result = (match L.string2
env.file
env.lb with
3674 (* The first token after ${ will lex as a word, but is actually
3675 * an lvar, so we need to fix it up. For example, "${foo}" should
3676 * be Lvar $foo, but will lex as Tdollar-Tlcb-Tword foo. *)
3677 let pos = Pos.make
env.file
env.lb in
3678 let lvar = pos, Lvar
(pos, "$" ^
Lexing.lexeme
env.lb) in
3679 encapsed_expr_reduce
pos env lvar
3681 error_expect env "variable";
3682 Pos.make
env.file
env.lb, Null
) in
3684 if !(env.errors
) != error_state
3686 else get_text () @ result :: encapsed_nested
start env
3688 restore_lexbuf_state env.lb saved;
3689 encapsed_nested_inner
start frag
env
3692 restore_lexbuf_state env.lb saved;
3693 let error_state = !(env.errors
) in
3694 let e = encapsed_expr
env in
3695 if !(env.errors
) != error_state
3697 else get_text () @ e :: encapsed_nested
start env
3698 | _ -> encapsed_nested_inner
start frag
env
3700 and encapsed_expr
env =
3701 match L.string2
env.file
env.lb with
3702 | Tlcb
when env.mode
= FileInfo.Mdecl
->
3703 Pos.make
env.file
env.lb, Null
3705 let pos = Pos.make
env.file
env.lb in
3706 let absolute_pos = env.lb.Lexing.lex_curr_pos
in
3707 expr_string
env pos absolute_pos
3709 let pos = Pos.make
env.file
env.lb in
3710 let tok_value = Lexing.lexeme
env.lb in
3711 pos, Int
(pos, tok_value)
3713 let pid = Pos.make
env.file
env.lb in
3714 let id = Lexing.lexeme
env.lb in
3717 let pos = Pos.make
env.file
env.lb in
3718 let lvar = pos, Lvar
(pos, Lexing.lexeme
env.lb) in
3719 encapsed_expr_reduce
pos env lvar
3721 error_expect env "expression";
3722 Pos.make
env.file
env.lb, Null
3724 and encapsed_expr_reduce
start env e1 =
3725 let e1, continue
= encapsed_expr_reduce_left
start env e1 in
3727 then encapsed_expr_reduce
start env e1
3730 and encapsed_expr_reduce_left
start env e1 =
3731 let saved = save_lexbuf_state env.lb in
3732 match L.string2
env.file
env.lb with
3735 match L.string2
env.file
env.lb with
3737 (* We need to special case this because any identifier
3738 * (including keywords) is allowed in this context.
3739 * For example: $x[function] is legal.
3741 let pid = Pos.make
env.file
env.lb in
3742 let id = Lexing.lexeme
env.lb in
3743 pid, (String
(pid, id))
3746 expr
{ env with priority = 0 }
3748 (match L.string2
env.file
env.lb with
3750 | _ -> error_expect env "]"
3752 let pos = Pos.btw start (Pos.make
env.file
env.lb) in
3753 (pos, Array_get
(e1, Some
e2)), true
3755 (match L.string2
env.file
env.lb with
3758 let e2 = encapsed_expr
env in
3759 let pos = Pos.btw start (Pos.make
env.file
env.lb) in
3760 (pos, Obj_get
(e1, e2, OG_nullthrows
)), true
3766 restore_lexbuf_state env.lb saved;
3769 (*****************************************************************************)
3771 (*****************************************************************************)
3773 and expr_heredoc
env =
3774 let abs_start = env.lb.Lexing.lex_curr_pos
in
3775 let tag = heredoc_tag
env in
3776 heredoc_body
tag env;
3777 let len = env.lb.Lexing.lex_curr_pos
- abs_start - 1 in
3778 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
3779 fst
tag, String
(fst
tag, content)
3781 and heredoc_tag
env =
3782 match L.token env.file
env.lb with
3784 Pos.make
env.file
env.lb, Lexing.lexeme
env.lb
3786 let pos = Pos.make
env.file
env.lb in
3787 let abs_pos = env.lb.Lexing.lex_curr_pos
in
3788 (match expr_string
env pos abs_pos with
3790 | _ -> assert false)
3792 error_expect env "heredoc or nowdoc identifier";
3793 Pos.make
env.file
env.lb, "HEREDOC"
3795 and heredoc_body
(pos, tag_value
as tag) env =
3796 match L.heredoc_token
env.lb with
3800 error_expect env tag_value
3802 heredoc_body
tag env
3804 and heredoc_end
(pos, tag_value
as tag) env =
3805 match L.heredoc_token
env.lb with
3807 let tag2 = Lexing.lexeme
env.lb in
3808 (match L.heredoc_token
env.lb with
3809 | Tnewline
when tag2 = tag_value
->
3813 | Tsc
when tag2 = tag_value
->
3817 heredoc_body
tag env
3822 heredoc_body
tag env
3825 (*****************************************************************************)
3827 (*****************************************************************************)
3829 (* Ideally, we would factor out the common logic in the following functions to
3830 take advantage of a function that looks like:
3835 (extract_field_function : env -> 'a)
3838 Unfortunately, because we are using imperative operations on the mutable
3839 env, this is not possible. Though expr_array_generic looks like it would be
3840 polymorphic, it would only be *weakly polymorphic*, which is to say that it
3841 can be used with any single type. Read more about Value Restriction from
3842 Real World OCaml at https://fburl.com/side-effects-and-weak-polymorphism. *)
3844 and expr_array
env pos =
3845 let fields = array_field_list
env in
3848 and array_field_list
env =
3850 array_field_list_remain
env Trp
[]
3852 and expr_short_array
env pos =
3853 let fields = array_field_list_remain
env Trb
[] in
3856 and array_field_list_remain
env terminal
acc =
3857 match L.token env.file
env.lb with
3858 | x
when x
= terminal
-> List.rev
acc
3861 let error_state = !(env.errors
) in
3862 let fd = array_field
env in
3863 let acc = fd :: acc in
3864 match L.token env.file
env.lb with
3865 | x
when x
= terminal
->
3868 if !(env.errors
) != error_state
3870 else array_field_list_remain
env terminal
acc
3871 | _ -> error_expect env ")"; [fd]
3873 and array_field
env =
3874 let env = { env with priority = 0 } in
3875 let e1 = expr
env in
3876 match L.token env.file
env.lb with
3878 let e2 = expr
env in
3884 and expr_darray
env pos =
3885 let darray_and_varray_allowed =
3886 TypecheckerOptions.experimental_feature_enabled
3888 TypecheckerOptions.experimental_darray_and_varray
in
3889 if not
darray_and_varray_allowed then Errors.darray_not_supported
pos;
3891 pos, Darray
(darray_field_list_remain
env [])
3893 and darray_field_list_remain
env acc =
3894 match L.token env.file
env.lb with
3895 | x
when x
= Trb
-> List.rev
acc
3898 let error_state = !(env.errors
) in
3899 let fd = darray_field
env in
3900 let acc = fd :: acc in
3901 match L.token env.file
env.lb with
3905 if !(env.errors
) != error_state
3907 else darray_field_list_remain
env acc
3908 | _ -> error_expect env "]"; [fd]
3910 and darray_field
env =
3911 let env = { env with priority = 0 } in
3912 let e1 = expr
env in
3914 let e2 = expr
env in
3917 and expr_varray
env pos =
3918 let darray_and_varray_allowed =
3919 TypecheckerOptions.experimental_feature_enabled
3921 TypecheckerOptions.experimental_darray_and_varray
in
3922 if not
darray_and_varray_allowed then Errors.varray_not_supported
pos;
3924 pos, Varray
(varray_field_list_remain
env [])
3926 and varray_field_list_remain
env acc =
3927 match L.token env.file
env.lb with
3928 | x
when x
= Trb
-> List.rev
acc
3931 let error_state = !(env.errors
) in
3932 let fd = varray_field
env in
3933 let acc = fd :: acc in
3934 match L.token env.file
env.lb with
3938 if !(env.errors
) != error_state
3940 else varray_field_list_remain
env acc
3941 | _ -> error_expect env "]"; [fd]
3943 and varray_field
env =
3944 let env = { env with priority = 0 } in
3947 (*****************************************************************************)
3949 (*****************************************************************************)
3951 and expr_shape
env pos =
3952 let fields = shape_field_list
env in
3955 and shape_field_list
env =
3957 shape_field_list_remain
env
3959 and shape_field_list_remain
env =
3960 match L.token env.file
env.lb with
3964 let error_state = !(env.errors
) in
3965 let fd = shape_field
env in
3966 match L.token env.file
env.lb with
3970 if !(env.errors
) != error_state
3972 else fd :: shape_field_list_remain
env
3973 | _ -> error_expect env ")"; [fd]
3975 and shape_field
env =
3976 if L.token env.file
env.lb = Tqm
then
3977 error env "Shape construction should not specify optional types.";
3980 let name = shape_field_name
env in
3982 let value = expr
{ env with priority = 0 } in
3985 and shape_field_name
env =
3986 let pos, e = expr
env in
3988 | String
p -> SFlit
p
3989 | Class_const
(id, ps
) -> SFclass_const
(id, ps
)
3990 | _ -> error_expect env "string literal or class constant";
3994 (*****************************************************************************)
3995 (* Array access ($my_array[]|$my_array[_]) *)
3996 (*****************************************************************************)
3998 and expr_array_get
env e1 =
3999 reduce
env e1 Tlb
begin fun e1 env ->
4000 match L.token env.file
env.lb with
4002 let end_ = Pos.make
env.file
env.lb in
4003 Pos.btw (fst
e1) end_, Array_get
(e1, None
)
4006 let e2 = expr
{ env with priority = 0 } in
4008 let end_ = Pos.make
env.file
env.lb in
4009 Pos.btw (fst
e1) end_, Array_get
(e1, Some
e2)
4012 (*****************************************************************************)
4014 (*****************************************************************************)
4017 look_ahead env begin fun env ->
4018 let tok = L.xhpname
env.file
env.lb in
4020 Lexing.lexeme
env.lb <> "new" &&
4021 Lexing.lexeme
env.lb <> "yield" &&
4022 let tok2 = L.xhpattr
env.file
env.lb in
4023 tok2 = Tgt
|| tok2 = Tword
||
4024 (tok2 = Tslash
&& L.xhpattr
env.file
env.lb = Tgt
)
4028 match L.xhpname
env.file
env.lb with
4030 let start = Pos.make
env.file
env.lb in
4031 let name = Lexing.lexeme
env.lb in
4032 let pname = start, ":"^
name in
4033 let attrl, closed
= xhp_attributes
env in
4034 let end_tag = Pos.make
env.file
env.lb in
4036 then Pos.btw start end_tag, Xml
(pname, attrl, [])
4038 let tag_pos = Pos.btw start end_tag in
4039 let el = xhp_body
tag_pos name env in
4040 let end_ = Pos.make
env.file
env.lb in
4041 Pos.btw start end_, Xml
(pname, attrl, el)
4043 error_expect env "xhpname";
4044 let pos = Pos.make
env.file
env.lb in
4045 pos, Xml
((pos, "xhp"), [], [])
4047 and xhp_attributes
env =
4048 match L.xhpattr
env.file
env.lb with
4050 if L.xhpattr
env.file
env.lb <> Tgt
4051 then error_expect env ">";
4056 let error_state = !(env.errors
) in
4057 let attr_name = Pos.make
env.file
env.lb, Lexing.lexeme
env.lb in
4059 let attr_value = xhp_attribute_value
env in
4060 if !(env.errors
) != error_state
4062 [attr_name, attr_value], true
4064 let rl, closed
= xhp_attributes
env in
4065 (attr_name, attr_value) :: rl, closed
4067 error_expect env ">";
4070 and xhp_attribute_value
env =
4071 match L.xhpattr
env.file
env.lb with
4072 | Tlcb
when env.mode
= FileInfo.Mdecl
->
4076 let result = expr
{ env with priority = 0 } in
4080 let start = Pos.make
env.file
env.lb in
4081 let abs_start = env.lb.Lexing.lex_curr_pos
in
4082 xhp_attribute_string
env start abs_start
4084 error_expect env "attribute value";
4085 let pos = Pos.make
env.file
env.lb in
4086 pos, String
(pos, "")
4088 and xhp_attribute_string
env start abs_start =
4089 match L.string2
env.file
env.lb with
4091 error_at env start "Xhp attribute not closed";
4092 start, String
(start, "")
4094 let len = env.lb.Lexing.lex_curr_pos
- abs_start - 1 in
4095 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
4096 let pos = Pos.btw start (Pos.make
env.file
env.lb) in
4097 pos, String
(pos, content)
4099 xhp_attribute_string
env start abs_start
4102 and xhp_body
pos name env =
4103 (* First grab any literal text that appears before the next
4105 let abs_start = env.lb.Lexing.lex_curr_pos
in
4106 let start_pos = File_pos.of_lexing_pos
env.lb.Lexing.lex_curr_p
in
4107 let start = Pos.make_from_file_pos
env.file
start_pos start_pos in
4108 let text = xhp_text
env start abs_start in
4109 (* Now handle any markup *)
4110 text @ xhp_body_inner
pos name env
4112 (* Grab literal text that appears inside of xhp. *)
4113 and xhp_text
env start abs_start =
4114 match L.xhptoken
env.file
env.lb with
4115 (* If we have hit something that is meaningful,
4116 * we have to stop collecting literal text and go back
4117 * to xhp_body. Grab any text, clean it up, and return. *)
4118 | Tlcb
| Tlt
| Topen_xhp_comment
| Teof
->
4121 let len = env.lb.Lexing.lex_curr_pos
- abs_start in
4122 let pos = Pos.btw start (Pos.make
env.file
env.lb) in
4124 let content = String.sub
env.lb.Lexing.lex_buffer
abs_start len in
4125 (* need to squash whitespace down to a single space *)
4126 let squished = Regexp_utils.squash_whitespace
content in
4127 (* if it is empty or all whitespace just ignore it *)
4128 if squished = "" || squished = " " then [] else
4129 [pos, String
(pos, squished)]
4130 (* TODO: xhp can contain html/xhp entities that need to be decoded. *)
4132 | _ -> xhp_text
env start abs_start
4134 (* parses an xhp body where we know that the next token is not
4135 * just more literal text *)
4136 and xhp_body_inner
pos name env =
4137 match L.xhptoken
env.file
env.lb with
4138 | Tlcb
when env.mode
= FileInfo.Mdecl
->
4140 xhp_body
pos name env
4142 let error_state = !(env.errors
) in
4143 let e = expr
{ env with priority = 0 } in
4145 if !(env.errors
) != error_state
4147 else e :: xhp_body
pos name env
4151 let xml = xhp
env in
4152 xml :: xhp_body
pos name env
4154 (match L.xhptoken
env.file
env.lb with
4156 let closing_tok = L.xhpname
env.file
env.lb in
4157 let closing_name = Lexing.lexeme
env.lb in
4158 if closing_tok = Txhpname
&&
4159 (L.xhptoken
env.file
env.lb = Tgt
)
4161 if closing_name = name
4164 error_expect env name;
4168 error_expect env "closing tag name";
4169 xhp_body
pos name env
4172 error_at env pos "Stray < in xhp";
4174 xhp_body
pos name env
4177 error_at env pos "Xhp tag not closed";
4179 (* The lexer returns open comments so that we can notice them and
4180 * drop them from our text fields. Parse the comment and continue. *)
4181 | Topen_xhp_comment
->
4182 xhp_comment
env.file
env.lb;
4183 xhp_body
pos name env
4184 (* xhp_body_inner only gets called when one of the above was seen *)
4187 (*****************************************************************************)
4189 (*****************************************************************************)
4191 and typedef ~
attr ~is_abstract
env =
4192 let id = identifier env in
4193 let tparams = class_params
env in
4194 let tconstraint = typedef_constraint
env in
4196 let td = hint
env in
4198 let kind = if is_abstract
then NewType
td else Alias
td in
4201 t_tparams
= tparams;
4202 t_constraint
= tconstraint;
4204 t_user_attributes
= attr;
4205 t_namespace
= Namespace_env.empty
env.popt
;
4209 and typedef_constraint
env =
4210 match L.token env.file
env.lb with
4211 | Tword
when Lexing.lexeme
env.lb = "as" ->
4217 and promote_nullable_to_optional_in_shapes
env =
4218 TypecheckerOptions.experimental_feature_enabled
4220 TypecheckerOptions.experimental_promote_nullable_to_optional_in_shapes
4222 and hint_shape_info
env shape_keyword_pos
=
4223 match L.token env.file
env.lb with
4224 | Tlp
-> hint_shape_info_remain
env
4227 error_at env shape_keyword_pos
"\"shape\" is an invalid type; you need to \
4228 declare and use a specific shape type.";
4230 si_allows_unknown_fields
= promote_nullable_to_optional_in_shapes
env;
4231 si_shape_field_list
= [];
4234 and hint_shape_info_remain
env =
4235 match L.token env.file
env.lb with
4238 si_allows_unknown_fields
= promote_nullable_to_optional_in_shapes
env;
4239 si_shape_field_list
= [];
4244 si_allows_unknown_fields
= true;
4245 si_shape_field_list
= [];
4249 let error_state = !(env.errors
) in
4250 let fd = hint_shape_field
env in
4251 match L.token env.file
env.lb with
4254 si_allows_unknown_fields
=
4255 promote_nullable_to_optional_in_shapes
env;
4256 si_shape_field_list
= [fd];
4259 if !(env.errors
) != error_state
4261 si_allows_unknown_fields
=
4262 promote_nullable_to_optional_in_shapes
env;
4263 si_shape_field_list
= [fd];
4266 let { si_shape_field_list
; _ } as shape_info
=
4267 hint_shape_info_remain
env in
4268 let si_shape_field_list = fd :: si_shape_field_list in
4269 { shape_info
with si_shape_field_list }
4271 error_expect env ")";
4273 si_allows_unknown_fields
=
4274 promote_nullable_to_optional_in_shapes
env;
4275 si_shape_field_list = [fd]
4278 and hint_shape_field
env =
4279 (* Consume the next token to determine if we're creating an optional field. *)
4281 if L.token env.file
env.lb = Tqm
then
4284 (* In this case, we did not find an optional type, so we'll back out by a
4285 token to parse the shape. *)
4286 (L.back
env.lb; false)
4288 let sf_name = shape_field_name
env in
4290 let sf_hint = hint
env in
4291 { sf_optional; sf_name; sf_hint }
4293 (*****************************************************************************)
4295 (*****************************************************************************)
4298 (* The safety of the recursive calls here is slightly subtle. Normally, we
4299 * check for errors when making a recursive call to make sure we don't get
4300 * stuck in a loop. Here, we actually don't need to do that, since the only
4301 * time we make a recursive call is when we see (and thus consume) a token
4302 * that we like. So every time we recurse we'll consume at least one token,
4303 * so we can't get stuck in an infinite loop. *)
4304 let tl = match env.mode
with
4305 | FileInfo.Mdecl
-> ignore_toplevel None ~
attr:[]
4307 (* The name for a namespace is actually optional, so we need to check for
4308 * the name first. Setting the name to an empty string if there's no
4309 * identifier following the `namespace` token *)
4310 let id = match L.token env.file
env.lb with
4311 | Tword
-> L.back
env.lb; identifier env
4312 | _ -> L.back
env.lb; Pos.make
env.file
env.lb, "" in
4313 match L.token env.file
env.lb with
4315 let body = tl [] env (fun x
-> x
= Trcb
|| x
= Teof
) in
4318 | Tsc
when (snd
id) = "" ->
4319 error_expect env "{";
4322 let terminate = function
4323 | Tword
-> Lexing.lexeme
env.lb = "namespace"
4326 let body = tl [] env terminate in
4329 error_expect env "{ or ;";
4332 and namespace_kind
env =
4333 match L.token env.file
env.lb with
4335 match Lexing.lexeme
env.lb with
4336 | "function" -> NSFun
4337 | "const" -> NSConst
4339 | "namespace" -> NSNamespace
4340 | _ -> (L.back
env.lb; NSClassAndNamespace
)
4342 | _ -> (L.back
env.lb; NSClassAndNamespace
)
4344 and namespace_use
env =
4345 let kind = namespace_kind
env in
4346 let maybe_group_use_prefix = try_parse env begin fun env ->
4347 match L.token env.file
env.lb with
4349 let prefix = Lexing.lexeme
env.lb in
4350 match L.token env.file
env.lb with
4351 | Tlcb
-> Some
prefix
4356 match maybe_group_use_prefix with
4357 | Some
prefix -> namespace_group_use
env kind prefix
4358 | None
-> namespace_use_list
env false Tsc
kind []
4360 and namespace_use_list
env allow_change_kind end_token
kind acc =
4361 let kind = if allow_change_kind
then namespace_kind
env else kind in
4362 let p1, s1
= identifier env in
4363 let id1 = p1, if s1
.[0] = '
\\'
then s1
else "\\" ^ s1
in
4365 match L.token env.file
env.lb with
4366 | Tword
when Lexing.lexeme
env.lb = "as" ->
4370 let str = snd
id1 in
4371 let start = try (String.rindex
str '
\\'
) + 1 with Not_found
-> 0 in
4372 let len = (String.length
str) - start in
4373 fst
id1, String.sub
str start len
4375 let acc = (kind, id1, id2) :: acc in
4376 match L.token env.file
env.lb with
4377 | x
when x
= end_token
-> acc
4378 | Tcomma
-> namespace_use_list
env allow_change_kind end_token
kind acc
4380 error_expect env "namespace use list";
4383 and namespace_group_use
env kind prefix =
4384 (* This should be an assert, but those tend to just crash the server in an
4385 * impossible to debug way. *)
4387 if String.length
prefix > 0 then prefix
4388 else (error env "Internal error: prefix length is 0"; "parse_error") in
4389 let prefix = if prefix.[0] = '
\\'
then prefix else "\\" ^
prefix in
4391 (* The prefix must end with a namespace separator in the syntax, but when we
4392 * smash it up with the suffixes, we don't want to double-up on the
4393 * separators, so check to make sure it's there and then strip it off. *)
4394 let prefix_len = String.length
prefix in
4395 let prefix = if prefix.[prefix_len - 1] = '
\\'
4396 then String.sub
prefix 0 (prefix_len - 1)
4397 else (error_expect env "group use prefix to end with '\\'"; prefix) in
4399 let allow_change_kind = (kind = NSClassAndNamespace
) in
4400 let unprefixed = namespace_use_list
env allow_change_kind Trcb
kind [] in
4402 List.map
unprefixed begin fun (kind, (p1, s1
), id2) ->
4403 (kind, (p1, prefix ^ s1
), id2)
4406 (*****************************************************************************)
4408 (*****************************************************************************)
4410 let from_file ?
(quick
= false) popt file
=
4412 try Sys_utils.cat (Relative_path.to_absolute file
) with _ -> "" in
4413 program ~quick popt file
content
4415 let get_file_mode popt file
content =
4416 let lb = Lexing.from_string
content in
4417 let env = init_env file
lb popt
false in
4418 snd
(get_header
env)
4420 let from_file_with_default_popt ?
(quick
= false) file
=
4421 from_file ~quick
ParserOptions.default file