Newer
Older
#!/usr/bin/env bin/crystal --run
require "../../spec_helper"
describe "Code gen: macro" do
it "expands macro" do
run("macro foo; 1 + 2; end; foo").to_i.should eq(3)
end
it "expands macro with arguments" do
run(%(
macro foo(n)
{{n}} + 2
end
foo(1)
)).to_i.should eq(3)
end
it "expands macro that invokes another macro" do
run(%(
macro foo
def x
1 + 2
end
end
macro bar
foo
end
bar
x
)).to_i.should eq(3)
end
it "expands macro defined in class" do
foo
end
foo = Foo.new
foo.bar
end
it "expands macro defined in base class" do
end
class Foo
foo
end
foo = Foo.new
foo.bar
it "expands inline macro" do
run(%(
a = {{ 1 }}
a
)).to_i.should eq(1)
end
it "expands inline macro for" do
run(%(
a = 0
{% for i in [1, 2, 3] %}
{% end %}
a
)).to_i.should eq(6)
end
it "expands inline macro if (true)" do
run(%(
a = 0
{% if 1 == 1 %}
{% end %}
a
)).to_i.should eq(1)
end
it "expands inline macro if (false)" do
run(%(
a = 0
{% if 1 == 2 %}
{% end %}
it "finds macro in class" do
run(%(
class Foo
macro foo
1 + 2
end
def bar
foo
end
end
Foo.new.bar
)).to_i.should eq(3)
end
Ary Borenszweig
committed
it "expands def macro" do
run(%(
def bar_baz
1
end
Ary Borenszweig
committed
macro def foo : Int32
Ary Borenszweig
committed
bar_{{ "baz".id }}
Ary Borenszweig
committed
end
foo
)).to_i.should eq(1)
end
it "expands def macro with var" do
run(%(
Ary Borenszweig
committed
macro def foo : Int32
Ary Borenszweig
committed
a = {{ 1 }}
end
foo
)).to_i.should eq(1)
end
Ary Borenszweig
committed
it "expands def macro with @instance_vars" do
run(%(
class Foo
def initialize(@x)
end
Ary Borenszweig
committed
macro def to_s : String
Ary Borenszweig
committed
{{ @instance_vars.first.stringify }}
end
end
foo = Foo.new(1)
foo.to_s
Ary Borenszweig
committed
)).to_string.should eq("x")
Ary Borenszweig
committed
end
Ary Borenszweig
committed
it "expands def macro with @instance_vars with subclass" do
run(%(
class Reference
Ary Borenszweig
committed
macro def to_s : String
Ary Borenszweig
committed
{{ @instance_vars.last.stringify }}
end
end
class Foo
def initialize(@x)
end
end
class Bar < Foo
def initialize(@x, @y)
end
end
Bar.new(1, 2).to_s
Ary Borenszweig
committed
)).to_string.should eq("y")
Ary Borenszweig
committed
end
it "expands def macro with @instance_vars with virtual" do
Ary Borenszweig
committed
run(%(
class Reference
Ary Borenszweig
committed
macro def to_s : String
Ary Borenszweig
committed
{{ @instance_vars.last.stringify }}
end
end
class Foo
def initialize(@x)
end
end
class Bar < Foo
def initialize(@x, @y)
end
end
(Bar.new(1, 2) || Foo.new(1)).to_s
Ary Borenszweig
committed
)).to_string.should eq("y")
Ary Borenszweig
committed
end
Ary Borenszweig
committed
it "expands def macro with @class_name" do
Ary Borenszweig
committed
run(%(
class Foo
def initialize(@x)
end
Ary Borenszweig
committed
macro def to_s : String
Ary Borenszweig
committed
{{@class_name}}
Ary Borenszweig
committed
end
end
foo = Foo.new(1)
foo.to_s
)).to_string.should eq("Foo")
end
it "expands macro and resolves type correctly" do
run(%(
class Foo
Ary Borenszweig
committed
macro def foo : Int32
Ary Borenszweig
committed
1
end
end
class Bar < Foo
Int32 = 2
end
Bar.new.foo
)).to_i.should eq(1)
end
Ary Borenszweig
committed
it "expands def macro with @class_name with virtual" do
run(%(
class Reference
Ary Borenszweig
committed
macro def to_s : String
{{ @class_name }}
end
end
class Foo
end
class Bar < Foo
end
(Bar.new || Foo.new).to_s
)).to_string.should eq("Bar")
end
it "expands def macro with @class_name with virtual (2)" do
run(%(
class Reference
Ary Borenszweig
committed
macro def to_s : String
{{ @class_name }}
end
end
class Foo
end
class Bar < Foo
end
(Foo.new || Bar.new).to_s
)).to_string.should eq("Foo")
end
Ary Borenszweig
committed
it "allows overriding macro definition when redefining base class" do
run(%(
class Foo
Ary Borenszweig
committed
macro def inspect : String
Ary Borenszweig
committed
{{@class_name}}
Ary Borenszweig
committed
end
end
class Bar < Foo
end
class Foo
def inspect
"OH NO"
end
end
Bar.new.inspect
)).to_string.should eq("OH NO")
end
it "uses invocation context" do
run(%(
macro foo
def bar
Ary Borenszweig
committed
{{@class_name}}
end
end
class Foo
foo
end
Foo.new.bar
)).to_string.should eq("Foo")
end
it "allows macro with default arguments" do
run(%(
def bar
2
end
macro foo(x, y = :bar)
{{x}} + {{y.id}}
end
foo(1)
)).to_i.should eq(3)
end
it "expands def macro with instance var and method call (bug)" do
run(%(
struct Nil
def to_i
0
end
end
class Foo
Ary Borenszweig
committed
macro def foo : Int32
name = 1
@name = name
end
end
Foo.new.foo.to_i
)).to_i.should eq(1)
end
Ary Borenszweig
committed
it "expands @class_name in virtual metaclass (1)" do
Ary Borenszweig
committed
run(%(
class Class
Ary Borenszweig
committed
macro def to_s : String
Ary Borenszweig
committed
{{ @class_name }}
Ary Borenszweig
committed
end
end
class Foo
end
class Bar < Foo
end
p = Pointer(Foo.class).malloc(1_u64)
p.value = Bar
p.value = Foo
p.value.to_s
)).to_string.should eq("Foo:Class")
end
it "expands @class_name in virtual metaclass (2)" do
Ary Borenszweig
committed
run(%(
class Class
Ary Borenszweig
committed
macro def to_s : String
Ary Borenszweig
committed
{{ @class_name }}
Ary Borenszweig
committed
end
end
class Foo
end
class Bar < Foo
end
p = Pointer(Foo.class).malloc(1_u64)
p.value = Foo
p.value = Bar
p.value.to_s
)).to_string.should eq("Bar:Class")
end
it "doesn't skip abstract classes when defining macro methods" do
run(%(
class Object
Ary Borenszweig
committed
macro def foo : Int32
1
end
end
class Type
end
class ModuleType < Type
def foo
2
end
end
class Type1 < ModuleType
end
class Type2 < Type
end
t = Type1.new || Type2.new
t.foo
)).to_i.should eq(2)
end
it "doesn't reuse macro nodes (bug)" do
run(%(
def foo(x)
{% for y in [1, 2] %}
x + 1
{% end %}
end
foo 1
foo(1.5).to_i
)).to_i.should eq(2)
end
it "can use constants" do
run(%(
A = 1
{{ A }}
)).to_i.should eq(1)
end
it "can refer to types" do
run(%(
class Foo
def initialize(@x, @y)
end
end
Foo.new(1, 2)
{{ Foo.instance_vars.last.name }}
)).to_string.should eq("y")
end
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
it "runs macro with splat" do
run(%(
macro foo(*args)
{{args.length}}
end
foo 1, 1, 1
)).to_i.should eq(3)
end
it "runs macro with arg and splat" do
run(%(
macro foo(name, *args)
{{args.length}}
end
foo bar, 1, 1, 1
)).to_i.should eq(3)
end
it "runs macro with arg and splat in first position (1)" do
run(%(
macro foo(*args, name)
{{args.length}}
end
foo 1, 1, 1, bar
)).to_i.should eq(3)
end
it "runs macro with arg and splat in first position (2)" do
run(%(
macro foo(*args, name)
{{name}}
end
foo 1, 1, 1, "hello"
)).to_string.should eq("hello")
end
it "runs macro with arg and splat in the middle (1)" do
run(%(
macro foo(foo, *args, name)
{{args.length}}
end
foo x, 1, 1, 1, bar
)).to_i.should eq(3)
end
it "runs macro with arg and splat in the middle (2)" do
run(%(
macro foo(foo, *args, name)
{{foo}}
end
foo "yellow", 1, 1, 1, bar
)).to_string.should eq("yellow")
end
it "runs macro with arg and splat in the middle (3)" do
run(%(
macro foo(foo, *args, name)
{{name}}
end
foo "yellow", 1, 1, 1, "cool"
)).to_string.should eq("cool")
end
it "expands macro that yields" do
run(%(
def foo
{% for i in 0 .. 2 %}
yield {{i}}
{% end %}
end
a = 0
foo do |x|
a += x
end
a
)).to_i.should eq(3)
end
it "can refer to abstract (1)" do
run(%(
class Foo
end
{{ Foo.abstract? }}
)).to_b.should be_false
end
it "can refer to abstract (2)" do
run(%(
abstract class Foo
end
{{ Foo.abstract? }}
)).to_b.should be_true
end