Skip to content

Commit

Permalink
enum outside libs. Similar to .Net's enum, allowing bitflags, to_s …
Browse files Browse the repository at this point in the history
…and methods on them
  • Loading branch information
Ary Borenszweig committed Nov 4, 2014
1 parent 04eefc5 commit 7e25c98
Show file tree
Hide file tree
Showing 23 changed files with 623 additions and 110 deletions.
12 changes: 6 additions & 6 deletions spec/compiler/codegen/c_enum_spec.cr
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
#!/usr/bin/env bin/crystal --run
require "../../spec_helper"

CodeGenEnumString = "lib Foo; enum Bar; X, Y, Z = 10, W; end end"
CodeGenCEnumString = "lib Foo; enum Bar; X, Y, Z = 10, W; end end"

describe "Code gen: enum" do
describe "Code gen: c enum" do
it "codegens enum value" do
run("#{CodeGenEnumString}; Foo::Bar::X").to_i.should eq(0)
run("#{CodeGenCEnumString}; Foo::Bar::X").to_i.should eq(0)
end

it "codegens enum value 2" do
run("#{CodeGenEnumString}; Foo::Bar::Y").to_i.should eq(1)
run("#{CodeGenCEnumString}; Foo::Bar::Y").to_i.should eq(1)
end

it "codegens enum value 3" do
run("#{CodeGenEnumString}; Foo::Bar::Z").to_i.should eq(10)
run("#{CodeGenCEnumString}; Foo::Bar::Z").to_i.should eq(10)
end

it "codegens enum value 4" do
run("#{CodeGenEnumString}; Foo::Bar::W").to_i.should eq(11)
run("#{CodeGenCEnumString}; Foo::Bar::W").to_i.should eq(11)
end

[
Expand Down
130 changes: 130 additions & 0 deletions spec/compiler/codegen/enum_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
require "../../spec_helper"

describe "Code gen: enum" do
it "codegens enum" do
run(%(
enum Foo
A = 1
end
Foo::A
)).to_i.should eq(1)
end

it "codegens enum without explicit value" do
run(%(
enum Foo
A
B
C
end
Foo::C
)).to_i.should eq(2)
end

it "codegens enum value" do
run(%(
enum Foo
A = 1
end
Foo::A.value
)).to_i.should eq(1)
end

it "creates enum from value" do
run(%(
enum Foo
A
B
end
Foo.new(1).value
)).to_i.should eq(1)
end

it "codegens enum bitflags (1)" do
run(%(
@[Flags]
enum Foo
A
end
Foo::A
)).to_i.should eq(1)
end

it "codegens enum bitflags (2)" do
run(%(
@[Flags]
enum Foo
A
B
end
Foo::B
)).to_i.should eq(2)
end

it "codegens enum bitflags (4)" do
run(%(
@[Flags]
enum Foo
A
B
C
end
Foo::C
)).to_i.should eq(4)
end

it "codegens enum bitflags None" do
run(%(
@[Flags]
enum Foo
A
end
Foo::None
)).to_i.should eq(0)
end

it "codegens enum bitflags All" do
run(%(
@[Flags]
enum Foo
A
B
C
end
Foo::All
)).to_i.should eq(1 + 2 + 4)
end

it "codegens enum None redefined" do
run(%(
@[Flags]
enum Foo
A
None = 10
end
Foo::None
)).to_i.should eq(10)
end

it "codegens enum All redefined" do
run(%(
@[Flags]
enum Foo
A
All = 10
end
Foo::All
)).to_i.should eq(10)
end
end
13 changes: 11 additions & 2 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,8 @@ describe "Parser" do
it_parses "lib C; struct Foo; x : Int**; end end", LibDef.new("C", [StructDef.new("Foo", [Arg.new("x", restriction: "Int".path.pointer_of.pointer_of)])] of ASTNode)
it_parses "lib C; struct Foo; x, y, z : Int; end end", LibDef.new("C", [StructDef.new("Foo", [Arg.new("x", restriction: "Int".path), Arg.new("y", restriction: "Int".path), Arg.new("z", restriction: "Int".path)])] of ASTNode)
it_parses "lib C; union Foo; end end", LibDef.new("C", [UnionDef.new("Foo")] of ASTNode)
it_parses "lib C; enum Foo; A\nB, C\nD = 1; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)])] of ASTNode)
it_parses "lib C; enum Foo; A = 1, B; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A", 1.int32), Arg.new("B")])] of ASTNode)
it_parses "lib C; enum Foo; A\nB, C\nD = 1; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)] of ASTNode)] of ASTNode)
it_parses "lib C; enum Foo; A = 1, B; end end", LibDef.new("C", [EnumDef.new("Foo", [Arg.new("A", 1.int32), Arg.new("B")] of ASTNode)] of ASTNode)
it_parses "lib C; enum Foo < UInt16; end end", LibDef.new("C", [EnumDef.new("Foo", base_type: "UInt16".path)] of ASTNode)
it_parses "lib C; Foo = 1; end", LibDef.new("C", [Assign.new("Foo".path, 1.int32)] of ASTNode)
it_parses "lib C\nfun getch = GetChar\nend", LibDef.new("C", [FunDef.new("getch", real_name: "GetChar")] of ASTNode)
Expand Down Expand Up @@ -869,6 +869,15 @@ describe "Parser" do
it_parses %("hello \#{1}" \\\n "\#{2} world"), StringInterpolation.new(["hello ".string, 1.int32, 2.int32, " world".string] of ASTNode)
assert_syntax_error %("foo" "bar")

it_parses "enum Foo; A\nB, C\nD = 1; end", EnumDef.new("Foo", [Arg.new("A"), Arg.new("B"), Arg.new("C"), Arg.new("D", 1.int32)] of ASTNode)
it_parses "enum Foo; A = 1, B; end", EnumDef.new("Foo", [Arg.new("A", 1.int32), Arg.new("B")] of ASTNode)
it_parses "enum Foo < UInt16; end", EnumDef.new("Foo", base_type: "UInt16".path)
it_parses "enum Foo : UInt16; end", EnumDef.new("Foo", base_type: "UInt16".path)
it_parses "enum Foo; def foo; 1; end; end", EnumDef.new("Foo", [Def.new("foo", body: 1.int32)] of ASTNode)
it_parses "enum Foo; A = 1\ndef foo; 1; end; end", EnumDef.new("Foo", [Arg.new("A", 1.int32), Def.new("foo", body: 1.int32)] of ASTNode)
it_parses "enum Foo; A = 1\ndef foo; 1; end\ndef bar; 2; end\nend", EnumDef.new("Foo", [Arg.new("A", 1.int32), Def.new("foo", body: 1.int32), Def.new("bar", body: 2.int32)] of ASTNode)
it_parses "enum Foo; A = 1\ndef self.foo; 1; end\nend", EnumDef.new("Foo", [Arg.new("A", 1.int32), Def.new("foo", receiver: "self".var, body: 1.int32)] of ASTNode)

%w(def macro class struct module fun alias abstract include extend lib).each do |keyword|
assert_syntax_error "def foo\n#{keyword}\nend", Def.new("foo", body: keyword.call)
end
Expand Down
2 changes: 1 addition & 1 deletion spec/compiler/type_inference/c_enum_spec.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env bin/crystal --run
require "../../spec_helper"

describe "Type inference: enum" do
describe "Type inference: c enum" do
it "types enum value" do
assert_type("lib Foo; enum Bar; X, Y, Z = 10, W; end; end; Foo::Bar::X") { int32 }
end
Expand Down
185 changes: 185 additions & 0 deletions spec/compiler/type_inference/enum_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
require "../../spec_helper"

describe "Type inference: enum" do
it "types enum" do
assert_type(%(
enum Foo
A = 1
end
Foo::A
)) { types["Foo"] }
end

it "types enum value" do
assert_type(%(
enum Foo
A = 1
end
Foo::A.value
)) { int32 }
end

it "disallows implicit conversion of int to enum" do
assert_error %(
enum Foo
A = 1
end
def foo(x : Foo)
end
foo 1
), "mno overload matches 'foo' with types Int32"
end

it "finds method in enum type" do
assert_type(%(
struct Enum
def foo
1
end
end
enum Foo
A = 1
end
Foo::A.foo
)) { int32 }
end

it "finds class method in enum type" do
assert_type(%(
struct Enum
def self.foo
1
end
end
enum Foo
A = 1
end
Foo.foo
)) { int32 }
end

it "errors if using a name twice" do
assert_error %(
enum Foo
A
A
end
),
"enum 'Foo' already contains a member named 'A'"
end

it "creates enum from value" do
assert_type(%(
enum Foo
A
B
end
Foo.new(1)
)) { types["Foo"] }
end

it "defines method on enum" do
assert_type(%(
enum Foo
A
B
def foo
1
end
end
Foo::A.foo
)) { int32 }
end

it "defines class method on enum" do
assert_type(%(
enum Foo
A
B
def self.foo
1
end
end
Foo.foo
)) { int32 }
end

it "reopens an enum" do
assert_type(%(
enum Foo
A
B
end
enum Foo
def foo
1
end
end
Foo::A.foo
)) { int32 }
end

it "errors if reopen but not enum" do
assert_error %(
class Foo
end
enum Foo
A
B
end
),
"Foo is not a enum, it's a class"
end

it "errors if reopen and tries to define constant" do
assert_error %(
enum Foo
A
B
end
enum Foo
C
end
),
"can't reopen enum and add more constants to it"
end

it "has None value when defined as @[Flags]" do
assert_type(%(
@[Flags]
enum Foo
A
B
end
Foo::None.value
)) { int32 }
end

it "has All value when defined as @[Flags]" do
assert_type(%(
@[Flags]
enum Foo
A
B
end
Foo::All.value
)) { int32 }
end
end
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/cast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
union_ptr
end

def upcast_distinct(value, to_type : CEnumType, from_type : Type)
def upcast_distinct(value, to_type : EnumType, from_type : Type)
value
end

Expand Down
Loading

0 comments on commit 7e25c98

Please sign in to comment.