èšäºã®ããã¹ãã³ããŒãšåæ§ã«ãæžããããœãŒã¹ã¯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 / 2ãæ€çŽ¢ããŠã handle_call / 3ã«å€æŽããŸãã
- handle_call / 2ã®é¢æ°å®çŸ©ã®äžã«ãStateãã©ã¡ãŒã¿ãŒãè¿œå ãã {replyã...ãState}ã®åå¥ã®æçµåŒãçµã¿ç«ãŠãŸã
ãã
ãã¬ãŒãã³ã°ããŸãã æ§æã§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ã®åŒã³åºãã§ãïŒãæŒç®åãæžãæããéåžžã«ç°¡æœãªæ¹æ³ããããŸã ã