Skip to content

Commit

Permalink
Merge pull request #49 from CASC-Lang/trait-impl
Browse files Browse the repository at this point in the history
Trait implmplemtation phase I: Refactor top-level parsing scheme
  • Loading branch information
ChAoSUnItY authored May 8, 2022
2 parents 3764140 + 17cb63e commit 4d7d45d
Show file tree
Hide file tree
Showing 16 changed files with 366 additions and 242 deletions.
28 changes: 0 additions & 28 deletions src/main/kotlin/org/casc/lang/ast/Class.kt

This file was deleted.

27 changes: 27 additions & 0 deletions src/main/kotlin/org/casc/lang/ast/ClassInstance.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.casc.lang.ast

import org.casc.lang.table.HasFlag
import org.casc.lang.table.Reference
import org.casc.lang.utils.getOrElse
import org.objectweb.asm.Opcodes

data class ClassInstance(
override val packageReference: Reference?,
val accessorToken: Token?,
val mutKeyword: Token?,
val classKeyword: Token?,
override val typeReference: Reference,
override val fields: List<Field>,
override val accessor: Accessor = Accessor.fromString(accessorToken?.literal)
) : TypeInstance(packageReference, typeReference), HasFlag {
override val flag: Int by lazy {
var flag = Opcodes.ACC_SUPER
flag += accessor.access
flag += mutKeyword.getOrElse(0, Opcodes.ACC_FINAL)
flag
}

val parentClassReference: Reference by lazy {
impl?.parentClassReference ?: Reference.OBJECT_TYPE_REFERENCE
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/org/casc/lang/ast/Field.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ data class Field(
override val flag: Int =
mutKeyword.getOrElse(0, Opcodes.ACC_FINAL) + accessor.access + compKeyword.getOrElse(Opcodes.ACC_STATIC)

fun asClassField(): ClassField =
ClassField(
fun asClassField(): TypeField =
TypeField(
ownerReference,
compKeyword != null,
mutKeyword != null,
Expand Down
8 changes: 7 additions & 1 deletion src/main/kotlin/org/casc/lang/ast/File.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package org.casc.lang.ast

data class File(val path: String, val relativeFilePath: String, var clazz: Class)
import org.casc.lang.table.Reference

data class File(val path: String, val relativeFilePath: String, val usages: List<Reference>, var typeInstance: TypeInstance) {
val fileName: String by lazy {
relativeFilePath.split('\\', '/').last()
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/org/casc/lang/ast/Impl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.casc.lang.ast

import org.casc.lang.table.Reference

data class Impl(
val implKeyword: Token,
val parentClassReference: Reference?,
var companionBlock: List<Statement>,
var constructors: List<Constructor>,
var functions: List<Function>
)
3 changes: 3 additions & 0 deletions src/main/kotlin/org/casc/lang/ast/TraitImpl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.casc.lang.ast

data class TraitImpl(val implKeyword: Token, val functions: List<Function>)
15 changes: 15 additions & 0 deletions src/main/kotlin/org/casc/lang/ast/TypeInstance.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.casc.lang.ast

import org.casc.lang.table.Reference

sealed class TypeInstance(open val packageReference: Reference?, open val typeReference: Reference) {
abstract val fields: List<Field>
var impl: Impl? = null
var traitImpls: List<TraitImpl>? = null
var memberTypeInstances: List<TypeInstance>? = null
abstract val accessor: Accessor

val reference: Reference by lazy {
Reference(packageReference?.fullQualifiedPath, typeReference.fullQualifiedPath)
}
}
153 changes: 81 additions & 72 deletions src/main/kotlin/org/casc/lang/checker/Checker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.casc.lang.compilation.Warning
import org.casc.lang.table.*
import org.casc.lang.utils.getOrElse
import java.lang.reflect.Modifier
import kotlin.math.sign
import java.io.File as JFile

class Checker(private val preference: AbstractPreference) {
Expand All @@ -23,16 +22,16 @@ class Checker(private val preference: AbstractPreference) {
private lateinit var topScope: Scope
private var reports: MutableSet<Report> = mutableSetOf()

fun checkDeclaration(file: File): Triple<List<Report>, File, Scope> {
val (checkedClass, scope) = checkClassDeclaration(file.clazz, file.path)

file.clazz = checkedClass
fun checkDeclaration(file: File): Pair<List<Report>, Scope> {
val scope = when (val typeInstance = file.typeInstance) {
is ClassInstance -> checkClassDeclaration(typeInstance, file.path)
}

return Triple(reports.toList(), file, scope)
return reports.toList() to scope
}

private fun checkClassDeclaration(clazz: Class, filePath: String): Pair<Class, Scope> {
topScope = Scope(preference, companion = false, mutable = false, Accessor.Pub, clazz.getReference())
private fun checkClassDeclaration(clazz: ClassInstance, filePath: String): Scope {
topScope = Scope(preference, companion = false, mutable = false, Accessor.Pub, clazz.reference)

val classScope = Scope(
topScope, false, // TODO: Allow (nested) class has `comp`
Expand All @@ -51,48 +50,52 @@ class Checker(private val preference: AbstractPreference) {
}
}

checkIdentifierIsKeyword(clazz.name)
checkIdentifierIsKeyword(clazz.typeReference.fullQualifiedPath, clazz.typeReference.pos)

if (clazz.parentClassReference != null) {
val parentClassType = findType(clazz.parentClassReference, classScope)
val parentClassType = findType(clazz.parentClassReference, classScope)

classScope.parentClassPath = parentClassType?.getReference()
classScope.parentClassPath = parentClassType?.getReference()

if (parentClassType != null) {
if (parentClassType !is ClassType) {
reports += Error(
clazz.parentClassReference.pos,
"Cannot inherit from non-class type ${parentClassType.asCASCStyle()}"
)
} else if (!parentClassType.mutable) {
reports += Error(
clazz.parentClassReference.pos,
"Cannot inherit from final class ${parentClassType.asCASCStyle()}",
"Add `mut` to class ${parentClassType.asCASCStyle()}"
)
} else if (parentClassType.type(preference)?.let { Modifier.isFinal(it.modifiers) } == true) {
reports += Error(
clazz.parentClassReference.pos,
"Cannot inherit from final class ${parentClassType.asCASCStyle()}",
"Add `mut` to class ${parentClassType.asCASCStyle()}"
)
}
} else {
reports.reportUnknownTypeSymbol(clazz.parentClassReference)
if (parentClassType != null) {
if (parentClassType !is ClassType) {
reports += Error(
clazz.parentClassReference.pos ?: clazz.classKeyword?.pos,
"Cannot inherit from non-class type ${parentClassType.asCASCStyle()}"
)
} else if (parentClassType != ClassType.OBJECT_TYPE && !parentClassType.mutable) {
reports += Error(
clazz.parentClassReference.pos ?: clazz.classKeyword?.pos,
"Cannot inherit from final class ${parentClassType.asCASCStyle()}",
"Add `mut` to class ${parentClassType.asCASCStyle()}"
)
} else if (parentClassType.type(preference)?.let { Modifier.isFinal(it.modifiers) } == true) {
reports += Error(
clazz.parentClassReference.pos ?: clazz.classKeyword?.pos,
"Cannot inherit from final class ${parentClassType.asCASCStyle()}",
"Add `mut` to class ${parentClassType.asCASCStyle()}"
)
}
} else {
reports.reportUnknownTypeSymbol(clazz.parentClassReference)
}

clazz.fields.forEach {
checkField(it, classScope)
}
clazz.constructors = clazz.constructors.map {
checkConstructor(it, classScope)
}
clazz.functions = clazz.functions.map {
checkFunction(it, classScope)

if (clazz.impl != null) {
val impl = clazz.impl!!

impl.constructors.forEach {
checkConstructor(it, classScope)
}

impl.functions.forEach {
checkFunction(it, classScope)
}
}

return clazz to classScope
return classScope
}

fun check(file: File, classScope: Scope): Pair<List<Report>, File> {
Expand All @@ -101,12 +104,12 @@ class Checker(private val preference: AbstractPreference) {
return reports.toList() to checkedFile
}

private fun checkIdentifierIsKeyword(identifierToken: Token?, isVariable: Boolean = false) {
if (isVariable && (identifierToken?.literal == "self" || identifierToken?.literal == "super")) return
private fun checkIdentifierIsKeyword(literal: String?, position: Position? = null, isVariable: Boolean = false) {
if (isVariable && (literal == "self" || literal == "super")) return

if (Token.keywords.contains(identifierToken?.literal)) {
if (Token.keywords.contains(literal)) {
reports += Error(
identifierToken?.pos, "Cannot use ${identifierToken?.literal} as identifier since it's a keyword"
position, "Cannot use $literal as identifier since it's a keyword"
)
}
}
Expand Down Expand Up @@ -143,16 +146,20 @@ class Checker(private val preference: AbstractPreference) {
}

private fun checkFile(file: File, classScope: Scope): File {
file.clazz = checkClass(file.clazz, classScope)
when (val typeInstance = file.typeInstance) {
is ClassInstance -> checkClass(file, typeInstance, classScope)
}

return file
}

private fun checkClass(clazz: Class, classScope: Scope): Class {
topScope = Scope(preference, companion = false, mutable = false, Accessor.Pub, clazz.getReference())
private fun checkClass(file: File, clazz: ClassInstance, classScope: Scope) {
topScope = Scope(preference, companion = false, mutable = false, Accessor.Pub, clazz.reference)

clazz.usages.mapNotNull {
it!!.tokens.forEach(::checkIdentifierIsKeyword)
file.usages.map {
it.tokens.forEach { token ->
checkIdentifierIsKeyword(token?.literal, token?.pos)
}

if (topScope.usages.find { usage -> usage.fullQualifiedPath == it.fullQualifiedPath } != null) {
// Using an already used package or class
Expand Down Expand Up @@ -200,30 +207,32 @@ class Checker(private val preference: AbstractPreference) {

val companionBlockScope = Scope(classScope)

clazz.companionBlock.forEach {
checkStatement(it, companionBlockScope, PrimitiveType.Unit)
}
if (clazz.impl != null) {
val impl = clazz.impl!!

clazz.constructors.forEach {
checkConstructorBody(it, Scope(classScope))
}
impl.companionBlock.forEach {
checkStatement(it, companionBlockScope, PrimitiveType.Unit)
}

clazz.functions.forEach {
checkFunctionBody(it, Scope(classScope, isCompScope = it.selfKeyword == null))
impl.constructors.forEach {
checkConstructorBody(it, Scope(classScope))
}

if (!checkControlFlow(it.statements, it.returnType)) {
// Not all code path returns value
reports += Error(
it.name?.pos, "Not all code path returns value"
)
impl.functions.forEach {
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"
)
}
}
}

return clazz
}

private fun checkField(field: Field, scope: Scope): Field {
checkIdentifierIsKeyword(field.name)
checkIdentifierIsKeyword(field.name?.literal, field.name?.pos)

val fieldType = scope.findType(field.typeReference)

Expand All @@ -243,7 +252,7 @@ class Checker(private val preference: AbstractPreference) {
localScope.registerVariable(true, "self", TypeUtil.asType(localScope.classReference, preference))

val duplicateParameters = constructor.parameters.groupingBy {
checkIdentifierIsKeyword(it.name)
checkIdentifierIsKeyword(it.name?.literal, it.name?.pos)

it.name
}.eachCount().filter { it.value > 1 }
Expand Down Expand Up @@ -293,12 +302,12 @@ class Checker(private val preference: AbstractPreference) {

private fun checkFunction(function: Function, scope: Scope): Function {
val localScope = Scope(scope)
checkIdentifierIsKeyword(function.name)
checkIdentifierIsKeyword(function.name?.literal, function.name?.pos)

// Validate types first then register it to scope
// Check if parameter has duplicate names
val duplicateParameters = function.parameters.groupingBy {
checkIdentifierIsKeyword(it.name)
checkIdentifierIsKeyword(it.name?.literal, it.name?.pos)

it.name
}.eachCount().filter { it.value > 1 }
Expand Down Expand Up @@ -421,7 +430,7 @@ class Checker(private val preference: AbstractPreference) {
)
} else constructor.parentConstructorSignature = thisCallSignature
} else {
if (scope.parentClassPath != null) {
if (scope.parentClassPath != Reference.OBJECT_TYPE_REFERENCE && scope.parentClassPath != null) {
// Requires `super` call
reports += Error(
constructor.newKeyword?.pos,
Expand Down Expand Up @@ -507,7 +516,7 @@ class Checker(private val preference: AbstractPreference) {
reports += Error(
nameToken.pos, "Cannot declare `$name` as local variable", "Rename this variable"
)
} else checkIdentifierIsKeyword(nameToken)
} else checkIdentifierIsKeyword(nameToken.literal, nameToken.pos)

if (!scope.registerVariable(mutKeyword != null, name, type)) {
reports += Error(
Expand Down Expand Up @@ -656,7 +665,7 @@ class Checker(private val preference: AbstractPreference) {
val leftPreviousType = expression.leftExpression.previousExpression?.type
val name = expression.leftExpression.name!!.literal

fun checkFieldAssignment(field: ClassField?) {
fun checkFieldAssignment(field: TypeField?) {
if (field == null) {
reports += Error(
expression.leftExpression.pos, "Unknown identifier $name"
Expand Down Expand Up @@ -758,9 +767,9 @@ class Checker(private val preference: AbstractPreference) {
is IdentifierCallExpression -> {
val ownerReference = expression.ownerReference

checkIdentifierIsKeyword(expression.name, isVariable = true)
checkIdentifierIsKeyword(expression.name?.literal, expression.name?.pos, isVariable = true)

fun checkCompanionAccessibility(field: ClassField) {
fun checkCompanionAccessibility(field: TypeField) {
if (!field.companion && scope.isCompScope) {
reports += Error(
expression.name?.pos,
Expand Down
Loading

0 comments on commit 4d7d45d

Please sign in to comment.