Skip to content

Commit

Permalink
CASC: Implement Trait implementation (Beta review)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChAoSUnItY committed May 16, 2022
1 parent 42ae195 commit da2036b
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 69 deletions.
3 changes: 2 additions & 1 deletion src/main/kotlin/org/casc/lang/ast/Function.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ data class Function(
parameterTypes?.fold("") { s, type ->
s + type?.descriptor
}
})${returnType?.descriptor}"
})${returnType?.descriptor ?: "V"}"
override val flag: Int by lazy {
var flag = accessor.access
flag += selfKeyword.getOrElse(0, Opcodes.ACC_STATIC)
Expand All @@ -35,6 +35,7 @@ data class Function(
flag += mutKeyword.getOrElse(0, Opcodes.ACC_FINAL)
flag
}
var abstract: Boolean = abstrKeyword != null

override fun asSignature() =
FunctionSignature(
Expand Down
48 changes: 41 additions & 7 deletions src/main/kotlin/org/casc/lang/checker/Checker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ class Checker(private val preference: AbstractPreference) {

if (clazz.traitImpls != null) {
clazz.traitImpls!!.forEach {
it.functions.forEach {

it.functions.forEach { function ->
checkFunction(function, classScope, it.implementedTraitReference)
}
}
}
Expand Down Expand Up @@ -143,8 +143,8 @@ class Checker(private val preference: AbstractPreference) {

if (trait.traitImpls != null) {
trait.traitImpls!!.forEach {
it.functions.forEach {

it.functions.forEach { function ->
checkFunction(function, traitScope, it.implementedTraitReference)
}
}
}
Expand Down Expand Up @@ -285,6 +285,23 @@ class Checker(private val preference: AbstractPreference) {
}
}
}

if (clazz.traitImpls != null) {
for (traitImpl in clazz.traitImpls!!) {
traitImpl.functions.forEach {
if (it.statements != null) {
checkFunctionBody(it, Scope(classScope, isCompScope = it.selfKeyword == null))

if (!checkControlFlow(it.statements, it.returnType)) {
// Not all code path returns value
reports += Error(
it.name?.pos, "Not all code path returns value"
)
}
}
}
}
}
}

private fun checkTrait(file: File, trait: TraitInstance, traitScope: Scope) {
Expand Down Expand Up @@ -316,6 +333,23 @@ class Checker(private val preference: AbstractPreference) {
}
}
}

if (trait.traitImpls != null) {
for (traitImpl in trait.traitImpls!!) {
traitImpl.functions.forEach {
if (it.statements != null) {
checkFunctionBody(it, Scope(traitScope, isCompScope = it.selfKeyword == null))

if (!checkControlFlow(it.statements, it.returnType)) {
// Not all code path returns value
reports += Error(
it.name?.pos, "Not all code path returns value"
)
}
}
}
}
}
}

private fun checkField(field: Field, scope: Scope): Field {
Expand Down Expand Up @@ -394,7 +428,7 @@ class Checker(private val preference: AbstractPreference) {
return constructor
}

private fun checkFunction(function: Function, scope: Scope, searchTraitFunction: Boolean = false): Function {
private fun checkFunction(function: Function, scope: Scope, traitReference: Reference? = null): Function {
val localScope = Scope(scope)
checkIdentifierIsKeyword(function.name?.literal, function.name?.pos)

Expand Down Expand Up @@ -445,7 +479,7 @@ class Checker(private val preference: AbstractPreference) {

// Check is overriding parent function
val parentFunction =
scope.findSignature(scope.parentClassPath, function.name!!.literal, function.parameterTypes ?: listOf())
scope.findSignature(traitReference ?: scope.parentClassPath, function.name!!.literal, function.parameterTypes ?: listOf())

if (parentFunction != null) {
if (function.ovrdKeyword == null) {
Expand Down Expand Up @@ -995,7 +1029,7 @@ class Checker(private val preference: AbstractPreference) {
// Check function call expression's context, e.g companion context
val ownerReference = expression.ownerReference ?: previousType?.getReference() ?: scope.typeReference
val functionSignature = scope.findSignature(
ownerReference, expression.name!!.literal, argumentTypes
ownerReference, expression.name!!.literal, argumentTypes, allowAbstract = false
)

if (functionSignature == null) {
Expand Down
77 changes: 49 additions & 28 deletions src/main/kotlin/org/casc/lang/compilation/Compilation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ package org.casc.lang.compilation

import com.diogonunes.jcolor.AnsiFormat
import com.diogonunes.jcolor.Attribute
import org.casc.lang.ast.ClassInstance
import org.casc.lang.ast.File
import org.casc.lang.ast.Token
import org.casc.lang.ast.TraitInstance
import org.casc.lang.ast.*
import org.casc.lang.checker.Checker
import org.casc.lang.emitter.Emitter
import org.casc.lang.lexer.Lexer
Expand All @@ -14,6 +11,7 @@ import org.casc.lang.table.Reference
import org.casc.lang.table.Scope
import org.casc.lang.table.Table
import java.io.BufferedReader
import java.util.*
import kotlin.time.DurationUnit
import kotlin.time.ExperimentalTime

Expand Down Expand Up @@ -117,37 +115,53 @@ class Compilation(private val preference: AbstractPreference) {
return@checkPrelude
}

val creationQueue = mutableListOf<Reference>()
val traitQueue = mutableListOf<Reference>()
val classQueue = mutableListOf<Reference>()
val declarations = compilationUnits.map(CompilationFileUnit::file)

for ((file, compilationUnit) in declarations.zip(compilationUnits)) {
// Check parent class has no cyclic inheritance
val (_, _, _, typeInstance) = file

creationQueue += typeInstance.reference

when (typeInstance) {
is ClassInstance -> {
val parentClassReferenceSet = hashSetOf<Reference>()
var currentTypeInstance = typeInstance

while (currentTypeInstance.impl != null && currentTypeInstance.impl!!.parentClassReference != null) {
currentTypeInstance =
Table.findTypeInstance(currentTypeInstance.impl!!.parentClassReference!!)
?: break

if (!parentClassReferenceSet.add(currentTypeInstance.typeReference)) {
// Cyclic inheritance
compilationUnit.reports += Error(
typeInstance.impl!!.parentClassReference!!.pos,
"Circular inheritance is forbidden",
"Class ${typeInstance.reference.asCASCStyle()} inherits class ${currentTypeInstance.reference.asCASCStyle()} but class ${currentTypeInstance.reference.asCASCStyle()} also inherits class ${typeInstance.reference.asCASCStyle()}"
)
break
} else creationQueue.add(0, currentTypeInstance.reference)
}
is ClassInstance ->
classQueue += typeInstance.reference
is TraitInstance ->
traitQueue += typeInstance.reference
}


if (typeInstance is ClassInstance) {
val parentClassReferenceSet = hashSetOf<Reference>()
var currentTypeInstance = typeInstance

while (currentTypeInstance.impl != null && currentTypeInstance.impl!!.parentClassReference != null) {
currentTypeInstance =
Table.findTypeInstance(currentTypeInstance.impl!!.parentClassReference!!)
?: break

if (!parentClassReferenceSet.add(currentTypeInstance.typeReference)) {
// Cyclic inheritance
compilationUnit.reports += Error(
typeInstance.impl!!.parentClassReference!!.pos,
"Circular inheritance is forbidden",
"Class ${typeInstance.reference.asCASCStyle()} inherits class ${currentTypeInstance.reference.asCASCStyle()} but class ${currentTypeInstance.reference.asCASCStyle()} also inherits class ${typeInstance.reference.asCASCStyle()}"
)
break
} else classQueue.add(0, currentTypeInstance.reference)
}
}

if (typeInstance.traitImpls != null) {
val currentTraitQueue =
LinkedList(typeInstance.traitImpls!!.map(TraitImpl::implementedTraitReference))

while (currentTraitQueue.isNotEmpty()) {
val trait = currentTraitQueue.remove()
traitQueue.add(0, trait)
Table.findFile(trait)!!.typeInstance.traitImpls?.map(TraitImpl::implementedTraitReference)
?.let(currentTraitQueue::addAll)
}
is TraitInstance -> {}
}
}

Expand All @@ -157,7 +171,14 @@ class Compilation(private val preference: AbstractPreference) {
}

// Define temporary class through bytecode for ASM library to process (See [getClassLoader][org.casc.lang.asm.CommonClassWriter])
for (reference in creationQueue.distinct()) {
for (reference in traitQueue.distinct()) {
val cachedFile = Table.findFile(reference)!!
val bytecode = Emitter(preference, true).emit(cachedFile)

preference.classLoader.defineClass(reference.fullQualifiedPath, bytecode)
}

for (reference in classQueue.distinct()) {
val cachedFile = Table.findFile(reference)!!
val bytecode = Emitter(preference, true).emit(cachedFile)

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/org/casc/lang/emitter/Emitter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Emitter(private val preference: AbstractPreference, private val declaratio
}

clazz.impl?.let { emitImpl(classWriter, it) }
clazz.traitImpls?.let { for (traitImpl in it) for (function in traitImpl.functions) emitFunction(classWriter, function) }
}

private fun emitTrait(classWriter: ClassWriter, trait: TraitInstance) {
Expand All @@ -74,6 +75,7 @@ class Emitter(private val preference: AbstractPreference, private val declaratio
}

trait.impl?.let { emitImpl(classWriter, it) }
trait.traitImpls?.let { for (traitImpl in it) for (function in traitImpl.functions) emitFunction(classWriter, function) }
}

private fun emitImpl(classWriter: ClassWriter, impl: Impl) {
Expand Down
68 changes: 35 additions & 33 deletions src/main/kotlin/org/casc/lang/table/Scope.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ data class Scope(
* - if previous step does not return available function signature, search parent class
*/
fun findSignature(
ownerPath: Reference?, functionName: String, argumentTypes: List<Type?>, searchTraitOnly: Boolean = false
ownerPath: Reference?, functionName: String, argumentTypes: List<Type?>, searchTraitOnly: Boolean = false, allowAbstract: Boolean = true
): FunctionSignature? {
if (ownerPath == null) return findSignatureInSameType(
functionName, argumentTypes
Expand All @@ -148,41 +148,42 @@ data class Scope(
canCast(l, r)
}
}?.asSignature()
} else {
// TODO: Find functions from implemented traits
var functionSignature = typeInstance.impl?.functions?.find {
it.name?.literal == functionName && it.parameterTypes?.size == argumentTypes.size && argumentTypes.zip(
it.parameterTypes!!
).all { (l, r) ->
canCast(l, r)
}
}?.let(HasSignature::asSignature)
}
// TODO: Find functions from implemented traits
var functionSignature = typeInstance.impl?.functions?.filter { if (!allowAbstract) !it.abstract else true }?.find {
it.name?.literal == functionName && it.parameterTypes?.size == argumentTypes.size && argumentTypes.zip(
it.parameterTypes!!
).all { (l, r) ->
canCast(l, r)
}
}?.let(HasSignature::asSignature)

if (functionSignature != null) return functionSignature
if (functionSignature != null) return functionSignature

// Search trait function first
if (typeInstance.traitImpls != null) {
for (traitImpl in typeInstance.traitImpls!!) {
functionSignature =
findSignature(traitImpl.implementedTraitReference, functionName, argumentTypes)
// Search trait function first
if (typeInstance.traitImpls != null) {
for (traitImpl in typeInstance.traitImpls!!) {
functionSignature =
findSignature(traitImpl.implementedTraitReference, functionName, argumentTypes, allowAbstract)

if (functionSignature != null) return functionSignature
}
if (functionSignature != null) return functionSignature
}
}

if (searchTraitOnly) return null
if (searchTraitOnly) return null

// Search function in parent class
return if (typeInstance.impl != null) {
findSignature(
typeInstance.impl!!.parentClassReference ?: Reference.OBJECT_TYPE_REFERENCE,
functionName,
argumentTypes
)
} else null
}
// Search function in parent class
return if (typeInstance.impl != null) {
findSignature(
typeInstance.impl!!.parentClassReference ?: Reference.OBJECT_TYPE_REFERENCE,
functionName,
argumentTypes,
allowAbstract
)
} else null
}


val ownerType = findType(reference)

if (ownerType != null) {
Expand Down Expand Up @@ -215,7 +216,7 @@ data class Scope(
)
else retrieveExecutableInfo(ownerType, argTypes)

var functions = filterFunction(ownerClazz, functionName, argumentClasses)
var functions = filterFunction(ownerClazz, functionName, argumentClasses, allowAbstract)

for (function in functions) {
if (isSameFunction(function, argumentClasses)) {
Expand All @@ -228,7 +229,7 @@ data class Scope(

while (traits.isNotEmpty()) {
val trait = traits.remove()
functions = filterFunction(trait, functionName, argumentClasses)
functions = filterFunction(trait, functionName, argumentClasses, allowAbstract)

for (function in functions) {
if (isSameFunction(function, argumentClasses)) {
Expand All @@ -246,7 +247,7 @@ data class Scope(
ownerClazz = ownerClazz.superclass

while (true) {
functions = filterFunction(ownerClazz, functionName, argumentClasses)
functions = filterFunction(ownerClazz, functionName, argumentClasses, allowAbstract)

for (function in functions) {
if (isSameFunction(function, argumentClasses)) {
Expand All @@ -266,9 +267,10 @@ data class Scope(
private fun filterFunction(
ownerClass: Class<*>,
functionName: String,
argumentClasses: Array<Class<*>>
argumentClasses: Array<Class<*>>,
allowAbstract: Boolean
): List<Method> =
ownerClass.declaredMethods.filter { it.name == functionName && it.parameters.size == argumentClasses.size }
ownerClass.declaredMethods.filter { it.name == functionName && it.parameters.size == argumentClasses.size && if (!allowAbstract) !Modifier.isAbstract(it.modifiers) else true }

private fun isSameFunction(function: Method, argumentClasses: Array<Class<*>>): Boolean {
for ((i, parameterType) in function.parameterTypes.withIndex()) {
Expand Down

0 comments on commit da2036b

Please sign in to comment.