├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── base.conf ├── cowboy.conf ├── efe.conf ├── otp.conf ├── rebar.config ├── rebar.lock ├── run.bash ├── samples.conf ├── samples ├── bincomp.erl ├── boollogic.erl ├── cons.erl ├── cornercases.erl ├── mapops.erl ├── records.erl ├── reserved_kws.erl ├── scopes.erl └── ueval.erl ├── shell.nix └── src ├── ast.erl ├── efe.app.src ├── efe.erl ├── efe_pp.erl └── efe_var_ann.erl /.gitignore: -------------------------------------------------------------------------------- 1 | .rebar3 2 | _* 3 | .eunit 4 | *.o 5 | *.beam 6 | *.plt 7 | *.swp 8 | *.swo 9 | .erlang.cookie 10 | ebin 11 | log 12 | erl_crash.dump 13 | .rebar 14 | logs 15 | _build 16 | .idea 17 | *.iml 18 | rebar3.crashdump 19 | *~ 20 | out 21 | .tool-versions 22 | efe 23 | otp 24 | cowboy 25 | cowlib 26 | TODO.txt 27 | errors*.txt 28 | efex 29 | result.txt 30 | samples/*.lfe 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | Copyright 2019, Mariano Guerra . 179 | 180 | Licensed under the Apache License, Version 2.0 (the "License"); 181 | you may not use this file except in compliance with the License. 182 | You may obtain a copy of the License at 183 | 184 | http://www.apache.org/licenses/LICENSE-2.0 185 | 186 | Unless required by applicable law or agreed to in writing, software 187 | distributed under the License is distributed on an "AS IS" BASIS, 188 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 189 | See the License for the specific language governing permissions and 190 | limitations under the License. 191 | 192 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | smoke-test: build 3 | ./efe pp samples.conf samples/*.erl 4 | 5 | lfe-test: lfe-samples build 6 | ./efe pp-lfe samples.conf samples/*.lfe 7 | 8 | lfe-samples: samples/ping_pong.lfe samples/simple-erl-exercises.lfe 9 | 10 | samples/ping_pong.lfe: 11 | wget https://raw.githubusercontent.com/lfe/lfe/develop/examples/ping_pong.lfe -O samples/ping_pong.lfe 12 | 13 | samples/simple-erl-exercises.lfe: 14 | wget https://raw.githubusercontent.com/lfe/lfe/develop/examples/simple-erl-exercises.lfe -O samples/simple-erl-exercises.lfe 15 | 16 | 17 | build: 18 | rebar3 escriptize 19 | cp _build/default/bin/efe . 20 | 21 | translate-otp: build 22 | ./efe pp otp.conf otp/lib/*/src/*.erl otp/erts/preloaded/src/*.erl 23 | 24 | compile-output: build 25 | ./run.bash compile-output 26 | 27 | format-output: 28 | ./run.bash format-output 29 | 30 | filter-result: 31 | @grep -v '^# ' out/result.txt | grep -v '== Compilation error in file' | grep -v '(elixir ' | grep -v '(stdlib 3' | sed 's/** (CompileError) //;s/** (SyntaxError) //' 32 | 33 | otp: 34 | git clone https://github.com/erlang/otp.git 35 | cd otp && git checkout OTP-24.0-rc1 36 | 37 | clean-inception: 38 | rm -rf efex out-erl out-ex out 39 | inception: clean-inception build otp translate-otp 40 | mv out out-erl 41 | mix new efex 42 | rm efex/lib/efex.ex 43 | ./efe pp efe.conf src/*.erl 44 | mv efex/lib/src/*.ex efex/lib/ 45 | rmdir efex/lib/src 46 | sed -i 's/deps()/deps(),\n escript: [main_module: :efe, name: "efe"]/g' efex/mix.exs 47 | cd efex && mix escript.build 48 | ./efex/efe pp otp.conf otp/lib/*/src/*.erl otp/erts/preloaded/src/*.erl 49 | mv out out-ex 50 | diff -r out-erl out-ex 51 | 52 | conf-test: build 53 | ./efe conf otp.conf otplib/stdlib/src/lists.erl otplib/stdlib/src/array.erl 54 | 55 | cons-test: build 56 | ./efe pp samples.conf samples/cons.erl 57 | @cat samples/cons.erl 58 | @cat out/samples/cons.ex 59 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elixir Flavoured Erlang 2 | 3 | An Erlang to Elixir Transpiler 4 | 5 | Check [otp.ex](https://github.com/marianoguerra/otp.ex) for examples of the output 6 | generated by this tool. 7 | 8 | Here are some blogposts with more details: 9 | 10 | - http://marianoguerra.org/posts/elixir-flavoured-erlang-an-erlang-to-elixir-transpiler/ 11 | - http://marianoguerra.org/posts/elixir-flavoured-erlang-who-transpiles-the-transpiler/ 12 | - http://marianoguerra.org/posts/how-to-transpile-a-complex-erlang-project-with-elixir-flavoured-erlang-erldns/ 13 | 14 | ## EFLFE: Elixir Flavoured Lisp Flavoured Erlang 15 | 16 | An LFE (Lisp Flavoured Erlang) to Elixir Transpiler 17 | 18 | ⚠️ Work in Progress ⚠️ 19 | 20 | ```sh 21 | ./efe pp-lfe file.conf my-code.lfe 22 | ``` 23 | 24 | Try some examples: 25 | 26 | ```sh 27 | make lfe-test 28 | ``` 29 | 30 | Check: 31 | 32 | - out/samples/ping_pong.ex 33 | - out/samples/sample-module.ex 34 | - out/samples/simple-erl-exercises.ex 35 | 36 | ## Build 37 | 38 | ```sh 39 | rebar3 escriptize 40 | cp _build/default/bin/efe . 41 | ``` 42 | 43 | ## Run 44 | 45 | The general format is: 46 | 47 | ```sh 48 | efe pp file.conf file.erl+ 49 | ``` 50 | 51 | where `file.conf` is a config file with the following format: 52 | 53 | ```erl 54 | #{ 55 | % blobs, if relative (start with .) are joined with current erlang file directory 56 | includes => [".", "../../*/include", "../../*/src"], 57 | % map of macro names and what they should expand to 58 | macros => #{'VSN' => '1.0', 'COMPILER_VSN' => '2.0'}, 59 | encoding => latin1, % or utf8 60 | output_dir => "./out", 61 | % prefix to add to compiled modules, useful when transpiling otp to avoid 62 | % conflicts 63 | mod_prefix => "m_" 64 | }. 65 | ``` 66 | 67 | Here is the config used to transpile [cowboy](https://github.com/ninenines/cowboy/) 68 | 69 | ```erl 70 | #{ 71 | includes => [".", "../include", "../../"], 72 | macros => #{}, 73 | encoding => utf8, 74 | output_dir => "./out" 75 | }. 76 | ``` 77 | 78 | ### Transpiling OTP 79 | 80 | ``` 81 | git clone git@github.com:erlang/otp.git 82 | mkdir -p out 83 | ./efe pp otp.conf otp/lib/*/src/*.erl otp/erts/preloaded/src/.erl 84 | 85 | ``` 86 | 87 | Results in out/otp/ 88 | 89 | ### Transpiling Cowboy 90 | 91 | ```sh 92 | git clone https://github.com/ninenines/cowboy.git 93 | # need header files from cowlib 94 | git clone https://github.com/ninenines/cowlib.git 95 | mkdir -p out 96 | ./efe pp cowboy.conf cowboy/src/*.erl 97 | ``` 98 | 99 | Results in out/cowboy 100 | -------------------------------------------------------------------------------- /base.conf: -------------------------------------------------------------------------------- 1 | #{ 2 | includes => [".", "../include"], 3 | macros => #{}, 4 | encoding => utf8, 5 | output_dir => "./out" 6 | }. 7 | -------------------------------------------------------------------------------- /cowboy.conf: -------------------------------------------------------------------------------- 1 | #{ 2 | includes => [".", "../include", "../../"], 3 | macros => #{}, 4 | encoding => utf8, 5 | output_dir => "./out" 6 | }. 7 | -------------------------------------------------------------------------------- /efe.conf: -------------------------------------------------------------------------------- 1 | #{ 2 | includes => [], 3 | macros => #{}, 4 | encoding => utf8, 5 | output_dir => "./efex/lib/" 6 | }. 7 | -------------------------------------------------------------------------------- /otp.conf: -------------------------------------------------------------------------------- 1 | #{ 2 | includes => [".", "../../*/include", "../../*/src"], 3 | macros => #{'VSN' => 'EFE_TODO_VSN_MACRO', 'COMPILER_VSN' => 'EFE_TODO_COMPILER_VSN_MACRO'}, 4 | encoding => latin1, 5 | output_dir => "./out", 6 | mod_prefix => "m_" 7 | }. 8 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | {erl_opts, [no_debug_info]}. 2 | {deps, [{lfe, "2.0.1"}]}. 3 | {project_plugins, [rebar3_format]}. 4 | {format, [ 5 | {files, ["src/*.erl", "include/*.hrl", "test/*.erl", "samples/*.erl"]}, 6 | {formatter, default_formatter}, 7 | {options, #{paper => 80}} 8 | ]}. 9 | 10 | {escript_incl_apps, [efe, lfe]}. 11 | {escript_main_app, efe}. 12 | {escript_name, efe}. 13 | {escript_emu_args, "%%! +sbtu +A1\n"}. 14 | 15 | %% Profiles 16 | {profiles, [{test, [{erl_opts, [debug_info]}]}]}. 17 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | {"1.2.0", 2 | [{<<"lfe">>,{pkg,<<"lfe">>,<<"2.0.1">>},0}]}. 3 | [ 4 | {pkg_hash,[ 5 | {<<"lfe">>, <<"C95185AC7E244EDB60C72CE96087D2D3FBC85334109B741790EF49F6A52E728A">>}]}, 6 | {pkg_hash_ext,[ 7 | {<<"lfe">>, <<"C3EA9732DF613186C14CAC840D25283B87914AE74D87AFCBA3230AAB41D25E9F">>}]} 8 | ]. 9 | -------------------------------------------------------------------------------- /run.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | case "$1" in 4 | "compile-output") 5 | cd out 6 | rm -f result.txt 7 | find -name \*.ex -exec sh -c "echo '#' {} >> result.txt ; elixirc {} >> result.txt" \; 8 | ;; 9 | "format-output") 10 | cd out 11 | find -name \*.ex -exec sh -c "echo '#' {}; mix format {}" \; 12 | esac 13 | -------------------------------------------------------------------------------- /samples.conf: -------------------------------------------------------------------------------- 1 | #{ 2 | includes => ["."], 3 | macros => #{'VSN' => 'EFE_TODO_VSN_MACRO', 'COMPILER_VSN' => 'EFE_TODO_COMPILER_VSN_MACRO'}, 4 | encoding => utf8, 5 | output_dir => "./out" 6 | }. 7 | -------------------------------------------------------------------------------- /samples/bincomp.erl: -------------------------------------------------------------------------------- 1 | -module(bincomp). 2 | 3 | -export([simple/0, bc/0, bc1/0, bsg/0, beam_asm1/2, specifier/1, resolve_inst/4, 4 | byte_align/1, bin_literal_str/1, ascii_to_lower/1]). 5 | 6 | simple() -> 7 | [Red || <> <= <<1, 2, 3, 4, 5, 6, 7, 8>>]. 8 | 9 | bc() -> 10 | [1 - Bit || <> <= <<240>>]. 11 | 12 | bc1() -> 13 | [<<(1 - Bit):1>> || <> <= <<240>>]. 14 | 15 | bsg() -> 16 | << <<(1 - Bit):1>> || <> <= <<240>> >>. 17 | 18 | beam_asm1(<<"FunT", Keep:8/binary, Table0/binary>>, MD5) -> 19 | <> = MD5, 20 | Table = finalize_fun_table_2(Table0, Uniq, <<>>), 21 | <<"FunT", Keep/binary, Table/binary>>. 22 | 23 | finalize_fun_table_2(<>, Uniq, Acc) -> 24 | finalize_fun_table_2(T, Uniq, <>); 25 | finalize_fun_table_2(<<>>, _, Acc) -> 26 | Acc. 27 | 28 | specifier(Bs) -> 29 | Sz = bit_size(Bs), 30 | Unused = 8 - bit_size(Bs), 31 | <>. 32 | 33 | resolve_inst({bs_match_string = I, [F, Ms, {u, Bits}, {u, Off}]}, 34 | _, 35 | Strings, 36 | _) -> 37 | Len = (Bits + 7) div 8, 38 | String = 39 | if Len > 0 -> 40 | <<_:Off/binary, Bin:Len/binary, _/binary>> = Strings, 41 | Bin; 42 | true -> 43 | <<>> 44 | end, 45 | {test, I, F, [Ms, Bits, String]}. 46 | 47 | byte_align(Bs) -> 48 | case bit_size(Bs) rem 8 of 49 | 0 -> 50 | size(Bs); 51 | N -> 52 | <> 53 | end. 54 | 55 | bin_literal_str(<<"//"/utf8, Rest/binary>>) -> 56 | Rest. 57 | 58 | ascii_to_lower(String) -> 59 | << < 60 | C + ($a - $A); 61 | true -> 62 | C 63 | end>> 64 | || <> <= iolist_to_binary(String) >>. 65 | -------------------------------------------------------------------------------- /samples/boollogic.erl: -------------------------------------------------------------------------------- 1 | -module(boollogic). 2 | 3 | -export([o_and/2, o_or/2, o_xor/2]). 4 | 5 | o_and(A, B) -> 6 | A and B. 7 | 8 | o_or(A, B) -> 9 | A or B. 10 | 11 | o_xor(A, B) -> 12 | A xor B. 13 | -------------------------------------------------------------------------------- /samples/cons.erl: -------------------------------------------------------------------------------- 1 | -module(cons). 2 | -export([cons/1]). 3 | 4 | cons([]) -> ok; 5 | cons([1]) -> ok; 6 | cons([1, 2]) -> ok; 7 | cons([1 | 2]) -> ok; 8 | cons([[1, 2], 3]) -> ok; 9 | cons([[1, 2] | 3]) -> ok; 10 | cons([[1 | 2] | 3]) -> ok; 11 | cons([0, [1, 2]]) -> ok; 12 | cons([0, [1 | 2]]) -> ok; 13 | % equivalent to [1, 2, 3] 14 | cons([0 | [1, 2]]) -> ok; 15 | cons([[1, [2, [3]]]]) -> ok; 16 | cons([[-1, 0] | [1, 2]]) -> ok. 17 | -------------------------------------------------------------------------------- /samples/cornercases.erl: -------------------------------------------------------------------------------- 1 | -module(cornercases). 2 | 3 | -export([varnames/12, atoms/0, chars/0, calls/3, calls/0, long_calc/0, binops/0, 4 | instant_call/0, lc_no_gen/0, module_macro/0, uppercase_funs/2, 5 | fun_no_args_when/1, 'substring-after'/0, call_call/0, 6 | escape_str_interpolation/0, printable_chars/0, wrap_stmt/1, 7 | local_calls_qualified/2, '+~+'/0, alias/2, def/2, call_conflict/0, 8 | in/3, encoding/0, module_info_calls/0, or_in_guard/1, 9 | guard_presedence/1, macros/0, template_cols/1, keyword_methods/0, 10 | cons/1]). 11 | 12 | % on_load is private 13 | -on_load on_load/0. 14 | 15 | % duplicated import 16 | -import(foo, [to_string/1, to_string/1]). 17 | 18 | cons(["pp", A | B]) -> 19 | {A, B}. 20 | 21 | send(A, B) -> 22 | A ! B. 23 | 24 | local_calls_qualified(A, B) -> 25 | % should add module to disambiguate from Kernel.* versions 26 | send(self(), ok), 27 | {to_string(42), monitor_node(A, B)}. 28 | 29 | on_load() -> 30 | ok. 31 | 32 | varnames(When, And, Or, Not, In, Fn, Do, End, Catch, Rescue, After, Else) -> 33 | {When, And, Or, Not, In, Fn, Do, End, Catch, Rescue, After, Else}. 34 | 35 | atoms() -> 36 | ['type-id']. 37 | 38 | calls(M, F, Arity) -> 39 | {F(), 40 | fun calls/3, 41 | fun cornercases:calls/3, 42 | fun M:F/Arity, 43 | fun M:calls/3, 44 | fun M:F/3, 45 | fun cornercases:F/Arity, 46 | fun cornercases:calls/Arity, 47 | fun M:calls/Arity}. 48 | 49 | calls() -> 50 | M = erlang, 51 | F = max, 52 | {M:max(1, 2), M:F(1, 2), erlang:F(1, 2), erlang:max(1, 2), max(1, 2)}. 53 | 54 | long_calc() -> 55 | 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 56 | 1 57 | + 1 58 | + 1 59 | + 1 60 | + 1 61 | + 1 62 | + 1 63 | + 1 64 | + 1 65 | + 1 66 | + 1 67 | + 1 68 | + 1 69 | + 1 70 | + 1 71 | + 1 72 | + 1 73 | + 1 74 | + 1 75 | + 1 76 | + 1. 77 | 78 | binops() -> 79 | bnot (1 bsl 2). 80 | 81 | instant_call() -> 82 | fun () -> 83 | ok 84 | end(). 85 | 86 | lc_no_gen() -> 87 | Enc = utf8, 88 | Opts = [], 89 | % elixir for needs a generator, add a dummy `_ <- [nil]` generator 90 | {[ok || true], 91 | [ok || false], 92 | [ok || _ <- [nil], true], 93 | [ok || _ <- [nil], false], 94 | [{encoding, Enc} || Enc =/= none, not proplists:get_bool(comments, Opts)]}. 95 | 96 | module_macro() -> 97 | ?MODULE. 98 | 99 | uppercase_funs(A, B) -> 100 | wxMenu:'Destroy'(A, B). 101 | 102 | fun_no_args_when(A) -> 103 | {fun () when A =:= 10 -> 104 | ok; 105 | () -> 106 | error 107 | end, 108 | fun () -> 109 | ok 110 | end, 111 | fun (B) -> 112 | B 113 | end}. 114 | 115 | % xerl_xpath_pred 116 | 'substring-after'() -> 117 | ok. 118 | 119 | return_fn() -> 120 | fun () -> 121 | ok 122 | end. 123 | 124 | call_call() -> 125 | (return_fn())(). 126 | 127 | escape_str_interpolation() -> 128 | ["#{", '#{', "'p'"]. 129 | 130 | chars() -> 131 | [$\s, $\t, $\r, $\n, $\f, $\e, $\d, $\b, $\v, $\^G, $\^C]. 132 | 133 | printable_chars() -> 134 | [$a, $z, $A, $Z, $0, $9, $\000, $\377, $\\, $\n]. 135 | 136 | wrap_stmt(Cs) -> 137 | [C 138 | || C <- Cs, 139 | lists:member(C, [ed25519, ed448, x25519, x448]) orelse 140 | try crypto:generate_key(ecdh, C) of 141 | _ -> 142 | true 143 | catch 144 | _:_ -> 145 | false 146 | end]. 147 | 148 | '+~+'() -> 149 | '+~+'(). 150 | 151 | alias(A, B) -> 152 | {A, B}. 153 | 154 | def(A, B) -> 155 | {A, B}. 156 | 157 | call_conflict() -> 158 | alias(1, 2), 159 | def(1, 2). 160 | 161 | in(A, B, C) -> 162 | A + B + C. 163 | 164 | encoding() -> 165 | {"ö", "ö"}, 166 | {"ä", "ä"}, 167 | {"å", "å"}. 168 | 169 | module_info_calls() -> 170 | {module_info(), module_info(md5)}. 171 | 172 | or_in_guard(A) when (A == 8) or (A == 16) and (A == 18) -> 173 | ok. 174 | 175 | guard_presedence(A) when A == 1, A == 2; A == 3 -> 176 | ok. 177 | 178 | macros() -> 179 | {?VSN, ?COMPILER_VSN}. 180 | 181 | template_cols(ColumnClasses) -> 182 | lists:sort([{{IdNo, Col}, lists:usort(Cs)} 183 | || Class <- ColumnClasses, 184 | {IdNo, Col} <- Class, 185 | IdNo =/= 1, 186 | [] =/= (Cs = [C || {1, C} <- Class])]). 187 | 188 | keyword_methods() -> 189 | {nil(), in()}. 190 | 191 | nil() -> nil. 192 | in() -> in. 193 | -------------------------------------------------------------------------------- /samples/mapops.erl: -------------------------------------------------------------------------------- 1 | -module(mapops). 2 | -export([put_atom/0, put_key/0, quoted_atom_key/1]). 3 | 4 | put_atom() -> 5 | M = #{}, 6 | M0 = M#{}, 7 | M1 = M#{a => 1}, 8 | M2 = M1#{a := 1}, 9 | M3 = M#{a => 1, b => 2}, 10 | M4 = M3#{a := 1, b => 2}, 11 | M5 = M1#{a := 1, b := 2}, 12 | M6 = M1#{a := 1, b := 2, c => 3, d => 4}, 13 | {M0, M1, M2, M3, M4, M5, M6}. 14 | 15 | put_key() -> 16 | M = #{}, 17 | M1 = M#{<<"a">> => 1}, 18 | M2 = M1#{<<"a">> := 1}, 19 | M3 = M#{<<"a">> => 1, <<"b">> => 2}, 20 | M4 = M3#{<<"a">> := 1, <<"b">> => 2}, 21 | M5 = M1#{<<"a">> := 1, <<"b">> := 2}, 22 | M6 = M1#{<<"a">> := 1, <<"b">> := 2, <<"c">> => 3, <<"d">> => 4}, 23 | {M1, M2, M3, M4, M5, M6}. 24 | 25 | quoted_atom_key(M) -> 26 | M#{'a-b' := 1}. 27 | -------------------------------------------------------------------------------- /samples/records.erl: -------------------------------------------------------------------------------- 1 | -module(records). 2 | 3 | -export([new/0, new/2, get_field/1, get_field_index/0, update_field/1, 4 | update_fields/1, update_no_fields/1]). 5 | -export([new_g/0, new_g/2, get_field_g/1, get_field_index_g/0, update_field_g/1, 6 | update_no_fields_g/1]). 7 | -export([quote_record/0, record_info_fn/0, record_call/0, is_record_guard/2]). 8 | 9 | -record(empty, {}). 10 | -record(user, {username = <<"meg">>, age = 25, team = #empty{}}). 11 | -record('Group', {username = <<"meg">>, age = 25, team, 'type-id' = 1}). 12 | -record('A-B-c', {a = quote_record()}). 13 | -record(state, {function}). 14 | 15 | quote_record() -> 16 | R = #'A-B-c'{}, 17 | R#'A-B-c'{a = 1}. 18 | 19 | new() -> 20 | #user{}. 21 | 22 | record_info_fn() -> 23 | {record_info(fields, user), record_info(size, user)}. 24 | 25 | new(Username, Age) -> 26 | #user{username = Username, age = Age}. 27 | 28 | get_field(Usr = #user{}) -> 29 | Usr#user.username. 30 | 31 | get_field_index() -> 32 | #user.age. 33 | 34 | update_field(Usr = #user{}) -> 35 | Usr#user{age = Usr#user.age + 1}. 36 | 37 | update_fields(Usr = #user{}) -> 38 | Usr#user{age = 39 | if Usr#user.age < 0 -> 40 | 1; 41 | true -> 42 | Usr#user.age + 1 43 | end, 44 | username = <<"bob">>, 45 | team = "sharks"}. 46 | 47 | update_no_fields(Usr) -> 48 | Usr#user{}. 49 | 50 | new_g() -> 51 | #'Group'{}. 52 | 53 | new_g(Username, Age) -> 54 | #'Group'{username = Username, age = Age, 'type-id' = 2}. 55 | 56 | get_field_g(Usr = #'Group'{}) -> 57 | Usr#'Group'.'type-id'. 58 | 59 | get_field_index_g() -> 60 | #'Group'.'type-id'. 61 | 62 | update_field_g(Usr = #'Group'{}) -> 63 | Usr#'Group'{'type-id' = Usr#'Group'.'type-id' + 1}. 64 | 65 | update_no_fields_g(Usr) -> 66 | Usr#'Group'{}. 67 | 68 | record_call() -> 69 | R = 70 | #state{function = 71 | fun () -> 72 | ok 73 | end}, 74 | (R#state.function)(). 75 | 76 | is_record_guard(R, _N) when is_record(R, state) -> 77 | is_record(R, state); 78 | is_record_guard(_R, _N) -> 79 | no. 80 | -------------------------------------------------------------------------------- /samples/reserved_kws.erl: -------------------------------------------------------------------------------- 1 | -module(reserved_kws). 2 | -export(['true'/0, 'false'/0, 'nil'/0, 'when'/0, 'and'/0, 'or'/0, 'not'/0, 3 | 'in'/0, 'fn'/0, 'do'/0, 'end'/0, 'catch'/0, 'rescue'/0, 'after'/0, 'else'/0]). 4 | 5 | 'true'() -> ok. 6 | 'false'() -> ok. 7 | 'nil'() -> ok. 8 | 'when'() -> ok. 9 | 'and'() -> ok. 10 | 'or'() -> ok. 11 | 'not'() -> ok. 12 | 'in'() -> ok. 13 | 'fn'() -> ok. 14 | 'do'() -> ok. 15 | 'end'() -> ok. 16 | 'catch'() -> ok. 17 | 'rescue'() -> ok. 18 | 'after'() -> ok. 19 | 'else'() -> ok. 20 | -------------------------------------------------------------------------------- /samples/scopes.erl: -------------------------------------------------------------------------------- 1 | -module(scopes). 2 | 3 | -export([noop/0, simple_scope/0, other_scope/1, multi_clause/1, case_match/0, 4 | receive_match/0, try_match/0, fun_scope/0, named_fun_scope/0, 5 | lc_scope/0, case_expr_not_matching/0, vars_in_clauses/1, 6 | var_in_prev_fun/1, var_in_prev_fun1/1, match_in_head/2, if_clauses/1, 7 | try_stacktrace/1, new_var_in_match/1, map_key_match/1, 8 | right_head_match/2, match_on_right_side/0, lc_pattern_vars_in_expr/0, 9 | match_left_var_not_on_right_side/0, take/2]). 10 | 11 | -record(user, {username = <<"meg">>}). 12 | -record(handler, 13 | {module :: atom(), 14 | id = false, 15 | state, 16 | supervised = false :: false | pid()}). 17 | 18 | -callback format_status(Opt, StatusData) -> Status when Opt :: normal | 19 | terminate, 20 | StatusData :: [PDict | 21 | State], 22 | PDict :: [{Key :: 23 | term(), 24 | Value :: 25 | term()}], 26 | State :: term(), 27 | Status :: term(). 28 | 29 | -type gb_tree_node(K, V) :: nil | 30 | {K, V, gb_tree_node(K, V), gb_tree_node(K, V)}. 31 | 32 | -opaque tree(Key, Value) :: {non_neg_integer(), gb_tree_node(Key, Value)}. 33 | 34 | noop() -> 35 | ok. 36 | 37 | simple_scope() -> 38 | A = 1, 39 | B = 2, 40 | A = 1, 41 | B = 2, 42 | A + B. 43 | 44 | other_scope(A) -> 45 | A = 2, 46 | {A, 2} = {1, 2}, 47 | B = A, 48 | A + B. 49 | 50 | multi_clause(0) -> 51 | A = 1, 52 | {A, 2} = {1, 2}, 53 | B = A, 54 | A + B; 55 | multi_clause(1) -> 56 | A = 1, 57 | {A, 2} = {1, 2}, 58 | B = A, 59 | A + B. 60 | 61 | case_match() -> 62 | A = 1, 63 | case make_ref() of 64 | A -> 65 | wat; 66 | B -> 67 | B = 1, % should match, defined in head of clause 68 | {ok, B} 69 | end. 70 | 71 | receive_match() -> 72 | A = 1, 73 | receive 74 | A -> 75 | wat; 76 | B -> 77 | B = 1, % should match, defined in head of clause 78 | {ok, B} 79 | end. 80 | 81 | try_match() -> 82 | A = 1, 83 | try 84 | make_ref() 85 | catch 86 | A -> 87 | ok; 88 | A:B -> 89 | B = 1, % should match, defined in head of clause 90 | {ok, B}; 91 | C:B:A -> 92 | {ok, C, B} 93 | end. 94 | 95 | fun_scope() -> 96 | A = 1, 97 | F = 98 | fun (B) -> 99 | B = 1, % match (from arg) 100 | A = 1, % match (from outer scope) 101 | C = B + A, 102 | C 103 | end, 104 | F(2), 105 | C = 3, % shouldn't match (vars in fun are in nested scope) 106 | B = 1, % shouldn't match (vars in fun are in nested scope) 107 | A + B + C. 108 | 109 | named_fun_scope() -> 110 | A = 1, 111 | F = 112 | fun F1(B) -> 113 | B = 1, % match (from arg) 114 | A = 1, % match (from outer scope) 115 | C = B + A, 116 | C 117 | end, 118 | F(2), 119 | C = 3, % shouldn't match (vars in fun are in nested scope) 120 | B = 1, % shouldn't match (vars in fun are in nested scope) 121 | A + B + C. 122 | 123 | lc_scope() -> 124 | O = 1, 125 | [begin 126 | O = 1, % match (from outer scope) 127 | case A of 128 | A -> 129 | ok; % match (from generator) 130 | _ -> 131 | wat 132 | end 133 | end 134 | || A <- lists:seq(0, 10)], 135 | A = 1, % shouldn't match (vars in list comprehension are in nested scope) 136 | A. 137 | 138 | case_expr_not_matching() -> 139 | A = 1, 140 | case A % shouldn't match, it's not a pattern 141 | of 142 | 1 -> 143 | ok 144 | end. 145 | 146 | vars_in_clauses(A) -> 147 | case A of 148 | {B, 1} -> 149 | B; 150 | {1, B} -> 151 | B 152 | end. 153 | 154 | var_in_prev_fun(A) -> 155 | A. 156 | 157 | var_in_prev_fun1(B) -> 158 | A = B + B - B + -B, 159 | A. 160 | 161 | match_in_head(Username, #user{username = Username}) -> 162 | Username. 163 | 164 | if_clauses(A) -> 165 | if A -> 166 | B = 1, 167 | B + 1; 168 | true -> 169 | B = 2, % shouldn't match 170 | B + 2 171 | end. 172 | 173 | try_stacktrace(F) -> 174 | try 175 | F() 176 | catch 177 | T:(E = asd):S -> 178 | {T, E, S}; 179 | E -> 180 | E; 181 | T:E -> 182 | {T, E} 183 | end. 184 | 185 | new_var_in_match(A) -> 186 | % second shouldn't match, var defined in pattern 187 | {Same, Same} = {1, A}. 188 | 189 | map_key_match(M) -> 190 | case M of 191 | #{M := 1} -> 192 | ok; 193 | _ -> 194 | error 195 | end. 196 | 197 | right_head_match(M, 1 = K) -> 198 | case M of 199 | #{K := 1} -> 200 | ok; 201 | _ -> 202 | error 203 | end. 204 | 205 | match_on_right_side() -> 206 | K = 1, 207 | % match on right side doesn't add hat (1 = k) 208 | 1 = K. 209 | 210 | lc_pattern_vars_in_expr() -> 211 | % vars in pattern are in expr (body) scope, (^a = 1) 212 | [A = 1 || A <- [1]], 213 | % this var is new, vars in lc have their own scope (a = 1) 214 | A = 1, 215 | [{Mod, Id, State} || #handler{module = Mod, id = Id, state = State} <- []], 216 | A. 217 | 218 | match_left_var_not_on_right_side() -> 219 | V = 220 | fun () -> 221 | % shouldn't match, right side before left 222 | V = 1, 223 | V 224 | end, 225 | V. 226 | 227 | take(_Smaller, Larger) -> 228 | {Key, Value} = Larger, 229 | Key + Value. 230 | -------------------------------------------------------------------------------- /samples/ueval.erl: -------------------------------------------------------------------------------- 1 | -module(ueval). 2 | 3 | -export([match/1, match/3]). 4 | 5 | match(+1) -> 6 | ok; 7 | match(-1) -> 8 | ok; 9 | match(1 + 1) -> 10 | ok; 11 | match(1 - 1) -> 12 | ok; 13 | match(1 * 2) -> 14 | ok; 15 | match(1 / 1) -> 16 | ok; 17 | match(bnot 1) -> 18 | ok; 19 | match(1 div 1) -> 20 | ok; 21 | match(1 rem 1) -> 22 | ok; 23 | match(1 band 1) -> 24 | ok; 25 | match(1 bor 1) -> 26 | ok; 27 | match(1 bxor 1) -> 28 | ok; 29 | match(1 bsl 1) -> 30 | ok; 31 | match(1 bsr 1) -> 32 | ok; 33 | match(1 + 1 band 1) -> 34 | ok; 35 | match(1 band 1 + 1) -> 36 | ok; 37 | match(_) -> 38 | error. 39 | 40 | match(_, 1 bsl 32 - 1, _) -> 41 | ok; 42 | match(_, {1, 1 bsl 32 - 1}, _) -> 43 | match(0, 1 bsl 32 - 1, 2). 44 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/e85975942742a3728226ac22a3415f2355bfc897.tar.gz") {} }: 2 | 3 | pkgs.mkShell { 4 | LOCALE_ARCHIVE_2_27 = "${pkgs.glibcLocales}/lib/locale/locale-archive"; 5 | buildInputs = [ 6 | pkgs.glibcLocales 7 | pkgs.erlang 8 | pkgs.elixir 9 | pkgs.rebar3 10 | pkgs.wget 11 | ]; 12 | shellHook = '' 13 | export LC_ALL=en_US.UTF-8 14 | ''; 15 | } 16 | -------------------------------------------------------------------------------- /src/ast.erl: -------------------------------------------------------------------------------- 1 | -module(ast). 2 | 3 | %% An map transformer of Erlang abstract syntax based on erl_id_trans.erl 4 | 5 | %% This module only traverses legal Erlang code. This is most noticeable 6 | %% in guards where only a limited number of expressions are allowed. 7 | %% N.B. if this module is to be used as a basis for transforms then 8 | %% all the error cases must be handled otherwise this module just crashes! 9 | 10 | -export([map/3, reduce/4]). 11 | -export([clause/3, expr/3, exprs/3, head/3, guard/3, pattern/3, 12 | function_type_list/3, farity_list/3, type/3, variable_list/3, 13 | bit_types/3]). 14 | 15 | map(Forms, St, MapFn) when is_list(Forms) -> 16 | forms(Forms, St, MapFn); 17 | map(Form, St, MapFn) -> 18 | form(Form, St, MapFn). 19 | 20 | leaf(Ast, St, Fn) -> 21 | case Fn(Ast, St) of 22 | auto -> 23 | {Ast, St}; 24 | {auto, St1} -> 25 | {Ast, St1}; 26 | {ok, Ast1, St1} -> 27 | {Ast1, St1} 28 | end. 29 | 30 | node(Ast, St, Fn, AutoFn) -> 31 | case Fn(Ast, St) of 32 | auto -> 33 | AutoFn(Ast, St, Fn); 34 | {auto, St1} -> 35 | AutoFn(Ast, St1, Fn); 36 | {ok, Ast1, St1} -> 37 | {Ast1, St1} 38 | end. 39 | 40 | reduce(Asts, St, Fn, RFn) -> 41 | reduce(Asts, St, Fn, RFn, []). 42 | 43 | reduce([], St, _Fn, _RFn, Accum) -> 44 | {lists:reverse(Accum), St}; 45 | reduce([Ast | Asts], St, Fn, RFn, Accum) -> 46 | {NewAst, NewSt} = RFn(Ast, St, Fn), 47 | reduce(Asts, NewSt, Fn, RFn, [NewAst | Accum]). 48 | 49 | forms(Forms, St, Fn) -> 50 | reduce(Forms, St, Fn, fun form/3). 51 | 52 | %% -type form(Form) -> Form. 53 | %% Here we show every known form and valid internal structure. We do not 54 | %% that the ordering is correct! 55 | 56 | %% First the various attributes. 57 | form({attribute, Line, module, Mod}, St, Fn) -> 58 | leaf({attribute, Line, module, Mod}, St, Fn); 59 | form({attribute, Line, file, {File, Line}}, 60 | St, 61 | Fn) -> %This is valid anywhere. 62 | leaf({attribute, Line, file, {File, Line}}, St, Fn); 63 | form(Ast = {attribute, Line, export, Es0}, St, Fn) -> 64 | node(Ast, 65 | St, 66 | Fn, 67 | fun (_Ast, St, Fn) -> 68 | {Es1, St1} = farity_list(Es0, St, Fn), 69 | {{attribute, Line, export, Es1}, St1} 70 | end); 71 | form(Ast = {attribute, Line, import, {Mod, Is0}}, St, Fn) -> 72 | node(Ast, 73 | St, 74 | Fn, 75 | fun (_Ast, St, Fn) -> 76 | {Is1, St1} = farity_list(Is0, St, Fn), 77 | {{attribute, Line, import, {Mod, Is1}}, St1} 78 | end); 79 | form(Ast = {attribute, Line, export_type, Es0}, St, Fn) -> 80 | node(Ast, 81 | St, 82 | Fn, 83 | fun (_Ast, St, Fn) -> 84 | {Es1, St1} = farity_list(Es0, St, Fn), 85 | {{attribute, Line, export_type, Es1}, St1} 86 | end); 87 | form(Ast = {attribute, Line, optional_callbacks, Es0}, St, Fn) -> 88 | node(Ast, 89 | St, 90 | Fn, 91 | fun (_Ast, St, Fn) -> 92 | try farity_list(Es0, St, Fn) of 93 | {Es1, St1} -> 94 | {{attribute, Line, optional_callbacks, Es1}, St1} 95 | catch 96 | _:_ -> 97 | {{attribute, Line, optional_callbacks, Es0}, St} 98 | end 99 | end); 100 | form(Ast = {attribute, _Line, compile, _C}, St, Fn) -> 101 | leaf(Ast, St, Fn); 102 | form(Ast = {attribute, Line, record, {Name, Defs0}}, St, Fn) -> 103 | node(Ast, 104 | St, 105 | Fn, 106 | fun (_Ast, St, Fn) -> 107 | {Defs1, St1} = record_defs(Defs0, St, Fn), 108 | {{attribute, Line, record, {Name, Defs1}}, St1} 109 | end); 110 | form(Ast = {attribute, _Line, asm, {function, _N, _A, _Code}}, St, Fn) -> 111 | leaf(Ast, St, Fn); 112 | form(Ast = {attribute, Line, type, {N, T, Vs}}, St, Fn) -> 113 | node(Ast, 114 | St, 115 | Fn, 116 | fun (_Ast, St, Fn) -> 117 | {T1, St1} = type(T, St, Fn), 118 | {Vs1, St2} = variable_list(Vs, St1, Fn), 119 | {{attribute, Line, type, {N, T1, Vs1}}, St2} 120 | end); 121 | form(Ast = {attribute, Line, opaque, {N, T, Vs}}, St, Fn) -> 122 | node(Ast, 123 | St, 124 | Fn, 125 | fun (_Ast, St, Fn) -> 126 | {T1, St1} = type(T, St, Fn), 127 | {Vs1, St2} = variable_list(Vs, St1, Fn), 128 | {{attribute, Line, opaque, {N, T1, Vs1}}, St2} 129 | end); 130 | form(Ast = {attribute, Line, spec, {{N, A}, FTs}}, St, Fn) -> 131 | node(Ast, 132 | St, 133 | Fn, 134 | fun (_Ast, St, Fn) -> 135 | {FTs1, St1} = function_type_list(FTs, St, Fn), 136 | {{attribute, Line, spec, {{N, A}, FTs1}}, St1} 137 | end); 138 | form(Ast = {attribute, Line, spec, {{M, N, A}, FTs}}, St, Fn) -> 139 | node(Ast, 140 | St, 141 | Fn, 142 | fun (_Ast, St, Fn) -> 143 | {FTs1, St1} = function_type_list(FTs, St, Fn), 144 | {{attribute, Line, spec, {{M, N, A}, FTs1}}, St1} 145 | end); 146 | form(Ast = {attribute, Line, callback, {{N, A}, FTs}}, St, Fn) -> 147 | node(Ast, 148 | St, 149 | Fn, 150 | fun (_Ast, St, Fn) -> 151 | {FTs1, St1} = function_type_list(FTs, St, Fn), 152 | {{attribute, Line, callback, {{N, A}, FTs1}}, St1} 153 | end); 154 | form(Ast = {attribute, _Line, _Attr, _Val}, 155 | St, 156 | Fn) -> %The general attribute. 157 | leaf(Ast, St, Fn); 158 | form(Ast = {function, Line, Name0, Arity0, Clauses0}, St, Fn) -> 159 | node(Ast, 160 | St, 161 | Fn, 162 | fun (_Ast, St, Fn) -> 163 | {{Name, Arity, Clauses}, St1} = 164 | function(Name0, Arity0, Clauses0, St, Fn), 165 | {{function, Line, Name, Arity, Clauses}, St1} 166 | end); 167 | %% Extra forms from the parser. 168 | form(Ast = {error, _E}, St, Fn) -> 169 | leaf(Ast, St, Fn); 170 | form(Ast = {warning, _W}, St, Fn) -> 171 | leaf(Ast, St, Fn); 172 | form(Ast = {eof, _Line}, St, Fn) -> 173 | leaf(Ast, St, Fn). 174 | 175 | %% -type farity_list(Ast=[Farity], St, Fn) -> [Farity] when Farity <= {atom(),integer()}. 176 | 177 | farity_list(Fas, St, Fn) -> 178 | reduce(Fas, St, Fn, fun farity/3). 179 | 180 | farity(Ast = {_Name, _Arity}, St, Fn) -> 181 | leaf(Ast, St, Fn). 182 | 183 | %% -type variable_list(Ast=[Var], St, Fn) -> [Var] 184 | 185 | variable_list(Vs, St, Fn) -> 186 | reduce(Vs, St, Fn, fun variable/3). 187 | 188 | variable(Ast = {var, _Line, _Var}, St, Fn) -> 189 | leaf(Ast, St, Fn). 190 | 191 | %% -type record_defs(Ast=[RecDef], St, Fn) -> [RecDef]. 192 | %% N.B. Field names are full expressions here but only atoms are allowed 193 | %% by the *parser*! 194 | 195 | record_defs(Is, St, Fn) -> 196 | reduce(Is, St, Fn, fun record_def/3). 197 | 198 | record_def(Ast = {record_field, Line, {atom, La, A}, Val0}, St, Fn) -> 199 | node(Ast, 200 | St, 201 | Fn, 202 | fun (_Ast, St, Fn) -> 203 | {Val1, St1} = expr(Val0, St, Fn), 204 | {{record_field, Line, {atom, La, A}, Val1}, St1} 205 | end); 206 | record_def(Ast = {record_field, _Line, {atom, _La, _A}}, St, Fn) -> 207 | leaf(Ast, St, Fn); 208 | record_def(Ast = 209 | {typed_record_field, 210 | {record_field, Line, {atom, La, A}, Val0}, 211 | Type}, 212 | St, 213 | Fn) -> 214 | node(Ast, 215 | St, 216 | Fn, 217 | fun (_Ast, St, Fn) -> 218 | {Val1, St1} = expr(Val0, St, Fn), 219 | {Type1, St2} = type(Type, St1, Fn), 220 | {{typed_record_field, 221 | {record_field, Line, {atom, La, A}, Val1}, 222 | Type1}, 223 | St2} 224 | end); 225 | record_def(Ast = 226 | {typed_record_field, {record_field, Line, {atom, La, A}}, Type}, 227 | St, 228 | Fn) -> 229 | node(Ast, 230 | St, 231 | Fn, 232 | fun (_Ast, St, Fn) -> 233 | {Type1, St1} = type(Type, St, Fn), 234 | {{typed_record_field, 235 | {record_field, Line, {atom, La, A}}, 236 | Type1}, 237 | St1} 238 | end). 239 | 240 | %% -type function(atom(), integer(), [Clause], St, Fn) -> {atom(),integer(Ast=),[Clause]}. 241 | 242 | function(Name, Arity, Clauses0, St, Fn) -> 243 | Ast = {Name, Arity, Clauses0}, 244 | node(Ast, 245 | St, 246 | Fn, 247 | fun (_Ast, StI, FnI) -> 248 | {Clauses1, St1} = clauses(Clauses0, StI, FnI), 249 | {{Name, Arity, Clauses1}, St1} 250 | end). 251 | 252 | %% -type clauses([Clause], St, Fn) -> [Clause]. 253 | 254 | clauses(Cs, St, Fn) -> 255 | reduce(Cs, St, Fn, fun clause/3). 256 | 257 | %% -type clause(Ast=Clause, St, Fn) -> Clause. 258 | 259 | clause(Ast = {clause, Line, H0, G0, B0}, St, Fn) -> 260 | node(Ast, 261 | St, 262 | Fn, 263 | fun (_Ast, StI, FnI) -> 264 | {H1, St1} = head(H0, StI, FnI), 265 | {G1, St2} = guard(G0, St1, FnI), 266 | {B1, St3} = exprs(B0, St2, FnI), 267 | {{clause, Line, H1, G1, B1}, St3} 268 | end). 269 | 270 | %% -type head(Ast=[Pattern], St, Fn) -> [Pattern]. 271 | 272 | head(Ps, St, Fn) -> 273 | patterns(Ps, St, Fn). 274 | 275 | %% -type patterns(Ast=[Pattern], St, Fn) -> [Pattern]. 276 | %% These patterns are processed "sequentially" for purposes of variable 277 | %% definition etc. 278 | 279 | patterns(Ps, St, Fn) -> 280 | reduce(Ps, St, Fn, fun pattern/3). 281 | 282 | %% -type pattern(Pattern, St, Fn) -> Pattern. 283 | %% N.B. Only valid patterns are included here. 284 | 285 | pattern(Ast = {var, _Line, _V}, St, Fn) -> 286 | leaf(Ast, St, Fn); 287 | pattern(Ast = {match, Line, L0, R0}, St, Fn) -> 288 | node(Ast, 289 | St, 290 | Fn, 291 | fun (_Ast, St, Fn) -> 292 | {L1, St1} = pattern(L0, St, Fn), 293 | {R1, St2} = pattern(R0, St1, Fn), 294 | {{match, Line, L1, R1}, St2} 295 | end); 296 | pattern(Ast = {integer, _Line, _I}, St, Fn) -> 297 | leaf(Ast, St, Fn); 298 | pattern(Ast = {char, _Line, _C}, St, Fn) -> 299 | leaf(Ast, St, Fn); 300 | pattern(Ast = {float, _Line, _F}, St, Fn) -> 301 | leaf(Ast, St, Fn); 302 | pattern(Ast = {atom, _Line, _A}, St, Fn) -> 303 | leaf(Ast, St, Fn); 304 | pattern(Ast = {string, _Line, _S}, St, Fn) -> 305 | leaf(Ast, St, Fn); 306 | pattern(Ast = {nil, _Line}, St, Fn) -> 307 | leaf(Ast, St, Fn); 308 | pattern(Ast = {cons, Line, H0, T0}, St, Fn) -> 309 | node(Ast, 310 | St, 311 | Fn, 312 | fun (_Ast, St, Fn) -> 313 | {H1, St1} = pattern(H0, St, Fn), 314 | {T1, St2} = pattern(T0, St1, Fn), 315 | {{cons, Line, H1, T1}, St2} 316 | end); 317 | pattern(Ast = {tuple, Line, Ps0}, St, Fn) -> 318 | node(Ast, 319 | St, 320 | Fn, 321 | fun (_Ast, St, Fn) -> 322 | {Ps1, St1} = pattern_list(Ps0, St, Fn), 323 | {{tuple, Line, Ps1}, St1} 324 | end); 325 | pattern(Ast = {map, Line, Ps0}, St, Fn) -> 326 | node(Ast, 327 | St, 328 | Fn, 329 | fun (_Ast, St, Fn) -> 330 | {Ps1, St1} = pattern_list(Ps0, St, Fn), 331 | {{map, Line, Ps1}, St1} 332 | end); 333 | pattern(Ast = {map_field_exact, Line, K, V}, St, Fn) -> 334 | node(Ast, 335 | St, 336 | Fn, 337 | fun (_Ast, St, Fn) -> 338 | {Ke, St1} = expr(K, St, Fn), 339 | {Ve, St2} = pattern(V, St1, Fn), 340 | {{map_field_exact, Line, Ke, Ve}, St2} 341 | end); 342 | pattern(Ast = {record, Line, Name, Pfs0}, St, Fn) -> 343 | node(Ast, 344 | St, 345 | Fn, 346 | fun (_Ast, St, Fn) -> 347 | {Pfs1, St1} = pattern_fields(Pfs0, St, Fn), 348 | {{record, Line, Name, Pfs1}, St1} 349 | end); 350 | pattern(Ast = {record_index, Line, Name, Field0}, St, Fn) -> 351 | node(Ast, 352 | St, 353 | Fn, 354 | fun (_Ast, St, Fn) -> 355 | {Field1, St1} = pattern(Field0, St, Fn), 356 | {{record_index, Line, Name, Field1}, St1} 357 | end); 358 | pattern(Ast = {record_field, Line, Rec0, Name, Field0}, St, Fn) -> 359 | node(Ast, 360 | St, 361 | Fn, 362 | fun (_Ast, St, Fn) -> 363 | {Rec1, St1} = expr(Rec0, St, Fn), 364 | {Field1, St2} = expr(Field0, St1, Fn), 365 | {{record_field, Line, Rec1, Name, Field1}, St2} 366 | end); 367 | pattern(Ast = {record_field, Line, Rec0, Field0}, St, Fn) -> 368 | node(Ast, 369 | St, 370 | Fn, 371 | fun (_Ast, St, Fn) -> 372 | {Rec1, St1} = expr(Rec0, St, Fn), 373 | {Field1, St2} = expr(Field0, St1, Fn), 374 | {{record_field, Line, Rec1, Field1}, St2} 375 | end); 376 | pattern(Ast = {bin, Line, Fs}, St, Fn) -> 377 | node(Ast, 378 | St, 379 | Fn, 380 | fun (_Ast, St, Fn) -> 381 | {Fs2, St1} = pattern_grp(Fs, St, Fn), 382 | {{bin, Line, Fs2}, St1} 383 | end); 384 | pattern(Ast = {op, _Line, _Op, _A}, St, Fn) -> 385 | leaf(Ast, St, Fn); 386 | pattern(Ast = {op, _Line, _Op, _L, _R}, St, Fn) -> 387 | leaf(Ast, St, Fn). 388 | 389 | pattern_grp(Fs, St, Fn) -> 390 | reduce(Fs, St, Fn, fun pattern_grp_item/3). 391 | 392 | pattern_grp_item({bin_element, L1, E1, S1, T1}, St, Fn) -> 393 | {S2, St1} = 394 | case S1 of 395 | default -> 396 | {default, St}; 397 | _ -> 398 | expr(S1, St, Fn) 399 | end, 400 | {T2, St2} = 401 | case T1 of 402 | default -> 403 | {default, St1}; 404 | _ -> 405 | bit_types(T1, St1, Fn) 406 | end, 407 | {E1_1, St3} = expr(E1, St2, Fn), 408 | {{bin_element, L1, E1_1, S2, T2}, St3}. 409 | 410 | bit_types(BTs, St, Fn) -> 411 | reduce(BTs, St, Fn, fun bit_type/3). 412 | 413 | bit_type(Atom, St, _Fn) when is_atom(Atom) -> 414 | {Atom, St}; 415 | bit_type({Atom, Integer}, St, _Fn) when is_atom(Atom), is_integer(Integer) -> 416 | {{Atom, Integer}, St}. 417 | 418 | %% -type pattern_list([Pattern], St, Fn) -> [Pattern]. 419 | %% These patterns are processed "in parallel" for purposes of variable 420 | %% definition etc. 421 | 422 | pattern_list(Ps, St, Fn) -> 423 | reduce(Ps, St, Fn, fun pattern/3). 424 | 425 | %% -type pattern_fields([Field], St, Fn) -> [Field]. 426 | %% N.B. Field names are full expressions here but only atoms are allowed 427 | %% by the *linter*!. 428 | 429 | pattern_fields(Pfs, St, Fn) -> 430 | reduce(Pfs, St, Fn, fun pattern_field/3). 431 | 432 | pattern_field(Ast = {record_field, Lf, {atom, La, F}, P0}, St, Fn) -> 433 | node(Ast, 434 | St, 435 | Fn, 436 | fun (_Ast, St, Fn) -> 437 | {P1, St1} = pattern(P0, St, Fn), 438 | {{record_field, Lf, {atom, La, F}, P1}, St1} 439 | end); 440 | pattern_field(Ast = {record_field, Lf, {var, La, '_'}, P0}, St, Fn) -> 441 | node(Ast, 442 | St, 443 | Fn, 444 | fun (_Ast, St, Fn) -> 445 | {P1, St1} = pattern(P0, St, Fn), 446 | {{record_field, Lf, {var, La, '_'}, P1}, St1} 447 | end). 448 | 449 | %% -type guard([GuardTest], St, Fn) -> [GuardTest]. 450 | 451 | guard(L = [G0 | _Gs], St, Fn) when is_list(G0) -> 452 | reduce(L, St, Fn, fun guard0/3); 453 | guard(L, St, Fn) -> 454 | guard0(L, St, Fn). 455 | 456 | guard0(Gs, St, Fn) -> 457 | reduce(Gs, St, Fn, fun guard_test/3). 458 | 459 | guard_test(Ast = {call, Line, {atom, La, F}, As0}, St, Fn) -> 460 | case erl_internal:type_test(F, length(As0)) of 461 | true -> 462 | node(Ast, 463 | St, 464 | Fn, 465 | fun (_Ast, St, Fn) -> 466 | {As1, St1} = gexpr_list(As0, St, Fn), 467 | {{call, Line, {atom, La, F}, As1}, St1} 468 | end); 469 | _ -> 470 | gexpr(Ast, St, Fn) 471 | end; 472 | guard_test(Any, St, Fn) -> 473 | gexpr(Any, St, Fn). 474 | 475 | %% Before R9, there were special rules regarding the expressions on 476 | %% top level in guards. Those limitations are now lifted - therefore 477 | %% there is no need for a special clause for the toplevel expressions. 478 | %% -type gexpr(GuardExpr, St, Fn) -> GuardExpr. 479 | 480 | gexpr(Ast = {var, _Line, _V}, St, Fn) -> 481 | leaf(Ast, St, Fn); 482 | gexpr(Ast = {integer, _Line, _I}, St, Fn) -> 483 | leaf(Ast, St, Fn); 484 | gexpr(Ast = {char, _Line, _C}, St, Fn) -> 485 | leaf(Ast, St, Fn); 486 | gexpr(Ast = {float, _Line, _F}, St, Fn) -> 487 | leaf(Ast, St, Fn); 488 | gexpr(Ast = {atom, _Line, _A}, St, Fn) -> 489 | leaf(Ast, St, Fn); 490 | gexpr(Ast = {string, _Line, _S}, St, Fn) -> 491 | leaf(Ast, St, Fn); 492 | gexpr(Ast = {nil, _Line}, St, Fn) -> 493 | leaf(Ast, St, Fn); 494 | gexpr(Ast = {map, Line, Map0, Es0}, St, Fn) -> 495 | node(Ast, 496 | St, 497 | Fn, 498 | fun (_Ast, St, Fn) -> 499 | {[Map1 | Es1], St1} = gexpr_list([Map0 | Es0], St, Fn), 500 | {{map, Line, Map1, Es1}, St1} 501 | end); 502 | gexpr(Ast = {map, Line, Es0}, St, Fn) -> 503 | node(Ast, 504 | St, 505 | Fn, 506 | fun (_Ast, St, Fn) -> 507 | {Es1, St1} = gexpr_list(Es0, St, Fn), 508 | {{map, Line, Es1}, St1} 509 | end); 510 | gexpr(Ast = {map_field_assoc, Line, K, V}, St, Fn) -> 511 | node(Ast, 512 | St, 513 | Fn, 514 | fun (_Ast, St, Fn) -> 515 | {Ke, St1} = gexpr(K, St, Fn), 516 | {Ve, St2} = gexpr(V, St1, Fn), 517 | {{map_field_assoc, Line, Ke, Ve}, St2} 518 | end); 519 | gexpr(Ast = {map_field_exact, Line, K, V}, St, Fn) -> 520 | node(Ast, 521 | St, 522 | Fn, 523 | fun (_Ast, St, Fn) -> 524 | {Ke, St1} = gexpr(K, St, Fn), 525 | {Ve, St2} = gexpr(V, St1, Fn), 526 | {{map_field_exact, Line, Ke, Ve}, St2} 527 | end); 528 | gexpr(Ast = {cons, Line, H0, T0}, St, Fn) -> 529 | node(Ast, 530 | St, 531 | Fn, 532 | fun (_Ast, St, Fn) -> 533 | {H1, St1} = gexpr(H0, St, Fn), 534 | %They see the same variables 535 | {T1, St2} = gexpr(T0, St1, Fn), 536 | {{cons, Line, H1, T1}, St2} 537 | end); 538 | gexpr(Ast = {tuple, Line, Es0}, St, Fn) -> 539 | node(Ast, 540 | St, 541 | Fn, 542 | fun (_Ast, St, Fn) -> 543 | {Es1, St1} = gexpr_list(Es0, St, Fn), 544 | {{tuple, Line, Es1}, St1} 545 | end); 546 | gexpr(Ast = {record_index, Line, Name, Field0}, St, Fn) -> 547 | node(Ast, 548 | St, 549 | Fn, 550 | fun (_Ast, St, Fn) -> 551 | {Field1, St1} = gexpr(Field0, St, Fn), 552 | {{record_index, Line, Name, Field1}, St1} 553 | end); 554 | gexpr(Ast = {record_field, Line, Rec0, Name, Field0}, St, Fn) -> 555 | node(Ast, 556 | St, 557 | Fn, 558 | fun (_Ast, St, Fn) -> 559 | {Rec1, St1} = gexpr(Rec0, St, Fn), 560 | {Field1, St2} = gexpr(Field0, St1, Fn), 561 | {{record_field, Line, Rec1, Name, Field1}, St2} 562 | end); 563 | gexpr(Ast = {record, Line, Name, Inits0}, St, Fn) -> 564 | node(Ast, 565 | St, 566 | Fn, 567 | fun (_Ast, St, Fn) -> 568 | {Inits1, St1} = grecord_inits(Inits0, St, Fn), 569 | {{record, Line, Name, Inits1}, St1} 570 | end); 571 | gexpr(Ast = {call, Line, {atom, La, F}, As0}, St, Fn) -> 572 | case erl_internal:guard_bif(F, length(As0)) of 573 | true -> 574 | node(Ast, 575 | St, 576 | Fn, 577 | fun (_Ast, St, Fn) -> 578 | {As1, St1} = gexpr_list(As0, St, Fn), 579 | {{call, Line, {atom, La, F}, As1}, St1} 580 | end) 581 | end; 582 | % Guard bif's can be remote, but only in the module erlang... 583 | gexpr(Ast = {call, Line, {remote, La, {atom, Lb, erlang}, {atom, Lc, F}}, As0}, 584 | St, 585 | Fn) -> 586 | case erl_internal:guard_bif(F, length(As0)) or 587 | erl_internal:arith_op(F, length(As0)) 588 | or erl_internal:comp_op(F, length(As0)) 589 | or erl_internal:bool_op(F, length(As0)) 590 | of 591 | true -> 592 | node(Ast, 593 | St, 594 | Fn, 595 | fun (_Ast, St, Fn) -> 596 | {As1, St1} = gexpr_list(As0, St, Fn), 597 | {{call, 598 | Line, 599 | {remote, La, {atom, Lb, erlang}, {atom, Lc, F}}, 600 | As1}, 601 | St1} 602 | end) 603 | end; 604 | gexpr(Ast = {bin, Line, Fs}, St, Fn) -> 605 | node(Ast, 606 | St, 607 | Fn, 608 | fun (_Ast, St, Fn) -> 609 | {Fs2, St1} = pattern_grp(Fs, St, Fn), 610 | {{bin, Line, Fs2}, St1} 611 | end); 612 | gexpr(Ast = {op, Line, Op, A0}, St, Fn) -> 613 | case erl_internal:arith_op(Op, 1) or erl_internal:bool_op(Op, 1) of 614 | true -> 615 | node(Ast, 616 | St, 617 | Fn, 618 | fun (_Ast, St, Fn) -> 619 | {A1, St1} = gexpr(A0, St, Fn), 620 | {{op, Line, Op, A1}, St1} 621 | end) 622 | end; 623 | gexpr(Ast = {op, Line, Op, L0, R0}, St, Fn) 624 | when Op =:= 'andalso'; Op =:= 'orelse' -> 625 | %% R11B: andalso/orelse are now allowed in guards. 626 | node(Ast, 627 | St, 628 | Fn, 629 | fun (_Ast, St, Fn) -> 630 | {L1, St1} = gexpr(L0, St, Fn), 631 | %They see the same variables 632 | {R1, St2} = gexpr(R0, St1, Fn), 633 | {{op, Line, Op, L1, R1}, St2} 634 | end); 635 | gexpr(Ast = {op, Line, Op, L0, R0}, St, Fn) -> 636 | case erl_internal:arith_op(Op, 2) or erl_internal:bool_op(Op, 2) or 637 | erl_internal:comp_op(Op, 2) 638 | of 639 | true -> 640 | node(Ast, 641 | St, 642 | Fn, 643 | fun (_Ast, St, Fn) -> 644 | {L1, St1} = gexpr(L0, St, Fn), 645 | {R1, St2} = gexpr(R0, St1, Fn), 646 | {{op, Line, Op, L1, R1}, St2} 647 | end) 648 | end. 649 | 650 | %% -type gexpr_list([GuardExpr], St, Fn) -> [GuardExpr]. 651 | %% These expressions are processed "in parallel" for purposes of variable 652 | %% definition etc. 653 | 654 | gexpr_list(Es, St, Fn) -> 655 | reduce(Es, St, Fn, fun gexpr/3). 656 | 657 | grecord_inits(Is, St, Fn) -> 658 | reduce(Is, St, Fn, fun grecord_init/3). 659 | 660 | grecord_init(Ast = {record_field, Lf, {atom, La, F}, Val0}, St, Fn) -> 661 | node(Ast, 662 | St, 663 | Fn, 664 | fun (_Ast, St, Fn) -> 665 | {Val1, St1} = gexpr(Val0, St, Fn), 666 | {{record_field, Lf, {atom, La, F}, Val1}, St1} 667 | end); 668 | grecord_init(Ast = {record_field, Lf, {var, La, '_'}, Val0}, St, Fn) -> 669 | node(Ast, 670 | St, 671 | Fn, 672 | fun (_Ast, St, Fn) -> 673 | {Val1, St1} = gexpr(Val0, St, Fn), 674 | {{record_field, Lf, {var, La, '_'}, Val1}, St1} 675 | end). 676 | 677 | %% -type exprs([Expression], St, Fn) -> [Expression]. 678 | %% These expressions are processed "sequentially" for purposes of variable 679 | %% definition etc. 680 | 681 | exprs(Es, St, Fn) -> 682 | reduce(Es, St, Fn, fun expr/3). 683 | 684 | %% -type expr(Expression, St, Fn) -> Expression. 685 | 686 | expr(Ast = {var, _Line, _V}, St, Fn) -> 687 | leaf(Ast, St, Fn); 688 | expr(Ast = {integer, _Line, _I}, St, Fn) -> 689 | leaf(Ast, St, Fn); 690 | expr(Ast = {float, _Line, _F}, St, Fn) -> 691 | leaf(Ast, St, Fn); 692 | expr(Ast = {atom, _Line, _A}, St, Fn) -> 693 | leaf(Ast, St, Fn); 694 | expr(Ast = {string, _Line, _S}, St, Fn) -> 695 | leaf(Ast, St, Fn); 696 | expr(Ast = {char, _Line, _C}, St, Fn) -> 697 | leaf(Ast, St, Fn); 698 | expr(Ast = {nil, _Line}, St, Fn) -> 699 | leaf(Ast, St, Fn); 700 | expr(Ast = {cons, Line, H0, T0}, St, Fn) -> 701 | node(Ast, 702 | St, 703 | Fn, 704 | fun (_Ast, St, Fn) -> 705 | {H1, St1} = expr(H0, St, Fn), 706 | %They see the same variables 707 | {T1, St2} = expr(T0, St1, Fn), 708 | {{cons, Line, H1, T1}, St2} 709 | end); 710 | expr(Ast = {lc, Line, E0, Qs0}, St, Fn) -> 711 | node(Ast, 712 | St, 713 | Fn, 714 | fun (_Ast, St, Fn) -> 715 | {Qs1, St1} = lc_bc_quals(Qs0, St, Fn), 716 | {E1, St2} = expr(E0, St1, Fn), 717 | {{lc, Line, E1, Qs1}, St2} 718 | end); 719 | expr(Ast = {bc, Line, E0, Qs0}, St, Fn) -> 720 | node(Ast, 721 | St, 722 | Fn, 723 | fun (_Ast, St, Fn) -> 724 | {Qs1, St1} = lc_bc_quals(Qs0, St, Fn), 725 | {E1, St2} = expr(E0, St1, Fn), 726 | {{bc, Line, E1, Qs1}, St2} 727 | end); 728 | expr(Ast = {tuple, Line, Es0}, St, Fn) -> 729 | node(Ast, 730 | St, 731 | Fn, 732 | fun (_Ast, St, Fn) -> 733 | {Es1, St1} = expr_list(Es0, St, Fn), 734 | {{tuple, Line, Es1}, St1} 735 | end); 736 | expr(Ast = {map, Line, Map0, Es0}, St, Fn) -> 737 | node(Ast, 738 | St, 739 | Fn, 740 | fun (_Ast, St, Fn) -> 741 | {[Map1 | Es1], St1} = exprs([Map0 | Es0], St, Fn), 742 | {{map, Line, Map1, Es1}, St1} 743 | end); 744 | expr(Ast = {map, Line, Es0}, St, Fn) -> 745 | node(Ast, 746 | St, 747 | Fn, 748 | fun (_Ast, St, Fn) -> 749 | {Es1, St1} = exprs(Es0, St, Fn), 750 | {{map, Line, Es1}, St1} 751 | end); 752 | expr(Ast = {map_field_assoc, Line, K, V}, St, Fn) -> 753 | node(Ast, 754 | St, 755 | Fn, 756 | fun (_Ast, St, Fn) -> 757 | {Ke, St1} = expr(K, St, Fn), 758 | {Ve, St2} = expr(V, St1, Fn), 759 | {{map_field_assoc, Line, Ke, Ve}, St2} 760 | end); 761 | expr(Ast = {map_field_exact, Line, K, V}, St, Fn) -> 762 | node(Ast, 763 | St, 764 | Fn, 765 | fun (_Ast, St, Fn) -> 766 | {Ke, St1} = expr(K, St, Fn), 767 | {Ve, St2} = expr(V, St1, Fn), 768 | {{map_field_exact, Line, Ke, Ve}, St2} 769 | end); 770 | expr(Ast = {record_index, Line, Name, Field0}, St, Fn) -> 771 | node(Ast, 772 | St, 773 | Fn, 774 | fun (_Ast, St, Fn) -> 775 | {Field1, St1} = expr(Field0, St, Fn), 776 | {{record_index, Line, Name, Field1}, St1} 777 | end); 778 | expr(Ast = {record, Line, Name, Inits0}, St, Fn) -> 779 | node(Ast, 780 | St, 781 | Fn, 782 | fun (_Ast, St, Fn) -> 783 | {Inits1, St1} = record_inits(Inits0, St, Fn), 784 | {{record, Line, Name, Inits1}, St1} 785 | end); 786 | expr(Ast = {record_field, Line, Rec0, Name, Field0}, St, Fn) -> 787 | node(Ast, 788 | St, 789 | Fn, 790 | fun (_Ast, St, Fn) -> 791 | {Rec1, St1} = expr(Rec0, St, Fn), 792 | {Field1, St2} = expr(Field0, St1, Fn), 793 | {{record_field, Line, Rec1, Name, Field1}, St2} 794 | end); 795 | expr(Ast = {record, Line, Rec0, Name, Upds0}, St, Fn) -> 796 | node(Ast, 797 | St, 798 | Fn, 799 | fun (_Ast, St, Fn) -> 800 | {Rec1, St1} = expr(Rec0, St, Fn), 801 | {Upds1, St2} = record_updates(Upds0, St1, Fn), 802 | {{record, Line, Rec1, Name, Upds1}, St2} 803 | end); 804 | expr(Ast = {record_field, Line, Rec0, Field0}, St, Fn) -> 805 | node(Ast, 806 | St, 807 | Fn, 808 | fun (_Ast, St, Fn) -> 809 | {Rec1, St1} = expr(Rec0, St, Fn), 810 | {Field1, St2} = expr(Field0, St1, Fn), 811 | {{record_field, Line, Rec1, Field1}, St2} 812 | end); 813 | expr(Ast = {block, Line, Es0}, St, Fn) -> 814 | %% Unfold block into a sequence. 815 | node(Ast, 816 | St, 817 | Fn, 818 | fun (_Ast, St, Fn) -> 819 | {Es1, St1} = exprs(Es0, St, Fn), 820 | {{block, Line, Es1}, St1} 821 | end); 822 | expr(Ast = {'if', Line, Cs0}, St, Fn) -> 823 | node(Ast, 824 | St, 825 | Fn, 826 | fun (_Ast, St, Fn) -> 827 | {Cs1, St1} = icr_clauses(Cs0, St, Fn), 828 | {{'if', Line, Cs1}, St1} 829 | end); 830 | expr(Ast = {'case', Line, E0, Cs0}, St, Fn) -> 831 | node(Ast, 832 | St, 833 | Fn, 834 | fun (_Ast, St, Fn) -> 835 | {E1, St1} = expr(E0, St, Fn), 836 | {Cs1, St2} = icr_clauses(Cs0, St1, Fn), 837 | {{'case', Line, E1, Cs1}, St2} 838 | end); 839 | expr(Ast = {'receive', Line, Cs0}, St, Fn) -> 840 | node(Ast, 841 | St, 842 | Fn, 843 | fun (_Ast, St, Fn) -> 844 | {Cs1, St1} = icr_clauses(Cs0, St, Fn), 845 | {{'receive', Line, Cs1}, St1} 846 | end); 847 | expr(Ast = {'receive', Line, Cs0, To0, ToEs0}, St, Fn) -> 848 | node(Ast, 849 | St, 850 | Fn, 851 | fun (_Ast, St, Fn) -> 852 | {To1, St1} = expr(To0, St, Fn), 853 | {ToEs1, St2} = exprs(ToEs0, St1, Fn), 854 | {Cs1, St3} = icr_clauses(Cs0, St2, Fn), 855 | {{'receive', Line, Cs1, To1, ToEs1}, St3} 856 | end); 857 | expr(Ast = {'try', Line, Es0, Scs0, Ccs0, As0}, St, Fn) -> 858 | node(Ast, 859 | St, 860 | Fn, 861 | fun (_Ast, St, Fn) -> 862 | {Es1, St1} = exprs(Es0, St, Fn), 863 | {Scs1, St2} = icr_clauses(Scs0, St1, Fn), 864 | {Ccs1, St3} = icr_clauses(Ccs0, St2, Fn), 865 | {As1, St4} = exprs(As0, St3, Fn), 866 | {{'try', Line, Es1, Scs1, Ccs1, As1}, St4} 867 | end); 868 | expr(Ast = {'fun', Line, {clauses, Cs0}}, St, Fn) -> 869 | node(Ast, 870 | St, 871 | Fn, 872 | fun (_Ast, StI, FnI) -> 873 | {Cs1, St1} = fun_clauses(Cs0, StI, FnI), 874 | {{'fun', Line, {clauses, Cs1}}, St1} 875 | end); 876 | expr(Ast = {'fun', _Line, {function, _F, _A}}, St, Fn) -> 877 | leaf(Ast, St, Fn); 878 | expr(Ast = {'fun', _Line, {function, M, F, A}}, St, Fn) 879 | when is_atom(M), is_atom(F), is_integer(A) -> 880 | %% R10B-6: fun M:F/A. (Backward compatibility) 881 | leaf(Ast, St, Fn); 882 | expr(Ast = {'fun', Line, {function, M0, F0, A0}}, St, Fn) -> 883 | %% R15: fun M:F/A with variables. 884 | node(Ast, 885 | St, 886 | Fn, 887 | fun (_Ast, StI, FnI) -> 888 | {M, St1} = expr(M0, StI, FnI), 889 | {F, St2} = expr(F0, St1, FnI), 890 | {A, St3} = expr(A0, St2, FnI), 891 | {{'fun', Line, {function, M, F, A}}, St3} 892 | end); 893 | expr(Ast = {named_fun, Loc, Name, Cs}, St, Fn) -> 894 | node(Ast, 895 | St, 896 | Fn, 897 | fun (_Ast, StI, FnI) -> 898 | {Cs1, St1} = fun_clauses(Cs, StI, FnI), 899 | {{named_fun, Loc, Name, Cs1}, St1} 900 | end); 901 | expr(Ast = {call, Line, F0, As0}, St, Fn) -> 902 | %% N.B. If F an atom then call to local function or BIF, if F a 903 | %% remote structure (see below) then call to other module, 904 | %% otherwise apply to "function". 905 | node(Ast, 906 | St, 907 | Fn, 908 | fun (_Ast, St, Fn) -> 909 | {F1, St1} = expr(F0, St, Fn), 910 | {As1, St2} = expr_list(As0, St1, Fn), 911 | {{call, Line, F1, As1}, St2} 912 | end); 913 | expr(Ast = {'catch', Line, E0}, St, Fn) -> 914 | %% No new variables added. 915 | node(Ast, 916 | St, 917 | Fn, 918 | fun (_Ast, St, Fn) -> 919 | {E1, St1} = expr(E0, St, Fn), 920 | {{'catch', Line, E1}, St1} 921 | end); 922 | expr(Ast = {match, Line, P0, E0}, St, Fn) -> 923 | node(Ast, 924 | St, 925 | Fn, 926 | fun (_Ast, St, Fn) -> 927 | {E1, St1} = expr(E0, St, Fn), 928 | {P1, St2} = pattern(P0, St1, Fn), 929 | {{match, Line, P1, E1}, St2} 930 | end); 931 | expr(Ast = {bin, Line, Fs}, St, Fn) -> 932 | node(Ast, 933 | St, 934 | Fn, 935 | fun (_Ast, St, Fn) -> 936 | {Fs2, St1} = pattern_grp(Fs, St, Fn), 937 | {{bin, Line, Fs2}, St1} 938 | end); 939 | expr(Ast = {op, Line, Op, A0}, St, Fn) -> 940 | node(Ast, 941 | St, 942 | Fn, 943 | fun (_Ast, St, Fn) -> 944 | {A1, St1} = expr(A0, St, Fn), 945 | {{op, Line, Op, A1}, St1} 946 | end); 947 | expr(Ast = {op, Line, Op, L0, R0}, St, Fn) -> 948 | node(Ast, 949 | St, 950 | Fn, 951 | fun (_Ast, St, Fn) -> 952 | {L1, St1} = expr(L0, St, Fn), 953 | %They see the same variables 954 | {R1, St2} = expr(R0, St1, Fn), 955 | {{op, Line, Op, L1, R1}, St2} 956 | end); 957 | %% The following are not allowed to occur anywhere! 958 | expr(Ast = {remote, Line, M0, F0}, St, Fn) -> 959 | node(Ast, 960 | St, 961 | Fn, 962 | fun (_Ast, St, Fn) -> 963 | {M1, St1} = expr(M0, St, Fn), 964 | {F1, St2} = expr(F0, St1, Fn), 965 | {{remote, Line, M1, F1}, St2} 966 | end). 967 | 968 | %% -type expr_list([Expression], St, Fn) -> [Expression]. 969 | %% These expressions are processed "in parallel" for purposes of variable 970 | %% definition etc. 971 | 972 | expr_list(Es, St, Fn) -> 973 | reduce(Es, St, Fn, fun expr/3). 974 | 975 | %% -type record_inits([RecordInit], St, Fn) -> [RecordInit]. 976 | %% N.B. Field names are full expressions here but only atoms are allowed 977 | %% by the *linter*!. 978 | 979 | record_inits(Is, St, Fn) -> 980 | reduce(Is, St, Fn, fun record_init/3). 981 | 982 | record_init(Ast = {record_field, Lf, {atom, La, F}, Val0}, St, Fn) -> 983 | node(Ast, 984 | St, 985 | Fn, 986 | fun (_Ast, St, Fn) -> 987 | {Val1, St1} = expr(Val0, St, Fn), 988 | {{record_field, Lf, {atom, La, F}, Val1}, St1} 989 | end); 990 | record_init(Ast = {record_field, Lf, {var, La, '_'}, Val0}, St, Fn) -> 991 | node(Ast, 992 | St, 993 | Fn, 994 | fun (_Ast, St, Fn) -> 995 | {Val1, St1} = expr(Val0, St, Fn), 996 | {{record_field, Lf, {var, La, '_'}, Val1}, St1} 997 | end). 998 | 999 | %% -type record_updates([RecordUpd], St, Fn) -> [RecordUpd]. 1000 | %% N.B. Field names are full expressions here but only atoms are allowed 1001 | %% by the *linter*!. 1002 | 1003 | record_updates(Us, St, Fn) -> 1004 | reduce(Us, St, Fn, fun record_update/3). 1005 | 1006 | record_update(Ast = {record_field, Lf, {atom, La, F}, Val0}, St, Fn) -> 1007 | node(Ast, 1008 | St, 1009 | Fn, 1010 | fun (_Ast, StI, FnI) -> 1011 | {Val1, St1} = expr(Val0, StI, FnI), 1012 | {{record_field, Lf, {atom, La, F}, Val1}, St1} 1013 | end). 1014 | 1015 | %% -type icr_clauses([Clause], St, Fn) -> [Clause]. 1016 | 1017 | icr_clauses(Cs, St, Fn) -> 1018 | reduce(Cs, St, Fn, fun icr_clause/3). 1019 | 1020 | icr_clause(Ast, St, Fn) -> 1021 | node(Ast, St, Fn, fun clause/3). 1022 | 1023 | %% -type lc_bc_quals([Qualifier], St, Fn) -> [Qualifier]. 1024 | %% Allow filters to be both guard tests and general expressions. 1025 | 1026 | lc_bc_quals(Qs, St, Fn) -> 1027 | reduce(Qs, St, Fn, fun lc_bc_qual/3). 1028 | 1029 | lc_bc_qual(Ast = {generate, Line, P0, E0}, St, Fn) -> 1030 | node(Ast, 1031 | St, 1032 | Fn, 1033 | fun (_Ast, StI, FnI) -> 1034 | {E1, St1} = expr(E0, StI, FnI), 1035 | {P1, St2} = pattern(P0, St1, FnI), 1036 | {{generate, Line, P1, E1}, St2} 1037 | end); 1038 | lc_bc_qual(Ast = {b_generate, Line, P0, E0}, St, Fn) -> 1039 | node(Ast, 1040 | St, 1041 | Fn, 1042 | fun (_Ast, StI, FnI) -> 1043 | {E1, St1} = expr(E0, StI, FnI), 1044 | {P1, St2} = pattern(P0, St1, FnI), 1045 | {{b_generate, Line, P1, E1}, St2} 1046 | end); 1047 | lc_bc_qual(Ast, St, Fn) -> 1048 | node(Ast, St, Fn, fun expr/3). 1049 | 1050 | %% -type fun_clauses([Clause], St, Fn) -> [Clause]. 1051 | 1052 | fun_clauses(Cs, St, Fn) -> 1053 | reduce(Cs, St, Fn, fun fun_clause/3). 1054 | 1055 | fun_clause(Ast, St, Fn) -> 1056 | node(Ast, St, Fn, fun clause/3). 1057 | 1058 | function_type_list(Fts, St, Fn) -> 1059 | reduce(Fts, St, Fn, fun function_type_list_item/3). 1060 | 1061 | function_type_list_item(Ast = {type, Line, bounded_fun, [Ft, Fc]}, St, Fn) -> 1062 | node(Ast, 1063 | St, 1064 | Fn, 1065 | fun (_Ast, StI, FnI) -> 1066 | {Ft1, St1} = function_type(Ft, StI, FnI), 1067 | {Fc1, St2} = function_constraint(Fc, St1, FnI), 1068 | {{type, Line, bounded_fun, [Ft1, Fc1]}, St2} 1069 | end); 1070 | function_type_list_item(Ft, St, Fn) -> 1071 | function_type(Ft, St, Fn). 1072 | 1073 | function_type(Ast = {type, Line, 'fun', [{type, Lt, product, As}, B]}, 1074 | St, 1075 | Fn) -> 1076 | node(Ast, 1077 | St, 1078 | Fn, 1079 | fun (_Ast, St, Fn) -> 1080 | {As1, St1} = type_list(As, St, Fn), 1081 | {B1, St2} = type(B, St1, Fn), 1082 | {{type, Line, 'fun', [{type, Lt, product, As1}, B1]}, St2} 1083 | end). 1084 | 1085 | function_constraint(Cs, St, Fn) -> 1086 | reduce(Cs, St, Fn, fun function_constraint_item/3). 1087 | 1088 | function_constraint_item(Ast, St, Fn) -> 1089 | node(Ast, St, Fn, fun constraint/3). 1090 | 1091 | constraint(Ast = {type, Line, constraint, [{atom, L, A}, [V, T]]}, St, Fn) -> 1092 | node(Ast, 1093 | St, 1094 | Fn, 1095 | fun (_Ast, St, Fn) -> 1096 | {V1, St1} = type(V, St, Fn), 1097 | {T1, St2} = type(T, St1, Fn), 1098 | {{type, Line, constraint, [{atom, L, A}, [V1, T1]]}, St2} 1099 | end). 1100 | 1101 | type(Ast = {ann_type, Line, [{var, Lv, V}, T]}, St, Fn) -> 1102 | node(Ast, 1103 | St, 1104 | Fn, 1105 | fun (_Ast, St, Fn) -> 1106 | {T1, St1} = type(T, St, Fn), 1107 | {{ann_type, Line, [{var, Lv, V}, T1]}, St1} 1108 | end); 1109 | type(Ast = {atom, _Line, _A}, St, Fn) -> 1110 | leaf(Ast, St, Fn); 1111 | type(Ast = {integer, _Line, _I}, St, Fn) -> 1112 | leaf(Ast, St, Fn); 1113 | type(Ast = {op, Line, Op, T}, St, Fn) -> 1114 | node(Ast, 1115 | St, 1116 | Fn, 1117 | fun (_Ast, St, Fn) -> 1118 | {T1, St1} = type(T, St, Fn), 1119 | {{op, Line, Op, T1}, St1} 1120 | end); 1121 | type(Ast = {op, Line, Op, L, R}, St, Fn) -> 1122 | node(Ast, 1123 | St, 1124 | Fn, 1125 | fun (_Ast, St, Fn) -> 1126 | {L1, St1} = type(L, St, Fn), 1127 | {R1, St2} = type(R, St1, Fn), 1128 | {{op, Line, Op, L1, R1}, St2} 1129 | end); 1130 | type(Ast = {type, Line, binary, [M, N]}, St, Fn) -> 1131 | node(Ast, 1132 | St, 1133 | Fn, 1134 | fun (_Ast, St, Fn) -> 1135 | {M1, St1} = type(M, St, Fn), 1136 | {N1, St2} = type(N, St1, Fn), 1137 | {{type, Line, binary, [M1, N1]}, St2} 1138 | end); 1139 | type(Ast = {type, _Line, 'fun', []}, St, Fn) -> 1140 | leaf(Ast, St, Fn); 1141 | type(Ast = {type, Line, 'fun', [{type, Lt, any}, B]}, St, Fn) -> 1142 | node(Ast, 1143 | St, 1144 | Fn, 1145 | fun (_Ast, St, Fn) -> 1146 | {B1, St1} = type(B, St, Fn), 1147 | {{type, Line, 'fun', [{type, Lt, any}, B1]}, St1} 1148 | end); 1149 | type(Ast = {type, Line, range, [L, H]}, St, Fn) -> 1150 | node(Ast, 1151 | St, 1152 | Fn, 1153 | fun (_Ast, St, Fn) -> 1154 | {L1, St1} = type(L, St, Fn), 1155 | {H1, St2} = type(H, St1, Fn), 1156 | {{type, Line, range, [L1, H1]}, St2} 1157 | end); 1158 | type(Ast = {type, _Line, map, any}, St, Fn) -> 1159 | leaf(Ast, St, Fn); 1160 | type(Ast = {type, Line, map, Ps}, St, Fn) -> 1161 | node(Ast, 1162 | St, 1163 | Fn, 1164 | fun (_Ast, St, Fn) -> 1165 | {Ps1, St1} = map_pair_types(Ps, St, Fn), 1166 | {{type, Line, map, Ps1}, St1} 1167 | end); 1168 | type(Ast = {type, Line, record, [{atom, La, N} | Fs]}, St, Fn) -> 1169 | node(Ast, 1170 | St, 1171 | Fn, 1172 | fun (_Ast, St, Fn) -> 1173 | {Fs1, St1} = field_types(Fs, St, Fn), 1174 | {{type, Line, record, [{atom, La, N} | Fs1]}, St1} 1175 | end); 1176 | type(Ast = {remote_type, Line, [{atom, Lm, M}, {atom, Ln, N}, As]}, St, Fn) -> 1177 | node(Ast, 1178 | St, 1179 | Fn, 1180 | fun (_Ast, St, Fn) -> 1181 | {As1, St1} = type_list(As, St, Fn), 1182 | {{remote_type, Line, [{atom, Lm, M}, {atom, Ln, N}, As1]}, St1} 1183 | end); 1184 | type(Ast = {type, _Line, tuple, any}, St, Fn) -> 1185 | leaf(Ast, St, Fn); 1186 | type(Ast = {type, Line, tuple, Ts}, St, Fn) -> 1187 | node(Ast, 1188 | St, 1189 | Fn, 1190 | fun (_Ast, St, Fn) -> 1191 | {Ts1, St1} = type_list(Ts, St, Fn), 1192 | {{type, Line, tuple, Ts1}, St1} 1193 | end); 1194 | type(Ast = {type, Line, union, Ts}, St, Fn) -> 1195 | node(Ast, 1196 | St, 1197 | Fn, 1198 | fun (_Ast, St, Fn) -> 1199 | {Ts1, St1} = type_list(Ts, St, Fn), 1200 | {{type, Line, union, Ts1}, St1} 1201 | end); 1202 | type(Ast = {var, _Line, _V}, St, Fn) -> 1203 | leaf(Ast, St, Fn); 1204 | type(Ast = {user_type, Line, N, As}, St, Fn) -> 1205 | node(Ast, 1206 | St, 1207 | Fn, 1208 | fun (_Ast, St, Fn) -> 1209 | {As1, St1} = type_list(As, St, Fn), 1210 | {{user_type, Line, N, As1}, St1} 1211 | end); 1212 | type(Ast = {type, Line, N, As}, St, Fn) -> 1213 | node(Ast, 1214 | St, 1215 | Fn, 1216 | fun (_Ast, St, Fn) -> 1217 | {As1, St1} = type_list(As, St, Fn), 1218 | {{type, Line, N, As1}, St1} 1219 | end). 1220 | 1221 | map_pair_types(Ps, St, Fn) -> 1222 | reduce(Ps, St, Fn, fun map_pair_type/3). 1223 | 1224 | map_pair_type(Ast = {type, Line, map_field_assoc, [K, V]}, St, Fn) -> 1225 | node(Ast, 1226 | St, 1227 | Fn, 1228 | fun (_Ast, St, Fn) -> 1229 | {K1, St1} = type(K, St, Fn), 1230 | {V1, St2} = type(V, St1, Fn), 1231 | {{type, Line, map_field_assoc, [K1, V1]}, St2} 1232 | end); 1233 | map_pair_type(Ast = {type, Line, map_field_exact, [K, V]}, St, Fn) -> 1234 | node(Ast, 1235 | St, 1236 | Fn, 1237 | fun (_Ast, St, Fn) -> 1238 | {K1, St1} = type(K, St, Fn), 1239 | {V1, St2} = type(V, St1, Fn), 1240 | {{type, Line, map_field_exact, [K1, V1]}, St2} 1241 | end). 1242 | 1243 | field_types(Fs, St, Fn) -> 1244 | reduce(Fs, St, Fn, fun field_type/3). 1245 | 1246 | field_type(Ast = {type, Line, field_type, [{atom, La, A}, T]}, St, Fn) -> 1247 | node(Ast, 1248 | St, 1249 | Fn, 1250 | fun (_Ast, St, Fn) -> 1251 | {T1, St1} = type(T, St, Fn), 1252 | {{type, Line, field_type, [{atom, La, A}, T1]}, St1} 1253 | end). 1254 | 1255 | type_list(Ts, St, Fn) -> 1256 | reduce(Ts, St, Fn, fun type_list_item/3). 1257 | 1258 | type_list_item(Ast, St, Fn) -> 1259 | node(Ast, St, Fn, fun type/3). 1260 | -------------------------------------------------------------------------------- /src/efe.app.src: -------------------------------------------------------------------------------- 1 | {application, efe, 2 | [{description, "An escript"}, 3 | {vsn, "0.1.0"}, 4 | {registered, []}, 5 | {applications, 6 | [kernel, 7 | stdlib 8 | ]}, 9 | {env,[]}, 10 | {modules, []}, 11 | 12 | {licenses, ["Apache 2.0"]}, 13 | {links, []} 14 | ]}. 15 | -------------------------------------------------------------------------------- /src/efe.erl: -------------------------------------------------------------------------------- 1 | -module(efe). 2 | 3 | %% API exports 4 | -export([main/1]). 5 | 6 | %%==================================================================== 7 | %% API functions 8 | %%==================================================================== 9 | 10 | %% escript Entry point 11 | main(["pp", ConfPath | FilePaths]) -> 12 | each_config_and_path(ConfPath, 13 | FilePaths, 14 | fun (Config, FilePath) -> 15 | pprint_ex(FilePath, true, Config) 16 | end, 17 | ".erl"); 18 | main(["pp-lfe", ConfPath | FilePaths]) -> 19 | each_config_and_path(ConfPath, 20 | FilePaths, 21 | fun (Config, FilePath) -> 22 | pprint_lfe(FilePath, true, Config) 23 | end, 24 | ".lfe"); 25 | main(["pp-lfe-ast", ConfPath | FilePaths]) -> 26 | each_config_and_path(ConfPath, 27 | FilePaths, 28 | fun (Config, FilePath) -> 29 | pprint_lfe_ast(FilePath, true, Config) 30 | end, 31 | ".lfe"); 32 | main(["ann", ConfPath | FilePaths]) -> 33 | each_config_and_path(ConfPath, 34 | FilePaths, 35 | fun (Config, FilePath) -> 36 | {Ast, _St} = annotate(FilePath, Config), 37 | pprint({ok, Ast}) 38 | end, 39 | ".erl"); 40 | main(["conf", ConfPath | FilePaths]) -> 41 | each_config_and_path(ConfPath, 42 | FilePaths, 43 | fun (Config, FilePath) -> 44 | io:format("~p: ~p~n~n", [FilePath, Config]) 45 | end, 46 | ".erl"); 47 | % to make it work when transpiled to elixir, elixir escripts receive args as list 48 | % of binaries 49 | main(Args = [H | _]) when is_binary(H) -> 50 | main([binary_to_list(V) || V <- Args]); 51 | main(_) -> 52 | io:format("Usage: efe pp|ann|conf path.erl+~n"), 53 | erlang:halt(0). 54 | 55 | %%==================================================================== 56 | %% Internal functions 57 | %%==================================================================== 58 | 59 | pprint({ok, R}) -> 60 | io:format("~p~n", [R]); 61 | pprint({error, E}) -> 62 | io:format("Error: ~p~n", [E]). 63 | 64 | with_ast(Path, Config, Fn) -> 65 | case from_erl(Path, Config) of 66 | {ok, Ast} -> 67 | try 68 | Fn(Ast) 69 | catch 70 | T:E:S -> 71 | {error, 72 | #{code => exception, type => T, error => E, stack => S}} 73 | end; 74 | Other -> 75 | Other 76 | end. 77 | 78 | annotate(Path, Config) -> 79 | with_ast(Path, 80 | Config, 81 | fun (Ast) -> 82 | efe_var_ann:do(Ast) 83 | end). 84 | 85 | from_erl(Path, 86 | #{encoding := Encoding, includes := Includes, macros := Macros}) -> 87 | IncludePaths = 88 | flatten1([filelib:wildcard(IncludeBlob) || IncludeBlob <- Includes]), 89 | epp:parse_file(Path, 90 | [{includes, IncludePaths}, 91 | {macros, Macros}, 92 | {default_encoding, Encoding}]). 93 | 94 | pprint_ex(Path, DoPrint, Config) -> 95 | case from_erl(Path, Config) of 96 | {ok, Ast} -> 97 | pprint_ex_ast(Path, DoPrint, Config, Ast); 98 | Other -> 99 | io:format("Error: ~p~n", [Other]) 100 | end. 101 | 102 | pprint_ex_ast(Path, 103 | DoPrint, 104 | #{output_path := OutputPath, mod_prefix := ModPrefix}, 105 | Ast) -> 106 | try 107 | {AnnAst, _St} = efe_var_ann:do(Ast), 108 | case DoPrint of 109 | true -> 110 | Code = 111 | unicode:characters_to_binary(efe_pp:format(AnnAst, 112 | #{mod_prefix => 113 | ModPrefix}), 114 | latin1, 115 | utf8), 116 | case filelib:ensure_dir(OutputPath) of 117 | ok -> 118 | case file:write_file(OutputPath, Code) of 119 | ok -> 120 | io:format("# ~s~n", [OutputPath]), 121 | ok; 122 | {error, Reason} -> 123 | io:format("Error writing file: ~p ~p~n", 124 | [Reason, OutputPath]) 125 | end; 126 | {error, Reason} -> 127 | io:format("Error creating file path: ~p ~p~n", 128 | [Reason, OutputPath]) 129 | end; 130 | false -> 131 | ok 132 | end 133 | catch 134 | T:E:S -> 135 | io:format("Error formatting ~p: ~p:~p~n~p~n", [Path, T, E, S]) 136 | end. 137 | 138 | pprint_lfe(Path, DoPrint, Config) -> 139 | case lfe_comp:file(Path, [to_erlang, to_ast, binary]) of 140 | {ok, [{ok, _ModName, Ast}]} -> 141 | pprint_ex_ast(Path, DoPrint, Config, Ast); 142 | Other -> 143 | io:format("Unexpected Result Parsing LFE ~p: ~p~n", [Path, Other]) 144 | end, 145 | ok. 146 | 147 | pprint_lfe_ast(Path, _DoPrint, _Config) -> 148 | case lfe_comp:file(Path, [to_erlang, to_ast, binary]) of 149 | {ok, [{ok, _ModName, Ast}]} -> 150 | io:format("~p~n", [Ast]); 151 | Other -> 152 | io:format("Unexpected Result Parsing LFE ~p: ~p~n", [Path, Other]) 153 | end, 154 | ok. 155 | 156 | config_for_path(ConfPath, FilePath, Ext) -> 157 | {ok, [ConfigMap]} = file:consult(ConfPath), 158 | FileDir = filename:dirname(FilePath), 159 | BaseName = filename:basename(FilePath, Ext), 160 | DesfFileName = BaseName ++ ".ex", 161 | Encoding = maps:get(encoding, ConfigMap, latin1), 162 | ModPrefix = maps:get(mod_prefix, ConfigMap, ""), 163 | MacrosMap = maps:get(macros, ConfigMap, #{}), 164 | Macros = maps:to_list(MacrosMap), 165 | OutputDir = maps:get(output_dir, ConfigMap, "."), 166 | OutputPath = 167 | filename:join([OutputDir, make_relative(FileDir), DesfFileName]), 168 | IncludeBlobs = maps:get(includes, ConfigMap, []), 169 | Includes = 170 | [canonicalize_include_blob(FileDir, Include) 171 | || Include <- IncludeBlobs], 172 | #{encoding => Encoding, 173 | macros => Macros, 174 | includes => Includes, 175 | output_dir => OutputDir, 176 | output_path => OutputPath, 177 | mod_prefix => ModPrefix}. 178 | 179 | canonicalize_include_blob(FileDir, Include = [$. | _]) -> 180 | filename:join([FileDir, Include]); 181 | canonicalize_include_blob(_FileDir, Include) -> 182 | Include. 183 | 184 | each_config_and_path(_ConfPath, [], _Fn, _Ext) -> 185 | ok; 186 | each_config_and_path(ConfPath, [FilePath | FilePaths], Fn, Ext) -> 187 | Config = config_for_path(ConfPath, FilePath, Ext), 188 | Fn(Config, FilePath), 189 | each_config_and_path(ConfPath, FilePaths, Fn, Ext). 190 | 191 | make_relative(Path = [$/ | _]) -> 192 | ["." | Path]; 193 | make_relative(Path) -> 194 | Path. 195 | 196 | flatten1(L) -> 197 | flatten1(L, []). 198 | 199 | flatten1([], Accum) -> 200 | lists:reverse(Accum); 201 | flatten1([HL | T], Accum) -> 202 | flatten1(T, flatten1_h(HL, Accum)). 203 | 204 | flatten1_h([], Accum) -> 205 | Accum; 206 | flatten1_h([H | T], Accum) -> 207 | flatten1_h(T, [H | Accum]). 208 | -------------------------------------------------------------------------------- /src/efe_pp.erl: -------------------------------------------------------------------------------- 1 | %% Copyright 2019 Mariano Guerra 2 | %% 3 | %% Licensed under the Apache License, Version 2.0 (the "License"); 4 | %% you may not use this file except in compliance with the License. 5 | %% You may obtain a copy of the License at 6 | %% 7 | %% http://www.apache.org/licenses/LICENSE-2.0 8 | %% 9 | %% Unless required by applicable law or agreed to in writing, software 10 | %% distributed under the License is distributed on an "AS IS" BASIS, 11 | %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | %% See the License for the specific language governing permissions and 13 | %% limitations under the License. 14 | 15 | -module(efe_pp). 16 | 17 | -export([format/1, format/2, layout/1]). 18 | 19 | -import(prettypr, 20 | [sep/1, 21 | beside/2, 22 | empty/0, 23 | text/1, 24 | floating/1, 25 | nest/2, 26 | par/2, 27 | above/2, 28 | follow/3]). 29 | -import(erl_parse, [inop_prec/1, preop_prec/1]). 30 | 31 | % used on tests 32 | -export([pp_guards/2, default_ctx/0]). 33 | 34 | -define(PADDING, 2). 35 | -define(PAPER, 80). % 80 36 | -define(RIBBON, 56). % 56 37 | -define(NOUSER, undefined). 38 | -define(NOHOOK, none). 39 | 40 | -record(ctxt, 41 | {prec = 0 :: integer(), 42 | sub_indent = 2 :: non_neg_integer(), 43 | exports_all = false, 44 | exports = #{}, 45 | records = #{}, 46 | imports = #{}, 47 | functions = #{}, 48 | in_guard = false, 49 | record_imported = false, 50 | stacktrace_varname = nil, 51 | mod_prefix = "", 52 | break_indent = 4 :: non_neg_integer(), 53 | paper = ?PAPER :: integer(), 54 | ribbon = ?RIBBON :: integer()}). 55 | 56 | enter_guard(Ctx) -> 57 | Ctx#ctxt{in_guard = true}. 58 | 59 | layout(V) -> 60 | layout(V, default_ctx()). 61 | 62 | layout(V, Ctx) when is_list(V) -> 63 | pp_mod(V, add_functions(V, Ctx)); 64 | layout(V, Ctx) -> 65 | pp(V, Ctx). 66 | 67 | format(V) -> 68 | format(V, #{}). 69 | 70 | format(V, Opts) -> 71 | Ctx0 = default_ctx(), 72 | ModPrefix = maps:get(mod_prefix, Opts, ""), 73 | Ctx = Ctx0#ctxt{mod_prefix = ModPrefix}, 74 | 75 | prettypr:format(layout(V, Ctx), ?PAPER, ?RIBBON). 76 | 77 | default_ctx() -> 78 | #ctxt{}. 79 | 80 | set_prec(Ctxt, Prec) -> 81 | Ctxt#ctxt{prec = Prec}. % used internally 82 | 83 | reset_prec(Ctxt) -> 84 | set_prec(Ctxt, 0). % used internally 85 | 86 | % don't set it if not explicitly set 87 | set_stacktrace_var(Ctx, '_') -> 88 | Ctx; 89 | set_stacktrace_var(Ctx, STraceVarName) -> 90 | Ctx#ctxt{stacktrace_varname = STraceVarName}. 91 | 92 | % add all top level functions to context before processing since definitions 93 | % can come after usage 94 | add_functions([], Ctx) -> 95 | Ctx; 96 | add_functions([{function, _, Name, Arity, _Clauses} | T], Ctx) -> 97 | NewFunctions = (Ctx#ctxt.functions)#{{Name, Arity} => true}, 98 | add_functions(T, Ctx#ctxt{functions = NewFunctions}); 99 | add_functions([{attribute, _, import, {ModNameAtom, ImportRefs}} | T], Ctx) -> 100 | NewImports = 101 | lists:foldl(fun (Key, ImportsIn) -> 102 | ImportsIn#{Key => #{mod => ModNameAtom}} 103 | end, 104 | Ctx#ctxt.imports, 105 | ImportRefs), 106 | add_functions(T, Ctx#ctxt{imports = NewImports}); 107 | add_functions([_ | T], Ctx) -> 108 | add_functions(T, Ctx). 109 | 110 | %is_local_function(Name, Arity, #ctxt{functions = Fs}) -> 111 | % Key = {Name, Arity}, 112 | % maps:get(Key, Fs, false) =/= false. 113 | 114 | %is_imported_function(Name, Arity, #ctxt{imports = Is}) -> 115 | % Key = {Name, Arity}, 116 | % case maps:get(Key, Is, nil) of 117 | % nil -> 118 | % false; 119 | % #{mod := Mod} -> 120 | % {true, Mod} 121 | % end. 122 | 123 | maybe_ignore_kernel_fns(Ctx = #ctxt{imports = Is, functions = Fs}) -> 124 | Fns = maps:keys(Is) ++ maps:keys(Fs), 125 | Except = [{Name, Arity} || {Name, Arity} <- Fns, is_kernel_fn(Name, Arity)], 126 | ImportLines = pp_imports(Is, Ctx), 127 | case Except of 128 | [] -> 129 | ImportLines; 130 | _ -> 131 | above(besidel([text("import Kernel, except: ["), 132 | join(Except, Ctx, fun pp_fn_import_ref/2, comma_f()), 133 | text("]")]), 134 | ImportLines) 135 | end. 136 | 137 | pp_mod([], _Ctx) -> 138 | empty(); 139 | pp_mod([{attribute, _, module, ModName} | Nodes], Ctx) -> 140 | abovel([text("defmodule :" ++ Ctx#ctxt.mod_prefix ++ a2l(ModName) ++ " do"), 141 | nestc(Ctx, 142 | abovel([text("use Bitwise"), 143 | maybe_ignore_kernel_fns(Ctx), 144 | pp_mod(Nodes, Ctx)])), 145 | text("end")]); 146 | pp_mod([Node = {attribute, _, record, {RecName, Fields}} | Nodes], Ctx) -> 147 | Ctx1 = add_record_declaration(RecName, Fields, Ctx), 148 | {Ctx2, Cont} = 149 | case Ctx1#ctxt.record_imported of 150 | true -> 151 | {Ctx1, empty()}; 152 | false -> 153 | {Ctx1#ctxt{record_imported = true}, text("require Record")} 154 | end, 155 | abovel([Cont, pp(Node, Ctx2), pp_mod(Nodes, Ctx2)]); 156 | pp_mod([Node | Nodes], Ctx) -> 157 | Ctx1 = maybe_update_ctx(Node, Ctx), 158 | above(pp(Node, Ctx1), pp_mod(Nodes, Ctx1)). 159 | 160 | maybe_update_ctx({attribute, _, compile, export_all}, Ctx) -> 161 | Ctx1 = Ctx#ctxt{exports_all = true}, 162 | Ctx1; 163 | maybe_update_ctx({attribute, _, export, Exports}, 164 | Ctx = #ctxt{exports = CurExports}) -> 165 | NewExports = 166 | maps:merge(CurExports, maps:from_list([{K, true} || K <- Exports])), 167 | Ctx1 = Ctx#ctxt{exports = NewExports}, 168 | Ctx1; 169 | maybe_update_ctx(_Node, Ctx) -> 170 | Ctx. 171 | 172 | pp({error, _}, _Ctx) -> 173 | empty(); 174 | % TODO: handle specs 175 | pp({attribute, _, spec, _}, _Ctx) -> 176 | empty(); 177 | % TODO: handle type 178 | pp({attribute, _, type, _}, _Ctx) -> 179 | empty(); 180 | % TODO: handle opaque 181 | pp({attribute, _, opaque, _}, _Ctx) -> 182 | empty(); 183 | pp({attribute, _, record, {RecName, []}}, _Ctx) -> 184 | besidel([text("Record.defrecord(:"), 185 | p_rec_name(RecName), 186 | text(", "), 187 | quote_atom(RecName), 188 | text(", [])")]); 189 | pp({attribute, _, record, {RecName, Fields}}, Ctx) -> 190 | besidel([text("Record.defrecord(:"), 191 | p_rec_name(RecName), 192 | text(", "), 193 | quote_atom(RecName), 194 | text(", "), 195 | join(Fields, Ctx, fun pp_record_field_decl/2, comma_f()), 196 | text(")")]); 197 | % TODO: handle dialyzer 198 | pp({attribute, _, dialyzer, _}, _Ctx) -> 199 | empty(); 200 | % TODO: handle callback 201 | pp({attribute, _, callback, _}, _Ctx) -> 202 | empty(); 203 | % TODO: 204 | pp({attribute, _, removed, _}, _Ctx) -> 205 | empty(); 206 | % TODO: handle optional_callbacks 207 | pp({attribute, _, optional_callbacks, _}, _Ctx) -> 208 | empty(); 209 | pp({attribute, _, file, _}, _Ctx) -> 210 | empty(); 211 | pp({attribute, _, Attr = behaviour, V}, _Ctx) -> 212 | gen_attr(Attr, V); 213 | pp({attribute, _, behavior, V}, _Ctx) -> 214 | gen_attr(behaviour, V); 215 | pp({attribute, _, Attr = author, V}, _Ctx) -> 216 | gen_attr(Attr, V); 217 | pp({attribute, _, Attr = vsn, V}, _Ctx) -> 218 | gen_attr(Attr, V); 219 | pp({attribute, _, Attr = date, V}, _Ctx) -> 220 | gen_attr(Attr, V); 221 | pp({attribute, _, Attr = vc, V}, _Ctx) -> 222 | gen_attr(Attr, V); 223 | pp({attribute, _, import, {_ModNameAtom, _Imports}}, _Ctx) -> 224 | % ignored here since they are coalesced by module and inserted before 225 | empty(); 226 | % TODO: 227 | pp({attribute, _, export_type, _Exports}, _Ctx) -> 228 | % pp_attr_fun_list("@export_type ", Exports, Ctx); 229 | empty(); 230 | pp({attribute, _, on_load, {FName, 0}}, _Ctx) -> 231 | besidel([text("@on_load "), quote_atom(FName)]); 232 | pp({attribute, _, deprecated, _V = {_FName, _Arity, _When}}, _Ctx) -> 233 | %besidel([text("@deprecated "), pp_fn_deprecated_ref(V, Ctx)]); 234 | empty(); 235 | pp({attribute, _, deprecated, module}, _Ctx) -> 236 | text("@deprecated :module"); 237 | pp({attribute, _, deprecated, _Funs}, _Ctx) -> 238 | %besidel([text("@deprecated "), 239 | % join(Funs, Ctx, fun pp_fn_deprecated_ref/2, comma_f())]); 240 | empty(); 241 | % TODO: 242 | pp({attribute, _, deprecated_type, _}, _Ctx) -> 243 | empty(); 244 | % TODO: 245 | pp({attribute, _, removed_type, _}, _Ctx) -> 246 | empty(); 247 | % TODO: 248 | pp({attribute, _, inline, _Exports}, _Ctx) -> 249 | %pp_attr_fun_list("@inline ", Exports, Ctx); 250 | empty(); 251 | pp({attribute, _, export, _}, _Ctx) -> 252 | empty(); 253 | pp({attribute, _, compile, export_all}, _Ctx) -> 254 | text("@compile :export_all"); 255 | pp({attribute, _, compile, _}, _Ctx) -> 256 | empty(); 257 | pp({var, _, V}, #ctxt{stacktrace_varname = V}) -> 258 | text("__STACKTRACE__"); 259 | pp({var, _, V, _}, #ctxt{stacktrace_varname = V}) -> 260 | text("__STACKTRACE__"); 261 | pp({var, _, V, #{new := false, matching := true}}, #ctxt{}) -> 262 | text("^" ++ transform_var_name(V)); 263 | pp({var, _, V, _}, _Ctx) -> 264 | text(transform_var_name(V)); 265 | pp({var, _, V}, _Ctx) -> 266 | text(transform_var_name(V)); 267 | pp({atom, _, true}, _Ctx) -> 268 | text("true"); 269 | pp({atom, _, false}, _Ctx) -> 270 | text("false"); 271 | pp({atom, _, nil}, _Ctx) -> 272 | text("nil"); 273 | pp({atom, _, V}, _Ctx) -> 274 | quote_atom(V); 275 | pp({integer, _, Num}, _Ctx) -> 276 | text(integer_to_list(Num)); 277 | pp({float, _, Num}, _Ctx) -> 278 | text(io_lib:write(Num)); 279 | pp({string, _, V}, _Ctx) -> 280 | quote_string(V, $'); 281 | pp({bin, _, [{bin_element, _, {string, _, V}, default, default}]}, _Ctx) -> 282 | quote_string(V, $"); 283 | pp({char, _, $\s}, _Ctx) -> 284 | text("?\\s"); 285 | pp({char, _, $\r}, _Ctx) -> 286 | text("?\\r"); 287 | pp({char, _, $\t}, _Ctx) -> 288 | text("?\\t"); 289 | pp({char, _, $\n}, _Ctx) -> 290 | text("?\\n"); 291 | pp({char, _, $\f}, _Ctx) -> 292 | text("?\\f"); 293 | pp({char, _, $\e}, _Ctx) -> 294 | text("?\\e"); 295 | pp({char, _, $\d}, _Ctx) -> 296 | text("?\\d"); 297 | pp({char, _, $\b}, _Ctx) -> 298 | text("?\\b"); 299 | pp({char, _, $\v}, _Ctx) -> 300 | text("?\\v"); 301 | pp({char, _, $\\}, _Ctx) -> 302 | text("?\\\\"); 303 | pp({char, _, $\^G}, _Ctx) -> 304 | text("?\\a"); 305 | pp({char, _, $\^C}, _Ctx) -> 306 | text("3"); 307 | pp({char, _, V}, _Ctx) when V >= 32 andalso V =< 127 -> 308 | text("?" ++ [V]); 309 | pp({char, _, V}, _Ctx) -> 310 | text(format_non_printable_char(V)); 311 | pp({record, _, RecName, Fields}, Ctx) -> 312 | pp_rec_new(RecName, Fields, Ctx); 313 | pp({record, _, CurRecExpr, RecName, Fields}, Ctx) -> 314 | pp_rec_update(RecName, Fields, CurRecExpr, Ctx); 315 | pp({record_field, _, RecExpr, RecName, Field}, Ctx) -> 316 | pp_rec_field(RecExpr, RecName, Field, Ctx); 317 | pp({record_index, _, RecName, Field}, Ctx) -> 318 | pp_rec_index(RecName, Field, Ctx); 319 | pp({bin, _, Elems}, Ctx) -> 320 | besidel([text("<<"), pp_bin_es(Elems, Ctx), text(">>")]); 321 | % fun references 322 | pp({'fun', Line, {function, FName, Arity}}, Ctx) -> 323 | beside(text("&"), 324 | wrap(text("/"), 325 | pp_call_pos({atom, Line, FName}, "", Ctx), 326 | pp({integer, Line, Arity}, Ctx))); 327 | pp({'fun', 328 | _, 329 | {function, 330 | MName = {atom, _, _}, 331 | FName = {atom, _, _}, 332 | Arity = {integer, _, _}}}, 333 | Ctx) -> 334 | beside(text("&"), 335 | wrap(text("/"), 336 | wrap(dot_f(), 337 | pp_call_pos(MName, ":", Ctx), 338 | pp_call_pos(FName, "", Ctx)), 339 | pp(Arity, Ctx))); 340 | pp({'fun', _, {function, MName, FName, Arity}}, Ctx) -> 341 | besidel([text("Function.capture("), 342 | pp(MName, Ctx), 343 | text(", "), 344 | pp(FName, Ctx), 345 | text(", "), 346 | pp(Arity, Ctx), 347 | text(")")]); 348 | pp({nil, _}, _Ctx) -> 349 | text("[]"); 350 | pp(V = {cons, _, _H, _T}, Ctx) -> 351 | pp_cons(V, Ctx); 352 | pp({tuple, _, Items}, Ctx) -> 353 | wrap_tuple(pp_items(Items, Ctx)); 354 | pp({map, _, []}, _Ctx) -> 355 | text("%{}"); 356 | pp({map, _, Items}, Ctx) -> 357 | pp_map(Items, Ctx); 358 | pp({map, _, CurMap, Items}, Ctx) -> 359 | pp_map_update(CurMap, Items, Ctx); 360 | % record_info expansion 361 | pp({call, _, {atom, _, record_info}, [{atom, _, fields}, {atom, _, RecName}]}, 362 | _Ctx) -> 363 | besidel([text("Keyword.keys("), 364 | p_rec_name(RecName), 365 | text("("), 366 | p_rec_name(RecName), 367 | text("()))")]); 368 | pp({call, _, {atom, _, record_info}, [{atom, _, size}, {atom, _, RecName}]}, 369 | _Ctx) -> 370 | besidel([text("length("), 371 | p_rec_name(RecName), 372 | text("("), 373 | p_rec_name(RecName), 374 | text("()))")]); 375 | pp({call, _, Expr = {call, _, _, _}, Args}, Ctx) -> 376 | wrap_call(Expr, Args, Ctx); 377 | pp({call, _, Expr = {'fun', _, _}, Args}, Ctx) -> 378 | wrap_call(Expr, Args, Ctx); 379 | pp({call, _, Expr = {record_field, _, _, _, _}, Args}, Ctx) -> 380 | wrap_call(Expr, Args, Ctx); 381 | pp({call, _, {remote, _, MName, FName}, Args}, Ctx) -> 382 | pp_call(MName, FName, Args, Ctx); 383 | pp({call, _, FName, Args}, Ctx) -> 384 | pp_call(FName, Args, Ctx); 385 | pp({'fun', _, {clauses, Clauses}}, Ctx) -> 386 | above(beside(text("fn "), pp_case_clauses(Clauses, Ctx, "()")), 387 | text("end")); 388 | pp({named_fun, _, AName, Clauses}, Ctx) -> 389 | above(sep([text("fn " ++ transform_var_name(AName)), 390 | pp_case_clauses(Clauses, Ctx)]), 391 | text("end")); 392 | pp({function, _, Name, Arity, Clauses}, Ctx) -> 393 | IsExported = function_exported(Ctx, Name, Arity), 394 | DefKw = 395 | if IsExported -> 396 | "def"; 397 | true -> 398 | "defp" 399 | end, 400 | % HACK: force a new line above each top level function 401 | pp_function_clauses(Clauses, Name, DefKw, Ctx); 402 | pp({match, _, Left, Right}, Ctx) -> 403 | besidel([pp(Left, Ctx), text(" = "), pp_oper(Right, Ctx)]); 404 | pp({op, _, 'div', Left, Right}, Ctx) -> 405 | call_op("div(", Left, Right, Ctx); 406 | pp({op, _, 'rem', Left, Right}, Ctx) -> 407 | call_op("rem(", Left, Right, Ctx); 408 | pp({op, _, '!', Left, Right}, Ctx) -> 409 | call_op("send(", Left, Right, Ctx); 410 | % or/and in guards are compiled to elixir or/and (right side not evaluated) 411 | % since guards should not have side effects and the result should be almost the 412 | % same (unless the right side throws and the guard evaluates to false?) 413 | pp({op, Line, 'or', Left, Right}, Ctx = #ctxt{in_guard = true}) -> 414 | pp({op, Line, 'orelse', Left, Right}, Ctx); 415 | pp({op, Line, 'and', Left, Right}, Ctx = #ctxt{in_guard = true}) -> 416 | pp({op, Line, 'andalso', Left, Right}, Ctx); 417 | pp({op, Line, Op, Left, Right}, Ctx) -> 418 | case is_erlang_op(Op) of 419 | true -> 420 | op_to_erlang_call(Line, Op, [Left, Right], Ctx); 421 | false -> 422 | {LeftPrec, Prec, RightPrec} = inop_prec(Op), 423 | D1 = pp_oper(Left, Ctx#ctxt{prec = LeftPrec}), 424 | D2 = text(atom_to_list(map_op_reverse(Op))), 425 | D3 = pp_oper(Right, Ctx#ctxt{prec = RightPrec}), 426 | D4 = besidel([D1, text(" "), D2, text(" "), D3]), 427 | maybe_paren(Prec, Ctx#ctxt.prec, D4) 428 | end; 429 | % unary 430 | pp({op, Line, Op, Right}, Ctx) -> 431 | case is_erlang_op(Op) of 432 | true -> 433 | op_to_erlang_call(Line, Op, [Right], Ctx); 434 | false -> 435 | {Prec, RightPrec} = preop_prec(Op), 436 | LOp = text(atom_to_list(map_op_reverse(Op))), 437 | LRight = pp(Right, Ctx#ctxt{prec = RightPrec}), 438 | L = sep([LOp, LRight]), 439 | maybe_paren(Prec, Ctx#ctxt.prec, L) 440 | end; 441 | pp({lc, _, Body, Gens}, Ctx) -> 442 | Ctx1 = reset_prec(Ctx), 443 | pp_for(Gens, Ctx1, pp(Body, Ctx1)); 444 | % https://elixir-lang.org/getting-started/comprehensions.html#bitstring-generators 445 | % http://www.arh68.com/2016/01/17/drinking-more-elixir.html 446 | pp({bc, _, Body, Gens}, Ctx) -> 447 | Ctx1 = reset_prec(Ctx), 448 | pp_bfor(Gens, Ctx1, pp(Body, Ctx1)); 449 | pp({block, _, Body}, Ctx) -> 450 | Ctx1 = reset_prec(Ctx), 451 | above(text("("), above(nestc(Ctx1, pp_body(Body, Ctx1)), text(")"))); 452 | pp({'if', _, Clauses}, Ctx) -> 453 | above(text("cond do"), 454 | above(nestc(Ctx, pp_if_clauses(Clauses, Ctx)), text("end"))); 455 | pp({'case', _, Expr, Clauses}, Ctx) -> 456 | % TODO: not always wrap Expr in paren, only nested statements 457 | above(besidel([text("case "), wrap_parens(pp(Expr, Ctx)), text(" do")]), 458 | above(nestc(Ctx, pp_case_clauses(Clauses, Ctx)), text("end"))); 459 | % receive no after 460 | pp({'receive', _, Clauses}, Ctx0) -> 461 | Ctx = reset_prec(Ctx0), 462 | abovel([text("receive do"), 463 | nestc(Ctx, pp_case_clauses(Clauses, Ctx)), 464 | text("end")]); 465 | pp({'receive', _, [], AfterExpr, AfterBody}, Ctx0) -> 466 | Ctx = reset_prec(Ctx0), 467 | abovel([besidel([text("receive do"), 468 | text(" after "), 469 | sep([pp(AfterExpr, Ctx), sarrow_f()])]), 470 | nestc(Ctx, pp_body(AfterBody, Ctx)), 471 | text("end")]); 472 | pp({'receive', _, Clauses, AfterExpr, AfterBody}, Ctx0) -> 473 | Ctx = reset_prec(Ctx0), 474 | abovel([text("receive do"), 475 | nestc(Ctx, pp_case_clauses(Clauses, Ctx)), 476 | beside(text("after "), sep([pp(AfterExpr, Ctx), sarrow_f()])), 477 | nestc(Ctx, pp_body(AfterBody, Ctx)), 478 | text("end")]); 479 | pp({'catch', _, Expr}, Ctx0) -> 480 | Ctx = reset_prec(Ctx0), 481 | above(text("(try do"), 482 | abovel([nestc(Ctx, pp(Expr, Ctx)), 483 | text("catch"), 484 | text(" :error, e -> {:EXIT, {e, __STACKTRACE__}}"), 485 | text(" :exit, e -> {:EXIT, e}"), 486 | text(" e -> e"), 487 | text("end)")])); 488 | pp({'try', _, Body, [], Clauses, AfterBody}, Ctx0) -> 489 | Ctx = reset_prec(Ctx0), 490 | above(text("try do"), 491 | above(maybe_above(maybe_above(nestc(Ctx, pp_body(Body, Ctx)), 492 | pp_try_catch_clauses(Clauses, Ctx)), 493 | pp_try_after(AfterBody, Ctx)), 494 | text("end"))); 495 | pp({'try', _, Expr, OfCases, Clauses, AfterBody}, Ctx0) -> 496 | Ctx = reset_prec(Ctx0), 497 | abovel([abovel([text("try do"), nestc(Ctx, pp_body(Expr, Ctx))]), 498 | maybe_above(maybe_above(pp_try_catch_clauses(Clauses, Ctx), 499 | above(text("else"), 500 | nestc(Ctx, 501 | pp_case_clauses(OfCases, 502 | Ctx)))), 503 | pp_try_after(AfterBody, Ctx)), 504 | text("end")]); 505 | pp({eof, _}, _Ctx) -> 506 | empty(). 507 | 508 | % wrap statements in value position in parenthesis 509 | pp_oper(Expr = {match, _, _Left, _Right}, Ctx) -> 510 | wrap_parens(pp(Expr, Ctx)); 511 | pp_oper(Expr = {lc, _, _Body, _Gens}, Ctx) -> 512 | wrap_parens(pp(Expr, Ctx)); 513 | pp_oper(Expr = {bc, _, _Body, _Gens}, Ctx) -> 514 | wrap_parens(pp(Expr, Ctx)); 515 | pp_oper(Expr = {block, _, _Body}, Ctx) -> 516 | wrap_parens(pp(Expr, Ctx)); 517 | pp_oper(Expr = {'if', _, _Clauses}, Ctx) -> 518 | wrap_parens(pp(Expr, Ctx)); 519 | pp_oper(Expr = {'case', _, _Expr, _Clauses}, Ctx) -> 520 | wrap_parens(pp(Expr, Ctx)); 521 | pp_oper(Expr = {'receive', _, _Clauses}, Ctx) -> 522 | wrap_parens(pp(Expr, Ctx)); 523 | pp_oper(Expr = {'receive', _, _Clauses, _AfterExpr, _AfterBody}, Ctx) -> 524 | wrap_parens(pp(Expr, Ctx)); 525 | pp_oper(Expr = {'catch', _, _Expr}, Ctx) -> 526 | wrap_parens(pp(Expr, Ctx)); 527 | pp_oper(Expr = {'try', _, _Body, [], _Clauses, _AfterBody}, Ctx) -> 528 | wrap_parens(pp(Expr, Ctx)); 529 | pp_oper(Expr = {'try', _, _Expr, _OfCases, _Clauses, _AfterBody}, Ctx) -> 530 | wrap_parens(pp(Expr, Ctx)); 531 | pp_oper(Expr, Ctx) -> 532 | pp(Expr, Ctx). 533 | 534 | function_exported(#ctxt{exports_all = true}, _, _) -> 535 | true; 536 | function_exported(#ctxt{exports = Exports}, Name, Arity) -> 537 | maps:is_key({Name, Arity}, Exports). 538 | 539 | pp_function_clauses([Clause], Name, DefKw, Ctx) -> 540 | pp_function_clause(Clause, Name, DefKw, Ctx); 541 | pp_function_clauses([Clause | Clauses], Name, DefKw, Ctx) -> 542 | above(pp_function_clause(Clause, Name, DefKw, Ctx), 543 | pp_function_clauses(Clauses, Name, DefKw, Ctx)). 544 | 545 | pp_function_clause({clause, _, [], [], Body}, Name, DefKw, Ctx) -> 546 | pp_header_and_body(Ctx, 547 | text(DefKw ++ " " ++ pp_def_fn_name(Name) ++ "() do"), 548 | Body); 549 | pp_function_clause({clause, _, Patterns, [], Body}, Name, DefKw, Ctx) -> 550 | pp_header_and_body(Ctx, 551 | beside(text(DefKw ++ " " ++ pp_def_fn_name(Name) ++ "("), 552 | beside(pp_args_inn(Patterns, Ctx), text(") do"))), 553 | Body); 554 | pp_function_clause({clause, _, Patterns, Guards, Body}, Name, DefKw, Ctx) -> 555 | pp_header_and_body(Ctx, 556 | followc(Ctx, 557 | besidel([text(DefKw ++ 558 | " " ++ 559 | pp_def_fn_name(Name) ++ 560 | "("), 561 | pp_args_inn(Patterns, Ctx), 562 | text(")")]), 563 | beside(text("when "), 564 | beside(pp_guards(Guards, Ctx), 565 | text(" do")))), 566 | Body). 567 | 568 | pp_args_inn(Args, Ctx) -> 569 | pp_args_inn(Args, Ctx, fun pp/2, fun ueval/1). 570 | 571 | pp_args_inn([], _Ctx, _PPFun, _EvalFn) -> 572 | empty(); 573 | pp_args_inn([Arg], Ctx, PPFun, EvalFn) -> 574 | PPFun(EvalFn(Arg), Ctx); 575 | pp_args_inn(Args, Ctx, PPFun, EvalFn) -> 576 | join([EvalFn(Arg) || Arg <- Args], Ctx, PPFun, comma_f()). 577 | 578 | pp_header_and_body_no_end(Ctx, HeaderLayout, Body) -> 579 | sep([HeaderLayout, nestc(Ctx, pp_body(Body, Ctx))]). 580 | 581 | pp_header_and_body(Ctx, HeaderLayout, Body) -> 582 | above(pp_header_and_body_no_end(Ctx, HeaderLayout, Body), endk()). 583 | 584 | gen_attr(Attr, V) when is_atom(V) -> 585 | text("@" ++ a2l(Attr) ++ " " ++ quote_atom_raw(V)); 586 | gen_attr(Attr, V) when is_list(V) -> 587 | text("@" ++ a2l(Attr) ++ " " ++ quote_string_raw(V)). 588 | 589 | quote_atom_raw(V) -> 590 | [":" | maybe_quote_atom_str(a2l(V))]. 591 | 592 | quote_string(V) -> 593 | quote_string(V, $"). 594 | 595 | quote_string(V, QuoteChar) -> 596 | text(quote_string_raw(V, QuoteChar)). 597 | 598 | quote_string_raw(V) -> 599 | quote_string_raw(V, $"). 600 | 601 | quote_string_raw(V, QuoteChar) -> 602 | string:replace(io_lib:write_string(V, QuoteChar), "#{", "\\#{", all). 603 | 604 | maybe_quote_atom_str(Chars) -> 605 | case should_quote_atom_str(Chars) of 606 | true -> 607 | quote_string_raw(Chars); 608 | false -> 609 | Chars 610 | end. 611 | 612 | should_quote_atom(A) -> 613 | should_quote_atom_str(a2l(A)). 614 | 615 | should_quote_atom_str(Chars) -> 616 | case re:run(Chars, "^[a-zA-Z_-][a-zA-Z0-9@_]*[?!]?$") of 617 | nomatch -> 618 | true; 619 | {match, _} -> 620 | false 621 | end. 622 | 623 | quote_record_field(V) -> 624 | Chars = a2l(V), 625 | % will quote all erlang reserved words, I think it's ok 626 | case io_lib:quote_atom(V, Chars) of 627 | true -> 628 | quote_string_raw(Chars); 629 | false -> 630 | Chars 631 | end. 632 | 633 | comma_f() -> 634 | floating(text(",")). 635 | 636 | olist_f() -> 637 | floating(text("[")). 638 | 639 | clist_f() -> 640 | floating(text("]")). 641 | 642 | oparen_f() -> 643 | floating(text("(")). 644 | 645 | cparen_f() -> 646 | floating(text(")")). 647 | 648 | otuple_f() -> 649 | floating(text("{")). 650 | 651 | ctuple_f() -> 652 | floating(text("}")). 653 | 654 | omap_f() -> 655 | floating(text("%{")). 656 | 657 | cmap_f() -> 658 | floating(text("}")). 659 | 660 | arrow_f() -> 661 | text("=>"). 662 | 663 | sarrow_f() -> 664 | text("->"). 665 | 666 | dot_f() -> 667 | text("."). 668 | 669 | dcolon_f() -> 670 | text("::"). 671 | 672 | endk() -> 673 | text("end\n"). 674 | 675 | pipe() -> 676 | text("|"). 677 | 678 | % not sure if the best way 679 | pp_body([], _Ctx) -> 680 | empty(); 681 | pp_body([H | T], Ctx) -> 682 | above(pp(H, Ctx), pp_body(T, Ctx)). 683 | 684 | abovel([]) -> 685 | empty(); 686 | abovel([H]) -> 687 | H; 688 | % maybe skip empty() here? 689 | abovel([H | T]) -> 690 | above(H, abovel(T)). 691 | 692 | besidel([]) -> 693 | empty(); 694 | besidel([H]) -> 695 | H; 696 | besidel([H | T]) -> 697 | beside(H, besidel(T)). 698 | 699 | followc(Ctx, L1, L2) -> 700 | follow(L1, L2, Ctx#ctxt.sub_indent * 2). 701 | 702 | parc(Ctx, L) -> 703 | par(L, Ctx#ctxt.sub_indent). 704 | 705 | nestc(Ctx, Layout) -> 706 | nest(Ctx#ctxt.sub_indent, Layout). 707 | 708 | % null is empty() 709 | maybe_above(L, null) -> 710 | L; 711 | maybe_above(null, L) -> 712 | L; 713 | maybe_above(LLeft, LRight) -> 714 | above(LLeft, LRight). 715 | 716 | join(Items, Ctx, PPFun, Sep) -> 717 | join(Items, Ctx, PPFun, Sep, []). 718 | 719 | join([], _Ctx, _PPFun, _Sep, []) -> 720 | empty(); 721 | join([Item], Ctx, PPFun, _Sep, []) -> 722 | PPFun(Item, Ctx); 723 | join([Item], Ctx, PPFun, _Sep, Accum) -> 724 | par(lists:reverse([PPFun(Item, Ctx) | Accum]), 2); 725 | join([H | T = [_ | _]], Ctx, PPFun, Sep, Accum) -> 726 | join(T, Ctx, PPFun, Sep, [beside(PPFun(H, Ctx), Sep) | Accum]); 727 | join([H | T], Ctx, PPFun, Sep, Accum) -> 728 | join([T], Ctx, PPFun, Sep, [beside(PPFun(H, Ctx), Sep) | Accum]). 729 | 730 | wrap_list(Items) -> 731 | wrap(Items, olist_f(), clist_f()). 732 | 733 | wrap(Items, Open, Close) -> 734 | beside(Open, beside(Items, Close)). 735 | 736 | pp_fn_import_ref({FNameAtom, Arity}, _Ctx) -> 737 | text("" ++ a2l(FNameAtom) ++ ": " ++ arity_to_list(Arity)). 738 | 739 | %pp_fn_deprecated_ref({FName, Arity, When}, _Ctx) when is_atom(When) -> 740 | % text("(" ++ 741 | % a2l(FName) ++ 742 | % ", " ++ arity_to_list(Arity) ++ ", " ++ a2l(When) ++ ")"); 743 | %pp_fn_deprecated_ref({FName, Arity, Msg}, _Ctx) when is_list(Msg) -> 744 | % text("(" ++ 745 | % a2l(FName) ++ 746 | % ", " ++ 747 | % arity_to_list(Arity) ++ 748 | % ", " ++ io_lib:write_string(Msg) ++ ")"); 749 | %pp_fn_deprecated_ref(FnRef, Ctx) -> 750 | % pp_fn_ref(FnRef, Ctx). 751 | 752 | arity_to_list('_') -> 753 | ":_"; 754 | arity_to_list(V) -> 755 | integer_to_list(V). 756 | 757 | % TODO: check tuple size if record info in ctx 758 | pp_call({atom, _, is_record}, [Expr, {atom, _, RecTag}], Ctx) -> 759 | besidel([text("elem("), 760 | pp(Expr, Ctx), 761 | text(", 0) === "), 762 | quote_atom(RecTag)]); 763 | pp_call(FName, Args, Ctx) -> 764 | case should_prefix_call(FName, length(Args), Ctx) of 765 | {true, {Type, Mod}} -> 766 | {_, Line, _} = FName, 767 | pp_call_f({Type, Line, Mod}, FName, Args, Ctx, fun pp/2); 768 | false -> 769 | pp_call_f(FName, Args, Ctx, fun pp/2) 770 | end. 771 | 772 | pp_call(MName, FName, Args, Ctx) -> 773 | pp_call_f(MName, FName, Args, Ctx, fun pp/2). 774 | 775 | wrap_call(Expr, Args, Ctx) -> 776 | besidel([wrap_parens(pp(Expr, Ctx)), 777 | text("."), 778 | pp_args(Args, Ctx, fun pp/2)]). 779 | 780 | pp_call_f(FName, Args, Ctx, PPFun) -> 781 | beside(pp_call_pos(FName, "", Ctx), pp_args(Args, Ctx, PPFun)). 782 | 783 | pp_call_f(MName, FName = {var, Line, _}, Args, Ctx, _PPFun) -> 784 | pp_call_dyn_f(MName, FName, Args, Line, Ctx); 785 | pp_call_f(MName, FName = {var, Line, _, _}, Args, Ctx, _PPFun) -> 786 | pp_call_dyn_f(MName, FName, Args, Line, Ctx); 787 | pp_call_f(MName, FName, Args, Ctx, PPFun) -> 788 | beside(wrap(dot_f(), 789 | pp_call_method_pos(MName, ":", Ctx), 790 | pp_call_pos(FName, "", Ctx)), 791 | pp_args(Args, Ctx, PPFun)). 792 | 793 | pp_call_dyn_f(MName, FName, Args, Line, Ctx) -> 794 | ApplyArgs = [MName, FName, list_to_cons(Args, Line)], 795 | pp({call, Line, {atom, Line, apply}, ApplyArgs}, Ctx). 796 | 797 | pp_call_pos(V = {var, _, _}, _, Ctx) -> 798 | beside(pp(V, Ctx), text(".")); 799 | pp_call_pos(V = {var, _, _, _}, _, Ctx) -> 800 | beside(pp(V, Ctx), text(".")); 801 | pp_call_pos({atom, _, V}, "", _Ctx) -> 802 | case should_quote_atom(V) of 803 | true -> 804 | text("unquote(" ++ quote_atom_raw(V) ++ ")"); 805 | false -> 806 | text(pp_call_fn_name(V)) 807 | end; 808 | pp_call_pos({atom, _, V}, Prefix, _Ctx) -> 809 | text(Prefix ++ pp_call_fn_name(V)); 810 | pp_call_pos(V, _, Ctx) -> 811 | beside(oparen_f(), beside(pp(V, Ctx), cparen_f())). 812 | 813 | pp_call_method_pos(V = {var, _, _}, _, Ctx) -> 814 | pp(V, Ctx); 815 | pp_call_method_pos(V = {var, _, _, _}, _, Ctx) -> 816 | pp(V, Ctx); 817 | pp_call_method_pos({atom, _, V}, Prefix, _Ctx) -> 818 | text(Prefix ++ a2l(V)); 819 | pp_call_method_pos(V, _, Ctx) -> 820 | beside(oparen_f(), beside(pp(V, Ctx), cparen_f())). 821 | 822 | pp_def_fn_name(V) -> 823 | pp_def_fn_name(V, ""). 824 | 825 | pp_def_fn_name(V, Prefix) -> 826 | Name = a2l(V), 827 | case should_quote_def_fn_name(Name) of 828 | true -> 829 | [Prefix, "unquote(", quote_atom_raw(V), ")"]; 830 | false -> 831 | [Prefix, Name] 832 | end. 833 | 834 | pp_call_fn_name(V) -> 835 | Name = a2l(V), 836 | case should_quote_atom_str(Name) of 837 | true -> 838 | quote_string_raw(Name, $'); 839 | false -> 840 | % words starting with uppercase should be quoted too 841 | case Name of 842 | [H | _] when H < $a orelse H > $z -> 843 | quote_string_raw(Name, $'); 844 | _ -> 845 | Name 846 | end 847 | end. 848 | 849 | % https://hexdocs.pm/elixir/syntax-reference.html#reserved-words 850 | should_quote_def_fn_name("true") -> 851 | true; 852 | should_quote_def_fn_name("false") -> 853 | true; 854 | should_quote_def_fn_name("nil") -> 855 | true; 856 | should_quote_def_fn_name("when") -> 857 | true; 858 | should_quote_def_fn_name("and") -> 859 | true; 860 | should_quote_def_fn_name("or") -> 861 | true; 862 | should_quote_def_fn_name("not") -> 863 | true; 864 | should_quote_def_fn_name("in") -> 865 | true; 866 | should_quote_def_fn_name("fn") -> 867 | true; 868 | should_quote_def_fn_name("do") -> 869 | true; 870 | should_quote_def_fn_name("end") -> 871 | true; 872 | should_quote_def_fn_name("catch") -> 873 | true; 874 | should_quote_def_fn_name("rescue") -> 875 | true; 876 | should_quote_def_fn_name("after") -> 877 | true; 878 | should_quote_def_fn_name("else") -> 879 | true; 880 | should_quote_def_fn_name(Name) -> 881 | should_quote_atom_str(Name). 882 | 883 | identity(V) -> 884 | V. 885 | 886 | pp_args([], _Ctx, _PPFun) -> 887 | text("()"); 888 | pp_args(Args, Ctx, PPFun) -> 889 | beside(oparen_f(), 890 | beside(pp_args_inn(Args, Ctx, PPFun, fun identity/1), cparen_f())). 891 | 892 | quote_atom(V) -> 893 | text(quote_atom_raw(V)). 894 | 895 | sep_for_tail({cons, _, _, _}) -> 896 | text(","); 897 | sep_for_tail(_) -> 898 | text(" |"). 899 | 900 | pp_cons({cons, _, H, {nil, _}}, Ctx) -> 901 | wrap_list(pp(H, Ctx)); 902 | pp_cons({cons, _, H, T}, Ctx) -> 903 | wrap_list(followc(Ctx, 904 | beside(pp(H, Ctx), sep_for_tail(T)), 905 | pp_cons_tail(T, Ctx))). 906 | 907 | pp_cons_tail({cons, _, H, {nil, _}}, Ctx) -> 908 | pp(H, Ctx); 909 | pp_cons_tail({cons, _, H, T}, Ctx) -> 910 | followc(Ctx, beside(pp(H, Ctx), sep_for_tail(T)), pp_cons_tail(T, Ctx)); 911 | pp_cons_tail(V, Ctx) -> 912 | pp(V, Ctx). 913 | 914 | pp_items(Items, Ctx) -> 915 | join(Items, Ctx, fun pp/2, comma_f()). 916 | 917 | list_to_cons([], Line) -> 918 | {nil, Line}; 919 | list_to_cons([H | T], Line) -> 920 | {cons, Line, H, list_to_cons(T, Line)}. 921 | 922 | wrap_tuple(Items) -> 923 | wrap(Items, otuple_f(), ctuple_f()). 924 | 925 | wrap_map(Items) -> 926 | wrap(Items, omap_f(), cmap_f()). 927 | 928 | wrap_parens(Items) -> 929 | wrap(Items, oparen_f(), cparen_f()). 930 | 931 | pp_map(Items, Ctx) -> 932 | wrap_map(pp_map_inner(Items, Ctx)). 933 | 934 | pp_map_inner(Items, Ctx) -> 935 | join(Items, Ctx, fun pp_pair/2, comma_f()). 936 | 937 | pp_map_update(CurMap, Items, Ctx) -> 938 | case split_map_pairs(Items, {[], []}) of 939 | % https://github.com/erlang/otp/blob/9cc36bfa910f1bfc6fc7e759eb58442020e74039/lib/observer/src/observer_perf_wx.erl#L291 940 | {[], []} -> 941 | pp(CurMap, Ctx); 942 | {[], Exact} -> 943 | pp_map_update_exact(CurMap, Exact, Ctx); 944 | {[Assoc], []} -> 945 | pp_map_update_put(CurMap, Assoc, Ctx); 946 | {Assoc, []} -> 947 | pp_map_update_merge(CurMap, Assoc, Ctx); 948 | {[Assoc], Exact} -> 949 | ExactPP = pp_map_update_exact(CurMap, Exact, Ctx), 950 | pp_map_update_put_h(ExactPP, Assoc, Ctx); 951 | {Assoc, Exact} -> 952 | ExactPP = pp_map_update_exact(CurMap, Exact, Ctx), 953 | pp_map_update_merge_h(ExactPP, Assoc, Ctx) 954 | end. 955 | 956 | % %{a: 1, "b" => 2} (one or more updates to current keys) 957 | pp_map_update_exact(CurMap, Items, Ctx) -> 958 | Updates = join(Items, Ctx, fun pp_pair/2, comma_f()), 959 | wrap_map(sep([pp(CurMap, Ctx), pipe(), Updates])). 960 | 961 | % Map.put(cur_m, key, val) 962 | pp_map_update_put(CurMap, Assoc, Ctx) -> 963 | pp_map_update_put_h(pp(CurMap, Ctx), Assoc, Ctx). 964 | 965 | pp_map_update_put_h(CurMapPP, {map_field_assoc, _, K, V}, Ctx) -> 966 | besidel([text("Map.put("), 967 | CurMapPP, 968 | text(", "), 969 | join([K, V], Ctx, fun pp/2, comma_f()), 970 | text(")")]). 971 | 972 | % Map.merge(cur_m, %{...}) 973 | pp_map_update_merge(CurMap, Items, Ctx) -> 974 | pp_map_update_merge_h(pp(CurMap, Ctx), Items, Ctx). 975 | 976 | pp_map_update_merge_h(CurMapPP, Items, Ctx) -> 977 | Updates = wrap_map(join(Items, Ctx, fun pp_pair/2, comma_f())), 978 | besidel([text("Map.merge("), CurMapPP, text(", "), Updates, text(")")]). 979 | 980 | split_map_pairs([], {Assoc, Exact}) -> 981 | {lists:reverse(Assoc), lists:reverse(Exact)}; 982 | split_map_pairs([H = {map_field_assoc, _, _, _} | T], {Assoc, Exact}) -> 983 | split_map_pairs(T, {[H | Assoc], Exact}); 984 | split_map_pairs([H = {map_field_exact, _, _, _} | T], {Assoc, Exact}) -> 985 | split_map_pairs(T, {Assoc, [H | Exact]}). 986 | 987 | pp_pair({Op, _, {atom, _, K}, V}, Ctx) 988 | when Op =:= map_field_exact orelse Op =:= map_field_assoc -> 989 | wrap_pair_no_left_space(Ctx, 990 | text(":"), 991 | text(quote_record_field(K)), 992 | pp(V, Ctx)); 993 | pp_pair({Op, _, K, V}, Ctx) 994 | when Op =:= map_field_exact orelse Op =:= map_field_assoc -> 995 | wrap_pair(Ctx, arrow_f(), pp(K, Ctx), pp(V, Ctx)). 996 | 997 | wrap_pair(Ctx, Sep, Left, Right) -> 998 | parc(Ctx, [sep([Left, Sep, Right])]). 999 | 1000 | wrap_pair_no_left_space(Ctx, Sep, Left, Right) -> 1001 | parc(Ctx, [sep([besidel([Left, Sep]), Right])]). 1002 | 1003 | %wrap_pair_no_left_space(Ctx, Sep, Left, Right) -> 1004 | % parc(Ctx, [beside(Left, Sep), Right]). 1005 | 1006 | maybe_paren(P, Prec, Expr) when P < Prec -> 1007 | wrap_paren(Expr); 1008 | maybe_paren(_P, _Prec, Expr) -> 1009 | Expr. 1010 | 1011 | wrap_paren(Expr) -> 1012 | beside(beside(oparen_f(), Expr), cparen_f()). 1013 | 1014 | pp_if_clauses([Clause], Ctx) -> 1015 | pp_if_clause(Clause, Ctx); 1016 | pp_if_clauses([Clause | Clauses = [_ | _]], Ctx) -> 1017 | above(pp_if_clause(Clause, Ctx), pp_if_clauses(Clauses, Ctx)). 1018 | 1019 | pp_if_clause({clause, _, _, Guards, Body}, Ctx) -> 1020 | pp_header_and_body_no_end(Ctx, pp_if_header(Ctx, "", Guards), Body). 1021 | 1022 | pp_if_header(Ctx, KwT, Guards) -> 1023 | besidel([text(KwT), pp_guards(Guards, Ctx), text(" ->")]). 1024 | 1025 | pp_case_clauses(Clause, Ctx) -> 1026 | pp_case_clauses(Clause, Ctx, ""). 1027 | 1028 | pp_case_clauses([Clause], Ctx, EmptyArgsTxt) -> 1029 | pp_case_clause(Clause, Ctx, EmptyArgsTxt); 1030 | pp_case_clauses([Clause | Clauses], Ctx, EmptyArgsTxt) -> 1031 | above(pp_case_clause(Clause, Ctx, EmptyArgsTxt), 1032 | pp_case_clauses(Clauses, Ctx, EmptyArgsTxt)). 1033 | 1034 | pp_case_clause({clause, _, [], [], Body}, Ctx, EmptyArgsTxt) -> 1035 | pp_header_and_body_no_end(Ctx, text(EmptyArgsTxt ++ " ->"), Body); 1036 | pp_case_clause({clause, _, Patterns, [], Body}, Ctx, _EmptyArgsTxt) -> 1037 | pp_header_and_body_no_end(Ctx, 1038 | besidel([pp_args_inn(Patterns, Ctx), 1039 | text(" ->")]), 1040 | Body); 1041 | pp_case_clause({clause, _, [], Guards, Body}, Ctx, EmptyArgsTxt) -> 1042 | pp_header_and_body_no_end(Ctx, 1043 | beside(text(EmptyArgsTxt ++ " when "), 1044 | sep([pp_guards(Guards, Ctx), sarrow_f()])), 1045 | Body); 1046 | pp_case_clause({clause, _, Patterns, Guards, Body}, Ctx, _EmptyArgsTxt) -> 1047 | pp_header_and_body_no_end(Ctx, 1048 | followc(Ctx, 1049 | pp_args_inn(Patterns, Ctx), 1050 | beside(text("when "), 1051 | sep([pp_guards(Guards, Ctx), 1052 | sarrow_f()]))), 1053 | Body). 1054 | 1055 | pp_try_catch_clauses([], _Ctx) -> 1056 | empty(); 1057 | pp_try_catch_clauses(Clauses, Ctx) -> 1058 | above(text("catch"), nestc(Ctx, pp_try_catch_cases(Clauses, Ctx))). 1059 | 1060 | pp_try_after([], _Ctx) -> 1061 | empty(); 1062 | pp_try_after(Body, Ctx) -> 1063 | above(text("after"), nestc(Ctx, pp_body(Body, Ctx))). 1064 | 1065 | pp_try_catch_cases([], _Ctx) -> 1066 | empty(); 1067 | pp_try_catch_cases([H | T], Ctx) -> 1068 | above(pp_try_catch_case(H, Ctx), pp_try_catch_cases(T, Ctx)). 1069 | 1070 | pp_try_catch_case({clause, _, [{tuple, _, TItems}], [], Body}, Ctx) -> 1071 | Ctx1 = maybe_set_catch_stacktrace_var(Ctx, TItems), 1072 | pp_header_and_body_no_end(Ctx1, 1073 | sep([pp_try_catch_case_items(TItems, Ctx), 1074 | sarrow_f()]), 1075 | Body); 1076 | pp_try_catch_case({clause, _, [{tuple, _, TItems}], Guards, Body}, Ctx) -> 1077 | Ctx1 = maybe_set_catch_stacktrace_var(Ctx, TItems), 1078 | pp_header_and_body_no_end(Ctx1, 1079 | followc(Ctx, 1080 | pp_try_catch_case_items(TItems, Ctx), 1081 | beside(text("when "), 1082 | sep([pp_guards(Guards, Ctx), 1083 | sarrow_f()]))), 1084 | Body). 1085 | 1086 | pp_try_catch_case_items([{atom, _, throw}, Var, _], Ctx) -> 1087 | pp(Var, Ctx); 1088 | pp_try_catch_case_items([Type, Var, _], Ctx) -> 1089 | pp_items([Type, Var], Ctx). 1090 | 1091 | maybe_set_catch_stacktrace_var(Ctx, [_, _, {var, _, Name}]) -> 1092 | set_stacktrace_var(Ctx, Name); 1093 | maybe_set_catch_stacktrace_var(Ctx, [_, _, {var, _, Name, _}]) -> 1094 | set_stacktrace_var(Ctx, Name). 1095 | 1096 | pp_for(Gens, Ctx, BodyL) -> 1097 | above(besidel([text("for "), pp_lc_gens(Gens, Ctx), text(" do")]), 1098 | above(nestc(Ctx, BodyL), text("end"))). 1099 | 1100 | pp_bfor(Gens, Ctx, BodyL) -> 1101 | above(besidel([text("for "), 1102 | pp_lc_gens(Gens, Ctx), 1103 | text(", into: <<>> do")]), 1104 | above(nestc(Ctx, BodyL), text("end"))). 1105 | 1106 | pp_lc_gens(Gens = [Filter | _], Ctx) 1107 | when element(1, Filter) =/= generate andalso 1108 | element(1, Filter) =/= b_generate -> 1109 | Line = element(2, Filter), 1110 | % a filter but no generator, add dummy generator 1111 | DummyGen = 1112 | {generate, 1113 | Line, 1114 | {var, Line, '_'}, 1115 | {cons, Line, {atom, Line, 'EFE_DUMMY_GEN'}, {nil, Line}}}, 1116 | pp_lc_gens([DummyGen | Gens], Ctx); 1117 | pp_lc_gens(Items, Ctx) -> 1118 | join(Items, Ctx, fun pp_lc_gen/2, comma_f()). 1119 | 1120 | pp_lc_gen({generate, _, Left, Right}, Ctx) -> 1121 | % TODO: add parens only when statement 1122 | wrap(text(" <- "), pp_oper(Left, Ctx), pp_oper(Right, Ctx)); 1123 | pp_lc_gen({b_generate, _, Left, Right}, Ctx) -> 1124 | besidel([text("<< "), 1125 | wrap(text(" <- "), pp_oper(Left, Ctx), pp_oper(Right, Ctx)), 1126 | text(" >>")]); 1127 | pp_lc_gen(Filter, Ctx) -> 1128 | pp_oper(Filter, Ctx). 1129 | 1130 | pp_guards(Guards, Ctx) -> 1131 | pp_guards_h(Guards, enter_guard(Ctx)). 1132 | 1133 | pp_guards_h([SGuards], Ctx) -> 1134 | pp_guard(SGuards, Ctx); 1135 | pp_guards_h(Guards, Ctx) -> 1136 | join(Guards, Ctx, fun pp_guard/2, text(" or")). 1137 | 1138 | pp_guard([SGuard], Ctx) -> 1139 | pp_sguard(SGuard, Ctx); 1140 | pp_guard(SGuards, Ctx) -> 1141 | wrap_parens(join(SGuards, Ctx, fun pp_sguard/2, text(" and"))). 1142 | 1143 | pp_sguard(SGuard, Ctx) -> 1144 | pp(SGuard, Ctx). 1145 | 1146 | pp_bin_es(Es, Ctx) -> 1147 | join(Es, Ctx, fun pp_bin_e/2, comma_f()). 1148 | 1149 | pp_bin_e({bin_element, _, Left, default, [binary]}, Ctx) -> 1150 | wrap_pair(Ctx, dcolon_f(), pp_bin_e_v(Left, Ctx), text("binary")); 1151 | pp_bin_e({bin_element, _, Left, Size, default}, Ctx) when Size =/= default -> 1152 | wrap_pair(Ctx, dcolon_f(), pp_bin_e_v(Left, Ctx), size_call(Size, Ctx)); 1153 | pp_bin_e({bin_element, _, Left, default, default}, Ctx) -> 1154 | pp_bin_e_v(Left, Ctx); 1155 | pp_bin_e({bin_element, _, Left, Size, Types}, Ctx) -> 1156 | TypeMap = pp_bin_e_types(Types, Size, Ctx), 1157 | wrap_pair(Ctx, dcolon_f(), pp_bin_e_v(Left, Ctx), TypeMap). 1158 | 1159 | size_call(Expr, Ctx) -> 1160 | besidel([text("size("), pp(Expr, Ctx), text(")")]). 1161 | 1162 | pp_bin_e_v({string, _, V}, _Ctx) -> 1163 | quote_string(V); 1164 | pp_bin_e_v(V, Ctx) -> 1165 | pp(V, Ctx). 1166 | 1167 | pp_bin_e_types(Types, default, Ctx) -> 1168 | pp_bin_e_types(Types, Ctx); 1169 | pp_bin_e_types(Types, Size, Ctx) -> 1170 | pp_bin_e_types([{size, Size} | Types], Ctx). 1171 | 1172 | pp_bin_e_types(Types, Ctx) -> 1173 | join(Types, Ctx, fun pp_bin_e_type/2, text(" -")). 1174 | 1175 | pp_attr_pair(_Ctx, _KeyTxt, ValTxt) -> 1176 | text(ValTxt). 1177 | 1178 | pp_bin_e_type({size, default}, _Ctx) -> 1179 | empty(); 1180 | pp_bin_e_type({size, V}, Ctx) -> 1181 | besidel([text("size("), pp(V, Ctx), text(")")]); 1182 | pp_bin_e_type({unit, V}, _Ctx) -> 1183 | besidel([text("unit("), text(integer_to_list(V)), text(")")]); 1184 | pp_bin_e_type(Type = integer, Ctx) -> 1185 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1186 | pp_bin_e_type(Type = float, Ctx) -> 1187 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1188 | pp_bin_e_type(Type = binary, Ctx) -> 1189 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1190 | pp_bin_e_type(Type = bytes, Ctx) -> 1191 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1192 | pp_bin_e_type(Type = bitstring, Ctx) -> 1193 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1194 | pp_bin_e_type(Type = bits, Ctx) -> 1195 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1196 | pp_bin_e_type(Type = utf8, Ctx) -> 1197 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1198 | pp_bin_e_type(Type = utf16, Ctx) -> 1199 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1200 | pp_bin_e_type(Type = utf32, Ctx) -> 1201 | pp_attr_pair(Ctx, "type", atom_to_list(Type)); 1202 | pp_bin_e_type(Type = signed, Ctx) -> 1203 | pp_attr_pair(Ctx, "sign", atom_to_list(Type)); 1204 | pp_bin_e_type(Type = unsigned, Ctx) -> 1205 | pp_attr_pair(Ctx, "sign", atom_to_list(Type)); 1206 | pp_bin_e_type(Type = big, Ctx) -> 1207 | pp_attr_pair(Ctx, "endianness", atom_to_list(Type)); 1208 | pp_bin_e_type(Type = little, Ctx) -> 1209 | pp_attr_pair(Ctx, "endianness", atom_to_list(Type)); 1210 | pp_bin_e_type(Type = native, Ctx) -> 1211 | pp_attr_pair(Ctx, "endianness", atom_to_list(Type)). 1212 | 1213 | call_op(OpenText, Left, Right, Ctx) -> 1214 | besidel([text(OpenText), 1215 | pp(Left, Ctx), 1216 | text(", "), 1217 | pp(Right, Ctx), 1218 | text(")")]). 1219 | 1220 | add_record_declaration(RecName, Fields, Ctx = #ctxt{records = Records}) -> 1221 | FieldInfo = 1222 | [parse_record_field(Field, Pos) || {Pos, Field} <- enumerate(Fields)], 1223 | RecInfo = #{fields => maps:from_list(FieldInfo), field_order => FieldInfo}, 1224 | NewRecords = Records#{RecName => RecInfo}, 1225 | Ctx#ctxt{records = NewRecords}. 1226 | 1227 | enumerate(Items) -> 1228 | enumerate(Items, [], 0). 1229 | 1230 | enumerate([], Accum, _) -> 1231 | lists:reverse(Accum); 1232 | enumerate([H | T], Accum, N) -> 1233 | enumerate(T, [{N, H} | Accum], N + 1). 1234 | 1235 | pp_record_field_decl({typed_record_field, Field, _Type}, Ctx) -> 1236 | pp_record_field_decl(Field, Ctx); 1237 | pp_record_field_decl({record_field, L1, {atom, L2, Name}}, Ctx) -> 1238 | pp_record_field_decl({record_field, 1239 | L1, 1240 | {atom, L2, Name}, 1241 | {atom, L2, undefined}}, 1242 | Ctx); 1243 | pp_record_field_decl({record_field, _, {atom, _, Name}, {record, _, _, _}}, 1244 | _Ctx) -> 1245 | besidel([text(quote_record_field(Name)), 1246 | text(": :EFE_TODO_NESTED_RECORD")]); 1247 | pp_record_field_decl({record_field, _, {atom, _, Name}, Default}, Ctx) -> 1248 | besidel([text(quote_record_field(Name)), text(": "), pp(Default, Ctx)]). 1249 | 1250 | parse_record_field({typed_record_field, Field, _Type}, Pos) -> 1251 | parse_record_field(Field, Pos); 1252 | parse_record_field({record_field, _, {atom, _, Name}}, Pos) -> 1253 | {Name, #{default => {atom, 0, undefined}, position => Pos + 1}}; 1254 | parse_record_field({record_field, _, {atom, _, Name}, Default}, Pos) -> 1255 | {Name, #{default => Default, position => Pos + 1}}. 1256 | 1257 | pp_rec_new(RecName, Fields, Ctx = #ctxt{}) -> 1258 | besidel([p_rec_name(RecName), 1259 | text("("), 1260 | join(Fields, Ctx, fun pp_rec_update_field/2, comma_f()), 1261 | text(")")]). 1262 | 1263 | pp_rec_update(RecName, [], CurRecExpr, Ctx = #ctxt{}) -> 1264 | % zero fields record update O.o 1265 | besidel([p_rec_name(RecName), text("("), pp(CurRecExpr, Ctx), text(")")]); 1266 | pp_rec_update(RecName, Fields, CurRecExpr, Ctx = #ctxt{}) -> 1267 | besidel([p_rec_name(RecName), 1268 | text("("), 1269 | pp(CurRecExpr, Ctx), 1270 | text(", "), 1271 | join(Fields, Ctx, fun pp_rec_update_field/2, text(", ")), 1272 | text(")")]). 1273 | 1274 | pp_rec_field(RecExpr, RecName, {atom, _, Field}, Ctx = #ctxt{}) -> 1275 | besidel([p_rec_name(RecName), 1276 | text("("), 1277 | pp(RecExpr, Ctx), 1278 | text(", :"), 1279 | text(quote_record_field(Field)), 1280 | text(")")]). 1281 | 1282 | pp_rec_index(RecName, {atom, _, Field}, #ctxt{}) -> 1283 | besidel([p_rec_name(RecName), 1284 | text("("), 1285 | text(":" ++ atom_to_list(Field)), 1286 | text(")")]). 1287 | 1288 | pp_rec_update_field({record_field, _, {var, _, '_'}, Value}, Ctx) -> 1289 | besidel([text("_"), text(": "), pp(Value, Ctx)]); 1290 | pp_rec_update_field({record_field, _, {atom, _, FieldName}, Value}, Ctx) -> 1291 | besidel([text(quote_record_field(FieldName)), text(": "), pp(Value, Ctx)]). 1292 | 1293 | % TODO: if bit ops are used Bitwise must be included: https://hexdocs.pm/elixir/Bitwise.html 1294 | map_op_reverse('rem') -> 1295 | 'rem'; 1296 | map_op_reverse('div') -> 1297 | 'div'; 1298 | map_op_reverse('=') -> 1299 | '='; 1300 | map_op_reverse('+') -> 1301 | '+'; 1302 | map_op_reverse('-') -> 1303 | '-'; 1304 | map_op_reverse('*') -> 1305 | '*'; 1306 | map_op_reverse('/') -> 1307 | '/'; 1308 | map_op_reverse('bor') -> 1309 | '|||'; 1310 | map_op_reverse('band') -> 1311 | '&&&'; 1312 | map_op_reverse('bxor') -> 1313 | '^^^'; 1314 | map_op_reverse('bsr') -> 1315 | '>>>'; 1316 | map_op_reverse('bsl') -> 1317 | '<<<'; 1318 | map_op_reverse('bnot') -> 1319 | '~~~'; 1320 | map_op_reverse('andalso') -> 1321 | 'and'; 1322 | map_op_reverse('orelse') -> 1323 | 'or'; 1324 | map_op_reverse('xor') -> 1325 | 'xor'; 1326 | map_op_reverse('!') -> 1327 | '!'; 1328 | map_op_reverse('not') -> 1329 | 'not'; 1330 | map_op_reverse('++') -> 1331 | '++'; 1332 | map_op_reverse('--') -> 1333 | '--'; 1334 | map_op_reverse('<') -> 1335 | '<'; 1336 | map_op_reverse('=<') -> 1337 | '<='; 1338 | map_op_reverse('>') -> 1339 | '>'; 1340 | map_op_reverse('>=') -> 1341 | '>='; 1342 | map_op_reverse('==') -> 1343 | '=='; 1344 | map_op_reverse('=:=') -> 1345 | '==='; 1346 | map_op_reverse('/=') -> 1347 | '!='; 1348 | map_op_reverse('=/=') -> 1349 | '!=='. 1350 | 1351 | is_erlang_op('and') -> 1352 | true; 1353 | is_erlang_op('or') -> 1354 | true; 1355 | is_erlang_op('xor') -> 1356 | true; 1357 | % is_erlang_op('bor') -> true; 1358 | % is_erlang_op('band') -> true; 1359 | % is_erlang_op('bxor') -> true; 1360 | % is_erlang_op('bsr') -> true; 1361 | % is_erlang_op('bsl') -> true; 1362 | % is_erlang_op('bnot') -> true; 1363 | is_erlang_op(_) -> 1364 | false. 1365 | 1366 | is_autoimported(abs, 1) -> 1367 | true; 1368 | is_autoimported(apply, 2) -> 1369 | true; 1370 | is_autoimported(apply, 3) -> 1371 | true; 1372 | is_autoimported(atom_to_binary, 1) -> 1373 | true; 1374 | is_autoimported(atom_to_binary, 2) -> 1375 | true; 1376 | is_autoimported(atom_to_list, 1) -> 1377 | true; 1378 | is_autoimported(binary_part, 2) -> 1379 | true; 1380 | is_autoimported(binary_part, 3) -> 1381 | true; 1382 | is_autoimported(binary_to_atom, 2) -> 1383 | true; 1384 | is_autoimported(binary_to_existing_atom, 2) -> 1385 | true; 1386 | is_autoimported(binary_to_float, 1) -> 1387 | true; 1388 | is_autoimported(binary_to_integer, 1) -> 1389 | true; 1390 | is_autoimported(binary_to_integer, 2) -> 1391 | true; 1392 | is_autoimported(binary_to_list, 1) -> 1393 | true; 1394 | is_autoimported(binary_to_list, 3) -> 1395 | true; 1396 | is_autoimported(binary_to_term, 1) -> 1397 | true; 1398 | is_autoimported(binary_to_term, 2) -> 1399 | true; 1400 | is_autoimported(bit_size, 1) -> 1401 | true; 1402 | is_autoimported(bitstring_to_list, 1) -> 1403 | true; 1404 | is_autoimported(byte_size, 1) -> 1405 | true; 1406 | is_autoimported(ceil, 1) -> 1407 | true; 1408 | is_autoimported(check_old_code, 1) -> 1409 | true; 1410 | is_autoimported(check_process_code, 2) -> 1411 | true; 1412 | is_autoimported(check_process_code, 3) -> 1413 | true; 1414 | is_autoimported(date, 0) -> 1415 | true; 1416 | is_autoimported(delete_module, 1) -> 1417 | true; 1418 | is_autoimported(demonitor, 1) -> 1419 | true; 1420 | is_autoimported(demonitor, 2) -> 1421 | true; 1422 | is_autoimported(disconnect_node, 1) -> 1423 | true; 1424 | is_autoimported(element, 2) -> 1425 | true; 1426 | is_autoimported(erase, 0) -> 1427 | true; 1428 | is_autoimported(erase, 1) -> 1429 | true; 1430 | is_autoimported(error, 1) -> 1431 | true; 1432 | is_autoimported(error, 2) -> 1433 | true; 1434 | is_autoimported(exit, 1) -> 1435 | true; 1436 | is_autoimported(exit, 2) -> 1437 | true; 1438 | is_autoimported(float, 1) -> 1439 | true; 1440 | is_autoimported(float_to_binary, 1) -> 1441 | true; 1442 | is_autoimported(float_to_binary, 2) -> 1443 | true; 1444 | is_autoimported(float_to_list, 1) -> 1445 | true; 1446 | is_autoimported(float_to_list, 2) -> 1447 | true; 1448 | is_autoimported(floor, 1) -> 1449 | true; 1450 | is_autoimported(garbage_collect, 0) -> 1451 | true; 1452 | is_autoimported(garbage_collect, 1) -> 1453 | true; 1454 | is_autoimported(garbage_collect, 2) -> 1455 | true; 1456 | is_autoimported(get, 0) -> 1457 | true; 1458 | is_autoimported(get, 1) -> 1459 | true; 1460 | is_autoimported(get_keys, 0) -> 1461 | true; 1462 | is_autoimported(get_keys, 1) -> 1463 | true; 1464 | is_autoimported(group_leader, 0) -> 1465 | true; 1466 | is_autoimported(group_leader, 2) -> 1467 | true; 1468 | is_autoimported(halt, 0) -> 1469 | true; 1470 | is_autoimported(halt, 1) -> 1471 | true; 1472 | is_autoimported(halt, 2) -> 1473 | true; 1474 | is_autoimported(hd, 1) -> 1475 | true; 1476 | is_autoimported(integer_to_binary, 1) -> 1477 | true; 1478 | is_autoimported(integer_to_binary, 2) -> 1479 | true; 1480 | is_autoimported(integer_to_list, 1) -> 1481 | true; 1482 | is_autoimported(integer_to_list, 2) -> 1483 | true; 1484 | is_autoimported(iolist_size, 1) -> 1485 | true; 1486 | is_autoimported(iolist_to_binary, 1) -> 1487 | true; 1488 | is_autoimported(is_alive, 0) -> 1489 | true; 1490 | is_autoimported(is_atom, 1) -> 1491 | true; 1492 | is_autoimported(is_binary, 1) -> 1493 | true; 1494 | is_autoimported(is_bitstring, 1) -> 1495 | true; 1496 | is_autoimported(is_boolean, 1) -> 1497 | true; 1498 | is_autoimported(is_float, 1) -> 1499 | true; 1500 | is_autoimported(is_function, 1) -> 1501 | true; 1502 | is_autoimported(is_function, 2) -> 1503 | true; 1504 | is_autoimported(is_integer, 1) -> 1505 | true; 1506 | is_autoimported(is_list, 1) -> 1507 | true; 1508 | is_autoimported(is_map, 1) -> 1509 | true; 1510 | is_autoimported(is_map_key, 2) -> 1511 | true; 1512 | is_autoimported(is_number, 1) -> 1513 | true; 1514 | is_autoimported(is_pid, 1) -> 1515 | true; 1516 | is_autoimported(is_port, 1) -> 1517 | true; 1518 | is_autoimported(is_process_alive, 1) -> 1519 | true; 1520 | is_autoimported(is_record, 2) -> 1521 | true; 1522 | is_autoimported(is_record, 3) -> 1523 | true; 1524 | is_autoimported(is_reference, 1) -> 1525 | true; 1526 | is_autoimported(is_tuple, 1) -> 1527 | true; 1528 | is_autoimported(length, 1) -> 1529 | true; 1530 | is_autoimported(link, 1) -> 1531 | true; 1532 | is_autoimported(list_to_atom, 1) -> 1533 | true; 1534 | is_autoimported(list_to_binary, 1) -> 1535 | true; 1536 | is_autoimported(list_to_bitstring, 1) -> 1537 | true; 1538 | is_autoimported(list_to_existing_atom, 1) -> 1539 | true; 1540 | is_autoimported(list_to_float, 1) -> 1541 | true; 1542 | is_autoimported(list_to_integer, 1) -> 1543 | true; 1544 | is_autoimported(list_to_integer, 2) -> 1545 | true; 1546 | is_autoimported(list_to_pid, 1) -> 1547 | true; 1548 | is_autoimported(list_to_port, 1) -> 1549 | true; 1550 | is_autoimported(list_to_ref, 1) -> 1551 | true; 1552 | is_autoimported(list_to_tuple, 1) -> 1553 | true; 1554 | is_autoimported(load_module, 2) -> 1555 | true; 1556 | is_autoimported(make_ref, 0) -> 1557 | true; 1558 | is_autoimported(map_get, 2) -> 1559 | true; 1560 | is_autoimported(map_size, 1) -> 1561 | true; 1562 | is_autoimported(max, 2) -> 1563 | true; 1564 | is_autoimported(min, 2) -> 1565 | true; 1566 | is_autoimported(module_loaded, 1) -> 1567 | true; 1568 | is_autoimported(monitor, 2) -> 1569 | true; 1570 | is_autoimported(monitor_node, 2) -> 1571 | true; 1572 | is_autoimported(node, 0) -> 1573 | true; 1574 | is_autoimported(node, 1) -> 1575 | true; 1576 | is_autoimported(nodes, 0) -> 1577 | true; 1578 | is_autoimported(nodes, 1) -> 1579 | true; 1580 | is_autoimported(now, 0) -> 1581 | true; 1582 | is_autoimported(open_port, 2) -> 1583 | true; 1584 | is_autoimported(pid_to_list, 1) -> 1585 | true; 1586 | is_autoimported(port_close, 1) -> 1587 | true; 1588 | is_autoimported(port_command, 2) -> 1589 | true; 1590 | is_autoimported(port_command, 3) -> 1591 | true; 1592 | is_autoimported(port_connect, 2) -> 1593 | true; 1594 | is_autoimported(port_control, 3) -> 1595 | true; 1596 | is_autoimported(port_to_list, 1) -> 1597 | true; 1598 | is_autoimported(pre_loaded, 0) -> 1599 | true; 1600 | is_autoimported(process_flag, 2) -> 1601 | true; 1602 | is_autoimported(process_flag, 3) -> 1603 | true; 1604 | is_autoimported(process_info, 1) -> 1605 | true; 1606 | is_autoimported(process_info, 2) -> 1607 | true; 1608 | is_autoimported(processes, 0) -> 1609 | true; 1610 | is_autoimported(purge_module, 1) -> 1611 | true; 1612 | is_autoimported(put, 2) -> 1613 | true; 1614 | is_autoimported(ref_to_list, 1) -> 1615 | true; 1616 | is_autoimported(register, 2) -> 1617 | true; 1618 | is_autoimported(registered, 0) -> 1619 | true; 1620 | is_autoimported(round, 1) -> 1621 | true; 1622 | is_autoimported(self, 0) -> 1623 | true; 1624 | is_autoimported(setelement, 3) -> 1625 | true; 1626 | is_autoimported(statistics, 1) -> 1627 | true; 1628 | is_autoimported(size, 1) -> 1629 | true; 1630 | is_autoimported(spawn, 1) -> 1631 | true; 1632 | is_autoimported(spawn, 2) -> 1633 | true; 1634 | is_autoimported(spawn, 3) -> 1635 | true; 1636 | is_autoimported(spawn, 4) -> 1637 | true; 1638 | is_autoimported(spawn_link, 1) -> 1639 | true; 1640 | is_autoimported(spawn_link, 2) -> 1641 | true; 1642 | is_autoimported(spawn_link, 3) -> 1643 | true; 1644 | is_autoimported(spawn_link, 4) -> 1645 | true; 1646 | is_autoimported(spawn_monitor, 1) -> 1647 | true; 1648 | is_autoimported(spawn_monitor, 3) -> 1649 | true; 1650 | is_autoimported(spawn_opt, 2) -> 1651 | true; 1652 | is_autoimported(spawn_opt, 3) -> 1653 | true; 1654 | is_autoimported(spawn_opt, 4) -> 1655 | true; 1656 | is_autoimported(spawn_opt, 5) -> 1657 | true; 1658 | is_autoimported(spawn_request, 1) -> 1659 | true; 1660 | is_autoimported(spawn_request, 2) -> 1661 | true; 1662 | is_autoimported(spawn_request, 3) -> 1663 | true; 1664 | is_autoimported(spawn_request, 4) -> 1665 | true; 1666 | is_autoimported(spawn_request, 5) -> 1667 | true; 1668 | is_autoimported(spawn_request_abandon, 1) -> 1669 | true; 1670 | is_autoimported(split_binary, 2) -> 1671 | true; 1672 | is_autoimported(term_to_binary, 1) -> 1673 | true; 1674 | is_autoimported(term_to_binary, 2) -> 1675 | true; 1676 | is_autoimported(throw, 1) -> 1677 | true; 1678 | is_autoimported(time, 0) -> 1679 | true; 1680 | is_autoimported(tl, 1) -> 1681 | true; 1682 | is_autoimported(trunc, 1) -> 1683 | true; 1684 | is_autoimported(tuple_size, 1) -> 1685 | true; 1686 | is_autoimported(tuple_to_list, 1) -> 1687 | true; 1688 | is_autoimported(unlink, 1) -> 1689 | true; 1690 | is_autoimported(unregister, 1) -> 1691 | true; 1692 | is_autoimported(whereis, 1) -> 1693 | true; 1694 | is_autoimported(_, _) -> 1695 | false. 1696 | 1697 | is_ex_autoimport(abs, 1) -> 1698 | true; 1699 | is_ex_autoimport(apply, 2) -> 1700 | true; 1701 | is_ex_autoimport(apply, 3) -> 1702 | true; 1703 | is_ex_autoimport(binary_part, 3) -> 1704 | true; 1705 | is_ex_autoimport(bit_size, 1) -> 1706 | true; 1707 | is_ex_autoimport(byte_size, 1) -> 1708 | true; 1709 | is_ex_autoimport(ceil, 1) -> 1710 | true; 1711 | is_ex_autoimport(exit, 1) -> 1712 | true; 1713 | is_ex_autoimport(floor, 1) -> 1714 | true; 1715 | is_ex_autoimport(hd, 1) -> 1716 | true; 1717 | is_ex_autoimport(is_atom, 1) -> 1718 | true; 1719 | is_ex_autoimport(is_binary, 1) -> 1720 | true; 1721 | is_ex_autoimport(is_bitstring, 1) -> 1722 | true; 1723 | is_ex_autoimport(is_boolean, 1) -> 1724 | true; 1725 | is_ex_autoimport(is_float, 1) -> 1726 | true; 1727 | is_ex_autoimport(is_function, 1) -> 1728 | true; 1729 | is_ex_autoimport(is_function, 2) -> 1730 | true; 1731 | is_ex_autoimport(is_integer, 1) -> 1732 | true; 1733 | is_ex_autoimport(is_list, 1) -> 1734 | true; 1735 | is_ex_autoimport(is_map, 1) -> 1736 | true; 1737 | is_ex_autoimport(is_number, 1) -> 1738 | true; 1739 | is_ex_autoimport(is_pid, 1) -> 1740 | true; 1741 | is_ex_autoimport(is_port, 1) -> 1742 | true; 1743 | is_ex_autoimport(is_reference, 1) -> 1744 | true; 1745 | is_ex_autoimport(is_tuple, 1) -> 1746 | true; 1747 | is_ex_autoimport(length, 1) -> 1748 | true; 1749 | is_ex_autoimport(make_ref, 0) -> 1750 | true; 1751 | is_ex_autoimport(map_size, 1) -> 1752 | true; 1753 | is_ex_autoimport(max, 2) -> 1754 | true; 1755 | is_ex_autoimport(min, 2) -> 1756 | true; 1757 | is_ex_autoimport(node, 0) -> 1758 | true; 1759 | is_ex_autoimport(node, 1) -> 1760 | true; 1761 | is_ex_autoimport(round, 1) -> 1762 | true; 1763 | is_ex_autoimport(self, 0) -> 1764 | true; 1765 | is_ex_autoimport(spawn, 1) -> 1766 | true; 1767 | is_ex_autoimport(spawn, 3) -> 1768 | true; 1769 | is_ex_autoimport(spawn_link, 1) -> 1770 | true; 1771 | is_ex_autoimport(spawn_link, 3) -> 1772 | true; 1773 | is_ex_autoimport(spawn_monitor, 1) -> 1774 | true; 1775 | is_ex_autoimport(spawn_monitor, 3) -> 1776 | true; 1777 | is_ex_autoimport(throw, 1) -> 1778 | true; 1779 | is_ex_autoimport(tl, 1) -> 1780 | true; 1781 | is_ex_autoimport(trunc, 1) -> 1782 | true; 1783 | is_ex_autoimport(tuple_size, 1) -> 1784 | true; 1785 | is_ex_autoimport(_, _) -> 1786 | false. 1787 | 1788 | should_prefix_erlang_call({atom, _, FName}, Arity) -> 1789 | should_prefix_erlang_call(FName, Arity); 1790 | should_prefix_erlang_call({_, _, _}, _Arity) -> 1791 | false; 1792 | should_prefix_erlang_call({var, _, _, _}, _Arity) -> 1793 | false; 1794 | should_prefix_erlang_call(FName, Arity) when is_atom(FName), is_number(Arity) -> 1795 | is_autoimported(FName, Arity) andalso not is_ex_autoimport(FName, Arity); 1796 | % stuff like {call,826,{atom,826,predef_fun},[]} 1797 | should_prefix_erlang_call({call, _, _, _}, _Arity) -> 1798 | false; 1799 | should_prefix_erlang_call({record_field, _, _RecExpr, _RecName, _Field}, 1800 | _Arity) -> 1801 | false; 1802 | should_prefix_erlang_call({named_fun, _, _AName, _Clauses}, _Arity) -> 1803 | false. 1804 | 1805 | should_prefix_call({atom, _, alias}, _, _Ctx) -> 1806 | {true, {var, '__MODULE__'}}; 1807 | should_prefix_call({atom, _, def}, _, _Ctx) -> 1808 | {true, {var, '__MODULE__'}}; 1809 | should_prefix_call({atom, _, in}, _, _Ctx) -> 1810 | {true, {var, '__MODULE__'}}; 1811 | should_prefix_call({atom, _, nil}, _, _Ctx) -> 1812 | {true, {var, '__MODULE__'}}; 1813 | should_prefix_call({atom, _, module_info}, 0, _Ctx) -> 1814 | {true, {var, '__MODULE__'}}; 1815 | should_prefix_call({atom, _, module_info}, 1, _Ctx) -> 1816 | {true, {var, '__MODULE__'}}; 1817 | should_prefix_call(Ast, Arity, _Ctx) -> 1818 | case should_prefix_erlang_call(Ast, Arity) of 1819 | true -> 1820 | {true, {atom, erlang}}; 1821 | false -> 1822 | false 1823 | end. 1824 | 1825 | is_ex_reserved_varname("nil") -> 1826 | true; 1827 | is_ex_reserved_varname("true") -> 1828 | true; 1829 | is_ex_reserved_varname("false") -> 1830 | true; 1831 | is_ex_reserved_varname("def") -> 1832 | true; 1833 | is_ex_reserved_varname("if") -> 1834 | true; 1835 | is_ex_reserved_varname("unless") -> 1836 | true; 1837 | is_ex_reserved_varname("cond") -> 1838 | true; 1839 | is_ex_reserved_varname("case") -> 1840 | true; 1841 | is_ex_reserved_varname("when") -> 1842 | true; 1843 | is_ex_reserved_varname("and") -> 1844 | true; 1845 | is_ex_reserved_varname("or") -> 1846 | true; 1847 | is_ex_reserved_varname("not") -> 1848 | true; 1849 | is_ex_reserved_varname("in") -> 1850 | true; 1851 | is_ex_reserved_varname("fn") -> 1852 | true; 1853 | is_ex_reserved_varname("do") -> 1854 | true; 1855 | is_ex_reserved_varname("end") -> 1856 | true; 1857 | is_ex_reserved_varname("catch") -> 1858 | true; 1859 | is_ex_reserved_varname("rescue") -> 1860 | true; 1861 | is_ex_reserved_varname("after") -> 1862 | true; 1863 | is_ex_reserved_varname("else") -> 1864 | true; 1865 | is_ex_reserved_varname(_) -> 1866 | false. 1867 | 1868 | a2l(V) -> 1869 | atom_to_list(V). 1870 | 1871 | transform_var_name(V) -> 1872 | transform_var_name_lstr(a2l(V)). 1873 | 1874 | transform_var_name_lstr(V) -> 1875 | case V of 1876 | L = [$_ | _] -> 1877 | L; 1878 | % LFE 1879 | [$- | T] -> 1880 | transform_lfe_var_name_tail(T, []); 1881 | [H] -> 1882 | string:lowercase([H]); 1883 | [H | T] -> 1884 | VarName = string:lowercase([H]) ++ T, 1885 | case is_ex_reserved_varname(VarName) of 1886 | true -> 1887 | VarName ++ "__"; 1888 | false -> 1889 | VarName 1890 | end 1891 | end. 1892 | 1893 | % vars seem to all end in - 1894 | transform_lfe_var_name_tail([$-], Accum) -> 1895 | transform_var_name_lstr(lists:reverse(Accum)); 1896 | % in case some don't end in - 1897 | transform_lfe_var_name_tail([], Accum) -> 1898 | transform_var_name_lstr(lists:reverse(Accum)); 1899 | transform_lfe_var_name_tail([$- | T = [_ | _]], Accum) -> 1900 | transform_lfe_var_name_tail(T, [$_ | Accum]); 1901 | transform_lfe_var_name_tail([$| | T = [_ | _]], Accum) -> 1902 | transform_lfe_var_name_tail(T, [$_ | Accum]); 1903 | transform_lfe_var_name_tail([H | T], Accum) -> 1904 | transform_lfe_var_name_tail(T, [H | Accum]). 1905 | 1906 | op_to_erlang_call(Line, Op, Args, Ctx) -> 1907 | pp({call, 1908 | Line, 1909 | {remote, Line, {atom, Line, erlang}, {atom, Line, Op}}, 1910 | Args}, 1911 | Ctx). 1912 | 1913 | p_rec_name(RecName) -> 1914 | Name = a2l(RecName), 1915 | Name1 = string:replace(["r_" | Name], "-", "_", all), 1916 | Name2 = string:replace(Name1, "$", "_", all), 1917 | text(Name2). 1918 | 1919 | format_non_printable_char(0) -> 1920 | "?\\0"; 1921 | format_non_printable_char(7) -> 1922 | "?\\a"; 1923 | format_non_printable_char($\b) -> 1924 | "?\\b"; 1925 | format_non_printable_char($\d) -> 1926 | "?\\d"; 1927 | format_non_printable_char($\e) -> 1928 | "?\\e"; 1929 | format_non_printable_char($\f) -> 1930 | "?\\f"; 1931 | format_non_printable_char($\n) -> 1932 | "?\\n"; 1933 | format_non_printable_char($\r) -> 1934 | "?\\r"; 1935 | format_non_printable_char($\s) -> 1936 | "?\\s"; 1937 | format_non_printable_char($\t) -> 1938 | "?\\t"; 1939 | format_non_printable_char($\v) -> 1940 | "?\\v"; 1941 | format_non_printable_char(V) -> 1942 | integer_to_list(V). 1943 | 1944 | is_kernel_fn('!', 1) -> 1945 | true; 1946 | is_kernel_fn('&&', 2) -> 1947 | true; 1948 | is_kernel_fn('++', 2) -> 1949 | true; 1950 | is_kernel_fn('--', 2) -> 1951 | true; 1952 | is_kernel_fn('..', 2) -> 1953 | true; 1954 | is_kernel_fn('<>', 2) -> 1955 | true; 1956 | is_kernel_fn('=~', 2) -> 1957 | true; 1958 | is_kernel_fn('@', 1) -> 1959 | true; 1960 | is_kernel_fn('|>', 2) -> 1961 | true; 1962 | is_kernel_fn('||', 2) -> 1963 | true; 1964 | is_kernel_fn('alias!', 1) -> 1965 | true; 1966 | is_kernel_fn(apply, 2) -> 1967 | true; 1968 | is_kernel_fn(apply, 3) -> 1969 | true; 1970 | is_kernel_fn(binding, 1) -> 1971 | true; 1972 | % can't not import this one :) 1973 | %is_kernel_fn(def, 2) -> 1974 | % true; 1975 | is_kernel_fn(defdelegate, 2) -> 1976 | true; 1977 | is_kernel_fn(defexception, 1) -> 1978 | true; 1979 | is_kernel_fn(defguard, 1) -> 1980 | true; 1981 | is_kernel_fn(defguardp, 1) -> 1982 | true; 1983 | is_kernel_fn(defimpl, 3) -> 1984 | true; 1985 | is_kernel_fn(defmacro, 2) -> 1986 | true; 1987 | is_kernel_fn(defmacrop, 2) -> 1988 | true; 1989 | is_kernel_fn(defmodule, 2) -> 1990 | true; 1991 | is_kernel_fn(defoverridable, 1) -> 1992 | true; 1993 | is_kernel_fn(defp, 2) -> 1994 | true; 1995 | is_kernel_fn(defprotocol, 2) -> 1996 | true; 1997 | is_kernel_fn(defstruct, 1) -> 1998 | true; 1999 | is_kernel_fn(destructure, 2) -> 2000 | true; 2001 | is_kernel_fn(exit, 1) -> 2002 | true; 2003 | is_kernel_fn('function_exported?', 3) -> 2004 | true; 2005 | is_kernel_fn(get_and_update_in, 2) -> 2006 | true; 2007 | is_kernel_fn(get_and_update_in, 3) -> 2008 | true; 2009 | is_kernel_fn(get_in, 2) -> 2010 | true; 2011 | is_kernel_fn('if', 2) -> 2012 | true; 2013 | is_kernel_fn(inspect, 2) -> 2014 | true; 2015 | is_kernel_fn(length, 1) -> 2016 | true; 2017 | is_kernel_fn('macro_exported?', 3) -> 2018 | true; 2019 | is_kernel_fn(make_ref, 0) -> 2020 | true; 2021 | is_kernel_fn('match?', 2) -> 2022 | true; 2023 | is_kernel_fn(max, 2) -> 2024 | true; 2025 | is_kernel_fn(min, 2) -> 2026 | true; 2027 | is_kernel_fn(pop_in, 1) -> 2028 | true; 2029 | is_kernel_fn(pop_in, 2) -> 2030 | true; 2031 | is_kernel_fn(put_elem, 3) -> 2032 | true; 2033 | is_kernel_fn(put_in, 2) -> 2034 | true; 2035 | is_kernel_fn(put_in, 3) -> 2036 | true; 2037 | is_kernel_fn(raise, 1) -> 2038 | true; 2039 | is_kernel_fn(raise, 2) -> 2040 | true; 2041 | is_kernel_fn(reraise, 2) -> 2042 | true; 2043 | is_kernel_fn(reraise, 3) -> 2044 | true; 2045 | is_kernel_fn(send, 2) -> 2046 | true; 2047 | is_kernel_fn(sigil_C, 2) -> 2048 | true; 2049 | is_kernel_fn(sigil_D, 2) -> 2050 | true; 2051 | is_kernel_fn(sigil_N, 2) -> 2052 | true; 2053 | is_kernel_fn(sigil_R, 2) -> 2054 | true; 2055 | is_kernel_fn(sigil_S, 2) -> 2056 | true; 2057 | is_kernel_fn(sigil_T, 2) -> 2058 | true; 2059 | is_kernel_fn(sigil_U, 2) -> 2060 | true; 2061 | is_kernel_fn(sigil_W, 2) -> 2062 | true; 2063 | is_kernel_fn(sigil_c, 2) -> 2064 | true; 2065 | is_kernel_fn(sigil_r, 2) -> 2066 | true; 2067 | is_kernel_fn(sigil_s, 2) -> 2068 | true; 2069 | is_kernel_fn(sigil_w, 2) -> 2070 | true; 2071 | is_kernel_fn(spawn, 1) -> 2072 | true; 2073 | is_kernel_fn(spawn, 3) -> 2074 | true; 2075 | is_kernel_fn(spawn_link, 1) -> 2076 | true; 2077 | is_kernel_fn(spawn_link, 3) -> 2078 | true; 2079 | is_kernel_fn(spawn_monitor, 1) -> 2080 | true; 2081 | is_kernel_fn(spawn_monitor, 3) -> 2082 | true; 2083 | is_kernel_fn(struct, 2) -> 2084 | true; 2085 | is_kernel_fn('struct!', 2) -> 2086 | true; 2087 | is_kernel_fn(throw, 1) -> 2088 | true; 2089 | is_kernel_fn(to_charlist, 1) -> 2090 | true; 2091 | is_kernel_fn(to_string, 1) -> 2092 | true; 2093 | is_kernel_fn(unless, 2) -> 2094 | true; 2095 | is_kernel_fn(update_in, 2) -> 2096 | true; 2097 | is_kernel_fn(update_in, 3) -> 2098 | true; 2099 | is_kernel_fn(use, 2) -> 2100 | true; 2101 | is_kernel_fn('var!', 2) -> 2102 | true; 2103 | is_kernel_fn(_, _) -> 2104 | false. 2105 | 2106 | deduplicate_list(L) -> 2107 | deduplicate_list(L, [], #{}). 2108 | 2109 | deduplicate_list([], Accum, _Seen) -> 2110 | lists:reverse(Accum); 2111 | deduplicate_list([H | T], Accum, Seen) -> 2112 | NewSeen = Seen#{H => true}, 2113 | NewAccum = 2114 | case maps:get(H, Seen, false) of 2115 | false -> 2116 | [H | Accum]; 2117 | true -> 2118 | Accum 2119 | end, 2120 | deduplicate_list(T, NewAccum, NewSeen). 2121 | 2122 | ueval(Ast = {op, Line, Op, E}) -> 2123 | case ueval(E) of 2124 | {integer, _, V} -> 2125 | Res = 2126 | case Op of 2127 | '+' -> 2128 | V; 2129 | '-' -> 2130 | -V; 2131 | 'bnot' -> 2132 | bnot V; 2133 | _ -> 2134 | error 2135 | end, 2136 | case Res of 2137 | error -> 2138 | Ast; 2139 | _ -> 2140 | {integer, Line, Res} 2141 | end; 2142 | _ -> 2143 | Ast 2144 | end; 2145 | ueval({tuple, Line, Items}) -> 2146 | {tuple, Line, [ueval(Item) || Item <- Items]}; 2147 | ueval(Ast = {op, Line, Op, EL, ER}) -> 2148 | case {ueval(EL), ueval(ER)} of 2149 | {{integer, _, L}, {integer, _, R}} -> 2150 | Res = 2151 | case Op of 2152 | '+' -> 2153 | L + R; 2154 | '-' -> 2155 | L - R; 2156 | '*' -> 2157 | L * R; 2158 | 'div' -> 2159 | L div R; 2160 | 'rem' -> 2161 | L rem R; 2162 | 'band' -> 2163 | L band R; 2164 | 'bor' -> 2165 | L bor R; 2166 | 'bxor' -> 2167 | L bxor R; 2168 | 'bsl' -> 2169 | L bsl R; 2170 | 'bsr' -> 2171 | L bsr R; 2172 | _ -> 2173 | error 2174 | end, 2175 | case Res of 2176 | error -> 2177 | Ast; 2178 | _ -> 2179 | {integer, Line, Res} 2180 | end; 2181 | _ -> 2182 | Ast 2183 | end; 2184 | ueval(Ast) -> 2185 | Ast. 2186 | 2187 | pp_imports(Is, Ctx) -> 2188 | ByMod = 2189 | lists:foldl(fun ({{FName, Arity}, #{mod := Mod}}, ByModIn) -> 2190 | ModImports = maps:get(Mod, ByModIn, []), 2191 | maps:put(Mod, 2192 | [{FName, Arity} | ModImports], 2193 | ByModIn) 2194 | end, 2195 | #{}, 2196 | maps:to_list(Is)), 2197 | 2198 | abovel([pp_import(Mod, lists:sort(ModImports), Ctx) 2199 | || {Mod, ModImports} <- lists:sort(maps:to_list(ByMod))]). 2200 | 2201 | pp_import(Mod, Imports, Ctx) -> 2202 | beside(text("import "), 2203 | followc(Ctx, 2204 | besidel([text(":" ++ a2l(Mod)), comma_f(), text(" only:")]), 2205 | % deduplicate is not needed because of how we accumulate 2206 | wrap_list(join(deduplicate_list(Imports), 2207 | Ctx, 2208 | fun pp_fn_import_ref/2, 2209 | comma_f())))). 2210 | -------------------------------------------------------------------------------- /src/efe_var_ann.erl: -------------------------------------------------------------------------------- 1 | -module(efe_var_ann). 2 | 3 | -export([do/1]). 4 | -export([map/2]). 5 | 6 | new_state() -> 7 | #{vars => #{}, 8 | vars_in_match => #{}, 9 | matching => false, 10 | never_match => false}. 11 | 12 | set_matching(S = #{never_match := true}) -> 13 | S; 14 | set_matching(S) -> 15 | set_matching(S, true). 16 | 17 | set_matching(S, Matching) -> 18 | S#{matching := Matching}. 19 | 20 | clear_matching(S = #{vars_in_match := VarsInMatch, vars := Vars}) -> 21 | % VarsInMatch first to override vars that weren't new (shouldn't happen) 22 | NewVars = maps:merge(VarsInMatch, Vars), 23 | S#{matching := false, vars_in_match := #{}, vars := NewVars}. 24 | 25 | set_never_match(S) -> 26 | S#{never_match := true}. 27 | 28 | clear_never_match(S) -> 29 | S#{never_match := false}. 30 | 31 | % http://icai.ektf.hu/pdf/ICAI2007-vol2-pp137-145.pdf 32 | 33 | add_var_if_not_there(S, _Line, '_') -> 34 | S; 35 | add_var_if_not_there(S = 36 | #{matching := true, 37 | vars_in_match := VarsInMatch, 38 | vars := Vars}, 39 | Line, 40 | Name) -> 41 | % while matching add new vars in different map, when clear_matching is called 42 | % we merge them in vars 43 | case maps:get(Name, Vars, nil) of 44 | nil -> 45 | NewVars = VarsInMatch#{Name => #{line => Line}}, 46 | S#{vars_in_match := NewVars}; 47 | _ -> 48 | S 49 | end; 50 | add_var_if_not_there(S = #{vars := Vars}, Line, Name) -> 51 | case maps:get(Name, Vars, nil) of 52 | nil -> 53 | NewVars = Vars#{Name => #{line => Line}}, 54 | S#{vars := NewVars}; 55 | _ -> 56 | S 57 | end. 58 | 59 | get_var(#{vars := Vars}, Name) -> 60 | maps:get(Name, Vars, nil). 61 | 62 | do(Ast) -> 63 | ast:map(Ast, new_state(), fun map/2). 64 | 65 | map({match, Line, Left, Right}, St) -> 66 | % evaluate right before left, vars on right should already exist, 67 | % it should not see vars that are new on left side 68 | % see scopes:match_left_var_not_on_right_side/0 69 | {Right1, St1} = expr(Right, St), 70 | {Left1, St2} = expr(Left, set_matching(St1)), 71 | R = {match, Line, Left1, Right1}, 72 | {ok, R, clear_matching(St2)}; 73 | % if doesn't match on head? 74 | map({'if', Line, Cs0}, St) -> 75 | {Cs1, St1} = icr_clauses(Cs0, St), 76 | {ok, {'if', Line, Cs1}, St1}; 77 | map({'case', Line, E0, Cs0}, St) -> 78 | {E1, St1} = expr(E0, St), 79 | {Cs1, St2} = icr_clauses(Cs0, St1), 80 | {ok, {'case', Line, E1, Cs1}, St2}; 81 | map({'receive', Line, Cs0}, St) -> 82 | {Cs1, St1} = icr_clauses(Cs0, St), 83 | {ok, {'receive', Line, Cs1}, St1}; 84 | map({'receive', Line, Cs0, To0, ToEs0}, St) -> 85 | {To1, _St1} = expr(To0, St), 86 | {ToEs1, _St2} = exprs(ToEs0, St), 87 | {Cs1, _St3} = icr_clauses(Cs0, St), 88 | {ok, {'receive', Line, Cs1, To1, ToEs1}, St}; 89 | map({'try', Line, Es0, Scs0, Ccs0, As0}, St) -> 90 | {Es1, St1} = exprs(Es0, St), 91 | {Scs1, St2} = icr_clauses(Scs0, St1), 92 | {Ccs1, St3} = icr_clauses(Ccs0, St2), 93 | {As1, St4} = exprs(As0, St3), 94 | {ok, {'try', Line, Es1, Scs1, Ccs1, As1}, St4}; 95 | % new scopes 96 | map({attribute, Line, spec, {{N, A}, FTs}}, St) -> 97 | % ignore vars introduced in specs 98 | Fn = fun map/2, 99 | {FTs1, _St1} = ast:function_type_list(FTs, St, Fn), 100 | {ok, {attribute, Line, spec, {{N, A}, FTs1}}, St}; 101 | map({attribute, Line, spec, {{M, N, A}, FTs}}, St) -> 102 | % ignore vars introduced in specs 103 | Fn = fun map/2, 104 | {FTs1, _St1} = ast:function_type_list(FTs, St, Fn), 105 | {ok, {attribute, Line, spec, {{M, N, A}, FTs1}}, St}; 106 | map({attribute, Line, type, {N, T, Vs}}, St) -> 107 | % ignore vars introduced in types 108 | Fn = fun map/2, 109 | {T1, St1} = ast:type(T, St, Fn), 110 | {Vs1, _St2} = ast:variable_list(Vs, St1, Fn), 111 | {ok, {attribute, Line, type, {N, T1, Vs1}}, St}; 112 | map({attribute, Line, opaque, {N, T, Vs}}, St) -> 113 | % ignore vars introduced in opaque 114 | Fn = fun map/2, 115 | {T1, St1} = ast:type(T, St, Fn), 116 | {Vs1, _St2} = ast:variable_list(Vs, St1, Fn), 117 | {ok, {attribute, Line, opaque, {N, T1, Vs1}}, St}; 118 | map({attribute, Line, callback, {{N, A}, FTs}}, St) -> 119 | % ignore vars introduced in callback 120 | Fn = fun map/2, 121 | {FTs1, _St1} = ast:function_type_list(FTs, St, Fn), 122 | {ok, {attribute, Line, callback, {{N, A}, FTs1}}, St}; 123 | map({attribute, Line, optional_callbacks, Es0}, St) -> 124 | % ignore vars introduced in optional_callback 125 | Fn = fun map/2, 126 | try ast:farity_list(Es0, St, Fn) of 127 | {Es1, _St1} -> 128 | {ok, {attribute, Line, optional_callbacks, Es1}, St} 129 | catch 130 | _:_ -> 131 | {ok, {attribute, Line, optional_callbacks, Es0}, St} 132 | end; 133 | map({'fun', Line, {clauses, Cs0}}, St) -> 134 | {Cs1, St1} = fun_clauses(Cs0, St), 135 | {ok, {'fun', Line, {clauses, Cs1}}, St1}; 136 | map({named_fun, Loc, Name, Cs}, St) -> 137 | {Cs1, St1} = fun_clauses(Cs, St), 138 | {ok, {named_fun, Loc, Name, Cs1}, St1}; 139 | map({lc, Line, E0, Qs0}, St) -> 140 | {Qs1, St1} = lc_bc_quals(Qs0, St), 141 | {E1, _St2} = expr(E0, St1), 142 | % discard all information inside a list comprehension 143 | {ok, {lc, Line, E1, Qs1}, St}; 144 | map({bc, Line, E0, Qs0}, St) -> 145 | {Qs1, St1} = lc_bc_quals(Qs0, St), 146 | {E1, _St2} = expr(E0, St1), 147 | % discard all information inside a binary comprehension 148 | {ok, {bc, Line, E1, Qs1}, St}; 149 | map({var, Line, Name}, St = #{matching := Matching}) -> 150 | R = 151 | case get_var(St, Name) of 152 | nil -> 153 | {var, 154 | Line, 155 | Name, 156 | #{new => true, defined => Line, matching => Matching}}; 157 | #{line := DLine} -> 158 | {var, 159 | Line, 160 | Name, 161 | #{new => false, defined => DLine, matching => Matching}} 162 | end, 163 | 164 | St1 = add_var_if_not_there(St, Line, Name), 165 | {ok, R, St1}; 166 | map({function, Line, Name0, Arity0, Clauses0}, St) -> 167 | {{Name, Arity, Clauses}, _St1} = function(Name0, Arity0, Clauses0, St), 168 | R = {function, Line, Name, Arity, Clauses}, 169 | {ok, R, St}; 170 | % binary types aren't in match position 171 | map({bin, Line, Fs}, St) -> 172 | {Fs2, St1} = pattern_grp(Fs, St), 173 | {ok, {bin, Line, Fs2}, St1}; 174 | map(_Ast, _St) -> 175 | auto. 176 | 177 | function(Name, Arity, Clauses0, St) -> 178 | {Clauses1, _St1} = ast:reduce(Clauses0, St, fun map/2, fun fun_clause/3), 179 | {{Name, Arity, Clauses1}, St}. 180 | 181 | expr(A, S) -> 182 | ast:expr(A, S, fun map/2). 183 | 184 | exprs(A, S) -> 185 | ast:exprs(A, S, fun map/2). 186 | 187 | icr_clauses(Cs, St) -> 188 | ast:reduce(Cs, St, fun map/2, fun match_clause/3). 189 | 190 | fun_clauses(Cs, St) -> 191 | {Cs1, _St1} = ast:reduce(Cs, St, fun map/2, fun fun_clause/3), 192 | % discard all information inside an inline fun 193 | {Cs1, St}. 194 | 195 | fun_clause({clause, Line, H0, G0, B0}, St, Fn) -> 196 | % don't set_matching in head since we don't need it in function head 197 | {H1, St1} = ast:head(H0, set_never_match(St), Fn), 198 | {G1, St2} = ast:guard(G0, clear_never_match(St1), Fn), 199 | {B1, _St3} = ast:exprs(B0, St2, Fn), 200 | {{clause, Line, H1, G1, B1}, St}. 201 | 202 | match_clause({clause, Line, H0, G0, B0}, St, Fn) -> 203 | {H1, St1} = ast:head(H0, set_matching(St), Fn), 204 | {G1, St2} = ast:guard(G0, clear_matching(St1), Fn), 205 | {B1, _St3} = ast:exprs(B0, St2, Fn), 206 | {{clause, Line, H1, G1, B1}, St}. 207 | 208 | lc_bc_quals(Qs, St) -> 209 | ast:reduce(Qs, St, fun map/2, fun lc_bc_qual/3). 210 | 211 | lc_bc_qual({generate, Line, P0, E0}, St, Fn) -> 212 | {E1, _St1} = ast:expr(E0, St, Fn), 213 | {P1, St2} = ast:pattern(P0, set_matching(St), Fn), 214 | {{generate, Line, P1, E1}, clear_matching(St2)}; 215 | lc_bc_qual({b_generate, Line, P0, E0}, St, Fn) -> 216 | {E1, _St1} = ast:expr(E0, St, Fn), 217 | {P1, St2} = ast:pattern(P0, set_matching(St), Fn), 218 | {{b_generate, Line, P1, E1}, clear_matching(St2)}; 219 | lc_bc_qual(Ast, St, Fn) -> 220 | % filter 221 | ast:expr(Ast, St, Fn). 222 | 223 | pattern_grp(Fs, St) -> 224 | ast:reduce(Fs, St, fun map/2, fun pattern_grp_item/3). 225 | 226 | % bin_element, Line, E1: Pattern, S1: Size, T1: Type Specifier List 227 | pattern_grp_item({bin_element, L1, E1, S1, T1}, St, Fn) -> 228 | #{matching := WasMatching} = St, 229 | {S2, St1_0} = 230 | case S1 of 231 | default -> 232 | {default, St}; 233 | _ -> 234 | % disable matching for 235 | ast:expr(S1, clear_matching(St), Fn) 236 | end, 237 | 238 | St1 = set_matching(St1_0, WasMatching), 239 | {T2, St2} = 240 | case T1 of 241 | default -> 242 | {default, St1}; 243 | _ -> 244 | ast:bit_types(T1, St1, Fn) 245 | end, 246 | {E1_1, St3} = ast:expr(E1, St2, Fn), 247 | {{bin_element, L1, E1_1, S2, T2}, St3}. 248 | --------------------------------------------------------------------------------