I'm working on a project in which I want to parse some input and use it to call a function. The input is a string that is processed at runtime, and the function to be called is specified by the developer. The tokenized input will be passed to the specified function.
This demonstrates how the code might be used. You pass in a function, something to parse, and a set of tokenPatterns that can be matched against to generate tokens.
fun main(args : Array<String>) {
val f = Funcs()
val myColor: String = f.whatColor(Color.GREEN) // Expected
val theirColor: String = Processor.process(f::whatColor, "green", Color.values().toSet())
println(myColor == theirColor) // Should be true
}
class Funcs {
fun whatColor(color: Color): String {
return when (color) {
Color.RED -> "nice red"
Color.GREEN -> "cool green"
}
}
}
enum class Color(override val pattern: Regex) : TokenPattern {
RED(Regex("red")),
GREEN(Regex("green"))
}
The code below shows a basic version of how it currently works (or rather doesn't). Essentially, I tokenize the input based on the set of tokenPatterns, and then call the specified function using those tokens. The issue is I need to cast them back to the original implementation of the pattern (in this case the Color enum) so I can call the original function with them.
/**
* Extend to define custom tokens.
*/
interface TokenPattern {
val pattern: Regex
}
class Token<T: Any>(val type: KClass<out T>)
/**
* Tokenizes and runs functions
*/
object Processor {
fun process(func: KFunction<String>, input: String, tokenPatterns: Set<TokenPattern>): String {
val tokens = tokenize(input, tokenPatterns)
return runIt(func, tokens)
}
private fun tokenize(input: String, tokenPatterns: Set<TokenPattern>): List<Token<*>> {
return tokenPatterns.filter { it.pattern.matches(input) }.map { Token(it::class) }
}
private fun runIt(func: KFunction<String>, tokens: List<Token<*>>): String {
val args: Array<Any> = tokens.toTypedArray()
// Some casting needs to be done
return func.call(*args)
}
The actual code is more complicated than this, but this more or less illustrates how it works together. The main issue I'm having is I'm not sure how I can dynamically cast back to the Color implementation so I can pass it to the function using reflection. I'm also open to other ways of accomplishing this task, and any help would be greatly appreciated! Thanks!
Just store the tokenPattern into the token, pass those tokenPatterns as args, and reflection will be able to cast it appropriately.
You can make the token like this:
class Token(val original: TokenPattern)
Then call the function like this:
private fun runIt(func: KFunction<String>, tokens: List<Token>): String {
val args: Array<Any> = tokens.map { it.original }.toTypedArray()
return func.call(*args)
}