I'm trying to write a visitor function in Kotlin that adds two integers together. I've been working off of some sample code and I can't figure out what these .value or .visit functions are. It doesn't seem to be declared in the sample code, so I'm unsure how to declare it in my code. Whenever I compile the code, I get an error saying that value is an unresolved reference.
Relevant Kotlin code:
package backend
import org.antlr.v4.runtime.*
import grammar.*
abstract class Data
class IntData(val value: Int): Data() {
override fun toString(): String
= "Int($value)"
}
class Context(): HashMap<String, Data>() {
constructor(parent: Context): this() {
this.putAll(parent)
}
}
abstract class Expr {
abstract fun eval(scope: Context): Data
fun run(program: Expr) {
try {
val data = program.eval(Context())
println("=> ${data}")
} catch(e: Exception) {
println("[err] ${e}")
}
}
}
class IntLiteral(val value: Int): Expr() {
override fun eval(scope:Context): Data
= IntData(value)
}
enum class Op {
Add,
Sub,
Mul,
Div
}
class Arithmetic(
val op: Op,
val left: Expr,
val right: Expr): Expr() {
override fun eval(scope: Context): Data {
val x = (left.eval(scope) as IntData).value
val y = (right.eval(scope) as IntData).value
return IntData(
when(op) {
Op.Add -> x + y
Op.Mul -> x * y
Op.Sub -> x - y
Op.Div -> x / y
}
)
}
}
}
class Compiler: PLBaseVisitor<Expr>() {
val scope = mutableMapOf<String, Expr>()
override fun visitAddExpr(ctx: PLParser.AddExprContext): Expr {
val xValue = this.visit(ctx.x)
val yValue = this.visit(ctx.y)
val result = xValue.value + yValue.value
return IntLiteral(result)
}
}
Relevant Antlr Grammar:
expr : x=expr '+' y=expr # addExpr
| x=expr '-' y=expr # subExpr
| x=expr '*' y=expr # mulExpr
| x=expr '/' y=expr # divExpr
;
Code I'm trying to execute:
val test = """
x=1+2
print(x)
"""
fun parse(source: String): PLParser.ProgramContext {
val input = CharStreams.fromString(source)
val lexer = PLLexer(input)
val tokens = CommonTokenStream(lexer)
val parser = PLParser(tokens)
}
val testTree = parse(source1)
val testTree = parse(source1)
fun execute(program: Expr?) {
if(program == null) {
println("Program is null.")
return
}
try {
val data = program.eval(Context())
println("> ${data}")
} catch(e: Exception) {
println("[err] ${e}")
}
}
execute(testProgram)
Code from sample:
data class NodeValue(val value: Int)
val visitor = object: CalcBaseVisitor<NodeValue>() {
override fun visitAddition(ctx: CalcParser.AdditionContext): NodeValue {
val xValue = this.visit(ctx.x)
val yValue = this.visit(ctx.y)
return NodeValue(xValue.value + yValue.value)
}
override fun visitValue(ctx: CalcParser.ValueContext): NodeValue {
val lexeme = ctx.Number().getText()
return NodeValue(lexeme.toInt())
}
}
You don’t show the code for your program.eval() method.
The eval function would need to create an instance of your Visitor. (You’ve done that and called it visitor).
You also have the root expr node in you program variable.
Now you would have your visitor “visit” that node and save the return value:
val nodeVal = visitor.visit(program)
At that point nodeVal.value will have the result of visiting that expression.
note: since you’re doing the evaluation in your visitor, there’s not really any use for your Arithmetic class (unless you refactor your visitor to use it instead of just doing the math, but I don’t see much value in that as the visitor is already pretty easy to read).
In next code I'm trying to write DSL, which allowed some constructions for hands of some robot.
It should have syntax below, which I should use in outer context for RobotBuilder class:
robot {
hands {
plastik width 3
load = light - medium
}
head {
...
}
etc...
}
The problem is in string load = light - medium. It's do nothing, but Robot created. What I do? Just overload minus operator for Int. For me when Kotlin compiler see expression of = - it should use overloaded operator. What I am doing wrong?
Should I create special class like LoadClassBuilder with only one attribute of type LoadClass and then overload minus operator in HandsBuilder for - and change types for load, light and medium or problem in outer context of my DSL?
class HandsBuilder {
var material: Material = Metal(0)
var minLoad: LoadClass = LoadClass.Medium
var maxLoad: LoadClass = LoadClass.Medium
val plastik: Plastik = Plastik(0)
val metal: Metal = Metal(0)
infix fun Metal.width(width: Int) {
material = Metal(width)
}
infix fun Plastik.width(width: Int) {
material = Plastik(width)
}
var load: Int = 0
val light: Int = 1
val medium: Int = 2
operator fun Int.minus(other: Int): Int {
println("minus call")
minLoad = LoadClass.values()[this]
println(this)
maxLoad = LoadClass.values()[other]
println(other)
return 1
}
fun build() = Hands(material, minLoad, maxLoad)
}
Update 1:
fun hands(init: HandsBuilder.() -> Unit) {
handsParamInRobotBuilderClass = HandsBuilder().apply(init).build()
}
How a generic result or error type could be defined in Kotlin? Something like this example from TypeScript
type Errorneous<E, R> =
{ is_error: true, error: E } | { is_error: false, result: R }
function calculate(): Errorneous<String, Number> {
return { is_error: false, result: 2 }
}
The problem is that Kotlin doesn't have generic sealed classes.
It's possible to define something like
data class Errorneous<E, R>(val error: E?, val result: R?)
But it not ideal as it allows wrong usage like
Errorneous<String, Int>(null, null)
Errorneous<String, Int>("", 2)
UPDATE
Possible (not compiling) Kotlin code
sealed class Errorneous
class Success<R>(val result: R) : Errorneous()
class Fail<R>(val error: R) : Errorneous()
fun calculate(): Errorneous {
return Success(2)
}
fun main() {
val result = calculate()
if (result is Success<*>) {
val r: Int = result.result // <= Problem here, no smart cast
}
}
You have to add generic parameters to the base class as well:
sealed class Errorneous<E,R>
class Error<E,R>(val error: E): Errorneous<E,R>()
class Success<E,R>(val result: R): Errorneous<E,R>()
fun calculate(): Errorneous<String, Int> {
return Success(2)
}
fun main() {
val result = calculate()
if (result is Success<*, Int>) {
val r: Int = result.result // <= smart cast
}
}
I am designing a DSL and run into a requirement where I have a variable which could be assigned to different ways. Greatly simplified, I would like to set value property either by an integer or by an expression in String. (The real need is even more complex.)
I would like to write in my DSL:
value = 42
or
value = "6*7"
Behind the scene, the value will be stored in a DynamicValue<Int> structure which contains either an integer or the expression.
class DynamicValue<T>(dv : T?, expr : String) {
val directValue : T? = dv
val script : String? = expr
...
}
I tried several ways (delegate, class, etc), but none of them provided these syntax.
Is there a way to declare this union like structure?
What do you think about the following syntax:
value(42)
value("6*7")
//or
value+=42
value+="6*7"
You can do this with operator functions:
class DynamicValue<T>() {
var dv: T? = null
var expr: String? = null
operator fun invoke(dv : T) {
this.dv = dv
this.expr = null
}
operator fun invoke(expr: String) {
this.dv = null
this.expr = expr
}
operator fun plusAssign(dv : T) {
this.dv = dv
this.expr = null
}
operator fun plusAssign(expr: String) {
this.dv = null
this.expr = expr
}
}
You can't redefine the assign operator in Kotlin, therefor the pure syntax value=42 is not possible.
But I wouldn't go with operator functions, it's to magical. I would do this:
val value = DynamicValue<Int>()
value.simple=42
value.expr="6*7"
class DynamicValue2<T>() {
private var _dv: T? = null
private var _expr: String? = null
var simple: T?
get() = _dv
set(value) {
_dv = value
_expr = null
}
var expr: String?
get() = _expr
set(value) {
_expr = value
_dv = null
}
}
Rene's answer gave me the lead and finally I turned up with this solution.
In this solution I took all my requirements in (the ones I dropped out in my original question) so this became much more complicated than my original question would have required.
My whole requirement was to be able to add static values or scripts (snippets) running on a well guarded context. These script would be stored, and executed later. I wanted to enable the whole power of the IDE when writing the script, but would like to guard my scripts from code injections and help the user to use only the context values the script requires.
The trick I used to achieve this is to enable adding script in kotlin, but before I run the whole DSL script and create the business objects, I convert the script into a string. (This string will be executed later in a guarded, wrapped context by JSR233 engine.) This conversation forced me to tokenize the whole script before execution and search/replace some of the tokens. (The whole tokenizer and converter is rather long and boring, so I won't insert here.)
First approach
What my goal was to be able to write any of this:
myobject {
value = static { 42 } // A static solution
value = static { 6 * 7 } // Even this is possible
value = dynamic{ calc(x, y) } // A pure cotlin solution with IDE support
value = dynamic("""calc(x * x)""") // This is the form I convert the above script to
}
where calc, x and y are defined in the context class:
class SpecialScriptContext : ScriptContextBase() {
val hello = "Hello"
val x = 29
val y = 13
fun calc(x: Int, y: Int) = x + y
fun greet(name: String) = println("$hello $name!")
}
So let's see the solution! First I need a DynamicValue class to hold one of the values:
class DynamicValue<T, C : ScriptContextBase, D: ScriptContextDescriptor<C>>
private constructor(val directValue: T?, val script: String?) {
constructor(value: T?) : this(value, null)
constructor(script: String) : this(null, script)
}
This structure will ensure that exactly one of the options (static, script) will be set. (Don't bother with the C and D type parameters, they are for context-based script support.)
Then I made top level DSL functions to support syntax:
#PlsDsl
fun <T, C : ScriptContextBase, D : ScriptContextDescriptor<C>> static(block: () -> T): DynamicValue<T, C, D>
= DynamicValue<T, C, D>(value = block.invoke())
#PlsDsl
fun <T, C : ScriptContextBase, D : ScriptContextDescriptor<C>> dynamic(s: String): DynamicValue<T, C, D>
= DynamicValue<T, C, D>(script = s)
#PlsDsl
fun <T, C : ScriptContextBase, D : ScriptContextDescriptor<C>> dynamic(block: C.() -> T): DynamicValue<T, C, D> {
throw IllegalStateException("Can't use this format")
}
An explanation to the third form. As I wrote before, I don't want to execute the block of the function. When the script is executed, this form is converted to the string form, so normally this function would never appear in the script when executed. The exception is a sanity warning, which would never be thrown.
Finally added the field to my business object builder:
#PlsDsl
class MyObjectBuilder {
var value: DynamicValue<Int, SpecialScriptContext, SpecialScriptContextDescriptor>? = null
}
Second approach
The previous solution worked but had some flaws: the expression was not associated with the variable it set, neither with the entity the value was set in. With my second approach I solved this problem and removed the need of equal sign and most of the unnecessary curly brackets.
What helped: extension functions, infix functions and sealed classes.
First, I split the two value types into separated classes defined a common ancestor:
sealed class Value<T, C : ScriptContextBase> {
abstract val scriptExecutor: ScriptExecutor
abstract val descriptor: ScriptContextDescriptor<C>
abstract val code: String
abstract fun get(context: C): T?
}
class StaticValue<T, C : ScriptContextBase>(override val code: String,
override val scriptExecutor: ScriptExecutor,
override val descriptor: ScriptContextDescriptor<C>,
val value: T? = null
) : Value<T, C>() {
override fun get(context: C) = value
constructor(oldValue: Value<T, C>, value: T?) : this(oldValue.code, oldValue.scriptExecutor, oldValue.descriptor, value)
}
class DynamicValue<T, C : ScriptContextBase>(override val code: String,
script: String,
override val scriptExecutor: ScriptExecutor,
override val descriptor: ScriptContextDescriptor<C>)
: Value<T, C>() {
constructor(oldValue: Value<T, C>, script: String) : this(oldValue.code, script, oldValue.scriptExecutor, oldValue.descriptor)
private val scriptCache = scriptExecutor.register(descriptor)
val source = script?.replace("\\\"\\\"\\\"", "\"\"\"")
private val compiledScript = scriptCache.register(generateUniqueId(code), source)
override fun get(context: C): T? = compiledScript.execute<T?>(context)
}
Note, that I made the primary constructor internal and created a kind of copy and alter constructor. Then I defined the new functions as extension of the common ancestor and marked them infix:
infix fun <T, C : ScriptContextBase> Value<T, C>.static(value: T?): Value<T, C> = StaticValue(this, value)
infix fun <T, C : ScriptContextBase> Value<T, C>.expr(script: String): Value<T, C> = DynamicValue(this, script)
infix fun <T, C : ScriptContextBase> Value<T, C>.dynamic(block: C.() -> T): Value<T, C> {
throw IllegalStateException("Can't use this format")
}
Using the secondary copy-and-alter constructor allows to inherit the context sensitive values. Finally I initialize the value inside the DSL builder:
#PlsDsl
class MyDslBuilder {
var value: Value<Int, SpecialScriptContext> = StaticValue("pl.value", scriptExecutor, SpecialScriptContextDescriptor)
var value2: Value<Int, SpecialScriptContext> = StaticValue("pl.value2", scriptExecutor, SpecialScriptContextDescriptor)
}
Everything is in place and now I can use it in my script:
myobject {
value static 42
value2 expr "6 * 7"
value2 dynamic { calc(x, y) }
}
I wish to use several classes interchangeably, all of them implementing a method "add", so I start to write a trait:
trait CanAdd{
def add(that: CanAdd): CanAdd
}
then implement my classes and hit a problem: I cannot add any "CanAdd" with any other, they need to be of the same class. I workaround the problem with some ugly code relying on "isInstanceOf":
class A(val value: Int) extends CanAdd{
def add(that: CanAdd): CanAdd = {
if(!that.isInstanceOf[A]) sys.error("")
val thatA: A = that.asInstanceOf[A]
new A(value + thatA.value)
}
}
class B(val value: Boolean) extends CanAdd{
def add(that: CanAdd): CanAdd = {
if(!that.isInstanceOf[B]) sys.error("")
val thatB: B = that.asInstanceOf[B]
new B(value ^ thatB.value)
}
}
Finally, I use the classes as follow:
class User(val stuff: Array[CanAdd]) {
def add(that: User): User = {
assume(stuff.length==that.stuff.length)
val out = new Array[CanAdd](stuff.length)
for( i <- 0 until stuff.length) out(i) = stuff(i).add(that.stuff(i))
new User(out)
}
}
val u1=new User(Array(new A(0)))
val u2=new User(Array(new B(false)))
val u3 = u1.add(u1)
val u4 = u1.add(u2) //should fail, ideally, should not even compile
I don't like it because first it is a burden to write the boiler plate code with "isInstanceOf" and second because it fails at run time instead of compile time.
My question: how would you do that, having in mind some plan to implement many more methods than just "add" and maybe to implement few other classes with very different internal representations ?
trait CanAdd[T <: CanAdd[T]] {
def add(a: T): T
}
class A(val value:Int) extends CanAdd[A] {
def add(that:A) = new A(value+that.value)
}
class B(val value:Boolean) extends CanAdd[B] {
def add(that:B) = new B(value ^ that.value)
}
class User[X <: CanAdd[X] : Manifest](val stuff:Array[X]) extends CanAdd[User[X]]{
def add(that:User[X]):User[X] = {
assume(stuff.length==that.stuff.length)
new User(stuff.zip(that.stuff).map(t => t._1.add(t._2)) toArray)
}
}
val u1 = new User(Array(new A(0)))
val u2 = new User(Array(new B(false)))
val u3 = u1.add(u1)
val u4 = u1.add(u2) // compile error: type mismatch