Improve new stdlib generator DSL

- Add Kotlin/Native platform.
- Rename classes.
- Introduce TemplateGroup and TemplateGroupBase to group template values.
- MemberBuilder: add support for more properties.
This commit is contained in:
Ilya Gorbunov
2017-11-02 07:25:28 +03:00
parent e68a6651d2
commit 4404257329
6 changed files with 123 additions and 103 deletions
@@ -6,6 +6,7 @@ sourceSets {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$bootstrapKotlinVersion"
compile "org.jetbrains.kotlin:kotlin-reflect:$bootstrapKotlinVersion"
}
compileKotlin {
@@ -83,7 +83,14 @@ enum class Inline {
enum class Platform {
Common,
JVM,
JS
JS,
Native;
val fullName get() = "Kotlin/$name"
companion object {
val values = values().toList()
}
}
enum class SequenceClass {
@@ -62,8 +62,8 @@ class MemberBuilder(
var operator: Boolean = false; private set
val typeParams = mutableListOf<String>()
var customReceiver: String? = null; private set
var receiverAsterisk: Boolean = false; private set
var toNullableT: Boolean = false; private set
var receiverAsterisk: Boolean = false // TODO: rename to genericStarProjection
var toNullableT: Boolean = false
var returns: String? = null; private set
var body: String? = null; private set
@@ -72,8 +72,8 @@ class MemberBuilder(
fun sourceFile(file: SourceFile) { sourceFile = file }
fun deprecate(value: Deprecation) { deprecate = value }
fun deprecate(value: String) { deprecate = Deprecation(value)
}
fun deprecate(value: String) { deprecate = Deprecation(value) }
fun since(value: String) { since = value }
fun platformName(name: String) { platformName = name }
fun visibility(value: String) { visibility = value }
@@ -87,10 +87,11 @@ class MemberBuilder(
annotation("""@Suppress("NOTHING_TO_INLINE")""")
}
}
fun inlineOnly() { inline = Inline.Only
}
fun inlineOnly() { inline = Inline.Only }
fun receiver(value: String) { customReceiver = value }
@Deprecated("Use receiver()", ReplaceWith("receiver(value)"))
fun customReceiver(value: String) = receiver(value)
fun signature(value: String) { signature = value }
fun returns(type: String) { returns = type }
@@ -113,6 +114,12 @@ class MemberBuilder(
fun body(valueBuilder: () -> String) {
body = valueBuilder()
}
fun body(f: Family, valueBuilder: () -> String) {
specialFor(f) { body(valueBuilder) }
}
fun body(vararg families: Family, valueBuilder: () -> String) {
specialFor(*families) { body(valueBuilder) }
}
fun on(platform: Platform, action: () -> Unit) {
@@ -128,6 +135,11 @@ class MemberBuilder(
if (family == f)
action()
}
fun specialFor(vararg families: Family, action: () -> Unit) {
require(families.isNotEmpty())
if (family in families)
action()
}
fun build(builder: Appendable) {
@@ -296,8 +308,8 @@ class MemberBuilder(
listOfNotNull(
visibility ?: "public",
"header".takeIf { headerOnly },
"impl".takeIf { isImpl },
"expect".takeIf { headerOnly },
"actual".takeIf { isImpl },
"external".takeIf { external },
"inline".takeIf { inline.isInline() },
"infix".takeIf { infix },
@@ -325,7 +337,7 @@ class MemberBuilder(
val body = (body ?:
deprecate?.replaceWith?.let { "return $it" } ?:
throw RuntimeException("No body specified for $signature for ${family to primitive}")
throw RuntimeException("$signature for ${platform.fullName}: no body specified for ${family to primitive}")
).trim('\n')
val indent: Int = body.takeWhile { it == ' ' }.length
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package templates
import kotlin.coroutines.experimental.buildSequence
import kotlin.reflect.KTypeProjection
import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubtypeOf
typealias TemplateGroup = () -> Sequence<MemberTemplate>
fun templateGroupOf(vararg templates: MemberTemplate): TemplateGroup = { templates.asSequence() }
abstract class TemplateGroupBase : TemplateGroup {
override fun invoke(): Sequence<MemberTemplate> = buildSequence {
with(this@TemplateGroupBase) {
this::class.members.filter { it.name.startsWith("f_") }.forEach {
require(it.parameters.size == 1) { "Member $it violates naming convention" }
when {
it.returnType.isSubtypeOf(typeMemberTemplate) ->
yield(it.call(this) as MemberTemplate)
it.returnType.isSubtypeOf(typeIterableOfMemberTemplates) ->
@Suppress("UNCHECKED_CAST")
yieldAll(it.call(this) as Iterable<MemberTemplate>)
else ->
error("Member $it violates naming convention")
}
}
}
}.run {
if (defaultActions.isEmpty()) this else onEach { t -> defaultActions.forEach(t::builder) }
}
private val defaultActions = mutableListOf<MemberBuildAction>()
fun defaultBuilder(builderAction: MemberBuildAction) {
defaultActions += builderAction
}
companion object {
private val typeMemberTemplate = MemberTemplate::class.createType()
private val typeIterableOfMemberTemplates = Iterable::class.createType(arguments = listOf(KTypeProjection.invariant(typeMemberTemplate)))
}
}
@@ -16,7 +16,7 @@
package templates
import kotlin.coroutines.experimental.buildSequence
import kotlin.coroutines.experimental.*
@DslMarker
annotation class TemplateDsl
@@ -37,26 +37,26 @@ private fun def(signature: String, memberKind: Keyword): MemberBuildAction = {
fun fn(defaultSignature: String): MemberBuildAction = def(defaultSignature, Keyword.Function)
fun fn(defaultSignature: String, setup: FamilyPrimitiveMemberDsl.() -> Unit): MemberTemplate =
FamilyPrimitiveMemberDsl().apply {
fun fn(defaultSignature: String, setup: FamilyPrimitiveMemberDefinition.() -> Unit): FamilyPrimitiveMemberDefinition =
FamilyPrimitiveMemberDefinition().apply {
builder(fn(defaultSignature))
setup()
}
fun MemberBuildAction.byTwoPrimitives(setup: PairPrimitiveMemberDsl.() -> Unit): MemberTemplate =
PairPrimitiveMemberDsl().apply {
fun MemberBuildAction.byTwoPrimitives(setup: PairPrimitiveMemberDefinition.() -> Unit): PairPrimitiveMemberDefinition =
PairPrimitiveMemberDefinition().apply {
builder(this@byTwoPrimitives)
setup()
}
fun pval(name: String, setup: FamilyPrimitiveMemberDsl.() -> Unit): MemberTemplate =
FamilyPrimitiveMemberDsl().apply {
fun pval(name: String, setup: FamilyPrimitiveMemberDefinition.() -> Unit): FamilyPrimitiveMemberDefinition =
FamilyPrimitiveMemberDefinition().apply {
builder(def(name, Keyword.Value))
setup()
}
fun pvar(name: String, setup: FamilyPrimitiveMemberDsl.() -> Unit): MemberTemplate =
FamilyPrimitiveMemberDsl().apply {
fun pvar(name: String, setup: FamilyPrimitiveMemberDefinition.() -> Unit): FamilyPrimitiveMemberDefinition =
FamilyPrimitiveMemberDefinition().apply {
builder(def(name, Keyword.Variable))
setup()
}
@@ -66,14 +66,16 @@ interface MemberTemplate {
/** Specifies which platforms this member template should be generated for */
fun platforms(vararg platforms: Platform)
fun instantiate(): Sequence<MemberBuilder>
fun instantiate(platforms: List<Platform> = Platform.values): Sequence<MemberBuilder>
/** Registers parameterless member builder function */
fun builder(b: MemberBuildAction)
}
abstract class GenericMemberDsl<TParam> : MemberTemplate {
infix fun <MT: MemberTemplate> MT.builder(b: MemberBuildAction): MT = apply { builder(b) }
infix fun <TParam, MT : MemberTemplateDefinition<TParam>> MT.builderWith(b: MemberBuildActionP<TParam>): MT = apply { builderWith(b) }
abstract class MemberTemplateDefinition<TParam> : MemberTemplate {
sealed class BuildAction {
class Generic(val action: MemberBuildAction) : BuildAction() {
@@ -87,7 +89,7 @@ abstract class GenericMemberDsl<TParam> : MemberTemplate {
}
}
val buildActions = mutableListOf<BuildAction>()
private val buildActions = mutableListOf<BuildAction>()
private var targetPlatforms = setOf(*Platform.values())
override fun platforms(vararg platforms: Platform) {
@@ -116,13 +118,15 @@ abstract class GenericMemberDsl<TParam> : MemberTemplate {
} ?: this
override fun instantiate(): Sequence<MemberBuilder> {
val specificPlatforms by lazy { targetPlatforms - Platform.Common }
override fun instantiate(platforms: List<Platform>): Sequence<MemberBuilder> {
val resultingPlatforms = platforms.intersect(targetPlatforms)
val specificPlatforms by lazy { resultingPlatforms - Platform.Common }
fun platformMemberBuilders(family: Family, p: TParam) =
if (Platform.Common in targetPlatforms) {
val commonMemberBuilder = createMemberBuilder(Platform.Common, family, p)
mutableListOf(commonMemberBuilder).also { builders ->
mutableListOf<MemberBuilder>().also { builders ->
if (Platform.Common in resultingPlatforms) builders.add(commonMemberBuilder)
if (commonMemberBuilder.hasPlatformSpecializations) {
specificPlatforms.mapTo(builders) {
createMemberBuilder(it, family, p)
@@ -130,7 +134,7 @@ abstract class GenericMemberDsl<TParam> : MemberTemplate {
}
}
} else {
targetPlatforms.map { createMemberBuilder(it, family, p) }
resultingPlatforms.map { createMemberBuilder(it, family, p) }
}
return parametrize()
@@ -152,17 +156,20 @@ abstract class GenericMemberDsl<TParam> : MemberTemplate {
}
private fun defaultPrimitives(f: Family): Set<PrimitiveType> =
if (f.isPrimitiveSpecialization) PrimitiveType.defaultPrimitives else emptySet()
@TemplateDsl
class FamilyPrimitiveMemberDsl : GenericMemberDsl<PrimitiveType?>() {
class FamilyPrimitiveMemberDefinition : MemberTemplateDefinition<PrimitiveType?>() {
private val familyPrimitives = mutableMapOf<Family, Set<PrimitiveType>>()
private val familyPrimitives = mutableMapOf<Family, Set<PrimitiveType?>>()
fun include(vararg fs: Family) {
for (f in fs) familyPrimitives[f] = defaultPrimitives(f)
}
@Deprecated("Use include()", ReplaceWith("include(*fs)"))
fun only(vararg fs: Family) = include(*fs)
fun include(fs: Collection<Family>) {
for (f in fs) familyPrimitives[f] = defaultPrimitives(f)
@@ -172,7 +179,7 @@ class FamilyPrimitiveMemberDsl : GenericMemberDsl<PrimitiveType?>() {
include(Family.defaultFamilies)
}
fun include(f: Family, primitives: Set<PrimitiveType>) {
fun include(f: Family, primitives: Set<PrimitiveType?>) {
familyPrimitives[f] = primitives
}
@@ -198,7 +205,7 @@ class FamilyPrimitiveMemberDsl : GenericMemberDsl<PrimitiveType?>() {
}
@TemplateDsl
class PairPrimitiveMemberDsl : GenericMemberDsl<Pair<PrimitiveType, PrimitiveType>>() {
class PairPrimitiveMemberDefinition : MemberTemplateDefinition<Pair<PrimitiveType, PrimitiveType>>() {
private val familyPrimitives = mutableMapOf<Family, Set<Pair<PrimitiveType, PrimitiveType>>>()
@@ -217,77 +224,9 @@ class PairPrimitiveMemberDsl : GenericMemberDsl<Pair<PrimitiveType, PrimitiveTyp
}
}
/*
val t_copyOfResized = MemberTemplatePar<DefaultParametrization>().apply {
parametrization = buildSequence<DefaultParametrization> {
val allPlatforms = setOf(*Platform.values())
yield(DefaultParametrization(InvariantArraysOfObjects, platforms = allPlatforms))
yieldAll(PrimitiveType.defaultPrimitives.map { DefaultParametrization(ArraysOfPrimitives, it, platforms = allPlatforms) })
}
builder = { p, platform, builder ->
builder.family = if (p.family == InvariantArraysOfObjects && platform == Platform.JS)
ArraysOfObjects else p.family
if (platform == Platform.JVM)
builder.inline = Inline.Only
builder.doc = "Returns new array which is a copy of the original array."
builder.returns = "SELF"
if (platform == Platform.JS && p.family == ArraysOfObjects)
builder.returns = "Array<T>"
if (platform == Platform.JVM) {
builder.body = "return java.util.Arrays.copyOf(this, size)"
} else if (platform == Platform.JS) {
when (p.primitive) {
null ->
builder.body = "return this.asDynamic().slice()"
PrimitiveType.Char, PrimitiveType.Boolean, PrimitiveType.Long ->
builder.body = "return withType(\"${p.primitive}Array\", this.asDynamic().slice())"
else -> {
builder.annotations += """@Suppress("NOTHING_TO_INLINE")"""
builder.inline = Inline.Yes
builder.body = "return this.asDynamic().slice()"
}
}
}
}
}
interface MemberTemplate {
fun instantiate(): Sequence<MemberInstance>
}
class MemberTemplatePar<TParametrization : Parametrization> : MemberTemplate {
val keyword: String = "fun"
lateinit var parametrization: Sequence<TParametrization>
lateinit var builder: (TParametrization, Platform, MemberBuilder) -> Unit
override fun instantiate(): Sequence<MemberInstance> =
parametrization.flatMap {
it.platforms.asSequence().map { p ->
val memberBuilder = MemberBuilder().apply {
builder(it, p, this)
}
MemberInstance(memberBuilder::build, PlatformSourceFile(p, memberBuilder.sourceFile))
}
}
}
Replacement pattern:
templates add f\(\"(\w+)(\(.*)
val f_$1 = fn("$1$2
*/
/*
class MemberInstance(
val textBuilder: (Appendable) -> Unit,
val platformSourceFile: PlatformSourceFile)
*/
@@ -28,7 +28,7 @@ data class PlatformSourceFile(
fun Sequence<MemberTemplate>.groupByFileAndWrite(
fileNameBuilder: (PlatformSourceFile) -> File
) {
val groupedMembers = map { it.instantiate() }.flatten().groupBy {
val groupedMembers = map { it.instantiate(Platform.values - Platform.Native) }.flatten().groupBy {
PlatformSourceFile(it.platform, it.sourceFile)
}