Newer
Older
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
448
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
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
Ary Borenszweig
committed
it "can refer to @type" do
run(%(
class Foo
macro def foo : String
{{@type.name}}
end
end
Foo.new.foo
)).to_string.should eq("Foo")
end
it "receives &block" do
run(%(
macro foo(&block)
bar {{block}}
end
def bar
yield 1
end
foo do |x|
x + 1
end
)).to_i.should eq(2)
end
it "executes with named arguments" do
run(%(
macro foo(x = 1)
{{x}} + 1
end
foo x: 2
)).to_i.should eq(3)
end
Ary Borenszweig
committed
it "gets correct class name when there are classes in the middle" do
run(%(
class Foo
macro def class_desc : String
{{@class_name}}
end
end
class Bar < Foo
end
class Baz < Bar
end
class Qux < Bar
end
a = Pointer(Foo).malloc(1_u64)
a.value = Qux.new
a.value.class_desc
)).to_string.should eq("Qux")
end
it "transforms hooks (bug)" do
build(%(
module GC
def self.add_finalizer(object : T)
object.responds_to?(:finalize)
end
end
abstract class Foo
ALL = Pointer(Foo).malloc(1_u64)
macro inherited
ALL.value = new
end
end
class Bar < Foo
end
))
end
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
it "executs subclasses" do
run(%(
require "prelude"
class Foo
end
class Bar < Foo
end
class Baz < Foo
end
class Qux < Baz
end
names = {{ Foo.subclasses.map &.name }}
names.join("-")
)).to_string.should eq("Bar-Baz")
end
it "executs all_subclasses" do
run(%(
require "prelude"
class Foo
end
class Bar < Foo
end
class Baz < Bar
end
names = {{ Foo.all_subclasses.map &.name }}
names.join("-")
)).to_string.should eq("Bar-Baz")
end