JS: add enum emulation to IDL2K

This commit is contained in:
Alexey Andreev
2017-01-26 19:18:44 +03:00
parent 04e9405082
commit 72bcdf8869
6 changed files with 69 additions and 26 deletions
+17 -9
View File
@@ -16,6 +16,8 @@
package org.jetbrains.idl2k package org.jetbrains.idl2k
import org.jetbrains.idl2k.util.mapEnumConstant
private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet().let { attributes -> private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet().let { attributes ->
when { when {
"getter" in attributes -> NativeGetterOrSetter.GETTER "getter" in attributes -> NativeGetterOrSetter.GETTER
@@ -89,7 +91,7 @@ fun generateAttribute(putNoImpl: Boolean, repository: Repository, attribute: Att
type = mapType(repository, attribute.type).let { if (nullableAttributes) it.toNullable() else it }, type = mapType(repository, attribute.type).let { if (nullableAttributes) it.toNullable() else it },
initializer = initializer =
if (putNoImpl && !attribute.static) { if (putNoImpl && !attribute.static) {
mapLiteral(attribute.defaultValue, mapType(repository, attribute.type)) mapLiteral(attribute.defaultValue, mapType(repository, attribute.type), repository.enums)
} }
else if (attribute.defaultValue != null) { else if (attribute.defaultValue != null) {
"definedExternally" "definedExternally"
@@ -228,15 +230,21 @@ fun generateUnions(ifaces: List<GenerateTraitOrClass>, typedefs: Iterable<Typede
) )
} }
private fun mapLiteral(literal: String?, expectedType: Type = DynamicType) = when (literal) { private fun mapLiteral(literal: String?, expectedType: Type = DynamicType, enums: Map<String, EnumDefinition>) =
"[]" -> when { if (literal != null && expectedType is SimpleType && expectedType.type in enums.keys) {
expectedType == DynamicType -> "arrayOf<dynamic>()" expectedType.type + "." + mapEnumConstant(literal.removeSurrounding("\"", "\""))
expectedType is AnyType -> "arrayOf<dynamic>()" }
expectedType is UnionType -> "arrayOf<dynamic>()" else {
else -> "arrayOf()" when (literal) {
"[]" -> when {
expectedType == DynamicType -> "arrayOf<dynamic>()"
expectedType is AnyType -> "arrayOf<dynamic>()"
expectedType is UnionType -> "arrayOf<dynamic>()"
else -> "arrayOf()"
}
else -> literal
}
} }
else -> literal
}
fun implementInterfaces(declarations: List<GenerateTraitOrClass>) { fun implementInterfaces(declarations: List<GenerateTraitOrClass>) {
val unimplementedMemberMap = getUnimplementedMembers(declarations) val unimplementedMemberMap = getUnimplementedMembers(declarations)
+16 -4
View File
@@ -20,13 +20,12 @@ import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.CommonTokenStream import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.ParserRuleContext import org.antlr.v4.runtime.ParserRuleContext
import org.antlr.v4.runtime.tree.ParseTree import org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.RuleNode
import org.antlr.v4.runtime.tree.TerminalNode import org.antlr.v4.runtime.tree.TerminalNode
import org.antlr.webidl.WebIDLBaseVisitor import org.antlr.webidl.WebIDLBaseVisitor
import org.antlr.webidl.WebIDLLexer import org.antlr.webidl.WebIDLLexer
import org.antlr.webidl.WebIDLParser import org.antlr.webidl.WebIDLParser
import org.antlr.webidl.WebIDLParser.* import org.antlr.webidl.WebIDLParser.*
import java.util.ArrayList import java.util.*
data class ExtendedAttribute(val name: String?, val call: String, val arguments: List<Attribute>) data class ExtendedAttribute(val name: String?, val call: String, val arguments: List<Attribute>)
data class Operation(val name: String, val returnType: Type, val parameters: List<Attribute>, val attributes: List<ExtendedAttribute>, val static: Boolean) data class Operation(val name: String, val returnType: Type, val parameters: List<Attribute>, val attributes: List<ExtendedAttribute>, val static: Boolean)
@@ -57,7 +56,7 @@ data class InterfaceDefinition(
) : Definition ) : Definition
data class ExtensionInterfaceDefinition(val namespace: String, val name: String, val implements: String) : Definition data class ExtensionInterfaceDefinition(val namespace: String, val name: String, val implements: String) : Definition
data class EnumDefinition(val namespace: String, val name: String) : Definition data class EnumDefinition(val namespace: String, val name: String, val entries: List<String>) : Definition
class ExtendedAttributeArgumentsParser(private val namespace: String) : WebIDLBaseVisitor<List<Attribute>>() { class ExtendedAttributeArgumentsParser(private val namespace: String) : WebIDLBaseVisitor<List<Attribute>>() {
private val arguments = ArrayList<Attribute>() private val arguments = ArrayList<Attribute>()
@@ -337,13 +336,15 @@ class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, val nam
private val constants = ArrayList<Constant>() private val constants = ArrayList<Constant>()
private var partial = false private var partial = false
private var callback = false private var callback = false
private val enumEntries = mutableListOf<String>()
private var enumEntryExpected = false
override fun defaultResult(): Definition = when (kind) { override fun defaultResult(): Definition = when (kind) {
DefinitionKind.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, false, partial, callback) DefinitionKind.INTERFACE -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, false, partial, callback)
DefinitionKind.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, /* dictionary = */ true, partial, callback) DefinitionKind.DICTIONARY -> InterfaceDefinition(name, namespace, extendedAttributes, operations, attributes, inherited, constants, /* dictionary = */ true, partial, callback)
DefinitionKind.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "") DefinitionKind.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "")
DefinitionKind.TYPEDEF -> TypedefDefinition(typedefType ?: AnyType(true), namespace, name) DefinitionKind.TYPEDEF -> TypedefDefinition(typedefType ?: AnyType(true), namespace, name)
DefinitionKind.ENUM -> EnumDefinition(namespace, name) DefinitionKind.ENUM -> EnumDefinition(namespace, name, enumEntries)
} }
override fun visitCallbackRestOrInterface(ctx: WebIDLParser.CallbackRestOrInterfaceContext): Definition { override fun visitCallbackRestOrInterface(ctx: WebIDLParser.CallbackRestOrInterfaceContext): Definition {
@@ -408,12 +409,23 @@ class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, val nam
} }
override fun visitEnum_(ctx: Enum_Context): Definition { override fun visitEnum_(ctx: Enum_Context): Definition {
enumEntryExpected = true
kind = DefinitionKind.ENUM kind = DefinitionKind.ENUM
name = getName(ctx) name = getName(ctx)
super.visitEnum_(ctx)
enumEntryExpected = false
return defaultResult() return defaultResult()
} }
override fun visitTerminal(node: TerminalNode): Definition {
if (enumEntryExpected && node.symbol.type == WebIDLParser.STRING_WEBIDL) {
enumEntries += node.symbol.text.removeSurrounding("\"", "\"")
}
return super.visitTerminal(node)
}
override fun visitDictionary(ctx: DictionaryContext): Definition { override fun visitDictionary(ctx: DictionaryContext): Definition {
kind = DefinitionKind.DICTIONARY kind = DefinitionKind.DICTIONARY
name = getName(ctx) name = getName(ctx)
@@ -40,7 +40,7 @@ fun main(args: Array<String>) {
} }
} }
val unions = generateUnions(definitions, repository.typeDefs.values) val unions = generateUnions(definitions, repository.typeDefs.values)
val allPackages = definitions.map { it.namespace }.distinct().sorted() val allPackages = (definitions.asSequence().map { it.namespace } + repository.enums.values.map { it.namespace }).distinct().sorted()
implementInterfaces(definitions) implementInterfaces(definitions)
outDir.deleteRecursively() outDir.deleteRecursively()
@@ -66,7 +66,7 @@ fun main(args: Array<String>) {
} }
w.appendln() w.appendln()
w.render(pkg, definitions, unions) w.render(pkg, definitions, unions, repository.enums.values.toList())
} }
} }
} }
+32 -10
View File
@@ -16,6 +16,7 @@
package org.jetbrains.idl2k package org.jetbrains.idl2k
import org.jetbrains.idl2k.util.mapEnumConstant
import java.math.BigInteger import java.math.BigInteger
private fun <O : Appendable> O.indent(commented: Boolean = false, level: Int) { private fun <O : Appendable> O.indent(commented: Boolean = false, level: Int) {
@@ -142,7 +143,9 @@ private fun GenerateFunction.isCommented(parent: String) =
private fun GenerateAttribute.isRequiredFunctionArgument(owner: String, functionName: String) = "$owner.$functionName.$name" in requiredArguments private fun GenerateAttribute.isRequiredFunctionArgument(owner: String, functionName: String) = "$owner.$functionName.$name" in requiredArguments
private fun GenerateFunction.fixRequiredArguments(parent: String) = copy(arguments = arguments.map { arg -> arg.copy(initializer = if (arg.isRequiredFunctionArgument(parent, name)) null else arg.initializer) }) private fun GenerateFunction.fixRequiredArguments(parent: String) = copy(arguments = arguments.map { arg -> arg.copy(initializer = if (arg.isRequiredFunctionArgument(parent, name)) null else arg.initializer) })
fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUnions: Map<String, List<String>>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) { fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, enums: List<EnumDefinition>, typeNamesToUnions: Map<String, List<String>>, iface: GenerateTraitOrClass, markerAnnotation: Boolean = false) {
val allTypesAndEnums = allTypes.keys + enums.map { it.name }
append("public external ") append("public external ")
if (markerAnnotation) { if (markerAnnotation) {
append("@marker ") append("@marker ")
@@ -159,7 +162,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
append(iface.name) append(iface.name)
val primary = iface.primaryConstructor val primary = iface.primaryConstructor
if (primary != null && (primary.constructor.arguments.isNotEmpty() || iface.secondaryConstructors.isNotEmpty())) { if (primary != null && (primary.constructor.arguments.isNotEmpty() || iface.secondaryConstructors.isNotEmpty())) {
renderArgumentsDeclaration(primary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypes.keys), false) renderArgumentsDeclaration(primary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false)
} }
val superTypesExclude = inheritanceExclude[iface.name] ?: emptySet() val superTypesExclude = inheritanceExclude[iface.name] ?: emptySet()
@@ -176,7 +179,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
iface.secondaryConstructors.forEach { secondary -> iface.secondaryConstructors.forEach { secondary ->
indent(false, 1) indent(false, 1)
append("constructor") append("constructor")
renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypes.keys), false) renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false)
appendln() appendln()
} }
@@ -188,7 +191,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
iface.memberAttributes iface.memberAttributes
.filter { it !in superAttributes && !it.static && (it.isVar || (it.isVal && superAttributesByName[it.name]?.hasNoVars() ?: true)) } .filter { it !in superAttributes && !it.static && (it.isVar || (it.isVal && superAttributesByName[it.name]?.hasNoVars() ?: true)) }
.map { it.dynamicIfUnknownType(allTypes.keys) } .map { it.dynamicIfUnknownType(allTypesAndEnums) }
.groupBy { it.name } .groupBy { it.name }
.mapValues { it.value.filter { "${iface.name}.${it.name}" !in commentOutDeclarations && "${iface.name}.${it.name}: ${it.type.render()}" !in commentOutDeclarations } } .mapValues { it.value.filter { "${iface.name}.${it.name}" !in commentOutDeclarations && "${iface.name}.${it.name}: ${it.type.render()}" !in commentOutDeclarations } }
.filterValues { it.isNotEmpty() } .filterValues { it.isNotEmpty() }
@@ -217,7 +220,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
} }
} }
val memberFunctions = iface.memberFunctions.filter { (it !in superFunctions || it.override) && !it.static } val memberFunctions = iface.memberFunctions.filter { (it !in superFunctions || it.override) && !it.static }
.map { it.dynamicIfUnknownType(allTypes.keys) }.groupBy { it.signature }.reduceValues(::betterFunction).values .map { it.dynamicIfUnknownType(allTypesAndEnums) }.groupBy { it.signature }.reduceValues(::betterFunction).values
fun doRenderFunction(function: GenerateFunction, level: Int = 1) { fun doRenderFunction(function: GenerateFunction, level: Int = 1) {
renderFunctionDeclaration( renderFunctionDeclaration(
@@ -255,7 +258,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
appendln() appendln()
if (iface.generateBuilderFunction) { if (iface.generateBuilderFunction) {
renderBuilderFunction(iface, allSuperTypes, allTypes.keys) renderBuilderFunction(iface, allSuperTypes, allTypesAndEnums)
} }
} }
@@ -336,21 +339,40 @@ private fun merge(a: GenerateAttribute, b: GenerateAttribute): GenerateAttribute
fun <K, V> List<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = groupBy { it.first }.mapValues { it.value.map { it.second } } fun <K, V> List<Pair<K, V>>.toMultiMap(): Map<K, List<V>> = groupBy { it.first }.mapValues { it.value.map { it.second } }
fun Appendable.render(namespace: String, ifaces: List<GenerateTraitOrClass>, unions : GenerateUnionTypes) { fun Appendable.render(enumDefinition: EnumDefinition) {
appendln("/* please, don't implement this interface! */")
appendln("public external interface ${enumDefinition.name} {")
indent(level = 1)
appendln("companion object")
appendln("}")
for (entry in enumDefinition.entries) {
val entryName = mapEnumConstant(entry)
appendln("public inline val ${enumDefinition.name}.Companion.$entryName: ${enumDefinition.name} " +
"get() = \"$entry\".asDynamic().unsafeCast<${enumDefinition.name}>()")
}
appendln()
}
fun Appendable.render(namespace: String, ifaces: List<GenerateTraitOrClass>, unions: GenerateUnionTypes, enums: List<EnumDefinition>) {
val declaredTypes = ifaces.associateBy { it.name } val declaredTypes = ifaces.associateBy { it.name }
val allTypes = declaredTypes + unions.anonymousUnionsMap + unions.typedefsMarkersMap val allTypes = declaredTypes + unions.anonymousUnionsMap + unions.typedefsMarkersMap
declaredTypes.values.filter { it.namespace == namespace }.forEach { declaredTypes.values.filter { it.namespace == namespace }.forEach {
render(allTypes, unions.typeNamesToUnionsMap, it) render(allTypes, enums, unions.typeNamesToUnionsMap, it)
} }
unions.anonymousUnionsMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach { unions.anonymousUnionsMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach {
render(allTypes, emptyMap(), it, markerAnnotation = true) render(allTypes, enums, emptyMap(), it, markerAnnotation = true)
} }
unions.typedefsMarkersMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach { unions.typedefsMarkersMap.values.filter { it.namespace == "" || it.namespace == namespace }.forEach {
render(allTypes, emptyMap(), it, markerAnnotation = true) render(allTypes, enums, emptyMap(), it, markerAnnotation = true)
} }
enums.filter { it.namespace == namespace }
.forEach { render(it) }
} }
enum class MemberModality { enum class MemberModality {
@@ -78,7 +78,6 @@ internal fun mapType(repository: Repository, type: Type): Type = when (type) {
typeName in typeMapper -> typeMapper[typeName]!!.withNullability(type.nullable) typeName in typeMapper -> typeMapper[typeName]!!.withNullability(type.nullable)
typeName in repository.interfaces -> type typeName in repository.interfaces -> type
typeName in repository.typeDefs -> mapTypedef(repository, type) typeName in repository.typeDefs -> mapTypedef(repository, type)
typeName in repository.enums -> SimpleType("String", type.nullable)
else -> type else -> type
} }
@@ -41,3 +41,5 @@ fun <T> List<List<T>>.mutations() : List<List<T>> {
return result return result
} }
fun mapEnumConstant(entry: String) = if (entry.isEmpty()) "EMPTY" else entry.toUpperCase().replace("-", "_")