Implement shapes with class constant fields.
[hiphop-php.git] / hphp / hack / src / parsing / parser_hack.ml
blob0b7072e841fdcf5fc9574eecf7f10028f45b478f
1 (**
2 * Copyright (c) 2014, 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 Lexer_hack
11 open Ast
13 module SMap = Utils.SMap
14 module L = Lexer_hack
16 (*****************************************************************************)
17 (* Environment *)
18 (*****************************************************************************)
20 type env = {
21 mode : Ast.mode;
22 priority : int;
23 lb : Lexing.lexbuf;
24 errors : (Pos.t * string) list ref;
27 let init_env lb = {
28 mode = Ast.Mpartial;
29 priority = 0;
30 lb = lb;
31 errors = ref [];
34 type parser_return = {
35 (* True if we are dealing with a hack file *)
36 is_hh_file : bool;
37 comments : (Pos.t * string) list;
38 ast : Ast.program;
41 (*****************************************************************************)
42 (* Lexer (with backtracking) *)
43 (*****************************************************************************)
45 type saved_lb = {
46 (* no need to save refill_buff because it's constant *)
47 lex_abs_pos : int;
48 lex_start_pos : int;
49 lex_curr_pos : int;
50 lex_last_pos : int;
51 lex_last_action : int;
52 lex_eof_reached : bool;
53 lex_mem : int array;
54 lex_start_p : Lexing.position;
55 lex_curr_p : Lexing.position;
58 let save_lexbuf_state (lb : Lexing.lexbuf) : saved_lb =
60 lex_abs_pos = lb.Lexing.lex_abs_pos;
61 lex_start_pos = lb.Lexing.lex_start_pos;
62 lex_curr_pos = lb.Lexing.lex_curr_pos;
63 lex_last_pos = lb.Lexing.lex_last_pos;
64 lex_last_action = lb.Lexing.lex_last_action;
65 lex_eof_reached = lb.Lexing.lex_eof_reached;
66 lex_mem = lb.Lexing.lex_mem;
67 lex_start_p = lb.Lexing.lex_start_p;
68 lex_curr_p = lb.Lexing.lex_curr_p;
71 let restore_lexbuf_state (lb : Lexing.lexbuf) (saved : saved_lb) : unit =
72 begin
73 lb.Lexing.lex_abs_pos <- saved.lex_abs_pos;
74 lb.Lexing.lex_start_pos <- saved.lex_start_pos;
75 lb.Lexing.lex_curr_pos <- saved.lex_curr_pos;
76 lb.Lexing.lex_last_pos <- saved.lex_last_pos;
77 lb.Lexing.lex_last_action <- saved.lex_last_action;
78 lb.Lexing.lex_eof_reached <- saved.lex_eof_reached;
79 lb.Lexing.lex_mem <- saved.lex_mem;
80 lb.Lexing.lex_start_p <- saved.lex_start_p;
81 lb.Lexing.lex_curr_p <- saved.lex_curr_p;
82 end
85 * Call a function with a forked lexing environment, and return its
86 * result.
88 let look_ahead (env : env) (f : env -> 'a) : 'a =
89 let saved = save_lexbuf_state env.lb in
90 let ret = f env in
91 restore_lexbuf_state env.lb saved;
92 ret
95 * Conditionally parse, saving lexer state in case we need to backtrack.
96 * The function parameter returns any optional type. If it's None, pop
97 * lexer state on the way out.
99 * Note that you shouldn't add any errors to the environment before
100 * you've committed to returning Some something. The error state is not
101 * popped.
103 let try_parse (env : env) (f : env -> 'a option) : 'a option =
104 let saved = save_lexbuf_state env.lb in
105 match f env with
106 | Some x -> Some x
107 | None -> (restore_lexbuf_state env.lb saved; None)
109 (* Return the next token without updating lexer state *)
110 let peek env =
111 let saved = save_lexbuf_state env.lb in
112 let ret = L.token env.lb in
113 restore_lexbuf_state env.lb saved;
116 (* Drop the next token unconditionally *)
117 let drop (env : env) : unit = match L.token env.lb with _ -> ()
119 let btw (p1, _) (p2, _) = Pos.btw p1 p2
121 let is_hh_file = ref false
123 (*****************************************************************************)
124 (* Errors *)
125 (*****************************************************************************)
127 let error_at env pos msg =
128 env.errors := (pos, msg) :: !(env.errors)
130 let error env msg =
131 error_at env (Pos.make env.lb) msg
133 let error_continue env =
134 error env
135 "Yeah...we're not going to support continue/break N. \
136 It makes static analysis tricky and it's not really essential"
138 let error_expect env expect =
139 let pos = Pos.make env.lb in
140 L.back env.lb;
141 env.errors := (pos, "Expected "^expect) :: !(env.errors)
143 let expect env x =
144 if L.token env.lb = x
145 then ()
146 else error_expect env (L.token_to_string x)
148 let expect_word env name =
149 let tok = L.token env.lb in
150 let value = Lexing.lexeme env.lb in
151 if tok <> Tword || value <> name
152 then error_expect env ("Was expecting: '"^name^ "' (not '"^value^"')");
155 (*****************************************************************************)
156 (* Modifiers checks (public private, final abstract etc ...) *)
157 (*****************************************************************************)
159 let rec check_modifiers env pos abstract final = function
160 | [] -> ()
161 | Final :: _ when abstract ->
162 error_at env pos "Parse error. Cannot mix final and abstract"
163 | Abstract :: _ when final ->
164 error_at env pos "Parse error. Cannot mix final and abstract"
165 | Final :: rl -> check_modifiers env pos abstract true rl
166 | Abstract :: rl -> check_modifiers env pos true final rl
167 | _ :: rl -> check_modifiers env pos abstract final rl
169 let check_visibility env pos l =
170 if List.exists begin function
171 | Private | Public | Protected | Static -> true
172 | _ -> false
173 end l
174 then ()
175 else error_at env pos
176 "Parse error. You are missing public, private or protected."
178 let rec check_mix_visibility env pos last_vis = function
179 | [] -> ()
180 | (Private | Public | Protected as vis) :: rl ->
181 (match last_vis with
182 | Some vis2 when vis <> vis2 ->
183 error_at env pos
184 "Parse error. Cannot mix different visibilities."
185 | _ ->
186 check_mix_visibility env pos (Some vis) rl
188 | _ :: rl -> check_mix_visibility env pos last_vis rl
190 let rec check_duplicates env pos = function
191 | [_] | [] -> ()
192 | Private :: rl -> check_duplicates env pos rl
193 | x :: (y :: _) when x = y ->
194 error_at env pos "Parse error. Duplicate modifier"
195 | _ :: rl -> check_duplicates env pos rl
197 let check_modifiers env pos l =
198 check_visibility env pos l;
199 check_modifiers env pos false false l;
200 check_duplicates env pos (List.sort compare l);
201 check_mix_visibility env pos None l;
204 let check_not_final env pos modifiers =
205 if List.exists (function Final -> true | _ -> false) modifiers
206 then error_at env pos "class variable cannot be final";
209 let check_toplevel env pos =
210 if env.mode = Ast.Mstrict
211 then error_at env pos "Remove all toplevel statements except for requires"
213 (*****************************************************************************)
214 (* Check expressions. *)
215 (*****************************************************************************)
217 let rec check_lvalue env = function
218 | _, (Lvar _ | Obj_get _ | Array_get _ | Class_get _) -> ()
219 | pos, Call ((_, Id (_, "tuple")), _) ->
220 error_at env pos
221 "Tuple cannot be used as an lvalue. Maybe you meant List?"
222 | _, List el -> List.iter (check_lvalue env) el
223 | pos, (Array _ | Shape _ | Collection _
224 | Null | True | False | Id _ | Clone _
225 | Class_const _ | Call _ | Int _ | Float _
226 | String _ | String2 _ | Yield _ | Yield_break
227 | Await _ | Expr_list _ | Cast _ | Unop _ |
228 Binop _ | Eif _ | InstanceOf _ | New _ | Efun _ | Lfun _ | Xml _) ->
229 error_at env pos "Invalid lvalue"
231 (*****************************************************************************)
232 (* Operator priorities.
234 * It is annoying to deal with priorities by hand (although it's possible).
235 * This list mimics what would typically look like yacc rules, defining
236 * the operators priorities (from low to high), and associativity (left, right
237 * or non-assoc).
239 * The priorities are then used by the "reducer" to auto-magically parse
240 * expressions in the right order (left, right, non-assoc) and with the right
241 * priority. Checkout the function "reduce" for more details.
243 (*****************************************************************************)
245 type assoc =
246 | Left (* a <op> b <op> c = ((a <op> b) <op> c) *)
247 | Right (* a <op> b <op> c = (a <op> (b <op> c)) *)
248 | NonAssoc (* a <op> b <op> c = error *)
250 let priorities = [
251 (* Lowest priority *)
252 (Left, [Tinclude; Tinclude_once; Teval; Trequire; Trequire_once]);
253 (Left, [Tcomma]);
254 (Right, [Tprint]);
255 (Left, [Tqm; Tcolon]);
256 (Left, [Tbarbar]);
257 (Left, [Txor]);
258 (Left, [Tampamp]);
259 (Left, [Tbar]);
260 (Left, [Tamp]);
261 (NonAssoc, [Teqeq; Tdiff; Teqeqeq; Tdiff2]);
262 (NonAssoc, [Tlt; Tlte; Tgt; Tgte]);
263 (Left, [Tltlt; Tgtgt]);
264 (Left, [Tplus; Tminus; Tdot]);
265 (Left, [Tstar; Tslash; Tpercent]);
266 (Right, [Tem]);
267 (NonAssoc, [Tinstanceof]);
268 (Right, [Ttild; Tincr; Tdecr; Tcast]);
269 (Right, [Tat; Tref]);
270 (NonAssoc, [Tyield]);
271 (NonAssoc, [Tawait]);
272 (Left, [Tlp]);
273 (NonAssoc, [Tnew; Tclone]);
274 (Left, [Tlb]);
275 (Right, [Teq; Tpluseq; Tminuseq; Tstareq;
276 Tslasheq; Tdoteq; Tpercenteq;
277 Tampeq; Tbareq; Txoreq; Tlshifteq; Trshifteq]);
278 (Left, [Tarrow]);
279 (Left, [Telseif]);
280 (Left, [Telse]);
281 (Left, [Tendif]);
282 (Left, [Tcolcol]);
283 (Left, [Tdollar]);
284 (* Highest priority *)
287 let get_priority =
288 (* Creating the table of assocs/priorities at initialization time. *)
289 let ptable = Hashtbl.create 23 in
290 (* Lowest priority = 0 *)
291 let priority = ref 0 in
292 List.iter begin fun (assoc, tokl) ->
293 List.iter begin fun token ->
294 (* Associates operator => (associativity, priority) *)
295 Hashtbl.add ptable token (assoc, !priority)
296 end tokl;
297 (* This is a bit subtle:
299 * The difference in priority between 2 lines should be 2, not 1.
301 * It's because of a trick we use in the reducer.
302 * For something to be left-associative, we just pretend
303 * that the right hand side expression has a higher priority.
305 * An example:
306 * expr "1 + 2 + 3"
307 * reduce (e1 = 1) "2 + 3" // priority = 0
308 * reduce (e1 = 1) (expr "2 + 3" with priority+1)
309 * reduce (e1 = 1) (2, "+ 3") <--- this is where the trick is:
310 * because we made the priority higher
311 * the reducer stops when it sees the
312 * "+" sign.
314 priority := !priority + 2
315 end priorities;
316 fun tok ->
317 assert (Hashtbl.mem ptable tok);
318 Hashtbl.find ptable tok
320 let with_priority env op f =
321 let _, prio = get_priority op in
322 let env = { env with priority = prio } in
323 f env
325 let with_base_priority env f =
326 let env = { env with priority = 0 } in
327 f env
329 (*****************************************************************************)
330 (* References *)
331 (*****************************************************************************)
333 let ref_opt env =
334 match L.token env.lb with
335 | Tamp when env.mode = Ast.Mstrict ->
336 error env "Don't use references!"
337 | Tamp ->
339 | _ ->
340 L.back env.lb
342 (*****************************************************************************)
343 (* Identifiers *)
344 (*****************************************************************************)
346 (* identifier *)
347 let identifier env =
348 match L.token env.lb with
349 | Tword ->
350 let pos = Pos.make env.lb in
351 let name = Lexing.lexeme env.lb in
352 pos, name
353 | Tcolon ->
354 (match L.xhpname env.lb with
355 | Txhpname ->
356 Pos.make env.lb, ":"^Lexing.lexeme env.lb
357 | _ ->
358 error_expect env "identifier";
359 Pos.make env.lb, "*Unknown*"
361 | _ ->
362 error_expect env "identifier";
363 Pos.make env.lb, "*Unknown*"
365 (* $variable *)
366 let variable env =
367 match L.token env.lb with
368 | Tlvar ->
369 Pos.make env.lb, Lexing.lexeme env.lb
370 | _ ->
371 error_expect env "variable";
372 Pos.make env.lb, "$_"
374 (* &$variable *)
375 let ref_variable env =
376 ref_opt env;
377 variable env
379 (* &...$arg *)
380 let ref_param env =
381 ref_opt env;
382 let is_variadic = match L.token env.lb with
383 | Tellipsis -> true
384 | _ -> L.back env.lb; false
386 let var = variable env in
387 is_variadic, var
389 (*****************************************************************************)
390 (* Entry point *)
391 (*****************************************************************************)
393 let rec program content =
394 is_hh_file := false;
395 L.comment_list := [];
396 let lb = Lexing.from_string content in
397 let env = init_env lb in
398 let ast = header env in
399 let comments = !L.comment_list in
400 L.comment_list := [];
401 if !(env.errors) <> []
402 then Errors.parsing_error (List.hd (List.rev !(env.errors)));
403 let is_hh_file = !is_hh_file in
404 let ast = Namespaces.elaborate_defs ast in
405 {is_hh_file; comments; ast}
407 (*****************************************************************************)
408 (* Hack headers (strict, decl, partial) *)
409 (*****************************************************************************)
411 and header env =
412 let file_type, head = get_header env in
413 match file_type, head with
414 | Ast.PhpFile, _
415 | _, Some Ast.Mdecl ->
416 let env = { env with mode = Ast.Mdecl } in
417 let attr = SMap.empty in
418 let result = ignore_toplevel ~attr [] env (fun x -> x = Teof) in
419 expect env Teof;
420 if head = Some Ast.Mdecl then is_hh_file := true;
421 result
422 | _, Some mode ->
423 let result = toplevel [] { env with mode = mode } (fun x -> x = Teof) in
424 expect env Teof;
425 is_hh_file := true;
426 result
427 | _ ->
430 and get_header env =
431 match L.header env.lb with
432 | `error -> Ast.HhFile, None
433 | `default_mode -> Ast.HhFile, Some Ast.Mpartial
434 | `php_decl_mode -> Ast.PhpFile, Some Ast.Mdecl
435 | `php_mode -> Ast.PhpFile, None
436 | `explicit_mode ->
437 let _token = L.token env.lb in
438 (match Lexing.lexeme env.lb with
439 | "strict" when !(Ide.is_ide_mode) -> Ast.HhFile, Some Ast.Mpartial
440 | "strict" -> Ast.HhFile, Some Ast.Mstrict
441 | ("decl"|"only-headers") -> Ast.HhFile, Some Ast.Mdecl
442 | "partial" -> Ast.HhFile, Some Ast.Mpartial
443 | _ ->
444 error env
445 "Incorrect comment; possible values include strict, decl, partial or empty";
446 Ast.HhFile, Some Ast.Mdecl
449 (*****************************************************************************)
450 (* Decl mode *)
451 (*****************************************************************************)
453 and ignore_toplevel ~attr acc env terminate =
454 match L.token env.lb with
455 | x when terminate x ->
456 L.back env.lb;
458 | Tltlt ->
459 (* Parsing attribute << .. >> *)
460 let attr = attribute_remain env SMap.empty in
461 ignore_toplevel ~attr acc env terminate
462 | Tlcb ->
463 let acc = ignore_toplevel ~attr acc env terminate in
464 ignore_toplevel ~attr acc env terminate
465 | Tquote ->
466 let pos = Pos.make env.lb in
467 let abs_pos = env.lb.Lexing.lex_curr_pos in
468 ignore (expr_string env pos abs_pos);
469 ignore_toplevel ~attr acc env terminate
470 | Tdquote ->
471 let pos = Pos.make env.lb in
472 ignore (expr_encapsed env pos);
473 ignore_toplevel ~attr acc env terminate
474 | Theredoc ->
475 ignore (expr_heredoc env);
476 ignore_toplevel ~attr acc env terminate
477 | Tlt when is_xhp env ->
478 ignore (xhp env);
479 ignore_toplevel ~attr acc env terminate
480 | Tword ->
481 (match Lexing.lexeme env.lb with
482 | "function" ->
483 (match L.token env.lb with
484 | Tword ->
485 L.back env.lb;
486 let def = toplevel_word ~attr env "function" in
487 ignore_toplevel ~attr:SMap.empty (def @ acc) env terminate
488 (* function &foo(...), we still want them in decl mode *)
489 | Tamp ->
490 (match L.token env.lb with
491 | Tword ->
492 L.back env.lb;
493 let def = toplevel_word ~attr env "function" in
494 ignore_toplevel ~attr:SMap.empty (def @ acc) env terminate
495 | _ ->
496 ignore_toplevel ~attr acc env terminate
498 | _ ->
499 ignore_toplevel ~attr acc env terminate
501 | "abstract" | "final"
502 | "class"| "trait" | "interface"
503 | "namespace"
504 | "async" | "newtype"| "type"| "const" ->
505 (* Parsing toplevel declarations (class, function etc ...) *)
506 let def = toplevel_word ~attr env (Lexing.lexeme env.lb) in
507 ignore_toplevel ~attr:SMap.empty (def @ acc) env terminate
508 | _ -> ignore_toplevel ~attr acc env terminate
510 | Tclose_php ->
511 error env "Hack does not allow the closing ?> tag";
513 | _ -> ignore_toplevel ~attr acc env terminate
515 (*****************************************************************************)
516 (* Toplevel statements. *)
517 (*****************************************************************************)
519 and toplevel acc env terminate =
520 match L.token env.lb with
521 | x when terminate x ->
522 L.back env.lb;
523 List.rev acc
524 | Tsc ->
525 (* Ignore extra semicolons at toplevel (important so we don't yell about
526 * them in strict mode). *)
527 toplevel acc env terminate
528 | Tltlt ->
529 (* Parsing attribute << .. >> *)
530 let attr = attribute_remain env SMap.empty in
531 let _ = L.token env.lb in
532 let def = toplevel_word ~attr env (Lexing.lexeme env.lb) in
533 toplevel (def @ acc) env terminate
534 | Tword ->
535 (* Parsing toplevel declarations (class, function etc ...) *)
536 let attr = SMap.empty in
537 let def = toplevel_word ~attr env (Lexing.lexeme env.lb) in
538 toplevel (def @ acc) env terminate
539 | Tclose_php ->
540 error env "Hack does not allow the closing ?> tag";
541 List.rev acc
542 | _ ->
543 (* All the other statements. *)
544 let pos = Pos.make env.lb in
545 L.back env.lb;
546 let error_state = !(env.errors) in
547 let stmt = Stmt (statement env) in
548 check_toplevel env pos;
549 if error_state != !(env.errors)
550 then ignore_toplevel ~attr:SMap.empty (stmt :: acc) env terminate
551 else toplevel (stmt :: acc) env terminate
553 and toplevel_word ~attr env = function
554 | "abstract" ->
555 expect_word env "class";
556 let class_ = class_ ~attr ~final:false ~kind:Cabstract env in
557 [Class class_]
558 | "final" ->
559 expect_word env "class";
560 let class_ = class_ ~attr ~final:true ~kind:Cnormal env in
561 [Class class_]
562 | "class" ->
563 let class_ = class_ ~attr ~final:false ~kind:Cnormal env in
564 [Class class_]
565 | "trait" ->
566 let class_ = class_ ~attr ~final:false ~kind:Ctrait env in
567 [Class class_]
568 | "interface" ->
569 let class_ = class_ ~attr ~final:false ~kind:Cinterface env in
570 [Class class_]
571 | "async" ->
572 expect_word env "function";
573 let fun_ = fun_ ~attr ~sync:FAsync env in
574 [Fun fun_]
575 | "function" ->
576 let fun_ = fun_ ~attr ~sync:FSync env in
577 [Fun fun_]
578 | "newtype" ->
579 let id, tparaml, tconstraint, typedef = typedef env in
580 [Typedef {
581 t_id = id;
582 t_tparams = tparaml;
583 t_constraint = tconstraint;
584 t_kind = NewType typedef;
585 t_namespace = Namespace_env.empty;
586 t_mode = env.mode;
588 | "type" ->
589 let id, tparaml, tconstraint, typedef = typedef env in
590 [Typedef {
591 t_id = id;
592 t_tparams = tparaml;
593 t_constraint = tconstraint;
594 t_kind = Alias typedef;
595 t_namespace = Namespace_env.empty;
596 t_mode = env.mode;
598 | "namespace" ->
599 let id, body = namespace env in
600 (* Check for an empty name and omit the Namespace wrapper *)
601 (match id with
602 | (_, "") -> body
603 | _ -> [Namespace (id, body)])
604 | "use" ->
605 let usel = namespace_use_list env [] in
606 [NamespaceUse usel]
607 | "const" ->
608 let consts = class_const_def env in
609 (match consts with
610 | Const (h, cstl) ->
611 List.map (fun (x, y) -> Constant {
612 cst_mode = env.mode;
613 cst_kind = Cst_const;
614 cst_name = x;
615 cst_type = h;
616 cst_value = y;
617 cst_namespace = Namespace_env.empty;
618 }) cstl
619 | _ -> assert false)
620 | "require" | "require_once" ->
621 let _ = expr env in
622 expect env Tsc;
623 [Stmt Noop]
624 | _ ->
625 let pos = Pos.make env.lb in
626 L.back env.lb;
627 let stmt = statement env in
628 check_toplevel env pos;
629 [define_or_stmt env stmt]
631 and define_or_stmt env = function
632 | Expr (_, Call ((_, Id (_, "define")), [(_, String name); value])) ->
633 Constant {
634 cst_mode = env.mode;
635 cst_kind = Cst_define;
636 cst_name = name;
637 cst_type = None;
638 cst_value = value;
639 cst_namespace = Namespace_env.empty;
641 | stmt ->
642 Stmt stmt
644 (*****************************************************************************)
645 (* Attributes: <<_>> *)
646 (*****************************************************************************)
648 (* <<_>> *)
649 and attribute env =
650 let acc = SMap.empty in
651 if look_ahead env (fun env -> L.token env.lb = Tltlt)
652 then begin
653 expect env Tltlt;
654 attribute_remain env acc;
656 else acc
658 (* _>> *)
659 and attribute_remain env acc =
660 match L.token env.lb with
661 | Tword ->
662 let attr_name = Lexing.lexeme env.lb in
663 let acc = attribute_parameter attr_name acc env in
664 attribute_list_remain acc env
665 | _ ->
666 error_expect env "attribute name";
669 (* empty | (parameter_list) *)
670 and attribute_parameter attr_name acc env =
671 match L.token env.lb with
672 | Tlp ->
673 let el = expr_list_remain env in
674 SMap.add attr_name el acc
675 | _ ->
676 let acc = SMap.add attr_name [] acc in
677 L.back env.lb;
680 (* ,_,>> *)
681 and attribute_list_remain acc env =
682 match L.token env.lb with
683 | Tgtgt -> acc
684 | Tcomma -> attribute_remain env acc
685 | _ ->
686 error_expect env ">>";
689 (*****************************************************************************)
690 (* Functions *)
691 (*****************************************************************************)
693 and fun_ ~attr ~sync env =
694 ref_opt env;
695 let name = identifier env in
696 let tparams = class_params env in
697 let params = parameter_list env in
698 let ret = hint_return_opt env in
699 let body = function_body env in
700 { f_name = name;
701 f_tparams = tparams;
702 f_params = params;
703 f_ret = ret;
704 f_body = body;
705 f_user_attributes = attr;
706 f_type = sync;
707 f_mode = env.mode;
708 f_mtime = 0.0;
709 f_namespace = Namespace_env.empty;
712 (*****************************************************************************)
713 (* Classes *)
714 (*****************************************************************************)
716 and class_ ~attr ~final ~kind env =
717 let cname = identifier env in
718 let is_xhp = (snd cname).[0] = ':' in
719 let tparams = class_params env in
720 let cextends = class_extends env in
721 let cimplements = class_implements env in
722 let cbody = class_body env in
723 let result =
724 { c_mode = env.mode;
725 c_final = final;
726 c_kind = kind;
727 c_is_xhp = is_xhp;
728 c_implements = cimplements;
729 c_tparams = tparams;
730 c_user_attributes = attr;
731 c_name = cname;
732 c_extends = cextends;
733 c_body = cbody;
734 c_namespace = Namespace_env.empty;
737 class_implicit_fields result
739 (*****************************************************************************)
740 (* Extends/Implements *)
741 (*****************************************************************************)
743 and class_extends env =
744 match L.token env.lb with
745 | Tword ->
746 (match Lexing.lexeme env.lb with
747 | "extends" -> class_extends_list env
748 | "implements" -> L.back env.lb; []
749 | _ -> error env "Expected: extends"; []
751 | Tlcb ->
752 L.back env.lb;
754 | _ ->
755 error_expect env "{";
758 and class_implements env =
759 match L.token env.lb with
760 | Tword ->
761 (match Lexing.lexeme env.lb with
762 | "implements" -> class_extends_list env
763 | "extends" -> L.back env.lb; []
764 | _ -> error env "Expected: implements"; []
766 | Tlcb ->
767 L.back env.lb;
769 | _ ->
770 error_expect env "{";
773 and class_extends_list env =
774 let error_state = !(env.errors) in
775 let c = class_hint env in
776 match L.token env.lb with
777 | Tlcb ->
778 L.back env.lb; [c]
779 | Tcomma ->
780 if !(env.errors) != error_state
781 then [c]
782 else c :: class_extends_list env
783 | Tword ->
784 (match Lexing.lexeme env.lb with
785 | "implements" | "extends" -> L.back env.lb; [c]
786 | _ -> error_expect env "{"; []
788 | _ -> error_expect env "{"; []
790 (*****************************************************************************)
791 (* Class parameters class A<T as X ..> *)
792 (*****************************************************************************)
794 and class_params env =
795 match L.token env.lb with
796 | Tlt -> class_param_list env
797 | _ -> L.back env.lb; []
799 and class_param_list env =
800 let error_state = !(env.errors) in
801 let cst = class_param env in
802 match L.gt_or_comma env.lb with
803 | Tgt ->
804 [cst]
805 | Tcomma ->
806 if !(env.errors) != error_state
807 then [cst]
808 else cst :: class_param_list_remain env
809 | _ ->
810 error_expect env ">";
811 [cst]
813 and class_param_list_remain env =
814 match L.gt_or_comma env.lb with
815 | Tgt -> []
816 | _ ->
817 L.back env.lb;
818 let error_state = !(env.errors) in
819 let cst = class_param env in
820 match L.gt_or_comma env.lb with
821 | Tgt ->
822 [cst]
823 | Tcomma ->
824 if !(env.errors) != error_state
825 then [cst]
826 else cst :: class_param_list_remain env
827 | _ -> error_expect env ">"; [cst]
829 and class_param env =
830 match L.token env.lb with
831 | Tword ->
832 let parameter_name = Pos.make env.lb, Lexing.lexeme env.lb in
833 let parameter_constraint = class_parameter_constraint env in
834 parameter_name, parameter_constraint
835 | _ ->
836 error_expect env "type parameter";
837 let parameter_name = Pos.make env.lb, "T*unknown*" in
838 parameter_name, None
841 and class_parameter_constraint env =
842 match L.token env.lb with
843 | Tword when Lexing.lexeme env.lb = "as" ->
844 Some (hint env)
845 | _ -> L.back env.lb; None
847 (*****************************************************************************)
848 (* Class hints (A<T> etc ...) *)
849 (*****************************************************************************)
851 and class_hint env =
852 let pname = identifier env in
853 class_hint_with_name env pname
855 and class_hint_with_name env pname =
856 let params = class_hint_params env in
857 (fst pname), Happly (pname, params)
859 and class_hint_params env =
860 match L.token env.lb with
861 | Tlt -> class_hint_param_list env
862 | _ -> L.back env.lb; []
864 and class_hint_param_list env =
865 let error_state = !(env.errors) in
866 let h = hint env in
867 match L.gt_or_comma env.lb with
868 | Tgt ->
870 | Tcomma ->
871 if !(env.errors) != error_state
872 then [h]
873 else h :: class_hint_param_list_remain env
874 | _ ->
875 error_expect env ">"; [h]
877 and class_hint_param_list_remain env =
878 match L.gt_or_comma env.lb with
879 | Tgt -> []
880 | _ ->
881 L.back env.lb;
882 let error_state = !(env.errors) in
883 let h = hint env in
884 match L.gt_or_comma env.lb with
885 | Tgt ->
887 | Tcomma ->
888 if !(env.errors) != error_state
889 then [h]
890 else h :: class_hint_param_list_remain env
891 | _ -> error_expect env ">"; [h]
893 (*****************************************************************************)
894 (* Type hints: int, ?int, A<T>, array<...> etc ... *)
895 (*****************************************************************************)
897 and hint env =
898 match L.token env.lb with
899 (* ?_ *)
900 | Tqm ->
901 let start = Pos.make env.lb in
902 let e = hint env in
903 Pos.btw start (fst e), Hoption e
904 (* A<_> | function(_):_ *)
905 | Tword ->
906 let pos = Pos.make env.lb in
907 let word = Lexing.lexeme env.lb in
908 hint_word env pos word
909 (* :XHPNAME *)
910 | Tcolon ->
911 L.back env.lb;
912 let cname = identifier env in
913 class_hint_with_name env cname
914 (* (_) *)
915 | Tlp ->
916 let start_pos = Pos.make env.lb in
917 hint_paren start_pos env
918 (* @_ *)
919 | Tat ->
920 let start = Pos.make env.lb in
921 let h = hint env in
922 Pos.btw start (fst h), snd h
923 | _ ->
924 error_expect env "type";
925 let pos = Pos.make env.lb in
926 pos, Happly ((pos, "*Unknown*"), [])
928 and hint_word env pos word =
929 (* function(_): _ *)
930 match word with
931 | "function" ->
932 hint_function pos env
933 (* A<_> *)
934 | name ->
935 class_hint_with_name env (pos, name)
937 (* (_) *)
938 and hint_paren start env =
939 let hintl = hint_list env in
940 let end_ = Pos.make env.lb in
941 let pos = Pos.btw start end_ in
942 match hintl with
943 | [] -> assert false
944 | [_, Hfun _ as h] -> pos, snd h
945 | [_] ->
946 error_at env pos "Tuples of one element are not allowed";
947 pos, Happly ((pos, "*Unkown*"), [])
948 | hl -> pos, Htuple hl
950 and hint_list env =
951 let error_state = !(env.errors) in
952 let h = hint env in
953 match L.token env.lb with
954 | Trp ->
956 | Tcomma ->
957 if !(env.errors) != error_state
958 then [h]
959 else h :: hint_list_remain env
960 | _ ->
961 error_expect env ">"; [h]
963 and hint_list_remain env =
964 match L.token env.lb with
965 | Trp -> []
966 | _ ->
967 L.back env.lb;
968 let error_state = !(env.errors) in
969 let h = hint env in
970 match L.token env.lb with
971 | Trp ->
973 | Tcomma ->
974 if !(env.errors) != error_state
975 then [h]
976 else h :: hint_list_remain env
977 | _ ->
978 error_expect env ">"; [h]
980 (*****************************************************************************)
981 (* Function hint (function(_): _) *)
982 (*****************************************************************************)
984 (* function(_): _ *)
985 and hint_function start env =
986 expect env Tlp;
987 let params, has_dots = hint_function_params env in
988 let ret = hint_return env in
989 Pos.btw start (fst ret), Hfun (params, has_dots, ret)
991 (* (parameter_1, .., parameter_n) *)
992 and hint_function_params env =
993 match L.token env.lb with
994 | Trp ->
995 ([], false)
996 | Tellipsis ->
997 hint_function_params_close env;
998 ([], true)
999 | _ ->
1000 L.back env.lb;
1001 hint_function_params_remain env
1003 (* ) | ,) *)
1004 and hint_function_params_close env =
1005 match L.token env.lb with
1006 | Trp ->
1008 | Tcomma ->
1009 expect env Trp
1010 | _ ->
1011 error_expect env ")";
1014 (* _, parameter_list | _) | ...) | ...,) *)
1015 and hint_function_params_remain env =
1016 let error_state = !(env.errors) in
1017 let h = hint env in
1018 match L.token env.lb with
1019 | Tcomma ->
1020 if !(env.errors) != error_state
1021 then ([h], false)
1022 else
1023 let hl, has_dots = hint_function_params env in
1024 (h :: hl, has_dots)
1025 | Trp ->
1026 ([h], false)
1027 | Tellipsis ->
1028 hint_function_params_close env;
1029 ([h], true)
1030 | _ ->
1031 error_expect env ")";
1032 ([h], false)
1034 (* : _ *)
1035 and hint_return env =
1036 expect env Tcolon;
1037 hint env
1039 and hint_return_opt env =
1040 match L.token env.lb with
1041 | Tcolon -> Some (hint env)
1042 | _ -> L.back env.lb; None
1044 (*****************************************************************************)
1045 (* Class statements *)
1046 (*****************************************************************************)
1048 (* { ... *)
1049 and class_body env =
1050 let error_state = !(env.errors) in
1051 expect env Tlcb;
1052 if error_state != !(env.errors)
1053 then L.look_for_open_cb env.lb;
1054 class_defs env
1056 and class_defs env =
1057 match L.token env.lb with
1058 (* ... } *)
1059 | Trcb ->
1061 (* xhp_format | const | use *)
1062 | Tword ->
1063 let word = Lexing.lexeme env.lb in
1064 class_toplevel_word env word
1065 | Tltlt ->
1066 (* variable | method *)
1067 L.back env.lb;
1068 let error_state = !(env.errors) in
1069 let m = class_member_def env in
1070 if !(env.errors) != error_state
1071 then [m]
1072 else m :: class_defs env
1073 | _ ->
1074 error_expect env "class member";
1075 let start = Pos.make env.lb in
1076 look_for_next_method start env;
1077 let _ = L.token env.lb in
1078 let word = Lexing.lexeme env.lb in
1079 class_toplevel_word env word
1081 and class_toplevel_word env word =
1082 match word with
1083 | "category" | "children" | "attribute" ->
1084 xhp_format env;
1085 class_defs env
1086 | "const" ->
1087 let error_state = !(env.errors) in
1088 let def = class_const_def env in
1089 if !(env.errors) != error_state
1090 then [def]
1091 else def :: class_defs env
1092 | "use" ->
1093 let traitl = class_use_list env in
1094 traitl @ class_defs env
1095 | "require" ->
1096 let traitl = trait_require env in
1097 traitl @ class_defs env
1098 | "abstract" | "public" | "protected" | "private" | "final" | "static" ->
1099 (* variable | method *)
1100 L.back env.lb;
1101 let start = Pos.make env.lb in
1102 let error_state = !(env.errors) in
1103 let m = class_member_def env in
1104 if !(env.errors) != error_state
1105 then look_for_next_method start env;
1106 m :: class_defs env
1107 | _ ->
1108 error_expect env "modifier";
1111 and look_for_next_method previous_pos env =
1112 match L.token env.lb with
1113 | Teof -> ()
1114 | Trcb -> ()
1115 | Tword ->
1116 (match Lexing.lexeme env.lb with
1117 | "abstract" | "public" | "protected"
1118 | "private" | "final" | "static" ->
1119 let pos = Pos.make env.lb in
1120 if Pos.compare pos previous_pos = 0
1121 then (* we are stuck in a circle *)
1122 look_for_next_method pos env
1123 else
1124 (L.back env.lb; ())
1125 | _ -> look_for_next_method previous_pos env
1127 | _ -> look_for_next_method previous_pos env
1129 (*****************************************************************************)
1130 (* Use (for traits) *)
1131 (*****************************************************************************)
1133 and class_use_list env =
1134 let error_state = !(env.errors) in
1135 let cst = ClassUse (class_hint env) in
1136 match L.token env.lb with
1137 | Tsc ->
1138 [cst]
1139 | Tcomma ->
1140 if !(env.errors) != error_state
1141 then [cst]
1142 else cst :: class_use_list_remain env
1143 | _ ->
1144 error_expect env ";"; [cst]
1146 and class_use_list_remain env =
1147 match L.token env.lb with
1148 | Tsc -> []
1149 | _ ->
1150 L.back env.lb;
1151 let error_state = !(env.errors) in
1152 let cst = ClassUse (class_hint env) in
1153 match L.token env.lb with
1154 | Tsc ->
1155 [cst]
1156 | Tcomma ->
1157 if !(env.errors) != error_state
1158 then [cst]
1159 else cst :: class_use_list_remain env
1160 | _ -> error_expect env ";"; [cst]
1162 and trait_require env =
1163 match L.token env.lb with
1164 | Tword ->
1165 let req_type = Lexing.lexeme env.lb in
1166 let ret = (match req_type with
1167 | "implements" -> [ClassTraitRequire (MustImplement, class_hint env)]
1168 | "extends" -> [ClassTraitRequire (MustExtend, class_hint env)]
1169 | _ -> error env "Expected: implements or extends"; []
1170 ) in
1171 (match L.token env.lb with
1172 | Tsc -> ret
1173 | _ -> error_expect env ";"; [])
1174 | _ -> error env "Expected: implements or extends"; []
1176 (*****************************************************************************)
1177 (* Class xhp_fromat *)
1179 * within a class body -->
1180 * children ...;
1181 * attribute ...;
1182 * category ...;
1184 (*****************************************************************************)
1186 and xhp_format env =
1187 match L.token env.lb with
1188 | Tsc -> ()
1189 | Tquote ->
1190 let pos = Pos.make env.lb in
1191 let abs_pos = env.lb.Lexing.lex_curr_pos in
1192 ignore (expr_string env pos abs_pos);
1193 xhp_format env
1194 | Tdquote ->
1195 let pos = Pos.make env.lb in
1196 ignore (expr_encapsed env pos);
1197 xhp_format env
1198 | x ->
1199 xhp_format env
1201 (*****************************************************************************)
1202 (* Class constants *)
1204 * within a class body -->
1205 * const ...;
1207 (*****************************************************************************)
1209 (* const_hint const_name1 = value1, ..., const_name_n = value_n; *)
1210 and class_const_def env =
1211 let h = class_const_hint env in
1212 let consts = class_const_list env in
1213 Const (h, consts)
1215 (* const _ X = ...; *)
1216 and class_const_hint env =
1217 if class_const_has_hint env
1218 then Some (hint env)
1219 else None
1221 (* Determines if there is a type-hint by looking ahead. *)
1222 and class_const_has_hint env =
1223 look_ahead env begin fun env ->
1224 match L.token env.lb with
1225 (* const_name = ... | hint_name const_name = ... *)
1226 | Tword ->
1227 (* If we see 'name =', there is no type hint *)
1228 L.token env.lb <> Teq
1229 | _ -> true
1232 and class_const_list env =
1233 let error_state = !(env.errors) in
1234 let cst = class_const env in
1235 match L.token env.lb with
1236 | Tsc ->
1237 [cst]
1238 | Tcomma ->
1239 if !(env.errors) != error_state
1240 then [cst]
1241 else cst :: class_const_list_remain env
1242 | _ ->
1243 error_expect env ";"; [cst]
1245 and class_const_list_remain env =
1246 match L.token env.lb with
1247 | Tsc -> []
1248 | _ ->
1249 L.back env.lb;
1250 let error_state = !(env.errors) in
1251 let cst = class_const env in
1252 match L.token env.lb with
1253 | Tsc ->
1254 [cst]
1255 | Tcomma ->
1256 if !(env.errors) != error_state
1257 then [cst]
1258 else cst :: class_const_list_remain env
1259 | _ ->
1260 error_expect env ";"; [cst]
1262 (* const_name = const_value *)
1263 and class_const env =
1264 let id = identifier env in
1265 expect env Teq;
1266 let e = expr env in
1267 id, e
1269 (*****************************************************************************)
1270 (* Modifiers *)
1271 (*****************************************************************************)
1273 and mandatory_modifier_list env =
1274 match L.token env.lb with
1275 | Tword ->
1276 let word = Lexing.lexeme env.lb in
1277 (match modifier_word env word with
1278 | None -> error_expect env "modifier"; []
1279 | Some v -> v :: optional_modifier_list env
1281 | _ ->
1282 error_expect env "modifier"; []
1284 and optional_modifier_list env =
1285 match L.token env.lb with
1286 | Tword ->
1287 let word = Lexing.lexeme env.lb in
1288 (match modifier_word env word with
1289 | None -> L.back env.lb; []
1290 | Some v -> v :: optional_modifier_list env
1292 | _ ->
1293 L.back env.lb; []
1295 and modifier_word env = function
1296 | "final" -> Some Final
1297 | "static" -> Some Static
1298 | "abstract" -> Some Abstract
1299 | "private" -> Some Private
1300 | "public" -> Some Public
1301 | "protected" -> Some Protected
1302 | _ -> None
1304 (*****************************************************************************)
1305 (* Class variables/methods. *)
1307 * within a class body -->
1308 * modifier_list ...;
1310 (*****************************************************************************)
1312 and class_member_def env =
1313 let attrs = attribute env in
1314 let modifier_start = Pos.make env.lb in
1315 let modifiers = mandatory_modifier_list env in
1316 let modifier_end = Pos.make env.lb in
1317 let modifier_pos = Pos.btw modifier_start modifier_end in
1318 check_modifiers env modifier_pos modifiers;
1319 match L.token env.lb with
1320 (* modifier_list $_ *)
1321 | Tlvar ->
1322 L.back env.lb;
1323 check_not_final env modifier_pos modifiers;
1324 let cvars = class_var_list env in
1325 ClassVars (modifiers, None, cvars)
1326 | Tword ->
1327 let word = Lexing.lexeme env.lb in
1328 class_member_word env ~modifiers ~attrs word
1329 | _ ->
1330 L.back env.lb;
1331 check_not_final env modifier_pos modifiers;
1332 let h = hint env in
1333 let cvars = class_var_list env in
1334 ClassVars (modifiers, Some h, cvars)
1336 (*****************************************************************************)
1337 (* Class variables *)
1339 * within a class body -->
1340 * modifier_list $x;
1341 * modifier_list hint $x;
1343 (*****************************************************************************)
1345 and class_var_list env =
1346 let error_state = !(env.errors) in
1347 let cvar = class_var env in
1348 if !(env.errors) != error_state
1349 then [cvar]
1350 else cvar :: class_var_list_remain env
1352 and class_var_list_remain env =
1353 match L.token env.lb with
1354 | Tsc ->
1356 | Tcomma ->
1357 (match L.token env.lb with
1358 | Tsc ->
1360 | _ ->
1361 L.back env.lb;
1362 let error_state = !(env.errors) in
1363 let var = class_var env in
1364 if !(env.errors) != error_state
1365 then [var]
1366 else var :: class_var_list_remain env
1368 | _ -> error_expect env ";"; []
1370 and class_var env =
1371 let pos, name = variable env in
1372 let name = class_var_name name in
1373 let default = parameter_default env in
1374 (pos, name), default
1376 and class_var_name name =
1377 String.sub name 1 (String.length name - 1)
1379 (*****************************************************************************)
1380 (* Methods *)
1382 * within a class body -->
1383 * modifier_list async function ...
1384 * modifier_list function ...
1386 (*****************************************************************************)
1388 and class_member_word env ~attrs ~modifiers = function
1389 | "async" ->
1390 expect_word env "function";
1391 ref_opt env;
1392 let fun_name = identifier env in
1393 let method_ = method_ env ~modifiers ~attrs ~sync:FAsync fun_name in
1394 Method method_
1395 | "function" ->
1396 ref_opt env;
1397 let fun_name = identifier env in
1398 let method_ = method_ env ~modifiers ~attrs ~sync:FSync fun_name in
1399 Method method_
1400 | _ ->
1401 L.back env.lb;
1402 let h = hint env in
1403 let cvars = class_var_list env in
1404 ClassVars (modifiers, Some h, cvars)
1406 and method_ env ~modifiers ~attrs ~sync pname =
1407 let pos, name = pname in
1408 let tparams = class_params env in
1409 let params = parameter_list env in
1410 let ret = hint_return_opt env in
1411 let body = function_body env in
1412 let ret = method_implicit_return env pname ret in
1413 if name = "__destruct" && params <> []
1414 then error_at env pos "Destructor must not have any parameters.";
1415 { m_name = pname;
1416 m_tparams = tparams;
1417 m_params = params;
1418 m_ret = ret;
1419 m_body = body;
1420 m_kind = modifiers;
1421 m_user_attributes = attrs;
1422 m_type = sync;
1425 (*****************************************************************************)
1426 (* Constructor/Destructors special cases. *)
1427 (*****************************************************************************)
1429 and method_implicit_return env (pos, name) ret =
1430 match name, ret with
1431 | ("__construct" | "__destruct"), None ->
1432 Some (pos, Happly((pos, "void"), []))
1433 | _, Some (_, Happly ((_, "void"), [])) -> ret
1434 | "__construct", Some _ ->
1435 error_at env pos "Constructor return type must be void or elided.";
1436 None
1437 | "__destruct", Some _ ->
1438 error_at env pos "Destructor return type must be void or elided.";
1439 None
1440 | _ -> ret
1442 (*****************************************************************************)
1443 (* Implicit class fields __construct(public int $x). *)
1444 (*****************************************************************************)
1446 and class_implicit_fields class_ =
1447 let class_body = method_implicit_fields class_.c_body in
1448 { class_ with c_body = class_body }
1450 and method_implicit_fields members =
1451 match members with
1452 | [] -> []
1453 | Method ({ m_name = _, "__construct"; _ } as m) :: rl ->
1454 let fields, assigns = param_implicit_fields m.m_params in
1455 fields @ Method { m with m_body = assigns @ m.m_body } :: rl
1456 | x :: rl ->
1457 x :: method_implicit_fields rl
1459 and param_implicit_fields params =
1460 match params with
1461 | [] -> [], []
1462 | { param_modifier = Some vis; _ } as p :: rl ->
1463 let member, stmt = param_implicit_field vis p in
1464 let members, assigns = param_implicit_fields rl in
1465 member :: members, stmt :: assigns
1466 | _ :: rl ->
1467 param_implicit_fields rl
1469 and param_implicit_field vis p =
1470 (* Building the implicit field (for example: private int $x;) *)
1471 let pos, name = p.param_id in
1472 let cvname = pos, class_var_name name in
1473 let member = ClassVars ([vis], p.param_hint, [cvname, None]) in
1474 (* Building the implicit assignment (for example: $this->x = $x;) *)
1475 let this = pos, "$this" in
1476 let stmt =
1477 Expr (pos, Binop (Eq None, (pos, Obj_get((pos, Lvar this),
1478 (pos, Id cvname))),
1479 (pos, Lvar p.param_id)))
1481 member, stmt
1483 (*****************************************************************************)
1484 (* Function/Method bodies. *)
1485 (*****************************************************************************)
1487 and function_body env =
1488 match L.token env.lb with
1489 | Tsc -> []
1490 | Tlcb ->
1491 (match env.mode with
1492 | Mdecl ->
1493 ignore_body env;
1494 (* This is a hack for the type-checker to make a distinction
1495 * Between function foo(); and function foo() {}
1497 [Noop]
1498 | _ ->
1499 (match statement_list env with
1500 | [] -> [Noop]
1501 | x -> x)
1503 | _ -> error_expect env "{"; []
1505 and ignore_body env =
1506 match L.token env.lb with
1507 | Tlcb -> ignore_body env; ignore_body env
1508 | Trcb -> ()
1509 | Tquote ->
1510 let pos = Pos.make env.lb in
1511 let abs_pos = env.lb.Lexing.lex_curr_pos in
1512 ignore (expr_string env pos abs_pos);
1513 ignore_body env
1514 | Tdquote ->
1515 let pos = Pos.make env.lb in
1516 ignore (expr_encapsed env pos);
1517 ignore_body env
1518 | Theredoc ->
1519 ignore (expr_heredoc env);
1520 ignore_body env
1521 | Tlt when is_xhp env ->
1522 ignore (xhp env);
1523 ignore_body env
1524 | Teof -> error_expect env "}"; ()
1525 | _ -> ignore_body env
1527 (*****************************************************************************)
1528 (* Statements *)
1529 (*****************************************************************************)
1531 and statement_list env =
1532 match L.token env.lb with
1533 | Trcb -> []
1534 | Tlcb ->
1535 let block = statement_list env in
1536 Block block :: statement_list env
1537 | Tsc ->
1538 statement_list env
1539 | Teof ->
1540 error_expect env "}";
1542 | _ ->
1543 L.back env.lb;
1544 let error_state = !(env.errors) in
1545 let stmt = statement env in
1546 if !(env.errors) != error_state
1547 then L.next_newline_or_close_cb env.lb;
1548 stmt :: statement_list env
1550 and statement env =
1551 match L.token env.lb with
1552 | Tword ->
1553 let word = Lexing.lexeme env.lb in
1554 let stmt = statement_word env word in
1555 stmt
1556 | Tlcb ->
1557 Block (statement_list env)
1558 | Tsc ->
1559 Noop
1560 | Tunsafe ->
1561 Unsafe
1562 | Tfallthrough ->
1563 Fallthrough
1564 | _ ->
1565 L.back env.lb;
1566 let e = expr env in
1567 expect env Tsc;
1568 Expr e
1570 and statement_word env = function
1571 | "break" -> statement_break env
1572 | "continue" -> statement_continue env
1573 | "throw" -> statement_throw env
1574 | "return" -> statement_return env
1575 | "static" -> statement_static env
1576 | "print" -> statement_echo env
1577 | "echo" -> statement_echo env
1578 | "if" -> statement_if env
1579 | "do" -> statement_do env
1580 | "while" -> statement_while env
1581 | "for" -> statement_for env
1582 | "switch" -> statement_switch env
1583 | "foreach" -> statement_foreach env
1584 | "try" -> statement_try env
1585 | "function" | "class" | "trait" | "interface" | "const"
1586 | "async" | "abstract" | "final" ->
1587 error env
1588 "Parse error: declarations are not supported outside global scope";
1589 ignore (ignore_toplevel SMap.empty [] env (fun _ -> true));
1590 Noop
1591 | x ->
1592 L.back env.lb;
1593 let e = expr env in
1594 expect env Tsc;
1595 Expr e
1597 (*****************************************************************************)
1598 (* Break statement *)
1599 (*****************************************************************************)
1601 and statement_break env =
1602 check_continue env;
1603 Break
1605 (*****************************************************************************)
1606 (* Continue statement *)
1607 (*****************************************************************************)
1609 and statement_continue env =
1610 check_continue env;
1611 Continue;
1613 and check_continue env =
1614 match L.token env.lb with
1615 | Tsc -> ()
1616 | Tint -> error_continue env
1617 | _ -> error_expect env ";"
1619 (*****************************************************************************)
1620 (* Throw statement *)
1621 (*****************************************************************************)
1623 and statement_throw env =
1624 let e = expr env in
1625 expect env Tsc;
1626 Throw e
1628 (*****************************************************************************)
1629 (* Return statement *)
1630 (*****************************************************************************)
1632 and statement_return env =
1633 let pos = Pos.make env.lb in
1634 let value = return_value env in
1635 Return (pos, value)
1637 and return_value env =
1638 match L.token env.lb with
1639 | Tsc -> None
1640 | _ ->
1641 L.back env.lb;
1642 let e = expr env in
1643 expect env Tsc;
1644 Some e
1646 (*****************************************************************************)
1647 (* Static variables *)
1648 (*****************************************************************************)
1650 and statement_static env =
1651 let pos = Pos.make env.lb in
1652 match L.token env.lb with
1653 | Tlvar ->
1654 L.back env.lb;
1655 let el = static_var_list env in
1656 Static_var el
1657 | _ ->
1658 L.back env.lb;
1659 let id = pos, Id (pos, "static") in
1660 let e = expr_remain env id in
1661 Expr e
1663 and static_var_list env =
1664 let error_state = !(env.errors) in
1665 let cst = static_var env in
1666 match L.token env.lb with
1667 | Tsc ->
1668 [cst]
1669 | Tcomma ->
1670 if !(env.errors) != error_state
1671 then [cst]
1672 else cst :: static_var_list_remain env
1673 | _ -> error_expect env ";"; [cst]
1675 and static_var_list_remain env =
1676 match L.token env.lb with
1677 | Tsc -> []
1678 | _ ->
1679 L.back env.lb;
1680 let error_state = !(env.errors) in
1681 let cst = static_var env in
1682 match L.token env.lb with
1683 | Tsc ->
1684 [cst]
1685 | Tcomma ->
1686 if !(env.errors) != error_state
1687 then [cst]
1688 else cst :: static_var_list_remain env
1689 | _ ->
1690 error_expect env ";"; [cst]
1692 and static_var env =
1693 expr env
1695 (*****************************************************************************)
1696 (* Switch statement *)
1697 (*****************************************************************************)
1699 and statement_switch env =
1700 let e = paren_expr env in
1701 expect env Tlcb;
1702 let casel = switch_body env in
1703 Switch (e, casel)
1705 (* switch(...) { _ } *)
1706 and switch_body env =
1707 match L.token env.lb with
1708 | Trcb ->
1710 | Tword ->
1711 let word = Lexing.lexeme env.lb in
1712 switch_body_word env word
1713 | _ ->
1714 error_expect env "}";
1717 and switch_body_word env = function
1718 | "case" ->
1719 let e = expr env in
1720 expect env Tcolon;
1721 let stl = case_body env in
1722 Case (e, stl) :: switch_body env
1723 | "default" ->
1724 expect env Tcolon;
1725 let stl = case_body env in
1726 Default stl :: switch_body env
1727 | _ -> error_expect env "case"; []
1729 (* switch(...) { case/default: _ } *)
1730 and case_body env =
1731 match L.token env.lb with
1732 | Tword ->
1733 (match Lexing.lexeme env.lb with
1734 | "case" | "default" -> L.back env.lb; []
1735 | _ ->
1736 L.back env.lb;
1737 let error_state = !(env.errors) in
1738 let st = statement env in
1739 if !(env.errors) != error_state
1740 then [st]
1741 else st :: case_body env
1743 | Trcb ->
1744 L.back env.lb; []
1745 | _ ->
1746 L.back env.lb;
1747 let error_state = !(env.errors) in
1748 let st = statement env in
1749 if !(env.errors) != error_state
1750 then [st]
1751 else st :: case_body env
1753 (*****************************************************************************)
1754 (* If statement *)
1755 (*****************************************************************************)
1757 and statement_if env =
1758 let e = paren_expr env in
1759 let st1 = statement env in
1760 let st2 = statement_else env in
1761 If (e, [st1], [st2])
1763 and statement_else env =
1764 match L.token env.lb with
1765 | Tword ->
1766 (match Lexing.lexeme env.lb with
1767 | "else" -> statement env
1768 | "elseif" -> statement_if env
1769 | _ -> L.back env.lb; Noop
1771 | _ -> L.back env.lb; Noop
1773 (*****************************************************************************)
1774 (* Do/While do statement *)
1775 (*****************************************************************************)
1777 and statement_do env =
1778 let st = statement env in
1779 expect_word env "while";
1780 let e = paren_expr env in
1781 expect env Tsc;
1782 Do ([st], e)
1784 and statement_while env =
1785 let e = paren_expr env in
1786 let st = statement env in
1787 While (e, [st])
1789 (*****************************************************************************)
1790 (* For statement *)
1791 (*****************************************************************************)
1793 and statement_for env =
1794 expect env Tlp;
1795 let start = Pos.make env.lb in
1796 let _ = L.token env.lb in
1797 let _ = L.back env.lb in
1798 let last, el = for_expr env in
1799 let e1 = Pos.btw start last, Expr_list el in
1800 let start = last in
1801 let last, el = for_expr env in
1802 let e2 = Pos.btw start last, Expr_list el in
1803 let start = last in
1804 let last, el = for_last_expr env in
1805 let e3 = Pos.btw start last, Expr_list el in
1806 let st = statement env in
1807 For (e1, e2, e3, [st])
1809 and for_expr env =
1810 match L.token env.lb with
1811 | Tsc ->
1812 Pos.make env.lb, []
1813 | _ ->
1814 L.back env.lb;
1815 let error_state = !(env.errors) in
1816 let e = expr env in
1817 match L.token env.lb with
1818 | Tsc ->
1819 Pos.make env.lb, [e]
1820 | _ when !(env.errors) != error_state ->
1821 L.back env.lb;
1822 Pos.make env.lb, [e]
1823 | Tcomma ->
1824 let last, el = for_expr env in
1825 last, e :: el
1826 | _ ->
1827 error_expect env ";";
1828 Pos.make env.lb, [e]
1830 and for_last_expr env =
1831 match L.token env.lb with
1832 | Trp ->
1833 Pos.make env.lb, []
1834 | _ ->
1835 L.back env.lb;
1836 let error_state = !(env.errors) in
1837 let e = expr env in
1838 match L.token env.lb with
1839 | Trp ->
1840 Pos.make env.lb, [e]
1841 | _ when !(env.errors) != error_state ->
1842 L.back env.lb;
1843 Pos.make env.lb, [e]
1844 | Tcomma ->
1845 let last, el = for_last_expr env in
1846 last, e :: el
1847 | _ ->
1848 error_expect env ")";
1849 Pos.make env.lb, [e]
1851 (*****************************************************************************)
1852 (* Foreach statement *)
1853 (*****************************************************************************)
1855 and statement_foreach env =
1856 expect env Tlp;
1857 let e = expr env in
1858 expect_word env "as";
1859 let as_expr = foreach_as env in
1860 let st = statement env in
1861 Foreach (e, as_expr, [st])
1863 and foreach_as env =
1864 let e1 = expr env in
1865 match L.token env.lb with
1866 | Tsarrow ->
1867 let e2 = expr env in
1868 expect env Trp;
1869 As_kv (e1, e2)
1870 | Trp ->
1871 As_id e1
1872 | _ ->
1873 error_expect env ")";
1874 As_id e1
1876 (*****************************************************************************)
1877 (* Try statement *)
1878 (*****************************************************************************)
1880 and statement_try env =
1881 let st = statement env in
1882 let cl = catch_list env in
1883 let fin = finally env in
1884 Try ([st], cl, fin)
1886 and catch_list env =
1887 match L.token env.lb with
1888 | Tword when Lexing.lexeme env.lb = "catch" ->
1889 expect env Tlp;
1890 let name = identifier env in
1891 let e = variable env in
1892 expect env Trp;
1893 let st = statement env in
1894 (name, e, [st]) :: catch_list env
1895 | _ -> L.back env.lb; []
1897 and finally env =
1898 match L.token env.lb with
1899 | Tword when Lexing.lexeme env.lb = "finally" ->
1900 let st = statement env in
1901 [st]
1902 | _ -> L.back env.lb; []
1904 (*****************************************************************************)
1905 (* Echo statement *)
1906 (*****************************************************************************)
1908 and statement_echo env =
1909 let pos = Pos.make env.lb in
1910 let args = echo_args env in
1911 let f = pos, Id (pos, "echo") in
1912 Expr (pos, Call (f, args))
1914 and echo_args env =
1915 let e = expr env in
1916 match L.token env.lb with
1917 | Tsc ->
1919 | Tcomma ->
1920 e :: echo_args env
1921 | _ ->
1922 error_expect env ";"; []
1924 (*****************************************************************************)
1925 (* Function/Method parameters *)
1926 (*****************************************************************************)
1928 and parameter_list env =
1929 expect env Tlp;
1930 parameter_list_remain env
1932 and parameter_list_remain env =
1933 match L.token env.lb with
1934 | Trp -> []
1935 | Tellipsis ->
1936 [parameter_varargs env]
1937 | _ ->
1938 L.back env.lb;
1939 let error_state = !(env.errors) in
1940 let p = param ~variadic:false env in
1941 match L.token env.lb with
1942 | Trp ->
1944 | Tellipsis ->
1945 [p ; parameter_varargs env]
1946 | Tcomma ->
1947 if !(env.errors) != error_state
1948 then [p]
1949 else p :: parameter_list_remain env
1950 | _ ->
1951 error_expect env ")"; [p]
1953 and parameter_varargs env =
1954 let pos = Pos.make env.lb in
1955 (match L.token env.lb with
1956 | Tcomma -> expect env Trp; make_param_ellipsis pos
1957 | Trp -> make_param_ellipsis pos;
1958 | _ ->
1959 L.back env.lb;
1960 let p = param ~variadic:true env in
1961 expect env Trp; p
1964 and make_param_ellipsis pos =
1965 { param_hint = None;
1966 param_is_reference = false;
1967 param_is_variadic = true;
1968 param_id = (pos, "...");
1969 param_expr = None;
1970 param_modifier = None;
1971 param_user_attributes = SMap.empty;
1974 and param ~variadic env =
1975 let attrs = attribute env in
1976 let modifs = parameter_modifier env in
1977 let h = parameter_hint env in
1978 let variadic_after_hint, name = ref_param env in
1979 assert ((not variadic_after_hint) || (not variadic));
1980 let variadic = variadic || variadic_after_hint in
1981 let default = parameter_default env in
1982 let default =
1983 if variadic && default <> None then
1984 let () = error env "Variadic arguments don't have default values" in
1985 None
1986 else default in
1987 if variadic_after_hint then begin
1988 expect env Trp;
1989 L.back env.lb
1990 end else ();
1991 { param_hint = h;
1992 param_is_reference = false;
1993 param_is_variadic = variadic;
1994 param_id = name;
1995 param_expr = default;
1996 param_modifier = modifs;
1997 param_user_attributes = attrs;
2000 and parameter_modifier env =
2001 match L.token env.lb with
2002 | Tword ->
2003 (match Lexing.lexeme env.lb with
2004 | "private" -> Some Private
2005 | "public" -> Some Public
2006 | "protected" -> Some Protected
2007 | _ -> L.back env.lb; None
2009 | _ -> L.back env.lb; None
2011 and parameter_hint env =
2012 if parameter_has_hint env
2013 then Some (hint env)
2014 else None
2016 and parameter_has_hint env =
2017 look_ahead env begin fun env ->
2018 match L.token env.lb with
2019 | Tellipsis | Tamp | Tlvar -> false
2020 | _ -> true
2023 and parameter_default env =
2024 match L.token env.lb with
2025 | Teq ->
2026 let default = expr env in
2027 Some default
2028 | _ -> L.back env.lb; None
2030 (*****************************************************************************)
2031 (* Expressions *)
2032 (*****************************************************************************)
2034 and expr env =
2035 let e1 = expr_atomic env in
2036 let e2 = expr_remain env e1 in
2039 and expr_list env =
2040 expect env Tlp;
2041 expr_list_remain env
2043 and expr_list_remain env =
2044 match L.token env.lb with
2045 | Trp -> []
2046 | _ ->
2047 L.back env.lb;
2048 let error_state = !(env.errors) in
2049 let e = expr { env with priority = 0 } in
2050 match L.token env.lb with
2051 | Trp ->
2053 | Tcomma ->
2054 if !(env.errors) != error_state
2055 then [e]
2056 else e :: expr_list_remain env
2057 | _ -> error_expect env ")"; [e]
2059 and expr_remain env e1 =
2060 match L.token env.lb with
2061 | Tplus ->
2062 expr_binop env Tplus Plus e1
2063 | Tminus ->
2064 expr_binop env Tminus Minus e1
2065 | Tstar ->
2066 expr_binop env Tstar Star e1
2067 | Tslash ->
2068 expr_binop env Tslash Slash e1
2069 | Teq ->
2070 expr_assign env Teq (Eq None) e1
2071 | Tbareq ->
2072 expr_assign env Tbareq (Eq (Some Bar)) e1
2073 | Tpluseq ->
2074 expr_assign env Tpluseq (Eq (Some Plus)) e1
2075 | Tstareq ->
2076 expr_assign env Tstareq (Eq (Some Star)) e1
2077 | Tslasheq ->
2078 expr_assign env Tslasheq (Eq (Some Slash)) e1
2079 | Tdoteq ->
2080 expr_assign env Tdoteq (Eq (Some Dot)) e1
2081 | Tminuseq ->
2082 expr_assign env Tminuseq (Eq (Some Minus)) e1
2083 | Tpercenteq ->
2084 expr_assign env Tpercenteq (Eq (Some Percent)) e1
2085 | Txoreq ->
2086 expr_assign env Txoreq (Eq (Some Xor)) e1
2087 | Tampeq ->
2088 expr_assign env Tampeq (Eq (Some Amp)) e1
2089 | Tlshifteq ->
2090 expr_assign env Tlshifteq (Eq (Some Ltlt)) e1
2091 | Trshifteq ->
2092 expr_assign env Trshifteq (Eq (Some Gtgt)) e1
2093 | Teqeqeq ->
2094 expr_binop env Teqeqeq EQeqeq e1
2095 | Tgt ->
2096 expr_binop env Tgt Gt e1
2097 | Tpercent ->
2098 expr_binop env Tpercent Percent e1
2099 | Tdot ->
2100 expr_binop env Tdot Dot e1
2101 | Teqeq ->
2102 expr_binop env Teqeq Eqeq e1
2103 | Tampamp ->
2104 expr_binop env Tampamp AMpamp e1
2105 | Tbarbar ->
2106 expr_binop env Tbarbar BArbar e1
2107 | Tdiff ->
2108 expr_binop env Tdiff Diff e1
2109 | Tlt ->
2110 expr_binop env Tlt Lt e1
2111 | Tdiff2 ->
2112 expr_binop env Tdiff2 Diff2 e1
2113 | Tgte ->
2114 expr_binop env Tgte Gte e1
2115 | Tlte ->
2116 expr_binop env Tlte Lte e1
2117 | Tamp ->
2118 expr_binop env Tamp Amp e1
2119 | Tbar ->
2120 expr_binop env Tbar Bar e1
2121 | Tltlt ->
2122 expr_binop env Tltlt Ltlt e1
2123 | Tgtgt ->
2124 expr_binop env Tgtgt Gtgt e1
2125 | Txor ->
2126 expr_binop env Txor Xor e1
2127 | Tincr | Tdecr as uop ->
2128 expr_postfix_unary env uop e1
2129 | Tarrow ->
2130 expr_arrow env e1
2131 | Tcolcol ->
2132 expr_colcol env e1
2133 | Tlp ->
2134 expr_call env e1
2135 | Tlb ->
2136 expr_array_get env e1
2137 | Tqm ->
2138 expr_if env e1
2139 | Tword when Lexing.lexeme env.lb = "instanceof" ->
2140 expr_instanceof env e1
2141 | Tword when Lexing.lexeme env.lb = "and" ->
2142 error env ("Do not use \"and\", it has surprising precedence. "^
2143 "Use \"&&\" instead");
2144 expr_binop env Tampamp AMpamp e1
2145 | Tword when Lexing.lexeme env.lb = "or" ->
2146 error env ("Do not use \"or\", it has surprising precedence. "^
2147 "Use \"||\" instead");
2148 expr_binop env Tbarbar BArbar e1
2149 | Tword when Lexing.lexeme env.lb = "xor" ->
2150 error env ("Do not use \"xor\", it has surprising precedence. "^
2151 "Cast to bool and use \"^\" instead");
2152 expr_binop env Txor Xor e1
2153 | _ ->
2154 L.back env.lb; e1
2156 (*****************************************************************************)
2157 (* Expression reducer *)
2158 (*****************************************************************************)
2160 and reduce env e1 op make =
2161 let e, continue = reduce_ env e1 op make in
2162 if continue then expr_remain env e else e
2164 and reduce_ env e1 op make =
2165 let current_prio = env.priority in
2166 let assoc, prio = get_priority op in
2167 let env = { env with priority = prio } in
2168 if prio = current_prio
2169 then
2170 match assoc with
2171 | Left ->
2172 let e = make e1 { env with priority = env.priority + 1 } in
2173 expr_remain env e, true
2174 | Right ->
2175 let e = make e1 env in
2176 e, false
2177 | NonAssoc ->
2178 error env "This operator is not associative, add parentheses";
2179 let e = make e1 env in
2180 e, false
2181 else if prio < current_prio
2182 then begin
2183 L.back env.lb;
2184 e1, false
2186 else begin
2187 assert (prio > current_prio);
2188 if assoc = NonAssoc
2189 then make e1 env, true
2190 else reduce_ env e1 op make
2193 (*****************************************************************************)
2194 (* lambda expressions *)
2195 (*****************************************************************************)
2197 and lambda_expr_body : env -> block = fun env ->
2198 let (p, e1) = expr env in
2199 [Return (p, (Some (p, e1)))]
2201 and lambda_body env params ret =
2202 let body =
2203 if peek env = Tlcb
2204 then function_body env
2205 else lambda_expr_body env
2207 let f = {
2208 f_name = (Pos.none, ";anonymous");
2209 f_tparams = [];
2210 f_params = params;
2211 f_ret = ret;
2212 f_body = body;
2213 f_user_attributes = Utils.SMap.empty;
2214 f_type = FSync;
2215 f_mode = env.mode;
2216 f_mtime = 0.0;
2217 f_namespace = Namespace_env.empty;
2219 in Lfun f;
2221 and make_lambda_param : id -> fun_param = fun var_id ->
2223 param_hint = None;
2224 param_is_reference = false;
2225 param_is_variadic = false;
2226 param_id = var_id;
2227 param_expr = None;
2228 param_modifier = None;
2229 param_user_attributes = Utils.SMap.empty;
2232 and lambda_single_arg env var_id =
2233 expect env Tlambda;
2234 lambda_body env [make_lambda_param var_id] None
2236 and try_short_lambda env =
2237 try_parse env begin fun env ->
2238 let error_state = !(env.errors) in
2239 let param_list = parameter_list_remain env in
2240 if !(env.errors) != error_state then begin
2241 env.errors := error_state;
2242 None
2243 end else begin
2244 let ret = hint_return_opt env in
2245 if !(env.errors) != error_state then begin
2246 env.errors := error_state;
2247 None
2248 end else if not (peek env = Tlambda)
2249 then None
2250 else begin
2251 drop env;
2252 Some (lambda_body env param_list ret)
2257 (*****************************************************************************)
2258 (* Expressions *)
2259 (*****************************************************************************)
2261 and expr_atomic ?(allow_class=false) env =
2262 let tok = L.token env.lb in
2263 let pos = Pos.make env.lb in
2264 match tok with
2265 | Tint ->
2266 let tok_value = Lexing.lexeme env.lb in
2267 pos, Int (pos, tok_value)
2268 | Tfloat ->
2269 let tok_value = Lexing.lexeme env.lb in
2270 pos, Float (pos, tok_value)
2271 | Tquote ->
2272 let absolute_pos = env.lb.Lexing.lex_curr_pos in
2273 expr_string env pos absolute_pos
2274 | Tdquote ->
2275 expr_encapsed env pos
2276 | Tlvar ->
2277 let tok_value = Lexing.lexeme env.lb in
2278 let var_id = (pos, tok_value) in
2279 pos, if peek env = Tlambda
2280 then lambda_single_arg env var_id
2281 else Lvar var_id
2282 | Tcolon ->
2283 L.back env.lb;
2284 let name = identifier env in
2285 fst name, Id name
2286 | Tem | Tincr | Tdecr | Ttild | Tplus | Tminus as op ->
2287 expr_prefix_unary env pos op
2288 | Tamp ->
2289 with_priority env Tref expr
2290 | Tat ->
2291 with_priority env Tat expr
2292 | Tword ->
2293 let word = Lexing.lexeme env.lb in
2294 expr_atomic_word ~allow_class env pos word
2295 | Tlp ->
2296 (match try_short_lambda env with
2297 | None ->
2298 if is_cast env
2299 then expr_cast env pos
2300 else with_base_priority env begin fun env ->
2301 let e = expr env in
2302 expect env Trp;
2303 let end_ = Pos.make env.lb in
2304 Pos.btw pos end_, snd e
2306 | Some l -> pos, l
2308 | Tlb ->
2309 expr_short_array env pos
2310 | Tlt when is_xhp env ->
2311 xhp env
2312 | Theredoc ->
2313 expr_heredoc env
2314 | Tdollar ->
2315 error env ("A valid variable name starts with a letter or underscore,"^
2316 "followed by any number of letters, numbers, or underscores");
2317 expr env
2318 | _ ->
2319 error_expect env "expression";
2320 pos, Null
2322 and expr_atomic_word ~allow_class env pos = function
2323 | "class" when not allow_class ->
2324 error_expect env "expression";
2325 pos, Null
2326 | "final" | "abstract" | "interface" | "trait" ->
2327 error_expect env "expression";
2328 pos, Null
2329 | "true" ->
2330 pos, True
2331 | "false" ->
2332 pos, False
2333 | "null" ->
2334 pos, Null
2335 | "array" ->
2336 expr_array env pos
2337 | "shape" ->
2338 expr_shape env pos
2339 | "new" ->
2340 expr_new env pos
2341 | "async" ->
2342 expr_anon_async env pos
2343 | "function" ->
2344 expr_anon_fun env pos ~sync:FSync
2345 | name when is_collection env ->
2346 expr_collection env pos name
2347 | "await" ->
2348 expr_await env pos
2349 | "yield" ->
2350 expr_yield env pos
2351 | "clone" ->
2352 expr_clone env pos
2353 | "list" ->
2354 expr_php_list env pos
2355 | "require" | "require_once" ->
2356 if env.mode = Ast.Mstrict
2357 then
2358 error env
2359 ("Parse error: require_once is supported only as a toplevel "^
2360 "declaration");
2361 let _ = expr env in
2362 pos, Null
2363 | x ->
2364 pos, Id (pos, x)
2366 (*****************************************************************************)
2367 (* Expressions in parens. *)
2368 (*****************************************************************************)
2370 and paren_expr env =
2371 with_base_priority env begin fun env ->
2372 expect env Tlp;
2373 let e = expr env in
2374 expect env Trp;
2378 (*****************************************************************************)
2379 (* Assignments (=, +=, -=, ...) *)
2380 (*****************************************************************************)
2382 and expr_assign env bop ast_bop e1 =
2383 reduce env e1 bop begin fun e1 env ->
2384 check_lvalue env e1;
2385 let e2 = expr { env with priority = 0 } in
2386 btw e1 e2, Binop (ast_bop, e1, e2)
2389 (*****************************************************************************)
2390 (* Binary operations (+, -, /, ...) *)
2391 (*****************************************************************************)
2393 and expr_binop env bop ast_bop e1 =
2394 reduce env e1 bop begin fun e1 env ->
2395 let e2 = expr env in
2396 btw e1 e2, Binop (ast_bop, e1, e2)
2399 (*****************************************************************************)
2400 (* Object Access ($obj->method) *)
2401 (*****************************************************************************)
2403 and expr_arrow env e1 =
2404 reduce env e1 Tarrow begin fun e1 env ->
2405 let e2 =
2406 match L.token env.lb with
2407 | Tword ->
2408 let name = Lexing.lexeme env.lb in
2409 let pos = Pos.make env.lb in
2410 pos, Id (pos, name)
2411 | _ -> L.back env.lb; expr env
2413 btw e1 e2, Obj_get (e1, e2)
2416 (*****************************************************************************)
2417 (* Class Access (ClassName::method_name) *)
2418 (*****************************************************************************)
2420 and expr_colcol env e1 =
2421 reduce env e1 Tcolcol begin fun e1 env ->
2422 (match e1 with
2423 | (_, Id cname) ->
2424 (* XYZ::class is OK ... *)
2425 expr_colcol_remain ~allow_class:true env e1 cname
2426 | pos, Lvar cname ->
2427 (* ... but get_class($x) should be used instead of $x::class *)
2428 expr_colcol_remain ~allow_class:false env e1 cname
2429 | pos, _ ->
2430 error_at env pos "Expected class name";
2435 and expr_colcol_remain ~allow_class env e1 cname =
2436 match expr_atomic env ~allow_class with
2437 | _, Lvar x ->
2438 btw e1 x, Class_get (cname, x)
2439 | _, Id x ->
2440 btw e1 x, Class_const (cname, x)
2441 | pos, _ ->
2442 error_at env pos "Expected identifier";
2445 (*****************************************************************************)
2446 (* Function call (foo(params)) *)
2447 (*****************************************************************************)
2449 and expr_call env e1 =
2450 reduce env e1 Tlp begin fun e1 env ->
2451 L.back env.lb;
2452 let args = expr_list env in
2453 let end_ = Pos.make env.lb in
2454 Pos.btw (fst e1) end_, Call (e1, args)
2457 (*****************************************************************************)
2458 (* Collections *)
2459 (*****************************************************************************)
2461 and is_collection env = peek env = Tlcb
2463 and expr_collection env pos name =
2464 if is_collection env
2465 then build_collection env pos name
2466 else pos, Id (pos, name)
2468 and build_collection env pos name =
2469 let name = pos, name in
2470 let fds = collection_field_list env in
2471 let end_ = Pos.make env.lb in
2472 Pos.btw pos end_, Collection (name, fds)
2474 and collection_field_list env =
2475 expect env Tlcb;
2476 collection_field_list_remain env
2478 and collection_field_list_remain env =
2479 match L.token env.lb with
2480 | Trcb -> []
2481 | _ ->
2482 L.back env.lb;
2483 let error_state = !(env.errors) in
2484 let fd = array_field env in
2485 match L.token env.lb with
2486 | Trcb ->
2487 [fd]
2488 | Tcomma ->
2489 if !(env.errors) != error_state
2490 then [fd]
2491 else fd :: collection_field_list_remain env
2492 | _ ->
2493 error_expect env "}"; []
2495 (*****************************************************************************)
2496 (* InstanceOf *)
2497 (*****************************************************************************)
2499 and expr_instanceof env e1 =
2500 reduce env e1 Tinstanceof begin fun e1 env ->
2501 let e2 = expr env in
2502 btw e1 e2, InstanceOf (e1, e2)
2505 (*****************************************************************************)
2506 (* Yield/Await *)
2507 (*****************************************************************************)
2509 and expr_yield env start =
2510 with_priority env Tyield begin fun env ->
2511 match L.token env.lb with
2512 | Tword when Lexing.lexeme env.lb = "break" ->
2513 let end_ = Pos.make env.lb in
2514 Pos.btw start end_, Yield_break
2515 | _ ->
2516 L.back env.lb;
2517 let e = expr env in
2518 Pos.btw start (fst e), Yield e
2521 and expr_await env start =
2522 with_priority env Tawait begin fun env ->
2523 let e = expr env in
2524 Pos.btw start (fst e), Await e
2527 (*****************************************************************************)
2528 (* Clone *)
2529 (*****************************************************************************)
2531 and expr_clone env start =
2532 with_base_priority env begin fun env ->
2533 let e = expr env in
2534 Pos.btw start (fst e), Clone e
2537 (*****************************************************************************)
2538 (* List *)
2539 (*****************************************************************************)
2541 and expr_php_list env start =
2542 let el = expr_list env in
2543 let end_ = Pos.make env.lb in
2544 Pos.btw start end_, List el
2546 (*****************************************************************************)
2547 (* Anonymous functions *)
2548 (*****************************************************************************)
2550 and is_function env =
2551 look_ahead env begin fun env ->
2552 let tok = L.token env.lb in
2553 tok = Tword &&
2554 Lexing.lexeme env.lb = "function"
2557 and expr_anon_async env pos =
2558 if is_function env
2559 then begin
2560 expect_word env "function";
2561 expr_anon_fun env pos ~sync:FAsync
2563 else pos, Id (pos, "async")
2565 and expr_anon_fun env pos ~sync =
2566 let env = { env with priority = 0 } in
2567 let params = parameter_list env in
2568 let ret = hint_return_opt env in
2569 let use = function_use env in
2570 let body = function_body env in
2571 let f = {
2572 f_name = (Pos.none, ";anonymous");
2573 f_tparams = [];
2574 f_params = params;
2575 f_ret = ret;
2576 f_body = body;
2577 f_user_attributes = Utils.SMap.empty;
2578 f_type = sync;
2579 f_mode = env.mode;
2580 f_mtime = 0.0;
2581 f_namespace = Namespace_env.empty;
2584 pos, Efun (f, use)
2586 (*****************************************************************************)
2587 (* Use (for functions) *)
2588 (*****************************************************************************)
2590 and function_use env =
2591 match L.token env.lb with
2592 | Tword when Lexing.lexeme env.lb = "use" ->
2593 expect env Tlp;
2594 use_list env
2595 | _ -> L.back env.lb; []
2597 and use_list env =
2598 match L.token env.lb with
2599 | Trp -> []
2600 | _ ->
2601 L.back env.lb;
2602 let error_state = !(env.errors) in
2603 let var = ref_variable env in
2604 match L.token env.lb with
2605 | Tcomma ->
2606 if !(env.errors) != error_state
2607 then [var]
2608 else var :: use_list env
2609 | Trp ->
2610 [var]
2611 | _ ->
2612 error_expect env ")";
2613 [var]
2615 (*****************************************************************************)
2616 (* New: new ClassName(...) *)
2617 (*****************************************************************************)
2619 and expr_new env pos_start =
2620 with_priority env Tnew begin fun env ->
2621 let cname =
2622 let e = expr env in
2623 match e with
2624 | p, Lvar _
2625 | p, Array_get _
2626 | p, Obj_get _
2627 | p, Class_get _
2628 | p, Call _ ->
2629 if env.mode = Ast.Mstrict
2630 then error env "Cannot use dynamic new in strict mode";
2631 p, "*Unknown*"
2632 | _, Id x -> x
2633 | p, _ ->
2634 error_expect env "class name";
2635 p, "*Unknown*"
2637 let args = expr_list env in
2638 let pos_end = Pos.make env.lb in
2639 Pos.btw pos_start pos_end, New (cname, args)
2642 (*****************************************************************************)
2643 (* Casts: (int|..|float) expr *)
2644 (*****************************************************************************)
2646 and is_cast_type = function
2647 | "int" | "float" | "double" | "string"
2648 | "array" | "object" | "bool" | "unset" -> true
2649 | _ -> false
2651 and is_cast env =
2652 look_ahead env begin fun env ->
2653 let _ = L.token env.lb in
2654 is_cast_type (Lexing.lexeme env.lb) &&
2655 L.token env.lb = Trp
2658 and expr_cast env start_pos =
2659 with_priority env Tcast begin fun env ->
2660 let tok = L.token env.lb in
2661 let cast_type = Lexing.lexeme env.lb in
2662 assert (tok = Tword);
2663 assert (is_cast_type cast_type);
2664 expect env Trp;
2665 let ty = Pos.make env.lb, cast_type in
2666 let ty = fst ty, Happly (ty, []) in
2667 let e = expr env in
2668 match cast_type with
2669 | "unset" -> e
2670 | _ -> Pos.btw start_pos (fst e), Cast (ty, e)
2673 (*****************************************************************************)
2674 (* Unary operators $i++ etc ... *)
2675 (*****************************************************************************)
2677 and unary_priority = function
2678 | Tplus | Tminus -> Tincr
2679 | x -> x
2681 and expr_prefix_unary env start op =
2682 with_priority env (unary_priority op) begin fun env ->
2683 let e = expr env in
2684 let op =
2685 match op with
2686 | Tem -> Unot
2687 | Tincr -> Uincr
2688 | Tdecr -> Udecr
2689 | Ttild -> Utild
2690 | Tplus -> Uplus
2691 | Tminus -> Uminus
2692 | _ -> assert false
2694 Pos.btw start (fst e), Unop (op, e)
2697 and expr_postfix_unary env uop e1 =
2698 let end_ = Pos.make env.lb in
2699 let e =
2700 reduce env e1 (unary_priority uop) begin fun e1 env ->
2701 let op =
2702 match uop with
2703 | Tincr -> Upincr
2704 | Tdecr -> Updecr
2705 | _ -> assert false
2707 Pos.btw (fst e1) end_, Unop (op, e1)
2710 let x = L.token env.lb in
2711 if x = uop
2712 then expr_remain env e
2713 else (L.back env.lb; expr_remain env e)
2715 (*****************************************************************************)
2716 (* If expression: _?_:_ *)
2717 (*****************************************************************************)
2719 and is_colon_if env =
2720 look_ahead env begin fun env ->
2721 let tok = L.token env.lb in
2722 tok = Tcolon &&
2723 (* At this point, we might still be dealing with an xhp identifier *)
2724 L.no_space_id env.lb <> Tword
2727 and expr_if env e1 =
2728 reduce env e1 Tqm begin fun e1 env ->
2729 if is_colon_if env
2730 then colon_if env e1
2731 else ternary_if env e1
2734 and ternary_if env e1 =
2735 let e2 = expr { env with priority = 0 } in
2736 expect env Tcolon;
2737 let e3 = expr env in
2738 (match e1 with
2739 | pos, Eif _ ->
2740 error_at env pos "You should add parentheses"
2741 | _ -> ());
2742 Pos.btw (fst e1) (fst e3), Eif (e1, Some e2, e3)
2744 and colon_if env e1 =
2745 expect env Tcolon;
2746 let e2 = expr env in
2747 Pos.btw (fst e1) (fst e2), Eif (e1, None, e2)
2750 (*****************************************************************************)
2751 (* Strings *)
2752 (*****************************************************************************)
2754 and expr_string env start abs_start =
2755 match L.string env.lb with
2756 | Tquote ->
2757 let pos = Pos.btw start (Pos.make env.lb) in
2758 let len = env.lb.Lexing.lex_curr_pos - abs_start - 1 in
2759 let content = String.sub env.lb.Lexing.lex_buffer abs_start len in
2760 pos, String (pos, content)
2761 | Teof ->
2762 error_at env start "string not closed";
2763 start, String (start, "")
2764 | _ -> assert false
2766 and expr_encapsed env start =
2767 let abs_start = env.lb.Lexing.lex_curr_pos in
2768 let pos_start = Pos.make env.lb in
2769 let el = encapsed_nested pos_start env in
2770 let pos_end = Pos.make env.lb in
2771 let pos = Pos.btw pos_start pos_end in
2772 let len = env.lb.Lexing.lex_curr_pos - abs_start - 1 in
2773 let content = String.sub env.lb.Lexing.lex_buffer abs_start len in
2774 pos, String2 (el, (pos, content))
2776 and encapsed_nested start env =
2777 match L.string2 env.lb with
2778 | Tdquote ->
2780 | Teof ->
2781 error_at env start "string not properly closed";
2783 | Tlcb when env.mode = Ast.Mdecl ->
2784 encapsed_nested start env
2785 | Tlcb ->
2786 (match L.string2 env.lb with
2787 | Tdollar ->
2788 error env "{ not supported";
2789 L.back env.lb;
2790 encapsed_nested start env
2791 | Tlvar ->
2792 L.back env.lb;
2793 let error_state = !(env.errors) in
2794 let e = encapsed_expr env in
2795 (match L.string2 env.lb with
2796 | Trcb -> ()
2797 | _ -> error_expect env "}");
2798 if !(env.errors) != error_state
2799 then [e]
2800 else e :: encapsed_nested start env
2801 | _ ->
2802 L.back env.lb;
2803 encapsed_nested start env
2805 | Trcb ->
2806 encapsed_nested start env
2807 | Tdollar ->
2808 (match L.string2 env.lb with
2809 | Tlcb ->
2810 if env.mode = Ast.Mstrict
2811 then error env "${ not supported";
2812 let error_state = !(env.errors) in
2813 let result = (match L.string2 env.lb with
2814 | Tword ->
2815 (* The first token after ${ will lex as a word, but is actually
2816 * an lvar, so we need to fix it up. For example, "${foo}" should
2817 * be Lvar $foo, but will lex as Tdollar-Tlcb-Tword foo. *)
2818 let pos = Pos.make env.lb in
2819 let lvar = pos, Lvar (pos, "$" ^ Lexing.lexeme env.lb) in
2820 encapsed_expr_reduce pos env lvar
2821 | _ ->
2822 error_expect env "variable";
2823 Pos.make env.lb, Null) in
2824 expect env Trcb;
2825 if !(env.errors) != error_state
2826 then [result]
2827 else result :: encapsed_nested start env
2828 | _ ->
2829 L.back env.lb;
2830 encapsed_nested start env
2832 | Tlvar ->
2833 L.back env.lb;
2834 let error_state = !(env.errors) in
2835 let e = encapsed_expr env in
2836 if !(env.errors) != error_state
2837 then [e]
2838 else e :: encapsed_nested start env
2839 | _ -> encapsed_nested start env
2841 and encapsed_expr env =
2842 match L.string2 env.lb with
2843 | Tlcb when env.mode = Ast.Mdecl ->
2844 Pos.make env.lb, Null
2845 | Tquote ->
2846 let pos = Pos.make env.lb in
2847 let absolute_pos = env.lb.Lexing.lex_curr_pos in
2848 expr_string env pos absolute_pos
2849 | Tint ->
2850 let pos = Pos.make env.lb in
2851 let tok_value = Lexing.lexeme env.lb in
2852 pos, Int (pos, tok_value)
2853 | Tword ->
2854 let pid = Pos.make env.lb in
2855 let id = Lexing.lexeme env.lb in
2856 pid, (Id (pid, id))
2857 | Tlvar ->
2858 let pos = Pos.make env.lb in
2859 let lvar = pos, Lvar (pos, Lexing.lexeme env.lb) in
2860 encapsed_expr_reduce pos env lvar
2861 | _ ->
2862 error_expect env "expression";
2863 Pos.make env.lb, Null
2865 and encapsed_expr_reduce start env e1 =
2866 let e1, continue = encapsed_expr_reduce_left start env e1 in
2867 if continue
2868 then encapsed_expr_reduce start env e1
2869 else e1
2871 and encapsed_expr_reduce_left start env e1 =
2872 match L.string2 env.lb with
2873 | Tlb ->
2874 let e2 =
2875 match L.string2 env.lb with
2876 | Tword ->
2877 (* We need to special case this because any identifier
2878 * (including keywords) is allowed in this context.
2879 * For example: $x[function] is legal.
2881 let pid = Pos.make env.lb in
2882 let id = Lexing.lexeme env.lb in
2883 pid, (Id (pid, id))
2884 | _ ->
2885 L.back env.lb;
2886 expr { env with priority = 0 }
2888 (match L.string2 env.lb with
2889 | Trb -> ()
2890 | _ -> error_expect env "]"
2892 let pos = Pos.btw start (Pos.make env.lb) in
2893 (pos, Array_get (e1, Some e2)), true
2894 | Tarrow ->
2895 (match L.string2 env.lb with
2896 | Tword ->
2897 L.back env.lb;
2898 let e2 = encapsed_expr env in
2899 let pos = Pos.btw start (Pos.make env.lb) in
2900 (pos, Obj_get (e1, e2)), true
2901 | _ ->
2902 L.back env.lb;
2903 e1, false
2905 | _ ->
2906 L.back env.lb;
2907 e1, false
2909 (*****************************************************************************)
2910 (* Heredocs *)
2911 (*****************************************************************************)
2913 and expr_heredoc env =
2914 let abs_start = env.lb.Lexing.lex_curr_pos in
2915 let tag = heredoc_tag env in
2916 heredoc_body tag env;
2917 let len = env.lb.Lexing.lex_curr_pos - abs_start - 1 in
2918 let content = String.sub env.lb.Lexing.lex_buffer abs_start len in
2919 fst tag, String (fst tag, content)
2921 and heredoc_tag env =
2922 match L.token env.lb with
2923 | Tword ->
2924 Pos.make env.lb, Lexing.lexeme env.lb
2925 | Tquote ->
2926 let pos = Pos.make env.lb in
2927 let abs_pos = env.lb.Lexing.lex_curr_pos in
2928 (match expr_string env pos abs_pos with
2929 | _, String x -> x
2930 | _ -> assert false)
2931 | _ ->
2932 error_expect env "heredoc or nowdoc identifier";
2933 Pos.make env.lb, "HEREDOC"
2935 and heredoc_body (pos, tag_value as tag) env =
2936 match L.heredoc_token env.lb with
2937 | Tnewline ->
2938 heredoc_end tag env
2939 | Teof ->
2940 error_expect env tag_value
2941 | _ ->
2942 heredoc_body tag env
2944 and heredoc_end (pos, tag_value as tag) env =
2945 match L.heredoc_token env.lb with
2946 | Tword ->
2947 let tag2 = Lexing.lexeme env.lb in
2948 (match L.heredoc_token env.lb with
2949 | Tnewline when tag2 = tag_value ->
2951 | Tnewline ->
2952 heredoc_end tag env
2953 | Tsc when tag2 = tag_value ->
2954 L.back env.lb;
2956 | _ ->
2957 heredoc_body tag env
2959 | Tnewline ->
2960 heredoc_end tag env
2961 | _ ->
2962 heredoc_body tag env
2965 (*****************************************************************************)
2966 (* Arrays *)
2967 (*****************************************************************************)
2969 and expr_array env pos =
2970 let fields = array_field_list env in
2971 pos, Array fields
2973 and array_field_list env =
2974 expect env Tlp;
2975 array_field_list_remain env Trp []
2977 and expr_short_array env pos =
2978 let fields = array_field_list_remain env Trb [] in
2979 pos, Array fields
2981 and array_field_list_remain env terminal acc =
2982 match L.token env.lb with
2983 | x when x = terminal -> List.rev acc
2984 | _ ->
2985 L.back env.lb;
2986 let error_state = !(env.errors) in
2987 let fd = array_field env in
2988 let acc = fd :: acc in
2989 match L.token env.lb with
2990 | x when x = terminal ->
2991 List.rev acc
2992 | Tcomma ->
2993 if !(env.errors) != error_state
2994 then List.rev acc
2995 else array_field_list_remain env terminal acc
2996 | _ -> error_expect env ")"; [fd]
2998 and array_field env =
2999 let env = { env with priority = 0 } in
3000 let e1 = expr env in
3001 match L.token env.lb with
3002 | Tsarrow ->
3003 let e2 = expr env in
3004 AFkvalue (e1, e2)
3005 | _ ->
3006 L.back env.lb;
3007 AFvalue e1
3009 (*****************************************************************************)
3010 (* Shapes *)
3011 (*****************************************************************************)
3013 and expr_shape env pos =
3014 let fields = shape_field_list env in
3015 pos, Shape fields
3017 and shape_field_list env =
3018 expect env Tlp;
3019 shape_field_list_remain env
3021 and shape_field_list_remain env =
3022 match L.token env.lb with
3023 | Trp -> []
3024 | _ ->
3025 L.back env.lb;
3026 let error_state = !(env.errors) in
3027 let fd = shape_field env in
3028 match L.token env.lb with
3029 | Trp ->
3030 [fd]
3031 | Tcomma ->
3032 if !(env.errors) != error_state
3033 then [fd]
3034 else fd :: shape_field_list_remain env
3035 | _ -> error_expect env ")"; [fd]
3037 and shape_field env =
3038 let name = shape_field_name env in
3039 expect env Tsarrow;
3040 let value = expr { env with priority = 0 } in
3041 name, value
3043 and shape_field_name env =
3044 let pos, e = expr env in
3045 match e with
3046 | String p -> SFlit p
3047 | Class_const (id, ps) -> SFclass_const (id, ps)
3048 | _ -> error_expect env "string literal or class constant";
3049 SFlit (pos, "")
3052 (*****************************************************************************)
3053 (* Array access ($my_array[]|$my_array[_]) *)
3054 (*****************************************************************************)
3056 and expr_array_get env e1 =
3057 reduce env e1 Tlb begin fun e1 env ->
3058 match L.token env.lb with
3059 | Trb ->
3060 let end_ = Pos.make env.lb in
3061 Pos.btw (fst e1) end_, Array_get (e1, None)
3062 | _ ->
3063 L.back env.lb;
3064 let e2 = expr { env with priority = 0 } in
3065 expect env Trb;
3066 let end_ = Pos.make env.lb in
3067 Pos.btw (fst e1) end_, Array_get (e1, Some e2)
3071 (*****************************************************************************)
3072 (* XHP *)
3073 (*****************************************************************************)
3075 and is_xhp env =
3076 look_ahead env begin fun env ->
3077 let tok = L.xhpname env.lb in
3078 tok = Txhpname &&
3079 let tok2 = L.xhpattr env.lb in
3080 tok2 = Tgt || tok2 = Tword ||
3081 (tok2 = Tslash && L.xhpattr env.lb = Tgt)
3084 and xhp env =
3085 match L.xhpname env.lb with
3086 | Txhpname ->
3087 let start = Pos.make env.lb in
3088 let name = Lexing.lexeme env.lb in
3089 let pname = start, ":"^name in
3090 let attrl, closed = xhp_attributes env in
3091 let end_tag = Pos.make env.lb in
3092 if closed
3093 then Pos.btw start end_tag, Xml (pname, attrl, [])
3094 else
3095 let tag_pos = Pos.btw start end_tag in
3096 let el = xhp_body tag_pos name env in
3097 let end_ = Pos.make env.lb in
3098 Pos.btw start end_, Xml (pname, attrl, el)
3099 | _ ->
3100 error_expect env "xhpname";
3101 let pos = Pos.make env.lb in
3102 pos, Xml ((pos, "xhp"), [], [])
3104 and xhp_attributes env =
3105 match L.xhpattr env.lb with
3106 | Tslash ->
3107 if L.xhpattr env.lb <> Tgt
3108 then error_expect env ">";
3109 [], true
3110 | Tgt ->
3111 [], false
3112 | Tword ->
3113 let error_state = !(env.errors) in
3114 let attr_name = Pos.make env.lb, Lexing.lexeme env.lb in
3115 expect env Teq;
3116 let attr_value = xhp_attribute_value env in
3117 if !(env.errors) != error_state
3118 then
3119 [attr_name, attr_value], true
3120 else
3121 let rl, closed = xhp_attributes env in
3122 (attr_name, attr_value) :: rl, closed
3123 | _ ->
3124 error_expect env ">";
3125 [], true
3127 and xhp_attribute_value env =
3128 match L.xhpattr env.lb with
3129 | Tlcb when env.mode = Ast.Mdecl ->
3130 ignore_body env;
3131 Pos.none, Null
3132 | Tlcb ->
3133 let result = expr { env with priority = 0 } in
3134 expect env Trcb;
3135 result
3136 | Tdquote ->
3137 let start = Pos.make env.lb in
3138 let abs_start = env.lb.Lexing.lex_curr_pos in
3139 xhp_attribute_string env start abs_start
3140 | _ ->
3141 error_expect env "attribute value";
3142 let pos = Pos.make env.lb in
3143 pos, String (pos, "")
3145 and xhp_attribute_string env start abs_start =
3146 match L.string2 env.lb with
3147 | Teof ->
3148 error_at env start "Xhp attribute not closed";
3149 start, String (start, "")
3150 | Tdquote ->
3151 let len = env.lb.Lexing.lex_curr_pos - abs_start - 1 in
3152 let content = String.sub env.lb.Lexing.lex_buffer abs_start len in
3153 let pos = Pos.btw start (Pos.make env.lb) in
3154 pos, String (pos, content)
3155 | _ ->
3156 xhp_attribute_string env start abs_start
3158 and xhp_body pos name env =
3159 match L.xhptoken env.lb with
3160 | Tlcb when env.mode = Ast.Mdecl ->
3161 ignore_body env;
3162 xhp_body pos name env
3163 | Tlcb ->
3164 let error_state = !(env.errors) in
3165 let e = expr { env with priority = 0 } in
3166 expect env Trcb;
3167 if !(env.errors) != error_state
3168 then [e]
3169 else e :: xhp_body pos name env
3170 | Tlt ->
3171 if is_xhp env
3172 then
3173 (match xhp env with
3174 | (_, Xml (_, _, _)) as xml ->
3175 xml :: xhp_body pos name env
3176 | _ -> xhp_body pos name env)
3177 else
3178 (match L.xhptoken env.lb with
3179 | Tslash ->
3180 let closing_tok = L.xhpname env.lb in
3181 let closing_name = Lexing.lexeme env.lb in
3182 if closing_tok = Txhpname &&
3183 (L.xhptoken env.lb = Tgt)
3184 then
3185 if closing_name = name
3186 then []
3187 else begin
3188 error_expect env name;
3191 else xhp_body pos name env
3192 | _ ->
3193 L.back env.lb;
3194 xhp_body pos name env
3196 | Teof ->
3197 error_at env pos "Xhp tag not closed";
3199 | Tword ->
3200 xhp_body pos name env
3201 | _ -> xhp_body pos name env
3203 (*****************************************************************************)
3204 (* Typedefs *)
3205 (*****************************************************************************)
3207 and typedef env =
3208 let id = identifier env in
3209 let tparams = class_params env in
3210 let tconstraint = typedef_constraint env in
3211 expect env Teq;
3212 let td = typedef_body env in
3213 expect env Tsc;
3214 id, tparams, tconstraint, td
3216 and typedef_constraint env =
3217 match L.token env.lb with
3218 | Tword when Lexing.lexeme env.lb = "as" ->
3219 Some (hint env)
3220 | _ ->
3221 L.back env.lb;
3222 None
3224 and typedef_body env =
3225 match L.token env.lb with
3226 | Tword when Lexing.lexeme env.lb = "shape" ->
3227 let pos = Pos.make env.lb in
3228 pos, Hshape (typedef_shape_field_list env)
3229 | _ -> L.back env.lb; hint env
3231 and typedef_shape_field_list env =
3232 expect env Tlp;
3233 typedef_shape_field_list_remain env
3235 and typedef_shape_field_list_remain env =
3236 match L.token env.lb with
3237 | Trp -> []
3238 | _ ->
3239 L.back env.lb;
3240 let error_state = !(env.errors) in
3241 let fd = typedef_shape_field env in
3242 match L.token env.lb with
3243 | Trp ->
3244 [fd]
3245 | Tcomma ->
3246 if !(env.errors) != error_state
3247 then [fd]
3248 else fd :: typedef_shape_field_list_remain env
3249 | _ ->
3250 error_expect env ")";
3251 [fd]
3253 and typedef_shape_field env =
3254 let name = shape_field_name env in
3255 expect env Tsarrow;
3256 let ty = hint env in
3257 name, ty
3259 (*****************************************************************************)
3260 (* Namespaces *)
3261 (*****************************************************************************)
3263 and namespace env =
3264 (* The safety of the recursive calls here is slightly subtle. Normally, we
3265 * check for errors when making a recursive call to make sure we don't get
3266 * stuck in a loop. Here, we actually don't need to do that, since the only
3267 * time we make a recursive call is when we see (and thus consume) a token
3268 * that we like. So every time we recurse we'll consume at least one token,
3269 * so we can't get stuck in an infinite loop. *)
3270 let tl = match env.mode with
3271 | Ast.Mdecl -> ignore_toplevel ~attr:SMap.empty
3272 | _ -> toplevel in
3273 (* The name for a namespace is actually optional, so we need to check for
3274 * the name first. Setting the name to an empty string if there's no
3275 * identifier following the `namespace` token *)
3276 let id = match L.token env.lb with
3277 | Tword -> L.back env.lb; identifier env
3278 | _ -> L.back env.lb; Pos.make env.lb, "" in
3279 match L.token env.lb with
3280 | Tlcb ->
3281 let body = tl [] env (fun x -> x = Trcb) in
3282 expect env Trcb;
3283 id, body
3284 | Tsc when (snd id) = "" ->
3285 error_expect env "{";
3286 id, []
3287 | Tsc ->
3288 let terminate = function
3289 | Tword -> Lexing.lexeme env.lb = "namespace"
3290 | Teof -> true
3291 | _ -> false in
3292 let body = tl [] env terminate in
3293 id, body
3294 | _ ->
3295 error_expect env "{ or ;";
3296 id, []
3298 and namespace_use_list env acc =
3299 let p1, s1 = identifier env in
3300 let id1 = p1, if s1.[0] = '\\' then s1 else "\\" ^ s1 in
3301 let id2 =
3302 match L.token env.lb with
3303 | Tword when Lexing.lexeme env.lb = "as" ->
3304 identifier env
3305 | _ ->
3306 L.back env.lb;
3307 let str = snd id1 in
3308 let start = try (String.rindex str '\\') + 1 with Not_found -> 0 in
3309 let len = (String.length str) - start in
3310 fst id1, String.sub str start len
3312 let acc = (id1, id2) :: acc in
3313 match L.token env.lb with
3314 | Tsc -> acc
3315 | Tcomma -> namespace_use_list env acc
3316 | _ ->
3317 error_expect env "Namespace use list";
3320 (*****************************************************************************)
3321 (* Helper *)
3322 (*****************************************************************************)
3324 let from_file filename =
3325 Pos.file := filename;
3326 let content = try Utils.cat filename with _ -> "" in
3327 program content