Curtis Ullerich
10/06/2022, 8:39 PMval matcher = plus { num { where { it.value > 2 } } variable {} }
val ast = Addition(Number(3), Variable("y"))
val matched = matcher.matches(ast)
I'm hoping to also support property extraction. I implemented something that works, but it feels gross using nullable vars like this:
var value: Int? = null
var name: String? = null
val extracter = plus { num { extract { value = it.value } } variable { extract { name = it.name } } }
val ast = Addition(Number(3), Variable("y"))
extracter.extractFrom(ast)
println("$value $name") // 3 y
Is there a pattern I could use to implement something more like this?
val (value: Int, name: String) = extracter.extractFrom(ast) ?: return null
molikuner
10/07/2022, 2:20 PMval extraction = extractor.extract(ast)
val value: Int by extraction
val variable: String by extraction
println("value: $value")
println("variable: $variable")
But you would loose some compile-time safety, that you have with the variables right now 🤔. Is that something, that you need?molikuner
10/07/2022, 2:31 PMCurtis Ullerich
10/07/2022, 5:00 PMCurtis Ullerich
10/07/2022, 5:06 PMmolikuner
10/07/2022, 5:38 PMmolikuner
10/07/2022, 5:51 PMCurtis Ullerich
10/07/2022, 6:05 PMCurtis Ullerich
10/07/2022, 6:08 PMval (name, value) = extracter.extract<String, Int>(ast) ?: return null
the extract
calls inside the dsl could specify the type too, like variable { extract<String> { it.name } }
Curtis Ullerich
10/07/2022, 6:10 PMmolikuner
10/07/2022, 9:25 PMval ast = Addition(listOf(Number(3), Variable("y")))
val extractor = object : plus() {
val operands = extracting(precondition = { it.isNotEmpty() }) { it.values }
val num = object : num(precondition = { it.value > 2 }) {}
val variable = object : variable() {
val isY = extracting { it.name == "y" }
val isX = extracting { it.name == "x" }
}
val otherVariable = object : variable(precondition = { it.name.count() == 1 }) {
val isX = extracting { it.name == "x" }
}
}
val operands = extractor.operands(ast)
val value = extractor.num(ast)?.value
val customVariableName = extractor.variable(ast)?.name
val isY = extractor.variable.isY(ast)
val isX = extractor.variable.isX(ast)
val isOtherX = extractor.otherVariable.isX(ast)
Unfortunately it has a few object :
and val
keywords, but it has a lot of compile-time safety. You're not able to reference a value, that doesn't exist and you can't mess up the type of the variable. Additionally it even supports type inference when reading from the AST. The syntax isn't as nice as your initial DSL, but how much of it, do you want to have? Also, how flexible is this matcher DSL of yours? You might be able to use it instead of this custom precondition "thing", that I created here.
If you want to check it out anyway, here is the link: https://pl.kotl.in/2xIC85wzo
maybe there's enough information for the compiler in a call like this?
val (name, value) = extracter.extract<String, Int>(ast) ?: return null
theYou might be able to use some kind of type modeling, as explained here https://kt.academy/article/type-modelling-kotlin, but I feel like this would lead to an explosion of symbols, since you're not working with simple lists and probably want to make extraction part compile-time safe.calls inside the dsl could specify the type too, likeextract
variable { extract<String> { it.name } }
Curtis Ullerich
10/07/2022, 10:31 PMCurtis Ullerich
10/07/2022, 10:51 PMdata class Values(name: String, value: Int)
val extractor = extractor<Values> {
plus {
variable { values.name = it.name }
number { values.value = it.value }
}
}
val ast = Addition(Variable("x"), Number(2))
val (name, value) = extractor.extract(ast) ?: return null
println("$name $value") // x 2
I think the only hope of something like that working would be if reified generics are applicable here; not sure about that yet.
I'll look at the article in more detail too!