@@ -401,6 +401,7 @@ defmodule Record do
401
401
# Using {} here is safe, since it's not valid AST
402
402
default = if Macro.Env . in_match? ( caller ) , do: { :_ , [ ] , nil } , else: { }
403
403
{ default , keyword } = Keyword . pop ( keyword , :_ , default )
404
+ { keyword , exprs } = hoist_expressions ( keyword , caller )
404
405
405
406
{ elements , remaining } =
406
407
Enum . map_reduce ( fields , keyword , fn { key , field_default } , remaining ->
@@ -413,6 +414,7 @@ defmodule Record do
413
414
case remaining do
414
415
[ ] ->
415
416
quote ( do: { unquote ( tag ) , unquote_splicing ( elements ) } )
417
+ |> maybe_prepend_reversed_exprs ( exprs )
416
418
417
419
[ { key , _ } | _ ] ->
418
420
raise ArgumentError , "record #{ inspect ( tag ) } does not have the key: #{ inspect ( key ) } "
@@ -425,27 +427,45 @@ defmodule Record do
425
427
raise ArgumentError , "cannot invoke update style macro inside match"
426
428
end
427
429
430
+ { keyword , exprs } = hoist_expressions ( keyword , caller )
431
+
428
432
if Keyword . has_key? ( keyword , :_ ) do
429
433
message = "updating a record with a default (:_) is equivalent to creating a new record"
430
434
IO . warn ( message , Macro.Env . stacktrace ( caller ) )
431
435
create ( tag , fields , keyword , caller )
432
436
else
433
- case build_updates ( keyword , fields , [ ] , [ ] , [ ] ) do
434
- { updates , [ ] , [ ] } ->
435
- build_update ( updates , var )
436
-
437
- { updates , vars , exprs } ->
438
- quote do
439
- { unquote_splicing ( :lists . reverse ( vars ) ) } = { unquote_splicing ( :lists . reverse ( exprs ) ) }
440
- unquote ( build_update ( updates , var ) )
437
+ updates =
438
+ Enum . map ( keyword , fn { key , value } ->
439
+ if index = find_index ( fields , key , 2 ) do
440
+ { index , value }
441
+ else
442
+ raise ArgumentError , "record #{ inspect ( tag ) } does not have the key: #{ inspect ( key ) } "
441
443
end
444
+ end )
442
445
443
- { :error , key } ->
444
- raise ArgumentError , "record #{ inspect ( tag ) } does not have the key: #{ inspect ( key ) } "
445
- end
446
+ build_update ( updates , var ) |> maybe_prepend_reversed_exprs ( exprs )
446
447
end
447
448
end
448
449
450
+ defp hoist_expressions ( keyword , % { context: nil } ) do
451
+ Enum . map_reduce ( keyword , [ ] , fn { key , expr } , acc ->
452
+ if simple_argument? ( expr ) do
453
+ { { key , expr } , acc }
454
+ else
455
+ var = Macro . var ( key , __MODULE__ )
456
+ { { key , var } , [ { := , [ ] , [ var , expr ] } | acc ] }
457
+ end
458
+ end )
459
+ end
460
+
461
+ defp hoist_expressions ( keyword , _ ) , do: { keyword , [ ] }
462
+
463
+ defp maybe_prepend_reversed_exprs ( expr , [ ] ) ,
464
+ do: expr
465
+
466
+ defp maybe_prepend_reversed_exprs ( expr , exprs ) ,
467
+ do: { :__block__ , [ ] , :lists . reverse ( [ expr | exprs ] ) }
468
+
449
469
defp build_update ( updates , initial ) do
450
470
updates
451
471
|> Enum . sort ( fn { left , _ } , { right , _ } -> right <= left end )
@@ -454,21 +474,6 @@ defmodule Record do
454
474
end )
455
475
end
456
476
457
- defp build_updates ( [ { key , value } | rest ] , fields , updates , vars , exprs ) do
458
- if index = find_index ( fields , key , 2 ) do
459
- if simple_argument? ( value ) do
460
- build_updates ( rest , fields , [ { index , value } | updates ] , vars , exprs )
461
- else
462
- var = Macro . var ( key , __MODULE__ )
463
- build_updates ( rest , fields , [ { index , var } | updates ] , [ var | vars ] , [ value | exprs ] )
464
- end
465
- else
466
- { :error , key }
467
- end
468
- end
469
-
470
- defp build_updates ( [ ] , _fields , updates , vars , exprs ) , do: { updates , vars , exprs }
471
-
472
477
defp simple_argument? ( { name , _ , ctx } ) when is_atom ( name ) and is_atom ( ctx ) , do: true
473
478
defp simple_argument? ( other ) , do: Macro . quoted_literal? ( other )
474
479
0 commit comments