@@ -401,6 +401,7 @@ defmodule Record do
401401 # Using {} here is safe, since it's not valid AST
402402 default = if Macro.Env . in_match? ( caller ) , do: { :_ , [ ] , nil } , else: { }
403403 { default , keyword } = Keyword . pop ( keyword , :_ , default )
404+ { keyword , exprs } = hoist_expressions ( keyword , caller )
404405
405406 { elements , remaining } =
406407 Enum . map_reduce ( fields , keyword , fn { key , field_default } , remaining ->
@@ -413,6 +414,7 @@ defmodule Record do
413414 case remaining do
414415 [ ] ->
415416 quote ( do: { unquote ( tag ) , unquote_splicing ( elements ) } )
417+ |> maybe_prepend_reversed_exprs ( exprs )
416418
417419 [ { key , _ } | _ ] ->
418420 raise ArgumentError , "record #{ inspect ( tag ) } does not have the key: #{ inspect ( key ) } "
@@ -425,27 +427,45 @@ defmodule Record do
425427 raise ArgumentError , "cannot invoke update style macro inside match"
426428 end
427429
430+ { keyword , exprs } = hoist_expressions ( keyword , caller )
431+
428432 if Keyword . has_key? ( keyword , :_ ) do
429433 message = "updating a record with a default (:_) is equivalent to creating a new record"
430434 IO . warn ( message , Macro.Env . stacktrace ( caller ) )
431435 create ( tag , fields , keyword , caller )
432436 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 ) } "
441443 end
444+ end )
442445
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 )
446447 end
447448 end
448449
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+
449469 defp build_update ( updates , initial ) do
450470 updates
451471 |> Enum . sort ( fn { left , _ } , { right , _ } -> right <= left end )
@@ -454,21 +474,6 @@ defmodule Record do
454474 end )
455475 end
456476
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-
472477 defp simple_argument? ( { name , _ , ctx } ) when is_atom ( name ) and is_atom ( ctx ) , do: true
473478 defp simple_argument? ( other ) , do: Macro . quoted_literal? ( other )
474479
0 commit comments