Skip to content

Commit d440045

Browse files
author
Ary Borenszweig
committed
Macros: rename argify to splat. Added double_splat method. Fixes #3630
1 parent 10ad407 commit d440045

File tree

4 files changed

+120
-47
lines changed

4 files changed

+120
-47
lines changed

spec/compiler/macro/macro_methods_spec.cr

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -564,15 +564,15 @@ describe "macro methods" do
564564
assert_macro "", %({{[1, 2, 3].last}}), [] of ASTNode, "3"
565565
end
566566

567-
it "executes argify" do
568-
assert_macro "", %({{[1, 2, 3].argify}}), [] of ASTNode, "1, 2, 3"
567+
it "executes splat" do
568+
assert_macro "", %({{[1, 2, 3].splat}}), [] of ASTNode, "1, 2, 3"
569569
end
570570

571-
it "executes argify with symbols and strings" do
572-
assert_macro "", %({{[:foo, "hello", 3].argify}}), [] of ASTNode, %(:foo, "hello", 3)
571+
it "executes splat with symbols and strings" do
572+
assert_macro "", %({{[:foo, "hello", 3].splat}}), [] of ASTNode, %(:foo, "hello", 3)
573573
end
574574

575-
it "executes argify with splat" do
575+
it "executes splat with splat" do
576576
assert_macro "", %({{*[1, 2, 3]}}), [] of ASTNode, "1, 2, 3"
577577
end
578578

@@ -730,6 +730,14 @@ describe "macro methods" do
730730
it "executes double splat" do
731731
assert_macro "", %({{**{1 => 2, 3 => 4}}}), [] of ASTNode, "1 => 2, 3 => 4"
732732
end
733+
734+
it "executes double splat" do
735+
assert_macro "", %({{{1 => 2, 3 => 4}.double_splat}}), [] of ASTNode, "1 => 2, 3 => 4"
736+
end
737+
738+
it "executes double splat with arg" do
739+
assert_macro "", %({{{1 => 2, 3 => 4}.double_splat(", ")}}), [] of ASTNode, "1 => 2, 3 => 4, "
740+
end
733741
end
734742

735743
describe "named tuple literal methods" do
@@ -784,6 +792,14 @@ describe "macro methods" do
784792
it "executes double splat" do
785793
assert_macro "", %({{**{a: 1, "foo bar": 2}}}), [] of ASTNode, %(a: 1, "foo bar": 2)
786794
end
795+
796+
it "executes double splat" do
797+
assert_macro "", %({{{a: 1, "foo bar": 2}.double_splat}}), [] of ASTNode, %(a: 1, "foo bar": 2)
798+
end
799+
800+
it "executes double splat with arg" do
801+
assert_macro "", %({{{a: 1, "foo bar": 2}.double_splat(", ")}}), [] of ASTNode, %(a: 1, "foo bar": 2, )
802+
end
787803
end
788804

789805
describe "tuple methods" do
@@ -867,15 +883,19 @@ describe "macro methods" do
867883
assert_macro "", %({{ {1, 2, 3}.last }}), [] of ASTNode, "3"
868884
end
869885

870-
it "executes argify" do
871-
assert_macro "", %({{ {1, 2, 3}.argify }}), [] of ASTNode, "1, 2, 3"
886+
it "executes splat" do
887+
assert_macro "", %({{ {1, 2, 3}.splat }}), [] of ASTNode, "1, 2, 3"
888+
end
889+
890+
it "executes splat with arg" do
891+
assert_macro "", %({{ {1, 2, 3}.splat(", ") }}), [] of ASTNode, "1, 2, 3, "
872892
end
873893

874-
it "executes argify with symbols and strings" do
875-
assert_macro "", %({{ {:foo, "hello", 3}.argify }}), [] of ASTNode, %(:foo, "hello", 3)
894+
it "executes splat with symbols and strings" do
895+
assert_macro "", %({{ {:foo, "hello", 3}.splat }}), [] of ASTNode, %(:foo, "hello", 3)
876896
end
877897

878-
it "executes argify with splat" do
898+
it "executes splat with splat" do
879899
assert_macro "", %({{ *{1, 2, 3} }}), [] of ASTNode, "1, 2, 3"
880900
end
881901

src/compiler/crystal/macros.cr

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,12 @@ module Crystal::Macros
477477

478478
# Returns a `MacroId` with all of this array's elements joined
479479
# by commas.
480-
def argify : MacroId
480+
#
481+
# If *trailing_string* is given, it will be appended to
482+
# the result unless this array is empty. This lets you
483+
# splat an array and optionally write a trailing comma
484+
# if needed.
485+
def splat(trailing_string : StringLiteral = nil) : MacroId
481486
end
482487

483488
# Similar to `Array#empty?`
@@ -608,6 +613,16 @@ module Crystal::Macros
608613
# This refers to the part before brackets in `MyHash{'a' => 1, 'b' => 2}`
609614
def type : Path | Nop
610615
end
616+
617+
# Returns a `MacroId` with all of this hash elements joined
618+
# by commas.
619+
#
620+
# If *trailing_string* is given, it will be appended to
621+
# the result unless this hash is empty. This lets you
622+
# splat a hash and optionally write a trailing comma
623+
# if needed.
624+
def double_splat(trailing_string : StringLiteral = nil) : MacroId
625+
end
611626
end
612627

613628
# A named tuple literal.
@@ -636,6 +651,10 @@ module Crystal::Macros
636651
def map : ArrayLiteral
637652
end
638653

654+
# Similar to `HashLiteral#double_splat`
655+
def double_splat(trailing_string : StringLiteral = nil) : MacroId
656+
end
657+
639658
# Similar to `NamedTuple#[]`
640659
def [](key : ASTNode) : ASTNode
641660
end
@@ -683,18 +702,9 @@ module Crystal::Macros
683702
end
684703

685704
# A tuple literal.
705+
#
706+
# It's macro methods are the same as `ArrayLiteral`
686707
class TupleLiteral < ASTNode
687-
# Similar to `Tuple#empty?`
688-
def empty? : BoolLiteral
689-
end
690-
691-
# Similar to `Tuple#size`
692-
def size : NumberLiteral
693-
end
694-
695-
# Similar to `Tuple#[]`
696-
def [](index : NumberLiteral) : ASTNode
697-
end
698708
end
699709

700710
# A fictitious node representing a variable or instance

src/compiler/crystal/macros/interpreter.cr

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -427,34 +427,13 @@ module Crystal
427427

428428
def visit(node : Splat)
429429
node.exp.accept self
430-
@last = @last.interpret("argify", [] of ASTNode, nil, self)
430+
@last = @last.interpret("splat", [] of ASTNode, nil, self)
431431
false
432432
end
433433

434434
def visit(node : DoubleSplat)
435435
node.exp.accept self
436-
437-
last = @last
438-
case last
439-
when HashLiteral
440-
@last = MacroId.new(
441-
last.entries.join(", ") do |entry|
442-
"#{entry.key} => #{entry.value}"
443-
end
444-
)
445-
when NamedTupleLiteral
446-
@last = MacroId.new(
447-
last.entries.join(", ") do |entry|
448-
if Symbol.needs_quotes?(entry.key)
449-
"#{entry.key.inspect}: #{entry.value}"
450-
else
451-
"#{entry.key}: #{entry.value}"
452-
end
453-
end
454-
)
455-
else
456-
node.raise "argument to ** must be HashLiteral or NamedTuple, not #{last.class_desc}"
457-
end
436+
@last = @last.interpret("double_splat", [] of ASTNode, nil, self)
458437
false
459438
end
460439

src/compiler/crystal/macros/methods.cr

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,24 @@ module Crystal
686686
interpreter.accept block.body
687687
end
688688
}
689+
when "double_splat"
690+
case args.size
691+
when 0
692+
to_double_splat
693+
when 1
694+
interpret_one_arg_method(method, args) do |arg|
695+
if entries.empty?
696+
to_double_splat
697+
else
698+
unless arg.is_a?(Crystal::StringLiteral)
699+
arg.raise "argument to double_splat must be a StringLiteral, not #{arg.class_desc}"
700+
end
701+
to_double_splat(arg.value)
702+
end
703+
end
704+
else
705+
wrong_number_of_arguments "double_splat", args.size, 0..1
706+
end
689707
when "[]"
690708
case args.size
691709
when 1
@@ -721,6 +739,12 @@ module Crystal
721739
super
722740
end
723741
end
742+
743+
private def to_double_splat(trailing_string = "")
744+
MacroId.new(entries.join(", ") do |entry|
745+
"#{entry.key} => #{entry.value}"
746+
end + trailing_string)
747+
end
724748
end
725749

726750
class NamedTupleLiteral
@@ -751,6 +775,24 @@ module Crystal
751775
interpreter.accept block.body
752776
end
753777
}
778+
when "double_splat"
779+
case args.size
780+
when 0
781+
to_double_splat
782+
when 1
783+
interpret_one_arg_method(method, args) do |arg|
784+
if entries.empty?
785+
to_double_splat
786+
else
787+
unless arg.is_a?(Crystal::StringLiteral)
788+
arg.raise "argument to double_splat must be a StringLiteral, not #{arg.class_desc}"
789+
end
790+
to_double_splat(arg.value)
791+
end
792+
end
793+
else
794+
wrong_number_of_arguments "double_splat", args.size, 0..1
795+
end
754796
when "[]"
755797
case args.size
756798
when 1
@@ -803,6 +845,16 @@ module Crystal
803845
super
804846
end
805847
end
848+
849+
private def to_double_splat(trailing_string = "")
850+
MacroId.new(entries.join(", ") do |entry|
851+
if Symbol.needs_quotes?(entry.key)
852+
"#{entry.key.inspect}: #{entry.value}"
853+
else
854+
"#{entry.key}: #{entry.value}"
855+
end
856+
end + trailing_string)
857+
end
806858
end
807859

808860
class TupleLiteral
@@ -1760,9 +1812,21 @@ private def intepret_array_or_tuple_method(object, klass, method, args, block, i
17601812
interpreter.accept(block.body).truthy?
17611813
end)
17621814
end
1763-
when "argify"
1764-
object.interpret_argless_method(method, args) do
1815+
when "splat"
1816+
case args.size
1817+
when 0
17651818
Crystal::MacroId.new(object.elements.join ", ")
1819+
when 1
1820+
object.interpret_one_arg_method(method, args) do |arg|
1821+
if object.elements.empty?
1822+
Crystal::MacroId.new("")
1823+
else
1824+
unless arg.is_a?(Crystal::StringLiteral)
1825+
arg.raise "argument to splat must be a StringLiteral, not #{arg.class_desc}"
1826+
end
1827+
Crystal::MacroId.new((object.elements.join ", ") + arg.value)
1828+
end
1829+
end
17661830
end
17671831
when "empty?"
17681832
object.interpret_argless_method(method, args) { Crystal::BoolLiteral.new(object.elements.empty?) }

0 commit comments

Comments
 (0)