Skip to content

Commit d08c906

Browse files
committed
spin off pexp_apply handling
1 parent f30e3ce commit d08c906

File tree

13 files changed

+880
-447
lines changed

13 files changed

+880
-447
lines changed

jscomp/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ SYNTAX_SRCS= \
255255
external_ffi_types\
256256
external_process\
257257
ast_util\
258+
ast_exp_apply\
258259
ast_exp_extension\
259260
ast_core_type_class_type\
260261
ast_tuple_pattern_flatten\

jscomp/all.depend

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ syntax/ast_util.cmx : ext/literals.cmx syntax/external_process.cmx \
239239
syntax/ast_literal.cmx syntax/ast_external_mk.cmx syntax/ast_exp.cmx \
240240
syntax/ast_core_type.cmx syntax/ast_comb.cmx syntax/ast_attributes.cmx \
241241
syntax/ast_util.cmi
242+
syntax/ast_exp_apply.cmx : ext/literals.cmx ext/ext_list.cmx \
243+
syntax/bs_ast_mapper.cmx syntax/bs_ast_invariant.cmx syntax/ast_util.cmx \
244+
syntax/ast_literal.cmx syntax/ast_attributes.cmx syntax/ast_exp_apply.cmi
242245
syntax/ast_exp_extension.cmx : ext/literals.cmx ext/ext_string.cmx \
243246
ext/ext_ref.cmx syntax/bs_ast_mapper.cmx syntax/ast_util.cmx \
244247
syntax/ast_payload.cmx syntax/ast_literal.cmx syntax/ast_derive.cmx \
@@ -254,11 +257,12 @@ syntax/ppx_entry.cmx : ext/string_map.cmx ext/literals.cmx \
254257
syntax/bs_ast_mapper.cmx syntax/bs_ast_invariant.cmx syntax/ast_util.cmx \
255258
syntax/ast_utf8_string_interp.cmx syntax/ast_utf8_string.cmx \
256259
syntax/ast_tuple_pattern_flatten.cmx syntax/ast_structure.cmx \
257-
syntax/ast_signature.cmx syntax/ast_payload.cmx syntax/ast_literal.cmx \
258-
syntax/ast_exp_extension.cmx syntax/ast_derive_projector.cmx \
259-
syntax/ast_derive_js_mapper.cmx syntax/ast_derive_abstract.cmx \
260-
syntax/ast_derive.cmx syntax/ast_core_type_class_type.cmx \
261-
syntax/ast_attributes.cmx syntax/ppx_entry.cmi
260+
syntax/ast_signature.cmx syntax/ast_payload.cmx \
261+
syntax/ast_exp_extension.cmx syntax/ast_exp_apply.cmx \
262+
syntax/ast_derive_projector.cmx syntax/ast_derive_js_mapper.cmx \
263+
syntax/ast_derive_abstract.cmx syntax/ast_derive.cmx \
264+
syntax/ast_core_type_class_type.cmx syntax/ast_attributes.cmx \
265+
syntax/ppx_entry.cmi
262266
syntax/bs_syntaxerr.cmi :
263267
syntax/ast_utf8_string.cmi :
264268
syntax/ast_utf8_string_interp.cmi :
@@ -289,6 +293,7 @@ syntax/external_ffi_types.cmi : syntax/external_arg_spec.cmi
289293
syntax/external_process.cmi : common/bs_loc.cmi syntax/ast_core_type.cmi \
290294
syntax/ast_attributes.cmi
291295
syntax/ast_util.cmi : syntax/bs_ast_mapper.cmi syntax/ast_payload.cmi
296+
syntax/ast_exp_apply.cmi : syntax/bs_ast_mapper.cmi
292297
syntax/ast_exp_extension.cmi : syntax/bs_ast_mapper.cmi
293298
syntax/ast_core_type_class_type.cmi : syntax/bs_ast_mapper.cmi
294299
syntax/ast_tuple_pattern_flatten.cmi : syntax/bs_ast_mapper.cmi

jscomp/bsb/bsb_templates.ml

Lines changed: 99 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,11 @@ let root = OCamlRes.Res.([
478478
"const path = require('path');\n\
479479
const outputDir = path.join(__dirname, \"build/\");\n\
480480
\n\
481+
const isProd = process.env.NODE_ENV === 'production';\n\
482+
\n\
481483
module.exports = {\n\
482484
\ entry: './src/Index.bs.js',\n\
485+
\ mode: isProd ? 'production' : 'development',\n\
483486
\ output: {\n\
484487
\ path: outputDir,\n\
485488
\ publicPath: outputDir,\n\
@@ -488,7 +491,78 @@ let root = OCamlRes.Res.([
488491
};\n\
489492
") ;
490493
Dir ("src", [
491-
File ("Page.re",
494+
File ("Index.re",
495+
"ReactDOMRe.renderToElementWithId(<Component1 message=\"Hello!\" />, \"index1\");\n\
496+
\n\
497+
ReactDOMRe.renderToElementWithId(<Component2 greeting=\"Hello!\" />, \"index2\");\n\
498+
") ;
499+
File ("index.html",
500+
"<!DOCTYPE html>\n\
501+
<html lang=\"en\">\n\
502+
<head>\n\
503+
\ <meta charset=\"UTF-8\">\n\
504+
\ <title>ReasonReact Examples</title>\n\
505+
</head>\n\
506+
<body>\n\
507+
\ Component 1:\n\
508+
\ <div id=\"index1\"></div>\n\
509+
\ Component 2:\n\
510+
\ <div id=\"index2\"></div>\n\
511+
\n\
512+
\ <script src=\"../build/Index.js\"></script>\n\
513+
</body>\n\
514+
</html>\n\
515+
") ;
516+
File ("Component2.re",
517+
"/* State declaration */\n\
518+
type state = {\n\
519+
\ count: int,\n\
520+
\ show: bool,\n\
521+
};\n\
522+
\n\
523+
/* Action declaration */\n\
524+
type action =\n\
525+
\ | Click\n\
526+
\ | Toggle;\n\
527+
\n\
528+
/* Component template declaration.\n\
529+
\ Needs to be **after** state and action declarations! */\n\
530+
let component = ReasonReact.reducerComponent(\"Example\");\n\
531+
\n\
532+
/* greeting and children are props. `children` isn't used, therefore ignored.\n\
533+
\ We ignore it by prepending it with an underscore */\n\
534+
let make = (~greeting, _children) => {\n\
535+
\ /* spread the other default fields of component here and override a few */\n\
536+
\ ...component,\n\
537+
\n\
538+
\ initialState: () => {count: 0, show: true},\n\
539+
\n\
540+
\ /* State transitions */\n\
541+
\ reducer: (action, state) =>\n\
542+
\ switch (action) {\n\
543+
\ | Click => ReasonReact.Update({...state, count: state.count + 1})\n\
544+
\ | Toggle => ReasonReact.Update({...state, show: ! state.show})\n\
545+
\ },\n\
546+
\n\
547+
\ render: self => {\n\
548+
\ let message =\n\
549+
\ \"You've clicked this \" ++ string_of_int(self.state.count) ++ \" times(s)\";\n\
550+
\ <div>\n\
551+
\ <button onClick=(_event => self.send(Click))>\n\
552+
\ (ReasonReact.stringToElement(message))\n\
553+
\ </button>\n\
554+
\ <button onClick=(_event => self.send(Toggle))>\n\
555+
\ (ReasonReact.stringToElement(\"Toggle greeting\"))\n\
556+
\ </button>\n\
557+
\ (\n\
558+
\ self.state.show ?\n\
559+
\ ReasonReact.stringToElement(greeting) : ReasonReact.nullElement\n\
560+
\ )\n\
561+
\ </div>;\n\
562+
\ },\n\
563+
};\n\
564+
") ;
565+
File ("Component1.re",
492566
"/* This is the basic component. */\n\
493567
let component = ReasonReact.statelessComponent(\"Page\");\n\
494568
\n\
@@ -507,41 +581,35 @@ let root = OCamlRes.Res.([
507581
\ `ReasonReact.element(Page.make(~message=\"hello\", [||]))` */\n\
508582
let make = (~message, _children) => {\n\
509583
\ ...component,\n\
510-
\ render: (self) =>\n\
511-
\ <div onClick=(self.handle(handleClick))> (ReasonReact.stringToElement(message)) </div>\n\
584+
\ render: self =>\n\
585+
\ <div onClick=(self.handle(handleClick))>\n\
586+
\ (ReasonReact.stringToElement(message))\n\
587+
\ </div>,\n\
512588
};\n\
513-
") ;
514-
File ("Index.re",
515-
"ReactDOMRe.renderToElementWithId(<Page message=\"Hello!\" />, \"index\");\n\
516-
") ;
517-
File ("index.html",
518-
"<!DOCTYPE html>\n\
519-
<html lang=\"en\">\n\
520-
<head>\n\
521-
\ <meta charset=\"UTF-8\">\n\
522-
\ <title>Pure Reason Example</title>\n\
523-
</head>\n\
524-
<body>\n\
525-
\ <div id=\"index\"></div>\n\
526-
\ <script src=\"../build/Index.js\"></script>\n\
527-
</body>\n\
528-
</html>\n\
529589
")]) ;
530590
File ("README.md",
531591
"# ${bsb:name}\n\
532592
\n\
533-
Run this project:\n\
593+
## Run Project\n\
534594
\n\
535-
```\n\
595+
```sh\n\
536596
npm install\n\
537597
npm start\n\
538598
# in another tab\n\
539599
npm run webpack\n\
540600
```\n\
541-
\n\
542-
After you see the webpack compilation succeed (the `npm run webpack` step), open up the nested html files in `src/*` (**no server needed!**). Then modify whichever file in `src` and refresh the page to see the changes.\n\
601+
After you see the webpack compilation succeed (the `npm run webpack` step), open up `src/index.html` (**no server needed!**). Then modify whichever `.re` file in `src` and refresh the page to see the changes.\n\
543602
\n\
544603
**For more elaborate ReasonReact examples**, please see https://github.com/reasonml-community/reason-react-example\n\
604+
\n\
605+
## Build for Production\n\
606+
\n\
607+
```sh\n\
608+
npm run build\n\
609+
npm run webpack:production\n\
610+
```\n\
611+
\n\
612+
This will replace the development artifact `build/Index.js` for an optimized version.\n\
545613
") ;
546614
File ("package.json",
547615
"{\n\
@@ -552,21 +620,23 @@ let root = OCamlRes.Res.([
552620
\ \"start\": \"bsb -make-world -w\",\n\
553621
\ \"clean\": \"bsb -clean-world\",\n\
554622
\ \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n\
555-
\ \"webpack\": \"webpack -w\"\n\
623+
\ \"webpack\": \"webpack -w\",\n\
624+
\ \"webpack:production\": \"NODE_ENV=production webpack\"\n\
556625
\ },\n\
557626
\ \"keywords\": [\n\
558627
\ \"BuckleScript\"\n\
559628
\ ],\n\
560629
\ \"author\": \"\",\n\
561630
\ \"license\": \"MIT\",\n\
562631
\ \"dependencies\": {\n\
563-
\ \"react\": \"^15.4.2\",\n\
564-
\ \"react-dom\": \"^15.4.2\",\n\
565-
\ \"reason-react\": \">=0.3.0\"\n\
632+
\ \"react\": \"^16.2.0\",\n\
633+
\ \"react-dom\": \"^16.2.0\",\n\
634+
\ \"reason-react\": \">=0.3.4\"\n\
566635
\ },\n\
567636
\ \"devDependencies\": {\n\
568637
\ \"bs-platform\": \"^${bsb:bs-version}\",\n\
569-
\ \"webpack\": \"^3.8.1\"\n\
638+
\ \"webpack\": \"^4.0.1\",\n\
639+
\ \"webpack-cli\": \"^2.0.10\"\n\
570640
\ }\n\
571641
}\n\
572642
") ;
@@ -585,7 +655,7 @@ let root = OCamlRes.Res.([
585655
\ \"subdirs\" : true\n\
586656
\ },\n\
587657
\ \"package-specs\": [{\n\
588-
\ \"module\": \"commonjs\",\n\
658+
\ \"module\": \"es6\",\n\
589659
\ \"in-source\": true\n\
590660
\ }],\n\
591661
\ \"suffix\": \".bs.js\",\n\

jscomp/syntax/ast_exp_apply.ml

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
(* Copyright (C) 2018 Authors of BuckleScript
2+
*
3+
* This program is free software: you can redistribute it and/or modify
4+
* it under the terms of the GNU Lesser General Public License as published by
5+
* the Free Software Foundation, either version 3 of the License, or
6+
* (at your option) any later version.
7+
*
8+
* In addition to the permissions granted to you by the LGPL, you may combine
9+
* or link a "work that uses the Library" with a publicly distributed version
10+
* of this file to produce a combined library or application, then distribute
11+
* that combined work under the terms of your choosing, with no requirement
12+
* to comply with the obligations normally placed on you by section 4 of the
13+
* LGPL version 3 (or the corresponding section of a later version of the LGPL
14+
* should you choose to use a later version).
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Lesser General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Lesser General Public License
22+
* along with this program; if not, write to the Free Software
23+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
24+
25+
open Ast_helper
26+
27+
let handle_exp_apply
28+
(e : Parsetree.expression)
29+
(self : Bs_ast_mapper.mapper)
30+
(fn : Parsetree.expression)
31+
(args : (Asttypes.label * Parsetree.expression) list)
32+
=
33+
let loc = e.pexp_loc in
34+
begin match fn with
35+
| {pexp_desc =
36+
Pexp_apply (
37+
{pexp_desc =
38+
Pexp_ident {txt = Lident "##" ; loc} ; _},
39+
[("", obj) ;
40+
("", {pexp_desc = Pexp_ident {txt = Lident name;_ } ; _} )
41+
]);
42+
_} -> (* f##paint 1 2 *)
43+
{e with pexp_desc = Ast_util.method_apply loc self obj name args }
44+
| {pexp_desc =
45+
Pexp_apply (
46+
{pexp_desc =
47+
Pexp_ident {txt = Lident "#@" ; loc} ; _},
48+
[("", obj) ;
49+
("", {pexp_desc = Pexp_ident {txt = Lident name;_ } ; _} )
50+
]);
51+
_} -> (* f##paint 1 2 *)
52+
{e with pexp_desc = Ast_util.property_apply loc self obj name args }
53+
54+
| {pexp_desc =
55+
Pexp_ident {txt = Lident "##" ; loc} ; _}
56+
->
57+
begin match args with
58+
| [("", obj) ;
59+
("", {pexp_desc = Pexp_apply(
60+
{pexp_desc = Pexp_ident {txt = Lident name;_ } ; _},
61+
args
62+
); pexp_attributes = attrs }
63+
(* we should warn when we discard attributes *)
64+
)
65+
] -> (* f##(paint 1 2 ) *)
66+
(* gpr#1063 foo##(bar##baz) we should rewrite (bar##baz)
67+
first before pattern match.
68+
currently the pattern match is written in a top down style.
69+
Another corner case: f##(g a b [@bs])
70+
*)
71+
Bs_ast_invariant.warn_unused_attributes attrs ;
72+
{e with pexp_desc = Ast_util.method_apply loc self obj name args}
73+
| [("", obj) ;
74+
("",
75+
{pexp_desc = Pexp_ident {txt = Lident name;_ } ; _}
76+
) (* f##paint *)
77+
] ->
78+
{ e with pexp_desc =
79+
Ast_util.js_property loc (self.expr self obj) name
80+
}
81+
82+
| _ ->
83+
Location.raise_errorf ~loc
84+
"Js object ## expect syntax like obj##(paint (a,b)) "
85+
end
86+
(* we can not use [:=] for precedece cases
87+
like {[i @@ x##length := 3 ]}
88+
is parsed as {[ (i @@ x##length) := 3]}
89+
since we allow user to create Js objects in OCaml, it can be of
90+
ref type
91+
{[
92+
let u = object (self)
93+
val x = ref 3
94+
method setX x = self##x := 32
95+
method getX () = !self##x
96+
end
97+
]}
98+
*)
99+
| {pexp_desc =
100+
Pexp_ident {txt = Lident ("#=" )}
101+
} ->
102+
begin match args with
103+
| ["",
104+
{pexp_desc =
105+
Pexp_apply ({pexp_desc = Pexp_ident {txt = Lident "##"}},
106+
["", obj;
107+
"", {pexp_desc = Pexp_ident {txt = Lident name}}
108+
]
109+
)};
110+
"", arg
111+
] ->
112+
Exp.constraint_ ~loc
113+
{ e with
114+
pexp_desc =
115+
Ast_util.method_apply loc self obj
116+
(name ^ Literals.setter_suffix) ["", arg ] }
117+
(Ast_literal.type_unit ~loc ())
118+
| _ -> Bs_ast_mapper.default_mapper.expr self e
119+
end
120+
| _ ->
121+
begin match
122+
Ext_list.exclude_with_val
123+
Ast_attributes.is_bs e.pexp_attributes with
124+
| false, _ -> Bs_ast_mapper.default_mapper.expr self e
125+
| true, pexp_attributes ->
126+
{e with pexp_desc = Ast_util.uncurry_fn_apply loc self fn args ;
127+
pexp_attributes }
128+
end
129+
end

jscomp/syntax/ast_exp_apply.mli

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
(* Copyright (C) 2018 Authors of BuckleScript
2+
*
3+
* This program is free software: you can redistribute it and/or modify
4+
* it under the terms of the GNU Lesser General Public License as published by
5+
* the Free Software Foundation, either version 3 of the License, or
6+
* (at your option) any later version.
7+
*
8+
* In addition to the permissions granted to you by the LGPL, you may combine
9+
* or link a "work that uses the Library" with a publicly distributed version
10+
* of this file to produce a combined library or application, then distribute
11+
* that combined work under the terms of your choosing, with no requirement
12+
* to comply with the obligations normally placed on you by section 4 of the
13+
* LGPL version 3 (or the corresponding section of a later version of the LGPL
14+
* should you choose to use a later version).
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Lesser General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Lesser General Public License
22+
* along with this program; if not, write to the Free Software
23+
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
24+
25+
26+
val handle_exp_apply :
27+
Parsetree.expression ->
28+
Bs_ast_mapper.mapper ->
29+
Parsetree.expression ->
30+
(Asttypes.label * Parsetree.expression) list ->
31+
Parsetree.expression

0 commit comments

Comments
 (0)