11-module (elixir_quote ).
2- -export ([escape /2 , linify /2 , linify /3 , linify_with_context_counter /3 , quote /4 ]).
2+ -export ([escape /3 , linify /2 , linify /3 , linify_with_context_counter /3 , quote /4 ]).
33-export ([dot /5 , tail_list /3 , list /2 ]). % % Quote callbacks
44
55-include (" elixir.hrl" ).
@@ -132,16 +132,15 @@ annotate(Tree, _Context) -> Tree.
132132
133133% % Escapes the given expression. It is similar to quote, but
134134% % lines are kept and hygiene mechanisms are disabled.
135- escape (Expr , Unquote ) ->
135+ escape (Expr , Kind , Unquote ) ->
136136 {Res , Q } = quote (Expr , nil , # elixir_quote {
137137 line = true ,
138138 file = nil ,
139139 vars_hygiene = false ,
140140 aliases_hygiene = false ,
141141 imports_hygiene = false ,
142- unquote = Unquote ,
143- escape = true
144- }, nil ),
142+ unquote = Unquote
143+ }, Kind ),
145144 {Res , Q # elixir_quote .unquoted }.
146145
147146% % Quotes an expression and return its quoted Elixir AST.
@@ -161,7 +160,7 @@ quote(Expr, Binding, Q, E) ->
161160 {'{}' , [], [K , [], Context ]},
162161 V
163162 ] ]
164- } || {K , V } <- Binding ],
163+ } || {K , V } <- Binding ],
165164
166165 {TExprs , TQ } = do_quote (Expr , Q , E ),
167166 {{'{}' , [], ['__block__' , [], Vars ++ [TExprs ] ]}, TQ }.
@@ -211,11 +210,7 @@ do_quote({'&', Meta, [{'/', _, [{F, _, C}, A]}] = Args},
211210do_quote ({Name , Meta , ArgsOrAtom }, # elixir_quote {imports_hygiene = true } = Q , E ) when is_atom (Name ) ->
212211 do_quote_import (Name , Meta , ArgsOrAtom , Q , E );
213212
214- do_quote ({_ , _ , _ } = Tuple , # elixir_quote {escape = false } = Q , E ) ->
215- Annotated = annotate (Tuple , Q # elixir_quote .context ),
216- do_quote_tuple (Annotated , Q , E );
217-
218- % % Literals
213+ % % Two element tuples
219214
220215do_quote ({Left , Right }, # elixir_quote {unquote = true } = Q , E ) when
221216 is_tuple (Left ) andalso (element (1 , Left ) == unquote_splicing );
@@ -227,7 +222,33 @@ do_quote({Left, Right}, Q, E) ->
227222 {TRight , RQ } = do_quote (Right , LQ , E ),
228223 {{TLeft , TRight }, RQ };
229224
230- do_quote (BitString , # elixir_quote {escape = true } = Q , _ ) when is_bitstring (BitString ) ->
225+ % % Everything else
226+
227+ do_quote (Other , Q , E ) when is_atom (E ) ->
228+ do_escape (Other , Q , E );
229+
230+ do_quote ({_ , _ , _ } = Tuple , Q , E ) ->
231+ Annotated = annotate (Tuple , Q # elixir_quote .context ),
232+ do_quote_tuple (Annotated , Q , E );
233+
234+ do_quote (List , Q , E ) when is_list (List ) ->
235+ do_quote_splice (lists :reverse (List ), Q , E );
236+
237+ do_quote (Other , Q , _ ) ->
238+ {Other , Q }.
239+
240+ % % do_escape
241+
242+ do_escape ({Left , _Meta , Right }, Q , E = prune_metadata ) ->
243+ {TL , QL } = do_quote (Left , Q , E ),
244+ {TR , QR } = do_quote (Right , QL , E ),
245+ {{'{}' , [], [TL , [], TR ]}, QR };
246+
247+ do_escape (Tuple , Q , E ) when is_tuple (Tuple ) ->
248+ {TT , TQ } = do_quote (tuple_to_list (Tuple ), Q , E ),
249+ {{'{}' , [], TT }, TQ };
250+
251+ do_escape (BitString , Q , _ ) when is_bitstring (BitString ) ->
231252 case bit_size (BitString ) rem 8 of
232253 0 ->
233254 {BitString , Q };
@@ -236,51 +257,41 @@ do_quote(BitString, #elixir_quote{escape=true} = Q, _) when is_bitstring(BitStri
236257 {{'<<>>' , [], [{'::' , [], [Bits , {size , [], [Size ]}]}, {'::' , [], [Bytes , {binary , [], []}]}]}, Q }
237258 end ;
238259
239- do_quote (Map , # elixir_quote { escape = true } = Q , E ) when is_map (Map ) ->
260+ do_escape (Map , Q , E ) when is_map (Map ) ->
240261 {TT , TQ } = do_quote (maps :to_list (Map ), Q , E ),
241262 {{'%{}' , [], TT }, TQ };
242263
243- do_quote (Tuple , # elixir_quote {escape = true } = Q , E ) when is_tuple (Tuple ) ->
244- {TT , TQ } = do_quote (tuple_to_list (Tuple ), Q , E ),
245- {{'{}' , [], TT }, TQ };
246-
247- do_quote (List , # elixir_quote {escape = true } = Q , E ) when is_list (List ) ->
264+ do_escape (List , Q , E ) when is_list (List ) ->
248265 % % The improper case is a bit inefficient, but improper lists are rare.
249266 case reverse_improper (List ) of
250- {L } -> do_splice (L , Q , E );
251- {L , R } ->
252- {TL , QL } = do_splice (L , Q , E , [], []),
267+ {L } -> do_quote_splice (L , Q , E );
268+ {L , R } ->
269+ {TL , QL } = do_quote_splice (L , Q , E , [], []),
253270 {TR , QR } = do_quote (R , QL , E ),
254271 {update_last (TL , fun (X ) -> {'|' , [], [X , TR ]} end ), QR }
255272 end ;
256273
257- do_quote (Other , # elixir_quote { escape = true } = Q , _ )
274+ do_escape (Other , Q , _ )
258275 when is_number (Other ); is_pid (Other ); is_atom (Other ) ->
259276 {Other , Q };
260277
261- do_quote (Fun , # elixir_quote { escape = true } = Q , _ ) when is_function (Fun ) ->
278+ do_escape (Fun , Q , _ ) when is_function (Fun ) ->
262279 case (erlang :fun_info (Fun , env ) == {env , []}) andalso
263280 (erlang :fun_info (Fun , type ) == {type , external }) of
264281 true -> {Fun , Q };
265282 false -> bad_escape (Fun )
266283 end ;
267284
268- do_quote (Other , # elixir_quote {escape = true }, _ ) ->
269- bad_escape (Other );
270-
271- do_quote (List , Q , E ) when is_list (List ) ->
272- do_splice (lists :reverse (List ), Q , E );
273-
274- do_quote (Other , Q , _ ) ->
275- {Other , Q }.
276-
277- % % Quote helpers
285+ do_escape (Other , _ , _ ) ->
286+ bad_escape (Other ).
278287
279288bad_escape (Arg ) ->
280289 argument_error (<<" cannot escape " , ('Elixir.Kernel' :inspect (Arg , []))/binary , " . " ,
281290 " The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, " ,
282291 " PIDs and remote functions in the format &Mod.fun/arity" >>).
283292
293+ % % do_quote_*
294+
284295do_quote_import (Name , Meta , ArgsOrAtom , # elixir_quote {imports_hygiene = true } = Q , E ) ->
285296 Arity = case is_atom (ArgsOrAtom ) of
286297 true -> 0 ;
@@ -332,10 +343,41 @@ do_quote_tuple(Left, Meta, [{{unquote, _, _}, _, _}, _] = Right, Q, E) when ?def
332343 {{'{}' , [], [TLeft , meta (Meta , Q ), [NewHead , Body ]]}, RQ };
333344
334345do_quote_tuple (Left , Meta , Right , Q , E ) ->
335- {TLeft , LQ } = do_quote (Left , Q , E ),
346+ {TLeft , LQ } = do_quote (Left , Q , E ),
336347 {TRight , RQ } = do_quote (Right , LQ , E ),
337348 {{'{}' , [], [TLeft , meta (Meta , Q ), TRight ]}, RQ }.
338349
350+ do_quote_splice ([{'|' , Meta , [{unquote_splicing , _ , [Left ]}, Right ]} | T ], # elixir_quote {unquote = true } = Q , E ) ->
351+ % % Process the remaining entries on the list.
352+ % % For [1, 2, 3, unquote_splicing(arg) | tail], this will quote
353+ % % 1, 2 and 3, which could even be unquotes.
354+ {TT , QT } = do_quote_splice (T , Q , E , [], []),
355+ {TR , QR } = do_quote (Right , QT , E ),
356+ {do_runtime_list (Meta , tail_list , [Left , TR , TT ]), QR # elixir_quote {unquoted = true }};
357+
358+ do_quote_splice (List , Q , E ) ->
359+ do_quote_splice (List , Q , E , [], []).
360+
361+ do_quote_splice ([{unquote_splicing , Meta , [Expr ]} | T ], # elixir_quote {unquote = true } = Q , E , Buffer , Acc ) ->
362+ Runtime = do_runtime_list (Meta , list , [Expr , do_list_concat (Buffer , Acc )]),
363+ do_quote_splice (T , Q # elixir_quote {unquoted = true }, E , [], Runtime );
364+
365+ do_quote_splice ([H | T ], Q , E , Buffer , Acc ) ->
366+ {TH , TQ } = do_quote (H , Q , E ),
367+ do_quote_splice (T , TQ , E , [TH | Buffer ], Acc );
368+
369+ do_quote_splice ([], Q , _E , Buffer , Acc ) ->
370+ {do_list_concat (Buffer , Acc ), Q }.
371+
372+ do_list_concat (Left , []) -> Left ;
373+ do_list_concat ([], Right ) -> Right ;
374+ do_list_concat (Left , Right ) -> {{'.' , [], [erlang , '++' ]}, [], [Left , Right ]}.
375+
376+ do_runtime_list (Meta , Fun , Args ) ->
377+ {{'.' , Meta , [elixir_quote , Fun ]}, Meta , Args }.
378+
379+ % % Helpers
380+
339381meta (Meta , Q ) ->
340382 generated (keep (Meta , Q ), Q ).
341383
@@ -381,33 +423,3 @@ keynew(Key, Meta, Value) ->
381423 {Key , _ } -> Meta ;
382424 _ -> keystore (Key , Meta , Value )
383425 end .
384-
385- % % Quote splicing
386-
387- do_splice ([{'|' , Meta , [{unquote_splicing , _ , [Left ]}, Right ]} | T ], # elixir_quote {unquote = true } = Q , E ) ->
388- % % Process the remaining entries on the list.
389- % % For [1, 2, 3, unquote_splicing(arg) | tail], this will quote
390- % % 1, 2 and 3, which could even be unquotes.
391- {TT , QT } = do_splice (T , Q , E , [], []),
392- {TR , QR } = do_quote (Right , QT , E ),
393- {do_runtime_list (Meta , tail_list , [Left , TR , TT ]), QR # elixir_quote {unquoted = true }};
394-
395- do_splice (List , Q , E ) ->
396- do_splice (List , Q , E , [], []).
397-
398- do_splice ([{unquote_splicing , Meta , [Expr ]} | T ], # elixir_quote {unquote = true } = Q , E , Buffer , Acc ) ->
399- do_splice (T , Q # elixir_quote {unquoted = true }, E , [], do_runtime_list (Meta , list , [Expr , do_join (Buffer , Acc )]));
400-
401- do_splice ([H | T ], Q , E , Buffer , Acc ) ->
402- {TH , TQ } = do_quote (H , Q , E ),
403- do_splice (T , TQ , E , [TH | Buffer ], Acc );
404-
405- do_splice ([], Q , _E , Buffer , Acc ) ->
406- {do_join (Buffer , Acc ), Q }.
407-
408- do_join (Left , []) -> Left ;
409- do_join ([], Right ) -> Right ;
410- do_join (Left , Right ) -> {{'.' , [], [erlang , '++' ]}, [], [Left , Right ]}.
411-
412- do_runtime_list (Meta , Fun , Args ) ->
413- {{'.' , Meta , [elixir_quote , Fun ]}, Meta , Args }.
0 commit comments