1
1
% % Implements Elixir quote.
2
2
-module (elixir_quote ).
3
- -export ([escape /2 , erl_escape /3 , erl_quote /4 , linify /2 , unquote /6 , join /5 ]).
3
+ -export ([escape /2 , erl_escape /3 , erl_quote /4 ,
4
+ linify /2 , unquote /4 , tail_join /3 , join /2 ]).
4
5
-include (" elixir.hrl" ).
5
6
6
7
% % Apply the line from site call on quoted contents.
@@ -24,44 +25,59 @@ do_linify(_, Else) -> Else.
24
25
25
26
% % Some expressions cannot be unquoted at compilation time.
26
27
% % This function is responsible for doing runtime unquoting.
27
- unquote (_File , _Line , Meta , Left , { '__aliases__' , _ , Args }, nil ) ->
28
+ unquote (Meta , Left , { '__aliases__' , _ , Args }, nil ) ->
28
29
{ '__aliases__' , Meta , [Left |Args ] };
29
30
30
- unquote (_File , _Line , Meta , Left , Right , nil ) when is_atom (Right ) ->
31
+ unquote (Meta , Left , Right , nil ) when is_atom (Right ) ->
31
32
case atom_to_list (Right ) of
32
33
" Elixir." ++ _ ->
33
34
{ '__aliases__' , Meta , [Left , Right ] };
34
35
_ ->
35
36
{ { '.' , Meta , [Left , Right ] }, Meta , [] }
36
37
end ;
37
38
38
- unquote (_File , _Line , Meta , Left , { Right , _ , Context }, nil ) when is_atom (Right ), is_atom (Context ) ->
39
+ unquote (Meta , Left , { Right , _ , Context }, nil ) when is_atom (Right ), is_atom (Context ) ->
39
40
{ { '.' , Meta , [Left , Right ] }, Meta , [] };
40
41
41
- unquote (_File , _Line , Meta , Left , { Right , _ , Args }, nil ) when is_atom (Right ) ->
42
+ unquote (Meta , Left , { Right , _ , Args }, nil ) when is_atom (Right ) ->
42
43
{ { '.' , Meta , [Left , Right ] }, Meta , Args };
43
44
44
- unquote (File , Line , _Meta , _Left , Right , nil ) ->
45
- elixir_errors : syntax_error ( Line , File , " expected unquote after dot to return an atom, "
46
- " an alias or a quoted call, got: ~ts " , [ 'Elixir.Macro' :to_string (Right )] );
45
+ unquote (_Meta , _Left , Right , nil ) ->
46
+ argument_error (<< " expected unquote after dot to return an atom, an alias or a quoted call, got: " ,
47
+ ( 'Elixir.Macro' :to_string (Right ))/ binary >> );
47
48
48
- unquote (_File , _Line , Meta , Left , Right , Args ) when is_atom (Right ) ->
49
+ unquote (Meta , Left , Right , Args ) when is_atom (Right ) ->
49
50
{ { '.' , Meta , [Left , Right ] }, Meta , Args };
50
51
51
- unquote (_File , _Line , Meta , Left , { Right , _ , Context }, Args ) when is_atom (Right ), is_atom (Context ) ->
52
+ unquote (Meta , Left , { Right , _ , Context }, Args ) when is_atom (Right ), is_atom (Context ) ->
52
53
{ { '.' , Meta , [Left , Right ] }, Meta , Args };
53
54
54
- unquote (File , Line , _Meta , _Left , Right , _Args ) ->
55
- elixir_errors : syntax_error ( Line , File , " expected unquote after dot with args to return an atom "
56
- " or a quoted call, got: ~ts " , [ 'Elixir.Macro' :to_string (Right )] ).
55
+ unquote (_Meta , _Left , Right , _Args ) ->
56
+ argument_error (<< " expected unquote after dot with args to return an atom or a quoted call, got: " ,
57
+ ( 'Elixir.Macro' :to_string (Right ))/ binary >> ).
57
58
58
- join (_File , _Line , Left , Right , Rest ) when is_list (Left ), is_list (Right ), is_list (Rest ) ->
59
- Rest ++ Left ++ Right ;
59
+ join (Left , Right ) when is_list (Right ) ->
60
+ validate_join (Left ),
61
+ Left ++ Right .
60
62
61
- join (_File , _Line , Left , Right , Rest ) ->
62
- [H |T ] = lists :reverse (Rest ++ Left ),
63
+ tail_join (Left , Right , Tail ) when is_list (Right ), is_list (Tail ) ->
64
+ validate_join (Left ),
65
+ Tail ++ Left ++ Right ;
66
+
67
+ tail_join (Left , Right , Tail ) when is_list (Left ) ->
68
+ validate_join (Left ),
69
+ [H |T ] = lists :reverse (Tail ++ Left ),
63
70
lists :reverse ([{ '|' , [], [H , Right ] }|T ]).
64
71
72
+ validate_join (List ) when is_list (List ) ->
73
+ ok ;
74
+ validate_join (List ) when not is_list (List ) ->
75
+ argument_error (<<" expected a list with quoted expressions in unquote_splicing/1, got: " ,
76
+ ('Elixir.Kernel' :inspect (List ))/binary >>).
77
+
78
+ argument_error (Message ) ->
79
+ 'Elixir.Kernel' :raise ('Elixir.ArgumentError' , [{message ,Message }]).
80
+
65
81
% % Escapes the given expression. It is similar to quote, but
66
82
% % lines are kept and hygiene mechanisms are disabled.
67
83
escape (Expr , Unquote ) ->
@@ -214,9 +230,9 @@ do_quote(Other, Q, _) ->
214
230
% % Quote helpers
215
231
216
232
do_quote_call (Left , Meta , Expr , Args , Q , S ) ->
217
- All = [? line ( Meta ), meta (Meta , Q ), Left , { unquote , Meta , [Expr ] }, Args ],
233
+ All = [meta (Meta , Q ), Left , { unquote , Meta , [Expr ] }, Args ],
218
234
{ TAll , TQ } = lists :mapfoldl (fun (X , Acc ) -> do_quote (X , Acc , S ) end , Q , All ),
219
- { { { '.' , Meta , [elixir_quote , unquote ] }, Meta , [{ '__FILE__' , [], nil }| TAll ] }, TQ }.
235
+ { { { '.' , Meta , [elixir_quote , unquote ] }, Meta , TAll }, TQ }.
220
236
221
237
do_quote_fa (Target , Meta , Args , F , A , Q , S ) ->
222
238
NewMeta =
@@ -264,24 +280,24 @@ do_splice([{ '|', Meta, [{ unquote_splicing, _, [Left] }, Right] }|T], #elixir_q
264
280
% % 1, 2 and 3, which could even be unquotes.
265
281
{ TT , QT } = do_splice (T , Q , S , [], []),
266
282
{ TR , QR } = do_quote (Right , QT , S ),
267
-
268
- % % Do the joining at runtime when we are aware of the values.
269
- Args = [{ '__FILE__' , [], nil }, ? line (Meta ), Left , TR , TT ],
270
- { { { '.' , Meta , [elixir_quote , join ] }, Meta , Args }, QR # elixir_quote {unquoted = true } };
283
+ { do_runtime_join (Meta , tail_join , [Left , TR , TT ]), QR # elixir_quote {unquoted = true } };
271
284
272
285
do_splice (List , Q , S ) ->
273
286
do_splice (List , Q , S , [], []).
274
287
275
- do_splice ([{ unquote_splicing , _ , [Expr ] }|T ], # elixir_quote {unquote = true } = Q , S , Buffer , Acc ) ->
276
- do_splice (T , Q # elixir_quote {unquoted = true }, S , [], do_splice_join ( do_splice_join ( Expr , Buffer ) , Acc ));
288
+ do_splice ([{ unquote_splicing , Meta , [Expr ] }|T ], # elixir_quote {unquote = true } = Q , S , Buffer , Acc ) ->
289
+ do_splice (T , Q # elixir_quote {unquoted = true }, S , [], do_runtime_join ( Meta , join , [ Expr , do_join ( Buffer , Acc )] ));
277
290
278
291
do_splice ([H |T ], Q , S , Buffer , Acc ) ->
279
292
{ TH , TQ } = do_quote (H , Q , S ),
280
293
do_splice (T , TQ , S , [TH |Buffer ], Acc );
281
294
282
295
do_splice ([], Q , _S , Buffer , Acc ) ->
283
- { do_splice_join (Buffer , Acc ), Q }.
296
+ { do_join (Buffer , Acc ), Q }.
297
+
298
+ do_join (Left , []) -> Left ;
299
+ do_join ([], Right ) -> Right ;
300
+ do_join (Left , Right ) -> { { '.' , [], ['Elixir.Kernel' , '++' ] }, [], [Left , Right ] }.
284
301
285
- do_splice_join (Left , []) -> Left ;
286
- do_splice_join ([], Right ) -> Right ;
287
- do_splice_join (Left , Right ) -> { { '.' , [], ['Elixir.Kernel' , '++' ] }, [], [Left , Right ] }.
302
+ do_runtime_join (Meta , Fun , Args ) ->
303
+ { { '.' , Meta , [elixir_quote , Fun ] }, Meta , Args }.
0 commit comments