2

Given are the following Swift protocols


public protocol Symbol {
    associatedtype Meaning
}

public protocol Interpretation : Symbol {}

public protocol Program : Symbol where Meaning == Body.Meaning {
    
    associatedtype Body : Symbol
    var body : Body {get}
    
}

Interpretation is just a marker protocol that I need for some other code generation. The code generation problem at hand is: Make every type implementing Program conform to every protocol that Body conforms to. Ideally, I would also like to restrict this "conform to every protocol" thing to only protocols inheriting from Interpretation. For simplicity, I restrict myself to protocols that only consist of instance variables and instance methods without input arguments.

Here is what I tried:


{% for type in types.based.Program|public %}

extension {{type.name}} : Interpretation {}

{% for proto in types.protocols|public %}{% if type.Body.based.{{proto.name}} %}
extension {{type.name}} : {{proto.name}} {

{% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        body.{{variable.name}}
    }
{%endfor%}
{% for method in proto.methods|instance %}
        
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        body.{{method.name}}
    }
{%endfor%}

}

{% endif %}{% endfor %}{% endfor %}

Here's my test types:



public protocol ArrayInterpretable : Interpretation {
    
    var asArray : Array<Meaning> {get}
    
}


public struct Nil<Meaning> : ArrayInterpretable {
    
    public var asArray: [Meaning] {
        []
    }
    
}

public struct Bar<T> : Program {

    public typealias Meaning = T
    public typealias Body = Nil<T>
    
    public let body = Nil<T>()
    
}

Here is what I get:

// Generated using Sourcery 1.6.0 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT



extension Bar : Interpretation {}


I would have expected to get automatic conformance of Bar to ArrayInterpretable through codegen. How to fix this?

  • Sorry but I saved your swift code as `a.swift` and your template as `a.swifttemplate` and run `sourcery --sources a.swift --templates a.swifttemplate --output b.swift` and got almost the same content of `b.swift` as `a.swifttemplate`. – Medo Paw Nov 13 '21 at 15:02
  • This is a stencil template, so I guess you'd have to save ```a.swifttemplate``` as ```a.stencil``` – MKasperczyk Nov 13 '21 at 18:01
  • I found that in line 5 `type.Body` is empty, which means that if you write `{% if type.Body %}` there's still nothing printed. And you can get the `Body` type using `type.typealiases.Body`, which prints `Typealias: aliasName = Body, typeName = Nil, module = nil, accessLevel = public, parentName = Optional("Bar"), name = Bar.Body`. But `{% if type.typealiases.Body.based.{{proto.name}} %}` or `{% if type.typealiases.Body.based.ArrayInterpretable %}` doesn't work either. Hope this helps. – Medo Paw Nov 14 '21 at 14:58

1 Answers1

0

I figured out a hack that seems to work, even though it is cringe on multiple levels.

import Free

{% for type in types.based.Program | public %}

extension {{type.name}} : Interpretation {}

{% for v in type.variables %}{%if v.name == "body"%}
{%for proto in types.protocols|public%}
{%if proto.based.Interpretation%}
{%for prt in v.type.basedTypes%}
{%if prt == proto.name %}
extension {{type.name}} : {{proto.name}} {

{% for variable in proto.variables|instance %}
    @inlinable
    public var {{variable.name}} : {{variable.typeName}} {
        body.{{variable.name}}
    }
{%endfor%}
{% for method in proto.methods|instance %}
    @inlinable
    public func {{method.name}} -> {{method.returnTypeName}} {
        body.{{method.name}}
    }
{%endfor%}

}

{%endif%}{%endfor%}{%endif%}{%endfor%}
{%endif%}
{% endfor %}{% endfor %}
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77