How to turn Macro.string_to_quoted_with_comments! back into string

In Elixir you can do this:

Code.string_to_quoted!(a_string) |> Macro.to_string()

And get back a_string , but if you try it with:

Code.string_to_quoted_with_comments!(a_string) |> Macro.to_string()

It doesn’t work. Is there a way to roundtrip AST with comments in it?

Examples

module = """
defmodule T do
  # this is a comment
  test "thing" do
  end
end
"""

Code.string_to_quoted!(module) |> Macro.to_string()

VS

module = """
defmodule T do
  # this is a comment
  test "thing" do
  end
end
"""

Code.string_to_quoted_with_comments!(module) |> Macro.to_string()

You need the lower-level Code.quoted_to_algebra. Here’s an example from the tests:

For your specific input:

module = """
defmodule T do
  # this is a comment
  test "thing" do
  end
end
"""

{ast, comments} = Code.string_to_quoted_with_comments!(module)

formatted_code = Code.quoted_to_algebra(ast, comments: comments) |> Inspect.Algebra.format(100) |> Enum.join("")

IO.puts formatted_code

will print the same code (modulo the trailing newline) as originally bound to module.

3 Likes

This Doesn’t quite work in all cases, for example:


module = """
defmodule T do
  # this is a comment
  @thing \"\"\"
  aa
  bb
  cc
  \"\"\"
end
"""

{ast, comments} = Code.string_to_quoted_with_comments!(module)

formatted_code = Code.quoted_to_algebra(ast, comments: comments) |> Inspect.Algebra.format(100) |> Enum.join("")

IO.puts formatted_code

The multi line string gets returned as a single line string which doesn’t work for my use case.

I have to do something like this:

module = """
defmodule T do
  # this is a comment
  @thing \"\"\"
  aa
  bb
  cc
  \"\"\"
end
"""

{ast, comments} = Code.string_to_quoted_with_comments!(module, 
  literal_encoder: &{:ok, {:__block__, &2, [&1]}},
    token_metadata: true
)

Code.quoted_to_algebra(ast, comments: comments, escape: false) 
|> Inspect.Algebra.format(100) 
|> IO.iodata_to_binary()
|> Code.format_string!()
|> IO.puts
2 Likes