Skip to content

Commit

Permalink
added inheritance of fields having parameterized type; parametric int…
Browse files Browse the repository at this point in the history
…erface declarations are not supported yet
  • Loading branch information
mind6 committed Aug 9, 2024
1 parent 86ab94d commit 6586c75
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 21 deletions.
18 changes: 13 additions & 5 deletions src/Inherit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ const H_FLAGS::Symbol = :__Inherit_jl_FLAGS

const E_SUMMARY_LEVEL = "INHERIT_JL_SUMMARY_LEVEL"

SymbolOrExpr = Union{Symbol, Expr}

#super type identifier
TypeIdentifier = @NamedTuple{
modulefullname::Tuple, #module where the supertype was originally defined
basename::Symbol} #name of the supertype

TypeSpec = @NamedTuple{
ismutable::Bool, #whether or not the fields of this type are mutable
typeparams::Vector{Expr}, #expressions that define the type parameters, including those inherited from supertype
typeparams::Vector{SymbolOrExpr}, #expressions that define the type parameters, including those inherited from supertype
fields::Vector{Expr} #expressions that define the type's fields (including those inherited from supertype)
}

Expand Down Expand Up @@ -207,7 +209,7 @@ macro abstractbase(ex)

function reset_type()
DBSPEC[T] = TypeSpec((ismutable,
P === nothing ? Vector{Expr}() : Vector{Expr}(P), # copy P into an Expr array, if P exists
Vector{SymbolOrExpr}(), #start with empty array, collect super class types first
Vector{Expr}()))
DBM[identT] = Vector{MethodDeclaration}()
DBS[identT] = Vector{Symbol}()
Expand All @@ -227,8 +229,11 @@ macro abstractbase(ex)
append!(specT.fields, specS.fields)
append!(DBM[identT], U_DBM[identS]) #but the original `line` may still be referring to the original module
end
if P !== nothing append!(DBSPEC[T].typeparams, P) end #add any type params from this struct after the supertype's params have been added

nothing
end

ret=reset_type()
if ret !== nothing
return ret
Expand Down Expand Up @@ -555,10 +560,13 @@ macro implement(ex)
# add the type parameters if they exist
if !isempty(specS.typeparams) || P !== nothing
if P === nothing
P = Vector{Expr}()
P = Vector{SymbolOrExpr}()
else
P = Vector{SymbolOrExpr}(P) #convert the type from Vector{Any} output by MacroTools, to emphasize what we're expecting.
end
append!(P, specS.typeparams)
ex.args[2].args[2] = Expr(:curly, T, P...)
prepend!(P, specS.typeparams)
# ex.args[2].args[2] = Expr(:curly, T, P...)
ex = replace_parameterized_type(ex, T, P)
end
# add the fields for the supertype to the front of list for derived type
prepend!(ex.args[3].args, specS.fields)
Expand Down
19 changes: 19 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,25 @@ function reducetype(expr::Expr, basemodule::NTuple{N, Symbol}, basetype::Symbol,
end, expr)
end

"""
Given an expression that looks like `struct <typename> ... end` or `struct <typename>{...} ... end`, modify the expression into `struct S{<params[1]>, <params[2]>, ...} ... end`
Note that <typename> is only expected to be found in one of the positions above. We process only the first occurrence of <typename>. If the input expression is not of the expected form the expression returned may be gibberish syntatically. We accept this limitation for simplicity while traversing different types of struct definitions.
"""
function replace_parameterized_type(ex::Expr, typename::Symbol, params::Vector{SymbolOrExpr})
replaced = false
MacroTools.prewalk(x->begin
# global replaced
if !replaced && (@capture(x, T_Symbol) || @capture(x, T_Symbol{__}))
if T == typename
replaced = true
return Expr(:curly, T, params...)
end
end
x
end, ex)
end

function make_variable_tupletype(curmodule::Module, types::Type ...)
curmodule.eval(:(Tuple{$(types...)}))
end
Expand Down
41 changes: 25 additions & 16 deletions test/testparametricstructs.jl
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
# module M20
# using Inherit, Test
module M20
using Inherit, Test

# @abstractbase struct Fruit{T}
# size::T
# end
# @implement struct Apple <: Fruit
# end
# end

ex = quote
struct Fruit{T <: Real, T2}
@abstractbase struct Fruit{T}
size::T
end
end
ex2 = quote
struct Fruit
size::T
@implement struct Apple <: Fruit
end
@implement struct Orange{U} <: Fruit
weight::U
end

@abstractbase struct NiceFruit{K} <: Fruit
price::K
end

@implement struct Strawberry{L} <: NiceFruit
location::L
end

@testset "inheriting parametric fields" begin
a = Apple(Int(1))
b = Orange(Int(2), Float32(3))
c = Strawberry("large", Float64(3.2), "far away")
@show c
@test collect(typeof(a).parameters) == [Int]
@test collect(typeof(b).parameters) == [Int, Float32]
@test collect(typeof(c).parameters) == [String, Float64, String]
end
end
# @capture(ex, struct T_Symbol{P__} lines__ end)
17 changes: 17 additions & 0 deletions test/testutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,23 @@ end
@test !@capture(:(function cost end), function name_(__) end)
@test !@capture(:(function cost() nothing end), function name_(__) end)
end

@testset "replace_parameterized_type" begin
ex1 = quote
struct Apple <: Fruit
end
end
ex2 = quote
struct Apple{T} <: Fruit
end
end

P = Inherit.SymbolOrExpr[:(T1 <: T2), :T3]
for ex in [ex1, ex2]
res = Inherit.replace_parameterized_type(ex, :Apple, P)
@test @capture(res, struct Apple{T1<:T2, T3} <: Fruit end)
end
end
# Inherit.reducetype(:(function f(::Vector{<:M1.Fruit}) end), (:Main, :M1), :Fruit, (:Main, :M2), :Orange)
# @capture( :( f(x::Vector{<:Type{<:Fruit}}) ), f(P_:: A_{<: T_} ) ); @show P A T
# @capture( :( f(x::M1.M1.Fruit) ), f(_::m__.T_ ) )
Expand Down

0 comments on commit 6586c75

Please sign in to comment.