parse_transformを䜿甚する

免責事項説明されおいるツヌルは、物議を醞す評刀がありたす。 どこにいおも䜿甚するこずを勧めるのではなく、テクノロゞヌのスリルを軜枛するために䜿甚される抂念に慣れるだけです。



蚘事のテキストコピヌず同様に、曞かれた゜ヌスはgithubにありたす。



parse_transformずは



parse_transform-コンパむル前のAST倉曎メカニズム。 Erlangの構文を超えるこずなく、構造意味の意味を倉曎するように蚭蚈されおいたす。



残念ながら、これに぀いおの情報はWeb䞊にほずんどないため、非グルアヌランの゚ントリのしきい倀は非垞に高くなりたす。



私たちは䜕をしたすか



この蚘事では、ASTアヌランに぀いお少し説明し、単玔な倉換の䟋を瀺したす。たた、parse_transformを蚘述しおステヌトレスgen_serverを䜜成するプロセスを瀺したすタスクはあたり意味がありたせんが、䟋ずしお機胜したす。最埌にリンクを瀺したす。初心者トランスのセットで。





アヌランのAST



念のため AST定矩



ASTの説明を 100回読むよりも、ASTを1回芋る方が良いでしょう。 したがっお、各行がどのように倉換されるかを確認する小さなモゞュヌルを䜜成したす。



したがっお、 astdemo.erlの゜ヌスコヌド

-module(astdemo). -export([hello/0, hello/2]). hello() -> hello("world", 1). hello(_What, 0) -> ok; hello(What, Count) -> io:format("Hello, ~s~n", [What]), hello(What, Count - 1).
      
      







ASTを衚瀺するには、このファむルのeppモゞュヌルからparse_file関数を蚭定する必芁がありたす。

  Eshell V5.8.5^ Gで䞭止
 1> {ok、Forms} = eppparse_file "astdemo.erl"、[]、[]、ioformat "〜p〜n"、[Forms]。
 [{属性、1、ファむル、{"astdemo.erl"、1}}、
  {属性、1、モゞュヌル、astdemo}、
  {属性、2、゚クスポヌト、[{hello、0}、{hello、2}]}、
  {関数、4、こんにちは、0、
            [{句、4、[]、[]、
                     [{呌び出し、5、
                            {アトム、5、こんにちは}、
                            [{string、5、 "world"}、{integer、5.1}]}]}]}、
  {関数、7、こんにちは、2
            [{clause、7、[{var、7、 '_ What'}、{integer、7.0}]、[]、[{atom、8、ok}]}、
             {句、9、
                     [{var、9、 'What'}、{var、9、 'Count'}]、
                     []、
                     [{呌び出し、10、
                            {リモヌト、10、{アトム、10、io}、{アトム、10、圢匏}}、
                            [{string、10、 "Hello、〜s〜n"}、
                             {cons、10、{var、10、 'What'}、{nil、10}}]}、
                      {呌び出し、11
                            {アトム、11、こんにちは}、
                            [{var、11、 'What'}、
                             {op、11、 '-'、{var、11、 'Count'}、{integer、11,1}}]}]}]}、
  {eof、12}]
わかった




各匏は少なくずも3぀の長さのチュヌブに倉換されたすが、最初の2぀の芁玠は垞にタむプず文字列であり、それに固有の説明が続きたす。 特定の堎所に䜕があるかが明確でない堎合、 ドキュメントはサヌビスにありたす。



Parse_transform / 2関数



次に、dummy-parse_transformを実行しお、次に察凊する必芁があるものを確認したす。 これを行うには、倉換を凊理するモゞュヌルを䜜成し、ASTを操䜜する代わりに、それを印刷したす。



したがっお、 demo_pt.erl 

 -module(demo_pt). -export([parse_transform/2]). parse_transform(Forms, _Options) -> io:format("~p~n", [Forms]), Forms.
      
      







察応するディレクティブをastdemo.erlに挿入したす 。

 -module(astdemo). -compile({parse_transform, demo_pt}). -export([hello/0, hello/2]). ...........
      
      







コンパむルしたす

  Eshell V5.8.5^ Gで䞭止
 1> castdemo。
 [{属性、1、ファむル、{"./ astdemo.erl"、1}}、
  {属性、1、モゞュヌル、astdemo}、
  {属性、3、゚クスポヌト、[{hello、0}、{hello、2}]}、
  {関数、5、こんにちは、0、
            [{句、5、[]、[]、
                     [{呌び出し、6、
                            {アトム、6、こんにちは}、
                            [{string、6、 "world"}、{integer、6.1}]}]}]}、
  {関数、8、こんにちは、2
            [{clause、8、[{var、8、 '_ What'}、{integer、8,0}]、[]、[{atom、9、ok}]}、
             {句、10、
                     [{var、10、 'What'}、{var、10、 'Count'}]、
                     []、
                     [{呌び出し、11
                            {リモヌト、11、{アトム、11、io}、{アトム、11、フォヌマット}}、
                            [{string、11、 "Hello、〜s〜n"}、
                             {cons、11、{var、11、 'What'}、{nil、11}}]}、
                      {呌び出し、12、
                            {アトム、12、こんにちは}、
                            [{var、12、 'What'}、
                             {op、12、 '-'、{var、12、 'Count'}、{integer、12,1}}]}]}]}、
  {eof、13}]
 {ok、astdemo} 




ご芧のずおり、ASTは同じ改行たでですが、今回はコンパむル時に出力されたした。

コンパむラディレクティブは、倉換のために到着したASTで既に削陀されおいるこずに泚意しおください。



オプションで䌝えられるこずは、奜奇心reader盛な読者はおそらく自分で芋぀けるでしょう。 この蚘事はASTに぀いおです。



最初の倉換



緎習に圹に立たないこずをやっおみたしょう-関数の名前を「hello / 0」から「hi / 0」に倉曎したす。 hello / 0はモゞュヌル内から呌び出されるのではなく、倖郚からしか呌び出せないため、これは簡単です。 したがっお、゚クスポヌトのリストず関数のタむトルを倉曎するだけで十分です。



シングルフォヌムトランス



ASTフォヌムバむンディングはリストであり、その各芁玠は非垞に短いタむプのリストのフォヌムであるため、ミュヌテヌタヌ関数を介しおすべおのフォヌムを枡すこずは論理的です。 タスクは単玔であり、各匏の倉換は他のコンテンツに䟝存しないため、 listsmapが適しおいたす。

゚クスポヌトず関数ヘッダヌを倉曎する関数は次のようになりたす。

 % hello_to_hi replaces occurences of hello/0 with hi/0 hello_to_hi({attribute, Line, export, Exports}) -> % export attribute. Replace {hello, 0} with {hi, 0} HiExports = lists:map( fun ({hello, 0}) -> {hi, 0}; (E) -> E end, Exports), {attribute, Line, export, HiExports}; hello_to_hi({function, Line, hello, 0, Clauses}) -> % Header of hello/0. Just replace hello with hi {function, Line, hi, 0, Clauses}; hello_to_hi(Form) -> % Default: do not modify form Form.
      
      







今、すべお䞀緒に



parse_transform関数のコヌドを倉曎するこずにより、この関数を有効にしたす。

 parse_transform(Forms, _Options) -> HiForms = lists:map(fun hello_to_hi/1, Forms), io:format("~p~n", [HiForms]), HiForms.
      
      







demo_ptをコンパむルし、混乱させないようにしたす。



確認する



新しいトランスフォヌマヌでastdemoをコンパむルしようずしたした



  Eshell V5.8.5^ Gで䞭止
 1> castdemo。
 [{属性、1、ファむル、{"./ astdemo.erl"、1}}、
  {属性、1、モゞュヌル、astdemo}、
  {属性、3、゚クスポヌト、[{hi、0}、{hello、2}]}、
  {関数、5、こんにちは、0、
            [{句、5、[]、[]、
                     [{呌び出し、6、
                            {アトム、6、こんにちは}、
                            [{string、6、 "world"}、{integer、6.1}]}]}]}、
  {関数、8、こんにちは、2
            [{clause、8、[{var、8、 '_ What'}、{integer、8,0}]、[]、[{atom、9、ok}]}、
             {句、10、
                     [{var、10、 'What'}、{var、10、 'Count'}]、
                     []、
                     [{呌び出し、11
                            {リモヌト、11、{アトム、11、io}、{アトム、11、フォヌマット}}、
                            [{string、11、 "Hello、〜s〜n"}、
                             {cons、11、{var、11、 'What'}、{nil、11}}]}、
                      {呌び出し、12、
                            {アトム、12、こんにちは}、
                            [{var、12、 'What'}、
                             {op、12、 '-'、{var、12、 'Count'}、{integer、12,1}}]}]}]}、
  {eof、13}]
 {ok、astdemo}
 2> astdemoこんにちは。
ハロヌワヌルド
わかった 




いいね 圌らが望んだように機胜した。 もう少し圹に立぀こずをする時間です。



ステヌトレスgen_server parse_transform



gen_serverの動䜜を䜿甚しおモゞュヌルを䜜成する堎合、栌玍するものがないため、Stateを䞀緒にドラッグする必芁がない堎合がありたす。Stateをhandle_anythingから最終匏にドラッグするず、コヌドが詰たりたす。 parse_transformを実行しお、 handle_call / 2、handle_cast / 1、handle_info / 1を定矩できるようにしたす。 かどうか。 蚘事をもう少し短くするために、倉換handle_call / 2-> handle_call / 3のみを瀺し 、興味のある人は他のすべおを再定矩したす。



コンセプト



gen_serverの動䜜には、この方法で簡単にするために handle_callの定矩が必芁です ドキュメント 

 handle_call(Request, From, State) -> ..... {reply,Reply,NewState}.
      
      





Stateを考慮する必芁がないため、構文は次のようにしたす。

 handle_call(Request, From) -> ..... Reply.
      
      







倉換蚈画







ねこ



トレヌニングしたす。 構文でhandle_callを定矩し、その類䌌物をトランスフォヌマヌの比范および䜜成甚の暙準圢匏で定矩したした。

 -module(sl_gs_demo). -behavior(gen_server). -compile({parse_transform, sl_gs}). -export([handle_call/2, ref_handle_call/3]). -export([handle_cast/2, handle_info/2]). -export([init/1, terminate/2, code_change/3]). % This will be transformed handle_call(Req, From) -> {Req, From}. % That's what handle_call should finally look like ref_handle_call(Req, From, State) -> {reply, {Req, From}, State}. % Dummy functions to make gen_server happy % Exercise: Try to insert them automatically during transformations :) handle_cast(_, State) -> {noreply, State}. handle_info(_, State) -> {noreply, State}. init(_) -> {ok, none}. terminate(_, _) -> ok. code_change(_, State, _) -> {ok, State}.
      
      







コヌド



すべおが前回ず同じように蚘述されたした-eppparse_fileの出力を芋お、必芁なものに合わせおカスタマむズしたす。



 -module(sl_gs). -export([parse_transform/2]). parse_transform(Forms, _Options) -> lists:map(fun add_missing_state/1, Forms). add_missing_state({attribute, Line, export, Exports}) -> % export attribute. Replace {handle_call, 2} with {handle_call, 3} NewExports = lists:map( fun ({handle_call, 2}) -> {handle_call, 3}; % You can add more clauses here for other function mutations (E) -> E end, Exports), {attribute, Line, export, NewExports}; add_missing_state({function, Line, handle_call, 2, Clauses}) -> % Mutate clauses NewClauses = lists:map(fun change_call_clause/1, Clauses), % Finally, change arity in header {function, Line, handle_call, 3, NewClauses}; add_missing_state(Form) -> % Default Form. change_call_clause({clause, Line, Arguments, Guards, Body}) -> % Change arity in clauses. NewArgs = Arguments ++ [{var, Line, 'State'}], % Add State argument % Then replace last statement of each clause with corresponding tuple NewBody = change_call_body(Body), {clause, Line, NewArgs, Guards, NewBody}. change_call_body([Statement | Rest=[_|_] ]) -> % Rest has to be non-empty list for this % Recurse to change only last statement [Statement|change_call_body(Rest)]; change_call_body([LastStatement]) -> % Put it into tuple. Lines are zero to omit parsing LastStatement [{tuple,0, [{atom,0,reply}, LastStatement, {var,0,'State'}] }].
      
      







ヘルスチェック



  Eshell V5.8.5^ Gで䞭止
 1> csl_gs_demo。
 {ok、sl_gs_demo}
 2> {ok、D} = gen_serverstart_linksl_gs_demo、[]、[]。
 {OK、<0.39.0>}
 3> gen_server呌び出しD、こんにちは。
 {hello、{<0.32.0>、Ref <0.0.0.83>}}


成功 フクロりを仕䞊げおgithubに眮くこずは残っおいたす。



たずめ



興味のある読者は、ErlangでASTに䌚い、その倉換方法のおおよそのアむデアを受け取ったこずを願っおいたす。 おそらく誰かがparse_transformに぀いお最初に孊んだのでしょう。

この蚘事には、独自の倉換を曞き始めるのに十分な情報が含たれおいたす。 少し䜎いのは批刀であり、倉換に圹立぀ラむブラリぞのリンクです。



メ゜ッドの批刀



たず、parse_transformを䜿甚するず別のプロゞェクトにある堎合、プロゞェクトに䟝存関係が远加されたす。 鉄筋の堎合、これは臎呜的ではありたせん。

第二に、そのようなコヌドを読んでいる人特に線集しおいる人は、その抂念をすぐには理解できないかもしれたせん。 したがっお、適切なドキュメントだけでなく、゜ヌスの最初にあるドキュメントぞの目立぀リンクも必芁です。

第䞉に、独自の方蚀を曞く胜力は非垞に限られおいたす。 ASTがメスの䞋に来る前に、通垞のパヌサヌが機胜したす。 したがっお、トリッキヌなキヌワヌドず独自の挔算子を導入するず、パヌサヌが砎損し、タスクが非垞に耇雑になりたす。



Parse_transラむブラリ



parse_transはparse_transformsを曞くのに䟿利です。 ツリヌぞの再垰的なマップを䜜成できたす。これは、可倉の深さで匏を倉曎するずきに非垞に䟿利です。 䟋では、 gprocsendの呌び出しで「」挔算子を曞き換える非垞に簡朔な方法がありたす 。



All Articles