├── .formatter.exs ├── .gitignore ├── LICENSE.md ├── README.md ├── lib ├── beam_to_ex_ast.ex └── beam_to_ex_ast │ ├── atom.ex │ ├── b_generate.ex │ ├── bc.ex │ ├── bin.ex │ ├── bin_element.ex │ ├── block.ex │ ├── call.ex │ ├── case.ex │ ├── char.ex │ ├── clause.ex │ ├── clauses.ex │ ├── cons.ex │ ├── float.ex │ ├── fun.ex │ ├── if.ex │ ├── integer.ex │ ├── lc.ex │ ├── list.ex │ ├── map.ex │ ├── map_field_assoc.ex │ ├── map_field_exact.ex │ ├── match.ex │ ├── nil.ex │ ├── op.ex │ ├── receive.ex │ ├── record.ex │ ├── record_field.ex │ ├── remote.ex │ ├── string.ex │ ├── translate.ex │ ├── try.ex │ ├── tuple.ex │ └── var.ex ├── mix.exs ├── mix.lock └── test ├── beam_to_ex_ast_test.exs ├── support ├── function.ex ├── function_atom.ex ├── function_binary.ex ├── function_body.ex ├── function_bool_compare.ex ├── function_case.ex ├── function_float.ex ├── function_int.ex ├── function_lists.ex ├── function_map.ex ├── function_math.ex ├── function_pipe.ex ├── function_record.ex ├── function_try.ex ├── function_tuple.ex └── functions.ex └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] 3 | ] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | /deps 3 | /doc 4 | /cover 5 | erl_crash.dump 6 | *.ez 7 | node_modules 8 | mix.lock 9 | profile/AllSets.json 10 | profile/AllSets-x.json 11 | profile/edg.json 12 | profile/usda.json 13 | 14 | # Vim 15 | *~ 16 | *.swp 17 | *.swo 18 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BeamToExAst 2 | 3 | [WIP] With this module you can move any code from the Erlang AST to Elixir AST. 4 | 5 | ## Installation 6 | 7 | If [available in Hex](https://hex.pm/docs/publish), the package can be installed as: 8 | 9 | 1. Add beam_to_ex_ast to your list of dependencies in `mix.exs`: 10 | 11 | def deps do 12 | [{:beam_to_ex_ast, "~> 0.0.1"}] 13 | end 14 | 15 | 2. Ensure beam_to_ex_ast is started before your application: 16 | 17 | def application do 18 | [applications: [:beam_to_ex_ast]] 19 | end 20 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst do 2 | alias BeamToExAst.Translate 3 | 4 | def convert(list, opts \\ []) do 5 | opts = Enum.into(opts, %{}) 6 | {mod_name, rest, _opts} = Enum.reduce(list, {"", [], opts}, &do_convert/2) 7 | 8 | case length(rest) do 9 | 1 -> 10 | {:defmodule, [line: 1], [{:__aliases__, [line: 1], [mod_name]}, [do: List.first(rest)]]} 11 | 12 | _ -> 13 | {:defmodule, [line: 1], 14 | [ 15 | {:__aliases__, [line: 1], [mod_name]}, 16 | [do: {:__block__, [], Enum.sort(rest, &sort_fun/2)}] 17 | ]} 18 | end 19 | end 20 | 21 | def sort_fun({:def, [line: lna], _}, {:def, [line: lnb], _}) do 22 | lna < lnb 23 | end 24 | 25 | # _n is number of parameters 26 | # ln is the line number 27 | def do_convert({:attribute, _ln, :module, name}, {_, rest, opts}) do 28 | {clean_module(name), rest, opts} 29 | end 30 | 31 | def do_convert({:attribute, _ln, :record, _ast}, {mod_name, rest, opts}) do 32 | {mod_name, rest, opts} 33 | end 34 | 35 | def do_convert({:attribute, _, _, _}, acc) do 36 | acc 37 | end 38 | 39 | def do_convert({:function, _, :__info__, _, _}, acc) do 40 | acc 41 | end 42 | 43 | def do_convert({:function, _ln, name, _n, body}, {mod_name, rest, opts}) do 44 | opts = Map.put(opts, :parents, [:function]) 45 | 46 | {mod_name, 47 | Enum.concat( 48 | Enum.map(body, fn 49 | {:clause, ln2, params, guard, body_def} -> 50 | case guard do 51 | [] -> 52 | {:def, [line: get_line(ln2)], 53 | [ 54 | {name, [line: get_line(ln2)], Translate.to_elixir(params, opts)}, 55 | def_body(body_def, opts) 56 | ]} 57 | 58 | [[g]] -> 59 | {:def, [line: get_line(ln2)], 60 | [ 61 | {:when, [line: get_line(ln2)], 62 | [ 63 | {name, [line: get_line(ln2)], Translate.to_elixir(params, opts)}, 64 | Translate.to_elixir(g, opts) 65 | ]}, 66 | def_body(body_def, opts) 67 | ]} 68 | 69 | [g1, g2] -> 70 | {:def, [line: get_line(ln2)], 71 | [ 72 | {:when, [line: get_line(ln2)], 73 | [ 74 | {name, [line: get_line(ln2)], Translate.to_elixir(params, opts)}, 75 | {:and, [], 76 | [ 77 | Translate.to_elixir(List.first(g1), opts), 78 | Translate.to_elixir(List.first(g2), opts) 79 | ]} 80 | ]}, 81 | def_body(body_def, opts) 82 | ]} 83 | end 84 | 85 | _ -> 86 | body 87 | end), 88 | rest 89 | ), opts} 90 | end 91 | 92 | def do_convert({:eof, _ln}, acc) do 93 | acc 94 | end 95 | 96 | def def_body(items, opts) do 97 | opts = Map.update!(opts, :parents, &[:body | &1]) 98 | 99 | filtered_items = 100 | items 101 | |> Enum.filter(fn 102 | {:atom, _, nil} -> false 103 | _ -> true 104 | end) 105 | 106 | case length(filtered_items) do 107 | 1 -> [do: Translate.to_elixir(List.first(filtered_items), opts)] 108 | _ -> [do: {:__block__, [], Translate.to_elixir(filtered_items, opts)}] 109 | end 110 | end 111 | 112 | def def_body_less(items, opts) do 113 | opts = Map.update!(opts, :parents, &[:body_less | &1]) 114 | 115 | case length(items) do 116 | 1 -> Translate.to_elixir(List.first(items), opts) 117 | _ -> {:__block__, [], Translate.to_elixir(items, opts)} 118 | end 119 | end 120 | 121 | def def_body_less_filter(items, opts) do 122 | opts = Map.update!(opts, :parents, &[:body_less_filter | &1]) 123 | 124 | items2 = 125 | items 126 | |> Translate.to_elixir(opts) 127 | |> Enum.filter(&filter_empty/1) 128 | 129 | case length(items2) do 130 | 1 -> List.first(items2) 131 | _ -> {:__block__, [], items2} 132 | end 133 | end 134 | 135 | def get_line({ln, _}), do: ln 136 | def get_line(ln), do: ln 137 | 138 | def get_caller(c_mod_call, ln, caller, params, opts) do 139 | case String.match?(c_mod_call, ~r"^[A-Z]") do 140 | true -> 141 | {{:., [line: get_line(ln)], 142 | [ 143 | {:__aliases__, [line: get_line(ln)], [String.to_atom(c_mod_call)]}, 144 | clean_atom(caller, opts) 145 | ]}, [line: get_line(ln)], Translate.to_elixir(params, opts)} 146 | 147 | false -> 148 | {{:., [line: get_line(ln)], [String.to_atom(c_mod_call), clean_atom(caller, opts)]}, 149 | [line: get_line(ln)], Translate.to_elixir(params, opts)} 150 | end 151 | end 152 | 153 | def remove_tuples(l1) when is_list(l1) do 154 | Enum.map(l1, &remove_tuple/1) 155 | end 156 | 157 | def remove_tuples(rest) do 158 | rest 159 | end 160 | 161 | def remove_tuple({:{}, [line: _ln], params}) do 162 | params 163 | end 164 | 165 | def remove_tuple(params) do 166 | params 167 | end 168 | 169 | def only_one(l1) do 170 | case length(l1) do 171 | 1 -> List.first(l1) 172 | _ -> l1 173 | end 174 | end 175 | 176 | def insert_line_number({:&, [line: 0], number}, ln) do 177 | {:&, [line: get_line(ln)], number} 178 | end 179 | 180 | def insert_line_number(var, _ln) do 181 | var 182 | end 183 | 184 | def check_params(params) do 185 | Enum.reduce(params, false, fn 186 | {:var, _ln, var}, acc -> 187 | case Atom.to_string(var) do 188 | <<"__@", _rest::binary>> -> true 189 | <<"_@", _rest::binary>> -> true 190 | <<"_capture@", _rest::binary>> -> true 191 | _ -> acc 192 | end 193 | 194 | _, acc -> 195 | acc 196 | end) 197 | end 198 | 199 | def check_bins(s1, acc) when is_binary(s1) do 200 | acc 201 | end 202 | 203 | def check_bins(_, _acc) do 204 | true 205 | end 206 | 207 | def clean_op(op1) do 208 | op1 209 | |> Atom.to_string() 210 | |> case do 211 | "=:=" -> "===" 212 | "=/=" -> "!==" 213 | "/=" -> "!=" 214 | "=<" -> "<=" 215 | "andalso" -> "and" 216 | s1 -> s1 217 | end 218 | |> String.to_atom() 219 | end 220 | 221 | def clean_module(a1) do 222 | s1 = 223 | a1 224 | |> Atom.to_string() 225 | |> String.replace("Elixir.", "") 226 | 227 | s1 228 | |> String.match?(~r"^[A-Z]") 229 | |> case do 230 | true -> s1 231 | false -> Macro.camelize(s1) 232 | end 233 | |> String.to_atom() 234 | end 235 | 236 | def clean_atom(a1, _) do 237 | a1 238 | |> Atom.to_string() 239 | |> String.replace("Elixir.", "") 240 | |> String.to_atom() 241 | end 242 | 243 | def half_clean_atom(a1, _) do 244 | a1 245 | |> Atom.to_string() 246 | |> String.replace("Elixir.", "") 247 | end 248 | 249 | def clean_var(v1, %{erlang: true}) do 250 | v1 251 | |> Atom.to_string() 252 | |> Macro.underscore() 253 | |> String.to_atom() 254 | end 255 | 256 | def clean_var(v1, %{elixir: true}) do 257 | v1_string = 258 | v1 259 | |> Atom.to_string() 260 | 261 | case System.version() do 262 | <<"1.6", _rest::binary>> -> 263 | v1_string 264 | |> String.replace(~r/^V/, "") 265 | 266 | _ -> 267 | if Regex.match?(~r/@\d*/, v1_string) do 268 | v1_string 269 | |> String.replace(~r/^_/, "") 270 | else 271 | v1_string 272 | end 273 | end 274 | |> String.replace(~r/@\d*/, "") 275 | |> String.to_atom() 276 | end 277 | 278 | def clean_var(v1, _) do 279 | v1 280 | |> Atom.to_string() 281 | |> String.replace(~r"@\d+", "") 282 | |> String.to_atom() 283 | end 284 | 285 | def filter_empty(:filter_this_thing_out_of_the_list_please) do 286 | false 287 | end 288 | 289 | def filter_empty(_) do 290 | true 291 | end 292 | end 293 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/atom.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Atom do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:atom, ln, caller}, %{parents: [:call | _]} = opts) do 6 | opts = Map.update!(opts, :parents, &[:atom | &1]) 7 | {params, opts} = Map.pop(opts, :call_params) 8 | {caller, [line: get_line(ln)], Translate.to_elixir(params, opts)} 9 | end 10 | 11 | def to_elixir({:atom, _ln, a1}, _) do 12 | a1 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/b_generate.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.BGenerate do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:b_generate, ln, {:bin, ln2, [bin]}, param2}, opts) do 6 | opts = Map.update!(opts, :parents, &[:b_generate | &1]) 7 | 8 | {:<<>>, [line: get_line(ln)], 9 | [ 10 | {:<-, [line: get_line(ln2)], 11 | [Translate.to_elixir(bin, opts), Translate.to_elixir(param2, opts)]} 12 | ]} 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/bc.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Bc do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | defp uncase(param, opts) when is_list(param) and length(param) == 1 do 6 | Translate.to_elixir(param, opts) 7 | end 8 | 9 | defp uncase([param, {:case, _ln, param2, _clauses}], opts) do 10 | [Translate.to_elixir(param, opts), Translate.to_elixir(param2, opts)] 11 | end 12 | 13 | def to_elixir({:bc, ln, param1, param2}, opts) do 14 | opts = Map.update!(opts, :parents, &[:bc | &1]) 15 | 16 | {:for, [line: get_line(ln)], 17 | uncase(param2, opts) ++ [[into: "", do: Translate.to_elixir(param1, opts)]]} 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/bin.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Bin do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:bin, ln, elements}, %{parents: [:match | _]} = opts) do 6 | opts = Map.update!(opts, :parents, &[:bin | &1]) 7 | 8 | case Translate.to_elixir(elements, opts) do 9 | bins when length(bins) === 1 -> 10 | List.first(bins) 11 | 12 | bins -> 13 | case Enum.reduce(bins, false, &check_bins/2) do 14 | true -> {:<<>>, [line: get_line(ln)], bins} 15 | false -> bins 16 | end 17 | end 18 | end 19 | 20 | def to_elixir({:bin, _ln, []}, _) do 21 | "" 22 | end 23 | 24 | def to_elixir({:bin, ln, elements}, opts) do 25 | opts = Map.update!(opts, :parents, &[:bin | &1]) 26 | 27 | elements 28 | |> Translate.to_elixir(opts) 29 | |> Enum.filter(fn 30 | {:atom, _, nil} -> false 31 | _ -> true 32 | end) 33 | |> case do 34 | [bin] when is_binary(bin) -> 35 | bin 36 | 37 | bins when length(bins) === 2 -> 38 | {:<>, [line: get_line(ln)], bins} 39 | 40 | bins -> 41 | case Enum.reduce(bins, false, &check_bins/2) do 42 | true -> 43 | {:<<>>, [line: get_line(ln)], bins} 44 | 45 | false -> 46 | bins 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/bin_element.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.BinElement do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir( 6 | {:bin_element, _ln, {:var, ln2, v1}, _, [:integer]}, 7 | %{parents: [:bin, :match | _]} = opts 8 | ) do 9 | opts = Map.update!(opts, :parents, &[:bin_element | &1]) 10 | Translate.to_elixir({:var, ln2, v1}, opts) 11 | end 12 | 13 | def to_elixir( 14 | {:bin_element, ln, {:var, ln2, v1}, _, [type]}, 15 | %{parents: [:bin, :match | _]} = opts 16 | ) do 17 | opts = Map.update!(opts, :parents, &[:bin_element | &1]) 18 | 19 | {:"::", [line: get_line(ln)], 20 | [Translate.to_elixir({:var, ln2, v1}, opts), {type, [line: get_line(ln)], nil}]} 21 | end 22 | 23 | def to_elixir({:bin_element, _ln, block, {:integer, _ln2, i}, _}, opts) do 24 | opts = Map.update!(opts, :parents, &[:bin_element | &1]) 25 | {:"::", [], [Translate.to_elixir(block, opts), i]} 26 | end 27 | 28 | def to_elixir({:bin_element, _ln, block, _, _}, opts) do 29 | opts = Map.update!(opts, :parents, &[:bin_element | &1]) 30 | Translate.to_elixir(block, opts) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/block.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Block do 2 | def to_elixir({:block, _ln, params}, opts) do 3 | opts = Map.update!(opts, :parents, &[:block | &1]) 4 | BeamToExAst.def_body(params, opts) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/call.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Call do 2 | alias BeamToExAst.Translate 3 | 4 | def to_elixir({:call, _ln, caller, params}, opts) do 5 | opts = Map.update!(opts, :parents, &[:call | &1]) 6 | opts = Map.put(opts, :call_params, params) 7 | Translate.to_elixir(caller, opts) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/case.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Case do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:case, ln, param, body}, opts) do 6 | opts = Map.update!(opts, :parents, &[:case | &1]) 7 | 8 | {:case, [line: get_line(ln)], 9 | [Translate.to_elixir(param, opts), [do: Translate.to_elixir(body, opts)]]} 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/char.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Char do 2 | def to_elixir({:char, _ln, c1}, _) do 3 | c1 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/clause.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Clause do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:clause, ln, params, guard, body}, opts) do 6 | opts = Map.update!(opts, :parents, &[:clause | &1]) 7 | 8 | case check_params(params) do 9 | false -> 10 | case guard do 11 | [] -> 12 | {:->, [line: get_line(ln)], 13 | [Translate.to_elixir(params, opts), def_body_less_filter(body, opts)]} 14 | 15 | [[g]] -> 16 | {:->, [line: get_line(ln)], 17 | [ 18 | [ 19 | {:when, [line: get_line(ln)], 20 | [only_one(Translate.to_elixir(params, opts)), Translate.to_elixir(g, opts)]} 21 | ], 22 | def_body_less_filter(body, opts) 23 | ]} 24 | end 25 | 26 | true -> 27 | {:&, [line: get_line(ln)], [def_body_less_filter(body, opts)]} 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/clauses.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Clauses do 2 | alias BeamToExAst.Translate 3 | 4 | def to_elixir({:clauses, params}, opts) do 5 | opts = Map.update!(opts, :parents, &[:clauses | &1]) 6 | Translate.to_elixir(params, opts) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/cons.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Cons do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | defp get_line_number({atom, [line: ln], l1}) when is_atom(atom) and is_list(l1) do 6 | ln 7 | end 8 | 9 | defp get_line_number({atom, [line: ln], atom2}) when is_atom(atom) and is_atom(atom2) do 10 | ln 11 | end 12 | 13 | defp get_line_number(other) do 14 | get_line_number(elem(other, 0)) 15 | end 16 | 17 | def to_elixir({:cons, _ln, c1, c2}, %{parents: [:cons | _]} = opts) do 18 | case {Translate.to_elixir(c1, opts), Translate.to_elixir(c2, opts)} do 19 | {cc1, cc2} when is_tuple(cc1) and is_tuple(cc2) -> 20 | ln2 = get_line_number(cc1) 21 | [{:|, [line: get_line(ln2)], [cc1, cc2]}] 22 | 23 | {cc1, cc2} -> 24 | [cc1 | cc2] 25 | end 26 | end 27 | 28 | def to_elixir({:cons, ln, _c1, _c2} = params, opts) do 29 | opts = Map.update!(opts, :parents, &[:cons | &1]) 30 | result = to_elixir(params, opts) 31 | 32 | if List.ascii_printable?(result) do 33 | {:sigil_c, [delimiter: "\"", line: get_line(ln)], 34 | [{:<<>>, [line: get_line(ln)], [to_string(result)]}, []]} 35 | else 36 | result 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/float.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Float do 2 | def to_elixir({:float, _ln, f1}, _) do 3 | f1 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/fun.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Fun do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:fun, ln, {:function, caller, number_of_params}}, _) do 6 | {:&, [line: get_line(ln)], 7 | [{:/, [line: get_line(ln)], [{caller, [line: get_line(ln)], nil}, number_of_params]}]} 8 | end 9 | 10 | def to_elixir({:fun, ln, param}, opts) do 11 | opts = Map.update!(opts, :parents, &[:fun | &1]) 12 | 13 | opts = Map.update!(opts, :parents, &[:fun | &1]) 14 | 15 | case Translate.to_elixir(param, opts) do 16 | [{:&, line, body}] -> {:&, line, body} 17 | p1 -> {:fn, [line: get_line(ln)], p1} 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/if.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.If do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:if, ln, params}, opts) do 6 | opts = Map.update!(opts, :parents, &[:if | &1]) 7 | {:cond, [line: get_line(ln)], [[do: Translate.to_elixir(params, opts)]]} 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/integer.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Integer do 2 | def to_elixir({:integer, _ln, i1}, _) do 3 | i1 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/lc.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Lc do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:lc, ln, param1, [param2]}, opts) do 6 | opts = Map.update!(opts, :parents, &[:lc | &1]) 7 | 8 | {:for, [line: get_line(ln)], 9 | [Translate.to_elixir(param2, opts), Translate.to_elixir(param1, opts)]} 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/list.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.List do 2 | alias BeamToExAst.Translate 3 | 4 | def to_elixir(params, opts) do 5 | Enum.map(params, &Translate.to_elixir(&1, opts)) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/map.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Map do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:map, ln, items}, opts) do 6 | opts = Map.update!(opts, :parents, &[:map | &1]) 7 | 8 | case Translate.to_elixir(items, opts) do 9 | [ 10 | __struct__: Regex, 11 | opts: "", 12 | re_pattern: {:{}, [line: ln2], [:re_pattern, 0, 0, 0, _]}, 13 | re_version: _, 14 | source: b1 15 | ] -> 16 | {:sigil_r, [line: get_line(ln)], [{:<<>>, [line: get_line(ln2)], [b1]}, []]} 17 | 18 | p1 -> 19 | {:%{}, [line: get_line(ln)], p1} 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/map_field_assoc.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.MapFieldAssoc do 2 | alias BeamToExAst.Translate 3 | 4 | def to_elixir({:map_field_assoc, _ln, key, val}, opts) do 5 | opts = Map.update!(opts, :parents, &[:map_field_assoc | &1]) 6 | {Translate.to_elixir(key, opts), Translate.to_elixir(val, opts)} 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/map_field_exact.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.MapFieldExact do 2 | alias BeamToExAst.Translate 3 | 4 | def to_elixir({:map_field_exact, _ln, key, val}, opts) do 5 | {Translate.to_elixir(key, opts), Translate.to_elixir(val, opts)} 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/match.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Match do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:match, _ln, {:var, _, _}, {:atom, _, nil}}, _) do 6 | :filter_this_thing_out_of_the_list_please 7 | end 8 | 9 | def to_elixir({:match, _ln, {:var, _, v1}, {:var, _, v1}}, _) do 10 | :filter_this_thing_out_of_the_list_please 11 | end 12 | 13 | def to_elixir({:match, ln, m1, m2}, opts) do 14 | opts = Map.update!(opts, :parents, &[:match | &1]) 15 | {:=, [line: get_line(ln)], [Translate.to_elixir(m1, opts), Translate.to_elixir(m2, opts)]} 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/nil.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Nil do 2 | def to_elixir({nil, _ln}, _) do 3 | [] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/op.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Op1 do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:op, ln, op1, p1}, opts) do 6 | opts = Map.update!(opts, :parents, &[:op | &1]) 7 | {clean_op(op1), [line: get_line(ln)], [Translate.to_elixir(p1, opts)]} 8 | end 9 | end 10 | 11 | defmodule BeamToExAst.Op2 do 12 | import BeamToExAst 13 | alias BeamToExAst.Translate 14 | 15 | def to_elixir({:op, ln, :!, p1, p2}, opts) do 16 | {:send, [line: get_line(ln)], [Translate.to_elixir(p1, opts), Translate.to_elixir(p2, opts)]} 17 | end 18 | 19 | def to_elixir({:op, ln, op1, p1, p2}, opts) do 20 | opts = Map.update!(opts, :parents, &[:op | &1]) 21 | 22 | {clean_op(op1), [line: get_line(ln)], 23 | [insert_line_number(Translate.to_elixir(p1, opts), ln), Translate.to_elixir(p2, opts)]} 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/receive.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Receive1 do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:receive, ln, params}, opts) do 6 | opts = Map.update!(opts, :parents, &[:receive | &1]) 7 | {:receive, [line: get_line(ln)], [[do: Translate.to_elixir(params, opts)]]} 8 | end 9 | end 10 | 11 | defmodule BeamToExAst.Receive2 do 12 | import BeamToExAst 13 | alias BeamToExAst.Translate 14 | 15 | def to_elixir({:receive, ln, params, period, after_body}, opts) do 16 | opts = Map.update!(opts, :parents, &[:receive | &1]) 17 | 18 | {:receive, [line: get_line(ln)], 19 | [ 20 | [ 21 | do: Translate.to_elixir(params, opts), 22 | after: [ 23 | {:->, [], 24 | [[Translate.to_elixir(period, opts)] | Translate.to_elixir(after_body, opts)]} 25 | ] 26 | ] 27 | ]} 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/record.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Record do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:record, ln, name, params}, opts) do 6 | opts = Map.update!(opts, :parents, &[:record | &1]) 7 | {:record, [line: get_line(ln)], [clean_atom(name, opts), Translate.to_elixir(params, opts)]} 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/record_field.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.RecordField do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:record_field, _ln, {:atom, _ln2, name}, p2}, opts) do 6 | opts = Map.update!(opts, :parents, &[:record_field | &1]) 7 | {clean_atom(name, opts), Translate.to_elixir(p2, opts)} 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/remote.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Remote do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir( 6 | {:remote, ln, {:atom, _ln, :erlang}, {:atom, ln2, :atom_to_binary}}, 7 | %{parents: [:call | _]} = opts 8 | ) do 9 | opts = Map.update!(opts, :parents, &[:remote | &1]) 10 | {params, opts} = Map.pop(opts, :call_params) 11 | 12 | {{:., [line: get_line(ln)], [{:__aliases__, [line: get_line(ln2)], [:Atom]}, :to_string]}, 13 | [line: get_line(ln2)], Translate.to_elixir(params, opts)} 14 | end 15 | 16 | def to_elixir( 17 | {:remote, ln, {:atom, _ln, :erlang}, {:atom, ln2, :binary_to_atom}}, 18 | %{parents: [:call | _]} = opts 19 | ) do 20 | opts = Map.update!(opts, :parents, &[:remote | &1]) 21 | {params, opts} = Map.pop(opts, :call_params) 22 | 23 | {{:., [line: get_line(ln)], [{:__aliases__, [line: get_line(ln2)], [:String]}, :to_atom]}, 24 | [line: get_line(ln2)], List.delete_at(Translate.to_elixir(params, opts), -1)} 25 | end 26 | 27 | def to_elixir( 28 | {:remote, ln, {:atom, _ln, :erlang}, {:atom, ln2, :binary_to_integer}}, 29 | %{parents: [:call | _]} = opts 30 | ) do 31 | opts = Map.update!(opts, :parents, &[:remote | &1]) 32 | {params, opts} = Map.pop(opts, :call_params) 33 | 34 | {{:., [line: get_line(ln)], [{:__aliases__, [line: get_line(ln2)], [:String]}, :to_integer]}, 35 | [line: get_line(ln2)], Translate.to_elixir(params, opts)} 36 | end 37 | 38 | def to_elixir( 39 | {:remote, ln, {:atom, _, mod_call}, {:atom, _, caller}}, 40 | %{parents: [:call | _]} = opts 41 | ) do 42 | opts = Map.update!(opts, :parents, &[:remote | &1]) 43 | {params, opts} = Map.pop(opts, :call_params) 44 | 45 | case half_clean_atom(mod_call, opts) do 46 | "erlang" -> {caller, [line: get_line(ln)], Translate.to_elixir(params, opts)} 47 | "Kernel" -> {caller, [line: get_line(ln)], Translate.to_elixir(params, opts)} 48 | c_mod_call -> get_caller(c_mod_call, ln, caller, params, opts) 49 | end 50 | end 51 | 52 | def to_elixir( 53 | {:remote, _meta, var = {:var, ln, _var}, {:atom, _, caller}}, 54 | %{parents: [:call | _]} = opts 55 | ) do 56 | opts = Map.update!(opts, :parents, &[:remote | &1]) 57 | {params, opts} = Map.pop(opts, :call_params) 58 | 59 | {{:., [line: get_line(ln)], [Translate.to_elixir(var, opts), clean_atom(caller, opts)]}, 60 | [line: get_line(ln)], Translate.to_elixir(params, opts)} 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/string.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.String do 2 | def to_elixir({:string, _ln, s1}, %{parents: [:bin_element | _]}) when is_list(s1) do 3 | List.to_string(s1) 4 | end 5 | 6 | def to_elixir({:string, _ln, s1}, _opts) when is_list(s1) do 7 | s1 8 | end 9 | 10 | def to_elixir({:string, _ln, s1}, _opts) do 11 | s1 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/translate.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Translate do 2 | def to_elixir({:atom, _ln, _a1} = ast, opts), do: BeamToExAst.Atom.to_elixir(ast, opts) 3 | def to_elixir({:bc, _ln, _param1, _param2} = ast, opts), do: BeamToExAst.Bc.to_elixir(ast, opts) 4 | 5 | def to_elixir({:b_generate, _ln, _param1, _param2} = ast, opts), 6 | do: BeamToExAst.BGenerate.to_elixir(ast, opts) 7 | 8 | def to_elixir({:bin_element, _ln, _block, _, _} = ast, opts), 9 | do: BeamToExAst.BinElement.to_elixir(ast, opts) 10 | 11 | def to_elixir({:bin, _ln, _params} = ast, opts), do: BeamToExAst.Bin.to_elixir(ast, opts) 12 | def to_elixir({:block, _ln, _param} = ast, opts), do: BeamToExAst.Block.to_elixir(ast, opts) 13 | 14 | def to_elixir({:call, _ln, _caller, _params} = ast, opts), 15 | do: BeamToExAst.Call.to_elixir(ast, opts) 16 | 17 | def to_elixir({:case, _ln, _param, _body} = ast, opts), 18 | do: BeamToExAst.Case.to_elixir(ast, opts) 19 | 20 | def to_elixir({:char, _ln, _c1} = ast, opts), do: BeamToExAst.Char.to_elixir(ast, opts) 21 | 22 | def to_elixir({:clause, _ln, _params, _guard, _body} = ast, opts), 23 | do: BeamToExAst.Clause.to_elixir(ast, opts) 24 | 25 | def to_elixir({:clauses, _params} = ast, opts), do: BeamToExAst.Clauses.to_elixir(ast, opts) 26 | 27 | def to_elixir({:cons, _ln, _param1, _param2} = ast, opts), 28 | do: BeamToExAst.Cons.to_elixir(ast, opts) 29 | 30 | def to_elixir({:float, _ln, _f1} = ast, opts), do: BeamToExAst.Float.to_elixir(ast, opts) 31 | def to_elixir({:fun, _ln, _params} = ast, opts), do: BeamToExAst.Fun.to_elixir(ast, opts) 32 | def to_elixir({:if, _ln, _params} = ast, opts), do: BeamToExAst.If.to_elixir(ast, opts) 33 | def to_elixir({:integer, _ln, _i1} = ast, opts), do: BeamToExAst.Integer.to_elixir(ast, opts) 34 | def to_elixir({:lc, _ln, _param1, _param2} = ast, opts), do: BeamToExAst.Lc.to_elixir(ast, opts) 35 | def to_elixir(ast, opts) when is_list(ast), do: BeamToExAst.List.to_elixir(ast, opts) 36 | def to_elixir({:map, _ln, _items} = ast, opts), do: BeamToExAst.Map.to_elixir(ast, opts) 37 | 38 | def to_elixir({:map_field_assoc, _ln, _key, _val} = ast, opts), 39 | do: BeamToExAst.MapFieldAssoc.to_elixir(ast, opts) 40 | 41 | def to_elixir({:map_field_exact, _ln, _key, _val} = ast, opts), 42 | do: BeamToExAst.MapFieldExact.to_elixir(ast, opts) 43 | 44 | def to_elixir({:match, _ln, _param1, _param2} = ast, opts), 45 | do: BeamToExAst.Match.to_elixir(ast, opts) 46 | 47 | def to_elixir({nil, _ln} = ast, opts), do: BeamToExAst.Nil.to_elixir(ast, opts) 48 | def to_elixir({:op, _ln, _op, _param} = ast, opts), do: BeamToExAst.Op1.to_elixir(ast, opts) 49 | 50 | def to_elixir({:op, _ln, _op, _param1, _param2} = ast, opts), 51 | do: BeamToExAst.Op2.to_elixir(ast, opts) 52 | 53 | def to_elixir({:receive, _ln, _params} = ast, opts), 54 | do: BeamToExAst.Receive1.to_elixir(ast, opts) 55 | 56 | def to_elixir({:receive, _ln, _params, _period, _after_body} = ast, opts), 57 | do: BeamToExAst.Receive2.to_elixir(ast, opts) 58 | 59 | def to_elixir({:record, _ln, _name, _params} = ast, opts), 60 | do: BeamToExAst.Record.to_elixir(ast, opts) 61 | 62 | def to_elixir({:record_field, _ln, _param1, _param2} = ast, opts), 63 | do: BeamToExAst.RecordField.to_elixir(ast, opts) 64 | 65 | def to_elixir({:remote, _ln, _param1, _param2} = ast, opts), 66 | do: BeamToExAst.Remote.to_elixir(ast, opts) 67 | 68 | def to_elixir({:string, _ln, _s1} = ast, opts), do: BeamToExAst.String.to_elixir(ast, opts) 69 | 70 | def to_elixir( 71 | {:try, _ln, _params, _else_params, _catch_rescue_params, _after_params} = ast, 72 | opts 73 | ), 74 | do: BeamToExAst.Try.to_elixir(ast, opts) 75 | 76 | def to_elixir({:tuple, _ln, _items} = ast, opts), do: BeamToExAst.Tuple.to_elixir(ast, opts) 77 | def to_elixir({:var, _ln, _var} = ast, opts), do: BeamToExAst.Var.to_elixir(ast, opts) 78 | # def to_elixir( = ast, opts), do: .to_elixir(ast, opts) 79 | 80 | def to_elixir(ast, opts) do 81 | _opts = opts 82 | 83 | s1 = 84 | :forms.from_abstract(ast) 85 | |> List.to_string() 86 | 87 | {:=, [], [{:_untranslated, [], nil}, s1]} 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/try.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Try do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:try, ln, params, else_params, catch_rescue_params, after_params}, opts) do 6 | opts = Map.update!(opts, :parents, &[:try | &1]) 7 | 8 | {:try, [line: get_line(ln)], 9 | [ 10 | [ 11 | do: Translate.to_elixir(params, opts), 12 | # rescue: Translate.to_elixir(rescue_params, opts), 13 | catch: Translate.to_elixir(catch_rescue_params, opts), 14 | else: Translate.to_elixir(else_params, opts), 15 | after: Translate.to_elixir(after_params, opts) 16 | ] 17 | ]} 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/tuple.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Tuple do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:tuple, ln, [{:atom, _ln2, a1}, {:atom, _ln3, a2}]}, opts) do 6 | # Need the correct line number to dogfood 7 | {a1, {:__aliases__, [line: get_line(ln)], [clean_atom(a2, opts)]}} 8 | end 9 | 10 | def to_elixir({:tuple, _ln, [{:atom, _ln2, a1}, p2]}, opts) do 11 | opts = Map.update!(opts, :parents, &[:tuple | &1]) 12 | {a1, Translate.to_elixir(p2, opts)} 13 | end 14 | 15 | def to_elixir({:tuple, _ln, items}, opts) when length(items) === 2 do 16 | opts = Map.update!(opts, :parents, &[:tuple | &1]) 17 | {Translate.to_elixir(List.first(items), opts), Translate.to_elixir(List.last(items), opts)} 18 | end 19 | 20 | def to_elixir({:tuple, ln, items}, opts) do 21 | opts = Map.update!(opts, :parents, &[:tuple | &1]) 22 | {:{}, [line: get_line(ln)], Translate.to_elixir(items, opts)} 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/beam_to_ex_ast/var.ex: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Var do 2 | import BeamToExAst 3 | alias BeamToExAst.Translate 4 | 5 | def to_elixir({:var, ln, caller}, %{parents: [:call | _]} = opts) do 6 | opts = Map.update!(opts, :parents, &[:var | &1]) 7 | {params, opts} = Map.pop(opts, :call_params) 8 | 9 | {{:., [line: get_line(ln)], [{clean_var(caller, opts), [line: get_line(ln)], nil}]}, 10 | [line: get_line(ln)], Translate.to_elixir(params, opts)} 11 | end 12 | 13 | def to_elixir({:var, ln, var}, opts) do 14 | case Atom.to_string(var) do 15 | <<"_@", rest::binary>> -> 16 | capture(ln, rest) 17 | 18 | <<"__@", rest::binary>> -> 19 | capture(ln, rest) 20 | 21 | <<"_capture@", rest::binary>> -> 22 | capture(ln, rest) 23 | 24 | _ -> 25 | {clean_var(var, opts), [line: get_line(ln)], nil} 26 | end 27 | end 28 | 29 | defp capture(ln, rest) do 30 | case Integer.parse(rest) do 31 | {number, ""} -> 32 | {:&, [line: get_line(ln)], [number]} 33 | :error -> 34 | {:&, [line: get_line(ln)], [rest]} 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAst.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :beam_to_ex_ast, 7 | version: "0.4.1", 8 | elixir: "~> 1.1", 9 | description: "Beam AST to Elixir AST transpiler", 10 | package: package(), 11 | compilers: Mix.compilers(), 12 | elixirc_paths: elixirc_paths(Mix.env()), 13 | build_embedded: Mix.env() == :prod, 14 | start_permanent: Mix.env() == :prod, 15 | extra_applications: [:logger], 16 | deps: deps() 17 | ] 18 | end 19 | 20 | defp package do 21 | [ 22 | files: ["lib", "mix.exs", "README*", "LICENSE*"], 23 | maintainers: ["Olafur Arason"], 24 | licenses: ["Apache-2.0"], 25 | links: %{"GitHub" => "https://github.com/olafura/beam_to_ex_ast"} 26 | ] 27 | end 28 | 29 | defp elixirc_paths(:test), do: ["lib", "test/support"] 30 | defp elixirc_paths(_), do: ["lib"] 31 | 32 | # Dependencies can be Hex packages: 33 | # 34 | # {:mydep, "~> 0.3.0"} 35 | # 36 | # Or git/path repositories: 37 | # 38 | # {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1.0"} 39 | # 40 | # Type "mix help deps" for more examples and options 41 | defp deps do 42 | [ 43 | {:ex_doc, "~> 0.34", only: :dev, runtime: false}, 44 | {:forms, "~> 0.0.1"}, 45 | {:credo, "~> 1.7", only: [:dev, :test], runtime: false} 46 | ] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /mix.lock: -------------------------------------------------------------------------------- 1 | %{ 2 | "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, 3 | "credo": {:hex, :credo, "1.7.12", "9e3c20463de4b5f3f23721527fcaf16722ec815e70ff6c60b86412c695d426c1", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8493d45c656c5427d9c729235b99d498bd133421f3e0a683e5c1b561471291e5"}, 4 | "dogma": {:hex, :dogma, "0.1.15", "5bceba9054b2b97a4adcb2ab4948ca9245e5258b883946e82d32f785340fd411", [:mix], [{:poison, ">= 2.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"}, 5 | "earmark": {:hex, :earmark, "1.2.5", "4d21980d5d2862a2e13ec3c49ad9ad783ffc7ca5769cf6ff891a4553fbaae761", [:mix], [], "hexpm", "c57508ddad47dfb8038ca6de1e616e66e9b87313220ac5d9817bc4a4dc2257b9"}, 6 | "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, 7 | "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, 8 | "file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"}, 9 | "forms": {:hex, :forms, "0.0.1", "45f3b10b6f859f95f2c2c1a1de244d63855296d55ed8e93eb0dd116b3e86c4a6", [:rebar3], [], "hexpm", "530f63ed8ed5a171f744fc75bd69cb2e36496899d19dbef48101b4636b795868"}, 10 | "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, 11 | "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, 12 | "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, 13 | "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, 14 | "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, 15 | "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"}, 16 | "protocol_ex": {:hex, :protocol_ex, "0.4.4", "c9717d1c0bdabe37d7653965dc02e78580d0d06a1f86d737b7941b55241f70d6", [:mix], [], "hexpm", "2b78ed0e5ec76f62b0debaf92dc8e795551cdaf7fc22b8816b3d57225020ac99"}, 17 | } 18 | -------------------------------------------------------------------------------- /test/beam_to_ex_ast_test.exs: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAstTest do 2 | use ExUnit.Case 3 | doctest BeamToExAst 4 | ExUnit.configure(exclude: [wip: true, dogfood: true]) 5 | import BeamToExAstTestUtils 6 | 7 | @builddir ~c"_build/test/lib/beam_to_ex_ast/ebin/" 8 | 9 | test "function" do 10 | fun_beam = 11 | {:function, 2, :hello, 0, 12 | [ 13 | {:clause, 2, [], [], 14 | [ 15 | {:call, 3, {:remote, 3, {:atom, 0, IO}, {:atom, 3, :puts}}, 16 | [ 17 | {:bin, 0, 18 | [ 19 | {:bin_element, 0, {:string, 0, ~c"Hello world"}, :default, :default} 20 | ]} 21 | ]} 22 | ]} 23 | ]} 24 | 25 | fun_ast = 26 | {:def, [line: 2], 27 | [ 28 | {:hello, [line: 2], []}, 29 | [ 30 | do: 31 | {{:., [line: 3], [{:__aliases__, [line: 3], [:IO]}, :puts]}, [line: 3], 32 | ["Hello world"]} 33 | ] 34 | ]} 35 | 36 | res1 = BeamToExAst.do_convert(fun_beam, {"", [], %{elixir: true}}) 37 | assert List.first(elem(res1, 1)) == fun_ast 38 | end 39 | 40 | test "module" do 41 | file = "test/support/function.ex" 42 | file_content = File.read!(file) 43 | beam_file = @builddir ++ ~c"Elixir.TestFunction.beam" 44 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 45 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 46 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 47 | end 48 | 49 | test "module function body" do 50 | file = "test/support/function_body.ex" 51 | file_content = File.read!(file) 52 | beam_file = @builddir ++ ~c"Elixir.TestFunctionBody.beam" 53 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 54 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 55 | assert clean_ast(BeamToExAst.convert(mod_beam, %{elixir: true})) == clean_ast(mod_ast) 56 | end 57 | 58 | test "module functions" do 59 | file = "test/support/functions.ex" 60 | file_content = File.read!(file) 61 | beam_file = @builddir ++ ~c"Elixir.TestFunctions.beam" 62 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 63 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 64 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 65 | end 66 | 67 | test "int" do 68 | file = "test/support/function_int.ex" 69 | file_content = File.read!(file) 70 | beam_file = @builddir ++ ~c"Elixir.TestFunctionInt.beam" 71 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 72 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 73 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 74 | end 75 | 76 | test "float" do 77 | file = "test/support/function_float.ex" 78 | file_content = File.read!(file) 79 | beam_file = @builddir ++ ~c"Elixir.TestFunctionFloat.beam" 80 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 81 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 82 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 83 | end 84 | 85 | test "atom" do 86 | file = "test/support/function_atom.ex" 87 | file_content = File.read!(file) 88 | beam_file = @builddir ++ ~c"Elixir.TestFunctionAtom.beam" 89 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 90 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 91 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 92 | end 93 | 94 | test "lists" do 95 | file = "test/support/function_lists.ex" 96 | file_content = File.read!(file) 97 | beam_file = @builddir ++ ~c"Elixir.TestFunctionLists.beam" 98 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 99 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 100 | # IO.inspect(mod_beam) 101 | # IO.inspect(mod_ast) 102 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 103 | end 104 | 105 | test "tuple" do 106 | file = "test/support/function_tuple.ex" 107 | file_content = File.read!(file) 108 | beam_file = @builddir ++ ~c"Elixir.TestFunctionTuple.beam" 109 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 110 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 111 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 112 | end 113 | 114 | test "map" do 115 | file = "test/support/function_map.ex" 116 | file_content = File.read!(file) 117 | beam_file = @builddir ++ ~c"Elixir.TestFunctionMap.beam" 118 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 119 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 120 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 121 | end 122 | 123 | test "math" do 124 | file = "test/support/function_math.ex" 125 | file_content = File.read!(file) 126 | beam_file = @builddir ++ ~c"Elixir.TestFunctionMath.beam" 127 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 128 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 129 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 130 | end 131 | 132 | test "bool compare" do 133 | file = "test/support/function_bool_compare.ex" 134 | file_content = File.read!(file) 135 | beam_file = @builddir ++ ~c"Elixir.TestFunctionBoolCompare.beam" 136 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 137 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 138 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 139 | end 140 | 141 | test "case" do 142 | file = "test/support/function_case.ex" 143 | file_content = File.read!(file) 144 | beam_file = @builddir ++ ~c"Elixir.TestFunctionCase.beam" 145 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 146 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 147 | assert clean_ast(BeamToExAst.convert(mod_beam, %{elixir: true})) == clean_ast(mod_ast) 148 | # mod_ast2 = BeamToExAst.convert(mod_beam) 149 | # unless mod_ast2 == mod_ast do 150 | # find_diff(mod_ast2, mod_ast) 151 | # end 152 | end 153 | 154 | test "pipe" do 155 | file = "test/support/function_pipe.ex" 156 | file_content = File.read!(file) 157 | beam_file = @builddir ++ ~c"Elixir.TestFunctionPipe.beam" 158 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 159 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 160 | assert clean_ast(BeamToExAst.convert(mod_beam, %{elixir: true})) == clean_ast(mod_ast) 161 | end 162 | 163 | test "binary" do 164 | file = "test/support/function_binary.ex" 165 | file_content = File.read!(file) 166 | beam_file = @builddir ++ ~c"Elixir.TestFunctionBinary.beam" 167 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 168 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 169 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 170 | end 171 | 172 | @tag :wip 173 | test "record" do 174 | file = "test/support/function_record.ex" 175 | file_content = File.read!(file) 176 | beam_file = @builddir ++ ~c"Elixir.TestFunctionRecord.beam" 177 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 178 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 179 | # IO.inspect(mod_beam) 180 | # IO.inspect(mod_ast) 181 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 182 | end 183 | 184 | @tag :wip 185 | test "try" do 186 | file = "test/support/function_try.ex" 187 | file_content = File.read!(file) 188 | beam_file = @builddir ++ ~c"Elixir.TestFunctionTry.beam" 189 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 190 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 191 | # IO.inspect(mod_beam) 192 | # IO.inspect(mod_ast) 193 | assert BeamToExAst.convert(mod_beam, %{elixir: true}) == mod_ast 194 | end 195 | 196 | @tag :dogfood 197 | test "dogfood" do 198 | file = "lib/beam_to_ex_ast.ex" 199 | file_content = File.read!(file) 200 | beam_file = @builddir ++ ~c"Elixir.BeamToExAst.beam" 201 | {:ok, {_, [{:abstract_code, {_, mod_beam}}]}} = :beam_lib.chunks(beam_file, [:abstract_code]) 202 | {:ok, mod_ast} = Code.string_to_quoted(file_content) 203 | # IO.inspect(mod_beam) 204 | # IO.inspect(clean_ast(mod_ast)) 205 | assert clean_ast(BeamToExAst.convert(mod_beam, %{elixir: true})) == clean_ast(mod_ast) 206 | # mod_ast2 = BeamToExAst.convert(mod_beam) 207 | # unless mod_ast2 == clean_ast(mod_ast) do 208 | # find_diff(mod_ast2, clean_ast(mod_ast)) 209 | # end 210 | end 211 | end 212 | -------------------------------------------------------------------------------- /test/support/function.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunction do 2 | def hello() do 3 | prufa = fn -> IO.puts("Hello world") end 4 | prufa.() 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/support/function_atom.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionAtom do 2 | def hello() do 3 | IO.puts(true) 4 | IO.puts(:atom) 5 | IO.puts(Atom.to_string(:atom)) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/support/function_binary.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionBinary do 2 | def hello() do 3 | <<"H", rest::binary>> = "Hello world" 4 | IO.puts("H" <> rest) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/support/function_body.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionBody do 2 | def hello() do 3 | IO.puts("Hello world") 4 | IO.puts("Hello world") 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/support/function_bool_compare.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionBoolCompare do 2 | def hello() do 3 | IO.inspect(true === false) 4 | IO.inspect(true == false) 5 | IO.inspect(true !== false) 6 | IO.inspect(true != false) 7 | IO.inspect(1 <= 2) 8 | IO.inspect(1 < 2) 9 | IO.inspect(1 > 2) 10 | IO.inspect(1 =~ "1") 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/support/function_case.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionCase do 2 | def hello() do 3 | case true do 4 | true -> IO.puts("true") 5 | false -> IO.puts("false") 6 | end 7 | end 8 | 9 | def hello(1) do 10 | IO.puts(1) 11 | end 12 | 13 | def hello(msg) do 14 | IO.puts(msg) 15 | 16 | Enum.filter([1, 2, 3, 4], fn 17 | x when x < 3 -> rem(x, 2) == 0 18 | _x -> true 19 | end) 20 | end 21 | 22 | def hello2(msg) do 23 | IO.puts(msg) 24 | 25 | case {1, 2} do 26 | {i1, i2} when is_integer(i1) and is_integer(i2) -> 27 | {_, i, _} = {1, 2, 3} 28 | i1 + i2 + i 29 | 30 | _ -> 31 | IO.puts("not numbers") 32 | end 33 | end 34 | 35 | def hello3(l1) when is_list(l1) do 36 | l1 37 | |> Enum.filter(fn x -> rem(x, 2) == 0 end) 38 | |> Enum.map(&(&1 * 2)) 39 | end 40 | 41 | def map_case(test) do 42 | case test do 43 | %{a: a} -> a 44 | %{b: b} -> b 45 | end 46 | end 47 | 48 | def test_map() do 49 | test = %{a: 1} 50 | map_case(test) 51 | end 52 | 53 | def convert_param({:cons, _ln, c1, c2}) do 54 | case {convert_param(c1), convert_param(c2)} do 55 | {cc1, cc2} when is_tuple(cc1) and is_tuple(cc2) -> 56 | {_, [line: ln2], _} = cc1 57 | [{:|, [line: ln2], [cc1, cc2]}] 58 | 59 | {cc1, cc2} -> 60 | [cc1 | cc2] 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /test/support/function_float.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionFloat do 2 | def hello() do 3 | IO.puts(1.0) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/support/function_int.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionInt do 2 | def hello() do 3 | IO.puts(1) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/support/function_lists.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionLists do 2 | def hello() do 3 | for <>, do: <> 4 | 5 | for <> do 6 | IO.inspect("prufa") 7 | <> 8 | end 9 | 10 | for <>, c != ?\s, into: "", do: <> 11 | # This is kind of complex since it gets mangled in the compile process 12 | # for <>, c != ?\s, into: "hi, ", do: <> 13 | IO.inspect(~c"Hello world") 14 | IO.inspect([1, 2, 3]) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/support/function_map.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionMap do 2 | def hello() do 3 | IO.inspect(%{a: 1, b: 2, c: 3}) 4 | IO.inspect(%{"a" => 1, "b" => 2, "c" => 3}) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/support/function_math.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionMath do 2 | def hello() do 3 | res = 1 + 2 4 | IO.puts(res) 5 | res2 = 1 * 2 6 | IO.puts(res2) 7 | res3 = 1 / 2 8 | IO.puts(res3) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/support/function_pipe.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionPipe do 2 | def hello() do 3 | "Hello world" 4 | |> IO.puts() 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/support/function_record.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionRecord do 2 | require Record 3 | Record.defrecord(:user, name: "Olaf", age: 36) 4 | 5 | @type user :: record(:user, name: String.t(), age: integer) 6 | 7 | def hello() do 8 | u1 = user() 9 | IO.puts("Hello " <> user(u1, :name)) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/support/function_try.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionTry do 2 | def hello() do 3 | try do 4 | raise "oops" 5 | rescue 6 | e in RuntimeError -> e 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/support/function_tuple.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctionTuple do 2 | def hello() do 3 | IO.inspect({1, 2, 3}) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/support/functions.ex: -------------------------------------------------------------------------------- 1 | defmodule TestFunctions do 2 | def hello() do 3 | test_log("Hello world") 4 | end 5 | 6 | def test_log(msg) do 7 | IO.puts(msg) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | defmodule BeamToExAstTestUtils do 2 | def ast_to_erl(ast) do 3 | e = :elixir.env_for_eval([]) 4 | :elixir.quoted_to_erl(ast, e) 5 | end 6 | 7 | def clean_ast(ast) do 8 | Macro.postwalk(ast, fn 9 | {:|>, _line1, [p1, {c1, line2, nil}]} -> 10 | {c1, line2, Enum.concat([p1], [])} 11 | 12 | {:|>, _line1, [p1, {c1, line2, p2}]} -> 13 | {c1, line2, Enum.concat([clean_ast(p1)], clean_ast(p2))} 14 | 15 | {atom, opts, params} when is_list(opts) -> 16 | {atom, Keyword.delete(opts, :line), params} 17 | 18 | other -> 19 | other 20 | end) 21 | end 22 | 23 | def find_diff([h1 | t1] = l1, [h2 | t2] = l2) when is_list(l1) and is_list(l2) do 24 | if l1 != l2 do 25 | IO.inspect("match1") 26 | IO.inspect(l1) 27 | IO.inspect(l2) 28 | find_diff(h1, h2) 29 | find_diff(t1, t2) 30 | end 31 | end 32 | 33 | def find_diff({:do, {:__block__, _g1, l1}}, {:do, {:__block__, _g2, l2}}) do 34 | if l1 != l2 do 35 | IO.inspect("match2") 36 | IO.inspect(l1) 37 | IO.inspect(l2) 38 | find_diff(l1, l2) 39 | end 40 | end 41 | 42 | def find_diff([do: {:__block__, _g1, l1}], do: {:__block__, _g2, l2}) do 43 | if l1 != l2 do 44 | IO.inspect("match2") 45 | IO.inspect(l1) 46 | IO.inspect(l2) 47 | find_diff(l1, l2) 48 | end 49 | end 50 | 51 | def find_diff({:do, p1}, {:do, p2}) do 52 | if p1 != p2 do 53 | IO.inspect("match3") 54 | IO.inspect(p1) 55 | IO.inspect(p2) 56 | end 57 | end 58 | 59 | def find_diff([do: p1], do: p2) do 60 | if p1 != p2 do 61 | IO.inspect("match3") 62 | IO.inspect(p1) 63 | IO.inspect(p2) 64 | end 65 | end 66 | 67 | def find_diff({:defmodule, line, [_, l1]}, {:defmodule, line, [_, l2]}) do 68 | if l1 != l2 do 69 | IO.inspect("match4") 70 | IO.inspect(l1) 71 | IO.inspect(l2) 72 | find_diff(l1, l2) 73 | end 74 | end 75 | 76 | def find_diff({a, line, [h1 | t1] = l1}, {a, line, [h2 | t2] = l2}) 77 | when is_list(l1) and is_list(l2) do 78 | if l1 != l2 do 79 | IO.inspect("match5") 80 | IO.inspect(l1) 81 | IO.inspect(l2) 82 | find_diff(h1, h2) 83 | find_diff(t1, t2) 84 | end 85 | end 86 | 87 | def find_diff([], []) do 88 | end 89 | 90 | def find_diff(i1, i2) do 91 | IO.inspect("nothing") 92 | IO.inspect(i1) 93 | IO.inspect(i2) 94 | end 95 | end 96 | 97 | ExUnit.configure(exclude: [wip: true, dogfood: true]) 98 | ExUnit.start() 99 | --------------------------------------------------------------------------------