Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Safe call operators ?. #2142

Closed
wants to merge 12 commits into from
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ _esybuild
_esyinstall
.merlin
_release
**/*.swp
**/*.swo
Binary file added src/reason-parser/.reason_parser.mly.swo
Binary file not shown.
Binary file added src/reason-parser/.reason_parser.mly.swp
Binary file not shown.
2 changes: 2 additions & 0 deletions src/reason-parser/reason_lexer.mll
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,8 @@ rule token = parse
| "#=" { SHARPEQUAL }
| "#" operator_chars+
{ SHARPOP(lexeme_operator lexbuf) }
| "?" operator_chars+
{ OPTIONALACCESS(lexeme_operator lexbuf) }
| "#" [' ' '\t']* (['0'-'9']+ as num) [' ' '\t']*
("\"" ([^ '\010' '\013' '"' ] * as name) "\"")?
[^ '\010' '\013'] * newline
Expand Down
83 changes: 83 additions & 0 deletions src/reason-parser/reason_parser.mly
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,7 @@ let package_type_of_module_type pmty =
%token SEMISEMI
%token SHARP
%token <string> SHARPOP
%token <string> OPTIONALACCESS
%token SHARPEQUAL
%token SIG
%token STAR
Expand Down Expand Up @@ -1386,6 +1387,8 @@ conflicts.
(* PREFIXOP and BANG precedence *)
%nonassoc below_DOT_AND_SHARP (* practically same as below_SHARP but we convey purpose *)
%nonassoc SHARP (* simple_expr/toplevel_directive *)

%right OPTIONALACCESS
%nonassoc below_DOT

(* We need SHARPEQUAL to have lower precedence than `[` to make e.g.
Expand All @@ -1405,6 +1408,7 @@ conflicts.
(* Entry points *)

%start implementation (* for implementation files *)

%type <Ast_404.Parsetree.structure> implementation
%start interface (* for interface files *)
%type <Ast_404.Parsetree.signature> interface
Expand Down Expand Up @@ -3138,6 +3142,84 @@ parenthesized_expr:
{ mkexp (Pexp_send($1, $3)) }
| E as_loc(SHARPOP) simple_expr_no_call
{ mkinfixop $1 (mkoperator $2) $3 }
(*
* let res = a?.b;
*
* --generates-->
*
* let res = switch a {
* | Some(a) => Some(a.b)
* | None => None
* };
*
* let res = c?.+d;
*
* -generates->
*
* let res = switch c {
* | Some(c) => c.d <-- assuming d is optional
* | None => None
* };
*
* Same goes for ?# and ?#+ for JS objects
*)
| E OPTIONALACCESS simple_expr_no_call
{
let loc_exp = mklocation $startpos($1) $endpos($1) in
let record_name = match $1.pexp_desc with Pexp_ident({txt = Lident(str); loc;}) -> str | _ -> "x" in
let prop_name = match $3.pexp_desc with
| Pexp_ident({txt = Lident(str); loc;}) -> str
| Pexp_match({pexp_desc = Pexp_ident({txt = Lident(str); loc;})}, _) -> str
| _ -> syntax_error ()
in
let access_property = match $2 with
| "?." | "?.+" ->
mkexp (Pexp_field (
mkexp (Pexp_ident({
txt = Lident(record_name);
loc = loc_exp;
})), {
txt = Lident(prop_name);
loc = loc_exp;
}))
| "?#" | "?#+" ->
mkexp (Pexp_apply(
mkexp (Pexp_ident({
txt = Lident("##");
loc = loc_exp;
})), [
Nolabel, mkexp (Pexp_ident({ txt = Lident(record_name); loc = loc_exp}));
Nolabel, mkexp (Pexp_ident({ txt = Lident(prop_name); loc = loc_exp }));
]
))
| _ -> syntax_error ()
in
let access_property_exp = match $2 with
| "?." | "?#" -> mkexp (Pexp_construct ({txt = Lident("Some"); loc = loc_exp}, Some(access_property)))
| "?.+" | "?#+" -> access_property
| _ -> syntax_error ()
in
let some_exp = match $3.pexp_desc with
| Pexp_ident(_) -> access_property_exp
| Pexp_match(_, _) -> mkexp (Pexp_let(Nonrecursive, [{
pvb_pat = mkpat (Ppat_var(mkloc prop_name loc_exp));
pvb_expr = access_property_exp;
pvb_attributes = [];
pvb_loc = loc_exp;
}], $3));
| _ -> syntax_error () in
let some_pat =
Pat.mk ~loc:loc_exp (Ppat_construct({ txt = Lident("Some"); loc = loc_exp}, Some(mkpat (Ppat_var(mkloc record_name loc_exp))))) in
let none_pat =
Pat.mk ~loc:loc_exp (Ppat_construct({ txt = Lident("None"); loc = loc_exp;}, None)) in
let some_case =
Exp.case some_pat some_exp
in
let none_case =
Exp.case none_pat (mkexp (Pexp_construct({ txt = Lident("None"); loc = loc_exp;}, None)));
in
mkexp (Pexp_match ($1, [some_case; none_case]))
}
| E as_loc(SHARPEQUAL) simple_expr
{ let op = { $2 with txt = "#=" } in
mkinfixop $1 (mkoperator op) $3 }
Expand Down Expand Up @@ -3758,6 +3840,7 @@ mark_position_pat

| LPAREN COLONCOLON RPAREN LPAREN pattern_without_or COMMA pattern_without_or RPAREN
{ let loc_coloncolon = mklocation $startpos($2) $endpos($2) in

let loc = mklocation $symbolstartpos $endpos in
mkpat_cons loc_coloncolon (mkpat ~ghost:true ~loc (Ppat_tuple[$5;$7])) loc
}
Expand Down