commonMain.com.sunnychung.lib.multiplatform.kotlite.model.Nodes.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlite-interpreter-jvm Show documentation
Show all versions of kotlite-interpreter-jvm Show documentation
A Kotlin Multiplatform library to interpret Kotlite code, which is a subset of Kotlin language, in runtime in a safe way.
The newest version!
package com.sunnychung.lib.multiplatform.kotlite.model
import com.sunnychung.lib.multiplatform.kotlite.Interpreter
import com.sunnychung.lib.multiplatform.kotlite.annotation.ModifyByAnalyzer
import com.sunnychung.lib.multiplatform.kotlite.error.CannotInferTypeException
import com.sunnychung.lib.multiplatform.kotlite.error.SemanticException
import com.sunnychung.lib.multiplatform.kotlite.extension.emptyToNull
import com.sunnychung.lib.multiplatform.kotlite.extension.resolveGenericParameterType
import kotlin.random.Random
fun generateId() = Random.nextInt()
sealed interface ASTNode {
val position: SourcePosition
fun toMermaid(): String
}
data class IntegerNode(override val position: SourcePosition, val value: Int) : ASTNode {
override fun toMermaid(): String {
return "${generateId()}[\"Integer ${value}\"]\n"
}
}
data class DoubleNode(override val position: SourcePosition, val value: Double) : ASTNode {
override fun toMermaid(): String {
return "${generateId()}[\"Double ${value}\"]\n"
}
}
data class LongNode(override val position: SourcePosition, val value: Long) : ASTNode {
override fun toMermaid(): String {
return "${generateId()}[\"Long ${value}\"]\n"
}
}
data class BooleanNode(override val position: SourcePosition, val value: Boolean) : ASTNode {
override fun toMermaid(): String {
return "${generateId()}[\"Boolean ${value}\"]\n"
}
}
data class ValueNode(override val position: SourcePosition, val value: RuntimeValue) : ASTNode {
override fun toMermaid(): String {
return "${generateId()}[\"Value ${value}\"]\n"
}
}
data object NullNode : ASTNode {
override val position: SourcePosition
get() = SourcePosition("", 1, 1)
override fun toMermaid(): String {
return "${generateId()}[\"Null\"]\n"
}
}
data class BinaryOpNode(override val position: SourcePosition, val node1: ASTNode, val node2: ASTNode, val operator: String, @ModifyByAnalyzer var type: TypeNode? = null,) : ASTNode {
@ModifyByAnalyzer var hasFunctionCall: Boolean? = null
@ModifyByAnalyzer var call: FunctionCallNode? = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Binary Op ${operator}\"]"
return "$self-->${node1.toMermaid()}\n$self-->${node2.toMermaid()}\n"
}
}
data class UnaryOpNode(override val position: SourcePosition, var node: ASTNode?, val operator: String, @ModifyByAnalyzer var type: TypeNode? = null,) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Unary Op ${operator}\"]"
return "$self-->${node?.toMermaid()}\n"
}
}
data class ScriptNode(override val position: SourcePosition, val nodes: List) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Script\"]"
return nodes.map { "$self-->${it.toMermaid()}\n" }.joinToString("\n")
}
}
/**
*
* @param position This value is only useful when the type is invalid and error detail is needed.
* Most of the time it can be set to `SourcePosition.NONE`.
*/
open class TypeNode(override val position: SourcePosition, val name: String, val arguments: List?, val isNullable: Boolean, @ModifyByAnalyzer var transformedRefName: String? = null) : ASTNode {
init {
if (arguments?.isEmpty() == true) {
throw IllegalArgumentException("empty argument")
}
if (name == "Function" && this !is FunctionTypeNode) {
throw IllegalArgumentException("function type node should be a FunctionTypeNode instance")
}
}
val nameWithNullable: String get() = "$name${if (isNullable) "?" else ""}"
open fun descriptiveName(): String = "$name${arguments?.let { "<${it.joinToString(", ") { it.descriptiveName() }}>" } ?: ""}${if (isNullable) "?" else ""}"
override fun toMermaid(): String {
val self = "${generateId()}[\"Type $name${if (isNullable) " ?" else ""}\"]"
return "$self\n" + (arguments?.joinToString("") { "$self-->${it.toMermaid()}\n" } ?: "")
}
open fun copy(isNullable: Boolean) = TypeNode(
position = position,
name = name,
arguments = arguments,
isNullable = isNullable,
)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is TypeNode) return false
// if (position != other.position) return false
if (name != other.name) return false
if (arguments != other.arguments) return false
if (isNullable != other.isNullable) return false
return true
}
override fun hashCode(): Int {
var result = 0 // position.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + (arguments?.hashCode() ?: 0)
result = 31 * result + isNullable.hashCode()
return result
}
override fun toString(): String = descriptiveName()
companion object {
val IGNORE = TypeNode(SourcePosition.NONE, "", null, false)
fun createRepeatedTypeNode(actualTypeName: String): TypeNode {
return TypeNode(
position = SourcePosition.NONE,
name = "",
arguments = listOf(TypeNode(
position = SourcePosition.NONE,
name = actualTypeName,
arguments = null,
isNullable = false,
)),
isNullable = false,
)
}
}
}
data class PropertyDeclarationNode(
override val position: SourcePosition,
val name: String,
val declaredModifiers: Set,
val typeParameters: List,
val receiver: TypeNode?,
val declaredType: TypeNode?,
val isMutable: Boolean,
val initialValue: ASTNode?,
val accessors: PropertyAccessorsNode? = null,
@ModifyByAnalyzer var transformedRefName: String? = null,
@ModifyByAnalyzer var inferredType: TypeNode? = null,
@ModifyByAnalyzer val inferredModifiers: MutableSet = mutableSetOf(),
) : ASTNode {
val type: TypeNode
get() = declaredType ?: inferredType ?: throw SemanticException(position, "Could not infer type for property `$name`")
val modifiers: Set
get() = declaredModifiers + inferredModifiers
override fun toMermaid(): String {
val self = "${generateId()}[\"Property Node `$name`\"]"
return "$self\n" +
(receiver?.let { "$self-- receiver type -->${it.toMermaid()}\n" } ?: "") +
(declaredType?.let { "$self-- declared type -->${it.toMermaid()}\n" } ?: "") +
(inferredType?.let { "$self-- inferred type -->${it.toMermaid()}\n" } ?: "") +
(initialValue?.let { "$self-- initial value -->${it.toMermaid()}\n" } ?: "")
}
}
data class AssignmentNode(val subject: ASTNode, val operator: String, val value: ASTNode, @ModifyByAnalyzer @Deprecated("To be removed") var transformedRefName: String? = null) : ASTNode {
/**
* Used to replace the call of the operator, e.g. "+=".
*/
@ModifyByAnalyzer var wholeFunctionCall: FunctionCallNode? = null
/**
* Only used when wholeFunctionCall is null. Used to replace the call of the operator excluding assignment, e.g. "+".
*/
@ModifyByAnalyzer var preAssignFunctionCall: FunctionCallNode? = null
/**
* Only used when wholeFunctionCall is null. Used to replace the call of the assignment, e.g. "=".
*/
@ModifyByAnalyzer var assignFunctionCall: FunctionCallNode? = null
override val position: SourcePosition get() = subject.position
override fun toMermaid(): String {
val self = "${generateId()}[\"Assignment Node `$operator`\"]"
return "$self-- subject -->${subject.toMermaid()}\n" +
"$self-- value -->${value.toMermaid()}\n"
}
}
open class VariableReferenceNode(override val position: SourcePosition, val variableName: String, @ModifyByAnalyzer var transformedRefName: String? = null, @ModifyByAnalyzer var ownerRef: PropertyOwnerInfo? = null, @ModifyByAnalyzer var type: TypeNode? = null) : ASTNode {
override fun toMermaid(): String = "${generateId()}[\"Variable Reference Node `$variableName`\"]"
}
/**
* Member names are the exact identifiers in Kotlin code
*/
enum class FunctionModifier {
operator, open, override, abstract, infix, nullaware
}
enum class FunctionValueParameterModifier {
vararg
}
enum class ClassModifier {
open, enum, abstract
}
enum class PropertyModifier {
open, override
}
data class FunctionValueParameterNode(override val position: SourcePosition, val name: String, val declaredType: TypeNode?, val defaultValue: ASTNode?, val modifiers: Set, @ModifyByAnalyzer var transformedRefName: String? = null) : ASTNode {
@ModifyByAnalyzer
var inferredType: TypeNode? = null
val type: TypeNode get() = declaredType ?: inferredType
?: throw CannotInferTypeException(position, "function value parameter type $name")
override fun toMermaid(): String {
val self = "${generateId()}[\"Function Value Parameter Node `$name` modifiers=[${modifiers.joinToString(", ")}] \"]"
return "$self\n" + (if (declaredType != null) "$self-- declared type -->${declaredType.toMermaid()}\n" else "") +
if (defaultValue != null) "$self-->${defaultValue.toMermaid()}\n" else ""
}
}
data class BlockNode(
val statements: List,
override val position: SourcePosition,
val type: ScopeType,
val format: FunctionBodyFormat,
@ModifyByAnalyzer var returnType: TypeNode? = null,
@ModifyByAnalyzer var returnTypeUpperBound: TypeNode? = null,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Block Node\"]"
return statements.joinToString("") { "$self-->${it.toMermaid()}\n" }
}
}
interface CallableNode {
val position: SourcePosition
val valueParameters: List
val typeParameters: List
val returnType: TypeNode
val receiverType: TypeNode?
val name: String?
val labelName: String?
fun execute(interpreter: Interpreter, receiver: RuntimeValue?, arguments: List, typeArguments: Map): RuntimeValue
}
open class FunctionDeclarationNode(
override val position: SourcePosition,
override val name: String,
val receiver: TypeNode? = null,
val declaredReturnType: TypeNode?,
override val valueParameters: List,
val body: BlockNode?,
override val typeParameters: List = emptyList(),
val extraTypeParameters: List = emptyList(), // for ad-hoc extension functions only. these type parameters have no type argument to resolve.
val declaredModifiers: Set = emptySet(),
@ModifyByAnalyzer var transformedRefName: String? = null,
@ModifyByAnalyzer var inferredReturnType: TypeNode? = null,
@ModifyByAnalyzer var isVararg: Boolean = false,
@ModifyByAnalyzer val inferredModifiers: MutableSet = mutableSetOf(),
) : ASTNode, CallableNode {
override val returnType: TypeNode
get() = declaredReturnType ?: inferredReturnType ?: throw CannotInferTypeException(position, "return type of function $name")
val modifiers: Set
get() = declaredModifiers + inferredModifiers
override val receiverType: TypeNode?
get() = receiver
override val labelName: String?
get() = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Function Node `$name` modifiers=[${modifiers.joinToString(", ")}]\"]"
return (declaredReturnType?.let { "$self-- type -->${it.toMermaid()}\n" } ?: "") +
(receiver?.let { "$self-- receiver -->${it.toMermaid()}\n" } ?: "") +
(body?.let { "$self--body-->${it.toMermaid()}\n" } ?: "")
}
fun resolveGenericParameterType(parameter: FunctionValueParameterNode): TypeNode {
return parameter.type.resolveGenericParameterType(typeParameters)
}
override fun execute(interpreter: Interpreter, receiver: RuntimeValue?, arguments: List, typeArguments: Map): RuntimeValue {
with (interpreter) {
return body?.eval() ?: throw RuntimeException("This function is not implemented")
}
}
open fun copy(
name: String = this.name,
receiver: TypeNode? = this.receiver,
declaredReturnType: TypeNode? = this.declaredReturnType,
typeParameters: List = this.typeParameters,
valueParameters: List = this.valueParameters,
modifiers: Set = this.modifiers,
body: BlockNode? = this.body,
transformedRefName: String? = this.transformedRefName,
inferredReturnType: TypeNode? = this.inferredReturnType,
): FunctionDeclarationNode {
if (this::class != FunctionDeclarationNode::class) {
throw UnsupportedOperationException("Copying subclasses is not supported")
}
return FunctionDeclarationNode(
position = position,
name = name,
receiver = receiver,
declaredReturnType = declaredReturnType,
typeParameters = typeParameters,
valueParameters = valueParameters,
declaredModifiers = modifiers,
body = body,
transformedRefName = transformedRefName,
inferredReturnType = inferredReturnType,
)
}
}
data class FunctionCallArgumentNode(override val position: SourcePosition, val index: Int, val name: String? = null, val value: ASTNode) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Function Argument Node #$index `$name`\"]"
return "$self-->${value.toMermaid()}\n"
}
}
data class FunctionCallNode(
val function: ASTNode,
val arguments: List,
val declaredTypeArguments: List,
override val position: SourcePosition,
val isSuperclassConstruction: Boolean = false,
@ModifyByAnalyzer var returnType: TypeNode? = null,
@ModifyByAnalyzer var functionRefName: String? = null,
@ModifyByAnalyzer var callableType: CallableType? = null,
@ModifyByAnalyzer var isSpecialFunction: Boolean? = null,
@ModifyByAnalyzer var inferredTypeArguments: List? = null,
@ModifyByAnalyzer var modifierFilter: SearchFunctionModifier? = null,
) : ASTNode {
val typeArguments: List
get() = declaredTypeArguments.emptyToNull() ?: inferredTypeArguments?.let { args ->
val nonNullArgs = args.filterNotNull()
if (nonNullArgs.size != args.size) {
null
} else {
nonNullArgs
}
} ?: emptyList()
override fun toMermaid(): String {
val self = "${generateId()}[\"Function Call\"]"
return "$self-- function -->${function.toMermaid()}\n" +
declaredTypeArguments.withIndex().joinToString("") { "$self-- \"type argument [${it.index}]\" -->${it.value.toMermaid()}\n" } +
arguments.withIndex().joinToString("") { "$self-- \"argument[${it.index}]\" -->${it.value.toMermaid()}\n" }
}
}
data class ReturnNode(override val position: SourcePosition, val value: ASTNode?, val returnToLabel: String, @ModifyByAnalyzer var returnToAddress: String) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Return Node `$returnToLabel`\"]"
return "$self${if (value != null) "-->${value.toMermaid()}" else "" }\n"
}
}
data class ContinueNode(override val position: SourcePosition, val returnToLabel: String, @ModifyByAnalyzer var returnToAddress: String) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Continue Node `$returnToLabel`\"]"
return self
}
}
data class BreakNode(override val position: SourcePosition, val returnToLabel: String, @ModifyByAnalyzer var returnToAddress: String) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Break Node `$returnToLabel`\"]"
return self
}
}
data class IfNode(override val position: SourcePosition, val condition: ASTNode, val trueBlock: BlockNode?, val falseBlock: BlockNode?, @ModifyByAnalyzer var type: TypeNode? = null,) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"If Node\"]"
return "$self-- condition -->${condition.toMermaid()}\n" +
(if (trueBlock != null) "$self-- true -->${trueBlock.toMermaid()}\n" else "") +
(if (falseBlock != null) "$self-- false -->${falseBlock.toMermaid()}\n" else "")
}
}
data class WhileNode(override val position: SourcePosition, val condition: ASTNode, val body: BlockNode?) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"While Node\"]"
return "$self-- condition -->${condition.toMermaid()}\n" +
"$self-- body -->${body?.toMermaid() ?: self}\n"
}
}
data class DoWhileNode(override val position: SourcePosition, val condition: ASTNode, val body: BlockNode?) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Do-While Node\"]"
return "$self-- condition -->${condition.toMermaid()}\n" +
"$self-- body -->${body?.toMermaid() ?: self}\n"
}
}
data class ClassParameterNode(
override val position: SourcePosition,
val isProperty: Boolean,
val isMutable: Boolean,
val modifiers: Set,
val parameter: FunctionValueParameterNode,
/**
* Used when this is a non-property class constructor parameter, where this parameter will have
* TWO transformedRefName:
* 1. While resolving default values of other constructor parameters (the one inside FunctionValueParameterNode is used)
* 2. While resolving initializers and default values of property declarations inside class body (this field is used)
*/
@ModifyByAnalyzer var transformedRefNameInBody: String? = null,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Class Primary Constructor Parameter Node isProperty=$isProperty isMutable=$isMutable\"]"
return "$self-->${parameter.toMermaid()}\n"
}
}
data class ClassPrimaryConstructorNode(override val position: SourcePosition, val parameters: List) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Class Primary Constructor Node\"]"
return "$self\n" +
parameters.joinToString("") { "$self-->${it.toMermaid()}\n" }
}
}
data class ClassInstanceInitializerNode(override val position: SourcePosition, val block: BlockNode) : ASTNode {
override fun toMermaid(): String {
return "${generateId()}[\"Class Init Node\"]-->${block.toMermaid()}\n"
}
}
data class ClassDeclarationNode(
override val position: SourcePosition,
val name: String,
val isInterface: Boolean, // TODO change to enum
val declaredModifiers: Set,
val typeParameters: List,
val primaryConstructor: ClassPrimaryConstructorNode?,
val superInvocations: List?,
val declarations: List,
val enumEntries: List = emptyList(),
@ModifyByAnalyzer var fullQualifiedName: String = name,
) : ASTNode {
@ModifyByAnalyzer val inferredModifiers: MutableSet = mutableSetOf()
val modifiers: Set
get() = declaredModifiers + inferredModifiers
override fun toMermaid(): String {
val self = "${generateId()}[\"${if (!isInterface) "Class" else "Interface"} Declaration Node `$name` modifiers=[${modifiers.joinToString(", ")}]\"]"
return "$self\n" +
(primaryConstructor?.let { "$self-- primary constructor -->${it.toMermaid()}\n" } ?: "") +
(superInvocations?.joinToString("") { "$self--super-->${it.toMermaid()}\n" } ?: "") +
declarations.joinToString("") { "$self-->${it.toMermaid()}\n" }
}
}
data class ClassMemberReferenceNode(override val position: SourcePosition, val name: String, @ModifyByAnalyzer var transformedRefName: String? = null) : ASTNode {
override fun toMermaid(): String = "${generateId()}[\"Class Member Reference Node `$name`\"]"
}
data class NavigationNode(
override val position: SourcePosition,
val subject: ASTNode,
val operator: String,
val member: ClassMemberReferenceNode,
@ModifyByAnalyzer var type: TypeNode? = null, // data type
@ModifyByAnalyzer var memberType: MemberType? = null,
@ModifyByAnalyzer var transformedRefName: String? = null, // for extension property use
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Navigation Node\"]"
return "$self-- subject -->${subject.toMermaid()}\n" +
"$self-- access -->${member.toMermaid()}\n"
}
enum class MemberType {
Direct, Extension, Enum
}
}
class PropertyAccessorsNode(
override val position: SourcePosition,
val type: TypeNode,
val getter: FunctionDeclarationNode?,
val setter: FunctionDeclarationNode?,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Navigation Node\"]"
return "$self\n${getter?.let {"$self-- getter -->${it.toMermaid()}\n"} ?: ""}\n" +
(setter?.let {"$self-- setter -->${it.toMermaid()}\n"} ?: "")
}
}
class StringLiteralNode(override val position: SourcePosition, val content: String) : ASTNode {
override fun toMermaid(): String = "${generateId()}[\"String Node `$content`\"]"
}
class StringFieldIdentifierNode(override val position: SourcePosition, fieldIdentifier: String) : VariableReferenceNode(position, fieldIdentifier) {
override fun toMermaid(): String = "${generateId()}[\"String Field Identifier Node `$variableName`\"]"
}
class StringNode(
override val position: SourcePosition,
val nodes: List
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"String Node\"]"
return "$self\n${nodes.joinToString("") {"$self-->${it.toMermaid()}\n"}}"
}
}
data class LambdaLiteralNode(
override val position: SourcePosition,
val declaredValueParameters: List,
val body: BlockNode,
val label: LabelNode?,
@ModifyByAnalyzer var type: FunctionTypeNode? = null,
@ModifyByAnalyzer var accessedRefs: SymbolReferenceSet? = null,
@ModifyByAnalyzer var parameterTypesUpperBound: List? = null,
@ModifyByAnalyzer var returnTypeUpperBound: TypeNode? = null,
@ModifyByAnalyzer override var receiverType: TypeNode? = null,
) : ASTNode, CallableNode {
@ModifyByAnalyzer var valueParameterIt: FunctionValueParameterNode? = null
override val labelName: String?
get() = label?.label
override val typeParameters: List = emptyList()
override val valueParameters: List
get() = if (declaredValueParameters.isEmpty() && parameterTypesUpperBound?.size == 1) {
if (valueParameterIt == null) {
valueParameterIt = FunctionValueParameterNode(
position = position,
name = "it",
declaredType = parameterTypesUpperBound!!.first(),
defaultValue = null,
modifiers = emptySet()
)
}
listOf(valueParameterIt!!)
} else {
declaredValueParameters
}
override val returnType: TypeNode
get() = type!!.returnType!!
override val name: String?
get() = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Lambda Node\"]"
return valueParameters.joinToString("") { "$self-- parameter -->${it.toMermaid() }\n" } +
"$self-->${body.toMermaid()}\n"
}
override fun execute(interpreter: Interpreter, receiver: RuntimeValue?, arguments: List, typeArguments: Map): RuntimeValue {
with (interpreter) {
return body.eval()
}
}
}
class FunctionTypeNode(
override val position: SourcePosition,
val receiverType: TypeNode? = null,
val parameterTypes: List?,
val returnType: TypeNode?,
isNullable: Boolean,
@ModifyByAnalyzer var extensionFunctionRefName: String? = null,
) : TypeNode(position, "Function", parameterTypes?.let { p -> returnType?.let { r -> p + r } }, isNullable) {
override fun descriptiveName(): String {
var s = "(${parameterTypes?.joinToString(", ") { it.descriptiveName() } ?: "?"}) -> ${returnType?.descriptiveName() ?: "?"}"
if (isNullable) s = "($s)?"
return s
}
override fun copy(isNullable: Boolean): FunctionTypeNode {
return FunctionTypeNode(
position = position,
receiverType = receiverType,
parameterTypes = parameterTypes,
returnType = returnType,
isNullable = isNullable,
)
}
override fun toMermaid(): String {
val self = "${generateId()}[\"Function Type $name${if (isNullable) " ?" else ""}\"]"
return "$self\n" +
(receiverType?.let { "$self- -receiver -->${it.toMermaid()}" } ?: "") +
parameterTypes!!.joinToString("") { "$self-- parameter -->${it.toMermaid()}\n" } +
"$self-- return -->${returnType!!.toMermaid()}"
}
}
class ClassTypeNode(val clazz: TypeNode) : TypeNode(clazz.position, "Class", listOf(clazz), false)
class CharNode(override val position: SourcePosition, val value: Char) : ASTNode {
override fun toMermaid(): String = "${generateId()}[\"Char Node `$value` (${value.code})\"]"
}
class AsOpNode(override val position: SourcePosition, val isNullable: Boolean, val expression: ASTNode, val type: TypeNode) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"As${if (isNullable) "?" else ""} Node\"]"
return "$self-- expr -->${expression.toMermaid()}\n" +
"$self-- type -->${type.toMermaid()}"
}
}
class TypeParameterNode(override val position: SourcePosition, val name: String, val typeUpperBound: TypeNode?): ASTNode {
override fun toMermaid(): String = "${generateId()}[\"Type Parameter Node `$name`\"]" +
(typeUpperBound?.let { "--type upper bound -->${it.toMermaid()}" } ?: "")
}
fun TypeParameterNode.typeUpperBoundOrAny() = typeUpperBound ?: TypeNode(position, "Any", null, true)
class IndexOpNode(override val position: SourcePosition, val subject: ASTNode, val arguments: List): ASTNode {
@ModifyByAnalyzer var hasFunctionCall: Boolean? = null
@ModifyByAnalyzer var call: FunctionCallNode? = null
@ModifyByAnalyzer var type: TypeNode? = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Index Op Node\"]"
return "$self-- subject -->${subject.toMermaid()}\n" +
arguments.mapIndexed { index, it ->
"$self-- \"argument[$index]\" -->${it.toMermaid()}"
}.joinToString("\n")
}
}
data class InfixFunctionCallNode(
override val position: SourcePosition,
val node1: ASTNode,
val node2: ASTNode,
val functionName: String,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
@ModifyByAnalyzer var call: FunctionCallNode? = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Infix Function Call ${functionName}\"]"
return "$self-->${node1.toMermaid()}\n$self-->${node2.toMermaid()}\n"
}
}
data class ElvisOpNode(
override val position: SourcePosition,
val primaryNode: ASTNode,
val fallbackNode: ASTNode,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Elvis Op\"]"
return "$self--primary-->${primaryNode.toMermaid()}\n$self--fallback-->${fallbackNode.toMermaid()}\n"
}
}
data class ThrowNode(override val position: SourcePosition, val value: ASTNode) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Throw\"]"
return "$self-->${value.toMermaid()}"
}
}
data class TryNode(
override val position: SourcePosition,
val mainBlock: BlockNode,
val catchBlocks: List,
val finallyBlock: BlockNode?,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Try\"]"
return "$self--exec-->${mainBlock.toMermaid()}" +
catchBlocks.withIndex().joinToString { "\n$self-- \"catch[${it.index}]\" -->${it.value.toMermaid()}" } +
(finallyBlock?.let { "\n$self--finally-->${it.toMermaid()}" } ?: "")
}
}
data class CatchNode(
override val position: SourcePosition,
val valueName: String,
val catchType: TypeNode,
val block: BlockNode,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
@ModifyByAnalyzer var valueTransformedRefName: String? = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Catch (val `$valueName`)\"]"
return "$self--type-->${catchType.toMermaid()}" +
"$self--exec-->${block.toMermaid()}"
}
}
data class WhenSubjectNode(
override val position: SourcePosition,
val valueName: String?,
val declaredType: TypeNode?,
val value: ASTNode,
@ModifyByAnalyzer var valueTransformedRefName: String? = null,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
fun hasValueDeclaration(): Boolean = valueName != null
override fun toMermaid(): String {
val self = "${generateId()}[\"When subject${valueName?.let { " (val `$valueName`)" } ?: ""}\"]"
return "$self--expr-->${value.toMermaid()}" +
(declaredType?.let { "\n$self--type-->${it.toMermaid()}" } ?: "")
}
}
data class WhenConditionNode(
override val position: SourcePosition,
val testType: TestType,
val isNegateResult: Boolean,
val expression: ASTNode,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
@ModifyByAnalyzer var call: FunctionCallNode? = null
enum class TestType {
RangeTest, TypeTest, Regular
}
override fun toMermaid(): String {
val self = "${generateId()}[\"When condition; type = $testType\"]"
return "$self--expr-->${expression.toMermaid()}"
}
}
data class WhenEntryNode(
override val position: SourcePosition,
val conditions: List,
val body: BlockNode,
@ModifyByAnalyzer var bodyType: TypeNode? = null,
) : ASTNode {
fun isElseCondition(): Boolean = conditions.isEmpty()
override fun toMermaid(): String {
val self = "${generateId()}[\"When entry\"]"
return "$self--expr-->${body.toMermaid()}" +
conditions.joinToString { "\n$self-->${it.toMermaid()}" }
}
}
data class WhenNode(
override val position: SourcePosition,
val subject: WhenSubjectNode?,
val entries: List,
@ModifyByAnalyzer var type: TypeNode? = null,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"When\"]"
return entries.withIndex().joinToString("\n") { "$self-- \"entry[${it.index}]\" -->${it.value.toMermaid()}" } +
(subject?.let { "\n$self--subject-->${it.toMermaid()}" } ?: "")
}
}
data class LabelNode(
override val position: SourcePosition,
val label: String,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"Label '$label'\"]"
return self
}
}
data class EnumEntryNode(
override val position: SourcePosition,
val name: String,
val arguments: List,
) : ASTNode {
@ModifyByAnalyzer var call: FunctionCallNode? = null
override fun toMermaid(): String {
val self = "${generateId()}[\"Enum '$name'\"]"
return self + arguments.withIndex().joinToString {
"\n$self-- \"arg[${it.index}]\" -->${it.value.toMermaid()}"
}
}
}
data class ValueParameterDeclarationNode(
override val position: SourcePosition,
val name: String,
val declaredType: TypeNode?,
@ModifyByAnalyzer var transformedRefName: String? = null,
) : ASTNode {
@ModifyByAnalyzer
var inferredType: TypeNode? = null
val type: TypeNode
get() = declaredType ?: inferredType
?: throw CannotInferTypeException(position, "value parameter type $name")
override fun toMermaid(): String {
val self = "${generateId()}[\"Value Parameter '$name'\"]"
return self + declaredType?.let {
"\n$self--type-->${it.toMermaid()}"
}
}
}
data class ForNode(
override val position: SourcePosition,
val variables: List,
val subject: ASTNode,
val body: BlockNode,
) : ASTNode {
override fun toMermaid(): String {
val self = "${generateId()}[\"For\"]"
return self +
subject.let { "\n$self--subject-->${it.toMermaid()}" } +
body.let { "\n$self--body-->${it.toMermaid()}" } +
variables.withIndex().joinToString("") { "\n$self-- \"var[${it.index}]\" -->${it.value.toMermaid()}" }
}
}