Skip to content

Commit

Permalink
Add method to get both named and positional args
Browse files Browse the repository at this point in the history
  • Loading branch information
Blacksmoke16 committed Jul 24, 2019
1 parent 0fd6fd2 commit b49f8df
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 11 deletions.
80 changes: 75 additions & 5 deletions spec/compiler/semantic/annotation_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,78 @@ describe "Semantic: annotation" do
type.name.should eq("Foo")
end

describe "#args/named_args" do
describe "arguments" do
describe "#args" do
it "returns a NamedTupleLiteral with empty values if there are non defined" do
assert_type(%(
annotation Foo
end
@[Foo]
module Moo
end
{% if (args = Moo.annotation(Foo).args) && args["named"].empty? && args["positional"].empty? %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "returns an empty NamedTuple if there no named arguments defined" do
assert_type(%(
annotation Foo
end
@[Foo(1, "foo", true)]
module Moo
end
{% if (args = Moo.annotation(Foo).args) && args["named"].empty? && args["positional"] == {1, "foo", true} %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "returns an empty Tuple if there no positional arguments defined" do
assert_type(%(
annotation Foo
end
@[Foo(foo: "bar", "cat": 0..0)]
module Moo
end
{% if (args = Moo.annotation(Foo).args) && args["positional"].empty? && args["named"] == {foo: "bar", cat: 0..0} %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "returns a correctly with both types of args" do
assert_type(%(
annotation Foo
end
@[Foo(1, "foo", true, foo: "bar", "cat": 0..0)]
module Moo
end
{% if Moo.annotation(Foo).args == {named: {foo: "bar", cat: 0..0}, positional: {1, "foo", true}} %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end
end

describe "#pos_args" do
it "returns an empty TupleLiteral if there are none defined" do
assert_type(%(
annotation Foo
Expand All @@ -23,7 +93,7 @@ describe "Semantic: annotation" do
module Moo
end
{% if (args = Moo.annotation(Foo).args) && args.is_a? TupleLiteral && args.empty? %}
{% if (args = Moo.annotation(Foo).pos_args) && args.is_a? TupleLiteral && args.empty? %}
1
{% else %}
'a'
Expand All @@ -40,7 +110,7 @@ describe "Semantic: annotation" do
module Moo
end
{% if Moo.annotation(Foo).args == {1, "foo", true} %}
{% if Moo.annotation(Foo).pos_args == {1, "foo", true} %}
1
{% else %}
'a'
Expand Down Expand Up @@ -85,7 +155,7 @@ describe "Semantic: annotation" do
end
end

it "returns a correctly with both positional and named arguments" do
it "returns a correctly with #named_args and #pos_args" do
assert_type(%(
annotation Foo
end
Expand All @@ -94,7 +164,7 @@ describe "Semantic: annotation" do
module Moo
end
{% if Moo.annotation(Foo).args == {1, "foo", true} && Moo.annotation(Foo).named_args == {foo: "bar", cat: 0..0} %}
{% if Moo.annotation(Foo).pos_args == {1, "foo", true} && Moo.annotation(Foo).named_args == {foo: "bar", cat: 0..0} %}
1
{% else %}
'a'
Expand Down
7 changes: 6 additions & 1 deletion src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
Expand Up @@ -878,8 +878,13 @@ module Crystal::Macros
def [](name : SymbolLiteral | StringLiteral | MacroId) : ASTNode
end

# Returns a `NamedTupleLiteral(named: NamedTupleLiteral, positional: TupleLiteral)`
# representing the positional and named arguments on `self`.
def args : NamedTupleLiteral(named: NamedTupleLiteral, positional: TupleLiteral)
end

# Returns a `TupleLiteral` representing the positional arguments on `self`.
def args : TupleLiteral
def pos_args : TupleLiteral
end

# Returns a `NamedTupleLiteral` representing the named arguments on `self`.
Expand Down
24 changes: 19 additions & 5 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2106,16 +2106,22 @@ module Crystal
named_arg.try(&.value) || NilLiteral.new
end
when "args"
interpret_argless_method(method, args) do
named_args = get_named_annotation_args self
pos_args = TupleLiteral.new self.args

NamedTupleLiteral.new([
NamedTupleLiteral::Entry.new("named", named_args),
NamedTupleLiteral::Entry.new("positional", pos_args),
])
end
when "pos_args"
interpret_argless_method(method, args) do
TupleLiteral.new self.args
end
when "named_args"
interpret_argless_method(method, args) do
if named_args = self.named_args
NamedTupleLiteral.new(named_args.map { |arg| NamedTupleLiteral::Entry.new(arg.name, arg.value) })
else
NamedTupleLiteral.new
end
get_named_annotation_args self
end
else
super
Expand All @@ -2124,6 +2130,14 @@ module Crystal
end
end

private def get_named_annotation_args(object)
if named_args = object.named_args
Crystal::NamedTupleLiteral.new(named_args.map { |arg| Crystal::NamedTupleLiteral::Entry.new(arg.name, arg.value) })
else
Crystal::NamedTupleLiteral.new
end
end

private def interpret_array_or_tuple_method(object, klass, method, args, block, interpreter)
case method
when "any?"
Expand Down

0 comments on commit b49f8df

Please sign in to comment.