From 3d68ae2f01afd31512ef8adab1b7ea6148449f45 Mon Sep 17 00:00:00 2001 From: Minjae Song Date: Mon, 10 Dec 2018 04:15:57 +0900 Subject: [PATCH] yaml: adding generic parse-as-invokable --- src/net/torvald/terrarum/Yaml.kt | 76 +++++++++++++++++++ .../terrarum/modulebasegame/BuildingMaker.kt | 11 +++ 2 files changed, 87 insertions(+) diff --git a/src/net/torvald/terrarum/Yaml.kt b/src/net/torvald/terrarum/Yaml.kt index 7cfbe4966..a11b8c94d 100644 --- a/src/net/torvald/terrarum/Yaml.kt +++ b/src/net/torvald/terrarum/Yaml.kt @@ -106,6 +106,61 @@ inline class Yaml(val text: String) { return root } + fun parseAsYamlInvokable(): QNDTreeNode> { + var currentIndentLevel = -1 + val root = QNDTreeNode>() + var currentNode = root + val nodesStack = Stack>>() + val validLineStartRe = Regex(""" *\- """) + + nodesStack.push(currentNode) + + text.split('\n') .forEach { + if (validLineStartRe.containsMatchIn(it)) { // take partial match; do the task if the text's line is valid + val indentLevel = it.countSpaces() + val it = it.trimIndent() + if (it.startsWith("- ")) { // just double check if indent-trimmed line looks valid + val nodeString = it.drop(2) + val nodeNameAndInvocation = nodeString.split(SEPARATOR) + val nodeName = nodeNameAndInvocation[0] + val nodeInvocation = loadClass(nodeNameAndInvocation[1]) + + val nameInvokePair = nodeName to nodeInvocation + + if (indentLevel == currentIndentLevel) { + val sibling = QNDTreeNode(nameInvokePair, currentNode.parent) + currentNode.parent!!.children.add(sibling) + currentNode = sibling + } + else if (indentLevel > currentIndentLevel) { + val childNode = QNDTreeNode(nameInvokePair, currentNode) + currentNode.children.add(childNode) + nodesStack.push(currentNode) + currentNode = childNode + currentIndentLevel = indentLevel + } + else { + repeat(currentIndentLevel - indentLevel) { currentNode = nodesStack.pop() } + currentIndentLevel = indentLevel + val sibling = QNDTreeNode(nameInvokePair, currentNode.parent) + currentNode.parent!!.children.add(sibling) + currentNode = sibling + } + } + } + } + + + // test traverse resulting tree + /*root.traversePreorder { node, depth -> + repeat(depth + 1) { print("-") } + println("${node.data} -> ${node.parent}") + }*/ + + + return root + } + private fun String.countSpaces(): Int { var c = 0 while (c <= this.length) { @@ -118,4 +173,25 @@ inline class Yaml(val text: String) { return c } + private fun loadClass(name: String): YamlInvokable { + val newClass = Class.forName(name) + val newClassConstructor = newClass.getConstructor(/* no args defined */) + val newClassInstance = newClassConstructor.newInstance(/* no args defined */) + return newClassInstance as YamlInvokable + } + +} + +/** + * A simple interface that meant to be attached with Yaml tree, so that the entry can be ```invoke()```d. + * + * Example usage in Yaml: + * ``` + * - File + * - Import : net.torvald.terrarum.whatever.package.ImportFile + * ``` + * + */ +interface YamlInvokable { + operator fun invoke() } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt index 30f5ada5c..214518523 100644 --- a/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt +++ b/src/net/torvald/terrarum/modulebasegame/BuildingMaker.kt @@ -6,6 +6,7 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch import net.torvald.terrarum.AppLoader import net.torvald.terrarum.IngameInstance import net.torvald.terrarum.Terrarum +import net.torvald.terrarum.Yaml import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gamecontroller.KeyToggler @@ -169,6 +170,16 @@ class BuildingMaker(batch: SpriteBatch) : IngameInstance(batch) { (Terrarum.WIDTH - notifier.width) / 2, Terrarum.HEIGHT - notifier.height) println("[BuildingMaker] Resize event") } + + + + private val menuYaml = Yaml(""" +- File + - New + - Export + - Import +- Edit + """.trimIndent()) }