1
1
-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 ]).
3
3
-export ([dot /5 , tail_list /3 , list /2 ]). % % Quote callbacks
4
4
5
5
-include (" elixir.hrl" ).
@@ -132,16 +132,15 @@ annotate(Tree, _Context) -> Tree.
132
132
133
133
% % Escapes the given expression. It is similar to quote, but
134
134
% % lines are kept and hygiene mechanisms are disabled.
135
- escape (Expr , Unquote ) ->
135
+ escape (Expr , Kind , Unquote ) ->
136
136
{Res , Q } = quote (Expr , nil , # elixir_quote {
137
137
line = true ,
138
138
file = nil ,
139
139
vars_hygiene = false ,
140
140
aliases_hygiene = false ,
141
141
imports_hygiene = false ,
142
- unquote = Unquote ,
143
- escape = true
144
- }, nil ),
142
+ unquote = Unquote
143
+ }, Kind ),
145
144
{Res , Q # elixir_quote .unquoted }.
146
145
147
146
% % Quotes an expression and return its quoted Elixir AST.
@@ -161,7 +160,7 @@ quote(Expr, Binding, Q, E) ->
161
160
{'{}' , [], [K , [], Context ]},
162
161
V
163
162
] ]
164
- } || {K , V } <- Binding ],
163
+ } || {K , V } <- Binding ],
165
164
166
165
{TExprs , TQ } = do_quote (Expr , Q , E ),
167
166
{{'{}' , [], ['__block__' , [], Vars ++ [TExprs ] ]}, TQ }.
@@ -211,11 +210,7 @@ do_quote({'&', Meta, [{'/', _, [{F, _, C}, A]}] = Args},
211
210
do_quote ({Name , Meta , ArgsOrAtom }, # elixir_quote {imports_hygiene = true } = Q , E ) when is_atom (Name ) ->
212
211
do_quote_import (Name , Meta , ArgsOrAtom , Q , E );
213
212
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
219
214
220
215
do_quote ({Left , Right }, # elixir_quote {unquote = true } = Q , E ) when
221
216
is_tuple (Left ) andalso (element (1 , Left ) == unquote_splicing );
@@ -227,7 +222,33 @@ do_quote({Left, Right}, Q, E) ->
227
222
{TRight , RQ } = do_quote (Right , LQ , E ),
228
223
{{TLeft , TRight }, RQ };
229
224
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 ) ->
231
252
case bit_size (BitString ) rem 8 of
232
253
0 ->
233
254
{BitString , Q };
@@ -236,51 +257,41 @@ do_quote(BitString, #elixir_quote{escape=true} = Q, _) when is_bitstring(BitStri
236
257
{{'<<>>' , [], [{'::' , [], [Bits , {size , [], [Size ]}]}, {'::' , [], [Bytes , {binary , [], []}]}]}, Q }
237
258
end ;
238
259
239
- do_quote (Map , # elixir_quote { escape = true } = Q , E ) when is_map (Map ) ->
260
+ do_escape (Map , Q , E ) when is_map (Map ) ->
240
261
{TT , TQ } = do_quote (maps :to_list (Map ), Q , E ),
241
262
{{'%{}' , [], TT }, TQ };
242
263
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 ) ->
248
265
% % The improper case is a bit inefficient, but improper lists are rare.
249
266
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 , [], []),
253
270
{TR , QR } = do_quote (R , QL , E ),
254
271
{update_last (TL , fun (X ) -> {'|' , [], [X , TR ]} end ), QR }
255
272
end ;
256
273
257
- do_quote (Other , # elixir_quote { escape = true } = Q , _ )
274
+ do_escape (Other , Q , _ )
258
275
when is_number (Other ); is_pid (Other ); is_atom (Other ) ->
259
276
{Other , Q };
260
277
261
- do_quote (Fun , # elixir_quote { escape = true } = Q , _ ) when is_function (Fun ) ->
278
+ do_escape (Fun , Q , _ ) when is_function (Fun ) ->
262
279
case (erlang :fun_info (Fun , env ) == {env , []}) andalso
263
280
(erlang :fun_info (Fun , type ) == {type , external }) of
264
281
true -> {Fun , Q };
265
282
false -> bad_escape (Fun )
266
283
end ;
267
284
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 ).
278
287
279
288
bad_escape (Arg ) ->
280
289
argument_error (<<" cannot escape " , ('Elixir.Kernel' :inspect (Arg , []))/binary , " . " ,
281
290
" The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, " ,
282
291
" PIDs and remote functions in the format &Mod.fun/arity" >>).
283
292
293
+ % % do_quote_*
294
+
284
295
do_quote_import (Name , Meta , ArgsOrAtom , # elixir_quote {imports_hygiene = true } = Q , E ) ->
285
296
Arity = case is_atom (ArgsOrAtom ) of
286
297
true -> 0 ;
@@ -332,10 +343,41 @@ do_quote_tuple(Left, Meta, [{{unquote, _, _}, _, _}, _] = Right, Q, E) when ?def
332
343
{{'{}' , [], [TLeft , meta (Meta , Q ), [NewHead , Body ]]}, RQ };
333
344
334
345
do_quote_tuple (Left , Meta , Right , Q , E ) ->
335
- {TLeft , LQ } = do_quote (Left , Q , E ),
346
+ {TLeft , LQ } = do_quote (Left , Q , E ),
336
347
{TRight , RQ } = do_quote (Right , LQ , E ),
337
348
{{'{}' , [], [TLeft , meta (Meta , Q ), TRight ]}, RQ }.
338
349
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
+
339
381
meta (Meta , Q ) ->
340
382
generated (keep (Meta , Q ), Q ).
341
383
@@ -381,33 +423,3 @@ keynew(Key, Meta, Value) ->
381
423
{Key , _ } -> Meta ;
382
424
_ -> keystore (Key , Meta , Value )
383
425
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