Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement pre-defining structs/unions #71

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions spec/lib_body_transformer_spec.cr
Expand Up @@ -261,4 +261,18 @@ describe LibBodyTransformer do
end
fun some_struct_with_other_struct_pointer(handle : StructBar*)
)

# Verify that an additional struct does not get generated
assert_transform "simple",
%(
struct SomeStruct1
x : LibC::Int
end
fun just_some_struct_1
), %(
struct SomeStruct1
x : LibC::Int
end
fun just_some_struct_1 : SomeStruct1
)
end
9 changes: 9 additions & 0 deletions src/crystal_lib/lib_body_transformer.cr
Expand Up @@ -81,6 +81,15 @@ class CrystalLib::LibBodyTransformer < Crystal::Transformer
check_pending_definitions(node)
end

def transform(node : Crystal::CStructOrUnionDef)
@mapper.predefined_structs_or_unions << node

# There will be no pending definitions, since the node will
# be skipped and replaced with the current one when going through
# its usage in the header
node
end

def map_type(type)
@mapper.map(type)
end
Expand Down
25 changes: 17 additions & 8 deletions src/crystal_lib/type_mapper.cr
Expand Up @@ -5,13 +5,15 @@ class CrystalLib::TypeMapper
original_name : String

getter pending_definitions
getter predefined_structs_or_unions

@typedef_name : String?

def initialize(@prefix_matcher : PrefixMatcher? = nil)
@pending_definitions = [] of Crystal::ASTNode
@pending_structs = [] of PendingStruct
@generated = {} of UInt64 => Crystal::ASTNode
@predefined_structs_or_unions = [] of Crystal::CStructOrUnionDef

# When completing a struct's fields we keep that struct and the field name in
# case we find a nested struct, such as in:#
Expand Down Expand Up @@ -163,18 +165,25 @@ class CrystalLib::TypeMapper
untouched_struct_name = check_anonymous_name(type.unscoped_name)
struct_name = crystal_type_name(untouched_struct_name)

if type.fields.empty?
# For an empty struct we just return an alias to Void
struct_def = Crystal::Alias.new(path(struct_name), path(["Void"])).tap(&.doc = type.doc)
# Try finding a struct or union that has already been defined. If such
# a struct/union exist, use their definition instead and don't insert anything
found_predefined = @predefined_structs_or_unions.find { |predefined| predefined.name == struct_name }
if found_predefined
struct_def = found_predefined
else
struct_def = Crystal::CStructOrUnionDef.new(struct_name, union: type.kind == :union).tap(&.doc = type.doc)
if type.fields.empty?
# For an empty struct we just return an alias to Void
struct_def = Crystal::Alias.new(path(struct_name), path(["Void"])).tap(&.doc = type.doc)
else
struct_def = Crystal::CStructOrUnionDef.new(struct_name, union: type.kind == :union).tap(&.doc = type.doc)

# Leave struct body for later, because of possible recursiveness
@pending_structs << PendingStruct.new(struct_def, type, untouched_struct_name)
end

# Leave struct body for later, because of possible recursiveness
@pending_structs << PendingStruct.new(struct_def, type, untouched_struct_name)
@pending_definitions << struct_def unless @generated.has_key?(type.object_id)
end

@pending_definitions << struct_def unless @generated.has_key?(type.object_id)

path(struct_name)
end

Expand Down