[BIG REFACTOR] Create new project structure and prepare scaffolding for external modules system
This commit is contained in:
27
api/src/main/kotlin/io/smnp/callable/function/Function.kt
Normal file
27
api/src/main/kotlin/io/smnp/callable/function/Function.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
package io.smnp.callable.function
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.FunctionInvocationException
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
abstract class Function(val name: String) {
|
||||
private var definitions: List<FunctionDefinition> = mutableListOf()
|
||||
|
||||
abstract fun define(new: FunctionDefinitionTool)
|
||||
|
||||
init {
|
||||
definitions = FunctionDefinitionTool().apply { define(this) }.definitions
|
||||
}
|
||||
|
||||
fun call(environment: Environment, vararg arguments: Value): Value {
|
||||
val (definition, args) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
?: throw FunctionInvocationException(this, arguments)
|
||||
|
||||
return definition.body(environment, args.arguments)
|
||||
}
|
||||
|
||||
val signature: String
|
||||
get() = definitions.joinToString("\nor\n") { "$name${it.signature}" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.smnp.callable.function
|
||||
|
||||
import io.smnp.callable.signature.Signature
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class FunctionDefinition(val signature: Signature, val body: (Environment, List<Value>) -> Value)
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.smnp.callable.function
|
||||
|
||||
import io.smnp.callable.signature.Signature
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class FunctionDefinitionTool {
|
||||
val definitions: MutableList<FunctionDefinition> = mutableListOf()
|
||||
|
||||
infix fun function(signature: Signature): FunctionDefinitionToolStage2 {
|
||||
return FunctionDefinitionToolStage2(signature)
|
||||
}
|
||||
|
||||
inner class FunctionDefinitionToolStage2(private val signature: Signature) {
|
||||
infix fun define(body: (Environment, List<Value>) -> Value) {
|
||||
definitions.add(FunctionDefinition(signature, body))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
30
api/src/main/kotlin/io/smnp/callable/method/Method.kt
Normal file
30
api/src/main/kotlin/io/smnp/callable/method/Method.kt
Normal file
@@ -0,0 +1,30 @@
|
||||
package io.smnp.callable.method
|
||||
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.error.MethodInvocationException
|
||||
import io.smnp.type.matcher.Matcher
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
abstract class Method(val typeMatcher: Matcher, val name: String) {
|
||||
private var definitions: List<MethodDefinition> = mutableListOf()
|
||||
|
||||
abstract fun define(new: MethodDefinitionTool)
|
||||
|
||||
init {
|
||||
definitions = MethodDefinitionTool().apply { define(this) }.definitions
|
||||
}
|
||||
|
||||
fun verifyType(type: Value) = typeMatcher.match(type)
|
||||
|
||||
fun call(environment: Environment, obj: Value, vararg arguments: Value): Value {
|
||||
val (definition, args) = definitions
|
||||
.map { Pair(it, it.signature.parse(arguments.toList())) }
|
||||
.firstOrNull { (_, args) -> args.signatureMatched }
|
||||
?: throw MethodInvocationException(this, obj, arguments)
|
||||
|
||||
return definition.body(environment, obj, args.arguments)
|
||||
}
|
||||
|
||||
val signature: String
|
||||
get() = definitions.joinToString("\nor\n") { "$typeMatcher.$name${it.signature}" }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.smnp.callable.method
|
||||
|
||||
import io.smnp.callable.signature.Signature
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class MethodDefinition(val signature: Signature, val body: (Environment, Value, List<Value>) -> Value)
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.smnp.callable.method
|
||||
|
||||
import io.smnp.callable.signature.Signature
|
||||
import io.smnp.environment.Environment
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class MethodDefinitionTool {
|
||||
val definitions: MutableList<MethodDefinition> = mutableListOf()
|
||||
|
||||
infix fun method(signature: Signature): MethodDefinitionToolStage2 {
|
||||
return MethodDefinitionToolStage2(signature)
|
||||
}
|
||||
|
||||
inner class MethodDefinitionToolStage2(private val signature: Signature) {
|
||||
infix fun define(body: (Environment, Value, List<Value>) -> Value) {
|
||||
definitions.add(MethodDefinition(signature, body))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.smnp.callable.signature
|
||||
|
||||
import io.smnp.type.enumeration.DataType
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
object ActualSignatureFormatter {
|
||||
fun format(arguments: Array<out Value>, parentheses: Boolean = true): String {
|
||||
val output = mutableListOf<String>()
|
||||
for(argument in arguments) {
|
||||
output.add(when(argument.type) {
|
||||
DataType.LIST -> listTypes(
|
||||
argument.value as List<Value>
|
||||
)
|
||||
DataType.MAP -> mapTypes(
|
||||
argument.value as Map<Value, Value>
|
||||
)
|
||||
else -> argument.type.name.toLowerCase()
|
||||
})
|
||||
}
|
||||
|
||||
return if(parentheses) "(${output.joinToString()})" else output.joinToString()
|
||||
}
|
||||
|
||||
private fun listTypes(list: List<Value>, output: MutableList<String> = mutableListOf()): String {
|
||||
for (item in list) {
|
||||
output.add(when (item.type) {
|
||||
DataType.LIST -> listTypes(
|
||||
item.value as List<Value>
|
||||
)
|
||||
DataType.MAP -> mapTypes(
|
||||
item.value as Map<Value, Value>
|
||||
)
|
||||
else -> item.type.name.toLowerCase()
|
||||
})
|
||||
}
|
||||
|
||||
return "list<${output.toSet().joinToString()}>"
|
||||
}
|
||||
|
||||
private fun mapTypes(map: Map<Value, Value>, output: MutableMap<Value, String> = mutableMapOf()): String {
|
||||
for ((k, v) in map) {
|
||||
output[k] = when (v.type) {
|
||||
DataType.LIST -> listTypes(
|
||||
v.value as List<Value>
|
||||
)
|
||||
DataType.MAP -> mapTypes(
|
||||
v.value as Map<Value, Value>
|
||||
)
|
||||
else -> v.type.name.toLowerCase()
|
||||
}
|
||||
}
|
||||
|
||||
return "map<${output.keys.toSet().joinToString()}><${output.values.toSet().joinToString()}}>"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.smnp.callable.signature
|
||||
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class ArgumentsList(val signatureMatched: Boolean, val arguments: List<Value>) {
|
||||
operator fun get(index: Int) = arguments[index]
|
||||
|
||||
fun toArray() = arguments.toTypedArray()
|
||||
|
||||
override fun toString() = if(signatureMatched) "valid($arguments)" else "invalid"
|
||||
|
||||
companion object {
|
||||
fun valid(arguments: List<Value>): ArgumentsList {
|
||||
return ArgumentsList(true, arguments)
|
||||
}
|
||||
|
||||
fun invalid(): ArgumentsList {
|
||||
return ArgumentsList(false, emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
18
api/src/main/kotlin/io/smnp/callable/signature/Signature.kt
Normal file
18
api/src/main/kotlin/io/smnp/callable/signature/Signature.kt
Normal file
@@ -0,0 +1,18 @@
|
||||
package io.smnp.callable.signature
|
||||
|
||||
import io.smnp.type.matcher.Matcher
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
interface Signature {
|
||||
fun parse(arguments: List<Value>): ArgumentsList
|
||||
|
||||
companion object {
|
||||
fun simple(vararg signature: Matcher): Signature {
|
||||
return SimpleSignature(*signature)
|
||||
}
|
||||
|
||||
fun vararg(varargMatcher: Matcher, vararg signature: Matcher): Signature {
|
||||
return VarargSignature(varargMatcher, *signature)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.smnp.callable.signature
|
||||
|
||||
import io.smnp.type.matcher.Matcher
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class SimpleSignature(private vararg val signature: Matcher) :
|
||||
Signature {
|
||||
override fun parse(arguments: List<Value>): ArgumentsList {
|
||||
if (arguments.size > signature.size || arguments.size < signature.count { !it.optional }) {
|
||||
return ArgumentsList.invalid()
|
||||
}
|
||||
|
||||
return ArgumentsList(signature.zip(arguments).all { (matcher, argument) ->
|
||||
matcher.match(
|
||||
argument
|
||||
)
|
||||
}, arguments)
|
||||
}
|
||||
|
||||
override fun toString() = "(${signature.joinToString(", ")})"
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package io.smnp.callable.signature
|
||||
|
||||
import io.smnp.type.matcher.Matcher
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class VarargSignature(private val varargMatcher: Matcher, private vararg val signature: Matcher) :
|
||||
Signature {
|
||||
override fun parse(arguments: List<Value>): ArgumentsList {
|
||||
if ((arrayListOf(varargMatcher) + signature).any { it.optional }) {
|
||||
throw RuntimeException("Vararg signature does not support optional arguments")
|
||||
}
|
||||
|
||||
if (signature.size > arguments.size) {
|
||||
return ArgumentsList.invalid()
|
||||
}
|
||||
|
||||
for (i in signature.indices) {
|
||||
if (!signature[i].match(arguments[i])) {
|
||||
return ArgumentsList.invalid()
|
||||
}
|
||||
}
|
||||
|
||||
for (i in signature.size until arguments.size) {
|
||||
if (!varargMatcher.match(arguments[i])) {
|
||||
return ArgumentsList.invalid()
|
||||
}
|
||||
}
|
||||
|
||||
return ArgumentsList.valid(
|
||||
arguments.subList(0, signature.size) + listOf(
|
||||
Value.list(
|
||||
arguments.subList(
|
||||
signature.size,
|
||||
arguments.size
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun toString() =
|
||||
"(${signature.joinToString(", ")}${if (signature.isNotEmpty()) ", " else ""}...$varargMatcher)"
|
||||
}
|
||||
17
api/src/main/kotlin/io/smnp/data/entity/Note.kt
Normal file
17
api/src/main/kotlin/io/smnp/data/entity/Note.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package io.smnp.data.entity
|
||||
|
||||
import io.smnp.data.enumeration.Pitch
|
||||
|
||||
class Note private constructor(val pitch: Pitch, val octave: Int, val duration: Int, val dot: Boolean) {
|
||||
data class Builder(var pitch: Pitch = Pitch.A, var octave: Int = 4, var duration: Int = 4, var dot: Boolean = false) {
|
||||
fun pitch(pitch: Pitch) = apply { this.pitch = pitch }
|
||||
fun octave(octave: Int) = apply { this.octave = octave }
|
||||
fun duration(duration: Int) = apply { this.duration = duration }
|
||||
fun dot(dot: Boolean) = apply { this.dot = dot }
|
||||
fun build() = Note(pitch, octave, duration, dot)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "${pitch}${octave}:${duration}${if (dot) "d" else ""}"
|
||||
}
|
||||
}
|
||||
51
api/src/main/kotlin/io/smnp/data/enumeration/Pitch.kt
Normal file
51
api/src/main/kotlin/io/smnp/data/enumeration/Pitch.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package io.smnp.data.enumeration
|
||||
|
||||
enum class Pitch {
|
||||
C, C_S, D, D_S, E, F, F_S, G, G_S, A, A_S, H;
|
||||
|
||||
override fun toString(): String {
|
||||
return when(this) {
|
||||
C -> "C"
|
||||
C_S -> "C#"
|
||||
D -> "D"
|
||||
D_S -> "D#"
|
||||
E -> "E"
|
||||
F -> "F"
|
||||
F_S -> "F#"
|
||||
G -> "G"
|
||||
G_S -> "G#"
|
||||
A -> "A"
|
||||
A_S -> "A#"
|
||||
H -> "H"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parse(symbol: String): Pitch {
|
||||
return when(symbol.toLowerCase()) {
|
||||
"cb" -> H
|
||||
"c" -> C
|
||||
"c#" -> C_S
|
||||
"db" -> C_S
|
||||
"d" -> D
|
||||
"d#" -> D_S
|
||||
"eb" -> D_S
|
||||
"e" -> E
|
||||
"e#" -> F
|
||||
"fb" -> E
|
||||
"f" -> F
|
||||
"f#" -> F_S
|
||||
"gb" -> F_S
|
||||
"g" -> G
|
||||
"g#" -> G_S
|
||||
"ab" -> G_S
|
||||
"a" -> A
|
||||
"a#" -> A_S
|
||||
"b" -> A_S
|
||||
"h" -> H
|
||||
"h#" -> C
|
||||
else -> throw RuntimeException("Unknown pitch symbol")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
api/src/main/kotlin/io/smnp/environment/Environment.kt
Normal file
16
api/src/main/kotlin/io/smnp/environment/Environment.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package io.smnp.environment
|
||||
|
||||
import io.smnp.type.module.Module
|
||||
|
||||
class Environment {
|
||||
private val rootModule = Module("<root>")
|
||||
private val loadedModules = mutableListOf<String>()
|
||||
|
||||
fun loadModule(path: String) {
|
||||
|
||||
}
|
||||
|
||||
fun printModules(printContent: Boolean) {
|
||||
rootModule.pretty(printContent)
|
||||
}
|
||||
}
|
||||
3
api/src/main/kotlin/io/smnp/error/CustomException.kt
Normal file
3
api/src/main/kotlin/io/smnp/error/CustomException.kt
Normal file
@@ -0,0 +1,3 @@
|
||||
package io.smnp.error
|
||||
|
||||
class CustomException(message: String?) : Exception(message)
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.smnp.error
|
||||
|
||||
import io.smnp.callable.function.Function
|
||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class FunctionInvocationException(private val function: Function, private val passedArguments: Array<out Value>) : Exception() {
|
||||
override fun toString() = "Invalid signature: ${function.name}${format(passedArguments)}\nAllowed signatures:\n${function.signature}"
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.smnp.error
|
||||
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.callable.signature.ActualSignatureFormatter.format
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class MethodInvocationException(private val method: Method, private val obj: Value, private val passedArguments: Array<out Value>) : Exception() {
|
||||
override fun toString() = "Invalid signature: $obj.${method.name}${format(passedArguments)}\nAllowed signatures:\n${method.signature}"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package io.smnp.error
|
||||
|
||||
class ShouldNeverReachThisLineException : Exception(
|
||||
"This exception should never be thrown. Please check stack trace and investigate the source of error."
|
||||
)
|
||||
45
api/src/main/kotlin/io/smnp/type/enumeration/DataType.kt
Normal file
45
api/src/main/kotlin/io/smnp/type/enumeration/DataType.kt
Normal file
@@ -0,0 +1,45 @@
|
||||
package io.smnp.type.enumeration
|
||||
|
||||
import io.smnp.data.entity.Note
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
enum class DataType(val kotlinType: KClass<out Any>?) {
|
||||
INT(Int::class),
|
||||
FLOAT(Float::class),
|
||||
STRING(String::class),
|
||||
LIST(List::class),
|
||||
MAP(Map::class),
|
||||
NOTE(Note::class),
|
||||
BOOL(Boolean::class),
|
||||
TYPE(DataType::class),
|
||||
VOID(null);
|
||||
|
||||
fun isInstance(value: Any?): Boolean {
|
||||
if(kotlinType == null) {
|
||||
return value == null
|
||||
}
|
||||
|
||||
return kotlinType.isInstance(value)
|
||||
}
|
||||
|
||||
fun isNumeric(): Boolean {
|
||||
return this == INT || this == FLOAT
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return super.toString().toLowerCase()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun inference(value: Any?): DataType {
|
||||
if (value == null) {
|
||||
return VOID
|
||||
}
|
||||
|
||||
return values()
|
||||
.filter { it.kotlinType != null }
|
||||
.find { it.kotlinType!!.isInstance(value) }
|
||||
?: throw RuntimeException("Cannot inference type for '$value'")
|
||||
}
|
||||
}
|
||||
}
|
||||
131
api/src/main/kotlin/io/smnp/type/matcher/Matcher.kt
Normal file
131
api/src/main/kotlin/io/smnp/type/matcher/Matcher.kt
Normal file
@@ -0,0 +1,131 @@
|
||||
package io.smnp.type.matcher
|
||||
|
||||
import io.smnp.type.enumeration.DataType
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class Matcher(val type: DataType?, private val matcher: (Value) -> Boolean, private val string: String, val optional: Boolean = false) {
|
||||
|
||||
fun match(value: Value): Boolean =
|
||||
(type != null && type == value.type && matcher(value)) || (type == null) && matcher(value)
|
||||
|
||||
infix fun and(matcher: Matcher): Matcher {
|
||||
if (type != matcher.type) {
|
||||
throw RuntimeException("Matchers' supported type are different to each other: $type != ${matcher.type}")
|
||||
}
|
||||
|
||||
val string = "[${this.string} and ${matcher.string}]"
|
||||
return Matcher(type, { this.match(it) && matcher.match(it) }, string)
|
||||
}
|
||||
|
||||
infix fun or(matcher: Matcher): Matcher {
|
||||
if (type != matcher.type) {
|
||||
throw RuntimeException("Matchers' supported type are different to each other: $type != ${matcher.type}")
|
||||
}
|
||||
|
||||
val string = "[${this.string} and ${matcher.string}]"
|
||||
return Matcher(type, { this.match(it) || matcher.match(it) }, string)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = toString() == other.toString()
|
||||
|
||||
override fun toString() = string
|
||||
|
||||
companion object {
|
||||
fun optional(matcher: Matcher): Matcher {
|
||||
return Matcher(
|
||||
matcher.type,
|
||||
matcher.matcher,
|
||||
"${matcher.string}?",
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
fun mapOfMatchers(keyMatchers: List<Matcher>, valueMatchers: List<Matcher>): Matcher {
|
||||
return Matcher(
|
||||
DataType.MAP,
|
||||
{
|
||||
(it.value!! as Map<Value, Value>).entries.all { (k, v) ->
|
||||
keyMatchers.any { m -> m.match(k) } && valueMatchers.any { m ->
|
||||
m.match(
|
||||
v
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
"map<${keyMatchers.joinToString(", ") { it.toString() }}><${valueMatchers.joinToString(", ") { it.toString() }}>"
|
||||
)
|
||||
}
|
||||
|
||||
fun recursiveListMatcher(matcher: Matcher): Matcher {
|
||||
if (matcher.type == DataType.LIST) {
|
||||
throw RuntimeException("Passed matcher will be handling non-list types, so it cannot have supported type of ${DataType.LIST}")
|
||||
}
|
||||
|
||||
fun match(value: Value): Boolean {
|
||||
if (value.type != DataType.LIST) {
|
||||
return matcher.match(value)
|
||||
}
|
||||
|
||||
return (value.value!! as List<Value>).all { match(it) }
|
||||
}
|
||||
|
||||
return Matcher(
|
||||
DataType.LIST,
|
||||
{ match(it) },
|
||||
"[LIST OF $matcher]"
|
||||
)
|
||||
}
|
||||
|
||||
fun listOfMatchers(vararg matchers: Matcher): Matcher {
|
||||
return Matcher(
|
||||
DataType.LIST,
|
||||
{ list ->
|
||||
(list.value as List<Value>).all { item ->
|
||||
matchers.any {
|
||||
it.match(
|
||||
item
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
"list<${matchers.joinToString(", ") { it.toString() }}>"
|
||||
)
|
||||
}
|
||||
|
||||
fun listOf(vararg types: DataType): Matcher {
|
||||
return Matcher(
|
||||
DataType.LIST,
|
||||
{ list -> (list.value as List<Value>).all { it.type in types } },
|
||||
"list<${types.joinToString(", ") { it.name.toLowerCase() }}>"
|
||||
)
|
||||
}
|
||||
|
||||
fun allTypes(): Matcher {
|
||||
return Matcher(
|
||||
null,
|
||||
{ it.type != DataType.VOID },
|
||||
"any"
|
||||
)
|
||||
}
|
||||
|
||||
fun ofTypes(vararg types: DataType): Matcher {
|
||||
return Matcher(
|
||||
null,
|
||||
{ it.type in types },
|
||||
"<${types.joinToString(", ") { it.name.toLowerCase() }}>"
|
||||
)
|
||||
}
|
||||
|
||||
fun ofType(type: DataType): Matcher {
|
||||
return Matcher(null, { it.type == type }, type.name.toLowerCase())
|
||||
}
|
||||
|
||||
fun oneOf(vararg matchers: Matcher): Matcher {
|
||||
return Matcher(
|
||||
null,
|
||||
{ value -> matchers.any { matcher -> matcher.match(value) } },
|
||||
"<${matchers.joinToString(", ") { it.toString() }}>"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
87
api/src/main/kotlin/io/smnp/type/model/Value.kt
Normal file
87
api/src/main/kotlin/io/smnp/type/model/Value.kt
Normal file
@@ -0,0 +1,87 @@
|
||||
package io.smnp.type.model
|
||||
|
||||
import io.smnp.data.entity.Note
|
||||
import io.smnp.error.ShouldNeverReachThisLineException
|
||||
import io.smnp.type.enumeration.DataType
|
||||
|
||||
class Value private constructor(val type: DataType, val value: Any?, val properties: Map<String, Value> = emptyMap()) {
|
||||
init {
|
||||
if(!type.isInstance(value)) {
|
||||
throw RuntimeException("'$value' is not of type $type")
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$type($value)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun int(value: Int): Value {
|
||||
return Value(DataType.INT, value)
|
||||
}
|
||||
|
||||
fun float(value: Float): Value {
|
||||
return Value(
|
||||
DataType.FLOAT,
|
||||
value
|
||||
)
|
||||
}
|
||||
|
||||
fun numeric(value: Number): Value {
|
||||
return when(value::class) {
|
||||
Int::class -> int(value.toInt())
|
||||
Float::class -> float(value.toFloat())
|
||||
else -> throw ShouldNeverReachThisLineException()
|
||||
}
|
||||
}
|
||||
|
||||
fun string(value: String): Value {
|
||||
return Value(
|
||||
DataType.STRING, value, hashMapOf(
|
||||
Pair("length", int(value.length))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun list(value: List<Value>): Value {
|
||||
return Value(
|
||||
DataType.LIST, value, hashMapOf(
|
||||
Pair("size", int(value.size))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun map(value: Map<Value, Value>): Value {
|
||||
return Value(
|
||||
DataType.MAP, value, hashMapOf(
|
||||
Pair("size", int(value.size)),
|
||||
Pair("keys", list(value.keys.toList())),
|
||||
Pair("values", list(value.values.toList()))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun note(value: Note): Value {
|
||||
return Value(
|
||||
DataType.NOTE, value, hashMapOf(
|
||||
Pair("pitch", string(value.pitch.toString())),
|
||||
Pair("octave", int(value.octave)),
|
||||
Pair("duration", int(value.duration)),
|
||||
Pair("dot", bool(value.dot))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun bool(value: Boolean): Value {
|
||||
return Value(DataType.BOOL, value)
|
||||
}
|
||||
|
||||
fun type(value: DataType): Value {
|
||||
return Value(DataType.TYPE, value)
|
||||
}
|
||||
|
||||
fun void(): Value {
|
||||
return Value(DataType.VOID, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
130
api/src/main/kotlin/io/smnp/type/module/Module.kt
Normal file
130
api/src/main/kotlin/io/smnp/type/module/Module.kt
Normal file
@@ -0,0 +1,130 @@
|
||||
package io.smnp.type.module
|
||||
|
||||
import io.smnp.callable.function.Function
|
||||
import io.smnp.callable.method.Method
|
||||
import io.smnp.type.model.Value
|
||||
|
||||
class Module(
|
||||
val name: String,
|
||||
functions: List<Function> = emptyList(),
|
||||
methods: List<Method> = emptyList(),
|
||||
children: List<Module> = emptyList()
|
||||
) {
|
||||
private var parent: Module? = null
|
||||
private val children = mutableListOf<Module>()
|
||||
private val functions = functions.toMutableList()
|
||||
private val methods = methods.toMutableList()
|
||||
|
||||
init {
|
||||
children.forEach { addSubmodule(it) }
|
||||
}
|
||||
|
||||
fun addSubmodule(module: Module) {
|
||||
module.parent = this
|
||||
children.indexOfFirst { it.name == module.name }.let {
|
||||
if (it > -1) {
|
||||
children[it] = children[it].merge(module)
|
||||
} else {
|
||||
children.add(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun attachTo(module: Module) {
|
||||
module.addSubmodule(this)
|
||||
}
|
||||
|
||||
val canonicalName: String
|
||||
get() {
|
||||
val modules = mutableListOf(this)
|
||||
var par = parent
|
||||
while(par != null) {
|
||||
modules.add(par)
|
||||
par = par.parent
|
||||
}
|
||||
|
||||
return modules.reversed().joinToString(".") { it.name }
|
||||
}
|
||||
|
||||
fun merge(module: Module): Module {
|
||||
if(name != module.name) {
|
||||
return this
|
||||
}
|
||||
|
||||
val functions = functions + module.functions
|
||||
val methods = methods + module.methods
|
||||
|
||||
val commonAndMyChildren = children.map { child ->
|
||||
module.children.find { it.name == child.name }?.merge(child) ?: child
|
||||
}
|
||||
|
||||
val moduleChildren = module.children.filter { child -> children.none { it.name == child.name } }
|
||||
|
||||
return Module(
|
||||
name,
|
||||
functions,
|
||||
methods,
|
||||
(commonAndMyChildren + moduleChildren).toMutableList()
|
||||
)
|
||||
}
|
||||
|
||||
fun findFunction(name: String): List<Function> {
|
||||
return functions.filter { it.name == name } + children.flatMap { it.findFunction(name) }
|
||||
}
|
||||
|
||||
fun findMethod(value: Value, name: String): List<Method> {
|
||||
return methods.filter { it.name == name && it.verifyType(value) } + children.flatMap { it.findMethod(value, name) }
|
||||
}
|
||||
|
||||
override fun toString() = name
|
||||
|
||||
fun pretty(printContent: Boolean = false, prefix: String = "", last: Boolean = true, first: Boolean = true) {
|
||||
var newPrefix = prefix
|
||||
var newLast = last
|
||||
|
||||
println(newPrefix + (if (first) "" else if (newLast) "└─ " else "├─ ") + name)
|
||||
newPrefix += if (newLast) " " else "│ "
|
||||
if(printContent) {
|
||||
val contentPrefix = newPrefix + if (children.isNotEmpty()) "|" else ""
|
||||
for ((index, function) in functions.withIndex()) {
|
||||
println(contentPrefix + (if (index == functions.size - 1 && methods.isEmpty()) "└ " else "├ ") + "${function.name}()")
|
||||
}
|
||||
|
||||
for ((index, method) in methods.withIndex()) {
|
||||
println(contentPrefix + (if (index == methods.size - 1) "└ " else "├ ") + "${method.typeMatcher}.${method.name}()")
|
||||
}
|
||||
}
|
||||
|
||||
for ((index, child) in children.withIndex()) {
|
||||
newLast = index == children.size - 1
|
||||
child.pretty(printContent, newPrefix, newLast, false)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
path: String,
|
||||
functions: List<Function> = emptyList(),
|
||||
methods: List<Method> = emptyList(),
|
||||
children: List<Module> = emptyList()
|
||||
): Module {
|
||||
val modules = path.split(".")
|
||||
if(modules.isEmpty()) {
|
||||
return Module(path, functions, methods, children)
|
||||
}
|
||||
|
||||
val root = modules.map { Module(it) }.reduceRight { m, n -> m.addSubmodule(n); m }
|
||||
|
||||
var youngest = root
|
||||
while(youngest.children.isNotEmpty()) {
|
||||
youngest = youngest.children[0]
|
||||
}
|
||||
|
||||
youngest.functions.addAll(functions)
|
||||
youngest.methods.addAll(methods)
|
||||
youngest.children.addAll(children)
|
||||
|
||||
return root
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user