Skip to content

Commit

Permalink
refactor LionWebLanguageConverter
Browse files Browse the repository at this point in the history
  • Loading branch information
ftomassetti committed Aug 4, 2023
1 parent ca484c7 commit 56dfc3c
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.full.createType

typealias LWLanguage = Language
typealias EnumKClass = KClass<out Enum<*>>

/**
* This class is able to convert between Kolasu and LionWeb languages, tracking the mapping.
*/
class LionWebLanguageConverter {
private val astClassesAndClassifiers = BiMap<KClass<*>, Classifier<*>>()
private val classesAndEnumerations = BiMap<KClass<out Enum<*>>, Enumeration>()
private val languages = BiMap<KolasuLanguage, Language>()
private val classesAndEnumerations = BiMap<EnumKClass, Enumeration>()
private val languages = BiMap<KolasuLanguage, LWLanguage>()

init {
val starLasuKLanguage = KolasuLanguage(StarLasuLWLanguage.name)
Expand All @@ -38,29 +41,8 @@ class LionWebLanguageConverter {
registerMapping(Named::class, StarLasuLWLanguage.Named)
}

fun getKolasuClassesToConceptsMapping(): Map<KClass<*>, Classifier<*>> {
return astClassesAndClassifiers.asToBsMap
}

fun getConceptsToKolasuClassesMapping(): Map<Classifier<*>, KClass<*>> {
return astClassesAndClassifiers.bsToAsMap
}

fun getEnumerationsToKolasuClassesMapping(): Map<Enumeration, KClass<*>> {
return classesAndEnumerations.bsToAsMap
}

fun knownLWLanguages(): Set<Language> {
return languages.bs
}

fun correspondingLanguage(kolasuLanguage: KolasuLanguage): Language {
return languages.byA(kolasuLanguage)
?: throw java.lang.IllegalArgumentException("Unknown Kolasu Language $kolasuLanguage")
}

fun export(kolasuLanguage: KolasuLanguage): Language {
val lionwebLanguage = Language()
fun exportToLionWeb(kolasuLanguage: KolasuLanguage): LWLanguage {
val lionwebLanguage = LWLanguage()
lionwebLanguage.version = "1"
lionwebLanguage.name = kolasuLanguage.qualifiedName
lionwebLanguage.key = kolasuLanguage.qualifiedName.replace('.', '-')
Expand Down Expand Up @@ -92,7 +74,7 @@ class LionWebLanguageConverter {
val superInterfaces = astClass.supertypes.map { it.classifier as KClass<*> }
.filter { it.java.isInterface }
superInterfaces.filter { it.isMarkedAsNodeType() }.forEach {
conceptInterface.addExtendedInterface(toConceptInterface(it))
conceptInterface.addExtendedInterface(correspondingConceptInterface(it))
}
} else {
val concept = featuresContainer as Concept
Expand All @@ -105,7 +87,7 @@ class LionWebLanguageConverter {
}
val interfaces = astClass.supertypes.map { it.classifier as KClass<*> }.filter { it.java.isInterface }
interfaces.filter { it.isMarkedAsNodeType() }.forEach {
concept.addImplementedInterface(toConceptInterface(it))
concept.addImplementedInterface(correspondingConceptInterface(it))
}
}
astClass.features().forEach {
Expand Down Expand Up @@ -142,49 +124,13 @@ class LionWebLanguageConverter {
return lionwebLanguage
}

private fun toLWDataType(kType: KType, lionwebLanguage: Language): DataType<*> {
return when (kType) {
Int::class.createType() -> LionCoreBuiltins.getInteger()
Long::class.createType() -> LionCoreBuiltins.getInteger()
String::class.createType() -> LionCoreBuiltins.getString()
Boolean::class.createType() -> LionCoreBuiltins.getBoolean()
else -> {
val kClass = kType.classifier as KClass<*>
val isEnum = kClass.supertypes.any { it.classifier == Enum::class }
if (isEnum) {
val enumeration = classesAndEnumerations.byA(kClass as KClass<out Enum<*>>)
if (enumeration == null) {
val newEnumeration = Enumeration(lionwebLanguage, kClass.simpleName)
lionwebLanguage.addElement(newEnumeration)
classesAndEnumerations.associate(kClass, newEnumeration)
return newEnumeration
} else {
return enumeration
}
} else {
throw UnsupportedOperationException("KType: $kType")
}
}
}
}

fun toConceptInterface(kClass: KClass<*>): ConceptInterface {
return toLWClassifier(kClass) as ConceptInterface
}

fun toConcept(kClass: KClass<*>): Concept {
return toLWClassifier(kClass) as Concept
}

fun matchingKClass(concept: Concept): KClass<*>? {
return this.astClassesAndClassifiers.bsToAsMap.entries.find {
it.key.key == concept.key &&
it.key.language!!.id == concept.language!!.id &&
it.key.language!!.version == concept.language!!.version
}?.value
}

fun importLanguages(lwLanguage: Language, kolasuLanguage: KolasuLanguage) {
/**
* Importing a LionWeb language as a Kolasu language requires the generation of classes, to be performed
* separately. Once that is done we associate the Kolasu language defined by those classes to a certain
* LionWeb language, so that we can import LionWeb models by instantiating the corresponding classes in the
* Kolasu language.
*/
fun associateLanguages(lwLanguage: LWLanguage, kolasuLanguage: KolasuLanguage) {
this.languages.associate(kolasuLanguage, lwLanguage)
kolasuLanguage.astClasses.forEach { astClass ->
var classifier: Classifier<*>? = null
Expand Down Expand Up @@ -212,11 +158,87 @@ class LionWebLanguageConverter {
}
}

fun knownLWLanguages(): Set<LWLanguage> {
return languages.bs
}

fun knownKolasuLanguages(): Set<KolasuLanguage> {
return languages.`as`
}

fun correspondingLanguage(kolasuLanguage: KolasuLanguage): LWLanguage {
return languages.byA(kolasuLanguage)
?: throw java.lang.IllegalArgumentException("Unknown Kolasu Language $kolasuLanguage")
}

fun correspondingLanguage(lwLanguage: LWLanguage): KolasuLanguage {
return languages.byB(lwLanguage)
?: throw java.lang.IllegalArgumentException("Unknown LionWeb Language $lwLanguage")
}

fun getKolasuClassesToClassifiersMapping(): Map<KClass<*>, Classifier<*>> {
return astClassesAndClassifiers.asToBsMap
}

fun getClassifiersToKolasuClassesMapping(): Map<Classifier<*>, KClass<*>> {
return astClassesAndClassifiers.bsToAsMap
}

fun getEnumerationsToKolasuClassesMapping(): Map<Enumeration, EnumKClass> {
return classesAndEnumerations.bsToAsMap
}

fun getKolasuClassesToEnumerationsMapping(): Map<EnumKClass, Enumeration> {
return classesAndEnumerations.asToBsMap
}

fun correspondingConceptInterface(kClass: KClass<*>): ConceptInterface {
return toLWClassifier(kClass) as ConceptInterface
}

fun correspondingConcept(kClass: KClass<*>): Concept {
return toLWClassifier(kClass) as Concept
}

fun correspondingKolasuClass(classifier: Classifier<*>): KClass<*>? {
return this.astClassesAndClassifiers.bsToAsMap.entries.find {
it.key.key == classifier.key &&
it.key.language!!.id == classifier.language!!.id &&
it.key.language!!.version == classifier.language!!.version
}?.value
}

private fun registerMapping(kolasuClass: KClass<*>, featuresContainer: Classifier<*>) {
astClassesAndClassifiers.associate(kolasuClass, featuresContainer)
}

private fun toLWClassifier(kClass: KClass<*>): Classifier<*> {
return astClassesAndClassifiers.byA(kClass) ?: throw IllegalArgumentException("Unknown KClass $kClass")
}

private fun toLWDataType(kType: KType, lionwebLanguage: LWLanguage): DataType<*> {
return when (kType) {
Int::class.createType() -> LionCoreBuiltins.getInteger()
Long::class.createType() -> LionCoreBuiltins.getInteger()
String::class.createType() -> LionCoreBuiltins.getString()
Boolean::class.createType() -> LionCoreBuiltins.getBoolean()
else -> {
val kClass = kType.classifier as KClass<*>
val isEnum = kClass.supertypes.any { it.classifier == Enum::class }
if (isEnum) {
val enumeration = classesAndEnumerations.byA(kClass as EnumKClass)
if (enumeration == null) {
val newEnumeration = Enumeration(lionwebLanguage, kClass.simpleName)
lionwebLanguage.addElement(newEnumeration)
classesAndEnumerations.associate(kClass, newEnumeration)
return newEnumeration
} else {
return enumeration
}
} else {
throw UnsupportedOperationException("KType: $kType")
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ class LionWebModelConverter {
}

fun recordLanguage(kolasuLanguage: KolasuLanguage): Language {
return languageExporter.export(kolasuLanguage)
return languageExporter.exportToLionWeb(kolasuLanguage)
}

fun importLanguages(lwLanguage: Language, kolasuLanguage: KolasuLanguage) {
this.languageExporter.importLanguages(lwLanguage, kolasuLanguage)
this.languageExporter.associateLanguages(lwLanguage, kolasuLanguage)
}

fun export(kolasuTree: com.strumenta.kolasu.model.Node): Node {
Expand Down Expand Up @@ -221,7 +221,7 @@ class LionWebModelConverter {
fun import(lwTree: Node): com.strumenta.kolasu.model.Node {
val referencesPostponer = ReferencesPostponer()
lwTree.thisAndAllDescendants().reversed().forEach { lwNode ->
val kClass = languageExporter.matchingKClass(lwNode.concept)
val kClass = languageExporter.correspondingKolasuClass(lwNode.concept)
?: throw RuntimeException("We do not have StarLasu AST class for LIonWeb Concept ${lwNode.concept}")
val kNode: com.strumenta.kolasu.model.Node = instantiate(kClass, lwNode, referencesPostponer)
registerMapping(kNode, lwNode)
Expand All @@ -235,7 +235,7 @@ class LionWebModelConverter {
}

private fun findConcept(kNode: com.strumenta.kolasu.model.Node): Concept {
return languageExporter.toConcept(kNode.javaClass.kotlin)
return languageExporter.correspondingConcept(kNode.javaClass.kotlin)
}

private fun nodeID(kNode: com.strumenta.kolasu.model.Node): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ class KolasuLanguageAssociatedToLionWebTest {
addClass(LWRoot::class)
}
val lie = LionWebLanguageConverter()
lie.importLanguages(lwLang, kolasuLanguage)
assertEquals(LWRoot::class, lie.matchingKClass(lwRoot))
assertEquals(LWNodeA::class, lie.matchingKClass(lwNodeA))
assertEquals(LWNodeB::class, lie.matchingKClass(lwNodeB))
lie.associateLanguages(lwLang, kolasuLanguage)
assertEquals(LWRoot::class, lie.correspondingKolasuClass(lwRoot))
assertEquals(LWNodeA::class, lie.correspondingKolasuClass(lwNodeA))
assertEquals(LWNodeB::class, lie.correspondingKolasuClass(lwNodeB))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class LionWebLanguageConverterTest {
addClass(SimpleRoot::class)
}
assertEquals(5, kLanguage.astClasses.size)
val lwLanguage = LionWebLanguageConverter().export(kLanguage)
val lwLanguage = LionWebLanguageConverter().exportToLionWeb(kLanguage)
assertEquals("1", lwLanguage.version)
assertEquals(5, lwLanguage.elements.size)

Expand Down

0 comments on commit 56dfc3c

Please sign in to comment.