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
import org.jetbrains.idl2k.util.mapEnumConstant
private fun Operation.getterOrSetter() = this.attributes.map { it.call }.toSet().let { attributes ->
when {
"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 },
initializer =
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) {
"definedExternally"
@@ -228,15 +230,21 @@ fun generateUnions(ifaces: List<GenerateTraitOrClass>, typedefs: Iterable<Typede
)
}
private fun mapLiteral(literal: String?, expectedType: Type = DynamicType) = when (literal) {
"[]" -> when {
expectedType == DynamicType -> "arrayOf<dynamic>()"
expectedType is AnyType -> "arrayOf<dynamic>()"
expectedType is UnionType -> "arrayOf<dynamic>()"
else -> "arrayOf()"
private fun mapLiteral(literal: String?, expectedType: Type = DynamicType, enums: Map<String, EnumDefinition>) =
if (literal != null && expectedType is SimpleType && expectedType.type in enums.keys) {
expectedType.type + "." + mapEnumConstant(literal.removeSurrounding("\"", "\""))
}
else {
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>) {
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.ParserRuleContext
import org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.RuleNode
import org.antlr.v4.runtime.tree.TerminalNode
import org.antlr.webidl.WebIDLBaseVisitor
import org.antlr.webidl.WebIDLLexer
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 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
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>>() {
private val arguments = ArrayList<Attribute>()
@@ -337,13 +336,15 @@ class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, val nam
private val constants = ArrayList<Constant>()
private var partial = false
private var callback = false
private val enumEntries = mutableListOf<String>()
private var enumEntryExpected = false
override fun defaultResult(): Definition = when (kind) {
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.EXTENSION_INTERFACE -> ExtensionInterfaceDefinition(namespace, name, implements ?: "")
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 {
@@ -408,12 +409,23 @@ class DefinitionVisitor(val extendedAttributes: List<ExtendedAttribute>, val nam
}
override fun visitEnum_(ctx: Enum_Context): Definition {
enumEntryExpected = true
kind = DefinitionKind.ENUM
name = getName(ctx)
super.visitEnum_(ctx)
enumEntryExpected = false
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 {
kind = DefinitionKind.DICTIONARY
name = getName(ctx)
@@ -40,7 +40,7 @@ fun main(args: Array<String>) {
}
}
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)
outDir.deleteRecursively()
@@ -66,7 +66,7 @@ fun main(args: Array<String>) {
}
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
import org.jetbrains.idl2k.util.mapEnumConstant
import java.math.BigInteger
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 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 ")
if (markerAnnotation) {
append("@marker ")
@@ -159,7 +162,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
append(iface.name)
val primary = iface.primaryConstructor
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()
@@ -176,7 +179,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
iface.secondaryConstructors.forEach { secondary ->
indent(false, 1)
append("constructor")
renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypes.keys), false)
renderArgumentsDeclaration(secondary.constructor.fixRequiredArguments(iface.name).arguments.dynamicIfUnknownType(allTypesAndEnums), false)
appendln()
}
@@ -188,7 +191,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
iface.memberAttributes
.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 }
.mapValues { it.value.filter { "${iface.name}.${it.name}" !in commentOutDeclarations && "${iface.name}.${it.name}: ${it.type.render()}" !in commentOutDeclarations } }
.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 }
.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) {
renderFunctionDeclaration(
@@ -255,7 +258,7 @@ fun Appendable.render(allTypes: Map<String, GenerateTraitOrClass>, typeNamesToUn
appendln()
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 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 allTypes = declaredTypes + unions.anonymousUnionsMap + unions.typedefsMarkersMap
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 {
render(allTypes, emptyMap(), it, markerAnnotation = true)
render(allTypes, enums, emptyMap(), it, markerAnnotation = true)
}
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 {
@@ -78,7 +78,6 @@ internal fun mapType(repository: Repository, type: Type): Type = when (type) {
typeName in typeMapper -> typeMapper[typeName]!!.withNullability(type.nullable)
typeName in repository.interfaces -> type
typeName in repository.typeDefs -> mapTypedef(repository, type)
typeName in repository.enums -> SimpleType("String", type.nullable)
else -> type
}
@@ -41,3 +41,5 @@ fun <T> List<List<T>>.mutations() : List<List<T>> {
return result
}
fun mapEnumConstant(entry: String) = if (entry.isEmpty()) "EMPTY" else entry.toUpperCase().replace("-", "_")