A shape should support unknown fields when declared with '...' even if 'promote_nulla...
[hiphop-php.git] / hphp / hack / src / parser / parser_hack.ml
blob3ddc621dc6a6a7fab19f918f21b4225b458cb728
1 (**
2 * Copyright (c) 2015, Facebook, Inc.
3 * All rights reserved.
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.
9 *)
10 open Ast
11 open Core
12 open Lexer_hack
13 open Prim_defs
15 module L = Lexer_hack
17 (*****************************************************************************)
18 (* Environment *)
19 (*****************************************************************************)
21 type env = {
22 file : Relative_path.t;
23 mode : FileInfo.mode;
24 priority : int;
25 lb : Lexing.lexbuf;
26 errors : (Pos.t * string) list ref;
27 in_generator : bool ref;
28 popt : ParserOptions.t;
29 quick : bool;
32 let init_env file lb popt quick = {
33 file = file;
34 mode = FileInfo.Mpartial;
35 priority = 0;
36 lb = lb;
37 errors = ref [];
38 in_generator = ref false;
39 popt = popt;
40 quick = quick;
43 type parser_return = {
44 file_mode : FileInfo.mode option; (* None if PHP *)
45 comments : (Pos.t * comment) list;
46 ast : Ast.program;
47 content : string;
50 (*****************************************************************************)
51 (* Lexer (with backtracking) *)
52 (*****************************************************************************)
54 type saved_lb = {
55 (* no need to save refill_buff because it's constant *)
56 lex_abs_pos : int;
57 lex_start_pos : int;
58 lex_curr_pos : int;
59 lex_last_pos : int;
60 lex_last_action : int;
61 lex_eof_reached : bool;
62 lex_mem : int array;
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 =
81 begin
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;
91 end
94 * Call a function with a forked lexing environment, and return its
95 * result.
97 let look_ahead (env : env) (f : env -> 'a) : 'a =
98 let saved = save_lexbuf_state env.lb in
99 let ret = f env 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
110 * popped.
112 let try_parse (env : env) (f : env -> 'a option) : 'a option =
113 let saved = save_lexbuf_state env.lb in
114 match f env with
115 | Some x -> Some x
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 =
120 let parse env =
121 let error_state = !(env.errors) in
122 let result = f env in
123 if !(env.errors) == error_state then
124 Some result
125 else
127 env.errors := error_state;
128 None
129 ) in
130 try_parse env parse
132 (* Return the next token without updating lexer state *)
133 let peek env =
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 (*****************************************************************************)
152 (* Errors *)
153 (*****************************************************************************)
155 let error_at env pos msg =
156 env.errors := (pos, msg) :: !(env.errors)
158 let error env msg =
159 error_at env (Pos.make env.file env.lb) msg
161 let error_continue env =
162 error 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
168 L.back env.lb;
169 error_at env pos msg
172 let error_expect env expect =
173 error_back env ("Expected "^expect)
175 let expect env x =
176 if L.token env.file env.lb = x
177 then ()
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
192 | [] -> ()
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
204 | _ -> false
206 then ()
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
211 | [] -> ()
212 | (Private | Public | Protected as vis) :: rl ->
213 (match last_vis with
214 | Some vis2 when vis <> vis2 ->
215 error_at env pos
216 "Parse error. Cannot mix different visibilities."
217 | _ ->
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
223 | [_] | [] -> ()
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")), _, _) ->
259 error_at env pos
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
282 * or non-assoc).
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 (*****************************************************************************)
290 type assoc =
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 *)
295 let priorities = [
296 (* Lowest priority *)
297 (NonAssoc, [Tyield]);
298 (NonAssoc, [Tawait]);
299 (Left, [Timport; Teval;]);
300 (Left, [Tcomma]);
301 (Right, [Tprint]);
302 (Left, [Tpipe]);
303 (Left, [Tqm; Tcolon]);
304 (Right, [Tqmqm]);
305 (Left, [Tbarbar]);
306 (Left, [Txor]);
307 (Left, [Tampamp]);
308 (Left, [Tbar]);
309 (Left, [Tamp]);
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]);
315 (Right, [Tem]);
316 (NonAssoc, [Tinstanceof]);
317 (Right, [Ttild; Tincr; Tdecr; Tcast]);
318 (Right, [Tstarstar]);
319 (Right, [Tat; Tref]);
320 (Left, [Tlp]);
321 (NonAssoc, [Tnew; Tclone]);
322 (Left, [Tlb]);
323 (Right, [Teq; Tpluseq; Tminuseq; Tstareq; Tstarstareq;
324 Tslasheq; Tdoteq; Tpercenteq;
325 Tampeq; Tbareq; Txoreq; Tlshifteq; Trshifteq]);
326 (Left, [Tarrow; Tnsarrow]);
327 (Left, [Telseif]);
328 (Left, [Telse]);
329 (Left, [Tendif]);
330 (Left, [Tcolcol]);
331 (Left, [Tdollar]);
332 (* Highest priority *)
335 let get_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)
344 end;
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.
353 * An example:
354 * expr "1 + 2 + 3"
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
360 * "+" sign.
362 priority := !priority + 2
363 end;
364 fun tok ->
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
371 f env
373 let with_base_priority env f =
374 let env = { env with priority = 0 } in
375 f env
377 (*****************************************************************************)
378 (* References *)
379 (*****************************************************************************)
381 let ref_opt env =
382 match L.token env.file env.lb with
383 | Tamp when env.mode = FileInfo.Mstrict ->
384 error env "Don't use references!";
385 true
386 | Tamp ->
387 true
388 | _ ->
389 L.back env.lb;
390 false
392 (*****************************************************************************)
393 (* Identifiers *)
394 (*****************************************************************************)
396 let xhp_identifier env =
397 (match L.xhpname env.file env.lb with
398 | Txhpname ->
399 Pos.make env.file env.lb, ":"^Lexing.lexeme env.lb
400 | _ ->
401 error_expect env "identifier";
402 Pos.make env.file env.lb, "*Unknown*"
405 (* identifier *)
406 let identifier env =
407 match L.token env.file env.lb with
408 | Tword ->
409 let pos = Pos.make env.file env.lb in
410 let name = Lexing.lexeme env.lb in
411 pos, name
412 | Tcolon ->
413 let start = Pos.make env.file env.lb in
414 let end_, name = xhp_identifier env in
415 Pos.btw start end_, name
416 | _ ->
417 error_expect env "identifier";
418 Pos.make env.file env.lb, "*Unknown*"
420 (* $variable *)
421 let variable env =
422 match L.token env.file env.lb with
423 | Tlvar ->
424 Pos.make env.file env.lb, Lexing.lexeme env.lb
425 | _ ->
426 error_expect env "variable";
427 Pos.make env.file env.lb, "$_" (* SpecialIdents.placeholder *)
429 (* &$variable *)
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
436 | Tellipsis -> true
437 | _ -> L.back env.lb; false
439 (*****************************************************************************)
440 (* Entry point *)
441 (*****************************************************************************)
443 let rec program
444 ?(quick = false) (* Quick parsing of only declarations *)
445 ?(elaborate_namespaces = true)
446 ?(include_line_comments = false)
447 ?(keep_errors = true)
448 popt file content =
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
462 end;
463 let ast = if elaborate_namespaces
464 then Namespaces.elaborate_defs env.popt ast
465 else ast in
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)
472 file content =
473 program
474 ~elaborate_namespaces
475 ~include_line_comments
476 ~keep_errors
477 ParserOptions.default
478 file
479 content
481 (*****************************************************************************)
482 (* Hack headers (strict, decl, partial) *)
483 (*****************************************************************************)
485 and header env =
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
491 let attr = [] in
492 let result = ignore_toplevel None ~attr [] env (fun x -> x = Teof) in
493 expect env Teof;
494 result, head
495 | _, Some mode ->
496 let result = toplevel [] { env with mode = mode } (fun x -> x = Teof) in
497 expect env Teof;
498 result, head
499 | _ ->
500 [], head
502 and get_header env =
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
508 | `explicit_mode ->
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
516 | _ ->
517 error env
518 "Incorrect comment; possible values include strict, decl, partial or empty";
519 FileInfo.HhFile, Some FileInfo.Mdecl
522 (*****************************************************************************)
523 (* Decl mode *)
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 ->
529 L.back env.lb;
531 | Tltlt ->
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
537 | Tlcb ->
538 let acc = ignore_toplevel attr_start ~attr acc env terminate in
539 ignore_toplevel attr_start ~attr acc env terminate
540 | Tquote ->
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
545 | Tdquote ->
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
549 | Theredoc ->
550 ignore (expr_heredoc env);
551 ignore_toplevel attr_start ~attr acc env terminate
552 | Tlt when is_xhp env ->
553 ignore (xhp env);
554 ignore_toplevel attr_start ~attr acc env terminate
555 | Tword ->
556 (match Lexing.lexeme env.lb with
557 | "function" ->
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
561 | Tword ->
562 L.back env.lb;
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 *)
566 | Tamp ->
567 (match L.token env.file env.lb with
568 | Tword ->
569 L.back env.lb;
570 let def = toplevel_word def_start ~attr env "function" in
571 ignore_toplevel None ~attr:[] (def @ acc) env terminate
572 | _ ->
573 ignore_toplevel attr_start ~attr acc env terminate
575 | _ ->
576 ignore_toplevel attr_start ~attr acc env terminate
579 | "abstract" | "final"
580 | "class"| "trait" | "interface"
581 | "namespace"
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
588 | "use" ->
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
594 | _ ->
595 begin
596 ignore_statement env;
597 ignore_toplevel attr_start ~attr (acc) env terminate
600 | Tclose_php ->
601 error env "Hack does not allow the closing ?> tag";
603 | _ ->
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 ->
614 L.back env.lb;
615 List.rev acc
616 | Tsc ->
617 (* Ignore extra semicolons at toplevel (important so we don't yell about
618 * them in strict mode). *)
619 toplevel acc env terminate
620 | Tltlt ->
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
627 | Tword ->
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
632 | Tclose_php ->
633 error env "Hack does not allow the closing ?> tag";
634 List.rev acc
635 | _ ->
636 (* All the other statements. *)
637 let pos = Pos.make env.file env.lb in
638 L.back env.lb;
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
660 ) in
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
667 [Class class_]
668 | "class" ->
669 let class_ = class_ def_start ~attr ~final:false ~kind:Cnormal env in
670 [Class class_]
671 | "trait" ->
672 let class_ = class_ def_start ~attr ~final:false ~kind:Ctrait env in
673 [Class class_]
674 | "interface" ->
675 let class_ = class_ def_start ~attr ~final:false ~kind:Cinterface env in
676 [Class class_]
677 | "enum" ->
678 let class_ = enum_ def_start ~attr env in
679 [Class class_]
680 | "async" ->
681 expect_word env "function";
682 let fun_ = fun_ def_start ~attr ~sync:FDeclAsync env in
683 [Fun fun_]
684 | "function" ->
685 let fun_ = fun_ def_start ~attr ~sync:FDeclSync env in
686 [Fun fun_]
687 | "newtype" ->
688 let typedef_ = typedef ~attr ~is_abstract:true env in
689 [Typedef typedef_]
690 | "type" ->
691 let typedef_ = typedef ~attr ~is_abstract:false env in
692 [Typedef typedef_]
693 | "namespace" ->
694 let id, body = namespace env in
695 [Namespace (id, body)]
696 | "use" ->
697 let usel = namespace_use env in
698 [NamespaceUse usel]
699 | "const" ->
700 let consts = class_const_def env in
701 (match consts with
702 | Const (h, cstl) ->
703 List.map cstl begin fun (x, y) -> Constant {
704 cst_mode = env.mode;
705 cst_kind = Cst_const;
706 cst_name = x;
707 cst_type = h;
708 cst_value = y;
709 cst_namespace = Namespace_env.empty env.popt;
710 } end
711 | _ -> assert false)
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
715 expect env Tsc;
716 [Stmt (Expr e)]
717 | _ ->
718 let pos = Pos.make env.file env.lb in
719 L.back env.lb;
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], [])) ->
726 Constant {
727 cst_mode = env.mode;
728 cst_kind = Cst_define;
729 cst_name = name;
730 cst_type = None;
731 cst_value = value;
732 cst_namespace = Namespace_env.empty env.popt;
734 | stmt ->
735 Stmt stmt
737 (*****************************************************************************)
738 (* Attributes: <<_>> *)
739 (*****************************************************************************)
741 (* <<_>> *)
742 and attribute env =
743 if look_ahead env (fun env -> L.token env.file env.lb = Tltlt)
744 then begin
745 expect env Tltlt;
746 attribute_remain env;
748 else []
750 (* _>> *)
751 and attribute_remain env =
752 match L.token env.file env.lb with
753 | Tword ->
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
759 | _ ->
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; []
769 (* ,_,>> *)
770 and attribute_list_remain env =
771 match L.token env.file env.lb with
772 | Tgtgt -> []
773 | Tcomma -> attribute_remain env
774 | _ ->
775 error_expect env ">>";
778 (*****************************************************************************)
779 (* Functions *)
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
793 { f_name = name;
794 f_tparams = tparams;
795 f_params = params;
796 f_ret = ret;
797 f_ret_by_ref = is_ref;
798 f_body = body_stmts;
799 f_constrs = constrs;
800 f_user_attributes = attr;
801 f_fun_kind = fun_kind sync is_generator;
802 f_mode = env.mode;
803 f_namespace = Namespace_env.empty env.popt;
804 f_span = Pos.btw fun_start fun_end;
805 f_doc_comment = None;
806 f_static = false;
809 (*****************************************************************************)
810 (* Classes *)
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
817 let cextends =
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
824 let result =
825 { c_mode = env.mode;
826 c_final = final;
827 c_kind = kind;
828 c_is_xhp = is_xhp;
829 c_implements = cimplements;
830 c_tparams = tparams;
831 c_user_attributes = attr;
832 c_name = cname;
833 c_extends = cextends;
834 c_body = cbody;
835 c_namespace = Namespace_env.empty env.popt;
836 c_enum = None;
837 c_span = span;
838 c_doc_comment = None;
841 class_implicit_fields result
843 (*****************************************************************************)
844 (* Enums *)
845 (*****************************************************************************)
847 and enum_base_ty env =
848 expect env Tcolon;
849 let h = hint env in
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
859 let result =
860 { c_mode = env.mode;
861 c_final = false;
862 c_kind = Cenum;
863 c_is_xhp = false;
864 c_implements = [];
865 c_tparams = [];
866 c_user_attributes = attr;
867 c_name = cname;
868 c_extends = [];
869 c_body = cbody;
870 c_namespace = Namespace_env.empty env.popt;
871 c_enum = Some
872 { e_base = basety;
873 e_constraint = constraint_;
875 c_span = span;
876 c_doc_comment = None;
879 result
881 (* { ... *)
882 and enum_body env =
883 expect env Tlcb;
884 enum_defs env
886 and enum_defs env =
887 match peek env with
888 (* ... } *)
889 | Trcb ->
890 drop env;
892 | Tword ->
893 let const = class_const env in
894 let elem = Const (None, [const]) in
895 expect env Tsc;
896 let rest = enum_defs env in
897 elem :: rest
898 | _ ->
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
909 | Tword ->
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); []
915 | Tlcb ->
916 L.back env.lb;
918 | _ ->
919 error_expect env "{";
922 and class_implements kind env =
923 match L.token env.file env.lb with
924 | Tword ->
925 (match Lexing.lexeme env.lb with
926 | "implements" ->
927 let impl = class_extends_list env in
928 if kind = Cinterface then begin
929 error env "Expected: extends; Got implements"; []
930 end else
931 impl
932 | "extends" -> L.back env.lb; []
933 | s -> error env ("Expected: implements; Got: "^s); []
935 | Tlcb ->
936 L.back env.lb;
938 | _ ->
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
946 | Tlcb ->
947 L.back env.lb; [c]
948 | Tcomma ->
949 if !(env.errors) != error_state
950 then [c]
951 else c :: class_extends_list env
952 | Tword ->
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
972 | Tgt ->
973 [cst]
974 | Tcomma ->
975 if !(env.errors) != error_state
976 then [cst]
977 else cst :: class_param_list_remain env
978 | _ ->
979 error_expect env ">";
980 [cst]
982 and class_param_list_remain env =
983 match L.gt_or_comma env.file env.lb with
984 | Tgt -> []
985 | _ ->
986 L.back env.lb;
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
990 | Tgt ->
991 [cst]
992 | Tcomma ->
993 if !(env.errors) != error_state
994 then [cst]
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
1000 | Tplus ->
1001 if L.token env.file env.lb <> Tword
1002 then class_param_error env
1003 else
1004 let parameter_name, parameter_constraint = class_param_name env in
1005 Covariant, parameter_name, parameter_constraint
1006 | Tminus ->
1007 if L.token env.file env.lb <> Tword
1008 then class_param_error env
1009 else
1010 let parameter_name, parameter_constraint = class_param_name env in
1011 Contravariant, parameter_name, parameter_constraint
1012 | Tword ->
1013 let parameter_name, parameter_constraint = class_param_name env in
1014 let variance = Invariant in
1015 variance, parameter_name, parameter_constraint
1016 | _ ->
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" ->
1032 let h = hint env in
1033 (Constraint_as, h) :: class_parameter_constraint_list env
1034 | Tword when Lexing.lexeme env.lb = "super" ->
1035 let h = hint env in
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
1058 let h = hint env in
1059 match L.gt_or_comma env.file env.lb with
1060 | Tgt ->
1062 | Tcomma ->
1063 if !(env.errors) != error_state
1064 then [h]
1065 else h :: class_hint_param_list_remain env
1066 | _ ->
1067 error_expect env ">"; [h]
1069 and class_hint_param_list_remain env =
1070 match L.gt_or_comma env.file env.lb with
1071 | Tgt -> []
1072 | _ ->
1073 L.back env.lb;
1074 let error_state = !(env.errors) in
1075 let h = hint env in
1076 match L.gt_or_comma env.file env.lb with
1077 | Tgt ->
1079 | Tcomma ->
1080 if !(env.errors) != error_state
1081 then [h]
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 (*****************************************************************************)
1089 and hint env =
1090 match L.token env.file env.lb with
1091 (* ?_ *)
1092 | Tqm ->
1093 let start = Pos.make env.file env.lb in
1094 let e = hint env 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" ->
1101 L.back env.lb;
1102 hint_apply_or_access env []
1103 | Tword ->
1104 let h = hint_function env in
1105 error_at env (fst h) "Function hints must be parenthesized";
1107 (* (_) | (function(_): _) *)
1108 | Tlp ->
1109 let start_pos = Pos.make env.file env.lb in
1110 hint_paren start_pos env
1111 (* @_ *)
1112 | Tat ->
1113 let start = Pos.make env.file env.lb in
1114 let h = hint env in
1115 Pos.btw start (fst h), Hsoft h
1116 | _ ->
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
1123 (* A | :XHPNAME *)
1124 | Tword | Tcolon ->
1125 L.back env.lb;
1126 hint_apply_or_access_remainder env (identifier env :: id_list)
1127 | _ ->
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
1134 (* ...::... *)
1135 | Tcolcol -> hint_apply_or_access env id_list
1136 (* ...<_> | ... *)
1137 | _ ->
1138 L.back env.lb;
1139 begin match List.rev id_list with
1140 | [id] ->
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
1146 ~init:(fst id2)
1147 ~f:(fun _acc (p, _) -> p) in
1148 Pos.btw pos1 pos2, Haccess (id1, id2, ids)
1149 | [] ->
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)
1163 | _ ->
1164 L.back env.lb;
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
1168 match hintl with
1169 | [] -> assert false
1170 | [_] ->
1171 error_at env pos "Tuples of one element are not allowed";
1172 pos, Happly ((pos, "*Unknown*"), [])
1173 | hl -> pos, Htuple hl
1175 and hint_list env =
1176 let error_state = !(env.errors) in
1177 let h = hint env in
1178 match L.token env.file env.lb with
1179 | Trp ->
1181 | Tcomma ->
1182 if !(env.errors) != error_state
1183 then [h]
1184 else h :: hint_list_remain env
1185 | _ ->
1186 error_expect env ">"; [h]
1188 and hint_list_remain env =
1189 match L.token env.file env.lb with
1190 | Trp -> []
1191 | _ ->
1192 L.back env.lb;
1193 let error_state = !(env.errors) in
1194 let h = hint env in
1195 match L.token env.file env.lb with
1196 | Trp ->
1198 | Tcomma ->
1199 if !(env.errors) != error_state
1200 then [h]
1201 else h :: hint_list_remain env
1202 | _ ->
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
1212 expect env Tlp;
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
1220 | Trp ->
1221 ([], false)
1222 | Tellipsis ->
1223 hint_function_params_close env;
1224 ([], true)
1225 | _ ->
1226 L.back env.lb;
1227 hint_function_params_remain env
1229 (* ) | ,) *)
1230 and hint_function_params_close env =
1231 match L.token env.file env.lb with
1232 | Trp ->
1234 | Tcomma ->
1235 expect env Trp
1236 | _ ->
1237 error_expect env ")";
1240 (* _, parameter_list | _) | ...) | ...,) *)
1241 and hint_function_params_remain env =
1242 let error_state = !(env.errors) in
1243 let h = hint env in
1244 match L.token env.file env.lb with
1245 | Tcomma ->
1246 if !(env.errors) != error_state
1247 then ([h], false)
1248 else
1249 let hl, has_dots = hint_function_params env in
1250 (h :: hl, has_dots)
1251 | Trp ->
1252 ([h], false)
1253 | Tellipsis ->
1254 hint_function_params_close env;
1255 ([h], true)
1256 | _ ->
1257 error_expect env ")";
1258 ([h], false)
1260 and xhp_enum_decl_list env =
1261 match L.token env.file env.lb with
1262 | Trcb -> []
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
1269 | Trcb ->
1271 | Tcomma ->
1272 if !(env.errors) != error_state
1273 then [v]
1274 else v :: xhp_enum_decl_list env
1275 | _ ->
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
1281 match tok with
1282 | Tint ->
1283 let tok_value = Lexing.lexeme env.lb in
1284 pos, Int (pos, tok_value)
1285 | Tquote ->
1286 let absolute_pos = env.lb.Lexing.lex_curr_pos in
1287 expr_string env pos absolute_pos
1288 | Tdquote ->
1289 expr_encapsed env pos
1290 | _ ->
1291 error_expect env "integer literal or string literal";
1292 pos, Null
1294 (* : _ *)
1295 and hint_return env =
1296 expect env Tcolon;
1297 hint 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 (*****************************************************************************)
1308 (* { ... *)
1309 and class_body env =
1310 let error_state = !(env.errors) in
1311 expect env Tlcb;
1312 if error_state != !(env.errors)
1313 then L.look_for_open_cb env.lb;
1314 class_defs env
1316 and class_defs env =
1317 match L.token env.file env.lb with
1318 (* ... } *)
1319 | Trcb ->
1321 (* xhp_format | const | use *)
1322 | Tword ->
1323 let word = Lexing.lexeme env.lb in
1324 class_toplevel_word env word
1325 | Tltlt ->
1326 (* variable | method | type const*)
1327 L.back env.lb;
1328 let error_state = !(env.errors) in
1329 let m = class_member_def env in
1330 if !(env.errors) != error_state
1331 then [m]
1332 else m :: class_defs env
1333 | _ ->
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 =
1342 match word with
1343 | "children" ->
1344 xhp_format env;
1345 class_defs env
1346 | "category" ->
1347 let cat = XhpCategory (xhp_category_list env) in
1348 cat :: class_defs env
1349 | "const" ->
1350 let error_state = !(env.errors) in
1351 let def_start = Pos.make env.file env.lb in (* TODO *)
1352 let def =
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
1358 then [def]
1359 else def :: class_defs env
1360 | "use" ->
1361 let traitl = class_use_list env in
1362 traitl @ class_defs env
1363 | "require" ->
1364 let traitl = trait_require env in
1365 traitl @ class_defs env
1366 | "attribute" ->
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;
1372 m @ class_defs env
1373 | "abstract" ->
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
1380 | _ ->
1381 error_expect env "modifier";
1384 and on_class_member_word env =
1385 (* variable | method | type const*)
1386 L.back env.lb;
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;
1392 m :: class_defs env
1394 and look_for_next_method previous_pos env =
1395 match L.token env.file env.lb with
1396 | Teof -> ()
1397 | Trcb -> ()
1398 | Tword ->
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
1406 else
1407 (L.back env.lb; ())
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
1421 | Tsc ->
1422 [cst]
1423 | Tcomma ->
1424 if !(env.errors) != error_state
1425 then [cst]
1426 else cst :: class_use_list env
1427 | _ ->
1428 error_expect env ";"; [cst]
1430 and trait_require env =
1431 match L.token env.file env.lb with
1432 | Tword ->
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"; []
1438 ) in
1439 (match L.token env.file env.lb with
1440 | Tsc -> ret
1441 | _ -> error_expect env ";"; [])
1442 | _ -> error env "Expected: implements or extends"; []
1444 (*****************************************************************************)
1445 (* Class xhp_format *)
1447 * within a class body -->
1448 * children ...;
1450 (*****************************************************************************)
1452 and xhp_format env =
1453 match L.token env.file env.lb with
1454 | Tsc -> ()
1455 | Teof ->
1456 error_expect env "end of XHP children declaration";
1458 | Tquote ->
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);
1462 xhp_format env
1463 | Tdquote ->
1464 let pos = Pos.make env.file env.lb in
1465 ignore (expr_encapsed env pos);
1466 xhp_format env
1467 | x ->
1468 xhp_format env
1470 (*****************************************************************************)
1471 (* Class constants *)
1473 * within a class body -->
1474 * const ...;
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
1486 | None ->
1487 let h = class_const_hint env in
1488 let id = identifier env in
1489 expect env Tsc;
1490 Some (AbsConst (h, id))
1492 | _ -> None
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))
1500 | _ -> None
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
1507 Const (h, consts)
1509 (* const _ X = ...; *)
1510 and class_const_hint env =
1511 if class_const_has_hint env
1512 then Some (hint env)
1513 else None
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 = ... *)
1520 | Tword ->
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)
1524 | _ -> true
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
1531 | Tsc ->
1532 [cst]
1533 | Tcomma ->
1534 if !(env.errors) != error_state
1535 then [cst]
1536 else cst :: class_const_list_remain env
1537 | _ ->
1538 error_expect env ";"; [cst]
1540 and class_const_list_remain env =
1541 match L.token env.file env.lb with
1542 | Tsc -> []
1543 | _ ->
1544 L.back env.lb;
1545 let error_state = !(env.errors) in
1546 let cst = class_const env in
1547 match L.token env.file env.lb with
1548 | Tsc ->
1549 [cst]
1550 | Tcomma ->
1551 if !(env.errors) != error_state
1552 then [cst]
1553 else cst :: class_const_list_remain env
1554 | _ ->
1555 error_expect env ";"; [cst]
1557 (* const_name = const_value *)
1558 and class_const env =
1559 let id = identifier env in
1560 expect env Teq;
1561 let e = expr env in
1562 id, e
1564 (*****************************************************************************)
1565 (* Modifiers *)
1566 (*****************************************************************************)
1568 and mandatory_modifier_list env =
1569 match L.token env.file env.lb with
1570 | Tword ->
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
1576 | _ ->
1577 error_expect env "modifier"; []
1579 and optional_modifier_list env =
1580 match L.token env.file env.lb with
1581 | Tword ->
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
1587 | _ ->
1588 L.back env.lb; []
1590 and modifier_word env = function
1591 | "final" -> Some Final
1592 | "static" ->
1593 (* We need to look ahead to make sure we are not looking at a type access
1595 * class C {
1596 * public static $x; // a static var
1597 * public static::T $y; // an instance var with type static::T
1600 if peek env = Tcolcol
1601 then None
1602 else Some Static
1603 | "abstract" -> Some Abstract
1604 | "private" -> Some Private
1605 | "public" -> Some Public
1606 | "protected" -> Some Protected
1607 | _ -> None
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 $_ *)
1627 | Tlvar ->
1628 L.back env.lb;
1629 check_not_final env modifier_pos modifiers;
1630 let cvars = class_var_list env in
1631 ClassVars (modifiers, None, cvars)
1632 | Tword ->
1633 let word = Lexing.lexeme env.lb in
1634 class_member_word env member_start ~modifiers ~attrs word
1635 | _ ->
1636 L.back env.lb;
1637 check_visibility env modifier_pos modifiers;
1638 check_not_final env modifier_pos modifiers;
1639 let h = hint env in
1640 let cvars = class_var_list env in
1641 ClassVars (modifiers, Some h, cvars)
1643 (*****************************************************************************)
1644 (* Class variables *)
1646 * within a class body -->
1647 * modifier_list $x;
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
1656 then [cvar]
1657 else cvar :: class_var_list_remain env
1659 and class_var_list_remain env =
1660 match L.token env.file env.lb with
1661 | Tsc ->
1663 | Tcomma ->
1664 (match L.token env.file env.lb with
1665 | Tsc ->
1667 | _ ->
1668 L.back env.lb;
1669 let error_state = !(env.errors) in
1670 let var = class_var env in
1671 if !(env.errors) != error_state
1672 then [var]
1673 else var :: class_var_list_remain env
1675 | _ -> error_expect env ";"; []
1677 and class_var 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
1682 | Some (p, _) -> p
1683 | None -> pos
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)
1691 and xhp_attr env =
1692 let maybe_use, maybe_enum = (match L.token env.file env.lb with
1693 | Tcolon ->
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)
1697 | _ ->
1698 L.back env.lb; (None, None)) in
1699 match maybe_use with
1700 | Some x -> x
1701 | None ->
1702 begin
1703 let h = (match maybe_enum with
1704 | Some x -> None
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
1709 | Some (p, _) -> p
1710 | None -> pos_start
1712 let span = Pos.btw pos_start pos_end in
1713 let is_required = (match L.token env.file env.lb with
1714 | Trequired -> true
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
1723 then [a]
1724 else [a] @ xhp_attr_list_remain env
1726 and xhp_attr_list_remain env =
1727 match L.token env.file env.lb with
1728 | Tsc ->
1730 | Tcomma ->
1731 (match L.token env.file env.lb with
1732 | Tsc ->
1734 | _ ->
1735 L.back env.lb;
1736 xhp_attr_list env)
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
1749 | Tsc ->
1751 | Tcomma ->
1752 (match L.token env.file env.lb with
1753 | Tsc ->
1755 | _ ->
1756 L.back env.lb;
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
1764 then [a]
1765 else [a] @ xhp_category_list_remain env
1768 (*****************************************************************************)
1769 (* Methods *)
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
1778 | "async" ->
1779 expect_word env "function";
1780 let is_ref = ref_opt env in
1781 if is_ref
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
1786 Method method_
1787 | "function" ->
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
1792 Method method_
1793 | _ ->
1794 L.back env.lb;
1795 let h = hint env in
1796 let cvars =
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
1814 expect env Tsc;
1815 { tconst_abstract = is_abstract;
1816 tconst_name = pname;
1817 tconst_constraint = constr;
1818 tconst_type = type_;
1819 tconst_span = span;
1822 and method_ env method_start ~modifiers ~attrs ~(sync:fun_decl_kind)
1823 is_ref pname =
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.";
1834 { m_name = pname;
1835 m_tparams = tparams;
1836 m_params = params;
1837 m_ret = ret;
1838 m_constrs = constrs;
1839 m_ret_by_ref = is_ref;
1840 m_body = body_stmts;
1841 m_kind = modifiers;
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.";
1858 None
1859 | "__destruct", Some _ ->
1860 error_at env pos "Destructor return type must be void or elided.";
1861 None
1862 | _ -> ret
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 =
1873 match members with
1874 | [] -> []
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
1878 | x :: rl ->
1879 x :: method_implicit_fields rl
1881 and param_implicit_fields params =
1882 match params with
1883 | [] -> [], []
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
1888 | _ :: rl ->
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
1897 | None -> pos
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
1902 let stmt =
1903 Expr (pos, Binop (Eq None, (pos, Obj_get((pos, Lvar this),
1904 (pos, Id cvname),
1905 OG_nullthrows)),
1906 (pos, Lvar p.param_id)))
1908 member, stmt
1910 (*****************************************************************************)
1911 (* Function/Method bodies. *)
1912 (*****************************************************************************)
1914 and function_body env =
1915 match L.token env.file env.lb with
1916 | Tsc ->
1917 false, []
1918 | Tlcb ->
1919 let previous_in_generator = !(env.in_generator) in
1920 env.in_generator := false;
1921 let statements = (match env.mode with
1922 | FileInfo.Mdecl ->
1923 ignore_body env;
1924 (* This is a hack for the type-checker to make a distinction
1925 * Between function foo(); and function foo() {}
1927 [Noop]
1928 | _ ->
1929 (match statement_list env with
1930 | [] -> [Noop]
1931 | _ when env.quick -> [Noop]
1932 | x -> x)
1933 ) in
1934 let in_generator = !(env.in_generator) in
1935 env.in_generator := previous_in_generator ;
1936 in_generator, statements
1937 | _ ->
1938 error_expect env "{";
1939 false, []
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
1951 | Trcb -> ()
1952 | Tquote ->
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);
1956 ignore_body env
1957 | Tdquote ->
1958 let pos = Pos.make env.file env.lb in
1959 ignore (expr_encapsed env pos);
1960 ignore_body env
1961 | Theredoc ->
1962 ignore (expr_heredoc env);
1963 ignore_body env
1964 | Tword when (Lexing.lexeme env.lb) = "yield" ->
1965 env.in_generator := true;
1966 ignore_body env
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));
1972 ignore_body env
1973 | Tlp ->
1974 with_ignored_yield env
1975 (fun () -> ignore (try_short_lambda env));
1976 ignore_body env
1977 | Tlt when is_xhp env ->
1978 ignore (xhp env);
1979 ignore_body env
1980 | Teof -> error_expect env "}"; ()
1981 | Tunsafeexpr ->
1982 ignore (L.comment (Buffer.create 256) env.file env.lb);
1983 ignore_body env
1984 | _ -> ignore_body env
1986 and with_ignored_yield env fn =
1987 let previous_in_generator = !(env.in_generator) in
1988 let () = fn () in
1989 env.in_generator := previous_in_generator; ()
1991 (*****************************************************************************)
1992 (* Statements *)
1993 (*****************************************************************************)
1995 and statement_list env =
1996 match L.token env.file env.lb with
1997 | Trcb -> []
1998 | Tlcb ->
1999 let block = statement_list env in
2000 Block block :: statement_list env
2001 | Tsc ->
2002 statement_list env
2003 | Teof ->
2004 error_expect env "}";
2006 | _ ->
2007 L.back env.lb;
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
2014 and statement env =
2015 match L.token env.file env.lb with
2016 | Tword ->
2017 let word = Lexing.lexeme env.lb in
2018 let stmt = statement_word env word in
2019 stmt
2020 | Tlcb ->
2021 Block (statement_list env)
2022 | Tsc ->
2023 Noop
2024 | Tunsafe ->
2025 Unsafe
2026 | Tfallthrough ->
2027 Fallthrough
2028 | _ ->
2029 L.back env.lb;
2030 let e = expr env in
2031 expect env Tsc;
2032 Expr e
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:
2039 <?hh // decl
2040 foo(]);
2041 As valid.
2043 ignore (statement env);
2044 env.errors := error_state
2046 and parse_expr env =
2047 L.back env.lb;
2048 let e = expr env in
2049 expect env Tsc;
2050 Expr e
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" ->
2070 error env
2071 "Parse error: declarations are not supported outside global scope";
2072 ignore (ignore_toplevel None ~attr:[] [] env (fun _ -> true));
2073 Noop
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
2092 | Some expr -> expr
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
2103 check_continue env;
2104 stmt
2106 (*****************************************************************************)
2107 (* Continue statement *)
2108 (*****************************************************************************)
2110 and statement_continue env =
2111 let stmt = Continue (Pos.make env.file env.lb, None) in
2112 check_continue env;
2113 stmt
2115 and check_continue env =
2116 match L.token env.file env.lb with
2117 | Tsc -> ()
2118 | Tint -> error_continue env
2119 | _ -> error_expect env ";"
2121 (*****************************************************************************)
2122 (* Throw statement *)
2123 (*****************************************************************************)
2125 and statement_throw env =
2126 let e = expr env in
2127 expect env Tsc;
2128 Throw e
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
2137 Return (pos, value)
2139 and return_value env =
2140 match L.token env.file env.lb with
2141 | Tsc -> None
2142 | _ ->
2143 L.back env.lb;
2144 let e = expr env in
2145 expect env Tsc;
2146 Some e
2148 (*****************************************************************************)
2149 (* Goto statement *)
2150 (*****************************************************************************)
2152 and statement_goto_label env label =
2153 let pos = Pos.make env.file env.lb in
2154 let goto_allowed =
2155 TypecheckerOptions.experimental_feature_enabled
2156 env.popt
2157 TypecheckerOptions.experimental_goto in
2158 if not goto_allowed then error env "goto is not supported.";
2159 expect env Tcolon;
2160 GotoLabel (pos, label)
2162 and statement_goto env =
2163 let pos = Pos.make env.file env.lb in
2164 let goto_allowed =
2165 TypecheckerOptions.experimental_feature_enabled
2166 env.popt
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
2170 | Tword ->
2171 let word = Lexing.lexeme env.lb in
2172 expect env Tsc;
2173 Goto (pos, word)
2174 | _ ->
2175 error env "goto must use a label.";
2176 Noop
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
2185 | Tlvar ->
2186 L.back env.lb;
2187 let el = static_var_list env in
2188 Static_var el
2189 | _ ->
2190 L.back env.lb;
2191 let id = pos, Id (pos, "static") in
2192 let e = expr_remain env id in
2193 Expr e
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
2199 | Tsc ->
2200 [cst]
2201 | Tcomma ->
2202 if !(env.errors) != error_state
2203 then [cst]
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
2209 | Tsc -> []
2210 | _ ->
2211 L.back env.lb;
2212 let error_state = !(env.errors) in
2213 let cst = static_var env in
2214 match L.token env.file env.lb with
2215 | Tsc ->
2216 [cst]
2217 | Tcomma ->
2218 if !(env.errors) != error_state
2219 then [cst]
2220 else cst :: static_var_list_remain env
2221 | _ ->
2222 error_expect env ";"; [cst]
2224 and static_var env =
2225 expr env
2227 (*****************************************************************************)
2228 (* Switch statement *)
2229 (*****************************************************************************)
2231 and statement_switch env =
2232 let e = paren_expr env in
2233 expect env Tlcb;
2234 let casel = switch_body env in
2235 Switch (e, casel)
2237 (* switch(...) { _ } *)
2238 and switch_body env =
2239 match L.token env.file env.lb with
2240 | Trcb ->
2242 | Tword ->
2243 let word = Lexing.lexeme env.lb in
2244 switch_body_word env word
2245 | _ ->
2246 error_expect env "}";
2249 and switch_body_word env = function
2250 | "case" ->
2251 let e = expr env in
2252 expect env Tcolon;
2253 let stl = case_body env in
2254 Case (e, stl) :: switch_body env
2255 | "default" ->
2256 expect env Tcolon;
2257 let stl = case_body env in
2258 Default stl :: switch_body env
2259 | _ -> error_expect env "case"; []
2261 (* switch(...) { case/default: _ } *)
2262 and case_body env =
2263 match L.token env.file env.lb with
2264 | Tword ->
2265 (match Lexing.lexeme env.lb with
2266 | "case" | "default" -> L.back env.lb; []
2267 | _ ->
2268 L.back env.lb;
2269 let error_state = !(env.errors) in
2270 let st = statement env in
2271 if !(env.errors) != error_state
2272 then [st]
2273 else st :: case_body env
2275 | Trcb ->
2276 L.back env.lb; []
2277 | _ ->
2278 L.back env.lb;
2279 let error_state = !(env.errors) in
2280 let st = statement env in
2281 if !(env.errors) != error_state
2282 then [st]
2283 else st :: case_body env
2285 (*****************************************************************************)
2286 (* If statement *)
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
2297 | Tword ->
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
2313 expect env Tsc;
2314 Do ([st], e)
2316 and statement_while env =
2317 let e = paren_expr env in
2318 let st = statement env in
2319 While (e, [st])
2321 (*****************************************************************************)
2322 (* For statement *)
2323 (*****************************************************************************)
2325 and statement_for env =
2326 expect env Tlp;
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
2332 let start = last in
2333 let last, el = for_expr env in
2334 let e2 = Pos.btw start last, Expr_list el in
2335 let start = last 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])
2341 and for_expr env =
2342 match L.token env.file env.lb with
2343 | Tsc ->
2344 Pos.make env.file env.lb, []
2345 | _ ->
2346 L.back env.lb;
2347 let error_state = !(env.errors) in
2348 let e = expr env in
2349 match L.token env.file env.lb with
2350 | Tsc ->
2351 Pos.make env.file env.lb, [e]
2352 | _ when !(env.errors) != error_state ->
2353 L.back env.lb;
2354 Pos.make env.file env.lb, [e]
2355 | Tcomma ->
2356 let last, el = for_expr env in
2357 last, e :: el
2358 | _ ->
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
2364 | Trp ->
2365 Pos.make env.file env.lb, []
2366 | _ ->
2367 L.back env.lb;
2368 let error_state = !(env.errors) in
2369 let e = expr env in
2370 match L.token env.file env.lb with
2371 | Trp ->
2372 Pos.make env.file env.lb, [e]
2373 | _ when !(env.errors) != error_state ->
2374 L.back env.lb;
2375 Pos.make env.file env.lb, [e]
2376 | Tcomma ->
2377 let last, el = for_last_expr env in
2378 last, e :: el
2379 | _ ->
2380 error_expect env ")";
2381 Pos.make env.file env.lb, [e]
2383 (*****************************************************************************)
2384 (* Foreach statement *)
2385 (*****************************************************************************)
2387 and statement_foreach env =
2388 expect env Tlp;
2389 let e = expr env in
2390 let await =
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
2402 | Tsarrow ->
2403 let e2 = expr env in
2404 check_foreach_lvalue env e2;
2405 expect env Trp;
2406 As_kv (e1, e2)
2407 | Trp ->
2408 check_foreach_lvalue env e1;
2409 As_v e1
2410 | _ ->
2411 error_expect env ")";
2412 As_v e1
2414 (*****************************************************************************)
2415 (* Try statement *)
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 *)
2423 match cl, fin with
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" ->
2430 expect env Tlp;
2431 let name = identifier env in
2432 let e = variable env in
2433 expect env Trp;
2434 let st = statement env in
2435 (name, e, [st]) :: catch_list env
2436 | _ -> L.back env.lb; []
2438 and finally env =
2439 match L.token env.file env.lb with
2440 | Tword when Lexing.lexeme env.lb = "finally" ->
2441 let st = statement env in
2442 [st]
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, []))
2455 and echo_args env =
2456 let e = expr env in
2457 match L.token env.file env.lb with
2458 | Tsc ->
2460 | Tcomma ->
2461 e :: echo_args env
2462 | _ ->
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
2483 Note that:
2484 * A variadic parameter is never followed by a comma
2485 * A variadic parameter with a type must also have a variable.
2488 expect env Tlp;
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
2495 | Trp -> []
2496 | Tellipsis ->
2497 [parameter_varargs env]
2498 | _ ->
2499 L.back env.lb;
2500 let error_state = !(env.errors) in
2501 let p = param env in
2502 match L.token env.file env.lb with
2503 | Trp ->
2505 | Tcomma ->
2506 if !(env.errors) != error_state
2507 then [p]
2508 else p :: parameter_list_remain env
2509 | _ ->
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."
2516 end;
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, "...");
2526 | _ ->
2527 L.back env.lb;
2528 let param_id = variable env in
2529 let _ = parameter_default_with_variadic true env in
2530 expect env Trp;
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;
2538 param_id;
2539 param_expr = None;
2540 param_modifier = None;
2541 param_user_attributes = [];
2544 and param env =
2545 (* We have a parameter that does not start with ... so it is of one of
2546 these two forms:
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."
2558 end;
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
2562 expect env Trp;
2563 L.back env.lb
2564 end;
2565 { param_hint;
2566 param_is_reference;
2567 param_is_variadic;
2568 param_id;
2569 param_expr;
2570 param_modifier;
2571 param_user_attributes;
2574 and parameter_modifier env =
2575 match L.token env.file env.lb with
2576 | Tword ->
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)
2588 else None
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
2594 | _ -> true
2597 and parameter_default env =
2598 match L.token env.file env.lb with
2599 | Teq ->
2600 let default = expr env in
2601 Some default
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
2618 | None -> []
2619 | Some c ->
2620 let t2 = hint env in
2621 let constr = (t1, c, t2) in
2622 match L.token env.file env.lb with
2623 | Tcomma ->
2624 if !(env.errors) != error_state
2625 then [constr]
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 (*****************************************************************************)
2638 (* Expressions *)
2639 (*****************************************************************************)
2641 and expr env =
2642 let e1 = expr_atomic ~allow_class:false ~class_const:false env in
2643 let e2 = expr_remain env e1 in
2646 and expr_list env =
2647 expect env Tlp;
2648 expr_list_remain env
2650 and expr_list_remain env =
2651 match L.token env.file env.lb with
2652 | Trp -> []
2653 | _ ->
2654 L.back env.lb;
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
2658 | Trp ->
2660 | Tcomma ->
2661 if !(env.errors) != error_state
2662 then [e]
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
2668 | Tplus ->
2669 expr_binop env Tplus Plus e1
2670 | Tminus ->
2671 expr_binop env Tminus Minus e1
2672 | Tstar ->
2673 expr_binop env Tstar Star e1
2674 | Tstarstar ->
2675 expr_binop env Tstarstar Starstar e1
2676 | Tslash ->
2677 expr_binop env Tslash Slash e1
2678 | Teq ->
2679 expr_assign env Teq (Eq None) e1
2680 | Tbareq ->
2681 expr_assign env Tbareq (Eq (Some Bar)) e1
2682 | Tpluseq ->
2683 expr_assign env Tpluseq (Eq (Some Plus)) e1
2684 | Tstarstareq ->
2685 expr_assign env Tstarstareq (Eq (Some Starstar)) e1
2686 | Tstareq ->
2687 expr_assign env Tstareq (Eq (Some Star)) e1
2688 | Tslasheq ->
2689 expr_assign env Tslasheq (Eq (Some Slash)) e1
2690 | Tdoteq ->
2691 expr_assign env Tdoteq (Eq (Some Dot)) e1
2692 | Tminuseq ->
2693 expr_assign env Tminuseq (Eq (Some Minus)) e1
2694 | Tpercenteq ->
2695 expr_assign env Tpercenteq (Eq (Some Percent)) e1
2696 | Txoreq ->
2697 expr_assign env Txoreq (Eq (Some Xor)) e1
2698 | Tampeq ->
2699 expr_assign env Tampeq (Eq (Some Amp)) e1
2700 | Tlshifteq ->
2701 expr_assign env Tlshifteq (Eq (Some Ltlt)) e1
2702 | Trshifteq ->
2703 expr_assign env Trshifteq (Eq (Some Gtgt)) e1
2704 | Teqeqeq ->
2705 expr_binop env Teqeqeq EQeqeq e1
2706 | Tgt ->
2707 expr_binop env Tgt Gt e1
2708 | Tpercent ->
2709 expr_binop env Tpercent Percent e1
2710 | Tdot ->
2711 expr_binop env Tdot Dot e1
2712 | Teqeq ->
2713 expr_binop env Teqeq Eqeq e1
2714 | Tampamp ->
2715 expr_binop env Tampamp AMpamp e1
2716 | Tbarbar ->
2717 expr_binop env Tbarbar BArbar e1
2718 | Tdiff ->
2719 expr_binop env Tdiff Diff e1
2720 | Tlt ->
2721 expr_binop env Tlt Lt e1
2722 | Tdiff2 ->
2723 expr_binop env Tdiff2 Diff2 e1
2724 | Tgte ->
2725 expr_binop env Tgte Gte e1
2726 | Tlte ->
2727 expr_binop env Tlte Lte e1
2728 | Tcmp ->
2729 expr_binop env Tcmp Cmp e1
2730 | Tamp ->
2731 expr_binop env Tamp Amp e1
2732 | Tbar ->
2733 expr_binop env Tbar Bar e1
2734 | Tltlt ->
2735 expr_binop env Tltlt Ltlt e1
2736 | Tgtgt ->
2737 expr_binop env Tgtgt Gtgt e1
2738 | Txor ->
2739 expr_binop env Txor Xor e1
2740 | Tpipe ->
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
2746 | Tcolcol ->
2747 expr_colcol env e1
2748 | Tlp ->
2749 expr_call env e1
2750 | Tlb ->
2751 expr_array_get env e1
2752 | Tlcb ->
2753 error env "Do not use { to subscript, use [";
2754 expr_array_get env e1
2755 | Tqm ->
2756 expr_if env e1
2757 | Tqmqm ->
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
2773 | _ ->
2774 L.back env.lb; 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
2789 then
2790 match assoc with
2791 | Left ->
2792 let e = make e1 { env with priority = env.priority + 1 } in
2793 expr_remain env e, true
2794 | Right ->
2795 let e = make e1 env in
2796 e, false
2797 | NonAssoc ->
2798 error env "This operator is not associative, add parentheses";
2799 let e = make e1 env in
2800 e, false
2801 else if prio < current_prio
2802 then begin
2803 L.back env.lb;
2804 e1, false
2806 else begin
2807 assert (prio > current_prio);
2808 if assoc = NonAssoc
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
2821 let () =
2822 if tok <> Tword || value <> "async"
2823 then L.back env.lb
2824 else
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 =
2836 (if peek env = Tlcb
2837 (** e.g.
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
2844 (** e.g.
2845 * ==> x + 5
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
2856 let f = {
2857 f_name = (Pos.none, ";anonymous");
2858 f_tparams = [];
2859 f_constrs = [];
2860 f_params = params;
2861 f_ret = ret;
2862 f_ret_by_ref = false;
2863 f_body = body_stmts;
2864 f_user_attributes = [];
2865 f_fun_kind;
2866 f_mode = env.mode;
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;
2870 f_static = false;
2872 in Lfun f
2874 and make_lambda_param : id -> fun_param = fun var_id ->
2876 param_hint = None;
2877 param_is_reference = false;
2878 param_is_variadic = false;
2879 param_id = var_id;
2880 param_expr = None;
2881 param_modifier = None;
2882 param_user_attributes = [];
2885 and lambda_single_arg ~(sync:fun_decl_kind) env var_id : expr_ =
2886 expect env Tlambda;
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;
2895 None
2896 end else begin
2897 let ret = hint_return_opt env in
2898 if !(env.errors) != error_state then begin
2899 env.errors := error_state;
2900 None
2901 end else if not (peek env = Tlambda)
2902 then None
2903 else begin
2904 drop env;
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
2913 | Tcolon ->
2914 (match L.xhpname env.file env.lb with
2915 | Txhpname ->
2916 let name = (Pos.make env.file env.lb, ":"^Lexing.lexeme env.lb) in
2917 (match L.token env.file env.lb with
2918 | Tcomma | Tsc ->
2919 L.back env.lb;
2920 Some (XhpAttrUse (class_hint_with_name env name))
2921 | _ ->
2922 L.back env.lb;
2923 None)
2924 | _ -> None)
2925 | _ -> None
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
2933 expect env Tlcb;
2934 let items = xhp_enum_decl_list env in
2935 Some (pos, items)
2936 | _ -> None
2939 (*****************************************************************************)
2940 (* Expressions *)
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
2946 match tok with
2947 | Tint ->
2948 let tok_value = Lexing.lexeme env.lb in
2949 pos, Int (pos, tok_value)
2950 | Tfloat ->
2951 let tok_value = Lexing.lexeme env.lb in
2952 pos, Float (pos, tok_value)
2953 | Tquote ->
2954 let absolute_pos = env.lb.Lexing.lex_curr_pos in
2955 expr_string env pos absolute_pos
2956 | Tdquote ->
2957 expr_encapsed env pos
2958 | Tlvar ->
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
2964 Lvar (pos, var_id)
2965 else if env.mode = FileInfo.Mdecl then
2966 Lvarvar (dollars, (pos, var_id))
2967 else begin
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))
2973 | Tcolon ->
2974 L.back env.lb;
2975 let name = identifier env in
2976 fst name, Id name
2977 | Tem | Tincr | Tdecr | Ttild | Tplus | Tminus | Tamp as op ->
2978 expr_prefix_unary env pos op
2979 | Tat ->
2980 with_priority env Tat expr
2981 | Tword ->
2982 let word = Lexing.lexeme env.lb in
2983 expr_atomic_word ~allow_class ~class_const env pos word
2984 | Tlp ->
2985 (match try_short_lambda env with
2986 | None ->
2987 if is_cast env
2988 then expr_cast env pos
2989 else with_base_priority env begin fun env ->
2990 let e = expr env in
2991 expect env Trp;
2992 let end_ = Pos.make env.file env.lb in
2993 Pos.btw pos end_, snd e
2995 | Some l -> pos, l
2997 | Tlb ->
2998 expr_short_array env pos
2999 | Tlt when is_xhp env ->
3000 xhp env
3001 | Theredoc ->
3002 expr_heredoc env
3003 | Tdollar ->
3004 error env ("A valid variable name starts with a letter or underscore,"^
3005 "followed by any number of letters, numbers, or underscores");
3006 expr env
3007 | Tdollardollar ->
3008 pos, Lvar (pos, "$$")
3009 | Tunsafeexpr ->
3010 (* Consume the rest of the comment. *)
3011 ignore (L.comment (Buffer.create 256) env.file env.lb);
3012 let e = expr env in
3013 let end_ = Pos.make env.file env.lb in
3014 Pos.btw pos end_, Unsafeexpr e
3015 | _ ->
3016 error_expect env "expression";
3017 pos, Null
3019 and expr_atomic_word ~allow_class ~class_const env pos = function
3020 | "class" when not allow_class ->
3021 error_expect env "expression";
3022 pos, Null
3023 | "final" | "abstract" | "interface" | "trait" ->
3024 error_expect env "expression";
3025 pos, Null
3026 | "true" when not class_const ->
3027 pos, True
3028 | "false" when not class_const ->
3029 pos, False
3030 | "null" when not class_const ->
3031 pos, Null
3032 | "array" ->
3033 expr_array env pos
3034 | "darray" when peek env = Tlb ->
3035 expr_darray env pos
3036 | "varray" when peek env = Tlb ->
3037 expr_varray env pos
3038 | "shape" ->
3039 expr_shape env pos
3040 | "new" ->
3041 expr_new env pos
3042 | "async" ->
3043 expr_anon_async env pos
3044 | "function" ->
3045 expr_anon_fun env pos ~sync:FDeclSync
3046 | name when is_collection env name ->
3047 expr_collection env pos name
3048 | "await" ->
3049 expr_await env pos
3050 | "yield" ->
3051 env.in_generator := true;
3052 expr_yield env pos
3053 | "clone" ->
3054 expr_clone env pos
3055 | "list" ->
3056 expr_php_list env pos
3057 | r when is_import r ->
3058 if env.mode = FileInfo.Mstrict
3059 then
3060 error env
3061 ("Parse error: "^r^" is supported only as a toplevel "^
3062 "declaration");
3063 expr_import r env pos
3064 | x when not class_const && String.lowercase x = "true" ->
3065 Lint.lowercase_constant pos x;
3066 pos, True
3067 | x when not class_const && String.lowercase x = "false" ->
3068 Lint.lowercase_constant pos x;
3069 pos, False
3070 | x when not class_const && String.lowercase x = "null" ->
3071 Lint.lowercase_constant pos x;
3072 pos, Null
3073 | x when String.lowercase x = "array" ->
3074 expr_array env pos
3075 | x ->
3076 pos, Id (pos, x)
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))
3082 else
3083 dollars, token
3085 (*****************************************************************************)
3086 (* Expressions in parens. *)
3087 (*****************************************************************************)
3089 and paren_expr env =
3090 with_base_priority env begin fun env ->
3091 expect env Tlp;
3092 let e = expr env in
3093 expect env Trp;
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
3126 pos, Pipe (e1, e2)
3129 (*****************************************************************************)
3130 (* Object Access ($obj->method) *)
3131 (*****************************************************************************)
3133 and expr_arrow env e1 tok =
3134 reduce env e1 tok begin fun e1 env ->
3135 let e2 =
3136 let saved = save_lexbuf_state env.lb in
3137 match L.varname env.lb with
3138 | Tword ->
3139 let name = Lexing.lexeme env.lb in
3140 let pos = Pos.make env.file env.lb in
3141 pos, Id (pos, name)
3142 | _ ->
3143 restore_lexbuf_state env.lb saved;
3144 expr env
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 ->
3158 (match e1 with
3159 | _, Id cname ->
3160 (* XYZ::class is OK ... *)
3161 expr_colcol_remain ~allow_class:true env e1 cname
3162 | _, Lvar cname ->
3163 (* ... but get_class($x) should be used instead of $x::class ... *)
3164 expr_colcol_remain ~allow_class:false env e1 cname
3165 | pos, _ ->
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)
3175 | _, Id x ->
3176 btw e1 x, Class_const (cname, x)
3177 | pos, _ ->
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 ->
3187 L.back env.lb;
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:
3195 * f(...$unpacked);
3197 and expr_call_list env =
3198 expect env Tlp;
3199 expr_call_list_remain env
3201 and expr_call_list_remain env =
3202 match L.token env.file env.lb with
3203 | Trp -> [], []
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
3209 | Tcomma ->
3210 expect env Trp;
3211 [], [unpack_e]
3212 | Trp -> [], [unpack_e]
3213 | _ -> error_expect env ")"; [], [unpack_e])
3214 | _ ->
3215 L.back env.lb;
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
3220 | Trp -> [e], []
3221 | Tcomma ->
3222 if !(env.errors) != error_state
3223 then [e], []
3224 else begin
3225 let reg, unpack = expr_call_list_remain env
3226 in e :: reg, unpack
3228 | _ -> error_expect env ")"; [e], []
3230 and check_call_time_reference = function
3231 | p, Unop (Uref, _) -> Errors.call_time_pass_by_reference p
3232 | _ -> ()
3234 (*****************************************************************************)
3235 (* Collections *)
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)
3247 | _ -> (Tlcb, Trcb)
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 -> []
3260 | _ ->
3261 L.back env.lb;
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 ->
3266 [fd]
3267 | Tcomma ->
3268 if !(env.errors) != error_state
3269 then [fd]
3270 else fd :: collection_field_list_remain env end_sentinel
3271 | _ ->
3272 error_expect env (L.token_to_string end_sentinel); []
3274 (*****************************************************************************)
3275 (* Imports - require/include/require_once/include_once *)
3276 (*****************************************************************************)
3278 and is_import r =
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 ->
3291 let e = expr env in
3292 Pos.btw start (fst e), Import (flavor, e)
3295 (*****************************************************************************)
3296 (* InstanceOf *)
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 (*****************************************************************************)
3306 (* Yield/Await *)
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
3315 | _ ->
3316 L.back env.lb;
3317 let af = array_field env in
3318 start, Yield af
3321 and expr_await env start =
3322 with_priority env Tawait begin fun env ->
3323 let e = expr env in
3324 Pos.btw start (fst e), Await e
3327 (*****************************************************************************)
3328 (* Clone *)
3329 (*****************************************************************************)
3331 and expr_clone env start =
3332 with_base_priority env begin fun env ->
3333 let e = expr env in
3334 Pos.btw start (fst e), Clone e
3337 (*****************************************************************************)
3338 (* List *)
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
3354 | Tlvar ->
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)
3357 | Tlp ->
3358 let param_list = parameter_list_remain env in
3359 let ret = hint_return_opt env in
3360 expect env Tlambda;
3361 pos, lambda_body ~sync:FDeclAsync env param_list ret
3362 | Tlcb -> (* async { ... } *)
3363 L.back env.lb;
3364 let lambda = pos, lambda_body ~sync:FDeclAsync env [] None in
3365 pos, Call (lambda, [], [])
3366 | _ ->
3367 L.back env.lb;
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
3376 let f = {
3377 f_name = (Pos.none, ";anonymous");
3378 f_tparams = [];
3379 f_constrs = [];
3380 f_params = params;
3381 f_ret = ret;
3382 f_ret_by_ref = false;
3383 f_body = body_stmts;
3384 f_user_attributes = [];
3385 f_fun_kind = fun_kind sync is_generator;
3386 f_mode = env.mode;
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;
3390 f_static = false;
3393 pos, Efun (f, use)
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" ->
3402 expect env Tlp;
3403 use_list env
3404 | _ -> L.back env.lb; []
3406 and use_list env =
3407 match L.token env.file env.lb with
3408 | Trp -> []
3409 | _ ->
3410 L.back env.lb;
3411 let error_state = !(env.errors) in
3412 let var = ref_variable env in
3413 match L.token env.file env.lb with
3414 | Tcomma ->
3415 if !(env.errors) != error_state
3416 then [var]
3417 else var :: use_list env
3418 | Trp ->
3419 [var]
3420 | _ ->
3421 error_expect env ")";
3422 [var]
3424 (*****************************************************************************)
3425 (* New: new ClassName(...) *)
3426 (*****************************************************************************)
3428 and expr_new env pos_start =
3429 with_priority env Tnew begin fun env ->
3430 let cname =
3431 let e = expr env in
3432 match e with
3433 | p, Id id ->
3434 let typeargs = class_hint_params env in
3435 if typeargs == []
3436 then e
3437 else (p, Id_type_arguments (id, typeargs))
3438 | _, Lvar _
3439 | _, Array_get _
3440 | _, Obj_get _
3441 | _, Class_get _
3442 | _, Call _ ->
3444 | p, _ ->
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
3460 | _ -> false
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. *)
3466 and is_cast env =
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
3477 | _ -> true
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
3487 expect env Trp;
3488 let ty = p, Happly ((p, cast_type), []) in
3489 let e = expr env 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
3499 | Tamp -> Tref
3500 | x -> x
3502 and expr_prefix_unary env start op =
3503 with_priority env (unary_priority op) begin fun env ->
3504 let e = expr env in
3505 let op =
3506 match op with
3507 | Tem -> Unot
3508 | Tincr -> (check_lvalue env e; Uincr)
3509 | Tdecr -> (check_lvalue env e; Udecr)
3510 | Ttild -> Utild
3511 | Tplus -> Uplus
3512 | Tminus -> Uminus
3513 | Tamp -> Uref
3514 | _ -> assert false
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
3521 let op =
3522 check_lvalue env e1;
3523 match uop with
3524 | Tincr -> Upincr
3525 | Tdecr -> Updecr
3526 | _ -> assert false
3528 let e = Pos.btw (fst e1) end_, Unop (op, e1) in
3529 expr_remain env e
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
3538 tok = Tcolon &&
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 ->
3545 if is_colon_if 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
3552 expect env Tcolon;
3553 let e3 = expr env in
3554 (match e1 with
3555 | pos, Eif _ ->
3556 error_at env pos "You should add parentheses"
3557 | _ -> ());
3558 Pos.btw (fst e1) (fst e3), Eif (e1, Some e2, e3)
3560 and colon_if env e1 =
3561 expect env Tcolon;
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 (*****************************************************************************)
3576 (* Strings *)
3577 (*****************************************************************************)
3579 and make_string env pos content f_unescape =
3580 let unescaped =
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
3587 | Tquote ->
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
3592 | Teof ->
3593 error_at env start "string not closed";
3594 start, String (start, "")
3595 | _ -> assert false
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 *)
3603 match el with
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
3616 ) in
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
3637 | Tdquote ->
3638 get_text ()
3639 | Teof ->
3640 error_at env start "string not properly closed";
3642 | Tlcb when env.mode = FileInfo.Mdecl ->
3643 encapsed_nested start env
3644 | Tlcb ->
3645 let saved = save_lexbuf_state env.lb in
3646 (match L.string2 env.file env.lb with
3647 | Tdollar ->
3648 error env "{ not supported";
3649 restore_lexbuf_state env.lb saved;
3650 encapsed_nested start env
3651 | Tlvar ->
3652 restore_lexbuf_state env.lb saved;
3653 let error_state = !(env.errors) in
3654 let e = expr env in
3655 (match L.string2 env.file env.lb with
3656 | Trcb -> ()
3657 | _ -> error_expect env "}");
3658 if !(env.errors) != error_state
3659 then [e]
3660 else get_text () @ e :: encapsed_nested start env
3661 | _ ->
3662 restore_lexbuf_state env.lb saved;
3663 encapsed_nested_inner start frag env
3665 | Tdollar ->
3666 let saved = save_lexbuf_state env.lb in
3667 (match L.string2 env.file env.lb with
3668 | Tlcb ->
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
3673 | Tword ->
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
3680 | _ ->
3681 error_expect env "variable";
3682 Pos.make env.file env.lb, Null) in
3683 expect env Trcb;
3684 if !(env.errors) != error_state
3685 then [result]
3686 else get_text () @ result :: encapsed_nested start env
3687 | _ ->
3688 restore_lexbuf_state env.lb saved;
3689 encapsed_nested_inner start frag env
3691 | Tlvar ->
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
3696 then [e]
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
3704 | Tquote ->
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
3708 | Tint ->
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)
3712 | Tword ->
3713 let pid = Pos.make env.file env.lb in
3714 let id = Lexing.lexeme env.lb in
3715 pid, (Id (pid, id))
3716 | Tlvar ->
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
3720 | _ ->
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
3726 if continue
3727 then encapsed_expr_reduce start env e1
3728 else 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
3733 | Tlb ->
3734 let e2 =
3735 match L.string2 env.file env.lb with
3736 | Tword ->
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))
3744 | _ ->
3745 L.back env.lb;
3746 expr { env with priority = 0 }
3748 (match L.string2 env.file env.lb with
3749 | Trb -> ()
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
3754 | Tarrow ->
3755 (match L.string2 env.file env.lb with
3756 | Tword ->
3757 L.back env.lb;
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
3761 | _ ->
3762 L.back env.lb;
3763 e1, false
3765 | _ ->
3766 restore_lexbuf_state env.lb saved;
3767 e1, false
3769 (*****************************************************************************)
3770 (* Heredocs *)
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
3783 | Tword ->
3784 Pos.make env.file env.lb, Lexing.lexeme env.lb
3785 | Tquote ->
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
3789 | _, String x -> x
3790 | _ -> assert false)
3791 | _ ->
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
3797 | Tnewline ->
3798 heredoc_end tag env
3799 | Teof ->
3800 error_expect env tag_value
3801 | _ ->
3802 heredoc_body tag env
3804 and heredoc_end (pos, tag_value as tag) env =
3805 match L.heredoc_token env.lb with
3806 | Tword ->
3807 let tag2 = Lexing.lexeme env.lb in
3808 (match L.heredoc_token env.lb with
3809 | Tnewline when tag2 = tag_value ->
3811 | Tnewline ->
3812 heredoc_end tag env
3813 | Tsc when tag2 = tag_value ->
3814 L.back env.lb;
3816 | _ ->
3817 heredoc_body tag env
3819 | Tnewline ->
3820 heredoc_end tag env
3821 | _ ->
3822 heredoc_body tag env
3825 (*****************************************************************************)
3826 (* Arrays *)
3827 (*****************************************************************************)
3829 (* Ideally, we would factor out the common logic in the following functions to
3830 take advantage of a function that looks like:
3832 expr_array_generic
3833 (env : env)
3834 (pos : Pos.t)
3835 (extract_field_function : env -> 'a)
3836 : 'a list
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
3846 pos, Array fields
3848 and array_field_list env =
3849 expect env Tlp;
3850 array_field_list_remain env Trp []
3852 and expr_short_array env pos =
3853 let fields = array_field_list_remain env Trb [] in
3854 pos, Array fields
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
3859 | _ ->
3860 L.back env.lb;
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 ->
3866 List.rev acc
3867 | Tcomma ->
3868 if !(env.errors) != error_state
3869 then List.rev acc
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
3877 | Tsarrow ->
3878 let e2 = expr env in
3879 AFkvalue (e1, e2)
3880 | _ ->
3881 L.back env.lb;
3882 AFvalue e1
3884 and expr_darray env pos =
3885 let darray_and_varray_allowed =
3886 TypecheckerOptions.experimental_feature_enabled
3887 env.popt
3888 TypecheckerOptions.experimental_darray_and_varray in
3889 if not darray_and_varray_allowed then Errors.darray_not_supported pos;
3890 expect env Tlb;
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
3896 | _ ->
3897 L.back env.lb;
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
3902 | x when x = Trb ->
3903 List.rev acc
3904 | Tcomma ->
3905 if !(env.errors) != error_state
3906 then List.rev acc
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
3913 expect env Tsarrow;
3914 let e2 = expr env in
3915 e1, e2
3917 and expr_varray env pos =
3918 let darray_and_varray_allowed =
3919 TypecheckerOptions.experimental_feature_enabled
3920 env.popt
3921 TypecheckerOptions.experimental_darray_and_varray in
3922 if not darray_and_varray_allowed then Errors.varray_not_supported pos;
3923 expect env Tlb;
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
3929 | _ ->
3930 L.back env.lb;
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
3935 | x when x = Trb ->
3936 List.rev acc
3937 | Tcomma ->
3938 if !(env.errors) != error_state
3939 then List.rev acc
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
3945 expr env
3947 (*****************************************************************************)
3948 (* Shapes *)
3949 (*****************************************************************************)
3951 and expr_shape env pos =
3952 let fields = shape_field_list env in
3953 pos, Shape fields
3955 and shape_field_list env =
3956 expect env Tlp;
3957 shape_field_list_remain env
3959 and shape_field_list_remain env =
3960 match L.token env.file env.lb with
3961 | Trp -> []
3962 | _ ->
3963 L.back env.lb;
3964 let error_state = !(env.errors) in
3965 let fd = shape_field env in
3966 match L.token env.file env.lb with
3967 | Trp ->
3968 [fd]
3969 | Tcomma ->
3970 if !(env.errors) != error_state
3971 then [fd]
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.";
3978 L.back env.lb;
3980 let name = shape_field_name env in
3981 expect env Tsarrow;
3982 let value = expr { env with priority = 0 } in
3983 name, value
3985 and shape_field_name env =
3986 let pos, e = expr env in
3987 match e with
3988 | String p -> SFlit p
3989 | Class_const (id, ps) -> SFclass_const (id, ps)
3990 | _ -> error_expect env "string literal or class constant";
3991 SFlit (pos, "")
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
4001 | Trb ->
4002 let end_ = Pos.make env.file env.lb in
4003 Pos.btw (fst e1) end_, Array_get (e1, None)
4004 | _ ->
4005 L.back env.lb;
4006 let e2 = expr { env with priority = 0 } in
4007 expect env Trb;
4008 let end_ = Pos.make env.file env.lb in
4009 Pos.btw (fst e1) end_, Array_get (e1, Some e2)
4012 (*****************************************************************************)
4013 (* XHP *)
4014 (*****************************************************************************)
4016 and is_xhp env =
4017 look_ahead env begin fun env ->
4018 let tok = L.xhpname env.file env.lb in
4019 tok = Txhpname &&
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)
4027 and xhp env =
4028 match L.xhpname env.file env.lb with
4029 | Txhpname ->
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
4035 if closed
4036 then Pos.btw start end_tag, Xml (pname, attrl, [])
4037 else
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)
4042 | _ ->
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
4049 | Tslash ->
4050 if L.xhpattr env.file env.lb <> Tgt
4051 then error_expect env ">";
4052 [], true
4053 | Tgt ->
4054 [], false
4055 | Tword ->
4056 let error_state = !(env.errors) in
4057 let attr_name = Pos.make env.file env.lb, Lexing.lexeme env.lb in
4058 expect env Teq;
4059 let attr_value = xhp_attribute_value env in
4060 if !(env.errors) != error_state
4061 then
4062 [attr_name, attr_value], true
4063 else
4064 let rl, closed = xhp_attributes env in
4065 (attr_name, attr_value) :: rl, closed
4066 | _ ->
4067 error_expect env ">";
4068 [], true
4070 and xhp_attribute_value env =
4071 match L.xhpattr env.file env.lb with
4072 | Tlcb when env.mode = FileInfo.Mdecl ->
4073 ignore_body env;
4074 Pos.none, Null
4075 | Tlcb ->
4076 let result = expr { env with priority = 0 } in
4077 expect env Trcb;
4078 result
4079 | Tdquote ->
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
4083 | _ ->
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
4090 | Teof ->
4091 error_at env start "Xhp attribute not closed";
4092 start, String (start, "")
4093 | Tdquote ->
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)
4098 | _ ->
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
4104 * bit of markup *)
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 ->
4119 L.back env.lb;
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 ->
4139 ignore_body env;
4140 xhp_body pos name env
4141 | Tlcb ->
4142 let error_state = !(env.errors) in
4143 let e = expr { env with priority = 0 } in
4144 expect env Trcb;
4145 if !(env.errors) != error_state
4146 then [e]
4147 else e :: xhp_body pos name env
4148 | Tlt ->
4149 if is_xhp env
4150 then
4151 let xml = xhp env in
4152 xml :: xhp_body pos name env
4153 else
4154 (match L.xhptoken env.file env.lb with
4155 | Tslash ->
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)
4160 then
4161 if closing_name = name
4162 then []
4163 else begin
4164 error_expect env name;
4167 else begin
4168 error_expect env "closing tag name";
4169 xhp_body pos name env
4171 | _ ->
4172 error_at env pos "Stray < in xhp";
4173 L.back env.lb;
4174 xhp_body pos name env
4176 | Teof ->
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 *)
4185 | _ -> assert false
4187 (*****************************************************************************)
4188 (* Typedefs *)
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
4195 expect env Teq;
4196 let td = hint env in
4197 expect env Tsc;
4198 let kind = if is_abstract then NewType td else Alias td in
4200 t_id = id;
4201 t_tparams = tparams;
4202 t_constraint = tconstraint;
4203 t_kind = kind;
4204 t_user_attributes = attr;
4205 t_namespace = Namespace_env.empty env.popt;
4206 t_mode = env.mode;
4209 and typedef_constraint env =
4210 match L.token env.file env.lb with
4211 | Tword when Lexing.lexeme env.lb = "as" ->
4212 Some (hint env)
4213 | _ ->
4214 L.back env.lb;
4215 None
4217 and promote_nullable_to_optional_in_shapes env =
4218 TypecheckerOptions.experimental_feature_enabled
4219 env.popt
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
4225 | _ ->
4226 L.back env.lb;
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
4236 | Trp ->
4238 si_allows_unknown_fields = promote_nullable_to_optional_in_shapes env;
4239 si_shape_field_list = [];
4241 | Tellipsis ->
4242 expect env Trp;
4244 si_allows_unknown_fields = true;
4245 si_shape_field_list = [];
4247 | _ ->
4248 L.back env.lb;
4249 let error_state = !(env.errors) in
4250 let fd = hint_shape_field env in
4251 match L.token env.file env.lb with
4252 | Trp ->
4254 si_allows_unknown_fields =
4255 promote_nullable_to_optional_in_shapes env;
4256 si_shape_field_list = [fd];
4258 | Tcomma ->
4259 if !(env.errors) != error_state
4260 then {
4261 si_allows_unknown_fields =
4262 promote_nullable_to_optional_in_shapes env;
4263 si_shape_field_list = [fd];
4265 else
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 }
4270 | _ ->
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. *)
4280 let sf_optional =
4281 if L.token env.file env.lb = Tqm then
4282 true
4283 else
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
4289 expect env Tsarrow;
4290 let sf_hint = hint env in
4291 { sf_optional; sf_name; sf_hint }
4293 (*****************************************************************************)
4294 (* Namespaces *)
4295 (*****************************************************************************)
4297 and namespace env =
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:[]
4306 | _ -> toplevel in
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
4314 | Tlcb ->
4315 let body = tl [] env (fun x -> x = Trcb || x = Teof) in
4316 expect env Trcb;
4317 id, body
4318 | Tsc when (snd id) = "" ->
4319 error_expect env "{";
4320 id, []
4321 | Tsc ->
4322 let terminate = function
4323 | Tword -> Lexing.lexeme env.lb = "namespace"
4324 | Teof -> true
4325 | _ -> false in
4326 let body = tl [] env terminate in
4327 id, body
4328 | _ ->
4329 error_expect env "{ or ;";
4330 id, []
4332 and namespace_kind env =
4333 match L.token env.file env.lb with
4334 | Tword -> begin
4335 match Lexing.lexeme env.lb with
4336 | "function" -> NSFun
4337 | "const" -> NSConst
4338 | "type" -> NSClass
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
4348 | Tword -> begin
4349 let prefix = Lexing.lexeme env.lb in
4350 match L.token env.file env.lb with
4351 | Tlcb -> Some prefix
4352 | _ -> None
4354 | _ -> None
4355 end in
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
4364 let id2 =
4365 match L.token env.file env.lb with
4366 | Tword when Lexing.lexeme env.lb = "as" ->
4367 identifier env
4368 | _ ->
4369 L.back env.lb;
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
4379 | _ ->
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. *)
4386 let prefix =
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
4401 expect env Tsc;
4402 List.map unprefixed begin fun (kind, (p1, s1), id2) ->
4403 (kind, (p1, prefix ^ s1), id2)
4406 (*****************************************************************************)
4407 (* Helper *)
4408 (*****************************************************************************)
4410 let from_file ?(quick = false) popt file =
4411 let content =
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