Add a scripting system.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-06-03 15:46:23 +02:00
parent 6ea8f90903
commit 9718056260
2 changed files with 146 additions and 0 deletions

View file

@ -0,0 +1,88 @@
package com.pixelized.rplexicon.script
import com.pixelized.rplexicon.data.model.alteration.Alteration
sealed class Script(
val predicate: ScriptExecutor.() -> Boolean
) {
class OR(
private vararg val scripts: Script,
) : Script(predicate = {
scripts.any { it.predicate.invoke(this) }
}) {
override fun toString(): String {
return scripts.joinToString(
prefix = "(",
postfix = ")",
separator = " || "
) { it.toString() }
}
}
class AND(
private vararg val scripts: Script,
) : Script(predicate = {
scripts.all { it.predicate.invoke(this) }
}) {
override fun toString(): String {
return scripts.joinToString(
prefix = "(",
postfix = ")",
separator = " && "
) { it.toString() }
}
}
class ACTIVE(
val alteration: String,
) : Script(predicate = {
alterations.any { it.name == alteration }
}) {
override fun toString(): String {
return "ACTIVE('$alteration')"
}
}
class ANY(
val alteration: String,
) : Script(predicate = {
alterations.any { it.name.contains(alteration) }
}) {
override fun toString(): String {
return "ANY('$alteration')"
}
}
class NOT(
val alteration: String,
) : Script(predicate = {
alterations.none { it.name == alteration }
}) {
override fun toString(): String {
return "NOT('$alteration')"
}
}
class NONE(
val alteration: String,
) : Script(predicate = {
alterations.none { it.name.contains(alteration) }
}) {
override fun toString(): String {
return "NONE('$alteration')"
}
}
operator fun ScriptExecutor.invoke() = predicate()
}
class ScriptExecutor(
val alterations: List<Alteration>,
)
fun scriptExecutor(
alterations: List<Alteration>,
block: ScriptExecutor.() -> Boolean
): Boolean {
return ScriptExecutor(alterations = alterations).run(block)
}

View file

@ -0,0 +1,58 @@
package com.pixelized.rplexicon.script
class ScriptParser {
companion object {
private val FUNCTION_REGEX = Regex("""^(\w+)\((?:(.*);(.*)|(.*))\)$""")
private const val AND = "AND"
private const val OR = "OR"
private const val IS_ACTIVE = "ACT"
private const val ANY = "ANY"
private const val NOT = "NOT"
private const val NONE = "NONE"
fun check(function: String, vararg params: String?, block: () -> Script): Script {
if (params.any { it.isNullOrEmpty() }) error("$function parameter malformed")
return block.invoke()
}
}
fun parse(value: String): Script {
val result = FUNCTION_REGEX.findAll(value)
return result.firstOrNull()?.let { matchResult ->
val (function, param1, param2, mono) = matchResult.destructured
when (function) {
AND -> check(AND, param1, param2) { Script.AND(parse(param1), parse(param2)) }
OR -> check(OR, param1, param2) { Script.OR(parse(param1), parse(param2)) }
IS_ACTIVE -> check(IS_ACTIVE, mono) { Script.ACTIVE(mono) }
ANY -> check(ANY, mono) { Script.ANY(mono) }
NOT -> check(NOT, mono) { Script.NOT(mono) }
NONE -> check(NONE, mono) { Script.NONE(mono) }
else -> error("Unrecognized function: $function")
}
} ?: error("Malformed script: should start with a function call.")
}
}
fun main() {
val parser = ScriptParser()
val script = listOf(
"""ACT(Hache de guerre en argent)""",
"""ACT(Forme sauvage : Loup)""",
"""ACT(Forme sauvage ; Chat)""",
"""AND(ACT(Hache de guerre en argent);NOT(Bouclier))""",
"""OR(AND(NOT(Hache);ACT(Épée));ACT(Bouclier))""",
"""NONE(Forme sauvage)""",
)
script.forEach {
println("Input << $it")
try {
println("Output >> ${parser.parse(it)}")
} catch (e: Exception) {
println("Output >> $e")
} finally {
println("")
}
}
}