ocaml-ppx/ocaml-migrate-parsetree

A workaround to print ## as it is, not as a infix operator.

Closed this issue · 4 comments

Taken from here

Eg. obj##foo will be translated to (## obj foo) when I invoke ppx_deriving as a command line tool (compiled by myself). I understand ## is not standard object dispatch but is it possible that we keep it as it is, and not transform it?

Thanks!

Best,
Bob

ocaml-migrate-parsetree does not do parsing nor printing.
The best that can be done to handle your case in the long term is to ask for '#' to be properly handled as an infix operator in:
https://github.com/ocaml/ocaml/blob/trunk/parsing/pprintast.ml#L32 (or eventually to have a specialized printing rule in https://github.com/ocaml/ocaml/blob/trunk/parsing/pprintast.ml#L526).

I am afraid this will take a lot of time. In the meantime, you can probably do a transform your self to turn '##' back to method application (valid only before printing, don't use the AST for anything else):

open Asttypes
open Parsetree
open Ast_mapper

let is_sharpop = function
  | Longident.Lident str -> String.length str > 1 && str.[0] = '#'
  | _ -> false

let is_ident = function
  | Longident.Lident _ -> true
  | _ -> false

let extract_sharpop = function
  | Longident.Lident str -> String.sub str 1 (String.length str - 1)
  | _ -> assert false

let extract_ident = function
  | Longident.Lident str -> str
  | _ -> assert false

let sharpop_mapper =
  let expr mapper (expr : expression) =
    match expr.pexp_desc with
    | Pexp_apply ({ pexp_desc = Pexp_ident {txt = op; _}; pexp_loc;
                    pexp_attributes=[]; _},
                  [(Nolabel, head); (Nolabel, { pexp_desc = Pexp_ident {txt = msg; _}; pexp_attributes=[]; _})])
      when is_sharpop op ->
      let op = extract_sharpop op in
      let msg = extract_ident msg in
      let head = mapper.expr mapper head in
      {expr with pexp_desc = Pexp_send (head, op ^ msg)}
    | _ -> default_mapper.expr mapper expr
  in
  {default_mapper with expr}

Which is a workaround:

# let expr = Parse.expression (Lexing.from_string "x##a b c");;
# Format.printf "%a\n" Format.std_formatter expr;;
(## x a) b c
- : unit = ()
# Format.printf "%a\n" Pprintast.expression (sharpop_mapper.Ast_mapper.expr sharpop_mapper expr);;
x##a b c
- : unit = ()

Will changing how OCaml compiler even work? By looking at the code

let infix_symbols = [ '='; '<'; '>'; '@'; '^'; '|'; '&'; '+'; '-'; '*'; '/';
                      '$'; '%' ]

The infix_symobls are all defined as one character, but in my case I need to handle ##.

No, these are the first characters of infix_symbols.