diff options
Diffstat (limited to 'src/main/kotlin/moe/nea/shot/ShotParser.kt')
-rw-r--r-- | src/main/kotlin/moe/nea/shot/ShotParser.kt | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/main/kotlin/moe/nea/shot/ShotParser.kt b/src/main/kotlin/moe/nea/shot/ShotParser.kt new file mode 100644 index 0000000..648baa4 --- /dev/null +++ b/src/main/kotlin/moe/nea/shot/ShotParser.kt @@ -0,0 +1,149 @@ +package moe.nea.shot + +import java.util.* + +class ShotParser { + + private data class Builder( + val indentation: Int, + val handler: Handler + ) + + private interface Handler { + fun handleLine(line: String): Handler? + } + + private inner class MethodInjectionBuilder : Handler { + val annotations = mutableListOf<ClassRef>() + val parameterAnnotations = mutableMapOf<Int, MutableList<ClassRef>>() + override fun handleLine(line: String): Handler? { + if (line.startsWith("annotate ")) { + annotations.add(ClassRef(line.substring("annotate ".length).trim())) + return null + } + if (line.startsWith("annotateParameter ")) { + val (idx, name) = line.substring("annotateParameter ".length).split(" ") + parameterAnnotations.getOrPut(idx.toInt()) { mutableListOf() }.add(ClassRef(name)) + return null + } + error("Unknown directive") + } + } + + private inner class FieldInjectionBuilder : Handler { + val annotations = mutableListOf<ClassRef>() + override fun handleLine(line: String): Handler? { + if (line.startsWith("annotate ")) { + annotations.add(ClassRef(line.substring("annotate ".length).trim())) + return null + } + error("Unknown directive") + } + } + + private inner class ClassInjectionBuilder : Handler { + val methodHandlers = mutableMapOf<MethodRef, MethodInjectionBuilder>() + val fieldHandlers = mutableMapOf<FieldRef, FieldInjectionBuilder>() + val annotations = mutableListOf<ClassRef>() + override fun handleLine(line: String): Handler? { + if (line.endsWith(":")) { + val data = line.substring(0, line.length - 1).trim() + if (data.contains("(")) { + return methodHandlers.getOrPut(parseMethod(data)) { MethodInjectionBuilder() } + } + if (" " !in data) { + return fieldHandlers.getOrPut(FieldRef(data)) { FieldInjectionBuilder() } + } + error("Unknown condition") + } + if (line.startsWith("annotate ")) { + annotations.add(ClassRef(line.substring("annotate ".length).trim())) + return null + } + error("Unknown line $line") + } + + private fun parseMethod(data: String): MethodRef { + require(data.endsWith(")")) + val name = data.substringBefore("(") + val parameterDescriptor = data.substringAfter("(").dropLast(1) + val parameters = parameterDescriptor.split(",").filter { it.isNotBlank() }.map { + mapTypeToDescriptor(false, it.trim()) + } + return MethodRef(name, parameters.joinToString("", "(", ")")) + } + } + + private fun mapTypeToDescriptor(allowVoid: Boolean, name: String): String { + require(" " !in name) + if (name.endsWith("[]")) { + return "[" + mapTypeToDescriptor(allowVoid, name.substring(0, name.length - 2)) + } + return when (name) { + "void" -> if (allowVoid) "V" else error("Void not allowed") + "boolean" -> "Z" + "byte" -> "B" + "int" -> "I" + "double" -> "D" + "long" -> "J" + "short" -> "S" + "float" -> "F" + "char" -> "C" + else -> "L" + name.replace(".", "/") + ";" + } + } + + private inner class Root : Handler { + val map = mutableMapOf<ClassRef, ClassInjectionBuilder>() + override fun handleLine(line: String): Handler? { + require(line.endsWith(":")) + val className = ClassRef(line.substring(0, line.length - 1).trim()) + return map.getOrPut(className) { ClassInjectionBuilder() } + } + } + + private val root = Root() + private val indentations = Stack<Builder>().also { + it.add(Builder(0, root)) + } + private var nextHandler: Handler? = null + + fun parse(lines: Iterable<String>): Map<ClassRef, ClassShots> { + lines.forEach { parseLine(it) } + return root.map.mapValues { + ClassShots( + it.value.annotations, + it.value.methodHandlers.mapValues { + MethodShots( + it.value.annotations, + it.value.parameterAnnotations + ) + }, + it.value.fieldHandlers.mapValues { + FieldShots( + it.value.annotations + ) + } + ) + } + } + + fun parseLine(line: String) { + val indentation = line.takeWhile { it == ' ' }.length + + if (indentation > indentations.peek().indentation) { + indentations.push(Builder(indentation, nextHandler ?: error("Illegal increase in indentation"))) + nextHandler = null + } + while (indentation < indentations.peek().indentation) { + indentations.pop() + } + + val nonCommentedLine = line.substringBefore('#') + if (nonCommentedLine.isBlank()) { + return + } + nextHandler = indentations.peek().handler.handleLine(line.trim()) + } + +}
\ No newline at end of file |