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

browser-ppx: process stritems #127

Merged
merged 3 commits into from
Apr 18, 2024
Merged

browser-ppx: process stritems #127

merged 3 commits into from
Apr 18, 2024

Conversation

jchavarri
Copy link
Contributor

@jchavarri jchavarri commented Apr 10, 2024

Allows to do things like:

module T = struct
  type t = Js.Json.t
end [@@platform js]

module U = struct
  type t = string
end [@@platform native]

Both platform or browser-ppx can be used as the attribute tag (this change applies to every existing preprocessing of browser-ppx as well).

@jchavarri jchavarri marked this pull request as ready for review April 10, 2024 07:20
@jchavarri jchavarri requested a review from davesnx April 10, 2024 07:23
else None
| Psig_extension _ | Psig_attribute _ -> Some sigi

let preprocess_impl _exp_ctxt str =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what the _exp_ctxt variable is about, it was introduced in ppxlib v2, @davesnx do you know?

Comment on lines +476 to +477
~preprocess_impl:Preprocess.preprocess_impl
~preprocess_intf:Preprocess.preprocess_intf
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to do this processing using just rules so no preprocess_impl or preprocess_intf is used?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't looked into the code, yet, but would be nice

Copy link
Member

@davesnx davesnx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to merge it as it is, we can push for a better impl with:

  • usage of context free
  • have proper errors when one is missing
  • more test cases

$ ./standalone.exe -impl input.ml | ocamlformat - --enable-outside-detected-project --impl
include struct
type t = string
end [@@platform native]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should remove the annotation no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think it should. Will look into this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried removing the attributes but it's quite cumbersome. The approach I took is to transform should_keep into st like this:

  let rec should_keep attrs =
    let rec f acc attrs =
      match attrs with
      | [] -> `keep (List.rev acc)
      | attr :: attrs -> (
          match eval_attr attr with
          | `found_drop -> `drop
          | `found_keep -> f acc attrs
          | `not_found -> f (attr :: acc) attrs)
    in
    f [] attrs

The main issue I found is on the apply_config_on_structure_item function. Instead of the current simple approach, it becomes this monster:

  let apply_config_on_structure_item stri =
    match stri.pstr_desc with
    | Pstr_typext ({ ptyext_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_typext { payload with ptyext_attributes = attrs })
        | `drop -> None)
    | Pstr_modtype ({ pmtd_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_modtype { payload with pmtd_attributes = attrs })
        | `drop -> None)
    | Pstr_open ({ popen_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_open { payload with popen_attributes = attrs })
        | `drop -> None)
    | Pstr_include ({ pincl_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_include { payload with pincl_attributes = attrs })
        | `drop -> None)
    | Pstr_exception ({ ptyexn_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_exception { payload with ptyexn_attributes = attrs })
        | `drop -> None)
    | Pstr_primitive ({ pval_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_primitive { payload with pval_attributes = attrs })
        | `drop -> None)
    | Pstr_eval (str_desc, attrs) -> (
        match should_keep attrs with
        | `keep attrs -> Some (Pstr_eval (str_desc, attrs))
        | `drop -> None)
    | Pstr_module ({ pmb_attributes = attrs; _ } as payload) -> (
        match should_keep attrs with
        | `keep attrs ->
            Some (Pstr_module { payload with pmb_attributes = attrs })
        | `drop -> None)
      ...

I am sure there are ways in which it could be simplified, but after discussing with @davesnx we're punting on this for later (maybe we end up using extensions instead of attributes...)

>
> include struct
> type t = string
> end [@@platform native]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering what happens (if ppx raises) when you add platform to only one

Copy link
Contributor Author

@jchavarri jchavarri Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing happens (in terms of errors), added a test here:

Use only one of the platforms
$ cat > input.ml << EOF
> include struct
> type t = Js.Json.t
> end [@@platform js]
>
> include struct
> type t = string
> end
> EOF
$ ./standalone.exe -impl input.ml | ocamlformat - --enable-outside-detected-project --impl
include struct
type t = string
end
$ ./standalone.exe -impl input.ml -js | ocamlformat - --enable-outside-detected-project --impl
include struct
type t = Js.Json.t
end [@@platform js]
include struct
type t = string
end

| { pstr_desc = Pstr_attribute attr; _ } :: rest
when is_platform_tag attr.attr_name.txt ->
if eval_attr attr = `keep then rest else []
| _ -> List.filter_map apply_config_on_structure_item str
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you might not implemented this part, but why do we have 2 passes?

Probably [@platform] can be applied to any structure, not only modules in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the q, where are the two passes? What I understand this code is doing is:

  • If the str item itself has an the platform attr, process it
  • Otherwise, process whatever is inside the str item

@jchavarri
Copy link
Contributor Author

have proper errors when one is missing

is this necessary? I can imagine scenarios where one might want to run something only on one platform (e.g. some side effect). I also think it'll be quite challenging to implement "exhaustiveness" considering how this ppx is integrating at the str item level 🤔

more test cases

I added a bunch of them in cb61e51. Lmk what you think!

@jchavarri
Copy link
Contributor Author

usage of context free

I took a stab at this too. It seems non-trivial because context-free:

  • only supports extensions, not attributes, so we should change the API somehow to adapt to that
  • it expects to return something from the rule function, but we need to erase nodes fully from the ast (Some / None)

I am not sure how to work around that 🤔

@jchavarri jchavarri merged commit ded3529 into main Apr 18, 2024
2 checks passed
@jchavarri jchavarri deleted the platform-stritems branch April 18, 2024 14:13
davesnx added a commit to davesnx/opam-repository that referenced this pull request Jul 23, 2024
CHANGES:

## 0.3.1
* Update quickjs dependency to 0.1.2 by @davesnx

## 0.3.0

* browser-ppx: process stritems by @jchavarri in ml-in-barcelona/server-reason-react#127
* Make React.Children.* APIs work as expected by @davesnx in ml-in-barcelona/server-reason-react#130
* Improve global crashes by @davesnx in ml-in-barcelona/server-reason-react#132
* Support assets in `mel.module` by @jchavarri in ml-in-barcelona/server-reason-react#134
* browser_only: don't convert to runtime errors on identifiers or function application by @jchavarri in ml-in-barcelona/server-reason-react#138
* Port `j` quoted strings interpolation from Melange by @jchavarri in ml-in-barcelona/server-reason-react#139
* mel.module: handle asset prefix by @jchavarri in ml-in-barcelona/server-reason-react#140
* Add browser_only transformation to useEffect automatically by @davesnx in ml-in-barcelona/server-reason-react#145
* Append doctype tag on html lowercase by @davesnx in ml-in-barcelona/server-reason-react#136
* Transform Pexp_function with browser_only by @davesnx in ml-in-barcelona/server-reason-react#146

## 0.2.0

- Remove data-reactroot attr from ReactDOM.renderToString ml-in-barcelona/server-reason-react#129 by @pedrobslisboa
- Make useUrl return the provided serverUrl ml-in-barcelona/server-reason-react#125 by @purefunctor
- Replace Js.Re implemenation from `pcre` to quickjs b1a3e225cdad1298d705fbbd9618e15b0427ef0f by @davesnx
- Remove Belt.Array.push ml-in-barcelona/server-reason-react#122 by @davesnx

## 0.1.0

Initial release of server-reason-react, includes:

- Server-side rendering of ReasonReact components (renderToString, renderToStaticMarkup & renderToLwtStream)
- `server-reason-react.browser_ppx` for skipping code from the server
- `server-reason-react.melange_ppx` for enabling melange bindings and extensions which run on the server
- `server-reason-react.belt` a native Belt implementation
- `server-reason-react.js` a native Js implementation (unsafe and limited)
- `server-reason-react.url` and `server-reason-react.url-native` a universal library with both implementations to work with URLs on the server and the client
- `server-reason-react.promise` and `server-reason-react.promise-native` a universal library with both implementations to work with Promises on the server and the client. Based on https://github.com/aantron/promise
- `server-reason-react.melange-fetch` a fork of melange-fetch which is a melange library to fetch data on the client via the Fetch API. This fork is to be able to compile it on the server (not running).
- `server-reason-react.webapi` a fork of melange-webapi which is a melange library to work with the Web API on the client. This fork is to be able to compile it on the server (not running).
avsm pushed a commit to avsm/opam-repository that referenced this pull request Sep 5, 2024
CHANGES:

## 0.3.1
* Update quickjs dependency to 0.1.2 by @davesnx

## 0.3.0

* browser-ppx: process stritems by @jchavarri in ml-in-barcelona/server-reason-react#127
* Make React.Children.* APIs work as expected by @davesnx in ml-in-barcelona/server-reason-react#130
* Improve global crashes by @davesnx in ml-in-barcelona/server-reason-react#132
* Support assets in `mel.module` by @jchavarri in ml-in-barcelona/server-reason-react#134
* browser_only: don't convert to runtime errors on identifiers or function application by @jchavarri in ml-in-barcelona/server-reason-react#138
* Port `j` quoted strings interpolation from Melange by @jchavarri in ml-in-barcelona/server-reason-react#139
* mel.module: handle asset prefix by @jchavarri in ml-in-barcelona/server-reason-react#140
* Add browser_only transformation to useEffect automatically by @davesnx in ml-in-barcelona/server-reason-react#145
* Append doctype tag on html lowercase by @davesnx in ml-in-barcelona/server-reason-react#136
* Transform Pexp_function with browser_only by @davesnx in ml-in-barcelona/server-reason-react#146

## 0.2.0

- Remove data-reactroot attr from ReactDOM.renderToString ml-in-barcelona/server-reason-react#129 by @pedrobslisboa
- Make useUrl return the provided serverUrl ml-in-barcelona/server-reason-react#125 by @purefunctor
- Replace Js.Re implemenation from `pcre` to quickjs b1a3e225cdad1298d705fbbd9618e15b0427ef0f by @davesnx
- Remove Belt.Array.push ml-in-barcelona/server-reason-react#122 by @davesnx

## 0.1.0

Initial release of server-reason-react, includes:

- Server-side rendering of ReasonReact components (renderToString, renderToStaticMarkup & renderToLwtStream)
- `server-reason-react.browser_ppx` for skipping code from the server
- `server-reason-react.melange_ppx` for enabling melange bindings and extensions which run on the server
- `server-reason-react.belt` a native Belt implementation
- `server-reason-react.js` a native Js implementation (unsafe and limited)
- `server-reason-react.url` and `server-reason-react.url-native` a universal library with both implementations to work with URLs on the server and the client
- `server-reason-react.promise` and `server-reason-react.promise-native` a universal library with both implementations to work with Promises on the server and the client. Based on https://github.com/aantron/promise
- `server-reason-react.melange-fetch` a fork of melange-fetch which is a melange library to fetch data on the client via the Fetch API. This fork is to be able to compile it on the server (not running).
- `server-reason-react.webapi` a fork of melange-webapi which is a melange library to work with the Web API on the client. This fork is to be able to compile it on the server (not running).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants