Foxhunt
Foxhunt

Reputation: 870

Kotlin - Evaluate functions from code string

General Question:

I would like to run in a kotlin app some code stored as String.

fun Evaluate(str: String,f:(s : String) -> Unit )
{
    f(str)
}

For example, an Hello World

var function : String = "fun a(s:String) = println(s)"
Evaluate ("Hello World",function)

Is this possible, or maybe something close to this result ?

Specific Question :

I have an activity containing a layout and a map of variable :

private lateinit var glayout: LinearLayout
val variable : MutableMap<String, Any> = mutableMapOf(),
val code : List<String>

override fun onCreate(savedInstanceState: Bundle?) {
   //Some init
   glayout = binding.root.findViewById(R.id.gamelayout)
   code = getCodeFromJson()

   for (c in code){
      //Here execute the code
   }
}

So i would like to be able in my interpreted code to :

Upvotes: 1

Views: 1331

Answers (1)

enzo
enzo

Reputation: 11486

I think the most reasonable way is to write a interpreter using your own language.

abstract class Interpreter {
    fun run(sentence: String) {
        val input = sentence.trim().split(" ")
        val cmd = input[0]
        val args = input.drop(1)
        execute(cmd, args)
    }
    
    protected abstract fun execute(command: String, args: List<String>)
}

For example, if you have a map and you want the user to modify it:

class MapInterpreter(private val map: MutableMap<String, String>) : Interpreter() {
    override protected fun execute(command: String, args: List<String>) {
        when (command) {
            "putmap" -> {
                require(args.size == 2) { "usage: addmap [key] [value]" }
                map[args[0]] = args[1]
            }
            "remmap" -> {
                require(args.size == 1) { "usage: remmap [key]" }
                map.remove(args[0])
            }
            "showmap" -> {
                require(args.size == 0) { "usage: showmap" }
                println(map)
            }
        }
    }
}

To use it, just call the run method with the user input (from a text field, for example):

val map: MutableMap<String, String> = hashMapOf()
    
val interpreter = MapInterpreter(map)
interpreter.run("putmap I one")
interpreter.run("putmap V five")
interpreter.run("putmap X ten")
interpreter.run("putmap 2 two")
interpreter.run("showmap")
interpreter.run("remmap 2")
interpreter.run("showmap")
// Output:
// {2=two, V=five, X=ten, I=one}
// {V=five, X=ten, I=one}

Another example; to instantiate a Android View dynamically:

class ViewBuilderInterpreter(private val context: Context, private val parent: View) : Interpreter() {
    override protected fun execute(command: String, args: List<String>) {
        when (command) {
            "textview" -> {
                require(args.size >= 1 && args.size <= 2) { "usage: textview [text] {color}" }
                parent.addView(Text(context).also {
                    it.text = args[0]
                    it.color = if (args.size == 1) Color.BLACK else Color.parseColor(args[1])
                })
            }
            // ...
        }
    }
}

Of course that's just an idea, you also need to handle invalid commands and exceptions that might occur.

Upvotes: 1

Related Questions