From 08c84c1e67c11e12cd1f5b3d3c8d8f4a22657ece Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 4 Aug 2017 15:38:25 -0400 Subject: [PATCH] EKT --- src/main/kotlin/net/shadowfacts/ekt/EKT.kt | 74 +++++++++++++++++++ .../services/javax.script.ScriptEngineFactory | 1 + src/test/kotlin/net/shadowfacts/ekt/Test.kt | 17 +++++ 3 files changed, 92 insertions(+) create mode 100644 src/main/kotlin/net/shadowfacts/ekt/EKT.kt create mode 100644 src/main/resources/META-INF/services/javax.script.ScriptEngineFactory create mode 100644 src/test/kotlin/net/shadowfacts/ekt/Test.kt diff --git a/src/main/kotlin/net/shadowfacts/ekt/EKT.kt b/src/main/kotlin/net/shadowfacts/ekt/EKT.kt new file mode 100644 index 0000000..273069b --- /dev/null +++ b/src/main/kotlin/net/shadowfacts/ekt/EKT.kt @@ -0,0 +1,74 @@ +package net.shadowfacts.ekt + +import java.io.File +import javax.script.ScriptContext +import javax.script.ScriptEngineManager + +/** + * @author shadowfacts + */ +object EKT { + + private val startString = ":]" + private val endString = "[:" + private val startStringRegex = Regex("(?:^|[^\\\\])([:=])]\n?") + private val endStringRegex = Regex("\\[([:=])") + + private val scriptPrefix = """ +val _result = StringBuilder() +fun echo(s: Any) { _result.append(s) } +""" + private val scriptSuffix = """ +_result.toString() +""" + + private val manager by lazy { + ScriptEngineManager() + } + + fun render(template: String, data: Map): String { + @Suppress("NAME_SHADOWING") + var template = template + template = template.replace("\"", "\\\"") + template = startString + template + endString + template = template.replace(startStringRegex, { + var res = "\necho(\"\"\"" + if (it.groups[1]!!.value == "=") res = ")" + res + res + }) + template = template.replace(endStringRegex, { + var res = "\"\"\")\n" + if (it.groups[1]!!.value == "=") res += "echo(" + res + }) + + val script = scriptPrefix + template + scriptSuffix + + File("script.kts").apply { + if (!exists()) createNewFile() + writeText(script) + } + + return eval(script, data) as String + } + + fun render(template: File, data: Map): String { + return render(template.readText(), data) + } + + internal fun eval(script: String, data: Map = mapOf()): Any? { + val engine = manager.getEngineByExtension("kts") + val bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE) + bindings.putAll(data) + +// Hack to allow data to be accessed by name from template instead of via bindings map + val unwrapBindings = data.keys.map { + val type = data[it]!!::class.qualifiedName + "val $it = bindings[\"$it\"] as $type;" + }.joinToString("\n") + engine.eval(unwrapBindings) + + return engine.eval(script) + } + +} \ No newline at end of file diff --git a/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory new file mode 100644 index 0000000..fa84d5e --- /dev/null +++ b/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory @@ -0,0 +1 @@ +org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory diff --git a/src/test/kotlin/net/shadowfacts/ekt/Test.kt b/src/test/kotlin/net/shadowfacts/ekt/Test.kt new file mode 100644 index 0000000..b1a3cc6 --- /dev/null +++ b/src/test/kotlin/net/shadowfacts/ekt/Test.kt @@ -0,0 +1,17 @@ +package net.shadowfacts.ekt + +import java.io.File + +/** + * @author shadowfacts + */ +fun main(args: Array) { + val res = EKT.render(File("template.ekt"), mapOf( + "value" to 11 + )) + + File("result.txt").apply { + if (!exists()) createNewFile() + writeText(res) + } +} \ No newline at end of file